Przejdź do głównej zawartości

Dostępność: prymitywy tagowania i model struktury PDF/UA-2

NextPDF Core udostępnia prymitywy do tworzenia dostępnych treści: logiczne drzewo struktury, standardowe mapowanie ról, tagowanie zawartości oznaczonej oraz atrybuty języka Best Current Practice (BCP) 47, zgodne z modelem drzewa struktury w ISO 14289-2 (PDF/UA-2) oraz ISO 32000-2 §14.7. Plik wynikowy jest zgodny tylko wtedy, gdy potwierdzają to ostateczny dokument, decyzje autora dotyczące treści oraz zewnętrzny walidator. Biblioteka nie poświadcza tego za Ciebie.

Okno terminala
composer require nextpdf/core

Otagowany plik Portable Document Format (PDF) zawiera logiczne drzewo struktury, w którego korzeniu znajduje się pojedynczy element struktury Document. Technologia wspomagająca odczytuje to drzewo, aby ustalić logiczną kolejność odczytu niezależną od układu wizualnego (ISO 32000-2 §14.7.2; ISO 14289-2 §8.2.5.2). NextPDF modeluje to za pomocą trzech współpracujących typów w przestrzeni nazw NextPDF\Accessibility.

StructureTree zarządza hierarchią. Przydziela identyfikatory zawartości oznaczonej dla każdej strony, śledzi zagnieżdżenie elementów nadrzędnych i podrzędnych oraz serializuje korzeń drzewa struktury, elementy struktury, drzewo elementów nadrzędnych, mapę ról i standardową przestrzeń nazw struktury PDF 2.0 zgodnie z ISO 32000-2 §14.7. createRoot() tworzy wymagany pojedynczy element Document z atrybutem języka. addElement() dołącza typowane elementy podrzędne. hasRoot() oraz rootHasChildren() raportują, czy drzewo istnieje i czy ma elementy potomne.

StructureElement to obiekt wartości dla pojedynczego słownika elementu struktury. Przechowuje standardowy typ struktury (nazwy z Tabeli 368, takie jak H1 do H6, P, L, LI, Table, Figure, Link), wpisy identyfikatorów zawartości oznaczonej oraz opcjonalne atrybuty dostępności dla tekstu alternatywnego, tekstu zastępczego, tytułu i języka. Pojedynczy element może obejmować wiele stron. Gromadzi po jednym wpisie identyfikatora na stronę, dzięki czemu tablica elementów potomnych odwołuje się do zawartości oznaczonej ponad granicami stron.

TaggedContentEmitter łączy potok Hypertext Markup Language (HTML) z drzewem struktury. Gdy Document::enableTaggedPdf() jest aktywne, mechanizm renderujący HTML podłącza emiter, dzięki czemu elementy blokowe tworzą sparowane operatory zawartości oznaczonej oraz odpowiadające im węzły elementów struktury. HtmlToStructureMap udostępnia tabelaryczne mapowanie znaczników HTML na typy struktury PDF (ISO 14289-2 §8). Emiter klasyfikuje dekoracyjną zawartość bieżącą, taką jak obszary nagłówka i stopki HTML, jako artefakt i utrzymuje ją poza kolejnością odczytu.

Bcp47Validator waliduje znaczniki języka (Request for Comments (RFC) 5646). Udostępnia kontrolę poprawności składniowej oraz kontrolę ważności opartą na rejestrze. Tryb ścisły (ConformancePolicy::strictUa2()) odrzuca niepoprawne znaczniki na granicy interfejsu programowania aplikacji (API), zamiast usuwać je po cichu podczas zapisu. Jest to zgodne z wymaganiem ISO 14289-2 §8.4.4, aby wpis języka w katalogu rozwiązywał się do konkretnego języka.

