Un PDF che appare perfetto sulla propria macchina e viene visualizzato come una serie di quadrati vuoti su quella di qualcun altro rappresenta il difetto di font più comune nei software di gestione documentale, e quasi mai significa che il testo sia errato. I caratteri sono intatti, la codifica è corretta, semplicemente i glifi non sono presenti. Ciò che cambia tra le due macchine sono i font installati nel sistema operativo, e la differenza tra un file portabile e uno fragile risiede in una decisione presa al momento della scrittura della pagina: se il font debba viaggiare all'interno del PDF o se si presupponga sia già presente sul sistema di destinazione
Comprendere perché ciò accade, e perché un malfunzionamento diverso produca un testo apparentemente ricercabile ma che se copiato risulta incomprensibile, richiede l'analisi di come il formato PDF memorizza il testo. Un PDF non memorizza frasi, bensì codici di glifi, un programma di font e tabelle che mappano gli uni agli altri. Ogni bug di rendering o di estrazione risiede proprio nel divario tra questi tre elementi. Di seguito viene presentata una panoramica di questo meccanismo, basata sullo standard ISO 32000, insieme alle chiamate Delphi che lo controllano nei punti critici
Caratteri, codici e glifi sono tre cose diverse
Il vocabolario spesso confonde perché nel linguaggio comune si tende a unificare tre concetti distinti nella parola "lettera". Un carattere è un'unità astratta di scrittura, come l'idea della A maiuscola, identificata in Unicode come U+0041. Un glifo è una forma disegnata, ovvero il contorno composto da curve e tratti che un determinato font utilizza per rappresentare quel carattere. Tra di essi si colloca il codice: il byte o i byte nel flusso di contenuto che indicano al visualizzatore quale glifo del font corrente disegnare
Il PDF funziona tramite codici. Quando un flusso di contenuto mostra una stringa, quei byte sono indici all'interno del font attivo, non valori Unicode. La codifica del font stabilisce che un codice pari a 65 significa "disegna il glifo registrato al numero 65", e nulla in questa operazione sa che il risultato assomiglia a una A per un essere umano. Questo è il motivo per cui il PDF viene renderizzato in modo identico ovunque riesca a trovare i glifi, ed è anche il motivo per cui l'estrazione del testo è un problema distinto dalla visualizzazione: il disegno richiede solo la mappa codice-glifo, mentre la lettura necessita della mappa codice-Unicode, e queste sono due tabelle diverse che possono non coincidere o mancare del tutto in modo indipendente
I tipi di font che incontrerete nella pratica
Lo standard ISO 32000 definisce diversi tipi di dizionari di font e, all'atto pratico, un documento ricevuto o generato ne utilizza uno tra tre tipi principali. Sapere quale si sta esaminando spiega la maggior parte dei problemi che possono verificarsi
Type 1 è il formato originale di profili PostScript di Adobe, basato su curve di Bezier cubiche. I quattordici font standard che ogni visualizzatore conforme deve fornire (le famiglie Helvetica, Times, Courier, Symbol e ZapfDingbats) sono Type 1, e un dizionario di font che fa riferimento a uno di essi può legalmente omettere il programma del font. Questo è l'unico caso in cui non incorporare un font è sicuro per specifica e non per fortuna. Per qualsiasi altro carattere Type 1, il programma del font deve essere incorporato, altrimenti il visualizzatore utilizzerà un sostituto, solitamente un font metricamente simile ma visibilmente diverso
TrueType utilizza curve quadratiche e proviene dal mondo Apple e Microsoft. È il formato della maggior parte dei font di sistema e quello che verrà incorporato più spesso. Un font TrueType semplice in un PDF è limitato a codici a singolo byte, quindi un tale font può indirizzare al massimo 256 glifi alla volta. Questo limite rappresenta la ragione strutturale per cui i caratteri CJK e altri alfabeti complessi non possono essere gestiti da un font semplice
Type 0, il font composito o basato su CID, è la risposta a tale limite. Utilizza codici multi-byte e una CMap per indirizzarli attraverso un CIDFont discendente, i cui profili sono a loro volta TrueType o CFF/Type 1. Questo è l'unico tipo di font in grado di contenere migliaia di glifi, pertanto qualsiasi PDF che contenga caratteri cinesi, giapponesi, coreani o una vasta combinazione multilingue utilizza un font Type 0, indipendentemente dal fatto che l'autore ne sia consapevole o meno. Il compromesso è la complessità: vi sono più parti in movimento e un numero maggiore di esse deve essere corretto sia per il rendering che per l'estrazione

