HotPDF THotPDF.CompressDocument yra vienas jungiklis, kuris priverčia BeginDoc pagaminti mažiausią be nuostolių PDF, kokį komponentas sugeba parašyti: FlateDecode maksimaliu lygiu, kryžminių nuorodų srautą su objektų srautais, šriftų poaibių sudarymą ir kompaktiškus šriftų poaibius, kurie paliktus glifus pernumeruoja už atviro /CIDToGIDMap. Tada EndDoc sugrąžina jūsų pačių nustatymus. Trys puslapiai Arial ir SimSun testinio dokumento nukrito nuo 10.2 MB iki 20 KB su identišku atvaizdavimu
Ką CompressDocument iš tikrųjų įjungia?
CompressDocument vienam dokumentui perrašo šešis rašytojo nustatymus, plus objektų srautų ribą, ir vėliau visus juos atkuria. BeginDoc metu, dar prieš apsispręsdama PDF versiją, HotPDF įsimena jūsų reikšmes ir nustato Compression į cmFlateDecode, CompressionLevel į clMaximum, įjungia EnableFontSubsetting ir CompactFontSubsetting bei paleidžia UseXRefStream ir UseObjectStreams (ISO 32000-1 §7.5.7 ir §7.5.8). Objektų srautams reikia PDF 1.5, tad senesnė Version pakeliama iki 1.5, kai ji neužrakinta. PDF/A-1 abu variantus draudžia, todėl PDF/A-1 dokumentas lieka prie klasikinės kryžminių nuorodų lentelės ir gauna tik Flate bei šriftų darbus. Vaizdai paliekami lygiai tokie, kokie buvo įdėti
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.AutoLaunch := False;
Pdf.FileName := 'invoice-2026-1042.pdf';
Pdf.CompressDocument := True; // pritaiko BeginDoc, atšaukia EndDoc
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 12);
Pdf.CurrentPage.TextOut(40, 40, 0, 'Invoice 2026-1042');
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Atkūrimas vyksta išoriniame EndDoc finally, todėl išimtis ataskaitos viduryje nepalieka ilgaamžio komponento įstrigusio prie maksimalaus glaudinimo kitam darbui. Pats CompressDocument atributas lieka True; sugrįžta tik šeši jo pasiskolinti nustatymai. Su versija elgiamasi atidžiau. HotPDF savo pakėlimą iki 1.5 atšaukia tik tada, kai dokumentas vis dar baigiasi ties 1.5, tad kai kitas bruožas paleidimo metu patraukė failą iki 1.6 (tarkim, įdėtas OpenType šriftas), aukštesnė versija lieka – lygiai taip, kaip būtų be glaudinimo
Kodėl šriftų poaibiai be kompaktiškumo vis tiek dideli?
Klasikinis TrueType poaibis meta kontūrus, kurių niekada nededate, bet palieka kiekvieną glifo ID ten, kur jis buvo, ir būtent tas numeravimas jį laiko storą. Turinio srautas rodo CID, lygius originaliems GID, tad poaibiui tenka laikyti loca poslinkį ir hmtx įrašą kiekvienai vietai iki aukščiausio palikto glifo – tuščiai ar ne. Lotyniškam šriftui ta antkaina yra triukšmas. CJK šriftui, tokiam kaip SimSun, kurio ideogramos sėdi gilioje didžiulėje glifo lentelėje, du kinų simboliai vežioja lenteles, pritaikytas visam šriftui. Šriftų poaibių uždarumo taisyklės formuotiems glifams nusprendžia, kurie glifai išgyvena; kompaktiškumas yra apie tai, kiek išgyvenėliams kainuoja
CompactFontSubsetting paliktus glifus pernumeruoja į tankų diapazoną, prasidedantį nuo nulio, ir ant CIDFont parašo /CIDToGIDMap srautą, kurį ISO 32000-1 §9.7.4.2 apibrėžia kaip dviejų baitų GID lentelę, indeksuotą CID. Ta lentelė yra visas triukas. Turinio srautai, /W pločių masyvas ir ToUnicode CMap visi laiko originalius CID, tad parašyto nieko keisti nereikia; tik paieška iš CID į glifą persikelia į žemėlapį. Teste, kuris iškėlė šį bruožą, SimSun su dviem simboliais sumažėjo nuo 24.8 KB šriftų duomenų iki 3.1 KB
Kompaktiškumas turi kietas ribas ir gedimo atveju tyliai suprastėja, o ne kelia klaidos. HotPDF kompaktiškus poaibius stato tik Type 0 TrueType šriftams – ir tiems, nustatytiems per SetFont su įjungtu poaibių sudarymu, ir šriftui, užregistruotam per RegisterUnicodeTTF. Paprastas TrueType šriftas savo glifus randa per cmap šriftų programos viduje, kurį pernumeravimas sulaužytų, tad jis lieka prie reto poaibio. OpenType-CFF šriftams kompaktiško kelio taip pat nėra. Kompaktiška statyba, kuri nepavyksta, grįžta prie reto poaibio vietoj klaidos kelio. Atributas pagal nutylėjimą išjungtas, tad egzistuojanti išvestis lieka baitas į baitą identiška, o po PDF/A užregistruotas Unicode šriftas visuomet gauna kompaktišką poaibį
Pdf.EnableFontSubsetting := True;
Pdf.CompactFontSubsetting := True; // naudinga ir be CompressDocument
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('SimSun', [], 12);
Pdf.CurrentPage.TextOut(40, 40, 0, WideString('Total: '#$4E2D#$6587));
Pdf.EndDoc;
Kaip supakuotas rašytojas suspaudžia failo struktūrą?
Kai šriftai ir srautai sumažėja, žodynai ir kryžminių nuorodų duomenys tampa didžiausia likusia išlaida, tad objektų srautų rašytojas, stovintis už CompressDocument, ir juos apkarpą. Objektų srautų ir prieaugio atnaujinimų vadove aprašytas pats konteinerio formatas; glaudinimo kelias ant viršaus prideda keturis patobulinimus:
- Kompaktiška sintaksė pagal ISO 32000-1 §7.2.2: tarpas rašomas tik tarp dviejų žetonų, kurie kitu atveju susilietų kaip paprasti simboliai, tad
/Type /Pagevirsta/Type/Page - Kryžminių nuorodų srauto laukai įgyja bet kokį §7.5.8.2 leistą plotį, tad iki 16 MB failas kiekvieną poslinkį saugo 3 baituose vietoj 4
- Iki 250 objektų telpa į kiekvieną objektų srautą vietoj įprastų 100, nebent savo ribą nustatote per
ConfigureAdaptiveObjectStreamPacking - Kai failas neužšifruotas, Catalog ir Info žodynas taip pat supakuojami į objektų srautus; užšifruota išvestis juos palieka viršutiniame lygyje
Kompaktiška sintaksė atsinešė spąstus, vertus žinoti, jei rašytoją pratęsiate. Pasirašymas užpildo parašą tada, kai failas jau parašytas, ieškodamas baituose literalų /ByteRange ( ir /Contents <, o kompaktiška rašyba tuos fragmentus paverstų /ByteRange( ir /Contents<, kurių paieška niekada nerastų. Parašų žodynai (Type Sig arba DocTimeStamp, FT Sig) ir šifravimo žodynas todėl lieka prie atstų rašybos. Susijęs defektas kamavo laidasy iki v2.766.41: kiekvienas objektų srautų išsaugojimas, CompressDocument įskaitant, prasidėdavo dviem %PDF- antraštės eilutėmis, tad atsinaujinkite, jei griežtas validatorius peikia jūsų išvestį
Ar galima suglaudinti jau pakrautą PDF?
Galima, per parinkčių perkrodą CompressLoadedDocument(Options, Info), kuris tuos pačius be nuostolių žingsnius atlieka egzistuojančiam failui. Su THPDFLoadedDocumentCompressionOptions.Default jis pašalina nenaudojamus puslapių išteklius, sulieja identiškus šriftus ir formas, sudaro įdėtų šriftų poaibius su kompaktiškais poaibiais, perspaudžia nefiltruotus, Flate, LZW, ASCII ir RunLength srautus Flate, kai rezultatas mažesnis, ir priverčia kitą išsaugojimą naudoti objektų srautus. HighRatioFlate pagal nutylėjimą išjungtas, o objektų srautai praleidžiami PDF/A-1 ir prieaugio išsaugojimams. Be parametrų likęs CompressLoadedDocument perkroda yra senesnis, siauresnis iškvietimas, kuris tik Flate suspaudžia nesuspaustus srautus
var
Doc: THotPDF;
Options: THPDFLoadedDocumentCompressionOptions;
Info: THPDFLoadedDocumentCompressionInfo;
begin
Doc := THotPDF.Create(nil);
try
Doc.AutoLaunch := False;
Doc.LoadFromFile('quarterly-report.pdf');
Options := THPDFLoadedDocumentCompressionOptions.Default;
Doc.CompressLoadedDocument(Options, Info);
if Info.RefusedBySignaturePolicy then
Writeln(Format('Left untouched: %d signature fields', [Info.SignatureCount]))
else
begin
Writeln(Format('Compact fonts: %d, stream bytes saved: %d',
[Info.Fonts.CompactSubsetFontCount, Info.BytesSaved]));
Doc.SaveLoadedDocument('quarterly-report-compact.pdf');
end;
finally
Doc.Free;
end;
end;
Pakrautame kelyje dvi ribos svarbios. Kiekvienas žingsnis perrašo baitus, kuriuos dengia parašas, tad dokumentas su parašo laukais atmetamas kaip visuma: iškvietimas grąžina 0, nustato RefusedBySignaturePolicy ir nieko nekeičia, nebent nustatote AllowSignatureInvalidation, po ko Info.SignaturesInvalidated pasako, ką atidavėte. Kompaktiškumas čia ir konservatyvesnis nei kūrimo kelyje. HotPDF glaudina tik šriftų programas, naudojamas vien tik CIDFontType2 šriftų su Identity /CIDToGIDMap, kur CID lygus GID, ir praleidžia programas su esamu žemėlapio srautu, /CIDSet ar spalvinėmis glifų lentelėmis, tokiomis kaip COLR, sbix, CBDT arba SVG, nes kompaktiška perstatyba numestų spalvų sluoksnius. Atkreipkite dėmesį ir tai, kad Info.BytesSaved sumuoja tik išteklių, šriftų ir srautų žingsnius; objektų srautų laimėjimas pasirodo, kai failas parašomas
Kokių rezultatų tikėtis praktikoje?
Laimėjimai seka tai, kiek failo yra nesuspaustos struktūros ir išpūstų šriftų duomenų, o ne tai, kiek jis turi puslapių. Trys puslapiai Arial ir SimSun pavyzdžio sumažėjo nuo 10.2 MB iki 20 KB, kai buvo gaminamas su CompressDocument, ir nuo 10.2 MB iki 19.8 KB, kai nesuspaustas originalas buvo pakrautas ir praleistas pro CompressLoadedDocument, abiem atvejais su identišku atvaizdavimu. PDF, kuris jau kompaktiškas, vos pajuda: regresijų rinkinyje tokie failai išsaugojo nuo -0.07% iki +0.06% savo originalaus dydžio. Nuotraukų pilni failai laimi mažai, nes nė vienas kelias vaizdų duomenų neliečia
Jei tuos pačius CJK ataskaitas gaminate kas naktį, kompaktiškus poaibius suporuokite su nuolatine šriftų poaibių atmintine diske, kad poaibių darbas nekartotųsi kiekvienam paleidimui, o suglaudintas išvestis lyginkite pagal objektų turinį, o ne pagal baitus, nes vienas pakeistas laukas priverčia visą objektų srautą vėl praeiti pro Flate. Pilnos atributų ir įrašų nuorodos yra HotPDF Delphi PDF komponento produkto puslapyje