콘텐츠로 이동

시맨틱 콘텐츠로 태그된 PDF/UA-2 구조 트리 생성

이 레시피는 ISO 14289-2(PDF/UA-2)를 대상으로 하는 태그된 PDF를 작성합니다. NextPDF는 논리 구조 트리, 마크된 콘텐츠 시퀀스, 카탈로그 언어, 문서 수준 식별 메타데이터를 생성합니다. 이 구조는 접근성 있는 저작을 지원하지만, 적합성 여부는 독립적인 검사기가 결정합니다. 이 레시피는 examples/31-pdfua2-tagged.php를 따릅니다.

Terminal window
composer require nextpdf/core:^3

검증 단계에서는 PATH에 PDF/UA-2 검사기가 있어야 합니다. 이 레시피의 예제는 ua2 플레이버를 사용하는 veraPDF를 사용합니다. 태그된 구조를 생성하는 데 Pro 또는 Enterprise 패키지는 필요하지 않습니다.

태그된 PDF는 시각적 콘텐츠 스트림과 나란히 논리 구조 트리를 담습니다. 보조 기술은 픽셀 레이아웃이 아니라 이 트리를 읽으므로, 노출되는 읽기 순서는 구조가 결정합니다. ISO 14289-2는 여기서 네 가지 요구사항을 정합니다. 실제(아티팩트가 아닌) 콘텐츠는 이 트리를 통해 도달 가능해야 합니다(§8.2.2). 구조 요소는 정의된 순서로 중첩되어야 합니다(§8.2.3). 모든 요소는 직접적으로든 역할 매핑을 통해서든 알려진 구조 네임스페이스로 해석되어야 합니다(§8.2.4). 그리고 콘텐츠의 자연 언어는 문서 수준에서 선언되며, 언어가 다른 경우에는 각 구조 요소별로 세분화됩니다(§8.4.4).

NextPDF는 이를 형식화된 ConformanceMode로 모델링합니다. enableTaggedPdf()ConformanceMode::PdfUa2를 설정하며, 이는 (a) HTML 파이프라인이 파서 생성 시점에 TaggedContentEmitter를 연결하게 하고, (b) 태그된 PDF임을 알리는 카탈로그 MarkInfoMarked 플래그를 켜며(ISO 32000-2 §14.7), (c) 카탈로그 Lang 항목에 사용할 BCP-47 언어를 기록합니다. 또한 라이터는 페이지별 Tabs 항목을 생성하여 탭 순서가 구조 순서를 따르게 합니다(ISO 32000-2 §14.8).

엄격한 UA-2 불변 조건은 ConformanceMode::PdfUa2에만 적용됩니다. 다른 모드에서 엄격한 ConformancePolicy를 생성하면 설계상 InvalidConfigException이 발생합니다.

API 표면은 PHPDoc에서 생성됩니다. 핵심 진입점은 다음과 같습니다:

  • \NextPDF\Core\Document::createStandalone(): Document
  • Document::enableTaggedPdf(string $lang = 'en', ?ConformancePolicy $policy = null): static
  • Document::setLanguage(string $lang): static
  • \NextPDF\Conformance\ConformancePolicy::strictUa2(): self
  • \NextPDF\Conformance\ConformanceMode::PdfUa2 (enableTaggedPdf()가 설정하는 모드)
  • Document::beginTag(string $type): static / Document::endTag(): static (HTML이 아닌 콘텐츠를 위한 수동 태깅)
examples/31-pdfua2-tagged.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
// Enable tagged mode BEFORE writeHtml(). The HTML pipeline detects the
// mode at parser construction time and wires the tagged-content emitter.
$doc->enableTaggedPdf(lang: 'en');
$doc->setTitle('Quarterly Accessibility Report');
$doc->setLanguage('en');
$doc->addPage();
$doc->writeHtml(<<<'HTML'
<h1>Quarterly Accessibility Report</h1>
<p>This document opts into tagged PDF so assistive technology can expose
a meaningful reading order.</p>
<ul>
<li>Headings carry semantic roles.</li>
<li>Lists keep their item structure.</li>
</ul>
HTML);
$doc->save(__DIR__ . '/output/31-pdfua2-tagged.pdf');
echo "Created: output/31-pdfua2-tagged.pdf\n";

