Artykuł techniczny

Konwersja PDF do PDF/A i naprawa jego metadanych

ConvertToPDFA zamienia zwykły dokument w archiwalny jednym wywołaniem: usuwa to, czego zabrania wybrana część, dodaje to, czego wymaga, deklaruje część, którą dokument rości, a następnie sprawdza wynik. Roszczenie jest raportowane jako spełnione dopiero wtedy, gdy sprawdzanie przejdzie, a GetPDFAConversionReport wylicza, co zostało zrobione i co wciąż stoi na przeszkodzie

Ta ostatnia właściwość to decyzja projektowa, której warto się przyjrzeć. Konwerter, który stempluje roszczenie bez sprawdzania, jest gorszy niż brak konwertera w ogóle, ponieważ plik twierdzący, że jest archiwalny, a nie będący nim, przechodzi prosto przez te same systemy, które inaczej by go złapały. Porażka ujawnia się latem później, w audycie, na dokumencie, którego nikt nie potrafi wygenerować ponownie

Dlaczego poprawnie wyglądający PDF nie przechodzi sprawdzenia PDF/A?

Najczęściej dlatego, że dwa miejsca, w których PDF mówi, kto go napisał, się nie zgadzają. Walidator czyta zarówno słownik informacji dokumentu, jak i pakiet XMP oraz odrzuca plik, w którym się różnią — a większość plików, które zawodzą na tym punkcie, po prostu nigdy nie miała zapisanej połowy XMP

RepairDocumentMetadata doprowadza je do zgody i zwraca liczbę naprawionych wpisów. Gdy tylko jedna połowa niesie wartość, druga jest z niej wypełniana, więc nic już zapisanego nie jest wyrzucane. Nikt nie musi decydować, która kopia jest wiążąca, bo w praktyce jedna kopia jest pusta

W tym samym wywołaniu siedzi druga naprawa, która wychwytuje subtelniejszy przypadek. Dokument ustawiony w tryb PDF/A odzyskuje identyfikację norm, jeśli została utracona, co następuje zawsze, gdy wywołujący dostarczy własny pakiet XMP. Bez tej identyfikacji walidator czyta plik jako zwykły PDF i raportuje każdą regułę rozczonej części jako niespełnioną — spektakularnie wyglądająca porażka o jednej małej przyczynie

var
  Lib: TPDFlib;
  Repaired: Integer;
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('incoming.pdf', '');
    Repaired := Lib.RepairDocumentMetadata;
    Log(Format('%d metadata entries brought into agreement', [Repaired]));
    Lib.SaveToFile('incoming-fixed.pdf');
  finally
    Lib.Free;
  end;
end;

Wybór części przed konwersją

SetPDFAMode i ConvertToPDFA współdzielą tę samą numerację trybów, a trzy z wartości są niedawne. Tryb 9 to PDF/A-4, część zbudowana na PDF 2.0. Tryb 10 to PDF/A-4e, który dodatkowo dopuszcza treści 3D i rich media, a tryb 11 to PDF/A-4f, który dopuszcza osadzony plik dowolnego formatu

Część 4 identyfikuje się inaczej niż części przed nią: numerem części i rokiem publikacji tej części, bez litery zgodności dla czystego PDF/A-4 oraz z literą E lub F dla dwóch rozszerzeń. Sprawdzenie rozpoznaje część 4, ocenia jej pliki wobec PDF 2.0 zamiast 1.7 i raportuje plik części 4, który nie podaje swojego roku rewizji

Każdy osadzony plik w dokumencie części 4 deklaruje swoją relację wobec dokumentu, czego wymagają zarówno części 3, jak i 4. To jest reguła, która kiedyś łapała zwykłe załączniki: relację zapisywano tylko dla załączników po pierwszym i nigdy dla ostatniego, więc dokument z jednym załącznikiem — częsty przypadek — nie niósł jej wcale i nie przechodził walidacji dokładnie na tym punkcie

var
  Verdict: Integer;
