Dve pritožbi prispetata v tednu, ko funkcija stiskanja pride ven: optično prebrana pogodba ima zdaj stopničaste, kosmate črkovne oblike, prosojni logotip na naslovni strani pa sedi znotraj bledega hala. PDFiumPas odgovori na obe na enem mestu. TPdf.OptimizeImages izmeri vsako sliko, preden jo pomanjša, nato izbere jedro prevzorčanja in nabira barvo v obliki, zavedajoči se alfe
To ni bilo vedno res. Pred v3.100.0 je ista metoda zmanjšala vsako sliko, ki ni bila dvolična, s fiksnim korakom najbližjega soseda, kar je točno algoritem, ki da obe pritožbi: točkovno vzorči en izvorni piksel na izhodni piksel, RGB pod popolnoma prosojnim pikselom pa obravnava, kot bi ga bralec kdaj videl. Prepisava v v3.100.0 zamenja to eno samo pot s petimi jedri, izmerjenim pravilom izbire in izrecnim proračunom delovnega pomnilnika
Zakaj zmanjšanje vzorčenja naredi prebrano besedilo raztrgano?
Ker točkovno vzorčenje odgovori na napačno vprašanje. Ko se optični prebris 300 DPI preusmeri na 150 DPI, vsak izhodni piksel pomeni blok dva-krat-dva izvornih pikslov, najbližji sosed pa ohrani enega od štirih in ostale zavrže. Kateri preživi, je odvisno od zaokroževanja, zato roba poteze, ki je bila v viru gladko protizobčena, postane met kovanca na piksel. Rezultat je klasično stopničenje z zubčki vzdolž robov glifov, plus moire na poltonskih območjih, kjer so zavrženi vzorci slučajno nosili vzorec. To je pomembnejše v PDF kot na zaslonu, ker je škoda trajna. Slikovni XObject nosi svoje podatke vzorcev poleg /Width, /Height in /BitsPerComponent (ISO 32000-1 §8.9.5), prevzorčanje pa prepise vse tri znotraj datoteke. Slaba povečava v pregledovalniku je sličica, ki jo lahko znova narišete, PDFiumPas pa ima za to ločeno mehaniko v predpomnilniku izrisovanja in zmogljivosti povečave. Slabo zmanjšanje vzorčenja je nov dokument, ki ga podate stranki
Kako PDFiumPas meri podrobnost in izbere jedro
PDFiumPas se odloča na sliko, ne na dokument. Pred izbiro jedra izračuna normalizirano oceno podrobnosti svetlosti iz omejene mreže vzorčenja: vodoravni in navpični koraka sta (Width + 63) div 64 in (Height + 63) div 64, tako da optični prebris s 12000 piksli in sličica s 300 piksli stanejo približno isto 64-krat-64 prebiranje. Na vsakem vzorčenem položaju sešteje absolutno razliko do soseda na desno in soseda spodaj, čez do tri kanale, nato deli s številom vzorcev krat 255. Ocena pristane med 0 in 1, kjer ravna poslovna grafika sedi blizu ničle in gosta fotografska tekstura pleza
Izborna lestvica nato teče v fiksnem vrstnem redu. Če je ResampleFilter karkoli drugega kot pirfAdaptive, se ta filter uporabi dobesedno. Sicer: 1-bitna vsebina dobi pirfBilevel; ContentClass piccLineArt dobi pirfBox; faktor merila 4 ali več dobi tudi pirfBox, ker je pri takem zmanjšanju povprečje po območju tako najcenejši kot najbolj pravilen odgovor; piccPhoto, ocena podrobnosti 0,08 ali več ali PreferredQuality 0,9 ali več dobi pirfLanczos s svojim jedrom s tremi režnji; merilo 2 ali več ali kakovost 0,7 ali več dobi pirfBicubic pri polmeru 2; vse, kar ostane, dobi pirfBilinear. Ker TPdfImageOptimizeOptions.Default nastavi PreferredQuality na 0,85, privzet zagon nikoli ne pade nazaj na dvolinearno, razen če je zmanjšanje blago in vsebina ravna
uses
PDFium;
procedure ShrinkScannedPdf(const InputFile, OutputFile: string);
var
Pdf: TPdf;
Options: TPdfImageOptimizeOptions;
Report: TPdfImageOptimizeReport;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := InputFile;
// Privzetke: TargetDpi 150, MinDpiRatio 1.5, PreserveBilevel True,
// MinDimension 8, pirfAdaptive, piccAuto, kakovost 0.85, proračun 64 MiB.
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;
Slika se dotakne šele, ko večji od njenih vodoravne in navpične umestitvene DPI, deljen z TargetDpi, doseže MinDpiRatio. Ta čuvaj obstaja, zato foto 160 DPI, namenjen cilju 150 DPI, ni ponovno kodiran za šestodstotni dobiček, ki stane eno generacijo kakovosti. Slike pod MinDimension na kateri koli osi, privzeto 8, se preskočijo kot ikone ali pravila
Zakaj prosojni logotipi poberejo belo obrobo?
Ker je barva pod popolnoma prosojnim pikselom poljubna, in preprosto uteženo povprečje mu da glas. Izvozite logotip iz oblikovalskega orodja in nevidni rob je pogosto bel, ali črn, ali kar kanvas je bil; kanal alfa ga skrije, ravna vsota po odtisu jedra pa ga takoj zmeša nazaj v vidni rob. PDFiumPas se temu izogne z nabiranjem vzorcev BGRA v predpomnoženi obliki in razveljavitvijo predpomnoženja šele pri izhodnem pikslu
Konkretno vsak prispevajoč vzorec doda channel * alpha * weight akumulatorju barve, alpha * weight akumulatorju alfe in weight vsoti uteži. Ciljno barvo nato deli akumulator alfe in ne vsota uteži, in to je korak, ki šteje: deljenje z vsoto uteži bi vleklo barvo proti nevidnim pikslom, deljenje z nabrano alfo pa obnovi barvo, na kateri so se vidni vzorci dejansko strinjali. Ciljna alfa je ločena količina, 255 * AlphaSum / WeightSum. Formati brez alfe delijo z vsoto uteži kot običajno, polnilni bajt cilja FPDFBitmap_BGRx se zapiše kot konstanta 255, vsak kanal pa se pripne na 0 do 255, preden se shrani. Ta alfa običajno izhaja iz vnosa mehke maske v slovarju slike (ISO 32000-1 §11.4), ki jo je PDFium že sestavil v medpomnilnik BGRA, ki ga prejemnik prevzorčanja dobi
// Oblika notranje zanke nabiranja, na prispevajoč izvorni vzorec
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;
// ... in pri izhodnem pikslu, razpredpomnoži proti vsoti alfe
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;
Ohranjanje 1-bitne linijske grafike izven sive cone
Vsako zvezno jedro, uporabljeno na dvoličnem prebrisu, da sivo, sivo pa je točno tisto, kar faks-sliki ni dovoljeno vsebovati. PDFiumPas zato pusti 1-bitne slike pri miru privzeto: PreserveBilevel je True v TPdfImageOptimizeOptions.Default, take slike pa pristanejo v SkippedCount nedotaknjene. Nastavite na False in pot pirfBilevel prevzame namesto gladilnega jedra. Ta prehodi točen izvorni pravokotnik, ki pokriva vsak izhodni piksel, povprečuje svetlost z utežmi 0,114, 0,587 in 0,299 v pomnilniškem vrstnem redu BGR in praguje rezultat pri 127,5 v raven 0 ali 255. Nič vmesnega se ne sme zapisati, zato robovi ostanejo ostri in okoli tankih potez ne nastane siv hala; kanal alfe vira BGRA se povprečuje običajno, cilj BGRx pa dobi konstanto 255. Če potrebujete podlegle piksle in ne manjšega dokumenta, je izvlečenje slik iz dokumentov PDF ločena pot
Kaj se zgodi, ko slika preseže proračun delovnega pomnilnika?
Pusti se točno takšna, kot je bila, in se prešteje. MaxWorkingBytes je privzeto 64 MiB in se uveljavi dvakrat. Preden se ustvari ciljna slika (bitmap), PDFiumPas zavrne sliko, če širina krat višina krat bajti na piksel preseže proračun. Potem ko FPDFBitmap_CreateEx uspe, preveri znova z realnim korakom vrste krat višino, ker lahko polnjenje vrst potisne dodelitev čez mejo, ki jo je naiven produkt opravil. Vsaka zavrnitev uniči cilj in ne vrne ničesar. Bodite jasni glede degradacije, ki jo to pomeni: slika čez proračun se ne prevzorči z nižjo kakovostjo in se ne razdeli na ploščice. Izvirnik ostane v dokumentu, BudgetExceededCount in SkippedCount oba narasteta, zato lahko zagon poroča uspeh, medtem ko je dokument le delno optimiziran. To je namerno varno odpovedoče vedenje, a pomeni, da poročilo ni izbirno branje. Ločen način spodletelosti prav tako obstaja: slike, katerih slike (bitmap) PDFium sploh ne more dati, kot so CMYK, JPX, JBIG2 ali maskirani viri, povečajo FailedCount in so prav tako puščene nedotaknjene
procedure OptimizeBatch(const Files: array of string);
var
Pdf: TPdf;
Options: TPdfImageOptimizeOptions;
Report: TPdfImageOptimizeReport;
I: Integer;
begin
Options := TPdfImageOptimizeOptions.Default;
Options.PreserveBilevel := False; // uporabi glasovanje po območju dvolično
Options.ContentClass := piccPhoto; // vsili Lanczos za fotografske nabore
Options.MaxWorkingBytes := 256 * 1024 * 1024; // rezerva za velike prebrise
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;
Branje poročila, preden odprete datoteko
TPdfImageOptimizeReport je zgrajen, da se diagnosticira, ne da bi se le zabeležil. Poleg OptimizedCount, SkippedCount in FailedCount izpostavi en števec na jedro, tako da BoxFilterCount, BilinearFilterCount, BicubicFilterCount, LanczosFilterCount in BilevelFilterCount povedo, česa se je pravilo prilagajanja dejansko sklenilo o vašem korpusu. Rezultat vse-box pomeni, da so bila zmanjšanja strma ali da je bila vsebina razvrščena kot linijska grafika; rezultat vse-Lanczos na dokumentu, za katerega ste verovali, da je linijska grafika, je znak, da ContentClass nastavite izrecno. AverageDetailScore je številka za primerjavo s pragom Lanczos 0,08 pri uglaševanju PreferredQuality, PeakWorkingBytes pa pokaže, koliko MaxWorkingBytes je zagon res potreboval. Neveljavne možnosti odpovedo glasno in ne tiho: nepozitiven TargetDpi, MinDpiRatio pod 1, PreferredQuality izven 0 do 1 ali nepozitiven MaxWorkingBytes sproži EPdfError, preden se katera koli stran dotakne. In OptimizeImages ureja le dokument v pomnilniku; vsaka spremenjena stran se uveljavi z FPDFPage_GenerateContent, po čemer še vedno sami pokličete SaveAs. Da si z očmi ogledate, kaj se je spremenilo, izrišite dokumente pred in po na slike (bitmape) kot opisano v pretvorbi strani PDF v slike JPEG in jih primerjajte pri polni povečavi
Prilagodljivo prevzorčanje je ena od tistih funkcij, ki so nevidne, ko deluje, in ustvarjajo prijave podpore, ko ne deluje, zato je morala meritev, obravnava alfe in proračun pomnilnika pristati skupaj in ne kot tri ločene izboljšave. Če to ocenjujete za izdelek Delphi, C++Builder ali Lazarus, sta celotna površina API in podrobnosti licenciranja na strani komponente PDFiumPas Delphi PDFium