다음은 하니스에서 실행할 수 있는 자체 완결형 프로그램입니다. 프로덕션 호출 측에서는 외부 검사기를 실행하고 나서야 잘못된 형식의 언어 태그를 발견하는 대신, 이를 초기에 실패로 처리합니다. ConformancePolicy::strictUa2()를 전달하여 API 경계에서 유효하지 않은 BCP-47 태그를 거부한 다음, 검사기 판정에 따라 빌드를 통제하십시오.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Conformance\ConformancePolicy;
use NextPDF\Core\Document;
use NextPDF\Exception\InvalidConfigException;
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: (__DIR__ . '/accessible.pdf');
try {
$doc = Document::createStandalone();
// Strict UA-2: a malformed BCP 47 tag throws here, not silently at
// write time. strictUa2() also forces the §8.4.4 Lang validation.
$doc->enableTaggedPdf(lang: 'en-GB', policy: ConformancePolicy::strictUa2());
$doc->setTitle('Accessible Annual Report 2026');
$doc->setLanguage('en-GB');
$doc->addPage();
$doc->writeHtml(<<<'HTML'
<h1>Annual Report 2026</h1>
<p>Audited results for the financial year ending March 2026.</p>
<h2>Segment performance</h2>
<table>
<tr><th>Segment</th><th>Revenue</th></tr>
<tr><td>Cloud</td><td>42.1</td></tr>
<tr><td>Services</td><td>18.7</td></tr>
</table>
HTML);
$doc->save($out);
} catch (InvalidConfigException $e) {
fwrite(STDERR, "Tagged PDF/UA-2 setup rejected: {$e->getMessage()}\n");
exit(1);
}
// The gate is the checker, not the library.
$exitCode = 0;
$report = [];
exec('verapdf --flavour ua2 ' . escapeshellarg($out), $report, $exitCode);
if ($exitCode !== 0) {
fwrite(STDERR, "veraPDF FAILED — output is not PDF/UA-2 conforming\n");
fwrite(STDERR, implode("\n", $report) . "\n");
exit(1);
}
echo "veraPDF PASS — accessible.pdf carries a conforming UA-2 structure\n";

호스트의 verapdf --flavour ua2가 파일을 적합하다고 보고할 때 예상되는 STDOUT:

veraPDF PASS — accessible.pdf carries a conforming UA-2 structure

enableTaggedPdf()가 언어 태그를 거부하면, 프로그램은 STDERR에 Tagged PDF/UA-2 setup rejected: …를 출력한 후 0이 아닌 값으로 종료합니다. 검사기가 문제를 보고하면, veraPDF FAILED — output is not PDF/UA-2 conforming를 출력한 후 0이 아닌 값으로 종료합니다. 판정은 검사기가 담당합니다. NextPDF는 구조를 생성하지만 적합성을 주장하지는 않습니다.

  • 호출 순서. enableTaggedPdf()writeHtml() 뒤에 호출해도 이미 작성된 콘텐츠를 소급해서 태깅하지는 않습니다. 태그 모드를 먼저 활성화하십시오.
  • 엄격한 언어 게이트. 정책이 없으면 파싱할 수 없는 BCP-47 태그가 조용히 누락되어 검사기 단계에서야 드러납니다. ConformancePolicy::strictUa2()를 사용하면 동일한 태그가 enableTaggedPdf() 경계에서 InvalidConfigException을 발생시킵니다(ISO 14289-2 §8.4.4 엄격 경로).
  • 멱등적 재활성화. enableTaggedPdf()를 두 번 호출하면 이미 채워진 구조 트리를 다시 빌드하지 않고 언어만 갱신합니다.
  • 수동 태깅. HTML이 아닌 콘텐츠의 경우 항목을 beginTag() / endTag()로 감싸십시오. 컨테이너 역할(Table, TR, L, LI)은 마크된 콘텐츠가 없는 그룹화 요소가 됩니다. 리프 역할(P, H1H6, TD)은 MCID를 받습니다.
  • 모드 배타성. 엄격한 ConformancePolicyConformanceMode::PdfUa2에서만 유효합니다. 엄격한 UA-2 플래그를 PDF/A 모드와 결합하면 InvalidConfigException이 발생합니다. 태그된 PDF/A 산출물은 태그 모드와 PDF/A 프로파일을 각각 별도로 활성화해 구성하십시오.

