PDFium Component waliduje limity implementacji ISO 19005-1 Załącznik C — tokeny nazw do 127 bajtów, 8191 elementów tablicy, 4095 wpisów słownika i 28 poziomów zagnieżdżenia kontenerów — oraz raportuje symboliczną czcionkę TrueType niosącą wpis /Encoding. Oba sprawdzenia biegną po ścieżce skanowania bajtów, więc aplikacja Delphi lub Lazarus dostaje werdykt bez ładowania DLL PDFium wcale
To są porażki, które frapują ludzi najbardziej, bo dokument wygląda dobrze. Renderuje się, drukuje, każda czcionka jest osadzona, intencja wyjścia jest obecna. Potem walidator odrzuca go z powodu słownika mającego 4096 wpisów, a nic w widocznym dokumencie nie wyjaśnia dlaczego
Co limity z Załącznika C faktycznie chronią?
Interoperacyjność z implementacjami starszymi niż Twój generator. Załącznik C przenosi limity implementacji z PDF Reference do każdej części PDF/A, a liczby nie są arbitralne — opisują to, co zgodny czytnik historycznie był zobowiązany obsłużyć. Plik, który je przekracza, może otwierać się idealnie w nowoczesnym viewerze i zawodzić w czytniku archiwalnym, na którym system ewidencji ustandaryzował się piętnaście lat temu, a to właśnie scenariusz, który PDF/A ma zapobiegać
Cztery limity są włącznie. Token nazwy o dokładnie 127 bajtach przechodzi walidację; 128 nie. Tablica o dokładnie 8191 elementach przechodzi; 8192 nie. PDFium Component przypina obie strony każdej granicy w swoim zestawie testów z tego powodu, bo błąd off-by-one w sprawdzeniu limitu produkuje najgorszy rodzaj walidatora: taki, który odrzuca zgodne pliki, a jest mu i tak wierzony
uses FPdfPdfa;
var
Src: TFileStream;
Res: TPdfAValidationResult;
begin
Src := TFileStream.Create('archive.pdf', fmOpenRead or fmShareDenyWrite);
try
Res := ValidatePdfACompliance(Src);
if pvaiArrayOverLimit in Res.Issues then
Memo1.Lines.Add('An array carries more than 8191 elements');
if pvaiDictOverLimit in Res.Issues then
Memo1.Lines.Add('A dictionary carries more than 4095 entries');
if pvaiNestingOverLimit in Res.Issues then
Memo1.Lines.Add('Containers nest deeper than 28 levels');
if pvaiNameOverLimit in Res.Issues then
Memo1.Lines.Add('A name token is longer than 127 bytes');
finally
Src.Free;
end;
end;
Które generatory faktycznie uderzają w te limity?
Te, które budują strukturę programowo, a to większość wyjścia linii biznesowej. Formularz z kilkoma tysiącami pól produkuje tablicę /Annots lub tablicę /Fields AcroForm rosnącą poza 8191. Strona, której słownik zasobów akumuluje jeden wpis na wygenerowany obraz albo instancję czcionki, przekracza 4095. Głęboko generowane drzewa struktury — otagowany dokument budowany rekurencją po zagnieżdżonym modelu danych — przechodzą poza 28 poziomów, zanim ktokolwiek zauważy, bo nikt nie patrzy na głębokość zagnieżdżenia
Długie nazwy pochodzą z innego nawyku: kodowania danych w tokenach nazw. Nazwa kolorantu zbudowana z identyfikatora klienta, grupa treści opcjonalnej nazwana pełną ścieżką pliku, pole formularza, którego w pełni kwalifikowana nazwa skleja sześć poziomów hierarchii. Nazwy są tanie w generacji i łatwe do wydłużenia, a 127 bajtów znika szybciej, niż byś się spodziewał, gdy w grę wchodzi etykieta kodowana UTF-8
Naprawa jest strukturalna w każdym przypadku. Podziel tablicę, podziel słownik, spłaszcz zagnieżdżenie, skróć nazwę — rekomendacja preflighitu dla każdej kwestii nazywa konkretny limit zamiast mówić, że plik jest niepoprawny. Wstrzykiwanie znaczników tu nie pomoże: to nie są roszczenia metadanych, to kształt grafu obiektów
Dlaczego symboliczna czcionka TrueType nie może nieść /Encoding
Ponieważ ISO 19005-1 §6.3.7 dopuszcza tylko wbudowane cmap czcionki dla symbolicznych czcionek TrueType, a wpis /Encoding by temu przeczył. Czcionka symboliczna mapuje kody do glifów na własnych zasadach — to właśnie oznacza symboliczna. Dodaj tabelę kodowania i są teraz dwie odpowiedzi na pytanie „który glif wybiera bajt 0x41", bez reguły w pliku mówiącej, która wygrywa. Różni czytelnicy rozwiązują to różnie, a dokument, który renderuje się jako tekst w jednym viewerze, renderuje się jako dingbaty w drugim
PDFium Component czyta flagę symboliczności z /FontDescriptor, niezależnie od tego, czy deskryptor jest zapisany inline w słowniku czcionki, czy referencjonowany pośrednio. Niesymboliczna czcionka TrueType zachowuje swoje wymagane /WinAnsiEncoding lub /MacRomanEncoding bez bycia oflagowaną, bo dla czcionek niesymbolicznych kodowanie jest dokładnie tym, o co prosi norma. Sprawdzenie odpala się na sprzeczności, nie na obecności kodowania
if pvaiSymbolicTrueTypeEncoding in Res.Issues then
Memo1.Lines.Add(
'A symbolic TrueType font carries /Encoding; PDF/A admits only its ' +
'built-in cmap (ISO 19005-1 6.3.7)');
Praktycznym źródłem tej wady jest subsetowanie czcionek robione przez producenta, który traktuje każdą czcionkę TrueType tak samo. Symbol, Wingdings, czcionki kreskowe i czcionki ikon to zwykli nosiciele — dokładnie te czcionki, których dokument biznesowy używa do pól wyboru, logotypów i kodów kreskowych, i dokładnie te, których nikt nie bada ponownie, gdy dokument zawodzi walidację z powodu „czcionek"
Jak kwestie trafiają do raportu preflighitu
Cztery limity kontenera są klasyfikowane pod strukturą; kwestia kodowania symbolicznej czcionki TrueType jest klasyfikowana pod treścią. Ten podział ma znaczenie, gdy raport idzie do dwóch różnych osób: ustalenia struktury należą zwykle do tego, kto napisał generator, a ustalenia treści do tego, kto dostarczył zasoby
Każda kwestia niesie rekomendację, która nazywa lekarstwo konkretnie — skróć tokeny nazw do 127 bajtów lub mniej, podziel tablice tak, by żadna nie niosła więcej niż 8191 elementów, usuń /Encoding ze symbolicznych czcionek TrueType. Raport mówiący „niezgodny z PDF/A" zaczyna śledztwo. Raport mówiący, który limit został przekroczony i przez co — kończy je
Walidacja bez DLL i dlaczego to tu ma znaczenie
Wszystkie powyższe sprawdzenia biegną wobec bajtów pliku, więc działają w usłudze, która nie ma wdrożonego pliku binarnego PDFium, w kroku budowania albo na maszynie, gdzie ładowanie natywnego DLL jest problemem politycznym. To celowa linia projektowa w PDFium Component: sprawdzenia, na które da się odpowiedzieć ze struktury, są odpowiadane ze struktury, a DLL jest zarezerwowany dla tych, które faktycznie potrzebują silnika renderującego
Dla otaczającego przepływu — uruchamiania walidacji na folderze, produkcji raportów i decydowania, co zrobić z ustaleniami — zobacz przewodniki po walidacji preflighitu PDF/A w Delphi i CLI wsadowego raportu preflighitu. Dla wyboru profilu archiwalnego siedzącego powyżej wszystkich tych sprawdzeń notatki o zgodności archiwalnej PDF/A omawiają, którą część i poziom celować, zanim zaczniesz naprawiać ustalenia
PDFium Component opakowuje silnik PDFium dla Delphi, C++Builder i Lazarus z API VCL wysokiego poziomu i zestawem walidatorów zgodności działających z DLL lub bez — zobacz stronę produktu PDFium Component, by poznać obsługiwane normy i platformy