commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-new / civicrm / packages / FPDI / fpdi.php
1 <?php
2 //
3 // FPDI - Version 1.5
4 //
5 // Copyright 2004-2014 Setasign - Jan Slabon
6 //
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
10 //
11 // http://www.apache.org/licenses/LICENSE-2.0
12 //
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
18 //
19
20 require_once('fpdf_tpl.php');
21
22 /**
23 * Class FPDI
24 */
25 class FPDI extends FPDF_TPL
26 {
27 /**
28 * FPDI version
29 *
30 * @string
31 */
32 const VERSION = '1.5.0';
33
34 /**
35 * Actual filename
36 *
37 * @var string
38 */
39 public $currentFilename;
40
41 /**
42 * Parser-Objects
43 *
44 * @var fpdi_pdf_parser[]
45 */
46 public $parsers;
47
48 /**
49 * Current parser
50 *
51 * @var fpdi_pdf_parser
52 */
53 public $currentParser;
54
55 /**
56 * The name of the last imported page box
57 *
58 * @var string
59 */
60 public $lastUsedPageBox;
61
62 /**
63 * Object stack
64 *
65 * @var array
66 */
67 protected $_objStack;
68
69 /**
70 * Done object stack
71 *
72 * @var array
73 */
74 protected $_doneObjStack;
75
76 /**
77 * Current Object Id.
78 *
79 * @var integer
80 */
81 protected $_currentObjId;
82
83 /**
84 * Cache for imported pages/template ids
85 *
86 * @var array
87 */
88 protected $_importedPages = array();
89
90 /**
91 * Set a source-file.
92 *
93 * Depending on the PDF version of the used document the PDF version of the resulting document will
94 * be adjusted to the higher version.
95 *
96 * @param string $filename A valid path to the PDF document from which pages should be imported from
97 * @return int The number of pages in the document
98 */
99 public function setSourceFile($filename)
100 {
101 $_filename = realpath($filename);
102 if (false !== $_filename)
103 $filename = $_filename;
104
105 $this->currentFilename = $filename;
106
107 if (!isset($this->parsers[$filename])) {
108 $this->parsers[$filename] = $this->_getPdfParser($filename);
109 $this->setPdfVersion(
110 max($this->getPdfVersion(), $this->parsers[$filename]->getPdfVersion())
111 );
112 }
113
114 $this->currentParser =& $this->parsers[$filename];
115
116 return $this->parsers[$filename]->getPageCount();
117 }
118
119 /**
120 * Returns a PDF parser object
121 *
122 * @param string $filename
123 * @return fpdi_pdf_parser
124 */
125 protected function _getPdfParser($filename)
126 {
127 require_once('fpdi_pdf_parser.php');
128 return new fpdi_pdf_parser($filename);
129 }
130
131 /**
132 * Get the current PDF version.
133 *
134 * @return string
135 */
136 public function getPdfVersion()
137 {
138 return $this->PDFVersion;
139 }
140
141 /**
142 * Set the PDF version.
143 *
144 * @param string $version
145 */
146 public function setPdfVersion($version = '1.3')
147 {
148 $this->PDFVersion = $version;
149 }
150
151 /**
152 * Import a page.
153 *
154 * The second parameter defines the bounding box that should be used to transform the page into a
155 * form XObject.
156 *
157 * Following values are available: MediaBox, CropBox, BleedBox, TrimBox, ArtBox.
158 * If a box is not especially defined its default box will be used:
159 *
160 * <ul>
161 * <li>CropBox: Default -> MediaBox</li>
162 * <li>BleedBox: Default -> CropBox</li>
163 * <li>TrimBox: Default -> CropBox</li>
164 * <li>ArtBox: Default -> CropBox</li>
165 * </ul>
166 *
167 * It is possible to get the used page box by the {@link getLastUsedPageBox()} method.
168 *
169 * @param int $pageNo The page number
170 * @param string $boxName The boundary box to use when transforming the page into a form XObject
171 * @param boolean $groupXObject Define the form XObject as a group XObject to support transparency (if used)
172 * @return int An id of the imported page/template to use with e.g. fpdf_tpl::useTemplate()
173 * @throws LogicException|InvalidArgumentException
174 * @see getLastUsedPageBox()
175 */
176 public function importPage($pageNo, $boxName = 'CropBox', $groupXObject = true)
177 {
178 if ($this->_inTpl) {
179 throw new LogicException('Please import the desired pages before creating a new template.');
180 }
181
182 $fn = $this->currentFilename;
183 $boxName = '/' . ltrim($boxName, '/');
184
185 // check if page already imported
186 $pageKey = $fn . '-' . ((int)$pageNo) . $boxName;
187 if (isset($this->_importedPages[$pageKey])) {
188 return $this->_importedPages[$pageKey];
189 }
190
191 $parser = $this->parsers[$fn];
192 $parser->setPageNo($pageNo);
193
194 if (!in_array($boxName, $parser->availableBoxes)) {
195 throw new InvalidArgumentException(sprintf('Unknown box: %s', $boxName));
196 }
197
198 $pageBoxes = $parser->getPageBoxes($pageNo, $this->k);
199
200 /**
201 * MediaBox
202 * CropBox: Default -> MediaBox
203 * BleedBox: Default -> CropBox
204 * TrimBox: Default -> CropBox
205 * ArtBox: Default -> CropBox
206 */
207 if (!isset($pageBoxes[$boxName]) && ($boxName == '/BleedBox' || $boxName == '/TrimBox' || $boxName == '/ArtBox'))
208 $boxName = '/CropBox';
209 if (!isset($pageBoxes[$boxName]) && $boxName == '/CropBox')
210 $boxName = '/MediaBox';
211
212 if (!isset($pageBoxes[$boxName]))
213 return false;
214
215 $this->lastUsedPageBox = $boxName;
216
217 $box = $pageBoxes[$boxName];
218
219 $this->tpl++;
220 $this->_tpls[$this->tpl] = array();
221 $tpl =& $this->_tpls[$this->tpl];
222 $tpl['parser'] = $parser;
223 $tpl['resources'] = $parser->getPageResources();
224 $tpl['buffer'] = $parser->getContent();
225 $tpl['box'] = $box;
226 $tpl['groupXObject'] = $groupXObject;
227 if ($groupXObject) {
228 $this->setPdfVersion(max($this->getPdfVersion(), 1.4));
229 }
230
231 // To build an array that can be used by PDF_TPL::useTemplate()
232 $this->_tpls[$this->tpl] = array_merge($this->_tpls[$this->tpl], $box);
233
234 // An imported page will start at 0,0 all the time. Translation will be set in _putformxobjects()
235 $tpl['x'] = 0;
236 $tpl['y'] = 0;
237
238 // handle rotated pages
239 $rotation = $parser->getPageRotation($pageNo);
240 $tpl['_rotationAngle'] = 0;
241 if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0) {
242 $steps = $angle / 90;
243
244 $_w = $tpl['w'];
245 $_h = $tpl['h'];
246 $tpl['w'] = $steps % 2 == 0 ? $_w : $_h;
247 $tpl['h'] = $steps % 2 == 0 ? $_h : $_w;
248
249 if ($angle < 0)
250 $angle += 360;
251
252 $tpl['_rotationAngle'] = $angle * -1;
253 }
254
255 $this->_importedPages[$pageKey] = $this->tpl;
256
257 return $this->tpl;
258 }
259
260 /**
261 * Returns the last used page boundary box.
262 *
263 * @return string The used boundary box: MediaBox, CropBox, BleedBox, TrimBox or ArtBox
264 */
265 public function getLastUsedPageBox()
266 {
267 return $this->lastUsedPageBox;
268 }
269
270 /**
271 * Use a template or imported page in current page or other template.
272 *
273 * You can use a template in a page or in another template.
274 * You can give the used template a new size. All parameters are optional.
275 * The width or height is calculated automatically if one is given. If no
276 * parameter is given the origin size as defined in beginTemplate() or of
277 * the imported page is used.
278 *
279 * The calculated or used width and height are returned as an array.
280 *
281 * @param int $tplIdx A valid template-id
282 * @param int $x The x-position
283 * @param int $y The y-position
284 * @param int $w The new width of the template
285 * @param int $h The new height of the template
286 * @param boolean $adjustPageSize If set to true the current page will be resized to fit the dimensions
287 * of the template
288 *
289 * @return array The height and width of the template (array('w' => ..., 'h' => ...))
290 * @throws LogicException|InvalidArgumentException
291 */
292 public function useTemplate($tplIdx, $x = null, $y = null, $w = 0, $h = 0, $adjustPageSize = false)
293 {
294 if ($adjustPageSize == true && is_null($x) && is_null($y)) {
295 $size = $this->getTemplateSize($tplIdx, $w, $h);
296 $orientation = $size['w'] > $size['h'] ? 'L' : 'P';
297 $size = array($size['w'], $size['h']);
298
299 if (is_subclass_of($this, 'TCPDF')) {
300 $this->setPageFormat($size, $orientation);
301 } else {
302 $size = $this->_getpagesize($size);
303
304 if($orientation != $this->CurOrientation ||
305 $size[0] != $this->CurPageSize[0] ||
306 $size[1] != $this->CurPageSize[1]
307 ) {
308 // New size or orientation
309 if ($orientation=='P') {
310 $this->w = $size[0];
311 $this->h = $size[1];
312 } else {
313 $this->w = $size[1];
314 $this->h = $size[0];
315 }
316 $this->wPt = $this->w * $this->k;
317 $this->hPt = $this->h * $this->k;
318 $this->PageBreakTrigger = $this->h - $this->bMargin;
319 $this->CurOrientation = $orientation;
320 $this->CurPageSize = $size;
321 $this->PageSizes[$this->page] = array($this->wPt, $this->hPt);
322 }
323 }
324 }
325
326 $this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values
327 $size = parent::useTemplate($tplIdx, $x, $y, $w, $h);
328 $this->_out('Q');
329
330 return $size;
331 }
332
333 /**
334 * Copy all imported objects to the resulting document.
335 */
336 protected function _putimportedobjects()
337 {
338 if (!is_array($this->parsers) || count($this->parsers) === 0) {
339 return;
340 }
341
342 foreach($this->parsers AS $filename => $p) {
343 $this->currentParser =& $p;
344 if (!isset($this->_objStack[$filename]) || !is_array($this->_objStack[$filename])) {
345 continue;
346 }
347 while(($n = key($this->_objStack[$filename])) !== null) {
348 $nObj = $this->currentParser->resolveObject($this->_objStack[$filename][$n][1]);
349
350 $this->_newobj($this->_objStack[$filename][$n][0]);
351
352 if ($nObj[0] == pdf_parser::TYPE_STREAM) {
353 $this->_writeValue($nObj);
354 } else {
355 $this->_writeValue($nObj[1]);
356 }
357
358 $this->_out("\nendobj");
359 $this->_objStack[$filename][$n] = null; // free memory
360 unset($this->_objStack[$filename][$n]);
361 reset($this->_objStack[$filename]);
362 }
363 }
364 }
365
366 /**
367 * Writes the form XObjects to the PDF document.
368 */
369 protected function _putformxobjects()
370 {
371 $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
372 reset($this->_tpls);
373 foreach($this->_tpls AS $tplIdx => $tpl) {
374 $this->_newobj();
375 $currentN = $this->n; // TCPDF/Protection: rem current "n"
376
377 $this->_tpls[$tplIdx]['n'] = $this->n;
378 $this->_out('<<' . $filter . '/Type /XObject');
379 $this->_out('/Subtype /Form');
380 $this->_out('/FormType 1');
381
382 $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]',
383 (isset($tpl['box']['llx']) ? $tpl['box']['llx'] : $tpl['x']) * $this->k,
384 (isset($tpl['box']['lly']) ? $tpl['box']['lly'] : -$tpl['y']) * $this->k,
385 (isset($tpl['box']['urx']) ? $tpl['box']['urx'] : $tpl['w'] + $tpl['x']) * $this->k,
386 (isset($tpl['box']['ury']) ? $tpl['box']['ury'] : $tpl['h'] - $tpl['y']) * $this->k
387 ));
388
389 $c = 1;
390 $s = 0;
391 $tx = 0;
392 $ty = 0;
393
394 if (isset($tpl['box'])) {
395 $tx = -$tpl['box']['llx'];
396 $ty = -$tpl['box']['lly'];
397
398 if ($tpl['_rotationAngle'] <> 0) {
399 $angle = $tpl['_rotationAngle'] * M_PI/180;
400 $c = cos($angle);
401 $s = sin($angle);
402
403 switch($tpl['_rotationAngle']) {
404 case -90:
405 $tx = -$tpl['box']['lly'];
406 $ty = $tpl['box']['urx'];
407 break;
408 case -180:
409 $tx = $tpl['box']['urx'];
410 $ty = $tpl['box']['ury'];
411 break;
412 case -270:
413 $tx = $tpl['box']['ury'];
414 $ty = -$tpl['box']['llx'];
415 break;
416 }
417 }
418 } else if ($tpl['x'] != 0 || $tpl['y'] != 0) {
419 $tx = -$tpl['x'] * 2;
420 $ty = $tpl['y'] * 2;
421 }
422
423 $tx *= $this->k;
424 $ty *= $this->k;
425
426 if ($c != 1 || $s != 0 || $tx != 0 || $ty != 0) {
427 $this->_out(sprintf('/Matrix [%.5F %.5F %.5F %.5F %.5F %.5F]',
428 $c, $s, -$s, $c, $tx, $ty
429 ));
430 }
431
432 $this->_out('/Resources ');
433
434 if (isset($tpl['resources'])) {
435 $this->currentParser = $tpl['parser'];
436 $this->_writeValue($tpl['resources']); // "n" will be changed
437 } else {
438
439 $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
440 if (isset($this->_res['tpl'][$tplIdx])) {
441 $res = $this->_res['tpl'][$tplIdx];
442
443 if (isset($res['fonts']) && count($res['fonts'])) {
444 $this->_out('/Font <<');
445 foreach ($res['fonts'] as $font)
446 $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
447 $this->_out('>>');
448 }
449 if (isset($res['images']) && count($res['images']) ||
450 isset($res['tpls']) && count($res['tpls']))
451 {
452 $this->_out('/XObject <<');
453 if (isset($res['images'])) {
454 foreach ($res['images'] as $image)
455 $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
456 }
457 if (isset($res['tpls'])) {
458 foreach ($res['tpls'] as $i => $_tpl)
459 $this->_out($this->tplPrefix . $i . ' ' . $_tpl['n'] . ' 0 R');
460 }
461 $this->_out('>>');
462 }
463 $this->_out('>>');
464 }
465 }
466
467 if (isset($tpl['groupXObject']) && $tpl['groupXObject']) {
468 $this->_out('/Group <</Type/Group/S/Transparency>>');
469 }
470
471 $newN = $this->n; // TCPDF: rem new "n"
472 $this->n = $currentN; // TCPDF: reset to current "n"
473
474 $buffer = ($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
475
476 if (is_subclass_of($this, 'TCPDF')) {
477 $buffer = $this->_getrawstream($buffer);
478 $this->_out('/Length ' . strlen($buffer) . ' >>');
479 $this->_out("stream\n" . $buffer . "\nendstream");
480 } else {
481 $this->_out('/Length ' . strlen($buffer) . ' >>');
482 $this->_putstream($buffer);
483 }
484 $this->_out('endobj');
485 $this->n = $newN; // TCPDF: reset to new "n"
486 }
487
488 $this->_putimportedobjects();
489 }
490
491 /**
492 * Creates and optionally write the object definition to the document.
493 *
494 * Rewritten to handle existing own defined objects
495 *
496 * @param bool $objId
497 * @param bool $onlyNewObj
498 * @return bool|int
499 */
500 public function _newobj($objId = false, $onlyNewObj = false)
501 {
502 if (!$objId) {
503 $objId = ++$this->n;
504 }
505
506 //Begin a new object
507 if (!$onlyNewObj) {
508 $this->offsets[$objId] = is_subclass_of($this, 'TCPDF') ? $this->bufferlen : strlen($this->buffer);
509 $this->_out($objId . ' 0 obj');
510 $this->_currentObjId = $objId; // for later use with encryption
511 }
512
513 return $objId;
514 }
515
516 /**
517 * Writes a PDF value to the resulting document.
518 *
519 * Needed to rebuild the source document
520 *
521 * @param mixed $value A PDF-Value. Structure of values see cases in this method
522 */
523 protected function _writeValue(&$value)
524 {
525 if (is_subclass_of($this, 'TCPDF')) {
526 parent::_prepareValue($value);
527 }
528
529 switch ($value[0]) {
530
531 case pdf_parser::TYPE_TOKEN:
532 $this->_straightOut($value[1] . ' ');
533 break;
534 case pdf_parser::TYPE_NUMERIC:
535 case pdf_parser::TYPE_REAL:
536 if (is_float($value[1]) && $value[1] != 0) {
537 $this->_straightOut(rtrim(rtrim(sprintf('%F', $value[1]), '0'), '.') . ' ');
538 } else {
539 $this->_straightOut($value[1] . ' ');
540 }
541 break;
542
543 case pdf_parser::TYPE_ARRAY:
544
545 // An array. Output the proper
546 // structure and move on.
547
548 $this->_straightOut('[');
549 for ($i = 0; $i < count($value[1]); $i++) {
550 $this->_writeValue($value[1][$i]);
551 }
552
553 $this->_out(']');
554 break;
555
556 case pdf_parser::TYPE_DICTIONARY:
557
558 // A dictionary.
559 $this->_straightOut('<<');
560
561 reset ($value[1]);
562
563 while (list($k, $v) = each($value[1])) {
564 $this->_straightOut($k . ' ');
565 $this->_writeValue($v);
566 }
567
568 $this->_straightOut('>>');
569 break;
570
571 case pdf_parser::TYPE_OBJREF:
572
573 // An indirect object reference
574 // Fill the object stack if needed
575 $cpfn =& $this->currentParser->filename;
576 if (!isset($this->_doneObjStack[$cpfn][$value[1]])) {
577 $this->_newobj(false, true);
578 $this->_objStack[$cpfn][$value[1]] = array($this->n, $value);
579 $this->_doneObjStack[$cpfn][$value[1]] = array($this->n, $value);
580 }
581 $objId = $this->_doneObjStack[$cpfn][$value[1]][0];
582
583 $this->_out($objId . ' 0 R');
584 break;
585
586 case pdf_parser::TYPE_STRING:
587
588 // A string.
589 $this->_straightOut('(' . $value[1] . ')');
590
591 break;
592
593 case pdf_parser::TYPE_STREAM:
594
595 // A stream. First, output the
596 // stream dictionary, then the
597 // stream data itself.
598 $this->_writeValue($value[1]);
599 $this->_out('stream');
600 $this->_out($value[2][1]);
601 $this->_straightOut("endstream");
602 break;
603
604 case pdf_parser::TYPE_HEX:
605 $this->_straightOut('<' . $value[1] . '>');
606 break;
607
608 case pdf_parser::TYPE_BOOLEAN:
609 $this->_straightOut($value[1] ? 'true ' : 'false ');
610 break;
611
612 case pdf_parser::TYPE_NULL:
613 // The null object.
614
615 $this->_straightOut('null ');
616 break;
617 }
618 }
619
620
621 /**
622 * Modified _out() method so not each call will add a newline to the output.
623 */
624 protected function _straightOut($s)
625 {
626 if (!is_subclass_of($this, 'TCPDF')) {
627 if ($this->state == 2) {
628 $this->pages[$this->page] .= $s;
629 } else {
630 $this->buffer .= $s;
631 }
632
633 } else {
634 if ($this->state == 2) {
635 if ($this->inxobj) {
636 // we are inside an XObject template
637 $this->xobjects[$this->xobjid]['outdata'] .= $s;
638 } else if ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
639 // puts data before page footer
640 $pagebuff = $this->getPageBuffer($this->page);
641 $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
642 $footer = substr($pagebuff, -$this->footerlen[$this->page]);
643 $this->setPageBuffer($this->page, $page . $s . $footer);
644 // update footer position
645 $this->footerpos[$this->page] += strlen($s);
646 } else {
647 // set page data
648 $this->setPageBuffer($this->page, $s, true);
649 }
650 } else if ($this->state > 0) {
651 // set general data
652 $this->setBuffer($s);
653 }
654 }
655 }
656
657 /**
658 * Ends the document
659 *
660 * Overwritten to close opened parsers
661 */
662 public function _enddoc()
663 {
664 parent::_enddoc();
665 $this->_closeParsers();
666 }
667
668 /**
669 * Close all files opened by parsers.
670 *
671 * @return boolean
672 */
673 protected function _closeParsers()
674 {
675 if ($this->state > 2) {
676 $this->cleanUp();
677 return true;
678 }
679
680 return false;
681 }
682
683 /**
684 * Removes cycled references and closes the file handles of the parser objects.
685 */
686 public function cleanUp()
687 {
688 while (($parser = array_pop($this->parsers)) !== null) {
689 /**
690 * @var fpdi_pdf_parser $parser
691 */
692 $parser->closeFile();
693 }
694 }
695 }