구조 트리는 경량 딕셔너리로 구성된 병렬 트리 하나와 텍스트 런별 BDC/EMC 연산자를 추가합니다. 일반적인 보고서에서는 오버헤드가 출력 크기의 몇 퍼센트 수준이며 2000 ms / 128 MB 예산 안에 충분히 머무릅니다. 검사기 지향 산출물은 원시 바이트가 아니라 구조적 추상 구문 트리(AST)와 메타데이터를 기준으로 비교되므로, 여기에는 시맨틱 재현성 프로파일이 적용됩니다. 적합성 섹션을 참조하십시오.

구조 트리는 보이는 콘텐츠와 동일한 텍스트를 담습니다. 소스 HTML에 개인 데이터가 포함되어 있으면 그 데이터 역시 트리와 ActualText/Alt 속성을 통해 도달 가능합니다. 저작 전에, 보이는 콘텐츠에 적용하는 것과 동일한 수정(redaction) 및 최소화를 적용하십시오. 태깅은 새로운 유출 경로를 추가하지 않지만, 설계상 텍스트를 프로그래밍 방식으로 추출할 수 있게 만듭니다.

안전한 텔레메트리 및 로그 스크러빙

섹션 제목: “안전한 텔레메트리 및 로그 스크러빙”

이 레시피는 STDOUT에 고정된 진행 표시줄 하나만 기록합니다. PDF는 하니스 사이드 채널(NEXTPDF_COOKBOOK_OUTPUT) 또는 호출자 경로로 라우팅됩니다. 문서 텍스트는 절대 로깅되지 않습니다. 콘텐츠 조각을 출력할 수 있는 검사기 출력은 공유 로그에서 제외하십시오.

태그된 PDF는 신뢰 경계가 아닙니다. 적대적인 생산자가 구조적으로는 적격이지만 오해를 유발하는 트리를 생성할 수 있으므로, 자동화된 처리를 위해 구조 트리를 신뢰하는 소비자는 여전히 파일을 검증해야 합니다. 구조를 무결성이나 진정성 신호가 아니라 접근성 보조 수단으로 취급하십시오.

이 레시피는 어떤 암호화 연산도 수행하지 않습니다. FIPS 모드는 이 동작을 변경하지 않습니다. 서명이나 암호화는 관여하지 않습니다.

