PDF/E-1 to profil archiwalny dla dokumentów inżynieryjnych, a PDFlibPas implementuje go jako tryb autora, który włączasz przez SetPDFEMode, plus ograniczony preflight czytający strumienie zawartości operator po operatorze. Ten profil to nie PDF/A z inną etykietą: ma własną przestrzeń nazw identyfikacyjną, własne wymaganie metadanych cyklu życia i jedną regułę, która robi walidację zawartości ostrzejszą niż w jakimkolwiek profilu archiwalnym, jaki dotąd spotkałeś
Dokumentacja inżynieryjna to powód istnienia tego profilu. Zestaw rysunków, który za dwadzieścia lat ma być czytelny i dający się udowodnić jako niezmieniony, z historią rewizji, która przetrwa, i z kolorem, który na ploterze w innym budynku znaczy to samo. Te wymagania rodzą specyfikację, której żądania siedzą głównie poza zawartością stron, w metadanych i zarządzaniu kolorem, czyli dokładnie tam, gdzie generyczny pisarz PDF-ów je gubi
Własna identyfikacja, nie wariacja na temat PDF/A
Pierwsza rzecz do zrobienia dobrze: identyfikacji PDF/E-1 nie da się wyprodukować przez zaadaptowanie wzorca z PDF/A albo PDF/X. Używa odrębnej przestrzeni nazw XMP, http://www.aim.org/pdfe/ns/id/, a wartość wersji musi wystąpić w dwóch miejscach: jako wpis w informacji o dokumencie i jako kwalifikowana przestrzenią nazw właściwość XMP. Emitowanie samej właściwości XMP albo samego wpisu daje plik, który niesie zamiar, ale walidacji nie przechodzi
Output intent ma równie konkretny kształt. PDF/E-1 wymaga osadzonego profilu ICC z identyfikatorem podtypu ISO_PDFE1, a profil musi mieć liczbę składowych pasującą do rodziny kolorów urządzeniowych, której dokument faktycznie używa. To ostatnie zastrzeżenie to miejsce, gdzie implementacje po cichu się mylą, bo oznacza ono, że intent nie może być wybrany z góry i potem zignorowany
Dlaczego kolor urządzeniowy wymaga przeglądu całego dokumentu?
Bo przestrzenie kolorów kryją się w słownikach zasobów, do których skan poziomu strony nigdy nie zagląda. PDF/E-1 traktuje DeviceRGB i DeviceCMYK jako wzajemnie wykluczające się rodziny w obrębie dokumentu, więc walidacja profilu oznacza znajomość każdej urządzeniowej przestrzeni kolorów, z czego cokolwiek w pliku korzysta. Form XObject ma własne zasoby. Pattern też, i obraz też. Tiling pattern wewnątrz form XObject wewnątrz strony leży trzy poziomy w głąb, a walidator sprawdzający tylko zasoby strony najwyższego poziomu przepuści dokument używający obu rodzin
Przegląd rejestruje więc przestrzenie kolorów w czasie jednego przejścia po stronach, formularzach, obrazach i patternach i dopiero potem rozstrzyga, czy dokument jest spójny i czy output intent pasuje. To samo rozumowanie napędza całą architekturę preflightu: częściowe przejście produkuje fałszywe zaliczenia, a fałszywe zaliczenie kontroli zgodności jest gorsze niż jej brak, bo zostaje zapisane jako dowód
var
Lib: TPDFlib;
Diag: WideString;
begin
Lib := TPDFlib.Create(nil);
try
Lib.LoadFromFile('assembly-drawings.pdf');
if Lib.SetPDFEMode(1) = 0 then
raise Exception.Create('PDF/E author mode was refused');
// Tryb autora trzyma metadane cyklu życia w ryzach przy każdym zapisie.
// Zapytaj przed zapisem, czy dokument przeszedłby własną bramkę
if not Lib.PDFEReadyForSave then
begin
Diag := Lib.GetPDFEDiagnostics;
Writeln('PDF/E blockers: ', Diag);
Exit;
end;
Lib.SaveToFile('assembly-drawings-pdfe.pdf');
finally
Lib.Free;
end;
end;
Metadane cyklu życia to obowiązek przy każdym zapisie
PDF/E-1 żąda więcej niż identyfikatora dokumentu. Minimalny zestaw obejmuje identyfikator dokumentu z zarządzania mediami, identyfikator wersji, klasę rendition, czas utworzenia, czas modyfikacji, czas metadanych i tytuł. To słownictwo śledzenia rewizji i istnieje, bo od dokumentacji inżynieryjnej oczekuje się ponownych wydań, a nie napisania raz na zawsze
Konsekwencja dla implementacji: tych pól nie da się ustawić przy tworzeniu dokumentu. Jeśli czas modyfikacji zostanie wpisany w momencie włączenia trybu, a dokument jest potem edytowany, migawka XMP i rzeczywisty stan dokumentu rozjadą się, a walidator porównujący oba zgłosi niespójność, której nikt nie zamierzał. Tryb autora synchronizuje więc pola bezpośrednio przed każdym zapisem, dzięki czemu metadane opisują bajty, które właśnie trafią na dysk, a nie te, które istniały w chwili włączania trybu
To ogólna zasada dla metadanych zgodności i warto ją wypowiedzieć osobno od PDF/E: metadane wyliczane należą na ścieżkę zapisu, nie na ścieżkę edycji. Każde pole liczone ze stanu dokumentu musi być przeliczone w momencie zamrożenia stanu, inaczej jest to cache bez inwalidacji
Reguła, która robi walidację zawartości ostrą
PDF/E-1 nie pozwala, by operatory sekcji zgodności wchłaniały nieznaną zawartość. W zwykłym PDF-ie BX i EX wyznaczają obszar, w którym konsument ma ignorować operatory, których nie rozpoznaje — to właz ewakuacyjny pozwalający producentowi emitować nowsze konstrukcje bez łamania starszych czytników. Pod PDF/E-1 ten właz jest zamknięty, więc każdy operator nierozpoznany przez preflight jest zgłaszany bezwarunkowo, niezależnie od tego, czy siedzi w sekcji zgodności
Efekt na walidator jest poważny. Nie może pomijać regionów, których nie rozumie, co znaczy, że parser operandów musi naprawdę sparsować każdy operator w każdym strumieniu zawartości. Tu wchodzą na scenę limity. Przejście jest capowane na 128 poziomach zagnieżdżenia, milionie obiektów i 64 MiB zawartości, i te limity to nie strojenie wydajności. Wrogi albo po prostu uszkodzony plik potrafi podać graf obiektów z cyklami albo z głębokością zagnieżdżenia, która zamienia rekurencyjny walidator w przepełnienie stosu, i to limity pilnują, by przebieg walidacji nie stał się wektorem denial-of-service. Tę samą postawę obronną opisuje bezpieczne parsowanie niezaufanych PDF-ów
// Samodzielna walidacja pliku, którego nie produkowałeś, bez ładowania
// go do instancji dokumentu
var
Issues: TStringList;
Stream: TFileStream;
I: Integer;
begin
Issues := TStringList.Create;
Stream := TFileStream.Create('incoming.pdf', fmOpenRead or fmShareDenyWrite);
try
if CheckCompliancePDFE(Stream, '', 0, Issues) = 0 then
for I := 0 to Issues.Count - 1 do
Writeln('PDF/E: ', Issues[I]);
finally
Stream.Free;
Issues.Free;
end;
end;
Co bramka zapisu naprawia, a czego odmawia
Bramka dzieli swoją pracę na dwa etapy, a ten podział to sam w sobie użyteczny pomysł projektowy. Najpierw normalizuje to, co da się bezpiecznie naprawić: flagi drukowania adnotacji, flagi no-zoom i no-rotate na adnotacjach tekstowych oraz flagę generowania wyglądu w słowniku formularza. To ustawienia z jedną poprawną wartością pod tym profilem i bez żadnej zawartości informacyjnej, więc ciche poprawienie jest właściwe, a odmowa byłaby pedanterią
Potem sprawdza ograniczenia, których nie da się naprawić bez zmiany znaczenia dokumentu: wersję, identyfikację, szyfrowanie, output intent, spójność kolorów urządzeniowych i obecność dynamicznej zawartości formularzy. Dokument obalający choć jedno z nich zostaje odrzucony, bo wymyślenie output intentu czy dobranie rodziny kolorów w imieniu autora dałoby plik, który przechodzi walidację, a zawartość fałszywie prezentuje
Odczyt diagnostyki przez GetPDFEDiagnostics przed zapisem zamienia tę odmowę w listę działań zamiast nieudanej operacji. W potoku wsadowym wołaj ją na każdym dokumencie, loguj blockery per plik i kieruj porażki do kolejki, którą przegląda człowiek. To znacznie pożyteczniejsze niż zapis rzucający wyjątkiem, bo blockery zwykle kłębią się tematycznie: czterdzieści dokumentów obalonych przez ten sam brakujący output intent to jedna poprawka, nie czterdzieści
Wybór między profilami archiwalnymi
PDF/E-1 to właściwy target, gdy dostarczasz dokumentację inżynieryjną z cyklem rewizji, a konkretnie wtedy, gdy spójność kolorów urządzeniowych ma znaczenie, bo wydruk idzie na plotery i drukarki wielkoformatowe. PDF/A to właściwy target, gdy celem jest długoterminowa czytelność dokumentów w ogóle, i to profil z najszerszym wsparciem walidatorów. Te dwa nie są wymienne i dokument może spełniać jeden, a oblać drugi
Jeśli wybierasz, zacznij od tego, kto waliduje plik na końcu łańcucha. Narzędzia walidacji PDF/A są wszędzie, a odpowiadający im preflight w PDFlibPas jest opisany w artykule preflight PDF/A i PDF/UA. Walidacja PDF/E jest bardziej specjalistyczna i zwykle jest wymaganiem kontraktowym, a nie domyślnym. Gdy istniejące archiwum trzeba doprowadzić do profilu, dla którego nigdy nie było pisane, wzorcem do naśladowania jest ścieżka naprawy metadanych z konwersji do PDF/A z naprawą metadanych, i ten sam kształt działa tutaj: zidentyfikuj, napraw to, co bezpieczne, resztę odrzuć z listą
Tryb autora, ograniczony preflight zawartości i samodzielna kontrola zgodności są wszystkie częścią biblioteki PDF PDFlibPas dla Delphi, więc dokument można wyprodukować pod profilem, a potem niezależnie zweryfikować osobną ścieżką kodu — i to jest jedyny układ, któremu przy twierdzeniu o zgodności warto ufać