Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
CRAP
100.00% covered (success)
100.00%
1 / 1
ConformanceXmpWriter
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
1 / 1
 buildXmp
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Pdf\Conformance\Metadata;
6
7use Phpdftk\Pdf\Conformance\Profile\ConformanceProfile;
8
9/**
10 * Builds XMP metadata with the conformance identification schema.
11 *
12 * PDF/A requires specific RDF types (rdf:Alt for dc:title, rdf:Seq
13 * for dc:creator) that the generic XmpWriter does not produce. This
14 * class builds the XMP packet directly, following the pattern used
15 * in the existing PdfAConformanceTest.
16 */
17final class ConformanceXmpWriter
18{
19    /**
20     * Build a complete XMP packet with conformance identification.
21     *
22     * @param ConformanceProfile $profile  The target conformance profile
23     * @param string             $title    Document title (for dc:title)
24     * @param string             $creator  Document creator/author (for dc:creator)
25     * @param string             $producer PDF producer string (for pdf:Producer)
26     */
27    public function buildXmp(
28        ConformanceProfile $profile,
29        string $title = '',
30        string $creator = '',
31        string $producer = '',
32    ): string {
33        $bom = "\xEF\xBB\xBF";
34        $titleEsc = htmlspecialchars($title, ENT_XML1 | ENT_COMPAT, 'UTF-8');
35        $creatorEsc = htmlspecialchars($creator, ENT_XML1 | ENT_COMPAT, 'UTF-8');
36        $producerEsc = htmlspecialchars($producer, ENT_XML1 | ENT_COMPAT, 'UTF-8');
37
38        $nsUri = htmlspecialchars($profile->getXmpNamespace(), ENT_XML1 | ENT_COMPAT, 'UTF-8');
39        $prefix = $profile->getXmpPrefix();
40
41        // Build identification properties
42        $identLines = '';
43        foreach ($profile->getXmpProperties() as $localName => $value) {
44            $valueEsc = htmlspecialchars($value, ENT_XML1 | ENT_COMPAT, 'UTF-8');
45            $identLines .= "              <{$prefix}:{$localName}>{$valueEsc}</{$prefix}:{$localName}>\n";
46        }
47
48        return <<<XML
49        <?xpacket begin="{$bom}" id="W5M0MpCehiHzreSzNTczkc9d"?>
50        <x:xmpmeta xmlns:x="adobe:ns:meta/">
51          <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
52            <rdf:Description rdf:about=""
53              xmlns:dc="http://purl.org/dc/elements/1.1/"
54              xmlns:pdf="http://ns.adobe.com/pdf/1.3/"
55              xmlns:{$prefix}="{$nsUri}">
56              <dc:title>
57                <rdf:Alt>
58                  <rdf:li xml:lang="x-default">{$titleEsc}</rdf:li>
59                </rdf:Alt>
60              </dc:title>
61              <dc:creator>
62                <rdf:Seq>
63                  <rdf:li>{$creatorEsc}</rdf:li>
64                </rdf:Seq>
65              </dc:creator>
66              <pdf:Producer>{$producerEsc}</pdf:Producer>
67        {$identLines}            </rdf:Description>
68          </rdf:RDF>
69        </x:xmpmeta>
70        <?xpacket end="w"?>
71        XML;
72    }
73}