Il PDF non ha alcun concetto di font variabile. Un font incorporato in un file PDF è un insieme fisso di contorni con metriche fisse, quindi un font variabile deve essere ridotto a un'unica istanza statica prima di poter entrare in un documento. HotPDF esegue internamente questa instanziazione: si ispezionano gli assi di un font variabile, si selezionano coordinate come peso 620 o larghezza 87,5, e la libreria incorpora quei valori in un programma font completo e autonomo che qualsiasi reader PDF conforme può renderizzare
Il motivo per cui questo conta è pratico, non teorico. Le fonderie di caratteri distribuiscono sempre più spesso un unico file variabile al posto di una dozzina di pesi statici, e i team di design scelgono valori che nessuna istanza denominata fornisce. Senza instanziazione, un generatore di report torna all'istanza predefinita, il che scarta la decisione di design, oppure incorpora l'intero font variabile sperando che il visualizzatore rispetti coordinate degli assi che non ha modo di conoscere, cosa che nessun reader è tenuto a fare
Cosa deve ricostruire davvero l'instanziazione?
Un font variabile OpenType memorizza un contorno predefinito per glifo più un insieme di delta indicizzati per posizione nello spazio di design. Applicare una coordinata di asse non significa scrivere un numero in un'intestazione; significa percorrere la tabella gvar, interpolare i delta per la posizione richiesta, spostare i punti e poi ricalcolare tutto ciò che derivava da quei punti. HotPDF ricostruisce i contorni dei glifi, la tabella loca lunga, le metriche orizzontali e verticali complete, il bounding box globale del font e l'aggiustamento del checksum sfnt
Altrettanto importante è ciò che viene rimosso. Un'istanza statica non deve conservare fvar, avar, gvar, HVAR, VVAR, MVAR, STAT o cvar, e anche una DSIG ormai obsoleta deve sparire, poiché i byte firmati non esistono più. Lasciare anche solo una di queste tabelle produce un font che dichiara di essere variabile pur portando contorni già spostati, e i reader che applicano davvero le variazioni le applicherebbero una seconda volta
Punti fantasma e la trappola della doppia applicazione
La regola più sottile dell'intero processo riguarda le metriche. In gvar, il conteggio dei punti per un glifo copre i punti del contorno, o i punti dei componenti per un glifo composito, più quattro punti fantasma che codificano il side bearing sinistro, l'advance width e i loro equivalenti verticali. Anche quei punti fantasma sono soggetti a delta
Quindi quando un font ha una tabella gvar, HotPDF deriva le metriche orizzontali e verticali dai punti fantasma interpolati e non applica in aggiunta HVAR o VVAR. Applicare entrambi è l'errore classico: la stessa variazione viene applicata due volte e ogni advance width risulta leggermente troppo largo, il che si manifesta come testo che deriva gradualmente verso destra lungo una riga giustificata. Solo quando un font non ha gvar la libreria incorpora il metric variation store direttamente in hmtx o vmtx
Altri due dettagli mantengono la geometria corretta. I punti fantasma non partecipano mai all'interpolazione del contorno, quindi i punti non elencati esplicitamente per un glifo semplice vengono inferiti con IUP per contorno, escludendo i punti fantasma. E i glifi compositi ricevono i propri delta applicati agli offset dei componenti che usano parametri XY, dopodiché i bound dei figli vengono ricalcolati ricorsivamente. Questa ricorsione ha una profondità limitata ed è controllata contro i cicli, perché un grafo dei componenti malevolo o semplicemente rotto potrebbe altrimenti ricorrere senza fine
Ispezionare lo spazio di design prima di scegliere
La prima chiamata in qualsiasi workflow di instanziazione è InspectVariableFont, che riporta gli assi e le istanze denominate definite dalla fonderia. I record degli assi portano il tag a quattro byte, i valori minimo, predefinito e massimo, i flag e un name ID; le istanze denominate portano un name ID di sottofamiglia, i flag, un name ID PostScript opzionale e una coordinata per asse:
var
Pdf: THotPDF;
Axes: THPDFVariableFontAxisArray;
Instances: THPDFVariableFontNamedInstanceArray;
I: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.InspectVariableFont('C:\Fonts\Inter.ttf', Axes, Instances) then
begin
for I := 0 to High(Axes) do
Writeln(Format('%s min=%.1f default=%.1f max=%.1f',
[string(Axes[I].Tag), Axes[I].MinimumValue,
Axes[I].DefaultValue, Axes[I].MaximumValue]));
Writeln(Format('%d named instance(s) defined', [Length(Instances)]));
end
else
Writeln('not a variable font - embed it as an ordinary TrueType face');
finally
Pdf.Free;
end;
end;
Riportare l'intervallo degli assi è importante perché i valori degli assi vengono limitati all'intervallo dichiarato dal font, non a quello offerto dalla propria interfaccia. Uno slider che permette a un utente di richiedere il peso 1000 su un font il cui asse wght si ferma a 900 dovrebbe essere corretto nell'interfaccia, non silenziosamente a livello del font, altrimenti l'output stampato non corrisponderà all'anteprima
Selezionare le coordinate ed emettere il documento
La selezione degli assi è stateful e si applica ai font registrati successivamente. SetVariableFontAxis accetta un tag ASCII stampabile a quattro byte e un valore finito, e rifiuta qualsiasi altra cosa con un'eccezione anziché ignorarla silenziosamente. ClearVariableFontAxes azzera la selezione, e GetVariableFontAxisSelections riporta cosa è attualmente in sospeso, il che vale la pena registrare nei motori di report dove più percorsi di codice possono aver toccato lo stesso oggetto documento. La famiglia stessa viene selezionata per nome tramite SetFont, esattamente come qualsiasi altro font TrueType incorporato:
begin
Pdf := THotPDF.Create(nil);
try
Pdf.BeginDoc;
Pdf.SetVariableFontAxis('wght', 620); // semibold, non un'istanza denominata
Pdf.SetVariableFontAxis('wdth', 87.5); // leggermente condensato
Pdf.CurrentPage.SetFont('Inter', [], 11);
Pdf.CurrentPage.TextOut(72, 720, 0, 'Quarterly results');
Pdf.ClearVariableFontAxes; // torna all'istanza predefinita
Pdf.CurrentPage.SetFont('Inter', [], 10);
Pdf.CurrentPage.TextOut(72, 700, 0, 'Prepared by the finance team');
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
L'evento OnVariableFontInstance scatta ogni volta che viene prodotta un'istanza e riporta i valori degli assi che sono stati usati, il modo più economico per dimostrare in un log cosa contiene davvero un dato PDF. Poiché ogni insieme distinto di coordinate produce un programma font distinto, tratta le selezioni degli assi come parte della chiave della cache dei font; le meccaniche di caching sono descritte in la cache persistente dei subset dei font
Come l'instanziazione interagisce con subsetting e shaping
L'instanziazione viene eseguita prima del subsetting, e questo è l'ordine corretto. Il font instanziato è un normale font TrueType statico, quindi il subsetter ordinario lo tratta come qualsiasi altro: calcola la chiusura dei glifi, mantiene i glifi effettivamente usati dal documento e scarta il resto. L'interazione da tenere presente è che due diverse selezioni di assi della stessa famiglia sono due programmi font diversi, quindi un documento che mescola peso 400 e peso 620 incorpora due subset, non un unico font condiviso con due istanze
Lo shaping in linea di principio non è influenzato, ma vale la pena verificarlo in pratica. Le funzionalità di layout risiedono in GSUB e GPOS, che l'instanziazione preserva, quindi legature e alternative stilistiche continuano a funzionare come descritto in alternative stilistiche GSUB OpenType. Ciò che cambia è il posizionamento: un'istanza condensata ha advance più stretti rispetto a quella predefinita, quindi qualsiasi layout che ha misurato il testo prima dell'instanziazione ha misurato larghezze sbagliate. Misura con la stessa selezione di assi con cui poi renderizzerai, e la discrepanza scompare
Un'ultima nota difensiva dall'implementazione, utile per chi estende questo percorso. I font privi di metriche verticali valutano comunque gli argomenti degli array dinamici nel punto di chiamata Delphi, quindi gli array al momento del parsing vengono sempre allocati anziché affidarsi a un controllo HasVerticalMetrics per saltare un indice vuoto. È il tipo di dettaglio a livello di linguaggio che trasforma un ramo apparentemente protetto in una access violation esattamente sui font che non si erano testati
Il supporto ai font variabili si inserisce nella stessa pipeline dei font usata per incorporamento, subsetting e chiusura dei glifi, descritta più in profondità in chiusura del subset dei font e glifi shaped. L'intero set di funzionalità tipografiche per Delphi e C++Builder è elencato nella pagina del componente PDF Delphi HotPDF