PDFium Component scrive testo in una pagina PDF a livello di glifo con AddCidType2Text, incorporando dati TrueType che voi fornite come font CID Type 2. Assegna a ogni istanza di glifo un CID sequenziale, genera la mappa esplicita CID-to-GID e la CMap ToUnicode, e unisce i glifi sulla stessa linea di base in un unico oggetto testo nativo. Il subsetting opzionale mantiene piccolo il file, con una policy esplicita per cosa succede quando il subsetting fallisce
La scrittura a livello di glifo è ciò di cui avete bisogno quando il testo è già stato shaped. L'arabo, il devanagari e qualsiasi script con forme contestuali producono una sequenza di identificatori di glifo che non si mappa più uno-a-uno sui caratteri, quindi un'API che prende una stringa e un nome di font non può esprimere il risultato. Consegnare direttamente glifi e posizioni è l'unico modo per inserire in un PDF testo di script complesso correttamente shaped
Perché ogni istanza di glifo ottiene il proprio CID
L'ottimizzazione allettante è la deduplicazione: un CID per identificatore di glifo distinto, riutilizzato ovunque quel glifo compaia. Produce un font più piccolo e rompe l'estrazione del testo, perché lo stesso glifo può legittimamente corrispondere a contenuto Unicode diverso in punti diversi
Un glifo di legatura è il caso più chiaro. Lo stesso glifo "ffi" può rappresentare i tre caratteri in una parola e, dopo una diversa decisione di shaping, qualcos'altro altrove. La mappa ToUnicode è indicizzata per CID, quindi un CID condiviso può portare una sola mappatura, e qualunque testo perda quella contesa diventa non estraibile
Quindi ogni istanza di glifo prende il proprio CID, e ogni mappatura è libera di essere composta da più unità di codice UTF-16. Un glifo senza testo significativo — un elemento decorativo, un glifo solo di posizionamento — viene mappato esplicitamente a U+200B, uno spazio a larghezza zero, così ogni CID ha un risultato di estrazione osservabile invece di una lacuna
uses
PDFium;
var
Pdf: TPdf;
Glyphs: TPdfCidGlyphs;
Options: TPdfCidFontOptions;
Report: TPdfCidFontReport;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'label.pdf';
Pdf.LoadDocument;
Pdf.PageNumber := 1;
// Una voce per glifo shaped: l'id del glifo, il testo che rappresenta,
// e il suo avanzamento e offset nello spazio testo
SetLength(Glyphs, 3);
Glyphs[0].GlyphID := 402; Glyphs[0].UnicodeText := 'ffi';
Glyphs[0].Advance := 18.4;
Glyphs[1].GlyphID := 71; Glyphs[1].UnicodeText := 'c';
Glyphs[1].Advance := 9.8;
Glyphs[2].GlyphID := 74; Glyphs[2].UnicodeText := 'e';
Glyphs[2].Advance := 9.2;
Options := TPdfCidFontOptions.Default; // subset preferito
Options.VerifyExtraction := True;
if Pdf.AddCidType2Text(LoadFileBytes('NotoSans.ttf'), Glyphs,
11, 72, 700, Options, Report) then
Writeln(Format('%d glyphs, %d unique, font %d -> %d bytes, match=%s',
[Report.GlyphCount, Report.UniqueGlyphCount,
Report.OriginalFontBytes, Report.EmbeddedFontBytes,
BoolToStr(Report.ExtractionMatches, True)]));
finally
Pdf.Free;
end;
end;
Il subsetting, e i controlli che un codice di ritorno non vi dà
Ridurre un font ai soli glifi effettivamente usati è la differenza tra incorporare 300 KB e incorporare 12 KB, e su un documento con più font questo decide se il file è inviabile via email. Il percorso di subsetting della piattaforma accetta direttamente un elenco di glifi, il che si adatta perfettamente a questa API dato che il chiamante conosce già ogni identificatore di glifo in uso
Ciò che non vi dà è la fiducia. Una chiamata di subsetting può riportare successo e restituire un output inutilizzabile, quindi il componente valida tre proprietà prima di accettare il risultato: l'output deve essere più piccolo dell'originale, deve ri-analizzarsi come un sfnt valido con una tabella maxp leggibile, e deve conservare i glifi fino al più alto identificatore originale richiesto. Qualsiasi fallimento significa che il subset viene rifiutato
Ciò che accade dopo è la policy del chiamante. Sotto pcfemSubsetPreferred, il valore predefinito, un subset rifiutato ricade sull'incorporamento del font completo, così la pagina è corretta e semplicemente più grande. Sotto pcfemSubsetRequired, l'operazione fallisce prima che la pagina venga modificata, il che è ciò che vuole una pipeline vincolata dalla dimensione. pcfemFull salta del tutto il subsetting. Il report vi dice quale percorso è stato preso tramite SubsetAttempted, SubsetApplied, UsedFullFontFallback e SubsetErrorCode
Una verifica che non può produrre un falso positivo
Con VerifyExtraction abilitato, il componente conferma che ciò che ha scritto può essere riletto. Il modo ingenuo di farlo è estrarre l'intera pagina e cercare la stringa attesa, ed è sbagliato: una pagina che già conteneva quel testo supera il controllo anche quando il nuovo testo è stato scritto in modo scorretto
Invece la pagina di testo viene ricostruita e gli handle degli oggetti inseriti da questa chiamata vengono letti individualmente, nell'ordine di inserimento, e concatenati. Il risultato viene confrontato con il testo atteso, e il report espone entrambe le stringhe insieme al booleano, così una discrepanza può essere diagnosticata anziché semplicemente rilevata
Attivatelo durante lo sviluppo e in qualsiasi pipeline in cui l'estraibilità sia un requisito — archivi ricercabili, conformità di accessibilità, text mining a valle. Il costo è una ricostruzione della pagina di testo per chiamata, motivo per cui non è attivo per impostazione predefinita nei cicli stretti
Budget e fallimento atomico
I byte del font, le istanze di glifo e le unità di codice Unicode sono ciascuna limitate prima dell'allocazione, e il formato del font, l'indice TTC, l'intervallo di identificatori di glifo e i valori di geometria vengono validati prima che venga scritto qualsiasi cosa. La geometria deve essere finita, il che suona ovvio finché un motore di shaping non vi consegna un avanzamento NaN da un font malformato
Il fallimento è atomico a livello di pagina. Se il caricamento del font, la scrittura degli oggetti, la generazione del contenuto o la verifica dell'estrazione falliscono, ogni oggetto inserito da quella chiamata viene rimosso in ordine inverso e il contenuto della pagina viene rigenerato. Una chiamata fallita lascia la pagina com'era, non con metà di un text run sopra
Dove questo si colloca in una pipeline di testo
Vale la pena affermare chiaramente la divisione del lavoro. Lo shaping — trasformare i caratteri in glifi posizionati — non è compito di questa API; appartiene a un motore di shaping, e il supporto bidirezionale e per script complessi del componente è trattato in gestione di emoji, CJK e coppie surrogate. Questa API è ciò che chiamate dopo, con il run di glifi che lo shaper ha prodotto
Per il normale testo latino senza shaping contestuale, le API di testo più semplici basate su stringa sono lo strumento giusto e producono codice più snello. Ricorrete alla scrittura CID Type 2 quando avete output già shaped, quando avete bisogno di un controllo esatto degli identificatori di glifo, o quando il font deve essere incorporato a partire da byte che possedete anziché risolto per nome — l'alternativa di risoluzione del font essendo il meccanismo di provider descritto in controllare la sostituzione dei font PDF
Una nota di distribuzione: incorporare un font è una questione di licenza tanto quanto una questione tecnica. I font variano nel se l'incorporamento sia permesso affatto, permesso solo per la visualizzazione, o permesso per la modifica. La libreria incorporerà qualsiasi byte gli forniate, e verificare la licenza è responsabilità vostra, non del formato file
La scrittura di testo a livello di glifo, il provisioning dei font e l'estrazione del testo condividono lo stesso modello di pagina in Delphi, C++Builder e Lazarus; l'API completa è descritta nella pagina di PDFium Component per Delphi