begin
  Lib.LoadFromFile('report.pdf', '');
  Verdict := Lib.ConvertToPDFA(9);        // 9 = PDF/A-4, 10 = 4e, 11 = 4f
  Memo1.Lines.Text := Lib.GetPDFAConversionReport;
  if Verdict = 1 then
    Lib.SaveToFile('report-pdfa4.pdf')
  else
    Log('conversion incomplete - see the report for what stands in the way');
end;

Po co jest raport konwersji

By zdecydować, co dalej. Konwersja, która się udaje, nie potrzebuje raportu; konwersja, która się nie udaje, to cały powód istnienia raportu. Niektóre przeszkody są usuwalne przez konwerter, a niektóre nie — szyfrowanie, zabroniona treść niosąca znaczenie, program czcionki, którego po prostu nigdzie na maszynie nie ma. Raport rozróżnia to, co zostało zrobione, od tego, co pozostaje, co zamienia „konwersja nieudana" w element pracy

Traktuj werdykt jako bramkę w potoku wsadowym. Konwertuj, czytaj werdykt i kieruj plik: archiwizuj te, które przeszły, kolejkuj resztę dla człowieka z dołączonym raportem. Czego nie powinieneś robić, to zapisywać wyniku nieudanej konwersji do archiwum, bo wygląda lepiej niż wejście — niesie teraz roszczenie, którego sprawdzenie odmówiło potwierdzić

Odczytywanie znacznika, jaki plik już niesie

Zanim cokolwiek przekonwertujesz, wiedz, co dokument mówi o sobie. Sprawdzenie PDF/A, które nie potrafi odczytać istniejącego znacznika norm, ocenia każdy plik wobec części 1 cokolwiek deklaruje, co oznacza, że doskonale zgodny dokument PDF/A-2 lub PDF/A-3 zostaje raportowany jako nieniosący żadnego znacznika i jako zbyt wysokiej wersji — odwrotność prawdy

Znacznik jest czytany niezależnie od tego, czy producent zapisał go jako element XMP, czy jako atrybut. Obie formy to zwykły XMP, a akceptowanie tylko jednej z nich zostawia pliki od innych producentów wyglądające jako nieotagowane. Jeśli kiedykolwiek zastanawiałeś się, dlaczego dokument, który przechodzi walidację gdzie indziej, zawodzi we własnym potoku, to jest dobre miejsce, by zajrzeć najpierw

Sanityzacja przed archiwizacją i błąd, o którym warto wiedzieć

Konwersja archiwalna i sanityzacja często biegną razem, ponieważ treść, jaką chce usunąć polityka bezpieczeństwa, mocno pokrywa się z treścią zabronioną przez PDF/A. SanitizeDocument usuwa JavaScript, a usunięcie ostatniego skryptu usuwa też puste drzewo nazw, które po sobie zostawia — drzewo, które inaczej wciąż mówiłoby czytnikowi, że dokument niósł skrypty

Ta druga połowa została nauczona na własnych błędach: błąd off-by-one w liście pakietów sprawiał, że sanityzacja raportowała usunięcie skryptów, nie usuwając żadnego, więc dokument, który został zsanityzowany, uruchamiał nadal swoje skrypty po otwarciu. To dobry argument za ogólną zasadą, na której opiera się cały ten artykuł — weryfikuj wynik zamiast ufać operacji, w swoim potoku tak samo jak w bibliotece

W sprawie otaczającej pracy archiwalnej zobacz przewodniki po preflighcie PDF/A i PDF/UA, prawdziwym redoagowaniu i usuwaniu treści oraz schematach rozszerzeń XMP PDF/A-3 dla Factur-X, który obejmuje stronę metadanych, gdy archiwizowany dokument niesie także ustrukturyzowane dane faktury

PDFlibPas to natywna biblioteka PDF w Pascalu dla środowisk Delphi, C++Builder i Lazarus, więc konwersja, naprawa i walidacja odbywają się wewnątrz własnego procesu bez zewnętrznego narzędzia w łańcuchu — zobacz stronę produktu PDFlibPas, by poznać obsługiwane części PDF/A i platformy