Teknisk artikel

PDF/A-implementeringsgränser och teckensnittskodningskontroller

PDFium Component validerar implementeringsgränserna i ISO 19005-1 Annex C — 127-byte-namntoken, 8191 arrayelement, 4095 ordboksposter och 28 nivåer av container-nästling — och rapporterar ett symboliskt TrueType-teckensnitt som bär en /Encoding-post. Båda kontrollerna körs på byte-skanningsvägen, så en Delphi- eller Lazarus-applikation får utslaget utan att ladda PDFium-DLL:en alls

Det här är de fel som förbryllar folk mest, eftersom dokumentet ser bra ut. Det renderar, det skrivs ut, varje teckensnitt är inbäddat, utdatamåttet finns. Sedan förkastar en validator det över en ordbok som har 4096 poster, och ingenting i det synliga dokumentet förklarar varför

Vad skyddar Annex C-gränserna faktiskt?

Interoperabilitet med implementeringar som föregår din generator. Annex C för PDF Reference-implementeringsgränserna in i varje PDF/A-del, och siffrorna är inte godtyckliga — de beskriver vad en överensstämmande läsare historiskt krävdes hantera. En fil som överskrider dem kan öppna perfekt i en modern visare och misslyckas i den arkivläsare ett arkivsystem standardiserade på för femton år sedan, vilket är precis det scenario PDF/A existerar för att förhindra

De fyra gränserna är inklusiva. Ett namntoken på exakt 127 byte godkänns; 128 gör det inte. En array med exakt 8191 element godkänns; 8192 gör det inte. PDFium Component fäster båda sidor av varje gräns i sin testsvit av den anledningen, eftersom en off-by-one i en gränskontroll producerar den värsta sortens validator: en som förkastar överensstämmande filer och ändå troddes

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;

Vilka generatorer träffar faktiskt dessa gränser?

Sådana som bygger struktur programmatiskt, vilket är det mesta affärssystem-utdata. Ett formulär med flera tusen fält producerar en /Annots-array eller en AcroForm /Fields-array som växer förbi 8191. En sida vars resursordbok ackumulerar en post per genererad bild eller teckensnittsinstans korsar 4095. Djupt genererade strukturträd — ett taggat dokument byggt genom rekursion över en nästlad datamodell — går förbi 28 nivåer utan att någon märker det, eftersom ingen tittar på nästlingsdjup

Långa namn kommer från en annan vana: att koda data i namntoken. Ett färgämnesnamn byggt från en kundidentifierare, en valfritt-innehålls-grupp namngiven efter en full fil sökväg, ett formulärfält vars fullt kvalificerade namn konkatenerar sex nivåer av hierarki. Namn är billiga att generera och lätta att göra långa, och 127 byte försvinner snabbare än du tror när en UTF-8-kodad etikett är inblandad

Fixen är strukturell i varje fall. Dela arrayen, dela ordboken, platta ut nästlingen, förkorta namnet — preflight-rekommendationen för varje issue namnger den konkreta gränsen i stället för att säga att filen är ogiltig. Märkinjektion kan inte hjälpa här: dessa är inte metadataanspråk, de är formen på objektgrafen

Varför får ett symboliskt TrueType-teckensnitt inte bära /Encoding

Eftersom ISO 19005-1 §6.3.7 endast medger teckensnittets inbyggda cmap för symboliska TrueType-teckensnitt, och en /Encoding-post skulle motsäga det. Ett symboliskt teckensnitt mappar koder till glyfer på sina egna villkor — det är vad symboliskt betyder. Lägg till en kodningstabell och det finns nu två svar på frågan "vilken glyf väljer byte 0x41", utan regel i filen som säger vilken som vinner. Olika läsare löser det olika, och ett dokument som renderas som text i en visare renderas som dingbats i en annan

PDFium Component läser den symboliska flaggan från /FontDescriptor, oavsett huruvida beskrivaren skrivs inline i teckensnittsordboken eller refereras indirekt. Ett icke-symboliskt TrueType-teckensnitt behåller sin obligatoriska /WinAnsiEncoding eller /MacRomanEncoding utan att flaggas, eftersom för icke-symboliska teckensnitt är kodningen exakt vad standarden ber om. Kontrollen avfyras på motsägelsen, inte på närvaron av en kodning

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)');

Den praktiska källan till denna defekt är teckensnittsdelmängdskapande gjort av en producent som behandlar varje TrueType-teckensnitt på samma sätt. Symbol, Wingdings, streckkodsteckensnitt och ikonteckensnitt är de vanliga bärarna — exakt de teckensnitt ett affärsdokument använder för kryssrutor, logotyper och streckkoder, och exakt de ingen granskar om när ett dokument misslyckas validering över "teckensnitt"

Hur issues anländer i en preflight-rapport

De fyra containergränserna klassificeras under struktur; det symboliska TrueType-kodningsproblemet klassificeras under innehåll. Den delningen betyder något när en rapport går till två olika personer: strukturfynd tillhör vanligtvis den som skrev generatorn, och innehållsfynd tillhör vanligtvis den som tillhandahöll tillgångarna

Varje issue bär en rekommendation som namnger åtgärden i konkreta termer — förkorta namntoken till 127 byte eller färre, dela arrayer så ingen bär mer än 8191 element, ta bort /Encoding från symboliska TrueType-teckensnitt. En rapport som säger "inte PDF/A-överensstämmande" startar en utredning. En rapport som säger vilken gräns som överskreds och av vad avslutar en

Att validera utan DLL och varför det betyder något här

Alla kontrollerna ovan körs mot filbyten, så de fungerar i en tjänst som inte har någon PDFium-binär utplacerad, i ett byggsteg, eller på en maskin där att ladda en inbyggd DLL är ett policyproblem. Det är en avsiktig designlinje i PDFium Component: de kontroller som kan besvaras från struktur besvaras från struktur, och DLL:en reserveras för de som genuint behöver en renderingsmotor

För det omgivande arbetsflödet — köra validering över en mapp, producera rapporter och besluta vad man ska göra med fynden — se genomgångarna av PDF/A-preflight-validering i Delphi och batch-preflight-rapport-CLI:n. För arkivprofilvalet som sitter ovanför alla dessa kontroller täcker noterna om PDF/A-arkivefterlevnad vilken del och nivå du ska sikta på innan du börjar fixa fynd

PDFium Component sveper in PDFium-motorn för Delphi, C++Builder och Lazarus med ett hög-nivå VCL-API och en uppsättning överensstämmelsevaliderare som körs med eller utan DLL — se PDFium Component-produktsidan för de standarder och plattformar som stöds