losLab PDF Library kan bygge inn de manglende fontprogrammene i en allerede lastet PDF med et enkelt kall: EmbedMissingFonts går gjennom hver fontordbok i dokumentet, finner den matchende installerte systemfonten ved hjelp av dens BaseFont-navn, og skriver fontprogrammet tilbake til filen. For team som reparerer tredjeparts dokumenter som feiler PDF/A-validering på fontinnbygging, er dette fiksen som får preflight-feil 00030 til å forsvinne
Scenarioet er deprimerende vanlig. En arkiveringspipeline mottar PDF-er fra leverandører, kunder eller et skannebyrå; dokumentene vises fint på hvert skrivebord i bygget; og deretter avviser PDF/A-valideringen hele batchen med samme klage gjentatt én gang per fil: minst én font er ikke innebygd. Ingen oppstrøms vil generere filene på nytt, så pipelinen må reparere dem. Denne artikkelen dekker den reparasjonsbanen. Den er ledsageren til preflight-artikkelen, som dekker detektering av PDF/A- og PDF/UA-brudd: den delen forteller deg hvilke dokumenter som er ødelagte, denne fikser den vanligste måten de er ødelagte på
Hvorfor krever PDF/A at alle fonter er innebygd?
ISO 19005-1 §6.3.4 krever at hver font som brukes av et samsvarende dokument har sitt fontprogram inne i filen, fordi hele løftet til PDF/A er reproduserbarhet: dokumentet må vises identisk på en maskin om femti år som ikke deler noen fonter med maskinen som produserte det. En ikke-innebygd font er en instruksjon om å finne Arial et eller annet sted på visningssystemet, og standardens holdning er at "et eller annet sted på visningssystemet" ikke er en arkiveringsgaranti. Uansett hvilke glyfer, metrikker og dekning erstatningsfonten har, er det det leseren får, og det er kanskje ikke det forfatteren så
Den historiske synderen er Standard 14-konvensjonen. PDF 1.0 lovet at alle visningsprogrammer leveres med Helvetica, Times, Courier, Symbol og ZapfDingbats, så generatorer lærte å referere til disse fontene ved navn og ikke bygge inn noe, og tretti år med verktøy gjør fortsatt akkurat det. losLab PDF Library tar kravet så alvorlig at i PDF/A-opprettelsesmodus er AddStandardFont bevisst en no-op: biblioteket sender ikke med Standard 14-fontprogrammene, kan ikke bygge inn det det ikke har, og nekter å skrive en ikke-innebygd referanse inn i et dokument som hevder samsvar. Det returnerer 0 uten å velge en font, så et PDF/A-dokument må bruke AddTrueTypeFont med innbygging i stedet, og enhver Embed=0-forespørsel blir lydløst fremmet til Embed=1 mens PDF/A-modus er aktiv. Det er skriversiden. Det vanskeligere problemet er lesersiden: et dokument noen andre allerede har skrevet, fullt av fontordbøker du ikke har opprettet
Hvordan reparerer EmbedMissingFonts et lastet dokument?
losLab PDF Library reparerer fonter på stedet i stedet for å bygge dem opp på nytt. Når en PDF-generator skriver en ikke-innebygd TrueType-font, er FontDescriptor-ordboken den produserer allerede komplett: FontName, FontBBox, Flags, Ascent, Descent, StemV, alt er til stede. Det eneste som skiller den fra en innebygd font er fraværet av én oppføring, /FontFile2-strømreferansen som inneholder selve fontprogrammet. Så EmbedMissingFonts berører ikke fontordboken, kodingen, breddetabellen eller noen innholdsstrøm som refererer til fonten med resursnavn. Det leser det matchende fontprogrammet fra systemet, komprimerer det til et nytt strømobjekt, og legger til en enkelt /FontFile2-referanse (eller /FontFile3 for CIDFontType0-fonter) til FontDescriptor som allerede er der. Alt dokumentets sider peker på forblir akkurat der det var, noe som gjør operasjonen trygg å kjøre på filer du ikke kontrollerer
Dekningen inkluderer begge fontarkitekturene du vil møte i praksis: enkle TrueType-fonter og sammensatte Type0/CID-fonter, typen som produseres for CJK-tekst og moderne Unicode-utdata. Gjennomgangen teller bevisst opp hver Font-ordbok i dokumentets objekt-tre i stedet for å stole på en side-for-side-ressursgjennomgang, slik at fonter referert til fra merknader eller delt på tvers av sider også blir fanget opp. API-et er et enkelt kall på det lastede dokumentet
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;
En detalj det er verdt å vite fordi den forklarer hvorfor navnematchingen fungerer bedre enn en naiv strengsammenligning: biblioteket normaliserer BaseFont-navn før det slår dem opp. Delsettprefikser (mønsteret ABCDEF+ med seks store bokstaver og et plusstegn) fjernes, PostScript-lignende suffikser som ArialMT løses til Arial, og TrueType Collection-filer oppdages og pakkes ut slik at et skriftsnitt som lever inne i en .ttc still embeds correctly
Verifisere reparasjonen med en preflight-rapport
CreatePreflightReport er verifikasjonstrinnet, og løkken er bevisst lukket: den samme revisjonen som fordømte filen, bør være den som godkjenner den. Feilkode 00030 is the PDF/A deep-audit finding that reads "At least one font is not embedded (FontFile/FontFile2/FontFile3 missing)", and it is reported against the file as a whole, so a single overlooked font keeps it alive. Run the report on the source file, repair, save, and run it again on the output
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;
For en visning per font i stedet for en dom per fil, last det reparerte dokumentet på nytt og gå gjennom: FindFonts etterfulgt av SelectFont og GetFontIsEmbedded rapporterer innbyggingsstatus font for font, noe som er det rette verktøyet når en batchjobb må logge nøyaktig hvilket skriftsnitt i hvilken fil som ikke kunne repareres. Det samme gjennomgangsmønsteret vises i artikkelen om uthenting av tekst, bilder og fonter fra lastede PDF-er, der det forer uthenting i stedet for reparasjon
Hva skjer når fonten ikke er installert på systemet?
EmbedMissingFonts hopper over enhver font hvis program den ikke finner, og rapporterer dette via returverdien: hvis antallet er lavere enn antallet ikke-innebygde fonter du telte, er differansen fonter systemet ikke har. Dette er den ærlige feilmodusen, og den er bedre enn alternativene, fordi det å finne opp et erstatningsprogram for en font som er navngitt i dokumentet ville endre visningen, noe en arkiveringsreparasjon aldri må gjøre. For disse tilfellene tilbyr losLab PDF Library tilbyr EmbedFontProgramFromFile, som bygger inn en innkommende .ttf eller .otf i den navngitte fonten, slik at en pipeline kan sende med bedriftsfontene den forventer å møte og falle tilbake på dem bevisst
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;
To grenser fortjener å bli nevnt tydelig. For det første blir ikke Type1-fonter reparert i den nåværende implementasjonen: deres /FontFile-oppføring krever den tre-segmenterte PFB-strukturen med eksplisitte lengdenøkler, og biblioteket hopper over dem i stedet for å skrive en feilformet strøm; de er sjeldne i moderne dokumenter, men de forekommer i gamle arkiver. For det andre er innbygging av en font en lisenshandling. En TrueType-fonts innbyggingstillatelser tilhører dens produsent, og en reparasjonspipeline som dytter lisensierte fontprogrammer inn i dokumenter som forlater organisasjonen, bør få noen til å bekrefte at fontlisensene faktisk tillater det. Biblioteket gjør det du ber om; om du kan be om det, er et spørsmål for din juridiske avdeling, ikke kompilatoren din
Innbygging er nødvendig, men ikke tilstrekkelig
Reparasjon av fonter fjerner feil 00030, og ingenting annet. Et dokument som feiler PDF/A på kryptering, på manglende XMP-metadata, på et enhetsavhengig fargerom uten en OutputIntent, eller på manglende ToUnicode-kart, vil fortsatt feile etter at hver font er bygd inn, noe som er grunnen til at reparasjonen hører hjemme i en preflight-drevet løkke i stedet for å erstatte den. Kjør hele rapporten, fiks det den nevner, og la rapporten fortelle deg når du er ferdig. Det er også en kostnadsdimensjon: et fullt CJK-fontprogram kan være på flere megabyte, så innbygging av flere av dem kan blåse opp et lite dokument dramatisk. Motvekten er delsett-innbygging, som dekkes i artikkelen om PDF-filstørrelsesoptimalisering og fontdelsett-innbygging, som kutter hvert innebygde program ned til glyfene dokumentet faktisk viser
Hindre nye dokumenter fra å regredere
SetEmbedAllFonts er den forebyggende halvdelen av samme funksjon: en vakt på skriversiden som hindrer din egen kode i å produsere dokumentene denne artikkelen reparerer. Med SetEmbedAllFonts(1) aktiv, blir ethvert påfølgende AddTrueTypeFont-kall som ber om Embed=0, forhøyet til en innebygd referanse, noe som utvider garantien som PDF/A-modus allerede håndhever til alle dokumenter. Det påvirker fonter som legges til etter kallet, ikke fonter som allerede er i en lastet fil, så arbeidsdelingen er ren: SetEmbedAllFonts for dokumentene du oppretter, EmbedMissingFonts for dokumentene du arver
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
Begge halvdelene, vakten på skriversiden og last-reparer-lagre-banen, er en del av losLab PDF Library for Delphi, C# og VB.NET, sammen med preflight-motoren som verifiserer resultatet; produktsiden inneholder hele font-API-referansen inkludert innbyggings- og delsettkall per font