Teknisk artikel

Validera PDF/raster-skannade dokument i Delphi

PDF/R, standardiserat som ISO 23504-1, är PDF-profilen för skannade dokument: varje sida bär exakt en remsbild och ingenting annat. PDFium Component validerar den från Delphi, Lazarus och C++Builder genom ValidatePdfRCompliance, som läser en ström och returnerar överensstämmelsenivån plus en mängd konkreta issues

Profilen existerar eftersom skannrar och dokumentfångstsystem behövde ett mål smalare än PDF/A. En arkiv-PDF får innehålla vad delen än tillåter; en raster-PDF är avsiktligt avmagrad, så att varje överensstämmande läsare kan visa den identiskt och varje överensstämmande skrivare kan producera den från en skanning utan en författarmotor

Vad förbjuder PDF/R som PDF/A tillåter?

Text, i praktiken. En raster-sida bär den skannade bilden och ingenting annat, så en teckensnittsresurs på en sida är en överträdelse — rapporterad som pvriFontForbidden under ISO 23504-1 §6.5.2. Det överraskar folk som lägger till ett osynligt OCR-textlager för sökbarhet, vilket är en normal och användbar sak att göra i ett PDF/A-arbetsflöde och enkelt inte är PDF/R

Relationen sida-till-bild är lika strikt. §6.5.1 gör varje sida till exakt en remsbild, så pvriPageImageMismatch avfyras när bildantalet inte matchar sidantalet — en sida utan bild och en sida med två är båda icke-överensstämmande. Och pvriBadMediaBox rapporterar en sida vars MediaBox inte har formen [0 0 w h] (§6.5.3), eftersom en skanning inte har någon anledning att sitta vid en förskjuten origin

uses FPdfPdfr;

var
  Src: TFileStream;
  Res: TPdfRValidationResult;
begin
  Src := TFileStream.Create('scan-batch-0142.pdf', fmOpenRead or fmShareDenyWrite);
  try
    Res := ValidatePdfRCompliance(Src);
    if Res.IsCompliant then
      Memo1.Lines.Add('PDF/R-1 conformant')
    else
    begin
      if pvriFontForbidden in Res.Issues then
        Memo1.Lines.Add('A page names a font resource; a raster page carries no text');
      if pvriPageImageMismatch in Res.Issues then
        Memo1.Lines.Add('Image count does not match page count');
      if pvriForbiddenImageFilter in Res.Issues then
        Memo1.Lines.Add('A strip image uses an encoding outside the white list');
    end;
  finally
    Src.Free;
  end;
end;

Vilka bildkodningar som är tillåtna

Fyra, och vitlistan är kort av en anledning. §6.6 medger /CCITTFaxDecode, /DCTDecode, /JPXDecode och /FlateDecode — två-nivå fax, JPEG, JPEG 2000 och förlustfri deflate, vilka mellan dem täcker varje skannerutdata som betyder något. Allt annat rapporteras som pvriForbiddenImageFilter, inklusive /LZWDecode, /RunLengthDecode, /ASCII85Decode, /ASCIIHexDecode, /JBIG2Decode och /Crypt

Två av dessa förkastanden är värda att förstå i stället för att memorera. /JBIG2Decode komprimerar två-nivå-skanningar extremt bra och är fullt lagligt i PDF/A, men dess symboldictionary-rekonstruktion kan byta ut visuellt lika glyfer — ett dokumenterat feltillstånd för skannade siffror — och en profil vars hela syfte är trogen rasterreproduktion kan inte medge den risken. ASCII-filterna exkluderas av motsatt anledning: de blåser upp filen utan att lägga till någonting en rasterprofil behöver

Strukturregler som avfyras innan någon sida läses

