PDF Library for Delphi accepterer de skrifttyper, et webprojekt allerede leverer. Den importerer WOFF2-containere, opremser designakserne for en variabel skrifttype som JSON, og instantierer et valgt sæt akse-koordinater til en statisk skrifttype, som de almindelige indlejrings-, delmængdedannelses- og udtrækningsstier håndterer uden at vide noget om variationer. Både TrueType-omridser og CFF2-omridser er dækket
Den situation, dette adresserer, er almindelig og irriterende. Et brands typografi er defineret i et designsystem, designsystemet leverer variable WOFF2-filer, og PDF-generatoren kan hverken læse containeren eller variationsdataene. Den sædvanlige workaround — at bede skrifttypehuset om statiske TTF-instanser af hver faktisk brugt vægt — tager uger og giver filer, ingen holder synkroniseret
Hvorfor er WOFF2 ikke bare en komprimeret skrifttype?
At behandle WOFF2 som en Brotli-wrapper og dekomprimere den giver dig bytes, der ikke er en gyldig sfnt. Formatet tillader også transformerede og omordnede tabeller: glyf, loca og hmtx kan gemmes i en omarrangeret form, der skal vendes tilbage, før skrifttypen kan parses. En generel dekompressor kan ikke det
På Windows har platformen leveret container-analyse og udpakning siden Windows 10 version 1703, så biblioteket bruger systemimplementeringen frem for at levere endnu en dekoder. Den beslutning holder deploymentet fri for en ekstra DLL og betyder, at transform-tilbagevendingen håndteres af den samme kode, operativsystemets egen tekststak bruger. På andre platforme rejser WOFF1 stadig ad en ren Pascal-sti, og WOFF2 rapporterer eksplicit som ikke-understøttet, hvilket er et ærligt svar frem for en stiltiende korrumperet skrifttype
Importstien validerer både før og efter afkodning: 48-byte-headeren, container-længden, de reserverede felter, den deklarerede sfnt-størrelse og et loft på 512 MiB tjekkes alle først, og den afkodede stream-størrelse sammenlignes med deklarationen bagefter. En webskrifttype er downloadet indhold, og de to fejltilstande, det er værd at blokere, er en størrelsesuoverensstemmelse og en dekomprimeringsbombe
uses
PDFlibrary;
var
Lib: TPDFlib;
FontID, InstanceID: Integer;
AxesJSON: WideString;
begin
Lib := TPDFlib.Create;
try
Lib.NewDocument;
Lib.SetPageSize('A4');
Lib.NewPage;
// WOFF2, WOFF1, TTF, OTF og TTC går alle ind gennem det samme kald
FontID := Lib.AddOpenTypeFontFromFile('Inter.woff2', 0);
if FontID <= 0 then
raise Exception.Create('font could not be imported');
// Hvad kan denne skrifttype egentlig?
if Lib.GetFontVariationAxes(FontID, AxesJSON) = 1 then
Log(AxesJSON); // tags, minimum, standard, maksimum pr. akse
// Bag én designposition ind i en statisk skrifttype
InstanceID := Lib.CreateOpenTypeFontVariation(FontID,
'wght=620,wdth=87.5');
Lib.DrawOpenTypeText(56, 760, InstanceID, 24, 'Quarterly results', 0);
Lib.SaveToFile('report.pdf');
finally
Lib.Free;
end;
end;
Hvad instantiering skal genopbygge, og hvad den fjerner
En variabel skrifttype gemmer ét standardomrids pr. glyf plus deltaer indekseret efter position i designrummet. At producere en statisk instans betyder at interpolere de deltaer ved de ønskede koordinater, flytte punkterne og derefter genberegne alt, der er udledt af dem
For TrueType-omridser betyder det at parse fvar, avar, gvar, HVAR og MVAR, anvende tuple-deltaer med delt og privat punktnummerering, og interpolere de punkter, der ikke bærer noget eksplicit delta. Vandrette phantom-punkter sammen med HVAR bestemmer advance width og venstre sidebæring. Sammensatte glyffer skal have deres komponentafhængigheder og punktkortlægninger opløst først, hvorefter translation, skalering, matrix og fastgørelsesoffsets anvendes — med dybdegrænser og cyklusdetektion, fordi en ondsindet komponentgraf ellers ville rekursere uden ende
Metadata flytter også med, og det er den del, de fleste implementeringer springer over. MVAR opdaterer de globale metrikker i hhea, OS/2 og post, så linjeafstand, understregningsposition og kursivvinkel matcher instansen frem for standarden. Akserne wght, wdth, ital og slnt opdaterer desuden vægtklassen, breddeklassen, selektionsflagene, macStyle og kursivvinklen, så et værktøj, der inspicerer den statiske skrifttype, rapporterer den stil, du bad om. En skrifttype, hvis geometri er fed, og hvis metadata siger almindelig, vil blive fejlmærket alle steder downstream
Outputtet flader derefter sammensatte omridser og fjerner de tabeller, der ikke længere gælder: variationstabellerne, hints, device-tabeller og enhver signatur. Glyfgrænser, hhea-sammenfatningsmetrikker, maxp-hint-lofterne, checksummer pr. tabel og checksummen for hele skrifttypen genberegnes alle
CFF2 tager en anden rute til samme sted
CFF2 kan ikke indlejres direkte som et PDF-skrifttypeprogram: dens charstrings bærer ingen bredder, og den er afhængig af de omgivende OpenType-tabeller for glyfrækkefølge og metrikker. Så at instantiere en CFF2-skrifttype producerer i stedet en CID-nøglet CFF OpenType-skrifttype, som de eksisterende tegne-, indlejrings- og delmængdedannelsesstier allerede håndterer
At indlæse en CFF2-skrifttype producerer straks en statisk instans ved standardkoordinaterne til det aktuelle dokument, mens den originale CFF2-sfnt bevares på skrifttypeobjektet, så akseopremsning og senere instantiering stadig ser det fulde designrum. Hver glyf de-subrutiniseres individuelt under instantiering, hvilket fjerner variationsaritmetikken helt og lader den eksisterende delmængdedanner arbejde uændret. Prisen er, at en statisk instans kan være lidt større end én, der beholdt delte subrutiner — en fair pris for ikke at skulle have en anden delmængdedannelsesimplementering
Verificering af en instans, før du stoler på den
Instantiering er den slags funktion, der kan være subtilt forkert på måder, ingen exception rapporterer. Tre tjek fanger næsten alt
Eksportér instansen, og åbn den i et skrifttypeværktøj. SaveFontToFile skriver det bevarede skrifttypeprogram, så en in-memory-instans kan dumpes uden først at producere en PDF. Den eksporterede fil bør parses rent, indeholde ingen variationstabeller, og rapportere den stil-metadata, du bad om
Sammenlign advances ved en ikke-standard position. Hvis din pipeline kan producere den samme instans på en anden måde, skal du sammenligne advance widths og kurvegrænser glyf for glyf; referenceimplementeringen her matchede en uafhængig instantiering af en 313-glyf, to-akset skrifttype med en maksimal forskel på nul på tværs af hver glyf
Og udtræk teksten tilbage ud af den genererede PDF. En instans, der indlejres, men ikke producerer udtrækbar tekst, har mistet sin ToUnicode-kortlægning, hvilket betyder noget for søgning og tilgængelighed længe efter, at det visuelle tjek bestod. Den bredere udtrækningssti er dækket i tekst-, billed- og skrifttypeudtrækning
// Dump en genereret instans til inspektion eller genbrug
Lib.SaveFontToFile('Inter-620-87.ttf');
// Beder du om koordinater uden for det deklarerede interval, klemmes de fast,
// så læs akse-JSON'en frem for at antage en skrifttypes grænser
// {"axes":[{"tag":"wght","min":100,"default":400,"max":900}, ...]}
Praktiske noter til en brand-skrifttype-pipeline
Cache instanser efter koordinatsæt. Hvert distinkt sæt koordinater er et distinkt skrifttypeprogram, så et dokument, der blander vægt 400 og vægt 620, indlejrer to delmængder frem for én delt face. At generere den samme instans gentagne gange er spildt arbejde; at generere dusinvis af næsten identiske vægte er spildt filstørrelse
Mål med den instans, du vil tegne med. En kondenseret instans har smallere advances end standarden, så ethvert layout, der målte før instantiering, målte de forkerte bredder. Dette er den enkeltstående mest almindelige årsag til tekst, der kun løber over sin boks i produktion
Behold en fallback-sti til de glyffer, dit brand-skrifttype mangler. En variabel brand-skrifttype dækker typisk latin og lidt andet, og en rapport, der indeholder et kyrillisk kundenavn, vil ellers vise manglende glyffer; den automatiske fallback-mekanisme er beskrevet i automatisk skrifttypefallback for manglende glyffer. Og når målet er en mindre fil frem for en bestemt vægt, udretter delmængdedannelse mere arbejde end instantiering, som dækket i optimering af PDF-filstørrelse og skrifttype-delmængdedannelse
Webskrifttype-import, variabel instantiering, delmængdedannelse og fallback er del af den samme skrifttype-pipeline til Delphi, C++Builder og Free Pascal; den komplette funktionsliste findes på PDF Library for Delphi-siden