PDFlibPas waliduje pliki PDF/UA-2 (ISO 14289-2) w Delphi przez TPDFlib.CheckFilePDFUACompliance z docelową częścią 2, co uruchamia osobny profil PDF/UA-2: rozwiązuje role struktury przez przestrzenie nazw PDF 2.0 i /RoleMapNS, raportuje obcięte lub nieczytelne wejście jako porażkę i zwraca raport pokrycia, który nigdy nie twierdzi pełnej certyfikacji
Ta ostatnia część znaczy więcej, niż brzmi. Walidacja dostępności to dziedzina, w której zielony haczyk trafia do arkusza zakupowego i nikt już nigdy go nie kwestionuje. Walidator, który po cichu pomija połowę standardu, a mimo to wypisuje "pass", jest gorszy niż brak walidatora, bo zamienia niewiadomą w fałszywą pewność. PDFlibPas staje na przeciwnym stanowisku: sprawdź, co da się sprawdzić mechanicznie, głośno powiedz, czego nie sprawdzono, a każde "nie udało się dokończyć" traktuj jako ustalenie, a nie sukces
Dlaczego nie da się walidować PDF/UA-2 regułami PDF/UA-1?
PDF/UA-2 to nie PDF/UA-1 z podbitym numerem części. PDF/UA-1 stoi na PDF 1.7 i jednej płaskiej mapie ról; PDF/UA-2 stoi na PDF 2.0 (ISO 32000-2), gdzie każdy element struktury może należeć do przestrzeni nazw, a każda przestrzeń nazw niesie własną mapę ról. Checker UA-1 skierowany na plik UA-2 i przeoczy prawdziwe problemy, i wymyśli fałszywe
Fałszywe to przy okazji bardziej żenująca połowa. Walidatory PDF/UA-1 zwykle wymuszają reguły o elementach Note niosących ID oraz o tym, jak wolno używać nienumerowanego nagłówka H. Żadna z tych reguł się nie przenosi. W przestrzeni nazw struktury PDF 2.0 przypisy dolne wyraża się przez FENote, a nagłówki to H1 do Hn bez sześciopoziomowego sufitu. PDF/UA-2 idzie dalej i całkowicie zakazuje roli nienumerowanego H. Zastosuj do takiego drzewa logikę nagłówków z UA-1, a albo narzekasz na to, na co standard pozwala, albo przepuszczasz to jedno, czego zabrania
Profil PDF/UA-2 w PDFlibPas jest więc osobną ścieżką kodu, nie flagą na starej. Odmawia też ponownego użycia numerów punktów UA-1. Diagnostykę wspólną z checkerem UA-1 raporty UA-2 przypinają na nowo do ISO 14289-2, a tam, gdzie numer punktu nie został zweryfikowany z tekstem standardu, raport zostawia pole punktu puste zamiast zgadywać. Pusty punkt satysfakcjonuje mniej niż pewny numer punktu, ale to jedyny wynik, który da się obronić, gdy nikt nie potwierdził mapowania
Jak PDFlibPas rozwiązuje role przez RoleMapNS
PDFlibPas rozwiązuje każdą rolę struktury do roli standardowej w znanej przestrzeni nazw, podążając za łańcuchami /RoleMapNS między przestrzeniami nazw, i raportuje każdy łańcuch, który kończy się na roli własnej, zapętla albo łamie reguły mapowania. To rdzeń walidacji struktury PDF/UA-2, bo technologie wspomagające rozumieją tylko role standardowe; własna rola /InvoiceLine nic nie znaczy, dopóki nie wyląduje na czymś w rodzaju P albo TR
Resolver startuje od nazwy /S elementu i jego przestrzeni nazw /NS. Bez przestrzeni nazw element jest domyślnie w standardowej przestrzeni nazw PDF 1.7 i obowiązuje dziedziczony /RoleMap w korzeniu drzewa struktury. Z przestrzenią nazw obowiązuje własny /RoleMapNS słownika przestrzeni nazw, a mapowanie może wskazywać rolę w innej przestrzeni nazw przez tablicę dwuelementową. Wynikające z tego sprawdzenia są konkretne:
- Przestrzeń nazw wskazywana z elementu struktury musi być słownikiem pośrednim z poprawnym URI w
/NSi musi być wymieniona w tablicy/Namespaceskorzenia drzewa struktury (kod diagnostyczny 10054) - Rola własna musi ostatecznie rozwiązać się do roli standardowej w przestrzeni nazw PDF 1.7, przestrzeni nazw PDF 2.0 (
http://iso.org/pdf2/ssn) albo przestrzeni nazw MathML; łańcuchy cykliczne i ślepe zaułki są raportowane jako 10055 - Rola standardowa nie może być przemapowana na własną, a rola nie może być przemapowana w ramach własnej jawnej przestrzeni nazw
- Korzeń struktury musi zawierać dokładnie jeden element
Documentw przestrzeni nazw PDF 2.0 (10057), a/Pkażdego elementu musi wskazywać jego rzeczywistego rodzica (10058) - Nienumerowane
Halbo dziedziczony z PDF 1.7Notenienamapowany na rolę PDF 2.0 jest raportowany jako 10056
Jeden szczegół wart skopiowania do własnych narzędzi: PDFlibPas waliduje każde zadeklarowane mapowanie, nie tylko te osiągalne z drzewa. Zepsuty wpis w mapie ról, której żaden element obecnie nie używa, to nadal utajona wada, bo następna aktualizacja treści zacznie z niej korzystać. MathML też jest potraktowany uczciwie. Przestrzeń nazw MathML jest uznawana za poprawny cel rozwiązania, ale samo słownictwo MathML nie jest walidowane, i raport pokrycia mówi to wprost
Uruchamianie sprawdzenia PDF/UA-2 z Delphi
TPDFlib.CheckFilePDFUACompliance to punkt wejścia w jednym wywołaniu: podajesz plik, hasło, docelową część i opcje, a dostajesz uchwyt listy stringów z zakodowanymi problemami. Uchwyt zero przy LastErrorCode równym zero znaczy, że zaimplementowane sprawdzenia nic nie znalazły; niezerowe LastErrorCode znaczy, że samo wywołanie padło
var
Lib: TPDFlib;
Issues, I: Integer;
begin
Lib := TPDFlib.Create;
try
// TargetPart: 0 = wykryj z XMP pdfuaid:part, 1 = PDF/UA-1, 2 = PDF/UA-2
// Options: 1 = zatrzymaj się na pierwszym błędzie, 0 = zbierz każdy problem
Issues := Lib.CheckFilePDFUACompliance('report.pdf', '', 2, 0);
if Issues = 0 then
begin
if Lib.LastErrorCode <> 0 then
Writeln('Validation did not run, error ', Lib.LastErrorCode)
else
Writeln('No issues from the implemented PDF/UA-2 checks');
end
else
try
for I := 1 to Lib.GetStringListCount(Issues) do // indeksy liczone od 1
Writeln(Lib.GetStringListItem(Issues, I));
finally
Lib.ReleaseStringList(Issues);
end;
finally
Lib.Free;
end;
end;
Docelową część wybieraj świadomie. Cel 0 czyta pdfuaid:part z metadanych XMP i dobiera profil z tego, co pasuje do generycznego walidatora skrzynki wejściowej. We własnym potoku wyjściowym przekaż wprost 2. Jeśli generator zapomniał zapisać identyfikację, autodetekcja cofa się do reguł UA-1 i walidujesz zły standard; jawne 2 daje w zamian kod diagnostyczny 10050 za brakującą część, plus 10051, gdy pdfuaid:rev nie jest 2024, i 10052, gdy efektywna wersja PDF nie jest 2.0. Po stronie autorskiej TPDFlib.SetPDFUA2Mode zapisuje tę identyfikację, podnosi wersję do 2.0 i wybiera przestrzeń nazw struktury PDF 2.0 jako domyślną; sam model przestrzeni nazw jest omówiony w przewodniku po przestrzeniach nazw PDF 2.0, PDF/A-4 i PDF/UA-2
Co się dzieje, gdy walidatorowi kończy się budżet?
PDFlibPas działa na zasadzie fail-closed: gdy checker PDF/UA-2 trafi na limit przechodzenia, cykl, limit parsera albo dokument, którego nie potrafi otworzyć, emituje diagnostykę, a wynik bez diagnostyki nigdy nie zostaje podbity do zaliczenia. Kod diagnostyczny 10059 jest zarezerwowany dokładnie dla przypadku "czytnik nie zdołał dokończyć"
Budżety są jawne i opublikowane w JSON z pokryciem: głębokość struktury 256, 100 000 obiektów struktury i 256 skoków wzdłuż dowolnego łańcucha mapy ról. Te liczby istnieją, bo drzewa struktury pochodzą z niezaufanego wejścia. Wrogi albo po prostu zepsuty plik potrafi zagnieździć elementy tysiące poziomów w głąb, skierować dziecko z powrotem na przodka albo zbudować mapę ról łańcuchującą przez setki przestrzeni nazw. Walidator bez budżetów albo się wywali, albo się zawiesi, a walidator z budżetami bez diagnostyki robi coś subtelniejszego i gorszego: kończy wcześnie, nie znajduje nic złego w przeczytanej części i raportuje sukces
Ta sama zasada działa poziom wyżej. Jeśli podstawowy czytnik zwraca wynik poniżej 100 bez linii problemów, co może się zdarzyć, gdy parsowanie abortuje zanim jakakolwiek reguła ruszy, PDFlibPas wstawia linię 10059, żeby pusta lista nie mogła zostać wzięta za czysty wynik. Wyjątek w trakcie sprawdzania również staje się linią 10059. To niewielka ilość kodu, a zamyka najczęstszy sposób, w jaki potoki walidacyjne kłamią swoim właścicielom
Współdzielenie jednego parsowania z PDF/A i budowanie raportu
Do pracy wsadowej API potoku walidacyjnego parsuje plik raz i puszcza kilka sprawdzeń na współdzielonym parsowaniu. RunPDFUAValidationPipelineTask to członek tej rodziny odpowiadający za PDF/UA i przyjmuje te same wartości docelowej części co funkcja jednowywołaniowa, dzięki czemu preflight PDF/A plus PDF/UA-2 nie płaci za dwa pełne odczyty dużego dokumentu
var
Pipe, PDFAIssues, UAIssues: Integer;
Json: WideString;
begin
Pipe := Lib.BeginValidationPipeline('archive.pdf', '');
if Pipe = 0 then
Exit;
try
PDFAIssues := Lib.RunValidationPipelineTask(Pipe, 1, 0); // 1 = PDF/A
UAIssues := Lib.RunPDFUAValidationPipelineTask(Pipe, 2, 0); // PDF/UA-2
// ... obejrzyj i zwolnij oba uchwyty jak w poprzednim przykładzie ...
Writeln('Parses: ', Lib.GetValidationPipelineParseCount(Pipe));
// ComplianceTests: 1 = PDF/A, 2 = PDF/UA, 0 = oba; ReportFormat 1 = JSON
Json := Lib.CreateValidationPipelineReport(Pipe, 0, 0, 1);
finally
Lib.ReleaseValidationPipeline(Pipe);
end;
end;
Raport strukturalny etykietuje sekcję dostępności jako PDF/UA-2 i ISO 14289-2, gdy plik identyfikuje się jako część 2, a każda sekcja PDF/UA niesie "fullCertification": false razem z osadzonym obiektem pokrycia. Tekstowy raport preflight dodaje linię stwierdzającą, że ruszyły tylko zaimplementowane sprawdzenia i że pełna certyfikacja dostępności wymaga dodatkowej walidacji i ludzkiego przeglądu. Szersze formaty raportów, ID reguł i wzorce bramkowania w CI są opisane w przewodniku po preflight PDF/A i PDF/UA
Co raport pokrycia naprawdę gwarantuje?
TPDFlib.GetPDFUAReadValidationCoverageJSON gwarantuje jedną rzecz: dokładną listę tego, co checker PDF/UA-2 obejrzał, a czego nie. Dla części 2 zwraca fullCertification zablokowane na false, tablicę checked, tablicę unverified i limity przechodzenia. Nie gwarantuje, że plik jest dostępny, i jest zaprojektowany tak, by żadna ścieżka kodu nie zmusiła go do powiedzenia czegoś innego
var
Coverage: WideString;
begin
Coverage := Lib.GetPDFUAReadValidationCoverageJSON(2);
// Zapisz go obok wyniku walidacji, a nie zamiast niego
TFile.WriteAllText('report.coverage.json', Coverage, TEncoding.UTF8);
if Pos('"fullCertification":false', Coverage) > 0 then
Writeln('Machine checks only; schedule a human accessibility review');
end;
Lista checked obejmuje identyfikację (część, rewizję, prefiks), efektywną wersję PDF, metadane tytułu, /MarkInfo, DisplayDocTitle, odwołania do przestrzeni nazw, rozwiązanie mapy ról, korzeń dokumentu, rodziców struktury, cykle w drzewie oraz reguły H i Note. Lista unverified jest równie konkretna: tagowanie treści i kolejność czytania, zawieranie struktury, fonty i mapowanie Unicode, adnotacje i formularze, język naturalny, opisy alternatywne, słownictwo MathML i ludzki przegląd dostępności. Kilka z tych punktów nie jest nawet mechanicznie rozstrzygalnych. Żaden parser nie powie ci, czy tekst alternatywny faktycznie opisuje wykres, ani czy kolejność czytania ma sens dla kogoś, kto jej słucha
Traktuj JSON pokrycia jako część wyniku. Archiwizuj go przy każdym uruchomieniu walidacji, a gdy audytor zapyta, co znaczyło "passed" w danym dniu, możesz wręczyć dokładny zakres zamiast rekonstruować go z pamięci i numeru wersji
Krzyżowe sprawdzenie z veraPDF
Rdzeń PDF/UA-2 został skrzyżowo sprawdzony walidatorem wiersza poleceń veraPDF 1.30.3 z jego profilem ua2, a wyniki zgadzają się tam, gdzie pokrycie się nakłada. Niezależnie wygenerowana poprawna próbka rdzenia przeszła oba narzędzia; próbka deklarująca pdfuaid:rev 2023 i próbka używająca nienumerowanej roli H poległy w obu
Czwarta próbka jest tą warta opowieści. Zawierała treść rysunkową całkowicie nieotagowaną i veraPDF ją oflagował, podczas gdy rdzenne sprawdzenia PDFlibPas już nie, bo tagowanie treści jest poza obecnym pokryciem. Ta luka jest dokładnie powodem, dla którego content-tagging-and-reading-order otwiera listę unverified. Puszczenie drugiego walidatora to tanie ubezpieczenie, a niezgodność między dwoma narzędziami bywa najszybszym sposobem znalezienia granicy tego, co każde z nich sprawdza. Po stronie autorską tego samego problemu, czyli budowanie drzew, które od razu przechodzą, zobacz przewodnik po drzewie struktury tagowanego PDF
FAQ
Czy PDFlibPas certyfikuje plik jako zgodny z PDF/UA-2?
Nie. PDFlibPas puszcza zaimplementowane sprawdzenia maszynowe dla PDF/UA-2 i zwraca raport pokrycia z fullCertification zawsze równym false, wymieniając każdy obszar, który nadal wymaga innych narzędzi lub ludzkiego przeglądu
Jaką wartość TargetPart przekazać do CheckFilePDFUACompliance?
Przekaż 2, gdy wiesz, że plik musi być PDF/UA-2, żeby brakująca identyfikacja została zgłoszona. Przekaż 0, żeby autodetekcja części czytała z XMP pdfuaid:part, i 1, żeby wymusić reguły PDF/UA-1
Co znaczy kod diagnostyczny 10059 w raporcie PDF/UA-2?
Kod diagnostyczny 10059 znaczy, że walidator nie zdołał dokończyć: pliku nie udało się sparsować ani otworzyć, przekroczono limit przechodzenia, znaleziono cykl albo wystąpił wyjątek. Zawsze liczy się jako porażka, nigdy jako zaliczenie
Walidacja PDF/UA-2, współdzielony potok walidacyjny i autorskie API PDF 2.0 są częścią losLab PDF Library for Delphi, razem z preflight PDF/A i strukturalnymi raportami walidacji