PDF/R, standaryzowany jako ISO 23504-1, to profil PDF dla dokumentów skanowanych: każda strona niesie dokładnie jeden obraz paskowy i nic więcej. PDFium Component waliduje go z Delphi, Lazarusa i C++Builder przez ValidatePdfRCompliance, który czyta strumień i zwraca poziom zgodności oraz zbiór konkretnych kwestii
Profil istnieje, ponieważ skanery i systemy przechwytywania dokumentów potrzebowały celu węższego niż PDF/A. Archiwalny PDF może zawierać wszystko, co dopuszcza część; PDF rastrowy jest celowo zubożały, tak by każdy zgodny czytnik wyświetlił go identycznie, a każdy zgodny zapisywacz wyprodukował go ze skanu bez silnika autorskiego
Czego PDF/R zabrania, a PDF/A dopuszcza?
Tekst, w praktyce. Strona rastrowa niesie zeskanowany obraz i nic więcej, więc zasób czcionki na stronie to naruszenie — raportowane jako pvriFontForbidden pod ISO 23504-1 §6.5.2. To zaskakuje ludzi, którzy dodają niewidzialną warstwę tekstu OCR dla przeszukiwalności, co jest normalną i pożyteczną rzeczą w przepływie PDF/A, a po prostu nie jest PDF/R
Relacja strony do obrazu jest równie surowa. §6.5.1 czyni każdą stronę dokładnie jednym obrazem paskowym, więc pvriPageImageMismatch odampa się, gdy liczba obrazów nie zgadza się z liczbą stron — strona bez obrazu i strona z dwoma są obie niezgodne. A pvriBadMediaBox raportuje stronę, której MediaBox nie ma postaci [0 0 w h] (§6.5.3), bo skan nie ma powodu siedzieć na przesuniętym początku układu
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;
Jakie kodowania obrazów są dopuszczone
Cztery, a biała lista jest krótka nie bez powodu. §6.6 dopuszcza /CCITTFaxDecode, /DCTDecode, /JPXDecode i /FlateDecode — faks dwupoziomowy, JPEG, JPEG 2000 i bezstratny deflate, które razem obejmują każde wyjście skanera mające znaczenie. Wszystko inne jest raportowane jako pvriForbiddenImageFilter, w tym /LZWDecode, /RunLengthDecode, /ASCII85Decode, /ASCIIHexDecode, /JBIG2Decode i /Crypt
Dwa z tych odrzuceń warto zrozumieć zamiast zapamiętać. /JBIG2Decode kompresuje skany dwupoziomowe skrajnie dobrze i jest doskonale legalny w PDF/A, ale jego rekonstrukcja słownika symboli może podstawić wizualnie podobne glify — udokumentowany tryb porażki dla zeskanowanych cyfr — a profil, którego całą celem jest wierne odtworzenie rastrowe, nie może dopuścić tego ryzyka. Filtry ASCII są wykluczone z przeciwnego powodu: pompują plik bez dodawania czegokolwiek, czego profil rastrowy potrzebuje
Reguły struktury, które odpalają, zanim jakakolwiek strona zostanie przeczytana
PDF/R ogranicza także kontener. pvriObjStmPresent raportuje strumień /Type /ObjStm, którego profil zabrania wprost — strumienie obiektów komplikują prosty, sekwencyjny parse, jaki czytnik rastrowy ma móc wykonać. pvriBadHeader raportuje nagłówek poza %PDF-1.4 do 1.7 oraz %PDF-2.0, a pvriEncryptVersionMismatch raportuje zaszyfrowany plik, którego nagłówek to nie %PDF-2.0, zgodnie z §6.2.3
Katalog i słownik Info są na białej liście, nie tylko sprawdzane. pvriProhibitedCatalogEntry i pvriProhibitedInfoEntry odampają się dla wpisów poza dozwolonym zestawem, a pvriInfoXmpMismatch, gdy wpis Info nie zgadza się ze swoim odpowiednikiem XMP. Brakujący strumień /Metadata katalogu, brakujący /ID w trailerze i nieobecny znacznik stopki %PDF-raster-1.0 mają też każdy własną kwestię
Dlaczego rekord opcji zapisu pomija Title i Author
TPdfRSaveOptions niesie Creator, Producer, CreationDate, ModDate, DocumentId i InstanceId, a celowo nie ma pola na Title, Author, Subject ani Keywords. Te cztery to wpisy, których §6.4.3 zabrania, więc rekord, który by je wystawiał, zapraszałby wywołujących do zapisu niezgodnego pliku przez zgodne API
Dwie opcje boolowskie kontrolują sprzątanie przy konwersji istniejącego PDF. StripInfoOptionalEntries domyślnie jest True i usuwa Title, Author, Subject, Keywords i Trapped ze źródłowego słownika Info. StripCatalogOptionalEntries też domyślnie jest True i usuwa Names, Outlines, StructTreeRoot, OutputIntents, Lang i resztę, zostawiając tylko białą listę §6.3. Ustaw którekolwiek na False, a zatrzymujesz wpisy — i tracisz zgodność, co czasem jest właśnie tym, czego wywołujący faktycznie chce dla pliku wewnętrznego
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;
Zauważ, czego wstrzykiwanie znaczników nie robi: dodaje metadane i identyfikację, a nie potrafi dostarczyć treści strony. Strona źródłowa, która nie niesie obrazu paskowego, wciąż zawiedzie pvriPageImageMismatch po wstrzyknięciu, bo brakujący obraz nigdy nie był problemem metadanych
Gdzie PDF/R się mieści w potoku przechwytywania
Używaj go tam, gdzie wynikiem jest sam skan, a wierność jest całym kontraktem — obrazowanie dowodowe, przechwytywanie czeków i wpłat, archiwa rysunków inżynieryjnych ze skanera wielkoformatowego. Używaj PDF/A zamiast tego w momencie, gdy dokument potrzebuje przeszukiwalnego tekstu, tagowania, osadzonych załączników albo czegokolwiek innego, co profil rastrowy obdiera
Typowym i wykonalnym układem jest produkcja obu: oryginału PDF/R, który nigdy się nie zmienia, i pochodnego PDF/A z warstwą OCR do pobierania. Walidatory są niezależne, więc to samo zadanie wsadowe może sprawdzić każdy artefakt wobec profilu, który faktycznie rości. Dla archiwalnej strony tej pary zobacz notatki o zgodności archiwalnej PDF/A i walidacji preflighitu PDF/A, a dla wyjścia zorientowanego na druk przewodnik po walidacji dokumentów PDF/X gotowych do druku
PDFium Component przynosi silnik PDFium do Delphi, C++Builder i Lazarus z API VCL i walidatorami zgodności dla PDF/A, PDF/X, PDF/E, PDF/UA i PDF/R — strona produktu PDFium Component wymienia obsługiwane normy i wersje IDE