PDF/R begränsar även containern. pvriObjStmPresent rapporterar en /Type /ObjStm-ström, vilken profilen förbjuder rakt av — objektströmmar komplicerar den enkla, sekventiella tolkning som en rasterläsare är avsedd att kunna utföra. pvriBadHeader rapporterar en header utanför %PDF-1.4 till 1.7 och %PDF-2.0, och pvriEncryptVersionMismatch rapporterar en krypterad fil vars header inte är %PDF-2.0, enligt §6.2.3

Katalogen och Info-ordboken är vitlistade, inte bara kontrollerade. pvriProhibitedCatalogEntry och pvriProhibitedInfoEntry avfyras för poster utanför den tillåtna mängden, och pvriInfoXmpMismatch avfyras när en Info-post inte stämmer med sin XMP-motsvarighet. En saknad katalog /Metadata-ström, en saknad trailer /ID och en frånvarande %PDF-raster-1.0-sidfotmarkör har var och en sitt eget issue likaså

Varför sparalternativsposten utelämnar Title och Author

TPdfRSaveOptions bär Creator, Producer, CreationDate, ModDate, DocumentId och InstanceId, och har avsiktligt inget fält för Title, Author, Subject eller Keywords. De fyra är de poster §6.4.3 förbjuder, så en post som exponerade dem skulle bjuda in anropare att skriva en icke-överensstämmande fil genom ett överensstämmande API

Två booleska optioner styr upprensning vid konvertering av en befintlig PDF. StripInfoOptionalEntries är som standard True och tar bort Title, Author, Subject, Keywords och Trapped från källans Info-ordbok. StripCatalogOptionalEntries är också som standard True och tar bort Names, Outlines, StructTreeRoot, OutputIntents, Lang och resten, vilket lämnar endast §6.3-vitlistan. Sätt endera till False så behåller du posterna — och förlorar överensstämmelse, vilket ibland är vad en anropare genuint vill för en intern fil

var
  Opts: TPdfRSaveOptions;
  Src, Dest: TFileStream;
begin
  Opts := TPdfRSaveOptions.Default;
  Opts.Creator := 'Capture Station 4';
  Opts.Producer := 'PDFium Component';
  Src := TFileStream.Create('scan-in.pdf', fmOpenRead or fmShareDenyWrite);
  try
    Dest := TFileStream.Create('scan-pdfr.pdf', fmCreate);
    try
      InjectPdfRMarkers(Src, Dest, Opts);   // markers + metadata, not page content
    finally
      Dest.Free;
    end;
  finally
    Src.Free;
  end;
end;

Notera vad märkinjektion inte gör: den lägger till metadata och identifiering, och kan inte tillhandahålla sidinnehåll. En källsida som bär ingen remsbild kommer fortfarande misslyckas pvriPageImageMismatch efter injektion, eftersom den saknade bilden aldrig var ett metadataproblem

Var PDF/R passar i ett fångstflöde

Använd det där leveransen är själva skanningen och fidelity är hela kontraktet — bevisbilddokumentation, check- och betalningsfångst, ingenjörsritningsarkiv från en storformatsskanner. Använd PDF/A i stället i det ögonblick dokumentet behöver sökbar text, taggning, inbäddade bilagor eller något annat rasterprofilen tar bort

Ett vanligt och fungerande arrangemang är att producera båda: ett PDF/R-original som aldrig ändras, och en PDF/A-derivat med ett OCR-lager för hämtning. Validatorerna är oberoende, så samma batchjobb kan kontrollera varje artefakt mot den profil den faktiskt gör anspråk på. För arkivsidan av det paret, se noterna om PDF/A-arkivefterlevnad och PDF/A-preflight-validering, och för tryckorienterad utdata genomgången av att validera tryckfärdiga PDF/X-dokument

PDFium Component för PDFium-motorn till Delphi, C++Builder och Lazarus med ett VCL-API och överensstämmelsevaliderare för PDF/A, PDF/X, PDF/E, PDF/UA och PDF/R — PDFium Component-produktsidan listar de standarder och IDE-versioner som stöds