1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* BaconPdf |
4
|
|
|
* |
5
|
|
|
* @link http://github.com/Bacon/BaconPdf For the canonical source repository |
6
|
|
|
* @copyright 2015 Ben Scholzen (DASPRiD) |
7
|
|
|
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
namespace Bacon\Pdf; |
11
|
|
|
|
12
|
|
|
use Bacon\Pdf\Encryption\EncryptionInterface; |
13
|
|
|
use Bacon\Pdf\Encryption\Pdf16Encryption; |
14
|
|
|
use Bacon\Pdf\Encryption\Permissions; |
15
|
|
|
use Bacon\Pdf\Writer\ObjectWriter; |
16
|
|
|
use SplFileObject; |
17
|
|
|
|
18
|
|
|
class PdfWriter |
19
|
|
|
{ |
20
|
|
|
/** |
21
|
|
|
* @var ObjectWriter |
22
|
|
|
*/ |
23
|
|
|
private $objectWriter; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var PdfWriterOptions |
27
|
|
|
*/ |
28
|
|
|
private $options; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var string |
32
|
|
|
*/ |
33
|
|
|
private $permanentFileIdentifier; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @var string |
37
|
|
|
*/ |
38
|
|
|
private $changingFileIdentifier; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @var EncryptionInterface|null |
42
|
|
|
*/ |
43
|
|
|
private $encryption; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* @var array |
47
|
|
|
*/ |
48
|
|
|
private $objectOffsets = []; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @param SplFileObject $fileObject |
52
|
|
|
*/ |
53
|
|
|
public function __construct(SplFileObject $fileObject, PdfWriterOptions $options) |
54
|
|
|
{ |
55
|
|
|
$this->objectWriter = new ObjectWriter($fileObject); |
56
|
|
|
|
57
|
|
|
$options->freeze(); |
58
|
|
|
$this->options = $options; |
59
|
|
|
|
60
|
|
|
$this->objectWriter->writeRawLine(sprintf("%PDF-%s", $this->options->getVersion())); |
61
|
|
|
$this->objectWriter->writeRawLine("%\xff\xff\xff\xff"); |
62
|
|
|
|
63
|
|
|
$this->permanentFileIdentifier = hex2bin(md5(microtime())); |
64
|
|
|
$this->changingFileIdentifierFileIdentifier = $this->permanentFileIdentifier; |
|
|
|
|
65
|
|
|
|
66
|
|
|
if ($this->options->hasEncryption()) { |
67
|
|
|
$this->encryption = $this->chooseEncryption( |
|
|
|
|
68
|
|
|
$this->options->getUserPassword(), |
69
|
|
|
$this->options->getOwnerPassword(), |
70
|
|
|
$this->options->getVersion() |
71
|
|
|
); |
72
|
|
|
} |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Closes the document by writing the file trailer. |
77
|
|
|
* |
78
|
|
|
* While the PDF writer will remove all references to the passed in file object in itself to avoid further writing |
79
|
|
|
* and to allow the file pointer to be closed, the callee may still have a reference to it. If that is the case, |
80
|
|
|
* make sure to unset it if you don't need it. |
81
|
|
|
* |
82
|
|
|
* Any further attempts to append data to the PDF writer will result in an exception. |
83
|
|
|
*/ |
84
|
|
|
public function closeDocument() |
85
|
|
|
{ |
86
|
|
|
$xrefOffset = $this->writeXrefTable(); |
87
|
|
|
$this->writeTrailer(); |
88
|
|
|
$this->writeFooter($xrefOffset); |
89
|
|
|
$this->objectWriter->close(); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Creates a PDF writer which writes everything to a file. |
94
|
|
|
* |
95
|
|
|
* @param string $filename |
96
|
|
|
* @return static |
97
|
|
|
*/ |
98
|
|
|
public static function toFile($filename) |
99
|
|
|
{ |
100
|
|
|
return new static(new SplFileObject($filename, 'wb')); |
|
|
|
|
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Creates a PDF writer which outputs everything to the STDOUT. |
105
|
|
|
* |
106
|
|
|
* @return static |
107
|
|
|
*/ |
108
|
|
|
public static function output() |
109
|
|
|
{ |
110
|
|
|
return new static(new SplFileObject('php://stdout', 'wb')); |
|
|
|
|
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Choose an encryption algorithm based on the PDF version. |
115
|
|
|
* |
116
|
|
|
* @param string $version |
117
|
|
|
* @param string $userPassword |
118
|
|
|
* @param string|null $ownerPassword |
119
|
|
|
* @param Permissions|null $userPermissions |
120
|
|
|
*/ |
121
|
|
|
private function chooseEncryption( |
122
|
|
|
$version, |
123
|
|
|
$userPassword, |
124
|
|
|
$ownerPassword = null, |
125
|
|
|
Permissions $userPermissions = null |
126
|
|
|
) { |
127
|
|
|
if (version_compare($version, '1.6', '>=')) { |
128
|
|
|
return new Pdf16Encryption($ownerPassword, $userPassword, $version, $userPermissions); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
if (version_compare($version, '1.4', '>=')) { |
132
|
|
|
return new Pdf14Encryption($ownerPassword, $userPassword, $version, $userPermissions); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
return new Pdf11Encryption($ownerPassword, $userPassword, $version, $userPermissions); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* Writes the xref table. |
140
|
|
|
* |
141
|
|
|
* @return int |
142
|
|
|
*/ |
143
|
|
|
private function writeXrefTable() |
144
|
|
|
{ |
145
|
|
|
$this->objectWriter->ensureBlankLine(); |
146
|
|
|
$xrefOffset = $this->objectWriter->currentOffset(); |
147
|
|
|
|
148
|
|
|
$this->objectWriter->writeRawLine('xref'); |
149
|
|
|
$this->objectWriter->writeRawLine(sprintf('0 %d', count($this->objectOffsets) + 1)); |
150
|
|
|
$this->objectWriter->writeRawLine(sprintf('%010d %05d f ', 0, 65535)); |
151
|
|
|
|
152
|
|
|
foreach ($this->objectOffsets as $offset) { |
153
|
|
|
$this->objectWriter->writeRawLine(sprintf('%010d %05d n ', $offset, 0)); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
return $xrefOffset; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* Writes the trailer. |
161
|
|
|
*/ |
162
|
|
|
private function writeTrailer() |
163
|
|
|
{ |
164
|
|
|
$this->objectWriter->ensureBlankLine(); |
165
|
|
|
$this->objectWriter->writeRawLine('trailer'); |
166
|
|
|
$this->objectWriter->startDictionary(); |
167
|
|
|
|
168
|
|
|
$this->objectWriter->writeName('Id'); |
169
|
|
|
$this->objectWriter->startArray(); |
170
|
|
|
$this->objectWriter->writeHexadecimalString($this->permanentFileIdentifier); |
171
|
|
|
$this->objectWriter->writeHexadecimalString($this->changingFileIdentifier); |
172
|
|
|
$this->objectWriter->endArray(); |
173
|
|
|
|
174
|
|
|
if (null !== $this->encryption) { |
175
|
|
|
$this->objectWriter->writeName('Encrypt'); |
176
|
|
|
$this->encryption->writeEncryptDictionary($this->objectWriter); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
$this->objectWriter->endDictionary(); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* Writes the footer. |
184
|
|
|
* |
185
|
|
|
* @param int $xrefOffset |
186
|
|
|
*/ |
187
|
|
|
private function writeFooter($xrefOffset) |
188
|
|
|
{ |
189
|
|
|
$this->objectWriter->ensureBlankLine(); |
190
|
|
|
$this->objectWriter->writeRawLine('startxref'); |
191
|
|
|
$this->objectWriter->writeRawLine((string) $xrefOffset); |
192
|
|
|
$this->objectWriter->writeRawLine("%%%EOF"); |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.