Articolo tecnico

Limiti di implementazione PDF/A e controlli di codifica dei font

PDFium Component convalida i limiti di implementazione di ISO 19005-1 Annex C — token di nome da 127 byte, 8191 elementi di array, 4095 voci di dizionario e 28 livelli di annidamento dei contenitori — e segnala un font TrueType simbolico che porta una voce /Encoding. Entrambi i controlli girano sul percorso di byte-scan, così un'applicazione Delphi o Lazarus ottiene il verdetto senza caricare affatto la DLL di PDFium

Questi sono i fallimenti che sconcertano di più le persone, perché il documento sembra a posto. Si renderizza, si stampa, ogni font è incorporato, l'output intent è presente. Poi un validatore lo rifiuta per un dizionario che ha 4096 voci, e nulla nel documento visibile spiega perché

Cosa proteggono davvero i limiti di Annex C?

L'interoperabilità con implementazioni che precedono il tuo generatore. Annex C porta avanti i limiti di implementazione del PDF Reference in ogni parte PDF/A, e i numeri non sono arbitrari — descrivono cosa un lettore conforme era storicamente tenuto a gestire. Un file che li supera può aprirsi perfettamente in un visualizzatore moderno e fallire nel lettore archivistico su cui un sistema di record si è standardizzato quindici anni fa, il che è esattamente lo scenario che PDF/A esiste per prevenire

I quattro limiti sono inclusivi. Un token di nome di esattamente 127 byte convalida; 128 no. Un array con esattamente 8191 elementi convalida; 8192 no. PDFium Component fissa entrambi i lati di ogni confine nella propria suite di test per quel motivo, perché un off-by-one in un controllo dei limiti produce il peggiore tipo di validatore: uno che rifiuta file conformi e a cui si crede comunque

uses FPdfPdfa;

var
  Src: TFileStream;
  Res: TPdfAValidationResult;
begin
  Src := TFileStream.Create('archive.pdf', fmOpenRead or fmShareDenyWrite);
  try
    Res := ValidatePdfACompliance(Src);
    if pvaiArrayOverLimit in Res.Issues then
      Memo1.Lines.Add('An array carries more than 8191 elements');
    if pvaiDictOverLimit in Res.Issues then
      Memo1.Lines.Add('A dictionary carries more than 4095 entries');
    if pvaiNestingOverLimit in Res.Issues then
      Memo1.Lines.Add('Containers nest deeper than 28 levels');
    if pvaiNameOverLimit in Res.Issues then
      Memo1.Lines.Add('A name token is longer than 127 bytes');
  finally
    Src.Free;
  end;
end;

Quali generatori colpiscono davvero questi limiti?

Quelli che costruiscono struttura in modo programmatico, il che è la maggior parte dell'output line-of-business. Un modulo con diverse migliaia di campi produce un array /Annots o un array AcroForm /Fields che cresce oltre 8191. Una pagina il cui dizionario risorse accumula una voce per ogni immagine o istanza di font generata supera 4095. Alberi di struttura generati in profondità — un documento tagged costruito per ricorsione su un modello dati annidato — oltrepassano i 28 livelli senza che nessuno se ne accorga, perché nessuno guarda la profondità di annidamento

I nomi lunghi vengono da un'abitudine diversa: codificare dati dentro token di nome. Un nome di colorante costruito da un identificatore cliente, un gruppo di contenuto opzionale chiamato come un percorso file completo, un campo di modulo il cui nome pienamente qualificato concatena sei livelli di gerarchia. I nomi sono economici da generare e facili da allungare, e 127 byte spariscono più in fretta di quanto crederesti una volta che entra in gioco un'etichetta codificata UTF-8

La correzione è strutturale in ogni caso. Splitta l'array, splitta il dizionario, appiattisci l'annidamento, accorcia il nome — la raccomandazione di preflight per ciascun problema nomina il limite concreto anziché dirti che il file non è valido. L'iniezione di marker qui non può aiutare: non sono rivendicazioni di metadati, sono la forma del grafo degli oggetti

Perché un font TrueType simbolico non deve portare /Encoding

Perché ISO 19005-1 §6.3.7 ammette solo la cmap incorporata nel font per i font TrueType simbolici, e una voce /Encoding la contraddirebbe. Un font simbolico mappa i codici ai glifi nei propri termini — è questo che significa simbolico. Aggiungi una tabella di encoding e ci sono ora due risposte alla domanda «quale glifo seleziona il byte 0x41», senza alcuna regola nel file che dica quale vinca. Lettori diversi lo risolvono in modo diverso, e un documento che si renderizza come testo in un visualizzatore si renderizza come dingbat in un altro

PDFium Component legge il flag simbolico dal /FontDescriptor, sia che il descrittore sia scritto inline nel dizionario del font sia che sia referenziato indirettamente. Un font TrueType non simbolico conserva la propria /WinAnsiEncoding o /MacRomanEncoding obbligatoria senza essere flaggato, perché per i font non simbolici l'encoding è esattamente ciò che lo standard chiede. Il controllo scatta sulla contraddizione, non sulla presenza di un encoding

if pvaiSymbolicTrueTypeEncoding in Res.Issues then
  Memo1.Lines.Add(
    'A symbolic TrueType font carries /Encoding; PDF/A admits only its ' +
    'built-in cmap (ISO 19005-1 6.3.7)');

La fonte pratica di questo difetto è il subsetting dei font fatto da un producer che tratta ogni font TrueType allo stesso modo. Symbol, Wingdings, font barcode e font icona sono i portatori abituali — esattamente i font che un documento aziendale usa per checkbox, logo e codici a barre, ed esattamente quelli che nessuno riesamina quando un documento fallisce la validazione per via dei font

Come i problemi arrivano in un report di preflight

I quattro limiti del contenitore sono classificati sotto struttura; il problema dell'encoding TrueType simbolico è classificato sotto contenuto. Quella divisione conta quando un report va a due persone diverse: i risultati di struttura appartengono di solito a chi ha scritto il generatore, e i risultati di contenuto appartengono di solito a chi ha fornito le risorse

Ciascun problema porta una raccomandazione che nomina il rimedio in termini concreti — accorcia i token di nome a 127 byte o meno, splitta gli array così che nessuno porti più di 8191 elementi, rimuovi /Encoding dai font TrueType simbolici. Un report che dice «non conforme a PDF/A» avvia un'indagine. Un report che dice quale limite è stato superato e da cosa ne conclude una

Validare senza la DLL, e perché qui conta

Tutti i controlli sopra girano contro i byte del file, così funzionano in un servizio che non ha alcun binario PDFium distribuito, in uno step di build, o su una macchina dove caricare una DLL nativa è un problema di policy. È una linea di progettazione deliberata in PDFium Component: i controlli che si possono rispondere dalla struttura si rispondono dalla struttura, e la DLL è riservata a quelli che hanno genuinamente bisogno di un motore di rendering

Per il flusso circostante — eseguire la validazione su una cartella, produrre report e decidere cosa fare dei risultati — vedi le guide sulla validazione preflight PDF/A in Delphi e sulla CLI batch di report di preflight. Per la scelta del profilo archivistico che sta sopra tutti questi controlli, le note sulla conformità archivistica PDF/A coprono quale parte e livello scegliere prima di iniziare a sistemare i risultati

PDFium Component avvolge il motore PDFium per Delphi, C++Builder e Lazarus con un'API VCL di alto livello e un insieme di validatori di conformità che girano con o senza la DLL — vedi la pagina prodotto PDFium Component per gli standard supportati e le piattaforme