SymbolRodzajPodsumowanie
Document::enableTaggedPdf(string $lang = 'en', ?ConformancePolicy $policy = null): staticmetodaAktywuje drzewo struktury i mostek HTML; ustawia wpisy mark-info oraz wpisy języka w katalogu.
Document::setLanguage(string $lang): staticmetodaUstawia język naturalny na poziomie dokumentu (BCP 47).
Document::isTaggedPdfEnabled(): boolmetodaRaportuje, czy aktywny tryb zgodności wymaga tagowania strukturalnego.
StructureTree::createRoot(string $lang = 'en'): intmetodaTworzy obowiązkowy pojedynczy element główny Document.
StructureTree::addElement(int $parentIndex, string $type, int $pageIndex, ...): intmetodaDołącza typowany element podrzędny struktury.
StructureTree::hasRoot(): bool oraz rootHasChildren(): boolmetodaRaportują, czy drzewo istnieje i czy ma elementy potomne.
StructureElementklasa finalObiekt wartości dla jednego elementu struktury (tekst alternatywny, tekst zastępczy, tytuł, język, identyfikatory).
RoleMap::standard(): array<string,string>staticZwraca standardowy słownik typów struktury (ISO 32000-2 Tabela 368 oraz typy PDF 2.0).
Bcp47Validator::isWellFormed/isValid/validate/normalisemetodaWaliduje znaczniki języka RFC 5646 za pomocą kontroli składniowych i kontroli opartych na rejestrze.
AccessibilityAutoFixerRegistryklasa finalOpcjonalnie włączany rejestr w stylu PHP Standards Recommendation (PSR)-11 dla heurystycznych korektorów struktury.
<?php
declare(strict_types=1);
use NextPDF\Core\Document;
$doc = Document::createStandalone();
// The BCP 47 tag drives the catalog language entry and the
// structure-tree root language attribute.
$doc->enableTaggedPdf(lang: 'en');
$doc->setTitle('Tagged accessibility demo');
$doc->addPage();
// Semantic HTML maps to structure elements: h1 to /H1, p to /P,
// ul and li to /L plus /LI. Text runs are wrapped in
// marked-content operators with stable identifiers.
$doc->writeHtml('<h1>Document title</h1><p>Body paragraph.</p>');
$doc->save(__DIR__ . '/output/tagged.pdf');
<?php
declare(strict_types=1);
use NextPDF\Conformance\ConformancePolicy;
use NextPDF\Core\Document;
use NextPDF\Exception\InvalidConfigException;
use Psr\Log\LoggerInterface;
final class AccessibleReportWriter
{
public function __construct(private readonly LoggerInterface $logger)
{
}
public function render(string $html, string $bcp47Lang, string $outPath): void
{
$doc = Document::createStandalone();
try {
// strictUa2() rejects malformed BCP 47 tags at the API
// boundary (ISO 14289-2 §8.4.4) instead of dropping silently.
$doc->enableTaggedPdf($bcp47Lang, ConformancePolicy::strictUa2());
} catch (InvalidConfigException $e) {
$this->logger->error('Rejected language tag for tagged PDF', [
'lang' => $bcp47Lang,
'reason' => $e->getMessage(),
]);
throw $e;
}
$doc->setTitle('Quarterly accessibility report')
->setLanguage($bcp47Lang)
->addPage();
$doc->writeHtml($html);
// The engine emits a Degraded / ComplianceRisk advisory directing
// the caller to validate externally; surface it to operators
// rather than treating tagged output as certified.
foreach ($doc->getWarnings() as $warning) {
$this->logger->warning('Tagged-PDF advisory', [
'code' => $warning->code->value,
'message' => $warning->message,
]);
}
$doc->save($outPath);
}
}
  • Kolejność wywołań. Wywołaj enableTaggedPdf() przed writeHtml(). Potok HTML sprawdza tryb zgodności podczas tworzenia parsera i nie podłącza emitera z mocą wsteczną do treści, która została już wyrenderowana.
  • Puste drzewo struktury. Dokument z enableTaggedPdf(), ale bez dołączonych potomków struktury, nie deklaruje PDF/UA-2 w swoich metadanych. Warunkiem dopuszczenia do publikacji jest rootHasChildren(), a nie hasRoot(), ponieważ walidatory odrzucają plik deklarujący PDF/UA-2 z pustym drzewem struktury (ISO 14289-2 §5; zweryfikowane przez EmptyTaggedPdfDoesNotAdvertisePdfUa2Test).
  • Zwinięcie trybu zgodności. Gdy wywołasz enablePdfA() oraz enableTaggedPdf() na tym samym dokumencie, jednowartościowy dyskryminator zgodności zwija się zgodnie z zasadą ostatni wygrywa. Efekty uboczne (drzewo struktury, mark-info) pozostają addytywne, a NextPDF emituje ostrzeżenie CONFORMANCE_MODE_CLOBBERED, dzięki czemu zwinięcie jest obserwowalne.
  • Automatyczne korektory nie są automatyczne. Wbudowane korektory (EmptyTagStripper, LegacyLangNormaliser, RootLangFallback) są dostarczane w NextPDF\Accessibility\AutoFixer\*, ale nigdy nie są rejestrowane automatycznie. Musisz je jawnie zarejestrować w AccessibilityAutoFixerRegistry.

