1
|
|
|
<?php |
2
|
|
|
// |
3
|
|
|
// TCPDI - Version 1.1 |
4
|
|
|
// Based on FPDI - Version 1.4.4 |
5
|
|
|
// |
6
|
|
|
// Copyright 2004-2013 Setasign - Jan Slabon |
7
|
|
|
// |
8
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); |
9
|
|
|
// you may not use this file except in compliance with the License. |
10
|
|
|
// You may obtain a copy of the License at |
11
|
|
|
// |
12
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0 |
13
|
|
|
// |
14
|
|
|
// Unless required by applicable law or agreed to in writing, software |
15
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS, |
16
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
17
|
|
|
// See the License for the specific language governing permissions and |
18
|
|
|
// limitations under the License. |
19
|
|
|
// |
20
|
|
|
|
21
|
|
|
// Dummy shim to allow unmodified use of fpdf_tpl |
22
|
|
|
class FPDF extends TCPDF {} |
23
|
|
|
|
24
|
|
|
require_once('fpdf_tpl.php'); |
25
|
|
|
|
26
|
|
|
require_once('tcpdi_parser.php'); |
27
|
|
|
|
28
|
|
|
|
29
|
|
|
class TCPDI extends FPDF_TPL { |
30
|
|
|
/** |
31
|
|
|
* Actual filename |
32
|
|
|
* @var string |
33
|
|
|
*/ |
34
|
|
|
public $current_filename; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Parser-Objects |
38
|
|
|
* @var array |
39
|
|
|
*/ |
40
|
|
|
public $parsers = array(); |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Current parser |
44
|
|
|
* @var object |
45
|
|
|
*/ |
46
|
|
|
public $current_parser; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* object stack |
50
|
|
|
* @var array |
51
|
|
|
*/ |
52
|
|
|
protected $_obj_stack = array(); |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* done object stack |
56
|
|
|
* @var array |
57
|
|
|
*/ |
58
|
|
|
protected $_don_obj_stack = array(); |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* Current Object Id. |
62
|
|
|
* @var integer |
63
|
|
|
*/ |
64
|
|
|
protected $_current_obj_id; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* The name of the last imported page box |
68
|
|
|
* @var string |
69
|
|
|
*/ |
70
|
|
|
public $lastUsedPageBox; |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Cache for imported pages/template ids |
74
|
|
|
* @var array |
75
|
|
|
*/ |
76
|
|
|
protected $_importedPages = array(); |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Cache for imported page annotations |
80
|
|
|
* @var array |
81
|
|
|
*/ |
82
|
|
|
protected $_importedAnnots = array(); |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Number of TOC pages, used for annotation offset |
86
|
|
|
* @var integer |
87
|
|
|
*/ |
88
|
|
|
protected $_numTOCpages = 0; |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* First TOC page, used for annotation offset |
92
|
|
|
* @var integer |
93
|
|
|
*/ |
94
|
|
|
protected $_TOCpagenum = 0; |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* Set a source-file |
98
|
|
|
* |
99
|
|
|
* @param string $filename a valid filename |
100
|
|
|
* @return int number of available pages |
101
|
|
|
*/ |
102
|
|
|
public function setSourceFile($filename) { |
103
|
|
|
$this->current_filename = $filename; |
104
|
|
|
|
105
|
|
|
if (!isset($this->parsers[$filename])) |
106
|
|
|
$this->parsers[$filename] = $this->_getPdfParser($filename); |
107
|
|
|
$this->current_parser =& $this->parsers[$filename]; |
108
|
|
|
$this->setPDFVersion(max($this->getPDFVersion(), $this->current_parser->getPDFVersion())); |
109
|
|
|
|
110
|
|
|
return $this->parsers[$filename]->getPageCount(); |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Set a source-file PDF data |
115
|
|
|
* |
116
|
|
|
* @param string $pdfdata The PDF file content |
117
|
|
|
* @return int number of available pages |
118
|
|
|
*/ |
119
|
|
|
public function setSourceData($pdfdata) { |
120
|
|
|
$filename = uniqid('tcpdi-'); |
121
|
|
|
$this->current_filename = $filename; |
122
|
|
|
|
123
|
|
|
if (!isset($this->parsers[$filename])) |
124
|
|
|
$this->parsers[$filename] = new tcpdi_parser($pdfdata, $filename); |
125
|
|
|
$this->current_parser =& $this->parsers[$filename]; |
126
|
|
|
$this->setPDFVersion(max($this->getPDFVersion(), $this->current_parser->getPDFVersion())); |
127
|
|
|
|
128
|
|
|
return $this->parsers[$filename]->getPageCount(); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Returns a PDF parser object |
133
|
|
|
* |
134
|
|
|
* @param string $filename |
135
|
|
|
* @return fpdi_pdf_parser |
136
|
|
|
*/ |
137
|
|
|
protected function _getPdfParser($filename) { |
138
|
|
|
$data = file_get_contents($filename); |
139
|
|
|
return new tcpdi_parser($data, $filename); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Get the current PDF version |
144
|
|
|
* |
145
|
|
|
* @return string |
146
|
|
|
*/ |
147
|
|
|
public function getPDFVersion() { |
148
|
|
|
return $this->PDFVersion; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* Set the PDF version |
153
|
|
|
* |
154
|
|
|
* @return string |
155
|
|
|
*/ |
156
|
|
|
public function setPDFVersion($version = '1.3') { |
157
|
|
|
$this->PDFVersion = $version; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Import a page |
162
|
|
|
* |
163
|
|
|
* @param int $pageno pagenumber |
164
|
|
|
* @return int Index of imported page - to use with fpdf_tpl::useTemplate() |
165
|
|
|
*/ |
166
|
|
|
public function importPage($pageno, $boxName = '/CropBox') { |
167
|
|
|
if ($this->_intpl) { |
168
|
|
|
return $this->error('Please import the desired pages before creating a new template.'); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
$fn = $this->current_filename; |
172
|
|
|
|
173
|
|
|
// check if page already imported |
174
|
|
|
$pageKey = $fn . '-' . ((int)$pageno) . $boxName; |
175
|
|
|
if (isset($this->_importedPages[$pageKey])) |
176
|
|
|
return $this->_importedPages[$pageKey]; |
177
|
|
|
|
178
|
|
|
$parser =& $this->parsers[$fn]; |
179
|
|
|
$parser->setPageno($pageno); |
180
|
|
|
|
181
|
|
|
if (!in_array($boxName, $parser->availableBoxes)) |
182
|
|
|
return $this->Error(sprintf('Unknown box: %s', $boxName)); |
183
|
|
|
|
184
|
|
|
$pageboxes = $parser->getPageBoxes($pageno, $this->k); |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* MediaBox |
188
|
|
|
* CropBox: Default -> MediaBox |
189
|
|
|
* BleedBox: Default -> CropBox |
190
|
|
|
* TrimBox: Default -> CropBox |
191
|
|
|
* ArtBox: Default -> CropBox |
192
|
|
|
*/ |
193
|
|
|
if (!isset($pageboxes[$boxName]) && ($boxName == '/BleedBox' || $boxName == '/TrimBox' || $boxName == '/ArtBox')) |
194
|
|
|
$boxName = '/CropBox'; |
195
|
|
|
if (!isset($pageboxes[$boxName]) && $boxName == '/CropBox') |
196
|
|
|
$boxName = '/MediaBox'; |
197
|
|
|
|
198
|
|
|
if (!isset($pageboxes[$boxName])) |
199
|
|
|
return false; |
|
|
|
|
200
|
|
|
|
201
|
|
|
$this->lastUsedPageBox = $boxName; |
202
|
|
|
|
203
|
|
|
$box = $pageboxes[$boxName]; |
204
|
|
|
|
205
|
|
|
$this->tpl++; |
206
|
|
|
$this->tpls[$this->tpl] = array(); |
207
|
|
|
$tpl =& $this->tpls[$this->tpl]; |
208
|
|
|
$tpl['parser'] =& $parser; |
209
|
|
|
$tpl['resources'] = $parser->getPageResources(); |
210
|
|
|
$tpl['buffer'] = $parser->getContent(); |
211
|
|
|
$tpl['box'] = $box; |
212
|
|
|
|
213
|
|
|
// To build an array that can be used by PDF_TPL::useTemplate() |
214
|
|
|
$this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl], $box); |
215
|
|
|
|
216
|
|
|
// An imported page will start at 0,0 everytime. Translation will be set in _putformxobjects() |
217
|
|
|
$tpl['x'] = 0; |
218
|
|
|
$tpl['y'] = 0; |
219
|
|
|
|
220
|
|
|
// handle rotated pages |
221
|
|
|
$rotation = $parser->getPageRotation($pageno); |
222
|
|
|
$tpl['_rotationAngle'] = 0; |
223
|
|
|
if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0) { |
224
|
|
|
$steps = $angle / 90; |
225
|
|
|
|
226
|
|
|
$_w = $tpl['w']; |
227
|
|
|
$_h = $tpl['h']; |
228
|
|
|
$tpl['w'] = $steps % 2 == 0 ? $_w : $_h; |
229
|
|
|
$tpl['h'] = $steps % 2 == 0 ? $_h : $_w; |
230
|
|
|
|
231
|
|
|
if ($angle < 0) |
232
|
|
|
$angle += 360; |
233
|
|
|
|
234
|
|
|
$tpl['_rotationAngle'] = $angle * -1; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
$this->_importedPages[$pageKey] = $this->tpl; |
238
|
|
|
|
239
|
|
|
return $this->tpl; |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
public function setPageFormatFromTemplatePage($pageno, $orientation) { |
243
|
|
|
$fn = $this->current_filename; |
244
|
|
|
$parser =& $this->parsers[$fn]; |
245
|
|
|
$parser->setPageno($pageno); |
246
|
|
|
$boxes = $parser->getPageBoxes($pageno, $this->k); |
247
|
|
|
foreach ($boxes as $name => $box) { |
248
|
|
|
if ($name[0] == '/') { |
249
|
|
|
$boxes[substr($name, 1)] = $box; |
250
|
|
|
unset($boxes[$name]); |
251
|
|
|
} |
252
|
|
|
} |
253
|
|
|
$this->setPageFormat($boxes, $orientation); |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/* Wrapper for AddPage() which tracks TOC pages to offset annotations later */ |
257
|
|
|
public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) { |
258
|
|
|
if ($this->inxobj) { |
259
|
|
|
// we are inside an XObject template |
260
|
|
|
return; |
261
|
|
|
} |
262
|
|
|
parent::AddPage($orientation, $format, $keepmargins, $tocpage); |
263
|
|
|
if ($this->tocpage) { |
264
|
|
|
$this->_numTOCpages++; |
265
|
|
|
} |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
/* Wrapper for AddTOC() which tracks TOC position to offset annotations later */ |
269
|
|
|
public function AddTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) { |
270
|
|
|
if (!TCPDF_STATIC::empty_string($page)) { |
271
|
|
|
$this->_TOCpagenum = $page; |
|
|
|
|
272
|
|
|
} else { |
273
|
|
|
$this->_TOCpagenum = $this->page; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
parent::AddTOC($page, $numbersfont, $filler, $toc_name, $style, $color); |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
public function importAnnotations($pageno) { |
280
|
|
|
$fn = $this->current_filename; |
281
|
|
|
$parser =& $this->parsers[$fn]; |
282
|
|
|
$parser->setPageno($pageno); |
283
|
|
|
$annots = $parser->getPageAnnotations(); |
284
|
|
|
|
285
|
|
|
if (is_array($annots) && $annots[0] == PDF_TYPE_OBJECT // We got an object |
286
|
|
|
&& is_array($annots[1]) && $annots[1][0] == PDF_TYPE_ARRAY // It's an array |
287
|
|
|
&& is_array($annots[1][1]) && count($annots[1][1] > 1) // It's not empty - there are annotations for this page |
288
|
|
|
) { |
289
|
|
|
if (!isset($this->_obj_stack[$fn])) { |
290
|
|
|
$this->_obj_stack[$fn] = array(); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
$this->_importedAnnots[$this->page] = array(); |
294
|
|
|
foreach ($annots[1][1] as $annot) { |
295
|
|
|
$this->importAnnotation($annot); |
296
|
|
|
} |
297
|
|
|
} |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
public function importAnnotation($annotation) { |
301
|
|
|
$fn = $this->current_filename; |
302
|
|
|
$old_id = $annotation[1]; |
303
|
|
|
$value = array(PDF_TYPE_OBJREF, $old_id, 0); |
304
|
|
|
if (!isset($this->_don_obj_stack[$fn][$old_id])) { |
305
|
|
|
$this->_newobj(false, true); |
306
|
|
|
$this->_obj_stack[$fn][$old_id] = array($this->n, $value); |
307
|
|
|
$this->_don_obj_stack[$fn][$old_id] = array($this->n, $value); |
308
|
|
|
} |
309
|
|
|
$objid = $this->_don_obj_stack[$fn][$old_id][0]; |
310
|
|
|
$this->_importedAnnots[$this->page][] = $objid; |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* Get references to page annotations. |
315
|
|
|
* @param $n (int) page number |
316
|
|
|
* @return string |
317
|
|
|
* @protected |
318
|
|
|
* @author Nicola Asuni |
319
|
|
|
* @since 5.0.010 (2010-05-17) |
320
|
|
|
*/ |
321
|
|
|
protected function _getannotsrefs($n) { |
322
|
|
|
if (!empty($this->_numTOCpages) && $n >= $this->_TOCpagenum) { |
323
|
|
|
// Offset page number to account for TOC being inserted before page containing annotations. |
324
|
|
|
$n -= $this->_numTOCpages; |
325
|
|
|
} |
326
|
|
|
if (!(isset($this->_importedAnnots[$n]) OR isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) { |
327
|
|
|
return ''; |
328
|
|
|
} |
329
|
|
|
$out = ' /Annots ['; |
330
|
|
|
if (isset($this->_importedAnnots[$n])) { |
331
|
|
|
foreach ($this->_importedAnnots[$n] as $key => $val) { |
332
|
|
|
$out .= ' '.$val.' 0 R'; |
333
|
|
|
} |
334
|
|
|
} |
335
|
|
|
if (isset($this->PageAnnots[$n])) { |
336
|
|
|
foreach ($this->PageAnnots[$n] as $key => $val) { |
337
|
|
|
if (!in_array($val['n'], $this->radio_groups)) { |
338
|
|
|
$out .= ' '.$val['n'].' 0 R'; |
339
|
|
|
} |
340
|
|
|
} |
341
|
|
|
// add radiobutton groups |
342
|
|
|
if (isset($this->radiobutton_groups[$n])) { |
343
|
|
|
foreach ($this->radiobutton_groups[$n] as $key => $data) { |
344
|
|
|
if (isset($data['n'])) { |
345
|
|
|
$out .= ' '.$data['n'].' 0 R'; |
346
|
|
|
} |
347
|
|
|
} |
348
|
|
|
} |
349
|
|
|
} |
350
|
|
|
if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) { |
351
|
|
|
// set reference for signature object |
352
|
|
|
$out .= ' '.$this->sig_obj_id.' 0 R'; |
353
|
|
|
} |
354
|
|
|
if (!empty($this->empty_signature_appearance)) { |
355
|
|
|
foreach ($this->empty_signature_appearance as $esa) { |
356
|
|
|
if ($esa['page'] == $n) { |
357
|
|
|
// set reference for empty signature objects |
358
|
|
|
$out .= ' '.$esa['objid'].' 0 R'; |
359
|
|
|
} |
360
|
|
|
} |
361
|
|
|
} |
362
|
|
|
$out .= ' ]'; |
363
|
|
|
return $out; |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
/** |
367
|
|
|
* Returns the last used page box |
368
|
|
|
* |
369
|
|
|
* @return string |
370
|
|
|
*/ |
371
|
|
|
public function getLastUsedPageBox() { |
372
|
|
|
return $this->lastUsedPageBox; |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
|
376
|
|
|
public function useTemplate($tplidx, $_x = null, $_y = null, $_w = 0, $_h = 0, $adjustPageSize = false) { |
377
|
|
|
if ($adjustPageSize == true && is_null($_x) && is_null($_y)) { |
|
|
|
|
378
|
|
|
$size = $this->getTemplateSize($tplidx, $_w, $_h); |
379
|
|
|
$orientation = $size['w'] > $size['h'] ? 'L' : 'P'; |
380
|
|
|
$size = array($size['w'], $size['h']); |
381
|
|
|
|
382
|
|
|
$this->setPageFormat($size, $orientation); |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
$this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values |
386
|
|
|
$s = parent::useTemplate($tplidx, $_x, $_y, $_w, $_h); |
387
|
|
|
$this->_out('Q'); |
388
|
|
|
|
389
|
|
|
return $s; |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
/** |
393
|
|
|
* Private method, that rebuilds all needed objects of source files |
394
|
|
|
*/ |
395
|
|
|
public function _putimportedobjects() { |
396
|
|
|
if (is_array($this->parsers) && count($this->parsers) > 0) { |
397
|
|
|
foreach($this->parsers AS $filename => $p) { |
398
|
|
|
$this->current_parser =& $this->parsers[$filename]; |
399
|
|
|
if (isset($this->_obj_stack[$filename]) && is_array($this->_obj_stack[$filename])) { |
400
|
|
|
while(($n = key($this->_obj_stack[$filename])) !== null) { |
401
|
|
|
$nObj = $this->current_parser->getObjectVal($this->_obj_stack[$filename][$n][1]); |
402
|
|
|
|
403
|
|
|
$this->_newobj($this->_obj_stack[$filename][$n][0]); |
404
|
|
|
|
405
|
|
|
if ($nObj[0] == PDF_TYPE_STREAM) { |
406
|
|
|
$this->pdf_write_value($nObj); |
407
|
|
|
} else { |
408
|
|
|
$this->pdf_write_value($nObj[1]); |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
$this->_out('endobj'); |
412
|
|
|
$this->_obj_stack[$filename][$n] = null; // free memory |
413
|
|
|
unset($this->_obj_stack[$filename][$n]); |
414
|
|
|
reset($this->_obj_stack[$filename]); |
415
|
|
|
} |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
// We're done with this parser. Clean it up to free a bit of RAM. |
419
|
|
|
$this->current_parser->cleanUp(); |
420
|
|
|
unset($this->parsers[$filename]); |
421
|
|
|
} |
422
|
|
|
} |
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
|
426
|
|
|
/** |
427
|
|
|
* Private Method that writes the form xobjects |
428
|
|
|
*/ |
429
|
|
|
public function _putformxobjects() { |
430
|
|
|
$filter=($this->compress) ? '/Filter /FlateDecode ' : ''; |
431
|
|
|
reset($this->tpls); |
432
|
|
|
foreach($this->tpls AS $tplidx => $tpl) { |
433
|
|
|
$p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer']; |
434
|
|
|
$this->_newobj(); |
435
|
|
|
$cN = $this->n; // TCPDF/Protection: rem current "n" |
436
|
|
|
|
437
|
|
|
$this->tpls[$tplidx]['n'] = $this->n; |
438
|
|
|
$this->_out('<<' . $filter . '/Type /XObject'); |
439
|
|
|
$this->_out('/Subtype /Form'); |
440
|
|
|
$this->_out('/FormType 1'); |
441
|
|
|
|
442
|
|
|
$this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]', |
443
|
|
|
(isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x']) * $this->k, |
444
|
|
|
(isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y']) * $this->k, |
445
|
|
|
(isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x']) * $this->k, |
446
|
|
|
(isset($tpl['box']['ury']) ? $tpl['box']['ury'] : $tpl['h'] - $tpl['y']) * $this->k |
447
|
|
|
)); |
448
|
|
|
|
449
|
|
|
$c = 1; |
450
|
|
|
$s = 0; |
451
|
|
|
$tx = 0; |
452
|
|
|
$ty = 0; |
453
|
|
|
|
454
|
|
|
if (isset($tpl['box'])) { |
455
|
|
|
$tx = -$tpl['box']['llx']; |
456
|
|
|
$ty = -$tpl['box']['lly']; |
457
|
|
|
|
458
|
|
|
if ($tpl['_rotationAngle'] <> 0) { |
459
|
|
|
$angle = $tpl['_rotationAngle'] * M_PI/180; |
460
|
|
|
$c=cos($angle); |
461
|
|
|
$s=sin($angle); |
462
|
|
|
|
463
|
|
|
switch($tpl['_rotationAngle']) { |
464
|
|
View Code Duplication |
case -90: |
|
|
|
|
465
|
|
|
$tx = -$tpl['box']['lly']; |
466
|
|
|
$ty = $tpl['box']['urx']; |
467
|
|
|
break; |
468
|
|
View Code Duplication |
case -180: |
|
|
|
|
469
|
|
|
$tx = $tpl['box']['urx']; |
470
|
|
|
$ty = $tpl['box']['ury']; |
471
|
|
|
break; |
472
|
|
View Code Duplication |
case -270: |
|
|
|
|
473
|
|
|
$tx = $tpl['box']['ury']; |
474
|
|
|
$ty = -$tpl['box']['llx']; |
475
|
|
|
break; |
476
|
|
|
} |
477
|
|
|
} |
478
|
|
|
} elseif ($tpl['x'] != 0 || $tpl['y'] != 0) { |
479
|
|
|
$tx = -$tpl['x'] * 2; |
480
|
|
|
$ty = $tpl['y'] * 2; |
481
|
|
|
} |
482
|
|
|
|
483
|
|
|
$tx *= $this->k; |
484
|
|
|
$ty *= $this->k; |
485
|
|
|
|
486
|
|
|
if ($c != 1 || $s != 0 || $tx != 0 || $ty != 0) { |
487
|
|
|
$this->_out(sprintf('/Matrix [%.5F %.5F %.5F %.5F %.5F %.5F]', |
488
|
|
|
$c, $s, -$s, $c, $tx, $ty |
489
|
|
|
)); |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
$this->_out('/Resources '); |
493
|
|
|
|
494
|
|
|
if (isset($tpl['resources'])) { |
495
|
|
|
$this->current_parser =& $tpl['parser']; |
496
|
|
|
$this->pdf_write_value($tpl['resources']); // "n" will be changed |
497
|
|
|
} else { |
498
|
|
|
$this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); |
499
|
|
View Code Duplication |
if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) { |
|
|
|
|
500
|
|
|
$this->_out('/Font <<'); |
501
|
|
|
foreach($this->_res['tpl'][$tplidx]['fonts'] as $font) |
502
|
|
|
$this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R'); |
503
|
|
|
$this->_out('>>'); |
504
|
|
|
} |
505
|
|
View Code Duplication |
if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) || |
|
|
|
|
506
|
|
|
isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) |
507
|
|
|
{ |
508
|
|
|
$this->_out('/XObject <<'); |
509
|
|
|
if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) { |
510
|
|
|
foreach($this->_res['tpl'][$tplidx]['images'] as $image) |
511
|
|
|
$this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R'); |
512
|
|
|
} |
513
|
|
|
if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) { |
514
|
|
|
foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl) |
515
|
|
|
$this->_out($this->tplprefix . $i . ' ' . $tpl['n'] . ' 0 R'); |
516
|
|
|
} |
517
|
|
|
$this->_out('>>'); |
518
|
|
|
} |
519
|
|
|
$this->_out('>>'); |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
$this->_out('/Group <</Type/Group/S/Transparency>>'); |
523
|
|
|
|
524
|
|
|
$nN = $this->n; // TCPDF: rem new "n" |
525
|
|
|
$this->n = $cN; // TCPDF: reset to current "n" |
526
|
|
|
|
527
|
|
|
$p = $this->_getrawstream($p); |
528
|
|
|
$this->_out('/Length ' . strlen($p) . ' >>'); |
529
|
|
|
$this->_out("stream\n" . $p . "\nendstream"); |
530
|
|
|
|
531
|
|
|
$this->_out('endobj'); |
532
|
|
|
$this->n = $nN; // TCPDF: reset to new "n" |
533
|
|
|
} |
534
|
|
|
|
535
|
|
|
$this->_putimportedobjects(); |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
/** |
539
|
|
|
* Rewritten to handle existing own defined objects |
540
|
|
|
*/ |
541
|
|
|
protected function _newobj($obj_id = false, $onlynewobj = false) { |
542
|
|
|
if (!$obj_id) { |
543
|
|
|
$obj_id = ++$this->n; |
544
|
|
|
} |
545
|
|
|
|
546
|
|
|
//Begin a new object |
547
|
|
|
if (!$onlynewobj) { |
548
|
|
|
$this->offsets[$obj_id] = $this->bufferlen; |
549
|
|
|
$this->_out($obj_id . ' 0 obj'); |
550
|
|
|
$this->_current_obj_id = $obj_id; // for later use with encryption |
|
|
|
|
551
|
|
|
} |
552
|
|
|
|
553
|
|
|
return $obj_id; |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
/** |
557
|
|
|
* Writes a value |
558
|
|
|
* Needed to rebuild the source document |
559
|
|
|
* |
560
|
|
|
* @param mixed $value A PDF-Value. Structure of values see cases in this method |
561
|
|
|
*/ |
562
|
|
|
public function pdf_write_value(&$value) |
563
|
|
|
{ |
564
|
|
|
switch ($value[0]) { |
565
|
|
View Code Duplication |
case PDF_TYPE_STRING: |
|
|
|
|
566
|
|
|
if ($this->encrypted) { |
567
|
|
|
$value[1] = $this->_unescape($value[1]); |
568
|
|
|
$value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]); |
569
|
|
|
$value[1] = TCPDF_STATIC::_escape($value[1]); |
570
|
|
|
} |
571
|
|
|
break; |
572
|
|
|
|
573
|
|
|
case PDF_TYPE_STREAM: |
574
|
|
|
if ($this->encrypted) { |
575
|
|
|
$value[2][1] = $this->_encrypt_data($this->_current_obj_id, $value[2][1]); |
576
|
|
|
$value[1][1]['/Length'] = array( |
577
|
|
|
PDF_TYPE_NUMERIC, |
578
|
|
|
strlen($value[2][1]) |
579
|
|
|
); |
580
|
|
|
} |
581
|
|
|
break; |
582
|
|
|
|
583
|
|
View Code Duplication |
case PDF_TYPE_HEX: |
|
|
|
|
584
|
|
|
if ($this->encrypted) { |
585
|
|
|
$value[1] = $this->hex2str($value[1]); |
586
|
|
|
$value[1] = $this->_encrypt_data($this->_current_obj_id, $value[1]); |
587
|
|
|
|
588
|
|
|
// remake hexstring of encrypted string |
589
|
|
|
$value[1] = $this->str2hex($value[1]); |
590
|
|
|
} |
591
|
|
|
break; |
592
|
|
|
} |
593
|
|
|
|
594
|
|
|
switch ($value[0]) { |
595
|
|
|
|
596
|
|
|
case PDF_TYPE_TOKEN: |
597
|
|
|
$this->_straightOut('/'.$value[1] . ' '); |
598
|
|
|
break; |
599
|
|
|
case PDF_TYPE_NUMERIC: |
600
|
|
|
case PDF_TYPE_REAL: |
601
|
|
|
if (is_float($value[1]) && $value[1] != 0) { |
602
|
|
|
$this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' '); |
603
|
|
|
} else { |
604
|
|
|
$this->_straightOut($value[1] . ' '); |
605
|
|
|
} |
606
|
|
|
break; |
607
|
|
|
|
608
|
|
|
case PDF_TYPE_ARRAY: |
609
|
|
|
|
610
|
|
|
// An array. Output the proper |
611
|
|
|
// structure and move on. |
612
|
|
|
|
613
|
|
|
$this->_straightOut('['); |
614
|
|
|
for ($i = 0; $i < count($value[1]); $i++) { |
|
|
|
|
615
|
|
|
$this->pdf_write_value($value[1][$i]); |
616
|
|
|
} |
617
|
|
|
|
618
|
|
|
$this->_out(']'); |
619
|
|
|
break; |
620
|
|
|
|
621
|
|
|
case PDF_TYPE_DICTIONARY: |
622
|
|
|
|
623
|
|
|
// A dictionary. |
624
|
|
|
$this->_straightOut('<<'); |
625
|
|
|
|
626
|
|
|
foreach ($value[1] as $k => $v) { |
627
|
|
|
$this->_straightOut($k . ' '); |
628
|
|
|
$this->pdf_write_value($v); |
629
|
|
|
} |
630
|
|
|
|
631
|
|
|
$this->_straightOut('>>'); |
632
|
|
|
break; |
633
|
|
|
|
634
|
|
|
case PDF_TYPE_OBJREF: |
635
|
|
|
|
636
|
|
|
// An indirect object reference |
637
|
|
|
// Fill the object stack if needed |
638
|
|
|
$cpfn =& $this->current_parser->uniqueid; |
639
|
|
|
|
640
|
|
|
if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) { |
641
|
|
|
$this->_newobj(false, true); |
642
|
|
|
$this->_obj_stack[$cpfn][$value[1]] = array($this->n, $value); |
643
|
|
|
$this->_don_obj_stack[$cpfn][$value[1]] = array($this->n, $value); // Value is maybee obsolete!!! |
644
|
|
|
} |
645
|
|
|
$objid = $this->_don_obj_stack[$cpfn][$value[1]][0]; |
646
|
|
|
|
647
|
|
|
$this->_out($objid . ' 0 R'); |
648
|
|
|
break; |
649
|
|
|
|
650
|
|
|
case PDF_TYPE_STRING: |
651
|
|
|
|
652
|
|
|
// A string. |
653
|
|
|
$this->_straightOut('(' . $value[1] . ')'); |
654
|
|
|
|
655
|
|
|
break; |
656
|
|
|
|
657
|
|
|
case PDF_TYPE_STREAM: |
658
|
|
|
|
659
|
|
|
// A stream. First, output the |
660
|
|
|
// stream dictionary, then the |
661
|
|
|
// stream data itself. |
662
|
|
|
$this->pdf_write_value($value[1]); |
663
|
|
|
$this->_out('stream'); |
664
|
|
|
$this->_out($value[2][1]); |
665
|
|
|
$this->_out('endstream'); |
666
|
|
|
break; |
667
|
|
|
|
668
|
|
|
case PDF_TYPE_HEX: |
669
|
|
|
$this->_straightOut('<' . $value[1] . '>'); |
670
|
|
|
break; |
671
|
|
|
|
672
|
|
|
case PDF_TYPE_BOOLEAN: |
673
|
|
|
$this->_straightOut($value[1] ? 'true ' : 'false '); |
674
|
|
|
break; |
675
|
|
|
|
676
|
|
|
case PDF_TYPE_NULL: |
677
|
|
|
// The null object. |
678
|
|
|
|
679
|
|
|
$this->_straightOut('null '); |
680
|
|
|
break; |
681
|
|
|
} |
682
|
|
|
} |
683
|
|
|
|
684
|
|
|
/** |
685
|
|
|
* Modified so not each call will add a newline to the output. |
686
|
|
|
*/ |
687
|
|
|
protected function _straightOut($s) { |
688
|
|
|
if ($this->state == 2) { |
689
|
|
|
if ($this->inxobj) { |
690
|
|
|
// we are inside an XObject template |
691
|
|
|
$this->xobjects[$this->xobjid]['outdata'] .= $s; |
692
|
|
|
} elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) { |
693
|
|
|
// puts data before page footer |
694
|
|
|
$pagebuff = $this->getPageBuffer($this->page); |
695
|
|
|
$page = substr($pagebuff, 0, -$this->footerlen[$this->page]); |
696
|
|
|
$footer = substr($pagebuff, -$this->footerlen[$this->page]); |
697
|
|
|
$this->setPageBuffer($this->page, $page.$s.$footer); |
698
|
|
|
// update footer position |
699
|
|
|
$this->footerpos[$this->page] += strlen($s); |
700
|
|
|
} else { |
701
|
|
|
// set page data |
702
|
|
|
$this->setPageBuffer($this->page, $s, true); |
703
|
|
|
} |
704
|
|
|
} elseif ($this->state > 0) { |
705
|
|
|
// set general data |
706
|
|
|
$this->setBuffer($s); |
707
|
|
|
} |
708
|
|
|
} |
709
|
|
|
|
710
|
|
|
/** |
711
|
|
|
* rewritten to close opened parsers |
712
|
|
|
* |
713
|
|
|
*/ |
714
|
|
|
protected function _enddoc() { |
715
|
|
|
parent::_enddoc(); |
716
|
|
|
$this->_closeParsers(); |
717
|
|
|
} |
718
|
|
|
|
719
|
|
|
/** |
720
|
|
|
* close all files opened by parsers |
721
|
|
|
*/ |
722
|
|
|
protected function _closeParsers() { |
723
|
|
|
if ($this->state > 2 && count($this->parsers) > 0) { |
724
|
|
|
$this->cleanUp(); |
725
|
|
|
return true; |
726
|
|
|
} |
727
|
|
|
return false; |
728
|
|
|
} |
729
|
|
|
|
730
|
|
|
/** |
731
|
|
|
* Removes cylced references and closes the file handles of the parser objects |
732
|
|
|
*/ |
733
|
|
|
public function cleanUp() { |
734
|
|
|
foreach ($this->parsers as $k => $_){ |
735
|
|
|
$this->parsers[$k]->cleanUp(); |
736
|
|
|
$this->parsers[$k] = null; |
737
|
|
|
unset($this->parsers[$k]); |
738
|
|
|
} |
739
|
|
|
} |
740
|
|
|
|
741
|
|
|
// Functions from here on are taken from FPDI's fpdi2tcpdf_bridge.php to remove dependence on it |
742
|
|
|
protected function _putstream($s, $n=0) { |
743
|
|
|
$this->_out($this->_getstream($s, $n)); |
744
|
|
|
} |
745
|
|
|
|
746
|
|
|
protected function _getxobjectdict() { |
747
|
|
|
$out = parent::_getxobjectdict(); |
748
|
|
View Code Duplication |
if (count($this->tpls)) { |
|
|
|
|
749
|
|
|
foreach($this->tpls as $tplidx => $tpl) { |
750
|
|
|
$out .= sprintf('%s%d %d 0 R', $this->tplprefix, $tplidx, $tpl['n']); |
751
|
|
|
} |
752
|
|
|
} |
753
|
|
|
|
754
|
|
|
return $out; |
755
|
|
|
} |
756
|
|
|
|
757
|
|
|
/** |
758
|
|
|
* Unescapes a PDF string |
759
|
|
|
* |
760
|
|
|
* @param string $s |
761
|
|
|
* @return string |
762
|
|
|
*/ |
763
|
|
|
protected function _unescape($s) { |
764
|
|
|
$out = ''; |
765
|
|
|
for ($count = 0, $n = strlen($s); $count < $n; $count++) { |
766
|
|
|
if ($s[$count] != '\\' || $count == $n-1) { |
767
|
|
|
$out .= $s[$count]; |
768
|
|
|
} else { |
769
|
|
|
switch ($s[++$count]) { |
770
|
|
|
case ')': |
771
|
|
|
case '(': |
772
|
|
|
case '\\': |
773
|
|
|
$out .= $s[$count]; |
774
|
|
|
break; |
775
|
|
|
case 'f': |
776
|
|
|
$out .= chr(0x0C); |
777
|
|
|
break; |
778
|
|
|
case 'b': |
779
|
|
|
$out .= chr(0x08); |
780
|
|
|
break; |
781
|
|
|
case 't': |
782
|
|
|
$out .= chr(0x09); |
783
|
|
|
break; |
784
|
|
|
case 'r': |
785
|
|
|
$out .= chr(0x0D); |
786
|
|
|
break; |
787
|
|
|
case 'n': |
788
|
|
|
$out .= chr(0x0A); |
789
|
|
|
break; |
790
|
|
|
case "\r": |
791
|
|
|
if ($count != $n-1 && $s[$count+1] == "\n") |
792
|
|
|
$count++; |
793
|
|
|
break; |
794
|
|
|
case "\n": |
795
|
|
|
break; |
796
|
|
|
default: |
797
|
|
|
// Octal-Values |
798
|
|
|
if (ord($s[$count]) >= ord('0') && |
799
|
|
|
ord($s[$count]) <= ord('9')) { |
800
|
|
|
$oct = ''. $s[$count]; |
801
|
|
|
|
802
|
|
|
if (ord($s[$count+1]) >= ord('0') && |
803
|
|
|
ord($s[$count+1]) <= ord('9')) { |
804
|
|
|
$oct .= $s[++$count]; |
805
|
|
|
|
806
|
|
|
if (ord($s[$count+1]) >= ord('0') && |
807
|
|
|
ord($s[$count+1]) <= ord('9')) { |
808
|
|
|
$oct .= $s[++$count]; |
809
|
|
|
} |
810
|
|
|
} |
811
|
|
|
|
812
|
|
|
$out .= chr(octdec($oct)); |
813
|
|
|
} else { |
814
|
|
|
$out .= $s[$count]; |
815
|
|
|
} |
816
|
|
|
} |
817
|
|
|
} |
818
|
|
|
} |
819
|
|
|
return $out; |
820
|
|
|
} |
821
|
|
|
|
822
|
|
|
/** |
823
|
|
|
* Hexadecimal to string |
824
|
|
|
* |
825
|
|
|
* @param string $hex |
826
|
|
|
* @return string |
827
|
|
|
*/ |
828
|
|
|
public function hex2str($hex) { |
829
|
|
|
return pack('H*', str_replace(array("\r", "\n", ' '), '', $hex)); |
830
|
|
|
} |
831
|
|
|
|
832
|
|
|
/** |
833
|
|
|
* String to hexadecimal |
834
|
|
|
* |
835
|
|
|
* @param string $str |
836
|
|
|
* @return string |
837
|
|
|
*/ |
838
|
|
|
public function str2hex($str) { |
839
|
|
|
return current(unpack('H*', $str)); |
840
|
|
|
} |
841
|
|
|
} |
842
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.