MDL-49515 libraries: Update FPDI to 1.5.4
[moodle.git] / mod / assign / feedback / editpdf / fpdi / fpdf_tpl.php
1 <?php
2 //
3 //  FPDI - Version 1.5.4
4 //
5 //    Copyright 2004-2015 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 //
20 if (!class_exists('fpdi_bridge')) {
21     require_once('fpdi_bridge.php');
22 }
24 /**
25  * Class FPDF_TPL
26  */
27 class FPDF_TPL extends fpdi_bridge
28 {
29     /**
30      * Array of template data
31      *
32      * @var array
33      */
34     protected $_tpls = array();
36     /**
37      * Current Template-Id
38      *
39      * @var int
40      */
41     public $tpl = 0;
43     /**
44      * "In Template"-Flag
45      *
46      * @var boolean
47      */
48     protected $_inTpl = false;
50     /**
51      * Name prefix of templates used in Resources dictionary
52      *
53      * @var string A String defining the Prefix used as Template-Object-Names. Have to begin with an /
54      */
55     public $tplPrefix = "/TPL";
57     /**
58      * Resources used by templates and pages
59      *
60      * @var array
61      */
62     protected  $_res = array();
64     /**
65      * Last used template data
66      *
67      * @var array
68      */
69     public $lastUsedTemplateData = array();
71     /**
72      * Start a template.
73      *
74      * This method starts a template. You can give own coordinates to build an own sized
75      * template. Pay attention, that the margins are adapted to the new template size.
76      * If you want to write outside the template, for example to build a clipped template,
77      * you have to set the margins and "cursor"-position manual after beginTemplate()-call.
78      *
79      * If no parameter is given, the template uses the current page-size.
80      * The method returns an id of the current template. This id is used later for using this template.
81      * Warning: A created template is saved in the resulting PDF at all events. Also if you don't use it after creation!
82      *
83      * @param int $x The x-coordinate given in user-unit
84      * @param int $y The y-coordinate given in user-unit
85      * @param int $w The width given in user-unit
86      * @param int $h The height given in user-unit
87      * @return int The id of new created template
88      * @throws LogicException
89      */
90     public function beginTemplate($x = null, $y = null, $w = null, $h = null)
91     {
92         if (is_subclass_of($this, 'TCPDF')) {
93             throw new LogicException('This method is only usable with FPDF. Use TCPDF methods startTemplate() instead.');
94         }
96         if ($this->page <= 0) {
97             throw new LogicException("You have to add at least a page first!");
98         }
100         if ($x == null)
101             $x = 0;
102         if ($y == null)
103             $y = 0;
104         if ($w == null)
105             $w = $this->w;
106         if ($h == null)
107             $h = $this->h;
109         // Save settings
110         $this->tpl++;
111         $tpl =& $this->_tpls[$this->tpl];
112         $tpl = array(
113             'o_x' => $this->x,
114             'o_y' => $this->y,
115             'o_AutoPageBreak' => $this->AutoPageBreak,
116             'o_bMargin' => $this->bMargin,
117             'o_tMargin' => $this->tMargin,
118             'o_lMargin' => $this->lMargin,
119             'o_rMargin' => $this->rMargin,
120             'o_h' => $this->h,
121             'o_w' => $this->w,
122             'o_FontFamily' => $this->FontFamily,
123             'o_FontStyle' => $this->FontStyle,
124             'o_FontSizePt' => $this->FontSizePt,
125             'o_FontSize' => $this->FontSize,
126             'buffer' => '',
127             'x' => $x,
128             'y' => $y,
129             'w' => $w,
130             'h' => $h
131         );
133         $this->SetAutoPageBreak(false);
135         // Define own high and width to calculate correct positions
136         $this->h = $h;
137         $this->w = $w;
139         $this->_inTpl = true;
140         $this->SetXY($x + $this->lMargin, $y + $this->tMargin);
141         $this->SetRightMargin($this->w - $w + $this->rMargin);
143         if ($this->CurrentFont) {
144             $fontKey = $this->FontFamily . $this->FontStyle;
145             if ($fontKey) {
146                 $this->_res['tpl'][$this->tpl]['fonts'][$fontKey] =& $this->fonts[$fontKey];
147                 $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
148             }
149         }
151         return $this->tpl;
152     }
154     /**
155      * End template.
156      *
157      * This method ends a template and reset initiated variables collected in {@link beginTemplate()}.
158      *
159      * @return int|boolean If a template is opened, the id is returned. If not a false is returned.
160      */
161     public function endTemplate()
162     {
163         if (is_subclass_of($this, 'TCPDF')) {
164             $args = func_get_args();
165             return call_user_func_array(array($this, 'TCPDF::endTemplate'), $args);
166         }
168         if ($this->_inTpl) {
169             $this->_inTpl = false;
170             $tpl = $this->_tpls[$this->tpl];
171             $this->SetXY($tpl['o_x'], $tpl['o_y']);
172             $this->tMargin = $tpl['o_tMargin'];
173             $this->lMargin = $tpl['o_lMargin'];
174             $this->rMargin = $tpl['o_rMargin'];
175             $this->h = $tpl['o_h'];
176             $this->w = $tpl['o_w'];
177             $this->SetAutoPageBreak($tpl['o_AutoPageBreak'], $tpl['o_bMargin']);
179             $this->FontFamily = $tpl['o_FontFamily'];
180             $this->FontStyle = $tpl['o_FontStyle'];
181             $this->FontSizePt = $tpl['o_FontSizePt'];
182             $this->FontSize = $tpl['o_FontSize'];
184             $fontKey = $this->FontFamily . $this->FontStyle;
185             if ($fontKey)
186                 $this->CurrentFont =& $this->fonts[$fontKey];
188             return $this->tpl;
189         } else {
190             return false;
191         }
192     }
194     /**
195      * Use a template in current page or other template.
196      *
197      * You can use a template in a page or in another template.
198      * You can give the used template a new size.
199      * All parameters are optional. The width or height is calculated automatically
200      * if one is given. If no parameter is given the origin size as defined in
201      * {@link beginTemplate()} method is used.
202      *
203      * The calculated or used width and height are returned as an array.
204      *
205      * @param int $tplIdx A valid template-id
206      * @param int $x The x-position
207      * @param int $y The y-position
208      * @param int $w The new width of the template
209      * @param int $h The new height of the template
210      * @return array The height and width of the template (array('w' => ..., 'h' => ...))
211      * @throws LogicException|InvalidArgumentException
212      */
213     public function useTemplate($tplIdx, $x = null, $y = null, $w = 0, $h = 0)
214     {
215         if ($this->page <= 0) {
216             throw new LogicException('You have to add at least a page first!');
217         }
219         if (!isset($this->_tpls[$tplIdx])) {
220             throw new InvalidArgumentException('Template does not exist!');
221         }
223         if ($this->_inTpl) {
224             $this->_res['tpl'][$this->tpl]['tpls'][$tplIdx] =& $this->_tpls[$tplIdx];
225         }
227         $tpl = $this->_tpls[$tplIdx];
228         $_w = $tpl['w'];
229         $_h = $tpl['h'];
231         if ($x == null) {
232             $x = 0;
233         }
235         if ($y == null) {
236             $y = 0;
237         }
239         $x += $tpl['x'];
240         $y += $tpl['y'];
242         $wh = $this->getTemplateSize($tplIdx, $w, $h);
243         $w = $wh['w'];
244         $h = $wh['h'];
246         $tplData = array(
247             'x' => $this->x,
248             'y' => $this->y,
249             'w' => $w,
250             'h' => $h,
251             'scaleX' => ($w / $_w),
252             'scaleY' => ($h / $_h),
253             'tx' => $x,
254             'ty' =>  ($this->h - $y - $h),
255             'lty' => ($this->h - $y - $h) - ($this->h - $_h) * ($h / $_h)
256         );
258         $this->_out(sprintf('q %.4F 0 0 %.4F %.4F %.4F cm',
259                 $tplData['scaleX'], $tplData['scaleY'], $tplData['tx'] * $this->k, $tplData['ty'] * $this->k)
260         ); // Translate
261         $this->_out(sprintf('%s%d Do Q', $this->tplPrefix, $tplIdx));
263         $this->lastUsedTemplateData = $tplData;
265         return array('w' => $w, 'h' => $h);
266     }
268     /**
269      * Get the calculated size of a template.
270      *
271      * If one size is given, this method calculates the other one.
272      *
273      * @param int $tplIdx A valid template-id
274      * @param int $w The width of the template
275      * @param int $h The height of the template
276      * @return array The height and width of the template (array('w' => ..., 'h' => ...))
277      */
278     public function getTemplateSize($tplIdx, $w = 0, $h = 0)
279     {
280         if (!isset($this->_tpls[$tplIdx]))
281             return false;
283         $tpl = $this->_tpls[$tplIdx];
284         $_w = $tpl['w'];
285         $_h = $tpl['h'];
287         if ($w == 0 && $h == 0) {
288             $w = $_w;
289             $h = $_h;
290         }
292         if ($w == 0)
293             $w = $h * $_w / $_h;
294         if($h == 0)
295             $h = $w * $_h / $_w;
297         return array("w" => $w, "h" => $h);
298     }
300     /**
301      * Sets the font used to print character strings.
302      *
303      * See FPDF/TCPDF documentation.
304      *
305      * @see http://fpdf.org/en/doc/setfont.htm
306      * @see http://www.tcpdf.org/doc/code/classTCPDF.html#afd56e360c43553830d543323e81bc045
307      */
308     public function SetFont($family, $style = '', $size = null, $fontfile = '', $subset = 'default', $out = true)
309     {
310         if (is_subclass_of($this, 'TCPDF')) {
311             $args = func_get_args();
312             return call_user_func_array(array($this, 'TCPDF::SetFont'), $args);
313         }
315         parent::SetFont($family, $style, $size);
317         $fontkey = $this->FontFamily . $this->FontStyle;
319         if ($this->_inTpl) {
320             $this->_res['tpl'][$this->tpl]['fonts'][$fontkey] =& $this->fonts[$fontkey];
321         } else {
322             $this->_res['page'][$this->page]['fonts'][$fontkey] =& $this->fonts[$fontkey];
323         }
324     }
326     /**
327      * Puts an image.
328      *
329      * See FPDF/TCPDF documentation.
330      *
331      * @see http://fpdf.org/en/doc/image.htm
332      * @see http://www.tcpdf.org/doc/code/classTCPDF.html#a714c2bee7d6b39d4d6d304540c761352
333      */
334     public function Image(
335         $file, $x = '', $y = '', $w = 0, $h = 0, $type = '', $link = '', $align = '', $resize = false,
336         $dpi = 300, $palign = '', $ismask = false, $imgmask = false, $border = 0, $fitbox = false,
337         $hidden = false, $fitonpage = false, $alt = false, $altimgs = array()
338     )
339     {
340         if (is_subclass_of($this, 'TCPDF')) {
341             $args = func_get_args();
342             return call_user_func_array(array($this, 'TCPDF::Image'), $args);
343         }
345         $ret = parent::Image($file, $x, $y, $w, $h, $type, $link);
346         if ($this->_inTpl) {
347             $this->_res['tpl'][$this->tpl]['images'][$file] =& $this->images[$file];
348         } else {
349             $this->_res['page'][$this->page]['images'][$file] =& $this->images[$file];
350         }
352         return $ret;
353     }
355     /**
356      * Adds a new page to the document.
357      *
358      * See FPDF/TCPDF documentation.
359      *
360      * This method cannot be used if you'd started a template.
361      *
362      * @see http://fpdf.org/en/doc/addpage.htm
363      * @see http://www.tcpdf.org/doc/code/classTCPDF.html#a5171e20b366b74523709d84c349c1ced
364      */
365     public function AddPage($orientation = '', $format = '', $keepmargins = false, $tocpage = false)
366     {
367         if (is_subclass_of($this, 'TCPDF')) {
368             $args = func_get_args();
369             return call_user_func_array(array($this, 'TCPDF::AddPage'), $args);
370         }
372         if ($this->_inTpl) {
373             throw new LogicException('Adding pages in templates is not possible!');
374         }
376         parent::AddPage($orientation, $format);
377     }
379     /**
380      * Puts a link on a rectangular area of the page.
381      *
382      * Overwritten because adding links in a template will not work.
383      *
384      * @see http://fpdf.org/en/doc/link.htm
385      * @see http://www.tcpdf.org/doc/code/classTCPDF.html#ab87bf1826384fbfe30eb499d42f1d994
386      */
387     public function Link($x, $y, $w, $h, $link, $spaces = 0)
388     {
389         if (is_subclass_of($this, 'TCPDF')) {
390             $args = func_get_args();
391             return call_user_func_array(array($this, 'TCPDF::Link'), $args);
392         }
394         if ($this->_inTpl) {
395             throw new LogicException('Using links in templates is not posible!');
396         }
398         parent::Link($x, $y, $w, $h, $link);
399     }
401     /**
402      * Creates a new internal link and returns its identifier.
403      *
404      * Overwritten because adding links in a template will not work.
405      *
406      * @see http://fpdf.org/en/doc/addlink.htm
407      * @see http://www.tcpdf.org/doc/code/classTCPDF.html#a749522038ed7786c3e1701435dcb891e
408      */
409     public function AddLink()
410     {
411         if (is_subclass_of($this, 'TCPDF')) {
412             $args = func_get_args();
413             return call_user_func_array(array($this, 'TCPDF::AddLink'), $args);
414         }
416         if ($this->_inTpl) {
417             throw new LogicException('Adding links in templates is not possible!');
418         }
420         return parent::AddLink();
421     }
423     /**
424      * Defines the page and position a link points to.
425      *
426      * Overwritten because adding links in a template will not work.
427      *
428      * @see http://fpdf.org/en/doc/setlink.htm
429      * @see http://www.tcpdf.org/doc/code/classTCPDF.html#ace5be60e7857953ea5e2b89cb90df0ae
430      */
431     public function SetLink($link, $y = 0, $page = -1)
432     {
433         if (is_subclass_of($this, 'TCPDF')) {
434             $args = func_get_args();
435             return call_user_func_array(array($this, 'TCPDF::SetLink'), $args);
436         }
438         if ($this->_inTpl) {
439             throw new LogicException('Setting links in templates is not possible!');
440         }
442         parent::SetLink($link, $y, $page);
443     }
445     /**
446      * Writes the form XObjects to the PDF document.
447      */
448     protected function _putformxobjects()
449     {
450         $filter=($this->compress) ? '/Filter /FlateDecode ' : '';
451         reset($this->_tpls);
453         foreach($this->_tpls AS $tplIdx => $tpl) {
454             $this->_newobj();
455             $this->_tpls[$tplIdx]['n'] = $this->n;
456             $this->_out('<<'.$filter.'/Type /XObject');
457             $this->_out('/Subtype /Form');
458             $this->_out('/FormType 1');
459             $this->_out(sprintf('/BBox [%.2F %.2F %.2F %.2F]',
460                 // llx
461                 $tpl['x'] * $this->k,
462                 // lly
463                 -$tpl['y'] * $this->k,
464                 // urx
465                 ($tpl['w'] + $tpl['x']) * $this->k,
466                 // ury
467                 ($tpl['h'] - $tpl['y']) * $this->k
468             ));
470             if ($tpl['x'] != 0 || $tpl['y'] != 0) {
471                 $this->_out(sprintf('/Matrix [1 0 0 1 %.5F %.5F]',
472                     -$tpl['x'] * $this->k * 2, $tpl['y'] * $this->k * 2
473                 ));
474             }
476             $this->_out('/Resources ');
477             $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
479             if (isset($this->_res['tpl'][$tplIdx])) {
480                 $res = $this->_res['tpl'][$tplIdx];
481                 if (isset($res['fonts']) && count($res['fonts'])) {
482                     $this->_out('/Font <<');
484                     foreach($res['fonts'] as $font) {
485                         $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
486                     }
488                     $this->_out('>>');
489                 }
491                 if(isset($res['images']) || isset($res['tpls'])) {
492                     $this->_out('/XObject <<');
494                     if (isset($res['images'])) {
495                         foreach($res['images'] as $image)
496                             $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
497                     }
499                     if (isset($res['tpls'])) {
500                         foreach($res['tpls'] as $i => $_tpl)
501                             $this->_out($this->tplPrefix . $i . ' ' . $_tpl['n'] . ' 0 R');
502                     }
504                     $this->_out('>>');
505                 }
506             }
508             $this->_out('>>');
510             $buffer = ($this->compress) ? gzcompress($tpl['buffer']) : $tpl['buffer'];
511             $this->_out('/Length ' . strlen($buffer) . ' >>');
512             $this->_putstream($buffer);
513             $this->_out('endobj');
514         }
515     }
517     /**
518      * Output images.
519      *
520      * Overwritten to add {@link _putformxobjects()} after _putimages().
521      */
522     public function _putimages()
523     {
524         parent::_putimages();
525         $this->_putformxobjects();
526     }
528     /**
529      * Writes the references of XObject resources to the document.
530      *
531      * Overwritten to add the the templates to the XObject resource dictionary.
532      */
533     public function _putxobjectdict()
534     {
535         parent::_putxobjectdict();
537         foreach($this->_tpls as $tplIdx => $tpl) {
538             $this->_out(sprintf('%s%d %d 0 R', $this->tplPrefix, $tplIdx, $tpl['n']));
539         }
540     }
542     /**
543      * Writes bytes to the resulting document.
544      *
545      * Overwritten to delegate the data to the template buffer.
546      *
547      * @param string $s
548      */
549     public function _out($s)
550     {
551         if ($this->state == 2 && $this->_inTpl) {
552             $this->_tpls[$this->tpl]['buffer'] .= $s . "\n";
553         } else {
554             parent::_out($s);
555         }
556     }