Technický článek

Validace skenovaných dokumentů PDF/raster v Delphi

PDF/R, standardizovaný jako ISO 23504-1, je profil PDF pro skenované dokumenty: každá stránka nese přesně jeden strip image a nic jiného. PDFium Component ho validuje z Delphi, Lazarus a C++Builder přes ValidatePdfRCompliance, který přečte stream a vrátí úroveň shody plus sadu konkrétních problémů

Profil existuje, protože skenery a systémy pro digitalizaci dokumentů potřebovaly cíl užší než PDF/A. Archivační PDF může obsahovat cokoliv, co část připouští; raster PDF je záměrně ochuzené, takže každý konformní čtenář ho zobrazí totožně a každý konformní writer ho vyrobí ze skenu bez authoring enginu

Co PDF/R zakazuje a PDF/A připouští?

V praxi text. Raster stránka nese skenovaný obrázek a nic jiného, takže font resource na stránce je porušení — hlášeno jako pvriFontForbidden podle ISO 23504-1 §6.5.2. To překvapí lidi, kteří přidávají neviditelnou OCR textovou vrstvu pro prohledavatelnost, což je v rouře PDF/A normální a užitečné, ale prostě to není PDF/R

Vztah stránky k obrázku je stejně přísný. §6.5.1 dělá každou stránku přesně jedním strip image, takže pvriPageImageMismatch spouští, kdy počet obrázků neodpovídá počtu stránek — stránka bez obrázku i stránka se dvěma jsou obě nekonformní. A pvriBadMediaBox hlásí stránku, jejíž MediaBox nemá formu [0 0 w h] (§6.5.3), protože sken nemá důvod sedět na posunuté 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;

Které kódování obrázků jsou povolena

Čtyři a white list je krátký z dobrého důvodu. §6.6 připouští /CCITTFaxDecode, /DCTDecode, /JPXDecode a /FlateDecode — bilevel fax, JPEG, JPEG 2000 a lossless deflate, které dohromady pokrývají každý scanner výstup, na kterém záleží. Vše ostatní je hlášeno jako pvriForbiddenImageFilter, včetně /LZWDecode, /RunLengthDecode, /ASCII85Decode, /ASCIIHexDecode, /JBIG2Decode a /Crypt

Dvě z těchto odmítnutí stojí za to pochopit spíše než memorovat. /JBIG2Decode komprimuje bilevel skeny mimořádně dobře a je v PDF/A naprosto legální, ale rekonstrukce jeho slovníku symbolů může nahradit vizuálně podobné glyphy — zdokumentovaný režim selhání u skenovaných číslic — a profil, jehož celým smyslem je věrná raster reprodukce, nemůže to riziko připustit. ASCII filtry jsou vyloučeny z opačného důvodu: nadýmají soubor bez toho, aby přinesly cokoliv, co by raster profil potřeboval

Strukturní pravidla, která spouští dříve, než se přečte jakákoliv stránka

PDF/R omezuje i kontejner. pvriObjStmPresent hlásí stream /Type /ObjStm, který profil rovnou zakazuje — object streamy komplikují jednoduché, sekvenční parsování, které má raster čtenář umět provádět. pvriBadHeader hlásí hlavičku mimo %PDF-1.4 až 1.7 a %PDF-2.0 a pvriEncryptVersionMismatch hlásí šifrovaný soubor, jehož hlavička není %PDF-2.0, podle §6.2.3

Slovník katalogu a Info jsou na white listu, ne jen kontrolovány. pvriProhibitedCatalogEntry a pvriProhibitedInfoEntry spouští pro položky mimo povolenou sadu a pvriInfoXmpMismatch spouští, když položka Info nesouhlasí se svým XMP ekvivalentem. Chybějící stream /Metadata katalogu, chybějící /ID v traileru a chybějící footer marker %PDF-raster-1.0 mají každý vlastní problém

Proč záznam save options vynechává Title a Author

TPdfRSaveOptions nese Creator, Producer, CreationDate, ModDate, DocumentId a InstanceId a záměrně nemá pole pro Title, Author, Subject ani Keywords. Tyto čtyři jsou položky, které §6.4.3 zakazuje, takže záznam, který by je vystavoval, by zval volající k zápisu nekonformního souboru přes konformní API

Dvě booleovské volby řídí úklid při převodu existujícího PDF. StripInfoOptionalEntries má výchozí True a odstraňuje ze zdrojového Info slovníku Title, Author, Subject, Keywords a Trapped. StripCatalogOptionalEntries má rovněž výchozí True a odstraňuje Names, Outlines, StructTreeRoot, OutputIntents, Lang a zbytek, přičemž ponechává jen §6.3 white list. Nastavte kteroukoliv na False a položky si ponecháte — a přijdete o shodu, což občas volající pro interní soubor skutečně chce

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;

Všimněte si, co injekce markerů nedělá: přidává metadata a identifikaci a nedokáže dodavat page content. Zdrojová stránka, která nenese strip image, po injekci stále selže na pvriPageImageMismatch, protože chybějící obrázek nebyl nikdy problémem metadat

Kam PDF/R patří v rouře digitalizace

Použijte ho tam, kde je výstupem samotný sken a věrnost je celý kontrakt — evidenciční snímkování, digitalizace šeků a plateb, archivy inženýrských výkresů z velkoformátového skeneru. Použijte PDF/A místo něj ve chvíli, kdy dokument potřebuje prohledávatelný text, značkování, vložené přílohy nebo cokoliv jiného, co raster profil odstraňuje

Běžné a funkční uspořádání je produkovat obojí: PDF/R originál, který se nikdy nemění, a PDF/A derivát s OCR vrstvou pro vyhledávání. Validátory jsou nezávislé, takže stejná dávková úloha může zkontrolovat každý artefakt proti profilu, který skutečně deklaruje. Pro archivační stranu tohoto páru viz poznámky k archivační shodě PDF/A a preflight validaci PDF/A a pro výstup orientovaný na tisk průvodce validací tiskově připravených PDF/X dokumentů

PDFium Component přináší engine PDFium do Delphi, C++Builder a Lazarus s VCL API a validátory shody pro PDF/A, PDF/X, PDF/E, PDF/UA a PDF/R — stránka produktu PDFium Component uvádí podporované standardy a verze IDE