Fixed php 7.2 count() compatibility and intval cast in number_format2 parameter 2.
[fa-stable.git] / reporting / includes / fpdi / fpdi.php
1 <?php
2 //
3 //  FPDI - Version 1.2.1
4 //
5 //    Copyright 2004-2008 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 define('FPDI_VERSION','1.2.1');
21
22 // Check for TCPDF and remap TCPDF to FPDF
23 if (class_exists('TCPDF')) {
24     require_once('fpdi2tcpdf_bridge.php');
25 }
26
27 require_once("fpdf_tpl.php");
28 require_once("fpdi_pdf_parser.php");
29
30
31 class FPDI extends FPDF_TPL {
32     /**
33      * Actual filename
34      * @var string
35      */
36     var $current_filename;
37
38     /**
39      * Parser-Objects
40      * @var array
41      */
42     var $parsers;
43     
44     /**
45      * Current parser
46      * @var object
47      */
48     var $current_parser;
49     
50     /**
51      * object stack
52      * @var array
53      */
54     var $_obj_stack;
55     
56     /**
57      * done object stack
58      * @var array
59      */
60     var $_don_obj_stack;
61
62     /**
63      * Current Object Id.
64      * @var integer
65      */
66     var $_current_obj_id;
67     
68     /**
69      * The name of the last imported page box
70      * @var string
71      */
72     var $lastUsedPageBox;
73     
74     var $_importedPages = array();
75     
76     
77     /**
78      * Set a source-file
79      *
80      * @param string $filename a valid filename
81      * @return int number of available pages
82      */
83     function setSourceFile($filename) {
84         $this->current_filename = $filename;
85         $fn =& $this->current_filename;
86
87         if (!isset($this->parsers[$fn]))
88             $this->parsers[$fn] = new fpdi_pdf_parser($fn,$this);
89         $this->current_parser =& $this->parsers[$fn];
90         
91         return $this->parsers[$fn]->getPageCount();
92     }
93     
94     /**
95      * Import a page
96      *
97      * @param int $pageno pagenumber
98      * @return int Index of imported page - to use with fpdf_tpl::useTemplate()
99      */
100     function importPage($pageno, $boxName='/CropBox') {
101         if ($this->_intpl) {
102             return $this->error("Please import the desired pages before creating a new template.");
103         }
104         
105         $fn =& $this->current_filename;
106         
107         // check if page already imported
108         $pageKey = $fn.((int)$pageno).$boxName;
109         if (isset($this->_importedPages[$pageKey]))
110             return $this->_importedPages[$pageKey];
111         
112         $parser =& $this->parsers[$fn];
113         $parser->setPageno($pageno);
114
115         $this->tpl++;
116         $this->tpls[$this->tpl] = array();
117         $tpl =& $this->tpls[$this->tpl];
118         $tpl['parser'] =& $parser;
119         $tpl['resources'] = $parser->getPageResources();
120         $tpl['buffer'] = $parser->getContent();
121         
122         if (!in_array($boxName, $parser->availableBoxes))
123             return $this->Error(sprintf("Unknown box: %s", $boxName));
124         $pageboxes = $parser->getPageBoxes($pageno);
125         
126         /**
127          * MediaBox
128          * CropBox: Default -> MediaBox
129          * BleedBox: Default -> CropBox
130          * TrimBox: Default -> CropBox
131          * ArtBox: Default -> CropBox
132          */
133         if (!isset($pageboxes[$boxName]) && ($boxName == "/BleedBox" || $boxName == "/TrimBox" || $boxName == "/ArtBox"))
134             $boxName = "/CropBox";
135         if (!isset($pageboxes[$boxName]) && $boxName == "/CropBox")
136             $boxName = "/MediaBox";
137         
138         if (!isset($pageboxes[$boxName]))
139             return false;
140         $this->lastUsedPageBox = $boxName;
141         
142         $box = $pageboxes[$boxName];
143         $tpl['box'] = $box;
144         
145         // To build an array that can be used by PDF_TPL::useTemplate()
146         $this->tpls[$this->tpl] = array_merge($this->tpls[$this->tpl],$box);
147         // An imported page will start at 0,0 everytime. Translation will be set in _putformxobjects()
148         $tpl['x'] = 0;
149         $tpl['y'] = 0;
150         
151         $page =& $parser->pages[$parser->pageno];
152         
153         // fix for rotated pages
154         $rotation = $parser->getPageRotation($pageno);
155         if (isset($rotation[1]) && ($angle = $rotation[1] % 360) != 0) {
156             $steps = $angle / 90;
157                 
158             $_w = $tpl['w'];
159             $_h = $tpl['h'];
160             $tpl['w'] = $steps % 2 == 0 ? $_w : $_h;
161             $tpl['h'] = $steps % 2 == 0 ? $_h : $_w;
162             
163             if ($steps % 2 != 0) {
164                 $x = $y = ($steps == 1 || $steps == -3) ? $tpl['h'] : $tpl['w'];
165             } else {
166                 $x = $tpl['w'];
167                 $y = $tpl['h'];
168             }
169             
170             $cx=($x/2+$tpl['box']['x'])*$this->k;
171             $cy=($y/2+$tpl['box']['y'])*$this->k;
172             
173             $angle*=-1; 
174             
175             $angle*=M_PI/180;
176             $c=cos($angle);
177             $s=sin($angle);
178             
179             $tpl['buffer'] = sprintf('q %.5f %.5f %.5f %.5f %.2f %.2f cm 1 0 0 1 %.2f %.2f cm %s Q',$c,$s,-$s,$c,$cx,$cy,-$cx,-$cy, $tpl['buffer']);
180         }
181         
182         $this->_importedPages[$pageKey] = $this->tpl;
183         
184         return $this->tpl;
185     }
186     
187     function getLastUsedPageBox() {
188         return $this->lastUsedPageBox;
189     }
190     
191     function useTemplate($tplidx, $_x=null, $_y=null, $_w=0, $_h=0) {
192         $this->_out('q 0 J 1 w 0 j 0 G 0 g'); // reset standard values
193         $s = parent::useTemplate($tplidx, $_x, $_y, $_w, $_h);
194         $this->_out('Q');
195         return $s;
196     }
197     
198     /**
199      * Private method, that rebuilds all needed objects of source files
200      */
201     function _putimportedobjects() {
202         if (is_array($this->parsers) && count($this->parsers) > 0) {
203             foreach($this->parsers AS $filename => $p) {
204                 $this->current_parser =& $this->parsers[$filename];
205                 if (isset($this->_obj_stack[$filename]) && is_array($this->_obj_stack[$filename])) {
206                     while($n = key($this->_obj_stack[$filename])) {
207                         $nObj = $this->current_parser->pdf_resolve_object($this->current_parser->c,$this->_obj_stack[$filename][$n][1]);
208                                                 
209                         $this->_newobj($this->_obj_stack[$filename][$n][0]);
210                         
211                         if ($nObj[0] == PDF_TYPE_STREAM) {
212                                                         $this->pdf_write_value ($nObj);
213                         } else {
214                             $this->pdf_write_value ($nObj[1]);
215                         }
216                         
217                         $this->_out('endobj');
218                         $this->_obj_stack[$filename][$n] = null; // free memory
219                         unset($this->_obj_stack[$filename][$n]);
220                         reset($this->_obj_stack[$filename]);
221                     }
222                 }
223             }
224         }
225     }
226     
227     /**
228      * Put resources
229      */
230     function _putresources() {
231         if (!is_subclass_of($this, 'TCPDF')) {
232             $this->_putfonts();
233                 $this->_putimages();
234                 $this->_putformxobjects();
235             $this->_putimportedobjects();
236             //Resource dictionary
237                 $this->offsets[2]=strlen($this->buffer);
238                 $this->_out('2 0 obj');
239                 $this->_out('<<');
240                 $this->_putresourcedict();
241                 $this->_out('>>');
242                 $this->_out('endobj');
243                 
244         } else { // TCPDF - Part
245             $this->_putextgstates();
246                 $this->_putocg();
247                 $this->_putfonts();
248                 $this->_putimages();
249                 $this->_putshaders();
250                 $this->_putformxobjects();
251             $this->_putimportedobjects();
252             //Resource dictionary
253                 $this->offsets[2]=strlen($this->buffer);
254                 $this->_out('2 0 obj');
255                 $this->_out('<<');
256                 $this->_putresourcedict();
257                 $this->_out('>>');
258                 $this->_out('endobj');
259                 $this->_putjavascript();
260                 $this->_putbookmarks();
261                 // encryption
262                 if ($this->encrypted) {
263                         $this->_newobj();
264                         $this->enc_obj_id = $this->n;
265                         $this->_out('<<');
266                         $this->_putencryption();
267                         $this->_out('>>');
268                         $this->_out('endobj');
269                 }
270         }
271     }
272     
273     /**
274      * Private Method that writes the form xobjects
275      */
276     function _putformxobjects() {
277         $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
278             reset($this->tpls);
279         foreach($this->tpls AS $tplidx => $tpl) {
280             $p=($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
281                 $this->_newobj();
282                 $cN = $this->n; // TCPDF/Protection: rem current "n"
283                 
284                 $this->tpls[$tplidx]['n'] = $this->n;
285                 $this->_out('<<'.$filter.'/Type /XObject');
286             $this->_out('/Subtype /Form');
287             $this->_out('/FormType 1');
288             
289             $this->_out(sprintf('/BBox [%.2f %.2f %.2f %.2f]',
290                 ($tpl['x'] + (isset($tpl['box']['x'])?$tpl['box']['x']:0))*$this->k,
291                 ($tpl['h'] + (isset($tpl['box']['y'])?$tpl['box']['y']:0) - $tpl['y'])*$this->k,
292                 ($tpl['w'] + (isset($tpl['box']['x'])?$tpl['box']['x']:0))*$this->k,
293                 ($tpl['h'] + (isset($tpl['box']['y'])?$tpl['box']['y']:0) - $tpl['y']-$tpl['h'])*$this->k)
294             );
295             
296             if (isset($tpl['box']))
297                 $this->_out(sprintf('/Matrix [1 0 0 1 %.5f %.5f]',-$tpl['box']['x']*$this->k, -$tpl['box']['y']*$this->k));
298             
299             $this->_out('/Resources ');
300
301             if (isset($tpl['resources'])) {
302                 $this->current_parser =& $tpl['parser'];
303                 $this->pdf_write_value($tpl['resources']); // "n" will be changed
304             } else {
305                 $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
306                 if (isset($this->_res['tpl'][$tplidx]['fonts']) && count($this->_res['tpl'][$tplidx]['fonts'])) {
307                         $this->_out('/Font <<');
308                     foreach($this->_res['tpl'][$tplidx]['fonts'] as $font)
309                                 $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
310                         $this->_out('>>');
311                 }
312                 if(isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images']) || 
313                    isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls']))
314                 {
315                     $this->_out('/XObject <<');
316                     if (isset($this->_res['tpl'][$tplidx]['images']) && count($this->_res['tpl'][$tplidx]['images'])) {
317                         foreach($this->_res['tpl'][$tplidx]['images'] as $image)
318                                         $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
319                     }
320                     if (isset($this->_res['tpl'][$tplidx]['tpls']) && count($this->_res['tpl'][$tplidx]['tpls'])) {
321                         foreach($this->_res['tpl'][$tplidx]['tpls'] as $i => $tpl)
322                             $this->_out($this->tplprefix.$i.' '.$tpl['n'].' 0 R');
323                     }
324                     $this->_out('>>');
325                 }
326                 $this->_out('>>');
327             }
328
329             $nN = $this->n; // TCPDF: rem new "n"
330             $this->n = $cN; // TCPDF: reset to current "n"
331             $this->_out('/Length '.strlen($p).' >>');
332                 $this->_putstream($p);
333                 $this->_out('endobj');
334                 $this->n = $nN; // TCPDF: reset to new "n"
335         }
336     }
337
338     /**
339      * Rewritten to handle existing own defined objects
340      */
341     function _newobj($obj_id=false,$onlynewobj=false) {
342         if (!$obj_id) {
343             $obj_id = ++$this->n;
344         }
345
346         //Begin a new object
347         if (!$onlynewobj) {
348             $this->offsets[$obj_id] = strlen($this->buffer);
349             $this->_out($obj_id.' 0 obj');
350             $this->_current_obj_id = $obj_id; // for later use with encryption
351         }
352     }
353
354     /**
355      * Writes a value
356      * Needed to rebuild the source document
357      *
358      * @param mixed $value A PDF-Value. Structure of values see cases in this method
359      */
360     function pdf_write_value(&$value)
361     {
362         if (is_subclass_of($this, 'TCPDF')) {
363             parent::pdf_write_value($value);
364         }
365         
366         switch ($value[0]) {
367
368                 case PDF_TYPE_NUMERIC :
369                 case PDF_TYPE_TOKEN :
370                 case PDF_TYPE_REAL :
371                 // A numeric value or a token.
372                         // Simply output them
373                 $this->_out($value[1]." ", false);
374                         break;
375
376                 case PDF_TYPE_ARRAY :
377
378                         // An array. Output the proper
379                         // structure and move on.
380
381                         $this->_out("[",false);
382                 for ($i = 0; $i < count($value[1]); $i++) {
383                                 $this->pdf_write_value($value[1][$i]);
384                         }
385
386                         $this->_out("]");
387                         break;
388
389                 case PDF_TYPE_DICTIONARY :
390
391                         // A dictionary.
392                         $this->_out("<<",false);
393
394                         reset ($value[1]);
395
396                         foreach ($value[1] as $k => $v) {
397                                 $this->_out($k . " ",false);
398                                 $this->pdf_write_value($v);
399                         }
400
401                         $this->_out(">>");
402                         break;
403
404                 case PDF_TYPE_OBJREF :
405
406                         // An indirect object reference
407                         // Fill the object stack if needed
408                         $cpfn =& $this->current_parser->filename;
409                         if (!isset($this->_don_obj_stack[$cpfn][$value[1]])) {
410                     $this->_newobj(false,true);
411                     $this->_obj_stack[$cpfn][$value[1]] = array($this->n, $value);
412                     $this->_don_obj_stack[$cpfn][$value[1]] = array($this->n, $value); // Value is maybee obsolete!!!
413                 }
414                 $objid = $this->_don_obj_stack[$cpfn][$value[1]][0];
415
416                         $this->_out("{$objid} 0 R");
417                         break;
418
419                 case PDF_TYPE_STRING :
420
421                         // A string.
422                 $this->_out('('.$value[1].')');
423
424                         break;
425
426                 case PDF_TYPE_STREAM :
427
428                         // A stream. First, output the
429                         // stream dictionary, then the
430                         // stream data itself.
431                 $this->pdf_write_value($value[1]);
432                         $this->_out("stream");
433                         $this->_out($value[2][1]);
434                         $this->_out("endstream");
435                         break;
436             case PDF_TYPE_HEX :
437             
438                 $this->_out("<".$value[1].">");
439                 break;
440
441             case PDF_TYPE_BOOLEAN :
442                     $this->_out($value[1] ? 'true ' : 'false ', false);
443                     break;
444             
445                 case PDF_TYPE_NULL :
446                 // The null object.
447
448                         $this->_out("null");
449                         break;
450         }
451     }
452     
453     
454     /**
455      * Modified so not each call will add a newline to the output.
456      */
457     function _out($s, $ln=true) {
458         //Add a line to the document
459         if ($this->state==2) {
460             if (!$this->_intpl) {
461                 if (is_subclass_of($this, 'TCPDF') && isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
462                                         // puts data before page footer
463                                         $page = substr($this->pages[$this->page], 0, -$this->footerlen[$this->page]);
464                                         $footer = substr($this->pages[$this->page], -$this->footerlen[$this->page]);
465                                         $this->pages[$this->page] = $page." ".$s."\n".$footer;
466                                 } else {
467                     $this->pages[$this->page] .= $s.($ln == true ? "\n" : '');
468                 }
469             } else
470                 $this->tpls[$this->tpl]['buffer'] .= $s.($ln == true ? "\n" : '');
471         } else {
472                     $this->buffer.=$s.($ln == true ? "\n" : '');
473         }
474     }
475
476     /**
477      * rewritten to close opened parsers
478      *
479      */
480     function _enddoc() {
481         parent::_enddoc();
482         $this->_closeParsers();
483     }
484     
485     /**
486      * close all files opened by parsers
487      */
488     function _closeParsers() {
489         if ($this->state > 2 && is_array($this->parsers) && count($this->parsers) > 0) {
490                 foreach ($this->parsers as $k => $_){
491                 $this->parsers[$k]->closeFile();
492                 $this->parsers[$k] = null;
493                 unset($this->parsers[$k]);
494             }
495             return true;
496         }
497         return false;
498     }
499
500 }