NextPDF emituje strukturę zgodną z modelem drzewa struktury PDF/UA-2, ale nie tworzy semantyki, której nie jest w stanie wywnioskować. Musisz dostarczyć znaczniki lub atrybuty dla następujących elementów; NextPDF nie generuje ich za Ciebie:

  • tekst alternatywny dla obrazów i innej zawartości nietekstowej;
  • zakres nagłówków tabeli oraz powiązania nagłówków z komórkami, które wykraczają poza to, co wyraża znacznik HTML;
  • tekst opisujący cel odnośnika, gdy widoczny tekst odnośnika nie jest samoopisujący;
  • semantyka listy dla treści, która jest wizualnie ułożona jak lista, ale nie ma znaczników listy;
  • skorygowana kolejność odczytu, gdy kolejność źródłowa różni się od zamierzonej kolejności odczytu;
  • klasyfikacja treści dwuznacznej jako dekoracyjnej lub znaczącej.

NextPDF nie przeprowadza kompleksowej weryfikacji PDF/UA-2. Podczas wykonywania emituje ostrzeżenie Degraded / ComplianceRisk (PDFUA2_FOUNDATIONAL), które kieruje wywołującego do zwalidowania wyniku zewnętrznym walidatorem przed zatwierdzeniem produkcyjnym. Zwaliduj wynik za pomocą walidatora PDF/UA (na przykład veraPDF). NextPDF nie poświadcza zgodności w Twoim imieniu. Zgodność ostatecznego dokumentu zależy od decyzji autorskich i walidatora, a nie od wywołania API.

Budowa drzewa struktury jest liniowa względem liczby elementów struktury. Przydział identyfikatorów odbywa się w zamortyzowanym stałym czasie dla sekwencji zawartości oznaczonej. Serializacja to pojedyncze liniowe przejście po zbiorze elementów. W przypadku tagowania sterowanego przez HTML dominującym kosztem jest sam potok HTML, a nie emisja tagów. Limit dla receptury zadeklarowany w performance_budget (1500 ms czasu rzeczywistego, 64 MB szczytowo) dotyczy typowego wielostronicowego dokumentu semantycznego. Obsługa dużych dokumentów skaluje się liniowo z liczbą elementów, a nie z liczbą stron.

Znaczniki języka i atrybuty dostępności trafiają do obiektów nazwy i ciągu znaków PDF. NextPDF zabezpiecza je znakami ucieczki za pomocą PdfStringEscaper, dzięki czemu niepoprawne lub wrogie wartości języka, tekstu alternatywnego, tekstu zastępczego i tytułu nie mogą wyłamać się z kontekstu swojego obiektu PDF. Tryb ścisły odrzuca także niezarejestrowane znaczniki BCP 47 na granicy API, zawężając powierzchnię wejścia, zanim trafi ono do zapisującego. Atrybuty dostępności mogą zawierać dowolny tekst dostarczony przez autora. Traktuj je jako niezaufane dane wyjściowe i sprawdzaj je tak, jak sprawdzasz pozostałą zawartość dokumentu. Zachowanie walidatora profili opisuje moduł Conformance.

Ta strona mapuje zachowanie biblioteki na identyfikatory klauzul. Nie poświadcza ona, że dane wyjściowe są zgodne. Cytowane klauzule są parafrazowane, nigdy cytowane dosłownie. Tabelę na poziomie postanowień oraz jawnie określony brak pokrycia opisuje mapowanie specyfikacji PDF/UA-2. Skróty fragmentów cytatów są zapisane w docs/public/modules/core/_normative-evidence-a11y.md.