Un dettaglio dietro questa immagine influisce direttamente sulla dimensione del file. Un font è una libreria di profili e non di bitmap a dimensione fissa, quindi lo stesso programma incorporato serve per ogni dimensione di punto sulla pagina. Il ridimensionamento è una trasformazione applicata al momento del disegno, motivo per cui un'intestazione e il testo del corpo condividono lo stesso carattere incorporato e il costo dell'incorporamento è per font, non per dimensione
L'incorporamento fa la differenza tra portabile e fragile
Incorporare significa che il programma del font, ovvero i dati reali del profilo, viene scritto nel PDF come un flusso di dati. Un visualizzatore su una macchina che non ha mai sentito parlare del vostro font leggerà quei profili direttamente dal file e disegnerà i glifi esatti. Se si salta l'incorporamento, si scommette che il sistema di destinazione disponga di un font con lo stesso nome; quando questo manca, il visualizzatore ripiega su un sostituto. Per i quattordici font standard questa sostituzione è definita e non problematica. Per tutto il resto, si va da una leggera differenza con un carattere diverso fino al risultato dei quadrati vuoti quando nessun font sostitutivo copre l'alfabeto in questione
Con HotPDF il controllo è affidato a una singola proprietà, da impostare prima dell'apertura del documento. FontEmbedding indica alla libreria di includere nel file i caratteri utilizzati per il disegno:
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'report.pdf';
Pdf.Compression := cmFlateDecode;
Pdf.FontEmbedding := True; // outlines travel inside the file
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Calibri', [], 11);
Pdf.CurrentPage.TextOut(72, 760, 0, 'This renders the same on a machine without Calibri.');
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
L'ordine delle chiamate non è un fattore estetico. BeginDoc è il punto in cui HotPDF consolida la struttura del documento, quindi FontEmbedding deve essere impostato su true prima di tale chiamata. Se lo si assegna successivamente, non si otterrà alcun errore o avviso, bensì solo un file che è stato generato silenziosamente senza i suoi font. Questo è il tipo peggiore di bug: supera ogni test sulla macchina dello sviluppatore, dove il font è installato, e si manifesta solo su quella del cliente, dove il font manca
L'incorporamento è anche il punto in cui le licenze incontrano l'ingegneria. Un programma di font contiene flag che descrivono se può essere incorporato liberamente, solo per l'anteprima o se non può esserlo affatto. Rispettare questi flag è responsabilità vostra, non del motore di rendering, e il fatto che "abbia funzionato" non equivale a dire che "sia consentito"
Subsetting: incorporare solo i glifi utilizzati
L'incorporamento completo scrive l'intero programma del font all'interno del file. Un carattere TrueType CJK di grandi dimensioni può raggiungere diversi megabyte, e incorporarlo per intero per mostrare una dozzina di caratteri è uno spreco che si accumula in un documento di più pagine. Il subsetting risolve questo problema scrivendo solo i glifi a cui il documento fa riferimento, rinominando poi il font con un prefisso di sei lettere e un segno più, ad esempio nella forma ABCDEF+Calibri nell'elenco dei font di qualsiasi PDF con subsetting, in modo che il visualizzatore non confonda mai il carattere parziale con un font di sistema completo avente lo stesso nome
Per la maggior parte dei documenti generati, il subsetting rappresenta la scelta predefinita corretta. Mantiene la dimensione del file proporzionale al contenuto piuttosto che al font di origine, il che è particolarmente importante per i grandi font multilingue che altrimenti dominerebbero la dimensione del file. L'unico inconveniente è che un sottoinsieme contiene solo ciò che è stato utilizzato al momento della creazione. Se un processo successivo tenta di aggiungere testo a un font con subsetting in un secondo momento, i glifi necessari potrebbero non essere presenti nel file, rappresentando un limite reale per la modifica incrementale di un PDF altrui
I font Unicode e il problema dei quadrati CJK
Quando il testo non è in semplice caratteri latini, la strada dei font semplici non è più percorribile, e la soluzione consiste nel registrare esplicitamente un font abilitato a Unicode, lasciando che HotPDF crei un font Type 0 a partire da esso. RegisterUnicodeTTF carica un file TrueType specificando il percorso; successivamente, il nome registrato è utilizzabile in SetFont come qualsiasi altro:
Pdf.FontEmbedding := True;
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansCJKsc-Regular.ttf');
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('NotoSansCJKsc-Regular', [], 14);
Pdf.CurrentPage.TextOut(72, 720, 0, '你好,世界 こんにちは 안녕하세요');
Pdf.EndDoc;
Due fattori determinano il successo o il fallimento di questa operazione. Innanzitutto, il font deve coprire gli alfabeti presenti nella stringa: un font TrueType solo latino non genererà glifi cinesi solo perché richiesto, e il risultato sarà nuovamente composto da quadrati vuoti, questa volta perché il glifo non esiste effettivamente in quel carattere. Inoltre, l'incorporamento deve rimanere attivo, poiché un font Type 0 assemblato da un file TTF registrato è privo di significato per un visualizzatore che non riesca a trovare i profili dei caratteri. Per contenuti misti, la scelta ideale e duratura ricade su font ad ampia copertura, come le famiglie Noto e Arial Unicode MS, incorporati e con subsetting attivo
I sistemi di scrittura da destra a sinistra e complessi aggiungono un livello di formattazione (shaping) oltre alla copertura del carattere. HotPDF espone la funzione RtLTextOut per l'arabo e l'ebraico, che gestisce il riordino direzionale in modo da poter passare l'ordine logico e lasciare che la libreria si occupi dell'impaginazione. Gestire correttamente l'arabo richiede copertura, formattazione e direzione (tre aspetti separati), e un quadrato vuoto in questo contesto può significare che uno qualsiasi di questi fattori sia venuto meno
La tabella ToUnicode: dove risiede il copia-incolla
Tutto ciò che è stato descritto sopra riguarda il disegno. L'estrazione è l'esatto contrario e fallisce per ragioni proprie. Un visualizzatore renderizza una pagina utilizzando la mappatura codice-glifo del font, ma quando un utente seleziona il testo e lo copia, il visualizzatore deve riconvertire quegli stessi codici in Unicode. Questa mappatura inversa è rappresentata dalla CMap ToUnicode, un flusso di dati opzionale allegato al font
Quando è presente e corretta, il testo copiato viene estratto con i caratteri giusti. Quando è assente o errata, o se il font è stato sottoposto a subsetting con codici di glifi personalizzati senza che venisse scritta una tabella ToUnicode, la pagina apparirà perfetta ma la clipboard si riempirà di caratteri illeggibili: i codici dei glifi vengono letti come se fossero Unicode, cosa che non sono in un sottoinsieme con codifica personalizzata. Questo spiega perché un documento scansionato con un livello di testo OCR può essere ricercabile, mentre un PDF nativo digitale generato in modo non accurato non lo è. Il rendering e l'estrazione si basano su tabelle diverse, pertanto un file può soddisfare il primo e fallire con la seconda. Se l'estrazione del testo è importante per i vostri output, considerate una mappa ToUnicode corretta come un requisito fondamentale e verificatela copiando il testo da un file di esempio, piuttosto che dare per scontata la sua presenza
Come diagnosticare rapidamente un bug dei font
La modalità in cui si presenta il problema indica dove cercare. I quadrati vuoti su un'altra macchina indicano quasi sempre un font non incorporato, quindi verificate prima l'incorporamento e poi la copertura dei glifi. I quadrati che compaiono anche sulla propria macchina indicano un problema di copertura: il font non contiene quell'alfabeto, indipendentemente dall'incorporamento. Il testo che viene renderizzato correttamente ma copiato come caratteri senza senso rappresenta un problema di ToUnicode e non di rendering; modificare i font o l'incorporamento non risolverà il problema poiché la parte visiva non è mai stata errata. Per analizzare un file finito, apritelo in Acrobat e andate su Proprietà del documento, Font: un elemento corretto mostra il tipo, riporta la dicitura Incorporato o Sottoinsieme incorporato e specifica la codifica. Un font che dovrebbe essere incorporato ma non lo è si palesa in quella sezione molto prima che lo segnali un cliente
Nulla di tutto questo è insolito una volta compresa la distinzione tra carattere, codice e glifo. Incorporate i font con cui disegnate, create sottoinsiemi per quelli grandi, utilizzate un font Unicode ed eseguite la funzione RegisterUnicodeTTF non appena il testo esce dai caratteri latini, e mantenete una mappa ToUnicode corretta se prevedete che qualcuno debba estrarre il testo. Seguendo queste regole, i quadrati smetteranno di apparire. Per i meccanismi correlati, l'anatomia di un PDF minimale mostra la posizione del dizionario dei font nell'albero degli oggetti, mentre l'analisi approfondita della struttura del documento illustra come le risorse vengono condivise tra le pagine
Le chiamate SetFont, FontEmbedding e RegisterUnicodeTTF qui illustrate fanno parte di HotPDF Component per Delphi e C++Builder