PDF Library for Delphi accetta i font che un progetto web distribuisce già. Importa contenitori WOFF2, enumera gli assi di design di un font variabile come JSON, e istanzia un insieme scelto di coordinate d'asse in un font statico che i normali percorsi di incorporamento, subsetting ed estrazione gestiscono senza sapere nulla delle variazioni. Sono coperti sia i contorni TrueType sia i contorni CFF2
La situazione che questo risolve è comune e fastidiosa. La tipografia di un brand è definita in un design system, il design system distribuisce file WOFF2 variabili, e il generatore PDF non può leggere né il contenitore né i dati di variazione. La soluzione alternativa abituale — chiedere alla fonderia istanze TTF statiche di ogni peso effettivamente usato — richiede settimane e produce file che nessuno mantiene sincronizzati
Perché WOFF2 non è semplicemente un font compresso?
Trattare WOFF2 come un semplice wrapper Brotli e decomprimerlo produce byte che non sono un sfnt valido. Il formato permette anche tabelle trasformate e riordinate: glyf, loca e hmtx possono essere memorizzate in una forma riorganizzata che deve essere invertita prima che il font sia analizzabile. Un decompressore generico non può farlo
Su Windows, la piattaforma fornisce l'analisi e lo scompattamento del contenitore fin da Windows 10 versione 1703, quindi la libreria usa l'implementazione di sistema anziché distribuire un altro decodificatore. Questa decisione mantiene il deployment libero da una DLL aggiuntiva e significa che l'inversione della trasformazione è gestita dallo stesso codice che usa lo stack di testo del sistema operativo. Su altre piattaforme, WOFF1 percorre ancora un percorso Pascal puro e WOFF2 viene esplicitamente segnalato come non supportato, il che è una risposta onesta invece di un font silenziosamente corrotto
Il percorso di importazione valida prima e dopo la decodifica: l'intestazione di 48 byte, la lunghezza del contenitore, i campi riservati, la dimensione sfnt dichiarata e un tetto di 512 MiB vengono tutti controllati per primi, e la dimensione dello stream decodificato viene confrontata con la dichiarazione in seguito. Un font web è contenuto scaricato, e le due modalità di fallimento che vale la pena bloccare sono una discrepanza di dimensione e una bomba di decompressione
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 e TTC entrano tutti attraverso la stessa chiamata
FontID := Lib.AddOpenTypeFontFromFile('Inter.woff2', 0);
if FontID <= 0 then
raise Exception.Create('font could not be imported');
// Cosa può fare realmente questo font?
if Lib.GetFontVariationAxes(FontID, AxesJSON) = 1 then
Log(AxesJSON); // tag, minimo, default, massimo per asse
// Fissa una posizione di design in un font statico
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;
Cosa deve ricostruire l'instanziazione, e cosa rimuove
Un font variabile memorizza un contorno predefinito per glifo più delta indicizzati per posizione nello spazio di design. Produrre un'istanza statica significa interpolare quei delta alle coordinate richieste, spostare i punti, e poi ricalcolare tutto ciò che ne deriva
Per i contorni TrueType questo significa analizzare fvar, avar, gvar, HVAR e MVAR, applicare delta di tupla con numerazione di punti condivisa e privata, e interpolare i punti che non portano alcun delta esplicito. I punti fantasma orizzontali insieme a HVAR determinano la larghezza di avanzamento e il side bearing sinistro. I glifi composti richiedono prima la risoluzione delle loro dipendenze di componente e delle mappature dei punti, poi l'applicazione di traslazione, scala, matrice e offset di attacco — con limiti di profondità e rilevamento di cicli, perché altrimenti un grafo di componenti malevolo ricorrerebbe senza fine
Si spostano anche i metadati, e questa è la parte che la maggior parte delle implementazioni salta. MVAR aggiorna le metriche globali in hhea, OS/2 e post, così l'interlinea, la posizione della sottolineatura e l'angolo corsivo corrispondono all'istanza anziché al default. Gli assi wght, wdth, ital e slnt aggiornano inoltre la classe di peso, la classe di larghezza, i flag di selezione, macStyle e l'angolo corsivo, così uno strumento che ispeziona il font statico riporta lo stile che avete richiesto. Un font la cui geometria è bold e i cui metadati dicono regular verrà etichettato erroneamente ovunque a valle
L'output appiattisce poi i contorni composti e rimuove le tabelle che non si applicano più: le tabelle di variazione, gli hint, le tabelle device e qualsiasi firma. I limiti dei glifi, le metriche di riepilogo hhea, i tetti degli hint maxp, i checksum per tabella e il checksum dell'intero font vengono tutti ricalcolati
CFF2 percorre una strada diversa verso lo stesso risultato
CFF2 non può essere incorporato direttamente come programma di font PDF: le sue charstring non portano larghezze, e si basa sulle tabelle OpenType circostanti per l'ordine dei glifi e le metriche. Quindi istanziare un font CFF2 produce invece un font OpenType CFF con chiave CID, che i percorsi esistenti di disegno, incorporamento e subsetting già gestiscono
Caricare un font CFF2 produce immediatamente un'istanza statica a coordinate predefinite per il documento corrente, mentre l'sfnt CFF2 originale viene conservato sull'oggetto font così l'enumerazione degli assi e l'instanziazione successiva vedono ancora l'intero spazio di design. Ogni glifo viene de-subroutinizzato individualmente durante l'instanziazione, il che rimuove del tutto l'aritmetica di variazione e permette al subsetter esistente di funzionare invariato. Il compromesso è che un'istanza statica può essere leggermente più grande di una che ha mantenuto le subroutine condivise — un prezzo equo per non dover avere una seconda implementazione di subsetting
Verificare un'istanza prima di fidarvene
L'instanziazione è il tipo di funzionalità che può essere sottilmente sbagliata in modi che nessuna eccezione segnala. Tre controlli catturano quasi tutto
Esportate l'istanza e apritela in uno strumento per font. SaveFontToFile scrive il programma di font conservato, così un'istanza in memoria può essere scaricata su file senza prima produrre un PDF. Il file esportato dovrebbe analizzarsi correttamente, non contenere tabelle di variazione, e riportare i metadati di stile che avete richiesto
Confrontate gli avanzamenti in una posizione non predefinita. Se la vostra pipeline può produrre la stessa istanza in un altro modo, confrontate le larghezze di avanzamento e i limiti delle curve glifo per glifo; l'implementazione di riferimento qui ha corrisposto a un'instanziazione indipendente di un font a due assi con 313 glifi con una differenza massima di zero su ogni glifo
E riestraete il testo dal PDF generato. Un'istanza che si incorpora ma non produce testo estraibile ha perso la sua mappatura ToUnicode, il che conta per la ricerca e l'accessibilità ben oltre il superamento del controllo visivo. Il percorso di estrazione più ampio è trattato in estrazione di testo, immagini e font
// Scarica su file un'istanza generata per l'ispezione o il riutilizzo
Lib.SaveFontToFile('Inter-620-87.ttf');
// Richiedere coordinate fuori dall'intervallo dichiarato le fa limitare (clamp),
// quindi leggete il JSON degli assi invece di presumere i limiti di un font
// {"axes":[{"tag":"wght","min":100,"default":400,"max":900}, ...]}
Note pratiche per una pipeline di font di brand
Mettete in cache le istanze per set di coordinate. Ogni set distinto di coordinate è un programma di font distinto, quindi un documento che mischia il peso 400 e il peso 620 incorpora due subset invece di un unico font condiviso. Generare ripetutamente la stessa istanza è lavoro sprecato; generare dozzine di pesi quasi identici è dimensione del file sprecata
Misurate con l'istanza con cui disegnerete. Un'istanza condensata ha avanzamenti più stretti del default, quindi qualsiasi layout che ha misurato prima dell'instanziazione ha misurato le larghezze sbagliate. Questa è la causa singola più comune di testo che deborda dal proprio riquadro solo in produzione
Mantenete un percorso di fallback per i glifi che il vostro font di brand non ha. Un font di brand variabile copre tipicamente il latino e poco altro, e un report contenente un nome cliente cirillico mostrerà altrimenti glifi mancanti; il meccanismo di fallback automatico è descritto in fallback automatico dei font per glifi mancanti. E quando l'obiettivo è un file più piccolo anziché un peso specifico, il subsetting fa più lavoro dell'instanziazione, come trattato in ottimizzazione della dimensione del file PDF e subsetting dei font
L'importazione dei font web, l'instanziazione variabile, il subsetting e il fallback fanno parte della stessa pipeline di font per Delphi, C++Builder e Free Pascal; l'elenco completo delle funzionalità è disponibile sulla pagina di PDF Library for Delphi