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\Writer; |
11
|
|
|
|
12
|
|
|
use Bacon\Pdf\Exception\InvalidArgumentException; |
13
|
|
|
use SplFileObject; |
14
|
|
|
|
15
|
|
|
class ObjectWriter |
16
|
|
|
{ |
17
|
|
|
/** |
18
|
|
|
* @var SplFileObject|null |
19
|
|
|
*/ |
20
|
|
|
private $fileObject; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* @var int |
24
|
|
|
*/ |
25
|
|
|
private $currentLineLength = 0; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @var bool |
29
|
|
|
*/ |
30
|
|
|
private $requiresWhitespace = false; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @param SplFileObject $fileObject |
34
|
|
|
*/ |
35
|
|
|
public function __construct(SplFileObject $fileObject) |
36
|
|
|
{ |
37
|
|
|
$this->fileObject = $fileObject; |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Writes raw data line to the stream. |
42
|
|
|
* |
43
|
|
|
* This method does not obey the normal line length limit, so you have to take care of that yourself. Note that the |
44
|
|
|
* writer may still be on an active line, so take that into account as well. |
45
|
|
|
* |
46
|
|
|
* @param string $data |
47
|
|
|
*/ |
48
|
|
|
public function writeRawLine($data) |
49
|
|
|
{ |
50
|
|
|
if (null === $this->fileObject) { |
51
|
|
|
throw new WriterClosedException('The writer object was closed'); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
$this->fileObject->fwrite($data. "\n"); |
55
|
|
|
$this->currentLineLength = 0; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Ensures that the writer is on a blank line. |
60
|
|
|
*/ |
61
|
|
|
public function ensureBlankLine() |
62
|
|
|
{ |
63
|
|
|
if ($this->currentLineLength === 0) { |
64
|
|
|
return; |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
$this->fileObject->fwrite("\n"); |
68
|
|
|
$this->currentLineLength = 0; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Returns the current position in the file. |
73
|
|
|
* |
74
|
|
|
* @return int |
75
|
|
|
*/ |
76
|
|
|
public function currentOffset() |
77
|
|
|
{ |
78
|
|
|
return $this->fileObject->ftell(); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Starts a dictionary. |
83
|
|
|
*/ |
84
|
|
|
public function startDictionary() |
85
|
|
|
{ |
86
|
|
|
$this->writeData('<<', false); |
87
|
|
|
$this->requiresWhitespace = false; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Ends a dictionary. |
92
|
|
|
*/ |
93
|
|
|
public function endDictionary() |
94
|
|
|
{ |
95
|
|
|
$this->writeData('>>', false); |
96
|
|
|
$this->requiresWhitespace = false; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Starts an array. |
101
|
|
|
*/ |
102
|
|
|
public function startArray() |
103
|
|
|
{ |
104
|
|
|
$this->writeData('[', false); |
105
|
|
|
$this->requiresWhitespace = false; |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Ends an array. |
110
|
|
|
*/ |
111
|
|
|
public function endArray() |
112
|
|
|
{ |
113
|
|
|
$this->writeData(']', false); |
114
|
|
|
$this->requiresWhitespace = false; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Writes a null value. |
119
|
|
|
*/ |
120
|
|
|
public function writeNull() |
121
|
|
|
{ |
122
|
|
|
$this->writeData('null', true); |
123
|
|
|
$this->requiresWhitespace = true; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* Writes a boolean. |
128
|
|
|
* |
129
|
|
|
* @param bool $boolean |
130
|
|
|
*/ |
131
|
|
|
public function writeBoolean($boolean) |
132
|
|
|
{ |
133
|
|
|
$this->writeData($boolean ? 'true' : 'false', true); |
134
|
|
|
$this->requiresWhitespace = true; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Writes a number. |
139
|
|
|
* |
140
|
|
|
* @param int|float $number |
141
|
|
|
* @throws InvalidArgumentException |
142
|
|
|
*/ |
143
|
|
|
public function writeNumber($number) |
144
|
|
|
{ |
145
|
|
|
if (is_int($number)) { |
146
|
|
|
$value = (string) $number; |
147
|
|
|
} elseif (is_float($number)) { |
148
|
|
|
$value = sprintf('%F', $number); |
149
|
|
|
} else { |
150
|
|
|
throw new InvalidArgumentException(sprintf( |
151
|
|
|
'Expected int or float, got %s', |
152
|
|
|
gettype($number) |
153
|
|
|
)); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
$this->writeData($value, true); |
157
|
|
|
$this->requiresWhitespace = true; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Writes a name. |
162
|
|
|
* |
163
|
|
|
* @param string $name |
164
|
|
|
*/ |
165
|
|
|
public function writeName($name) |
166
|
|
|
{ |
167
|
|
|
$this->writeData('/' . $name, false); |
168
|
|
|
$this->requiresWhitespace = true; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* Writes a literal string. |
173
|
|
|
* |
174
|
|
|
* The string itself is splitted into multiple lines after 248 characters. We chose that specific limit to avoid |
175
|
|
|
* splitting mutli-byte characters in half. |
176
|
|
|
* |
177
|
|
|
* @param string $string |
178
|
|
|
*/ |
179
|
|
|
public function writeLiteralString($string) |
180
|
|
|
{ |
181
|
|
|
$this->writeData('(' . chunk_split(strtr($string, [ |
182
|
|
|
'(' => '\\(', |
183
|
|
|
')' => '\\)', |
184
|
|
|
'\\' => '\\\\', |
185
|
|
|
]), 248, "\\\n") . ')', false, "\\\n"); |
|
|
|
|
186
|
|
|
$this->requiresWhitespace = false; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* Writes a hexadecimal string. |
191
|
|
|
* |
192
|
|
|
* @param string $string |
193
|
|
|
*/ |
194
|
|
|
public function writeHexadecimalString($string) |
195
|
|
|
{ |
196
|
|
|
$this->writeData('<' . chunk_split(bin2hex($string), 248, "\n") . '>', false, "\n"); |
|
|
|
|
197
|
|
|
$this->requiresWhitespace = false; |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* Unsets the file object so it can release the file pointer. |
202
|
|
|
*/ |
203
|
|
|
public function close() |
204
|
|
|
{ |
205
|
|
|
$this->fileObject = null; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Writes data to the stream while obeying the maximum line length of 255 characters. |
210
|
|
|
* |
211
|
|
|
* If $this->requiresWhitespace is true, it means that the last token requires a whitespace character in case the |
212
|
|
|
* next token begins with an alphanumeric character. If that is the case, the callee should set $prependWhitespace |
213
|
|
|
* to true, so that a whitespace is appended in that case, which may either be a space or a newline. |
214
|
|
|
* |
215
|
|
|
* @param string $data |
216
|
|
|
* @param bool $prependWhitespace |
217
|
|
|
*/ |
218
|
|
|
private function writeData($data, $prependWhitespace) |
219
|
|
|
{ |
220
|
|
|
if (null === $this->fileObject) { |
221
|
|
|
throw new WriterClosedException('The writer object was closed'); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
$dataSize = strlen($data); |
225
|
|
|
|
226
|
|
|
if ($this->requiresWhitespace && $prependWhitespace) { |
227
|
|
|
$dataSize += 1; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
if ($this->currentLineLength + $dataSize >= 255) { |
231
|
|
|
$this->fileObject->fwrite("\n"); |
232
|
|
|
$this->currentLineLength = 0; |
233
|
|
|
$this->requiresWhitespace = false; |
234
|
|
|
$dataSize -= 1; |
235
|
|
|
} elseif ($this->requiresWhitespace && $prependWhitespace) { |
236
|
|
|
$this->fileObject->fwrite(' '); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
$this->fileObject->fwrite($data); |
240
|
|
|
$this->currentLineLength += $dataSize; |
241
|
|
|
} |
242
|
|
|
} |
243
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.