losLab PDF Library kan integrere de manglende skrifttypeprogrammer i en allerede indlæst PDF med et enkelt kald: EmbedMissingFonts gennemgår alle skrifttypeordbøger i dokumentet, finder den matchende installerede systemskrifttype ud fra dens BaseFont-navn og skriver skrifttypeprogrammet tilbage i filen. For teams, der reparerer tredjepartsdokumenter, som fejler i PDF/A-valideringen på grund af manglende integrering af skrifttyper, især ved preflightfejl 00030, er dette rettelsen, der løser problemet
Scenariet er deprimerende almindeligt. En arkivindlæsningspipeline modtager PDF-filer fra leverandører, kunder eller et scanningsbureau; dokumenterne renderes fint på alle skriveborde i bygningen; og derefter afviser PDF/A-validatoren hele partiet med den samme klage gentaget én gang pr. fil: Mindst én skrifttype er ikke integreret. Ingen opstrøms vil generere filerne igen, så pipelinen er nødt til at reparere dem. Denne artikel dækker den reparationsvej. Den er ledsageren til preflight-artiklen, som dækker detektering af PDF/A- og PDF/UA-overtrædelser: Den artikel fortæller dig, hvilke dokumenter der er beskadigede, mens denne retter den hyppigste årsag til, at de er beskadigede
Hvorfor kræver PDF/A, at alle skrifttyper er integreret?
ISO 19005-1 §6.3.4 kræver, at alle skrifttyper, der anvendes af et overensstemmende dokument, indeholder deres skrifttypeprogram i filen, fordi hele PDF/A's løfte er reproducerbarhed: Dokumentet skal renderes identisk på en maskine om halvtreds år, der ikke deler nogen skrifttyper med den maskine, der producerede det. En ikke-integreret skrifttype is en instruktion om at finde Arial et eller andet sted på visningssystemet, og standardens holdning er, at "et eller andet sted på visningssystemet" ikke er en arkiveringsgaranti. Uanset hvilke tegn (glyphs), metrikker og dækning erstatningsskrifttypen har, er det det, læseren får, og det er måske ikke det, forfatteren så
Den historiske synder er Standard 14-konventionen. PDF 1.0 lovede, at enhver fremviser leveres med Helvetica, Times, Courier, Symbol og ZapfDingbats, så generatorer lærte at referere til disse skrifttyper ved navn og intet integrere, og tredive års værktøjer gør stadig præcis det. losLab PDF Library tager kravet alvorligt nok til, at i PDF/A-oprettelsestilstand er AddStandardFont bevidst en no-op: Biblioteket leverer ikke Standard 14-skrifttypeprogrammerne, kan ikke integrere det, det ikke har, og nægter at skrive en ikke-integreret reference i et dokument, der hævder at være overensstemmende. Det returnerer 0 uden at vælge en skrifttype, så et PDF/A-dokument skal bruge AddTrueTypeFont med integrering i stedet, og enhver Embed=0-anmodning opgraderes stiltiende til Embed=1, mens PDF/A-tilstand er aktiv. Det er skriversiden. Det sværere problem er læsersiden: Et dokument, som en anden allerede har skrevet, fyldt med skrifttypeordbøger, du ikke har oprettet
Hvordan reparerer EmbedMissingFonts et indlæst dokument?
losLab PDF Library reparerer skrifttyper på stedet frem for at genopbygge dem. Når en PDF-generator skriver en ikke-integreret TrueType-skrifttype, er den producerede FontDescriptor-ordbog allerede komplet: FontName, FontBBox, Flags, Ascent, Descent, StemV er alle til stede. Det eneste, der adskiller den fra en integreret skrifttype, is manglen på én post, nemlig stream-referencen /FontFile2, der indeholder det faktiske skrifttypeprogram. Derfor rører EmbedMissingFonts ikke ved skrifttypeordbogen, kodningen, breddetabellen eller nogen indholdsstrøm, der refererer til skrifttypen efter ressourcenavn. Den læser det matchende skrifttypeprogram fra systemet, komprimerer det til et nyt stream-objekt og tilføjer en enkelt /FontFile2-reference (eller /FontFile3 til CIDFontType0-skrifttyper) til den FontDescriptor, der allerede er der. Alt, hvad dokumentets sider peger på, forbliver præcis, hvor det var, hvilket gør operationen sikker at køre på filer, du ikke kontrollerer
Dækningen omfatter begge skrifttypearkitekturer, du vil møde i praksis: Simple TrueType-skrifttyper og sammensatte Type0/CID-skrifttyper, af den slags der produceres til CJK-tekst og moderne Unicode-output. Gennemgangen oplister bevidst alle Font-ordbøger i dokumentets objektræ i stedet for at forlade sig på en side-for-side ressourcegennemgang, så skrifttyper, der refereres til fra annoteringer eller deles på tværs af sider, også medtages. API'en er et enkelt kald på det indlæste dokument
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 detalje, der er værd at kende, fordi den forklarer, hvorfor navnematchingen fungerer bedre end en simpel strengsammenligning: Biblioteket normaliserer BaseFont-navne, før det søger efter dem. Subset-præfikser (mønsteret ABCDEF+ bestående af seks store bogstaver og et plustegn) fjernes, PostScript-lignende suffikser som f.eks. ArialMT opløses til Arial, og TrueType Collection-filer detekteres og pakkes ud, så en skrifttype i en .ttc-fil stadig integreres korrekt
Verificering af reparationen med en preflight-rapport
CreatePreflightReport er verificeringstrinnet, og løkken lukkes bevidst: Den samme revision, der dømte filen, skal være den, der godkender den. Fejlkode 00030 er det fund ved PDF/A-dybdegående revision, der lyder "At least one font is not embedded (FontFile/FontFile2/FontFile3 missing)", og den rapporteres for filen som helhed, så en enkelt overset skrifttype holder fejlen i live. Kør rapporten på kildefilen, reparer, gem, og kør den igen på outputtet
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 at få en visning pr. skrifttype frem for en afgørelse pr. fil kan du indlæse det reparerede dokument igen og gennemgå: FindFonts efterfulgt af SelectFont og GetFontIsEmbedded rapporterer integreringsstatus skrifttype for skrifttype, hvilket er det rette værktøj, når et batchjob skal logge præcis, hvilket skriftsnit i hvilken fil der ikke kunne repareres. Det samme gennemgangsmønster optræder i artiklen om udtrækning af tekst, billeder og skrifttyper fra indlæste PDF-filer, hvor det understøtter udtrækning i stedet for reparation
Hvad sker der, hvis skrifttypen ikke er installeret på systemet?
EmbedMissingFonts springer over enhver skrifttype, hvis program den ikke kan finde, og rapporterer dette via sin returværdi: Hvis antallet er lavere end det antal ikke-integrerede skrifttyper, du talte, skyldes forskellen skrifttyper, som systemet ikke har. Dette er den ærlige fejltilstand, og den er bedre end alternativerne, da det at opfinde et erstatningsprogram til en skrifttype navngivet i dokumentet ville ændre renderingen, hvilket er netop det, en arkivmæssig reparation aldrig må gøre. Til disse tilfælde tilbyder losLab PDF Library EmbedFontProgramFromFile, som integrerer en brugerdefineret .ttf- eller .otf-fil i den navngivne skrifttype, så en pipeline kan distribuere de virksomhedsskrifttyper, den forventer at støde på, og bevidst falde tilbage på dem
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 begrænsninger fortjener at blive nævnt klart. For det første repareres Type1-skrifttyper ikke i den nuværende implementering: Deres /FontFile-post kræver PFB-strukturen med tre segmenter med eksplicitte længdenøgler, og biblioteket springer dem over frem for at skrive en fejlbehæftet strøm; de er sjældne i moderne dokumenter, men de forekommer i gamle arkiver. For det andet er integrering af en skrifttype en licensmæssig handling. En TrueType-skrifttypes integreringsrettigheder tilhører dens udbyder (foundry), og en reparationspipeline, der propper licenserede skrifttypeprogrammer ind i dokumenter, som forlader organisationen, bør få bekræftet, at skrifttypelicenserne faktisk tillader det. Biblioteket vil gøre, hvad du beder om; om du må bede om det, er et spørgsmål til din juridiske afdeling, ikke til din compiler
Integrering er nødvendig, men ikke tilstrækkelig
Reparation af skrifttyper fjerner fejlkode 00030 og intet andet. Et dokument, der fejler PDF/A på kryptering, manglende XMP-metadata, et enhedsafhængigt farverum uden en OutputIntent eller manglende ToUnicode-kort, vil stadig fejle, efter at alle skrifttyperer blevet integreret, og derfor hører reparationen hjemme i en preflight-styret løkke i stedet for at erstatte den. Kør den fulde rapport, ret det, den nævner, og lad rapporten fortælle dig, hvornår du er færdig. Der er også en økonomisk dimension: Et komplet CJK-skrifttypeprogram fylder megabytes, så integrering af flere af dem kan puste et lille dokument dramatisk op. Modvægten er delmængdedannelse (subsetting), som er beskrevet i artiklen om optimering af PDF-filstørrelse og delmængdedannelse af skrifttyper, hvilket reducerer hvert integreret program til kun de tegn, dokumentet rent faktisk renderer
Forhindring af regression i nye dokumenter
SetEmbedAllFonts er den forebyggende halvdel af samme funktion: En vagt på skriversiden, der forhindrer din egen kode i at producere de dokumenter, som denne artikel reparerer. Når SetEmbedAllFonts(1) er aktiv, vil ethvert efterfølgende AddTrueTypeFont-kald, der anmoder om Embed=0, blive opgraderet til en integreret reference, hvilket udvider den garanti, som PDF/A-tilstand allerede håndhæver, til alle dokumenter. Det påvirker skrifttyper, der tilføjes efter kaldet, ikke skrifttyper, der allerede findes i en indlæst fil, så arbejdsdelingen er klar: SetEmbedAllFonts til de dokumenter, du opretter, og EmbedMissingFonts til de dokumenter, 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 halvdele, vagten på skriversiden og indlæs-reparer-gem-vejen, er en del af losLab PDF Library til Delphi, C# og VB.NET sammen med preflight-motoren, der verificerer resultatet; produktsiden indeholder hele skrifttype-API-referencen, inklusive kaldene til integrering og delmængdedannelse pr. skrifttype