Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
SignatureValue
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
2 / 2
18
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 toPdf
100.00% covered (success)
100.00%
35 / 35
100.00% covered (success)
100.00%
1 / 1
16
1<?php
2
3declare(strict_types=1);
4
5namespace Phpdftk\Pdf\Core\Interactive\Signature;
6
7use Phpdftk\Pdf\Core\PdfArray;
8use Phpdftk\Pdf\Core\PdfDictionary;
9use Phpdftk\Pdf\Core\PdfName;
10use Phpdftk\Pdf\Core\PdfNumber;
11use Phpdftk\Pdf\Core\PdfObject;
12use Phpdftk\Pdf\Core\PdfString;
13use Phpdftk\Pdf\Core\PdfVersion;
14use Phpdftk\Pdf\Core\RequiresPdfVersion;
15
16/**
17 * Signature value dictionary (/Type /Sig) — ISO 32000-2 §12.8.1, Table 258.
18 *
19 * Holds the signed bytes (/Contents), the byte range over the PDF that was
20 * signed (/ByteRange), the handler that produced the signature (/Filter,
21 * /SubFilter), and optional metadata (name, location, reason, contact,
22 * date, references).
23 *
24 * Actual PKCS#7/CAdES signing is out of scope for the object model; this
25 * class only carries the placeholder and structural entries. Callers that
26 * implement signing compute /ByteRange and overwrite /Contents in the
27 * serialized PDF.
28 */
29#[RequiresPdfVersion(PdfVersion::V1_3)]
30class SignatureValue extends PdfObject
31{
32    public const PDF_TYPE = 'Sig';
33
34    public PdfName $filter;               // /Filter     (required)
35    public ?PdfName $subFilter = null;    // /SubFilter
36    public PdfString $contents;           // /Contents   (required, usually hex)
37    public ?PdfArray $cert = null;        // /Cert       chain (single or array)
38    public ?PdfArray $byteRange = null;   // /ByteRange  [start1 len1 start2 len2]
39    public ?PdfArray $reference = null;   // /Reference  array of SignatureReference
40    public ?PdfArray $changes = null;     // /Changes
41    public ?PdfString $name = null;       // /Name
42    public ?PdfString $m = null;          // /M          signing date (PDF date)
43    public ?PdfString $location = null;   // /Location
44    public ?PdfString $reason = null;     // /Reason
45    public ?PdfString $contactInfo = null; // /ContactInfo
46    public ?int $r = null;                // /R   handler revision
47    public ?int $v = null;                // /V   dictionary version
48    public ?PdfDictionary $propBuild = null; // /Prop_Build
49    public ?int $propAuthTime = null;     // /Prop_AuthTime
50    public ?PdfName $propAuthType = null; // /Prop_AuthType
51
52    public function __construct(
53        string $filter = 'Adobe.PPKLite',
54        ?string $subFilter = 'adbe.pkcs7.detached',
55        ?PdfString $contents = null,
56    ) {
57        $this->filter = new PdfName($filter);
58        if ($subFilter !== null) {
59            $this->subFilter = new PdfName($subFilter);
60        }
61        // Default placeholder: 2048 bytes of zeros in hex form, typical for
62        // signature byte-range precomputation.
63        $this->contents = $contents ?? new PdfString(str_repeat("\x00", 2048), hex: true);
64    }
65
66    public function toPdf(): string
67    {
68        $dict = new PdfDictionary();
69        $dict->set('Type', new PdfName(self::PDF_TYPE));
70        $dict->set('Filter', $this->filter);
71        if ($this->subFilter !== null) {
72            $dict->set('SubFilter', $this->subFilter);
73        }
74        $dict->set('Contents', $this->contents);
75        if ($this->cert !== null) {
76            $dict->set('Cert', $this->cert);
77        }
78        if ($this->byteRange !== null) {
79            $dict->set('ByteRange', $this->byteRange);
80        }
81        if ($this->reference !== null) {
82            $dict->set('Reference', $this->reference);
83        }
84        if ($this->changes !== null) {
85            $dict->set('Changes', $this->changes);
86        }
87        if ($this->name !== null) {
88            $dict->set('Name', $this->name);
89        }
90        if ($this->m !== null) {
91            $dict->set('M', $this->m);
92        }
93        if ($this->location !== null) {
94            $dict->set('Location', $this->location);
95        }
96        if ($this->reason !== null) {
97            $dict->set('Reason', $this->reason);
98        }
99        if ($this->contactInfo !== null) {
100            $dict->set('ContactInfo', $this->contactInfo);
101        }
102        if ($this->r !== null) {
103            $dict->set('R', new PdfNumber($this->r));
104        }
105        if ($this->v !== null) {
106            $dict->set('V', new PdfNumber($this->v));
107        }
108        if ($this->propBuild !== null) {
109            $dict->set('Prop_Build', $this->propBuild);
110        }
111        if ($this->propAuthTime !== null) {
112            $dict->set('Prop_AuthTime', new PdfNumber($this->propAuthTime));
113        }
114        if ($this->propAuthType !== null) {
115            $dict->set('Prop_AuthType', $this->propAuthType);
116        }
117        return $dict->toPdf();
118    }
119}