losLab PDF Library lahko vgradi manjkajoče pisave v že naložen dokument PDF z enim samim klicem: EmbedMissingFonts pregleda vsak slovar pisav v dokumentu, poišče ustrezno nameščeno sistemsko pisavo po njenem imenu BaseFont in zapiše pisavo nazaj v datoteko. Za ekipe, ki popravljajo dokumente tretjih oseb, ki ne opravijo preverjanja PDF/A zaradi nevgrajenih pisav, je to rešitev, ki odpravi napako preverjanja 00030
Ta scenarij je zelo pogost. Cevovod za vnos arhivov prejme dokumente PDF od dobaviteljev, strank ali skenirnega urada; dokumenti se pravilno prikažejo na vsaki pisalni mizi v stavbi; nato pa validator PDF/A zavrne celotno serijo z enako napako, ki se ponovi enkrat za vsako datoteko: vsaj ena pisava ni vgrajena. Nihče v verigi ne bo znova ustvaril datotek, zato jih mora cevovod popraviti. Ta članek obravnava to pot popravila. Je spremljevalec članka o predhodnem preverjanju (preflight), ki obravnava odkrivanje kršitev PDF/A in PDF/UA: tisti prispevek vam pove, kateri dokumenti so poškodovani, ta pa odpravlja najpogostejši razlog za njihovo poškodbo
Zakaj PDF/A zahteva vgradnjo vseh pisav?
ISO 19005-1 §6.3.4 zahteva, da vsaka pisava, ki jo uporablja skladen dokument, vsebuje svoj program pisave znotraj datoteke, saj je celotna obljuba PDF/A ponovljivost: dokument se mora prikazati enako na računalniku čez petdeset let, ki nima nameščenih enakih pisav kot računalnik, na katerem je bil dokument ustvarjen. Nevgrajena pisava je navodilo bralniku, naj poišče Arial nekje v sistemu za ogledovanje, stališče standarda pa je, da "nekje v sistemu za ogledovanje" ni arhivsko zagotovilo. Ne glede na to, katere znake, metriko in pokritost ima nadomestna pisava, je to tisto, kar bralec dobi, in morda ni tisto, kar je avtor videl
Zgodovinski krivec je konvencija Standard 14. PDF 1.0 je obljubljal, da vsak bralnik vsebuje pisave Helvetica, Times, Courier, Symbol in ZapfDingbats, zato so se generatorji naučili sklicevati na te pisave po imenu in niso vgrajevali ničesar, in trideset let orodij še vedno dela natanko to. losLab PDF Library to zahtevo jemlje tako resno, da je v načinu ustvarjanja PDF/A klic AddStandardFont namerno brez učinka (no-op): knjižnica ne vsebuje programov pisav Standard 14, ne more vgraditi tistega, česar nima, in zavrača zapis nevgrajenega sklica v dokument, ki trdi, da je skladen. Vrne 0 brez izbire pisave, zato mora dokument PDF/A namesto tega uporabiti AddTrueTypeFont z vgradnjo, vsaka zahteva Embed=0 pa se tiho spremeni v Embed=1, ko je aktiven način PDF/A. To je stran zapisovalnika. Težji problem pa je stran bralnika: dokument, ki ga je že napisal nekdo drug, poln slovarjev pisav, ki jih niste ustvarili
Kako EmbedMissingFonts popravi naložen dokument?
losLab PDF Library popravi pisave na mestu (in-place), namesto da bi jih znova zgradila. Ko generator PDF zapiše nevgrajeno pisavo TrueType, je slovar FontDescriptor, ki ga ustvari, že popoln: FontName, FontBBox, Flags, Ascent, Descent, StemV so vsi prisotni. Edina stvar, ki ga loči od vgrajene pisave, je odsotnost enega vnosa, to je sklica na tok /FontFile2, ki vsebuje dejanski program pisave. Zato se EmbedMissingFonts ne dotika slovarja pisave, kodiranja, polja širin ali katerega koli toka vsebine, ki se sklicuje na pisavo po imenu vira. Prebere ustrezno pisavo iz sistema, jo stisne v nov objekt toka in FontDescriptorju, ki je že tam, doda en sam sklic /FontFile2 (ali /FontFile3 za pisave CIDFontType0). Vse, na kar kažejo strani dokumenta, ostane natanko tam, kjer je bilo, zato je ta operacija varna za izvajanje na datotekah, ki jih ne nadzorujete
Pokritost vključuje obe arhitekturi pisav, s katerimi se boste srečali v praksi: preproste pisave TrueType in sestavljene pisave Type0/CID, ki se uporabljajo za besedilo CJK in sodobne izhode Unicode. Pregledovanje namerno preišče vsak slovar Font v drevesu objektov dokumenta, namesto da bi se zanašalo na pregledovanje virov stran za stranjo, tako da se zaznajo tudi pisave, na katere se sklicujejo opombe ali pa so v skupni rabi med stranmi. API je en sam klic na naloženem dokumentu
var
PDF: TPDFlib;
Repaired: Integer;
begin
PDF := TPDFlib.Create;
try
if PDF.LoadFromFile('supplier-invoice.pdf', '') <> 1 then
raise Exception.Create('Could not load PDF');
// Walks every Font dictionary; returns how many fonts
// gained a font program. Fonts whose program cannot be
// found on the system are skipped, not failed.
Repaired := PDF.EmbedMissingFonts;
Writeln(Format('%d font program(s) embedded', [Repaired]));
PDF.SaveToFile('supplier-invoice-repaired.pdf');
finally
PDF.Free;
end;
end;
Ena podrobnost, ki jo je vredno vedeti, saj pojasnjuje, zakaj ujemanje imen deluje bolje kot preprosta primerjava nizov: knjižnica normalizira imena BaseFont, preden jih poišče. Predpone podnaborov (vzorec ABCDEF+ iz šestih velikih črk in znaka plus) se odstranijo, pripone v slogu PostScript, kot je ArialMT, se razrešijo v Arial, datoteke TrueType Collection pa se zaznajo in razpakirajo, tako da se pisava, ki se nahaja znotraj datoteke .ttc, še vedno pravilno vgradi
Preverjanje popravila s poročilom o predhodnem preverjanju
CreatePreflightReport je korak preverjanja, krog pa je namerno sklenjen: ista revizija, ki je obsodila datoteko, bi morala biti tista, ki jo odobri. Koda napake 00030 je ugotovitev globoke revizije PDF/A, ki se glasi "At least one font is not embedded (FontFile/FontFile2/FontFile3 missing)", in se poroča za datoteko som celoto, tako da jo ena sama spregledana pisava ohranja aktivno. Zaženite poročilo na izvorni datoteki, jo popravite, shranite in znova zaženite poročilo na izhodni datoteki
function HasFontEmbeddingViolation(PDF: TPDFlib;
const FileName: string): Boolean;
var
Report: string;
begin
// ComplianceTests = 1 selects the PDF/A checks
Report := PDF.CreatePreflightReport(FileName, '', 1, 0);
Result := Pos('00030', Report) > 0;
end;
Za pogled po posameznih pisavah namesto razsodbe za celotno datoteko znova naložite popravljeni dokument in izvedite naslednje: FindFonts, ki mu sledita SelectFont in GetFontIsEmbedded, poroča o stanju vgradnje za vsako pisavo posebej. To je pravo orodje, ko mora paketno opravilo zabeležiti, katere pisave v kateri datoteki ni bilo mogoče popraviti. Enak vzorec iskanja se pojavi v članku o ekstrakciji besedila, slik in pisav iz naloženih PDF-jev, kjer služi ekstrakciji namesto popravilu
Kaj se zgodi, če pisava ni nameščena v sistemu?
EmbedMissingFonts preskoči vsako pisavo, katere programa ne najde, in preskok sporoči prek svoje povratne vrednosti: če je štetje manjše od števila nevgrajenih pisav, ki ste jih našteli, razlika predstavlja pisave, ki jih sistem nima. To je pošten način neuspeha in je boljši od alternativ, saj bi ustvarjanje nadomestnega programa za pisavo, poimenovano v dokumentu, spremenilo prikazovanje, česar arhivsko popravilo ne sme nikoli storiti. Za te primere losLab PDF Library ponuja EmbedFontProgramFromFile, ki vgradi datoteko .ttf ali .otf, ki jo zagotovi klicatelj, v poimenovano pisavo, tako da lahko cevovod vsebuje pisave podjetja, za katere pričakuje, da jih bo srečal, in se nanje namerno zanese
var
I, FontID: Integer;
begin
PDF.FindFonts;
for I := 1 to PDF.FontCount do
begin
FontID := PDF.GetFontID(I);
if (FontID > 0) and (PDF.SelectFont(FontID) = 1) then
if PDF.GetFontIsEmbedded = 0 then
// Try the installed system font first, then fall back
// to a font file shipped alongside the application
if PDF.EmbedFontProgram(PDF.FontName) = 0 then
PDF.EmbedFontProgramFromFile(PDF.FontName,
'fonts\CorporateSans.ttf');
end;
end;
Dve omejitvi si zaslužita jasno navedbo. Prvič, pisave Type1 v trenutni implementaciji niso popravljene: njihov vnos /FontFile zahteva tristopenjsko strukturo PFB z eksplicitnimi ključi dolžine, knjižnica pa jih preskoči, namesto da bi zapisala napačno oblikovan tok; v sodobnih dokumentih so redki, vendar se pojavljajo v starih arhivih. Drugič, vgradnja pisave je licenčno dejanje. Pravice za vgradnjo pisave TrueType pripadajo njenemu lastniku, cevovod za popravilo, ki vgrajuje licenčne pisave v dokumente, ki zapuščajo organizacijo, pa mora potrditi, da licence za pisave to dejansko dovoljujejo. Knjižnica bo naredila to, kar zahtevate; ali smete to zahtevati, pa je vprašanje za vaš pravni oddelek, ne za vaš prevajalnik
Vgradnja je nujna, vendar ne zadostna
Popravilo pisav odpravi napako 00030 in nič drugega. Dokument, ki ne opravi preverjanja PDF/A zaradi šifriranja, manjkajočih metapodatkov XMP, barvnega prostora, odvisnega od naprave brez OutputIntent, ali manjkajočih tabel ToUnicode, bo še vedno neuspešen po tem, ko so vgrajene vse pisave. Zato popravilo spada v zanko, ki jo vodi predhodno preverjanje, in ga ne nadomešča. Zaženite celotno poročilo, popravite tisto, kar navaja, in pustite, da vam poročilo pove, kdaj ste končali. Obstaja tudi dimenzija velikosti: celoten program pisave CJK lahko obsega več megabajtov, zato lahko vgradnja več takih pisav močno poveča majhen dokument. Protivutež je podnabor (subsetting), ki je obravnavan v članku o optimizaciji velikosti datoteke PDF in podnaborih pisav, ki vsak vgrajeni program zmanjša le na tiste znake, ki se dejansko prikazujejo v dokumentu
Preprečevanje poslabšanja stanja pri novih dokumentih
SetEmbedAllFonts je preventiva za isto funkcijo: zaščita na strani zapisovalnika, ki preprečuje, da bi vaša lastna koda ustvarila dokumente, ki jih ta članek popravlja. Ko je aktivna funkcija SetEmbedAllFonts(1), se vsak naslednji klic AddTrueTypeFont, ki zahteva Embed=0, spremeni v vgrajen sklic, kar razširi jamstvo, ki ga način PDF/A že uveljavlja, na vsak dokument. Vpliva na pisave, dodane po klicu, ne na pisave, ki so že v naloženi datoteki, zato je delitev dela jasna: SetEmbedAllFonts za dokumente, ki jih ustvarite, EmbedMissingFonts za dokumente, ki jih podedujete
PDF.NewDocument;
PDF.SetEmbedAllFonts(1);
// From here on, AddTrueTypeFont(Name, 0) behaves
// like AddTrueTypeFont(Name, 1): no non-embedded
// reference can reach the output file
Obe polovici, zaščita na strani zapisovalnika in pot nalaganja-popravila-shranjevanja, sta del knjižnice losLab PDF Library za Delphi, C# in VB.NET, skupaj z mehanizmom za predhodno preverjanje, ki preveri rezultat; stran izdelka vsebuje celoten referenčni priročnik za API za pisave, vključno s klici za vgradnjo in podnabor pisav za posamezno pisavo