PDF/UA-2 요구사항NextPDF가 생성하는 것조항
실제 콘텐츠가 구조 트리에 있음StructTreeRoot 및 블록별 StructElem, MCID로 연결된 마크된 콘텐츠ISO 14289-2 §8.2.2
정의된 중첩 및 읽기 순서문서 순서에 따라 grouping/leaf 역할에 매핑된 블록 요소ISO 14289-2 §8.2.3
알려진 구조 네임스페이스PDF 2.0 네임스페이스의 역할; 필요한 경우 역할 매핑된 HTML 태그ISO 14289-2 §8.2.4
문서 및 요소 언어BCP-47 태그에서 가져온 카탈로그 Lang; 언어가 다른 경우 요소별 Lang 사용ISO 14289-2 §8.4.4
텍스트가 아닌 콘텐츠에 텍스트 대체물이 있음figure/non-text 구조 요소에 포함되는 Alt/ActualTextISO 14289-2 §8.5.1
표 관계Table/TR/TH/TD 역할, 헤더 연결 포함ISO 14289-2 §8.2.5.26
부(part) 식별 메타데이터저장 시 예약되는 문서 수준 식별ISO 14289-2 §Intro(서론) (pdfua2#p17)

PDF/UA-2는 ISO 32000-2 태그된 PDF 메커니즘 위에 접근성 요구사항을 계층화합니다. NextPDF가 의존하는 매핑은 다음과 같습니다:

NextPDF 생성ISO 32000-2 §14 기능조항
논리 구조 트리(StructTreeRoot)태그된 PDF 논리 구조§14.7 (iso32000_2_sec14#x1.x38.p13)
카탈로그 MarkInfo << /Marked true >>태그된 PDF 마커§14.7 (iso32000_2_sec14#x1.x40.p3)
구조 순서를 따르는 페이지별 Tabs 항목구조적 내비게이션 / 탭 순서§14.8 (iso32000_2_sec14#x1.x50)

PDF/UA-2는 WCAG 2.2가 형식에 독립적으로 명시하는 구조 요구사항의 PDF 형식 표현입니다. 관련 매핑은 다음과 같습니다:

WCAG 2.2 성공 기준이 레시피가 생성하는 PDF/UA-2 메커니즘
1.3.1 정보와 관계(Level A)구조 트리는 제목, 목록, 표 관계를 프로그래밍 방식으로 판별 가능하게 만듭니다(wcag_2_2#x2.x3.x3.x1.p3).
1.3.2 의미 있는 순서(Level A)구조 순서는 시각적 레이아웃과 독립적으로 읽기 순서를 정의합니다.
3.1.1 페이지의 언어(Level A)BCP-47 태그에서 가져온 카탈로그 Lang 항목.
1.1.1 텍스트가 아닌 콘텐츠(Level A)Alt/ActualText는 텍스트가 아닌 구조 요소에 있음(ISO 14289-2 §8.5.1).

이 매핑은 생성된 구조가 WCAG 2.2 기준을 어디서 지원하는지를 보여줍니다. 이는 WCAG 적합성 주장이 아닙니다. WCAG 적합성은 사용자 경험 전체를 포괄하며, 생산자의 주장이 아니라 접근성 평가에 의해 결정됩니다.

진술규격조항reference_id(참조 ID)
실제 콘텐츠에는 논리 구조가 필요합니다.ISO 14289-2§8.2.2
구조 요소는 정의된 중첩 및 읽기 순서를 따릅니다.ISO 14289-2§8.2.3
모든 구조 요소는 직접적으로든 역할 매핑을 통해서든 알려진 네임스페이스로 해석됩니다.ISO 14289-2§8.2.4
자연 언어는 문서 및 구조 요소 수준에서 선언됩니다.ISO 14289-2§8.4.4
텍스트가 아닌 콘텐츠는 텍스트 대체물을 담습니다.ISO 14289-2§8.5.1
표 셀은 row/header/데이터 관계를 담습니다.ISO 14289-2§8.2.5.26
태그된 PDF 마커는 카탈로그 MarkInfoMarked 플래그입니다.ISO 32000-2§14.7
적합성은 생산자가 주장하는 것이 아니라 부(part)에 대해 결정됩니다.ISO 14289-2§8.14.2

NextPDF는 접근성 있는 저작을 지원하는 태그된 구조를 생성합니다. 지원은 적합성이 아닙니다. 이 레시피는 PDF/UA-2 적합성을 주장하지 않습니다. 독립적인 검사기(예: veraPDF)가 그 판정을 내립니다. 파일이 적합하다고 진술하기 전에 검사기를 실행하십시오.