PDFlibPas lost tekens op die het geselecteerde font niet kan tekenen door een fallbackketen van geïnstalleerde fonts te doorzoeken, cluster voor cluster, met behoud van shaping en de volgorde van bidirectionele runs. U schakelt dit in met SetAutomaticFontFallback, breidt de keten uit met AddFontFallback, en alleen de fallbackfonts die daadwerkelijk voor de output zijn gebruikt, worden in het bestand ingesloten
Het probleem dat dit oplost, komt elke documentgenerator tegen zodra een klantnaam arriveert in een schrift waar het templatefont nooit rekening mee hield. De storing is stil, en juist dat maakt haar kostbaar
Waarom verdwijnt niet-ondersteunde tekst in plaats van een fout te geven?
Omdat PDF geen concept kent van een font dat een teken niet kan tekenen. Een simpel font mapt bytecodes via een encoding naar glyphnamen; een samengesteld font mapt codes via een CMap naar glyph-indexen. Vraag om een glyph die het font niet bevat en u krijgt glyph-index nul, .notdef, die de meeste fonts als niets of als een leeg kader tekenen. Het bestand is structureel geldig, de tekstoperator is correct gevormd, en de pagina rendert. Er staat alleen niets waar de naam had moeten staan
Niets in ISO 32000-1 verplicht een producent om dit op te merken. Een generator die tekst wegschrijft zonder dekking te controleren, produceert een technisch conforme PDF die stilzwijgend inhoud heeft verloren, en het verlies komt weken later aan het licht op het scherm van een klant. Daarom worden de fallbackfunctie en het rapport over ontbrekende glyphs samen geleverd: oplossen wat opgelost kan worden is maar de helft van het werk, en rapporteren wat niet opgelost kon worden is de andere helft
Fallback gebeurt per cluster, niet per codepunt
Granulariteit is het detail dat een werkende implementatie onderscheidt van een aannemelijke. Tekst is geen reeks onafhankelijke tekens. Een Devanagari-lettergreep, een emoji met een huidskleurmodifier, een basisletter met combinerende tekens: elk daarvan is één cluster dat door één font gerenderd moet worden, omdat de shaping-beslissingen erbinnen afhangen van tabellen in dat font
PDFlibPas lost clusters op, dus een cluster dat een fallbackfont dekt, wordt volledig door dat font getekend. Een cluster middendoor splitsen en de helft door het primaire font en de helft door een fallback laten tekenen, zou een technisch aanwezig maar zichtbaar kapot resultaat opleveren, wat aantoonbaar erger is dan de lege ruimte waarmee u begon. Ook de volgorde van runs blijft behouden, dus een fallback binnen een rechts-naar-links-run herschikt de omringende tekst niet; dezelfde machinerie ligt onder de verticale opmaak beschreven in verticaal schrijven voor Japans en Chinees
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1);
Lib.SetAutomaticFontFallback(1);
// Zoekvolgorde: de eerste match wint, dus zet de breedste fonts achteraan
Lib.AddFontFallback('Microsoft YaHei'); // Vereenvoudigd Chinees
Lib.AddFontFallback('Meiryo'); // Japans
Lib.AddFontFallback('Segoe UI Symbol');
Lib.AddFontFallback('Segoe UI Emoji');
Lib.SetMissingGlyphPolicy(PDF_MISSING_GLYPH_REPORT);
Lib.AddTrueTypeFont('Arial', 1); // 1 = font insluiten
Lib.SetTextSize(11);
Lib.DrawText(72, 720, 'Invoice for 北京示例科技有限公司');
Lib.DrawText(72, 700, 'Delivery status: on time');
Lib.SaveToFile('invoice.pdf');
finally
Lib.Free;
end;
end;
Bepaal de volgorde van de keten bewust. Resolutie neemt het eerste font dat het cluster dekt, dus een brede pan-Unicode-font vooraan wint bijna alles en uw zorgvuldig gekozen schriftspecifieke fonts worden dan nooit geraadpleegd. Zet de specifieke fonts vooraan en de vangnetfont achteraan
Rapporteren of afbreken: welke storing wilt u?
SetMissingGlyphPolicy accepteert PDF_MISSING_GLYPH_REPORT, de compatibele standaard, of PDF_MISSING_GLYPH_ABORT. Onder het rapportbeleid gaat de tekstbewerking door, onoplosbare codepunten worden zoals voorheen weggelaten, en elk ervan wordt geregistreerd. Onder het afbreekbeleid wordt de tekstbewerking geweigerd voordat er inhoud wordt geschreven en LastErrorCode wordt op 521 gezet
Kies op basis van het doel van het document. Een batch interne rapporten moet blijven renderen en de gaten loggen, want een licht onvolledig rapport is vandaag beter dan geen rapport. Een juridisch bindend contract, een factuur, of iets met een naam erop, moet afbreken, want een stilzwijgend weggevallen teken in een partijnaam is een defect dat u liever in uw eigen proces ontdekt dan in een geschil. Het afbreekbeleid faalt vóór het schrijven, zodat er nooit een halfgevormde inhoudsstroom achterblijft
var
Lib: TPDFlib;
Report: WideString;
begin
Lib := TPDFlib.Create;
try
Lib.SetMissingGlyphPolicy(PDF_MISSING_GLYPH_ABORT);
// ... bouw het document ...
if Lib.DrawText(72, 660, CustomerName) <> 1 then
if Lib.LastErrorCode = PDFLIB_ERROR_MISSING_GLYPH then
begin
Report := Lib.GetMissingGlyphReportJSON;
// {"valid":false,"policy":1,"eventCount":1,"events":[
// {"sequence":1,"documentIndex":0,"page":1,"utf16Index":12,
// "codePoint":21271,"unicode":"U+5317","fontName":"Arial",
// "fontType":"TrueType","operation":"DrawText"}]}
EscalateToOperator(Report);
end;
finally
Lib.Free;
end;
end;
Het rapport is bewust machineleesbaar en begrensd. Elke gebeurtenis draagt de pagina, de UTF-16-index binnen de string, het codepunt in zowel numerieke als U+XXXX-vorm, het geselecteerde font, het foutype en de bewerking die op het probleem stuitte, zodat een supportticket het exacte teken kan benoemen in plaats van een symptoom te beschrijven. De tracker bewaart de laatste 256 gebeurtenissen, wat genoeg is om een document te diagnosticeren en klein genoeg zodat een pathologische run diagnostiek niet in een geheugenprobleem kan veranderen
Meten en tekenen moeten overeenstemmen
Breedtemeting gebruikt dezelfde clustergerichte fallbackbeslissingen als het tekenen. Dit klinkt vanzelfsprekend en is precies waar de meeste zelfgebouwde fallbacklagen het mis hebben: ze patchen het tekenpad, laten de meting op het primaire font staan, en elk tekstvak, elke rechtse uitlijning en elke tabelkolom wordt uiteindelijk berekend op basis van breedtes die niet overeenkomen met wat er werd gerenderd
Omdat beide paden dezelfde resolutie delen, neemt een string die vóór het tekenen gemeten is de breedte in waarop ze gemeten is, inclusief de fallbackruns. Dat is wat fallback veilig maakt om globaal in te schakelen, in plaats van alleen op de plekken die u handmatig hebt gecontroleerd
Alleen wat u gebruikte, wordt ingesloten
Fallbackfonts worden lui ingesloten: een font in de keten dat nooit een cluster oploste, draagt niets bij aan de output. Een document met één Chinees teken en 5.000 Latijnse tekens bevat geen volledig CJK-font; het bevat wat de subsetting-stap voor dat ene glyph opleverde, wat het gedrag is dat beschreven wordt in optimalisatie van bestandsgrootte en fontsubsetting
Die luiheid maakt een brede keten goedkoop om te configureren. Registreer de fonts die uw documentenset over elke door u bediende locale nodig zou kunnen hebben, en elke individuele PDF betaalt alleen voor wat daadwerkelijk gebruikt is. Voor documenten die u niet zelf hebt gegenereerd, waar de ontbrekende fonts al binnen een bestaand bestand zitten, is het reparatiepad anders en wordt dat behandeld in ontbrekende fonts insluiten in een bestaande PDF
Eén implementatiewaarschuwing is de moeite waard om expliciet te noemen: fallback lost op tegen fonts die geïnstalleerd zijn op de machine waarop de code draait. Een server zonder geïnstalleerde CJK-fonts heeft niets om op terug te vallen, en het rapport vertelt u dat al bij het eerste document in plaats van pas na de eerste klacht. Lever de fonts mee waarvan u afhankelijk bent, en bevestig de licentie voor het insluiten ervan
PDFlibPas is een PDF-bibliotheek voor Delphi, C++Builder en Lazarus met bijpassende DLL- en ActiveX-interfaces, dus de fallback- en missing-glyph-API's zijn ook beschikbaar voor niet-Pascal-aanroepers. Volledige documentatie staat op de PDFlibPas Delphi PDF-bibliotheekpagina