PDFiumPas, Delphi ir C++Builder skirta Google PDFium variklio apvalkalas, leidžia išsaugoti dokumentą tikslia PDF versija nuo 1.3 iki 1.7, naudodamas TPdf.SaveAs metodo parametrą PdfVersion. Paties PDFium iškvietimas FPDF_SaveWithVersion tik perrašo antraštę %PDF-M.m, nepatikrindamas, ar tikrasis dokumento turinys toje versijoje teisėtas. PDFiumPas panaikina šią spragą po išsaugojimo atliekama atitikties patikra, kuri pereina per aktyvią kryžminės nuorodos redakcijų grandinę ir prieš failui paliekant metodą patikrina Adobe Extension Level deklaracijas
Šis skirtumas svarbiausias spaudos gamyboje, kur PDF/X profilis nurodo tikslią PDF versiją, o paruošimo spaudai tikrintuvas arba RIP atmeta viską, kas tyliai nesutampa su antrašte — išvesties pusėje šis scenarijus aprašytas paruoštų spaudai PDF/X dokumentų tikrinime su PDFiumPas. SaveAs pateikia tikslą kaip TPdfVersion enumą — nuo pv13 iki pv17 kartu su senesnėmis pv10–pv12 reikšmėmis, taip pat su nepriklausoma TSaveOption parinktimi inkrementiniam arba visiškam perrašymui. Perduodamas PdfVersion, PDFiumPas vienu iškvietimu atlieka du darbus: paprašo PDFium įrašyti prašomą antraštę, tada iš naujo perskaito ką tik įrašytus baitus ir atsisako grąžinti failą, kurio aktyvus turinys negali teisėtai egzistuoti toje versijoje
var
Pdf: TPdf;
begin
Pdf:= TPdf.Create(nil);
try
Pdf.FileName:= 'source.pdf';
Pdf.Active:= True;
try
Pdf.SaveAs('press-ready.pdf', saNoIncremental, pv17);
except
on E: Exception do
// E.Message names the offending feature and the version or
// extension level it actually needs, for example:
// "RichMedia annotations and RichMediaExecute actions require
// /Extensions /ADBE with /BaseVersion /1.7 and /ExtensionLevel 3
// or newer."
raise;
end;
finally
Pdf.Free;
end;
end;
Kodėl paskutinis objekto apibrėžimas faile nėra tas, kuriuo verta pasitikėti?
Paskutinis fiziškai faile esantis objekto su konkrečiu numeriu egzempliorius nebūtinai yra tas, kurį šiandien išspręstų atitiktį užtikrinantis skaitytuvas. PDF, perėjęs kelis inkrementinius atnaujinimus, turi ne vieną objektų grafą, o jų istoriją, sluoksniais sudėtą į vieną failą, ir kiekvienas pridėjimo ciklas gali atlaisvinti objektą, iš naujo jį apibrėžti nauju generacijos numeriu arba palikti seną fizinį kūną tarp dviejų endobj žymeklių, nebeturintį į jį nukreipiančio kryžminės nuorodos įrašo
PDFiumPas tiksliai susidūrė su tokiu gedimo režimu, kol aiškiai nesekė xref redakcijų: vėlesnio puslapio objekto perrašymo našlaičiu palikta Redact anotacija arba fiziškai likęs /MarkInfo žodynas, į kurį neberodė joks xref įrašas, vis dar galėjo būti aptikti nuskaitant baitus ir suaktyvinti versijos funkcijos patikrą, kuri dokumentui, kurį iš tikrųjų atvertų skaitytuvas, jau nebegaliojo. Rezultatas buvo klaidingas atmetimas, o ne klaidingas priėmimas: failas, kurio dabartinė redakcija iš tikrųjų nebeturėjo funkcijos, vis dar galėjo būti sustabdytas bandant jį išsaugoti žemesnei versijai dėl turinio, kurio niekas nebegalėjo pasiekti
Kaip PDFiumPas nustato, kurie objektų apibrėžimai iš tikrųjų yra aktyvūs?
PDFiumPas išsprendžia aktyvų objektų rinkinį taip pat kaip atitiktį užtikrinantis skaitytuvas, eidamas kryžminių nuorodų grandine, o ne ieškodamas baitų srautuose objektų antraščių. Sprendiklis pradeda nuo paskutinio faile esančio startxref poslinkio ir seka kiekvieną /Prev nuorodą atgal per senesnes redakcijas, pakeliui analizuodamas klasikines kryžminių nuorodų lenteles, hibridinius su /XRefStm susietus srautus ir grynuosius kryžminių nuorodų srautus. Kelias eina nuo naujausio prie seniausio ir kiekvieną objekto numerį galutinai nustato pirmą kartą jį pamatęs, todėl vėlesnės redakcijos laisvas įrašas teisingai užgožia ankstesnėje parašytą objekto kūną, o naujas apibrėžimas su kitu poslinkiu ar generacija visada laimi prieš tai, ką pakeitė
Objektų srauto nariams reikia papildomos patikros, kurios vien tik poslinkio paieška negali suteikti, o šis mechanizmas išsamiau aprašytas objektų ir kryžminių nuorodų srautų tikrinime su PDFiumPas. Suspaustam objektui, atkurtam iš /ObjStm, būtina patvirtinti, kad jo pirminis srautas toje pačioje eigoje yra aktyvus, o jo indeksas turi sutapti su paties nario vieta to srauto antraštėje, tik tada PDFiumPas laiko jį gyvu turiniu. ISO 32000-1 7.5.8.4 netgi aprašo hibridinių nuorodų atvejį, kai klasikinė suderinamumo lentelė pažymi objektą kaip laisvą, o priekabos /XRefStm įrašas tuo pat metu kitur apibrėžia tą patį objektą kaip suspaustą narį; PDFiumPas sujungia papildomą xref srautą į tą pačią redakciją prieš pritaikydamas klasikinius įrašus, todėl suspaustas apibrėžimas laimi taip, kaip numato specifikacija
Adobe Extension Level: vartai virš versijos numerio
%PDF-1.7 antraštė žada tik tą funkcijų rinkinį, kurį 2008 m. standartizavo ISO 32000-1, o kelios šiandien naudojamos galimybės buvo išleistos vėliau kaip tik Adobe papildiniai, uždėti ant to paties versijos numerio. Adobe kiekvieną papildinį užregistravo kaip BaseVersion ir ExtensionLevel porą, įrašomą dokumento katalogo /Extensions žodyne pagal kūrėjo prefiksą, o pačių Adobe plėtinių atveju — ADBE, kad skaitytuvas galėtų atskirti paprastą PDF 1.7 failą nuo tokio, kuris taip pat įgyvendina sunumeruotą plėtinio lygį. Išsaugoti naudojant pv17 be šios deklaracijos savaime nėra klaida; ji atsiranda tik tada, kai aktyvus turinys iš tikrųjų priklauso nuo funkcijos, kurią ši deklaracija turi apimti
Kokios aukštesnės versijos funkcijos suaktyvina aiškios versijos vartus?
PDFiumPas tikrina konkretų specifikacija pagrįstą sąrašą, o ne vien spėja pagal versijos numerį. Vaizdų žodynams su aiškiu /SMaskInData įrašu arba /BitsPerComponent reikšme 16 reikia PDF 1.5, o šešiolikos bitų atvejis tiesiogiai remiasi PDF Reference 1.5 4.8 skyriaus vaizdo komponentų taisyklėmis. RichMedia anotacijoms ir RichMediaExecute veiksmams reikia /BaseVersion /1.7 su /ExtensionLevel 3 arba naujesniu lygiu. PRC 3D srautams, atpažįstamiems pagal žodyną, kuriame yra ir /Type /3D, ir /Subtype /PRC, reikia tos pačios bazinės versijos, bet tik /ExtensionLevel 1. Geografinio turinio Measure žodynams ir Projection anotacijoms reikia /BaseVersion /1.7 su /ExtensionLevel 3, to paties Adobe papildinio, nuo kurio priklauso RichMedia
Geografinio turinio patikroje yra specifikacijos skaitymo detalė, kurią verta žinoti kuriant savo versijos vartais pagrįstą logiką ant PDFiumPas. ISO 32000-1 254 lentelėje Measure žodyno /Type įrašas pažymėtas kaip pasirinktinis ir nurodoma tik "if present, shall be Measure", o 311 lentelėje /Type yra privalomas 3D srauto žodyne, kuriame laikomas PRC turinys. Tikro pasaulio GeoPDF išvestis iš žemėlapių įrankių dažnai praleidžia /Type Measure žodyne ir įrašo tik /Subtype /GEO, todėl PDFiumPas geografinio turinio detektorius tikrina vien /Subtype, o ne reikalauja abiejų raktų, kaip saugiai gali daryti PRC 3D detektorius. Reikalaujant /Type abiejuose žodynuose, atitiktį užtikrinantis GeoPDF turinys būtų galėjęs nepastebėtas praslysti pro vartus ir atsidurti paprastame PDF 1.7 faile be jį pagrindžiančios plėtinio lygio deklaracijos
Ar PDFiumPas automatiškai sumažina nepalaikomų funkcijų versiją?
Ne kaip bendrąją galimybę, ir būtent tai klaida, kurios reikia vengti. SaveAs perduoda tikslinę versiją vidinei procedūrai ValidatePdfVersionCompliance, o kai ši procedūra aptinka funkciją, kurios tikslinė versija arba jos plėtinio lygio deklaracija nepalaiko, SaveAs iškelia išimtį su procedūros klaidos tekstu, užuot įrašęs failą; iškvietėjas gauna tikslų, funkciją įvardijantį paaiškinimą, o ne tyliai perrašytą dokumentą. Vienintelė vieta, kur PDFiumPas automatiškai perrašo turinį, yra PDF 1.3 taikinys, kuriam jis pašalina semantiškai neutralius /BM /Normal, /CA 1 ir /ca 1 skaidrumo numatytuosius įrašus, kuriuos PDFium visada įrašo į ExtGState žodynus nepaisydamas tikslinės versijos, nes šios konkrečios reikšmės neturi vaizdinės prasmės, o PDF 1.3 atsirado anksčiau už šiuos raktus
// PDF 1.3 targets rewrite the saved bytes to strip transparency
// defaults PDFium always emits, so incremental mode cannot apply
Pdf.SaveAs('legacy-archive.pdf', saIncremental, pv13);
// raises: PDF 1.3 normalization is incompatible with incremental
// save mode
Nenumatytas skaidrumas ir vaizdų minkštosios kaukės vis tiek iškart atmetamos, kai taikinys yra PDF 1.3, nes jų pašalinimas pakeistų puslapio išvaizdą, o PDFiumPas šio sprendimo už jus nepriims. Prieš įtraukiant tikslią versiją į paketinį konvejerį verta suplanuoti dar du susijusius apribojimus. Aiškios versijos išvestis niekada neturi /Encrypt žodyno; jei šaltinis apsaugotas, išsaugojimas iškart nutraukiamas, o tai sutampa su PDF/X ir PDF/A profiliais, kurie draudžia šifravimą, tačiau reiškia, kad dešifravimas jūsų darbo eigoje yra atskiras žingsnis, o ne tai, ką už jus atlieka SaveAs. PDFiumPas taip pat neturi viešo metodo, skirto į katalogą įrašyti /Extensions /ADBE deklaraciją, todėl šaltinio faile esantis RichMedia, PRC 3D ar geografinis turinys, kuriam trūksta šios deklaracijos, nepraeis vartų, nesvarbu, kokios PdfVersion prašysite; deklaracija jau turi būti šaltinyje, paprastai ją įrašo kūrimo įrankis, arba funkciją reikia pašalinti prieš išsaugojant. Prieš mėginant išsaugoti tikslia versija verta patikrinti tik skaitymui skirtą TPdf.PdfVersion savybę, nes ji išsprendžia tą pačią katalogą atsižvelgiančią efektyvią versiją — antraštę arba /Version perrašą, atsižvelgiant į tai, kuris galioja — kuria remiasi ir išsaugojimo metu atliekamas tikrintuvas
Pdf.FileName:= 'incoming.pdf';
Pdf.Active:= True;
// PdfVersion resolves the same catalog-aware effective version the
// save-time validator uses, so a mismatch here is worth investigating
// before spending a full SaveAs attempt on it
LogSourceVersion('incoming.pdf', Pdf.PdfVersion);
SaveAs išimtį, kai taikoma tiksli versija, laikykite parengiamąja ataskaita, o ne klaida: pranešime įvardijama tiksli sąlyga, kurią šaltinio dokumentas pažeidžia, ir būtent tokios informacijos reikia spaudos cechui ar archyvo konvejeriui, kol failas keliauja toliau. Čia aprašytas aiškios versijos išsaugojimo kelias, aktyvios xref redakcijos sprendiklis ir Adobe Extension Level patikros yra standartinio PDFiumPas Component skirto Delphi ir C++Builder dalis; produkto puslapyje pateikta visa TPdf.SaveAs nuoroda kartu su likusia atitikties ir formų API