Két panasz érkezik abban a héten, amikor egy tömörítési funkció megjelenik: a beszkennelt szerződés betűalakjai most lépcsősek és szálkásak, a címlap átlátszó logóját pedig halvány haló övezi. A PDFiumPas mindkettőre egyetlen helyen válaszol. A TPdf.OptimizeImages minden képet megmér, mielőtt zsugorítaná, majd átméretezési magot választ, és a színt alfa-tudatos formában halmozza fel
Ez nem mindig volt így. A v3.100.0 előtt ugyanez a módszer minden nem bilevel képet egy rögzített legközelebbi szomszéd lépéssel méretezett át, ami pontosan az az algoritmus, amely mindkét panaszt előidézi: kimeneti pixelenként egy forráspixelt tart meg, és a teljesen átlátszó pixel alatt álló RGB-vel úgy bánik, mintha egy olvasó valaha látná. A v3.100.0 újraírása ezt az egyetlen útvonalat öt magra, egy mért kiválasztási szabályra és egy explicit munkamemória-keretre cseréli
Miért lesz a beszkennelt szöveg recés a kicsinyítéstől?
Mert a pontminta a rossz kérdésre válaszol. Amikor egy 300 DPI-s szkennelést 150 DPI-re céloznak át, minden célpixel egy 2×2-es forráspixel-blokkot képvisel, és a legközelebbi szomszéd a négy közül egyet megtart, a többit eldobja. Az dönti el, melyik marad meg, hogy a kerekítés hogyan esik, így a forrásban simán élsimított vonalél pixelenként pénzérme-feldobássá válik. Az eredmény a klasszikus aliasos lépcső a glyph-éleken, plusz moaré a halftone területeken, ahol az eldobott minták épp a mintázatot hordozták. Ez PDF-ben fontosabb, mint a képernyőn, mert a kár végleges. Egy image XObject a mintaadatát a /Width, /Height és /BitsPerComponent mellett hordozza (ISO 32000-1 §8.9.5), és az átméretezés mindhármat újraírja a fájlban. A rossz nagyítás egy megjelenítőben csak újrarajzolható képkocka, és arra a PDFiumPasnak külön gépezete van a render cache és zoom teljesítmény cikkben. A rossz kicsinyítés viszont egy új dokumentum, amelyet az ügyfélnek ad át
Hogyan méri a PDFiumPas a részletgazdagságot, és hogyan választ magot?
A PDFiumPas képenként dönt, nem dokumentumonként. Mielőtt magot választana, normalizált luminancia-részletpontszámot számol egy korlátos mintavételi rácsból: a vízszintes és függőleges lépések (Width + 63) div 64 és (Height + 63) div 64, így egy 12000 pixeles szkennelés és egy 300 pixeles indexkép egyaránt nagyjából ugyanannyiba kerülő 64×64-es bejárást jelent. Minden mintavett pozíción összegzi az abszolút különbséget a jobbra és az alatta lévő szomszédhoz, legfeljebb három csatornán, majd elosztja a mintaszám szorozva 255-tel. A pontszám 0 és 1 közé esik, ahol a lapos üzleti grafika a nulla közelében ül, a sűrű fotós textúra pedig felkúszik
A kiválasztási létra rögzített sorrendben fut. Ha a ResampleFilter bármi más, mint pirfAdaptive, az a szűrő szó szerint alkalmazásra kerül. Egyébként: az 1 bites tartalom pirfBilevel-t kap; a piccLineArt ContentClass pirfBox-ot kap; a 4 vagy afeletti skálafaktor szintén pirfBox-ot, mert akkora kicsinyítésnél a területi átlag egyszerre a legolcsóbb és a leghelyesebb válasz; a piccPhoto, a 0.08 vagy afeletti részletpontszám vagy a 0.9 vagy afeletti PreferredQuality pirfLanczos-t kap a háromlebenyes magjával; a 2 vagy afeletti skála vagy a 0.7 vagy afeletti minőség pirfBicubic-ot kap 2 sugárral; ami marad, az pirfBilinear-t kap. Mivel a TPdfImageOptimizeOptions.Default a PreferredQuality-t 0.85-re állítja, egy alapértelmezett futás csak akkor esik vissza bilineárisra, ha a kicsinyítés enyhe és a tartalom lapos
uses
PDFium;
procedure ShrinkScannedPdf(const InputFile, OutputFile: string);
var
Pdf: TPdf;
Options: TPdfImageOptimizeOptions;
Report: TPdfImageOptimizeReport;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := InputFile;
// Alapértékek: TargetDpi 150, MinDpiRatio 1.5, PreserveBilevel True,
// MinDimension 8, pirfAdaptive, piccAuto, minőség 0.85, 64 MiB keret.
Options := TPdfImageOptimizeOptions.Default;
Options.TargetDpi := 150;
Options.MinDpiRatio := 1.5;
Options.ContentClass := piccAuto;
Options.PreferredQuality := 0.85;
if Pdf.OptimizeImages(Options, Report) and (Report.OptimizedCount > 0) then
Pdf.SaveAs(OutputFile);
finally
Pdf.Free;
end;
end;
Egy kép csak akkor kerül módosításra, amikor a vízszintes és függőleges elhelyezési DPI közül a nagyobb, osztva a TargetDpi-vel, eléri a MinDpiRatio-t. Ez az őr azért létezik, hogy egy 150 DPI-s céléra szánt 160 DPI-s fotó ne kódolódjon újra egy hat százalékos nyereségért, amely egy minőségi generációt emészt fel. A MinDimension alatt, bármelyik tengelyen — alapértelmezésben 8 — lévő képek ikonként vagy vonalként kihagyásra kerülnek
Miért kapnak az átlátszó logók fehér szegélyt?
Mert a teljesen átlátszó pixel alatti szín tetszőleges, és egy sima súlyozott átlag engedi, hogy szavazzon. Ha egy logót tervezőeszközből exportál, a láthatatlan margó gyakran fehér, fekete, vagy bármilyen színű, amilyen a vászon volt; az alfa-csatorna elrejti, és egy egyenes összegzés a mag lábnyoma felett azonnal visszakeveri a látható élbe. A PDFiumPas úgy kerüli el ezt, hogy a BGRA mintákat premultiplikált formában halmozza, és a premultiplikációt csak a célpixelnél vonja vissza
Konkrétan minden hozzájáruló minta channel * alpha * weight-et ad a színakkumulátorhoz, alpha * weight-et egy alfaakkumulátorhoz és weight-et a súlyösszeghez. A célszínt ezután az alfaakkumulátorral, nem a súlyösszeggel osztják, és ez a számításban lényeges lépés: a súlyösszeggel osztva a szín a láthatatlan pixelek felé húzódna, míg a felhalmozott alfával osztva rekonstruálja azt a színt, amelyen a látható minták ténylegesen megegyeztek. A célalfa külön mennyiség: 255 * AlphaSum / WeightSum. A nem alfa formátumok a szokásos módon a súlyösszeggel osztanak, egy FPDFBitmap_BGRx cél kitöltőbájtja konstans 255-öt kap, és minden csatorna 0 és 255 közé szorul, mielőtt tárolásra kerülne. Ez az alfa normálisan a képszótár soft mask bejegyzéséből származik (ISO 32000-1 §11.4), amelyet a PDFium már a BGRA pufferbe kompozitált, amelyet az átméretező kap
// A belső akkumulációs ciklus alakja, hozzájáruló forrásmintánként
if SrcFormat = FPDFBitmap_BGRA then
Alpha := PByte(PAnsiChar(Pixel) + 3)^ / 255
else
Alpha := 1;
for Channel := 0 to Min(BytesPerPixel, 3) - 1 do
Accumulated[Channel] := Accumulated[Channel] +
PByte(PAnsiChar(Pixel) + Channel)^ * Alpha * Weight;
AlphaSum := AlphaSum + Alpha * Weight;
WeightSum := WeightSum + Weight;
// ... és a célpixelnél a premultiplikáció visszavonása az alfaösszeg alapján
if SrcFormat = FPDFBitmap_BGRA then
begin
if Abs(AlphaSum) > 1E-12 then
ValueSum := Accumulated[Channel] / AlphaSum
else
ValueSum := 0;
end
else
ValueSum := Accumulated[Channel] / WeightSum;
Az 1 bites vonalgrafika távol tartása a szürke zónától
Bármely folytonos mag, amelyet bilevel szkennelésre alkalmaznak, szürkét ad, és a szürke pontosan az, amit egy fax-stílusú kép nem tartalmazhat. A PDFiumPas ezért alapból békén hagyja az 1 bites képeket: a PreserveBilevel True a TPdfImageOptimizeOptions.Default-ban, és az ilyen képek érintetlenül a SkippedCount-ba kerülnek. Állítsa False-ra, és a pirfBilevel útvonal veszi át a feladatot simító mag helyett. Végigjárja a minden célpixelt lefedő pontos forrástéglalapot, a luminanciát a 0.114, 0.587 és 0.299 súlyokkal átlagolja BGR memóriasorrendben, és az eredményt 127.5-nél simán 0-ra vagy 255-re küszöböli. Semmi köztes nem írható, így az élek élesek maradnak, és vékony vonások körül nem keletkezik szürke haló; egy BGRA forrás alfa-csatornája a szokásos módon átlagol, egy BGRx cél a konstans 255-öt kapja. Ha magára a pixeladatra van szüksége egy kisebb dokumentum helyett, a képek kinyerése PDF-dokumentumokból a külön út
Mi történik, amikor egy kép átlépi a munkamemória-keretet?
Pontosan úgy marad, ahogy volt, és megszámolásra kerül. A MaxWorkingBytes alapértéke 64 MiB, és kétszer érvényesül. Mielőtt a célbitkép létrejönne, a PDFiumPas elutasítja a képet, ha a szélesség szorozva a magassággal szorozva a pixelenkénti bájtokkal meghaladja a keretet. Miután a FPDFBitmap_CreateEx sikerrel jár, a valós stride szorozva a magassággal alapján újra ellenőriz, mert a soreltolás (padding) egy olyan foglalást a határ fölé tolhat, amelyet a naiv szorzat még átengedett. Bármelyik elutasítás megsemmisíti a célt, és semmit nem ad vissza. Legyen világos a degradáció, amelyet ez jelent: a keretet túllépő képet nem méretezik át alacsonyabb minőséggel, és nem bontják csempékre. Az eredeti a dokumentumban marad, a BudgetExceededCount és a SkippedCount egyaránt nő, és egy futás ezért jelenthet sikert, miközben a dokumentum csak részben optimalizált. Ez szándékos fail-safe viselkedés, de azt jelenti, hogy a jelentés nem opcionális olvasmány. Külön hibamód is létezik: azok a képek, amelyeknek a bitképét a PDFium egyáltalán nem tudja előállítani, például CMYK, JPX, JBIG2 vagy maszkolt források, FailedCount-ot növelnek, és szintén érintetlenek maradnak
procedure OptimizeBatch(const Files: array of string);
var
Pdf: TPdf;
Options: TPdfImageOptimizeOptions;
Report: TPdfImageOptimizeReport;
I: Integer;
begin
Options := TPdfImageOptimizeOptions.Default;
Options.PreserveBilevel := False; // a bilevel területi szavazás használata
Options.ContentClass := piccPhoto; // Lanczos kényszerítése fotóhalmazokhoz
Options.MaxWorkingBytes := 256 * 1024 * 1024; // tartalék hely nagy szkennelésekhez
Pdf := TPdf.Create(nil);
try
for I := Low(Files) to High(Files) do
begin
Pdf.FileName := Files[I];
if not Pdf.OptimizeImages(Options, Report) then
begin
WriteLn('optimize failed: ', Report.ErrorMessage);
Continue;
end;
if Report.BudgetExceededCount > 0 then
WriteLn(Files[I], ': ', Report.BudgetExceededCount,
' image(s) over budget and kept at full size');
if Report.FailedCount > 0 then
WriteLn(Files[I], ': ', Report.FailedCount,
' image(s) could not be decoded to a bitmap');
if Report.OptimizedCount > 0 then
Pdf.SaveAs(ChangeFileExt(Files[I], '.opt.pdf'));
end;
finally
Pdf.Free;
end;
end;
A jelentés elolvasása, mielőtt kiküldené a fájlt
A TPdfImageOptimizeReport diagnosztizálásra készült, nem pusztán naplózásra. A OptimizedCount, SkippedCount és FailedCount mellett magonként egy számlálót tár fel, így a BoxFilterCount, BilinearFilterCount, BicubicFilterCount, LanczosFilterCount és BilevelFilterCount megmondja, mit állapított meg valójában az adaptív szabály az Ön állományáról. Egy csupa-box eredmény azt jelenti, hogy a kicsinyítések meredekek voltak, vagy a tartalmat vonalgrafikának sorolták; egy csupa-Lanczos eredmény egy olyan dokumentumon, amelyről hitte, hogy vonalgrafika, annak jele, hogy a ContentClass-t explicit módon kell beállítani. Az AverageDetailScore az a szám, amelyet a 0.08-as Lanczos küszöbhöz kell hasonlítani a PreferredQuality hangolásakor, és a PeakWorkingBytes megmutatja, mennyi MaxWorkingBytes-ra volt valóban szüksége a futásnak. Az érvénytelen beállítások hangosan, nem csendben hibáznak: nem pozitív TargetDpi, 1 alatti MinDpiRatio, a 0 és 1 közé nem eső PreferredQuality vagy nem pozitív MaxWorkingBytes EPdfError-t dob, mielőtt bármelyik lapot érintené. És az OptimizeImages csak a memóriabeli dokumentumot szerkeszti; minden módosított lap a FPDFPage_GenerateContent hívással rögzítődik, ezt követően Önnek még magának kell a SaveAs-t meghívnia. Hogy szemrevételezze, mi változott, renderelje le az eredeti és az optimalizált dokumentumokat bitképekre a PDF-lapok JPEG képekké alakítása cikkben leírt módon, és hasonlítsa össze őket teljes nagyításban
Az adaptív átméretezés azon funkciók egyike, amely működés közben láthatatlan, és amikor nem működik, támogatási jegyeket generál; ezért kellett a mérésnek, az alfa-kezelésnek és a memóriakeretnek együtt megérkeznie, nem pedig három külön finomításként. Ha ezt Delphi, C++Builder vagy Lazarus termékhez vizsgálja, a teljes API-felület és a licenc részletei a PDFiumPas Delphi PDFium komponens oldalán találhatók