Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
CRAP
100.00% covered (success)
100.00%
1 / 1
TaggedStructureConstraint
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
10
100.00% covered (success)
100.00%
1 / 1
 check
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
10
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Pdf\Conformance\Constraint;
6
7use Phpdftk\Pdf\Conformance\Inspection\DocumentInspector;
8use Phpdftk\Pdf\Conformance\Profile\ConformanceProfile;
9use Phpdftk\Pdf\Conformance\Profile\PdfAProfile;
10use Phpdftk\Pdf\Conformance\Profile\PdfUaProfile;
11use Phpdftk\Pdf\Conformance\Result\ConformanceViolation;
12use Phpdftk\Pdf\Conformance\Result\ViolationSeverity;
13
14/**
15 * Tagged structure requirements for PDF/A Level A and PDF/UA.
16 *
17 * PDF/A-1a/2a/3a (ISO 19005 clause 6.8):
18 *   - MarkInfo /Marked must be true
19 *   - StructTreeRoot must be present
20 *   - Catalog /Lang must be set
21 *
22 * PDF/UA-1 (ISO 14289-1 clause 7):
23 *   - Same base requirements as PDF/A Level A
24 */
25final class TaggedStructureConstraint implements ConformanceConstraint
26{
27    public function check(DocumentInspector $inspector, ConformanceProfile $profile): array
28    {
29        // PDF/UA always requires tagged structure
30        // PDF/A only requires it for Level A
31        if ($profile instanceof PdfAProfile && !$profile->requiresTaggedStructure()) {
32            return [];
33        }
34
35        $isPdfUa = $profile instanceof PdfUaProfile;
36        $violations = [];
37        $catalog = $inspector->getCatalog();
38
39        // MarkInfo /Marked must be true
40        if ($catalog->markInfo === null || $catalog->markInfo->marked !== true) {
41            $violations[] = new ConformanceViolation(
42                clause: $isPdfUa ? '7.1' : '6.8.1',
43                message: 'MarkInfo /Marked must be true — all content must be tagged',
44                severity: ViolationSeverity::Error,
45                objectPath: 'Catalog.MarkInfo',
46            );
47        }
48
49        // StructTreeRoot must be present
50        if ($catalog->structTreeRoot === null) {
51            $violations[] = new ConformanceViolation(
52                clause: $isPdfUa ? '7.1' : '6.8.2',
53                message: 'StructTreeRoot is required — all content must be in the structure tree',
54                severity: ViolationSeverity::Error,
55                objectPath: 'Catalog.StructTreeRoot',
56            );
57        }
58
59        // Document language must be set
60        if ($catalog->lang === null) {
61            $violations[] = new ConformanceViolation(
62                clause: $isPdfUa ? '7.2' : '6.8.4',
63                message: 'Catalog /Lang is required — document must specify its natural language',
64                severity: ViolationSeverity::Error,
65                objectPath: 'Catalog.Lang',
66            );
67        }
68
69        return $violations;
70    }
71}