losLab PDF Library može ugraditi nedostajuće programe fontova već učitanog PDF-a jednim pozivom: EmbedMissingFonts prolazi kroz svaki rječnik fontova u dokumentu, pronalazi odgovarajući instalirani sustavni font prema njegovu BaseFont nazivu i zapisuje program fonta natrag u datoteku. Za timove koji popravljaju dokumente trećih strana koji ne prolaze PDF/A provjeru valjanosti pri ugradnji fonta, ovo je popravak koji uklanja preflight pogrešku 00030
Scenarij je deprimirajuće čest. Cjevovod za unos arhive prima PDF-ove od dobavljača, kupaca ili ureda za skeniranje; dokumenti se ispravno prikazuju na svakom stolu u zgradi; a zatim PDF/A validator odbacuje cijelu seriju s istom pritužbom koja se ponavlja jednom po datoteci: barem jedan font nije ugrađen. Nitko uzvodno neće ponovno generirati datoteke, pa ih cjevovod mora popraviti. Ovaj članak pokriva taj put popravka. On je pratitelj članka o preflightu, koji pokriva otkrivanje PDF/A i PDF/UA povreda: taj dio vam govori koji su dokumenti neispravni, a ovaj popravak rješava najčešći način na koji su oni neispravni
Zašto PDF/A zahtijeva da svaki font bude ugrađen?
ISO 19005-1 §6.3.4 zahtijeva da svaki font koji koristi sukladni dokument sadrži svoj program fonta unutar datoteke, jer je cijelo obećanje PDF/A-a ponovljivost: dokument se mora prikazati identično na računalu za pedeset godina koje ne dijeli nijedan font s računalom koje ga je proizvelo. Neugrađeni font je instrukcija da se Arial pronađe negdje na sustavu za prikazivanje, a stav standarda je da "negdje na sustavu za prikazivanje" nije arhivsko jamstvo. Kakve god glifove, metriku i pokrivenost zamjenski font imao, to je ono što čitatelj dobiva, a to možda nije ono što je autor vidio
Povijesni krivac je konvencija Standard 14. PDF 1.0 je obećao da svaki preglednik dolazi s fontovima Helvetica, Times, Courier, Symbol i ZapfDingbats, pa su generatori naučili referencirati te fontove po nazivu i ne ugrađivati ništa, a trideset godina alata i dalje radi upravo to. losLab PDF Library shvaća ovaj zahtjev dovoljno ozbiljno da je u načinu stvaranja PDF/A dokumenta AddStandardFont namjerno prazna operacija: knjižnica ne isporučuje programe fontova Standard 14, ne može ugraditi ono što nema i odbija zapisati neugrađenu referencu u dokument koji tvrdi da je sukladan. Vraća 0 bez odabira fonta, pa PDF/A dokument umjesto toga mora koristiti AddTrueTypeFont s ugradnjom, a svaki zahtjev za Embed=0 tiho se promovira u Embed=1 dok je aktivan PDF/A način rada. To je strana pisca. Teži problem je strana čitatelja: dokument koji je netko drugi već napisao, pun rječnika fontova koje niste vi stvorili
Kako EmbedMissingFonts popravlja učitani dokument?
losLab PDF Library popravlja fontove na licu mjesta radije nego da ih ponovno izgrađuje. Kada PDF generator zapiše neugrađeni TrueType font, rječnik FontDescriptor koji proizvodi već je potpun: FontName, FontBBox, Flags, Ascent, Descent, StemV, svi su prisutni. Jedina stvar koja ga dijeli od ugrađenog fonta je nedostatak jednog unosa, reference toka /FontFile2 koja sadrži stvarni program fonta. Stoga EmbedMissingFonts ne dira rječnik fonta, kodiranje, niz širina ili bilo koji tok sadržaja koji referencira font prema nazivu resursa. Čita odgovarajući program fonta iz sustava, komprimira ga u novi objekt toka i dodaje jednu referencu /FontFile2 (ili /FontFile3 za CIDFontType0 fontove) u FontDescriptor koji je već tamo. Sve na što stranice dokumenta upućuju ostaje točno tamo gdje je i bilo, što operaciju čini sigurnom za pokretanje na datotekama koje ne kontrolirate
Pokrivenost uključuje obje arhitekture fontova s kojima ćete se susresti u praksi: jednostavne TrueType fontove i kompozitne Type0/CID fontove, vrstu koja se proizvodi za CJK tekst i moderni Unicode izlaz. Prolazak namjerno nabraja svaki rječnik Font u stolu objekata dokumenta, umjesto da se oslanja na prolazak kroz resurse stranicu po stranicu, pa se preuzimaju i fontovi referencirani iz anotacija ili dijeljeni na više stranica. API je jedan poziv na učitanom 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;
Jedan detalj vrijedan pažnje, jer objašnjava zašto podudaranje naziva radi bolje od jednostavne usporedbe nizova znakova: knjižnica normalizira BaseFont nazive prije nego što ih potraži. Prefiksi podskupova (uzorak ABCDEF+ od šest velikih slova i znaka plus) se uklanjaju, sufiksi u PostScript stilu kao što je ArialMT razrešavaju se u Arial, a TrueType Collection datoteke se otkrivaju i raspakiraju tako da se pismo koje se nalazi unutar .ttc datoteke i dalje ispravno ugrađuje
Provjera popravka pomoću preflight izvješća
CreatePreflightReport je korak provjere, a petlja je namjerno zatvorena: ista revizija koja je osudila datoteku trebala bi biti ona koja je oslobađa. Kod pogreške 00030 je nalaz dubinske revizije PDF/A koji glasi "At least one font is not embedded (FontFile/FontFile2/FontFile3 missing)", a prijavljuje se za datoteku kao cjelinu, pa je samo jedan zanemareni font održava aktivnom. Pokrenite izvješće na izvornoj datoteci, popravite je, spremite i ponovno ga pokrenite na izlaznoj datoteci
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 prikaz po fontu umjesto presude po datoteci, ponovno učitajte popravljeni dokument i nabrojite: FindFonts nakon čega slijede SelectFont i GetFontIsEmbedded izvješćuje o statusu ugradnje font po font, što je pravi alat kada serijski posao treba zabilježiti točno koje pismo u kojoj datoteci nije moglo biti popravljeno. Isti obrazac nabrajanja pojavljuje se u članku o izdvajanju teksta, slika i fontova iz učitanih PDF-ova, gdje napaja izdvajanje umjesto popravka
Što se događa kada font nije instaliran na sustavu?
EmbedMissingFonts preskače svaki font čiji program ne može pronaći i prijavljuje preskakanje putem svoje povratne vrijednosti: ako se broj vrati manji od broja neugrađenih fontova koje ste izbrojali, razlika je u fontovima koje sustav nema. Ovo je pošten način neuspjeha i bolji je od alternativa, karena bi izmišljanje zamjenskog programa za font naveden u dokumentu promijenilo prikaz, što je upravo ono što arhivski popravak nikada ne smije učiniti. Za te slučajeve losLab PDF Library nudi EmbedFontProgramFromFile, koji ugrađuje .ttf ili .otf koji je isporučio pozivatelj u navedeni font, tako da cjevovod može isporučiti korporativne fontove koje očekuje susresti i namjerno se vratiti na njih
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;
Dvije granice zaslužuju biti jasno izrečene. Prvo, Type1 fontovi se ne popravljaju u trenutnoj implementaciji: njihov unos /FontFile zahtijeva trodijelnu PFB strukturu s eksplicitnim ključevima duljine, a knjižnica ih preskače umjesto da zapisuje neispravan tok; oni su rijetki u modernim dokumentima, ali se pojavljuju u starim arhivama. Drugo, ugradnja fonta je čin licenciranja. Dozvole za ugradnju TrueType fonta pripadaju njegovoj ljevaonici, a cjevovod za popravak koji ugurava licencirane programe fontova u dokumente koji napuštaju organizaciju trebao bi imati nekoga tko će potvrditi da licence za fontove to doista dopuštaju. Knjižnica će učiniti ono što tražite; smijete li to tražiti pitanje je za vaš pravni odjel, a ne za vaš prevoditelj
Ugradnja je nužna, ali ne i dovoljna
Popravak fontova uklanja samo pogrešku 00030 i ništa više. Dokument koji ne prolazi PDF/A zbog šifriranja, nedostatka XMP metapodataka, prostora boja ovisnog o uređaju bez OutputIntent-a ili nedostatka ToUnicode mapa i dalje neće proći nakon što se ugrade svi fontovi, zbog čega popravak pripada petlji vođenoj preflightom, a ne da je zamjenjuje. Pokrenite cijelo izvješće, popravite ono što imenuje i dopustite izvješću da vam kaže kada ste gotovi. Također postoji i dimenzija troška: puni CJK program fonta može težiti megabajtima, pa ugradnja nekoliko takvih može dramatično napuhati mali dokument. Protuteža je izdvajanje podskupa (subsetting), pokriveno u članku o optimizaciji veličine PDF datoteke i izdvajanju podskupova fontova, što skraćuje svaki ugrađeni program na glifove koje dokument stvarno prikazuje
Sprječavanje regresije novih dokumenata
SetEmbedAllFonts je preventivna polovica iste značajke: zaštita na strani pisca koja sprječava vaš vlastiti kod da proizvodi dokumente koje ovaj članak popravlja. Kada je aktivan SetEmbedAllFonts(1), svaki sljedeći poziv AddTrueTypeFont koji zahtijeva Embed=0 promovira se u ugrađenu referencu, što na svaki dokument proširuje jamstvo koje PDF/A način već provodi. To utječe na fontove dodane nakon poziva, a ne na fontove koji su već u učitanoj datoteci, pa je podjela rada čista: SetEmbedAllFonts za dokumente koje stvarate, a EmbedMissingFonts za dokumente koje nasljeđujete
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
Obje polovice, zaštita na strani pisca i staza učitaj-popravi-spremi, dio su losLab PDF Libraryja za Delphi, C# i VB.NET, zajedno s preflight mehanizmom koji provjerava rezultat; stranica proizvoda sadrži cjelovitu referencu API-ja za fontove uključujući pozive za ugradnju po fontu i izdvajanje podskupa