losLab PDF Library for Delphi komprimuje naskenovanou stránku metodou Mixed Raster Content přes TPDFlib.CompressPageAsMRC. Metoda stránku vyrenderuje, z plného rozlišení vytvoří prahováním 1bitovou masku popředí, zbytek downsampluje do JPEG pozadí a postaví stránku znovu ze dvou obrázků: pozadí /DCTDecode a stencil masky /CCITTFaxDecode malované aktuální barvou výplně
Stránka, pro kterou tohle existuje, je ta, jichž je každý dokumentový archiv plný: barevný sken faktury nebo smlouvy v 300 DPI uložený jako jeden velký JPEG. Zakódujte ho v kvalitě, která udrží text čitelný, a soubor je velký; pusťte kvalitu dolů a kolem písmen se objeví halo a rozmazané patky, protože to, v čem je JPEG nejhorší, je ostrá černá hrana na bílém papíru. Mixed Raster Content přestane požadovat po jednom kodeku dvě práce
Proč jediný JPEG dělá naskenovaný text rozmazaný i velký?
Jediný JPEG si naskenovaný text poradí špatně, protože DCT kodek utrácí bity za hladké přechody a tvrdé hrany aproximuje vysokofrekvenčními koeficienty, které kvantizace zahazuje jako první. Text na skenu není skoro nic jiného než tvrdé hrany. Udržet ty hrany čisté vynutí vysokou kvalitu pro celou stránku, včetně textury papíru, loga a kávové skvrny, což nepotřebovalo žádné z toho
Myšlenka MRC rozdělí stránku podle toho, co každá oblast skutečně je. Inkoust se stane bilevel obrázkem, kde faxový kodek jako CCITT zaznamenává běhy černých a bílých pixelů bezztrátově a velmi kompaktně. Všechno ostatní — tón papíru, fotky, barevná pozadí — se stane spojitotónovým obrázkem, který se dá uložit ve zlomku rozlišení, protože nikdo nečte pozadí v 300 DPI. Bilevel vrstva udrží text ostrý v plném rozlišení; vrstva pozadí udrží barvu za nízkou cenu
Jak CompressPageAsMRC rozdělí stránku na dvě vrstvy?
TPDFlib.CompressPageAsMRC jede jednu pevnou pipeline na stránku. Stránku vyrenderuje v požadovaném DPI do 24bitové bitmapy, každý pixel klasifikuje podle váženého jasu Rec. 601 (299/587/114 z tisíce) proti Threshold a výsledek zapíše do 1bitové masky ve stejném rozlišení. Paralelně tentýž render zmenší přes BGScale boxovým průměrem, takže BGScale 4 promění každý blok 4×4 pixelů na jeden pixel pozadí
Obsah stránky se pak vymění. Původní operátory se odstraní, zmenšené pozadí se embedduje jako obrázek /DCTDecode v /DeviceRGB (ISO 32000-1 §7.4.8), maska se zakóduje knihovním CCITT enkodérem jako obrázek /CCITTFaxDecode (§7.4.6) s příznakem /ImageMask true a obojí se nakreslí přes celý page box s maskou navrchu. Signatura je krátká:
function CompressPageAsMRC(Page: Integer; DPI: Double;
BGScale, JPEGQuality, Threshold, Options: Integer): Integer;
Pagese počítá od 1 a musí ležet vPageCountDPIje renderovací rozlišení zdrojového rastru, 50 až 1200BGScaleje faktor zmenšení pozadí, 1 až 8JPEGQualityje JPEG kvalita pozadí, 1 až 100Thresholdje řez jasem, 1 až 255; pixely na hodnotě a světlejší jsou papír, tmavší pixely jsou inkoustOptionsje rezervované a musí být 0
Typická dávka nad naskenovaným souborem vypadá takhle. SetFillColor volejte těsně před každou stránkou, z důvodu, který vysvětluje další oddíl
uses
SysUtils, PDFlibrary;
procedure CompressScannedFile(const SrcFile, DstFile: string);
var
Lib: TPDFlib;
P, Replaced: Integer;
begin
Lib := TPDFlib.Create;
try
if Lib.LoadFromFile(SrcFile, '') <> 1 then
raise Exception.CreateFmt('Cannot open %s', [SrcFile]);
for P := 1 to Lib.PageCount do
begin
Lib.SetFillColor(0, 0, 0); // barva inkoustu stencil vrstvy
// maska 300 DPI, pozadí 75 DPI, JPEG kvalita 60, řez při luma 160
Replaced := Lib.CompressPageAsMRC(P, 300, 4, 60, 160, 0);
if Replaced = 0 then
raise Exception.CreateFmt('MRC failed on page %d', [P]);
end;
if Lib.SaveToFile(DstFile) <> 1 then
raise Exception.CreateFmt('Cannot save %s', [DstFile]);
finally
Lib.Free;
end;
end;
Proč se popředí maluje aktuální barvou výplně?
Vrstva popředí je stencil maska a stencil maska nemá žádnou vlastní barvu. ISO 32000-1 §8.9.6.2 definuje obrázek s /ImageMask true jako 1bitový tvar: vzorové hodnoty značí, kam barva patří, a barva je aktuální nonstroking barva v grafickém stavu. Přesně to dělá vrstvu levnou a je to i důvod, proč CompressPageAsMRC nebere žádný barevný parametr
TPDFlib.CompressPageAsMRC maluje masku barvou nastavenou přes SetFillColor, takže pořadí volání záleží. Painter vysílá barvu výplně líně: SetFillColor si barvu zapamatuje a označí ji jako změněnou a stencil kreslení zapíše operátor rg uvnitř vlastního bloku q/Q jen tehdy, dokud je ta změna stále nevyřízená. Nakreslíte mezi tím vyplněný tvar a nevyřízenou změnu spotřebuje místo toho ten tvar. Nastavení barvy těsně před každou stránkou, jako v dávce výše, odstraní luštění. Černá je normální volba; tmavě modrá (navy) funguje u formulářů psaných modrým inkoustem, kde chcete, aby výstup vypadal méně jako fotokopie
// Formulář psaný modrou kuličkovou perem: vykreslete vrstvu inkoustu v tmavé modré
Lib.SetFillColor(0.08, 0.12, 0.35);
Replaced := Lib.CompressPageAsMRC(PageNo, 300, 4, 55, 170, 0);
Umístění se řídí souřadnicovým počátkem knihovny. CompressPageAsMRC čte GetOrigin a kreslí oba obrázky od horní hrany page boxu: horní souřadnice 0, když je počátek nahoře vlevo (SetOrigin(1)), horní souřadnice PageHeight při výchozím počátku dole vlevo. Nic nemusíte upravovat, ale vysvětluje to, proč vrstvy dopadnou správně, ať už vaše pipeline používala jakýkoli počátek
Jeden důsledek stencil sémantiky stojí za výslovné řízení: každý pixel klasifikovaný jako inkoust dostane tutéž barvu. Rudé razítko, modrý podpis i černý text článku skončí v jedné barvě výplně na vrstvě popředí. Jen pozadí JPEG uchová slabou, nízkorozlišenou stopu původních barev pod tím
Volba DPI, BGScale, Threshold a JPEG kvality
Hodnota DPI rozhoduje o ostrosti textu a 300 je rozumný výchozí bod pro kancelářské skeny. Maska se drží v plném renderovacím rozlišení, takže DPI určuje, jak tenký tah přežije prahování. Jít výš než původní rozlišení skenu nekupuje nic, protože by renderer jen upsamplvoval embedded skenový obrázek. Paměť je druhý limit: stránka US Letter v 300 DPI se vyrenderuje na 2550×3300 pixelů, 25 245 000 bajtů 24bitových pixelových dat, než se odvodí maska a pozadí, a ta samá stránka u stropu 1200 DPI je 10200×13200 pixelů, zhruba 404 MB
BGScale vyměňuje detail pozadí za velikost. Na 300 DPI dává faktor 4 pozadí 75 DPI, 638×825 pixelů pro Letter, což pro tón papíru a ploché barevné plochy bohatě stačí. Stránky s fotografiemi nebo rastrovými ilustracemi, které si čtenář skutečně prohlédne, si zaslouží 2; čistý text na bílém papíru zvládne 6 nebo 8, protože pozadí je sotva víc než nádech
Threshold je parametr, který mění to, jak stránka vypadá. Zvýšení klasifikuje víc polosedých pixelů jako inkoust, což ztlustí tenké tahy a tužkové čáry, ale natáhne to i šum skeneru a stín papíru jako tečky. Snížení dá čistší papír a tenčí, někdy přerušená písmena. Hodnoty kolem 128 až 170 jsou rozumný start pro černý text na bílém; kompromis mezi oběma je tentýž, o kterém pojednává vykreslování stránek PDF do 1bitové monochromie v Delphi, kde stejný jasový práh řídí RenderPageToMonochromeFileEx. Na rozdíl od té metody nemá CompressPageAsMRC žádný despeckle průchod, takže šumivé skeny poslouží lépe mírně nižší práh
JPEGQuality záleží méně než u skenu jako jediného obrázku, protože pozadí už nenese žádné hrany textu. Kvalita 50 až 70 bývá neviditelná na pozadí 75 DPI za ostrou maskou
Co říká návratová hodnota a co ne?
TPDFlib.CompressPageAsMRC vrací velikost v bajtech obsahu stránky, který nahradila, a 0, když něco selhalo. Je to indikátor úspěchu, ne míra úspory. Na typické naskenované stránce je původní content stream pár desítek bajtů, jen dost operátorů na umístění jednoho obrázku, takže malé kladné číslo je normální výsledek, i když stránka zeštíhle o megabajty. Stránka, jejíž původní content stream byl prázdný, hlásí 0, i když byla postavena znovu
Chování při selhání není atomické a právě na to je potřeba navrhovat. Kontroly argumentů, render i JPEG kódování běží dřív, než se na stránku sáhne, takže BGScale mimo rozsah, JPEGQuality 101 nebo selhání renderu nechají stránku přesně tak, jak byla. Jakmile se ale původní obsah vymaže, selhání embeddu jedné z vrstev nebo jejího vykreslení také vrátí 0, a v tom okamžiku už může být stránka částečně postavena znovu. Jakoukoli nulu berte jako důvod zahodit dokument v paměti místo jeho uložení, a výsledek nikdy nepište přes zdrojový soubor
Abyste zjistili, co konverze skutečně ušetřila, změřte dokument. Srovnání délky SaveToString před a po je nejrychlejší kontrola; odpověď po kategoriích dá audit velikosti PDF s AuditDocumentSpace, který ukáže, jestli se váha přesunula z obrázků podle očekávání
function MRCSavings(Lib: TPDFlib): Int64;
var
Before: Int64;
P: Integer;
begin
Before := Length(Lib.SaveToString);
for P := 1 to Lib.PageCount do
begin
Lib.SetFillColor(0, 0, 0);
if Lib.CompressPageAsMRC(P, 300, 4, 60, 160, 0) = 0 then
raise Exception.CreateFmt('Page %d failed; discard this document', [P]);
end;
Result := Before - Length(Lib.SaveToString);
end;
Kde tohle dvouvrstvé MRC za ITU-T T.44 zaostává
CompressPageAsMRC implementuje záměrně zjednodušené MRC. Plný model v ITU-T T.44 používá tři vrstvy na stripe: obrázek pozadí, obrázek popředí, který nese barvy inkoustu, a bilevel masku, která mezi nimi vybírá pixel po pixelu, s místem pro několik párů maska a popředí na jedné stránce. V PDF řeči by třetí vrstva byla nízkorozlišený barevný obrázek popředí s explicitní maskou (§8.9.6.3) místo stencilu malovaného jednou barvou. PDF Library for Delphi vynechá barevnou vrstvu popředí, což výsledek dělá tak malým a je to i důvod, proč barevný inkoust sklouzne do barvy výplně
- Metoda sedí na černý nebo jednobarevný text na naskenovaném papíru; dokumenty kódované barvami, zvýrazněný text a barevná razítka přijdou o barvu inkoustu
- Znovu postavená stránka je čistý raster: skutečný text, vektorová grafika i vybíratelné znaky na původní stránce se stanou pixely, takže to pouštějte na skeny, ne na born-digital stránky, kde jsou správnými nástroji font subsetting a downsampling obrázků
- Maska používá knihovní CCITT enkodér ve dvourozměrném režimu T.4, zapsaný jako
/CCITTFaxDecodes/K 1; enkodér má i režim Group 4 přesAddImageCCITTFromBitmapStream, aleCompressPageAsMRCho nevystavuje a JBIG2 nepoužívá - Anotace a formulářová pole se neflattují; zůstávají jako objekty nad znovu postaveným obsahem
- Pozadí je
/DeviceRGB, takže výstup nesedí na workflow PDF/X-1a a samotnéSetFillColorje v režimu PDF/X-1a odmítáno - Sken, který bude potřebovat OCR, se má rozpoznat před MRC, dokud původní pixely ještě existují v plné barevné hloubce
FAQ
Udržuje CompressPageAsMRC text vyhledavatelný?
Ne. CompressPageAsMRC vymění obsah stránky za dva obrázky, takže všechny textové operátory na původní stránce, včetně neviditelné OCR textové vrstvy, zmizí s nimi. OCR pusťte na komprimované stránce poté, nebo textovou vrstvu po komprimaci přidejte zpět
Dá se komprimovat jen část stránek dokumentu?
Ano. CompressPageAsMRC pracuje na jednu stránku (od 1) na volání, takže stránky vybíráte vy a můžete i měnit BGScale a Threshold po stránkách, třeba faktor 2 pro vloženou fotku a 6 pro stránky s čistým textem
Jaký má být parametr Options?
Předávejte 0. Options je v aktuální verzi rezervované pro budoucí použití a žádné bity nejsou definované, takže nenulová hodnota dnes neznamená nic
CompressPageAsMRC přibyl v losLab PDF Library for Delphi v3.561.0 spolu se stavebními bloky render, JPEG, CCITT a stencil maska, ze kterých je složen, všechny dostupné jako veřejné metody, kdybyste potřebovali variantu pipeline. Knihovna, její podpora C++Builderu a kompletní API reference jsou na stránce produktu PDF Library for Delphi