PDF/R, standardisiert als ISO 23504-1, ist das PDF-Profil für gescannte Dokumente: Jede Seite trägt exakt ein Streifenbild und nichts sonst. PDFium Component validiert es aus Delphi, Lazarus und C++Builder über ValidatePdfRCompliance, das einen Stream liest und das Konformitäts-Level plus eine Menge konkreter Issues zurückgibt
Das Profil existiert, weil Scanner und Dokument-Erfassungssysteme ein Ziel brauchten, das enger ist als PDF/A. Ein Archiv-PDF darf alles enthalten, was der Part zulässt; ein Raster-PDF ist bewusst verarmt, sodass jeder konforme Reader es identisch anzeigen und jeder konforme Writer es aus einem Scan ohne Autoren-Engine erzeugen kann
Was verbietet PDF/R, das PDF/A erlaubt?
In der Praxis Text. Eine Raster-Seite trägt das gescannte Bild und nichts sonst, sodass eine Schrift-Ressource auf einer Seite ein Verstoß ist — gemeldet als pvriFontForbidden unter ISO 23504-1 §6.5.2. Das überrascht Leute, die eine unsichtbare OCR-Textschicht für Durchsuchbarkeit hinzufügen, was in einem PDF/A-Workflow eine normale und nützliche Sache ist und einfach kein PDF/R ist
Die Seite-zu-Bild-Beziehung ist ebenso streng. §6.5.1 macht jede Seite zu exakt einem Streifenbild, sodass pvriPageImageMismatch feuert, wenn die Bildanzahl nicht zur Seitenanzahl passt — eine Seite ohne Bild und eine Seite mit zwei sind beide nicht-konform. Und pvriBadMediaBox meldet eine Seite, deren MediaBox nicht die Form [0 0 w h] hat (§6.5.3), denn ein Scan hat keinen Grund, an einem verschobenen Ursprung zu sitzen
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;
Welche Bild-Enkodierungen erlaubt sind
Vier, und die White-List ist aus gutem Grund kurz. §6.6 lässt /CCITTFaxDecode, /DCTDecode, /JPXDecode und /FlateDecode zu — bi-level Fax, JPEG, JPEG 2000 und verlustfreies Deflate, die zusammen jede Scanner-Ausgabe abdecken, die zählt. Alles andere wird als pvriForbiddenImageFilter gemeldet, einschließlich /LZWDecode, /RunLengthDecode, /ASCII85Decode, /ASCIIHexDecode, /JBIG2Decode und /Crypt
Zwei dieser Zurückweisungen lohnt sich zu verstehen statt auswendig zu lernen. /JBIG2Decode komprimiert bi-level Scans extrem gut und ist in PDF/A völlig legal, doch seine Symbol-Wörterbuch-Rekonstruktion kann visuell ähnliche Glyphen substituieren — ein dokumentierter Fehlermodus für gescannte Ziffern — und ein Profil, dessen ganzer Sinn treue Raster-Wiedergabe ist, kann dieses Risiko nicht aufnehmen. Die ASCII-Filter sind aus dem entgegengesetzten Grund ausgeschlossen: Sie blähen die Datei auf, ohne etwas hinzuzufügen, das ein Raster-Profil braucht
Struktur-Regeln, die feuern, bevor eine Seite gelesen wird
PDF/R schränkt auch den Container ein. pvriObjStmPresent meldet einen /Type /ObjStm-Stream, den das Profil rundheraus verbietet — Objekt-Streams komplizieren das einfache, sequenzielle Parsen, das ein Raster-Reader durchführen können soll. pvriBadHeader meldet einen Header außerhalb %PDF-1.4 bis 1.7 und %PDF-2.0, und pvriEncryptVersionMismatch meldet eine verschlüsselte Datei, deren Header nicht %PDF-2.0 ist, gemäß §6.2.3
Das Catalog- und das Info-Dictionary werden nicht nur geprüft, sondern gewhitelistet. pvriProhibitedCatalogEntry und pvriProhibitedInfoEntry feuern für Einträge außerhalb der zulässigen Menge, und pvriInfoXmpMismatch feuert, wenn ein Info-Eintrag von seinem XMP-Äquivalent abweicht. Ein fehlender Catalog-/Metadata-Stream, ein fehlender Trailer-/ID und ein abwesender %PDF-raster-1.0-Footer-Marker haben jeweils ihr eigenes Issue
Warum der Save-Options-Datensatz Title und Author auslässt
TPdfRSaveOptions trägt Creator, Producer, CreationDate, ModDate, DocumentId und InstanceId und hat bewusst kein Feld für Title, Author, Subject oder Keywords. Diese vier sind die Einträge, die §6.4.3 verbietet, also würde ein Datensatz, der sie freilegte, Aufrufer einladen, eine nicht-konforme Datei über eine konforme API zu schreiben
Zwei boolesche Optionen steuern die Bereinigung beim Konvertieren eines bestehenden PDF. StripInfoOptionalEntries ist standardmäßig wahr und entfernt Title, Author, Subject, Keywords und Trapped aus dem Quell-Info-Dictionary. StripCatalogOptionalEntries ist ebenfalls standardmäßig wahr und entfernt Names, Outlines, StructTreeRoot, OutputIntents, Lang und den Rest und lässt nur die White-List aus §6.3 stehen. Beide auf False setzen und man behält die Einträge — und verliert die Konformität, was gelegentlich das ist, was ein Aufrufer für eine interne Datei genuine will
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;
Beachten, was Marker-Injection nicht tut: Sie fügt Metadaten und Identifikation hinzu und kann keinen Seiten-Inhalt liefern. Eine Quell-Seite, die kein Streifenbild trägt, wird nach der Injektion weiterhin an pvriPageImageMismatch scheitern, denn das fehlende Bild war nie ein Metadaten-Problem
Wo PDF/R in eine Erfassungs-Pipeline passt
Verwenden, wo das Lieferbare der Scan selbst ist und die Treue der ganze Vertrag ist — beweissichere Bildgebung, Scheck- und Zahlungserfassung, Konstruktionszeichnungs-Archive von einem Großformat-Scanner. Stattdessen PDF/A verwenden, sobald das Dokument durchsuchbaren Text, Tagging, eingebettete Anhänge oder irgendetwas anderes braucht, was das Raster-Profil abstreift
Eine verbreitete und praktikable Anordnung ist, beides zu erzeugen: ein PDF/R-Original, das sich nie ändert, und ein PDF/A-Derivat mit einer OCR-Schicht für den Abruf. Die Validatoren sind unabhängig, sodass derselbe Batch-Auftrag jedes Artefakt gegen das Profil prüfen kann, das es tatsächlich beansprucht. Für die Archiv-Seite dieses Paars siehe die Hinweise zur PDF/A-Archiv-Konformität und zur PDF/A-Preflight-Validierung, und für druckorientierte Ausgaben die Erläuterung zum Validieren druckfertiger PDF/X-Dokumente
PDFium Component bringt die PDFium-Engine zu Delphi, C++Builder und Lazarus mit einer VCL-API und Konformitäts-Validatoren für PDF/A, PDF/X, PDF/E, PDF/UA und PDF/R — die PDFium-Component-Produktseite listet die unterstützten Standards und IDE-Versionen