MDL-49515 libraries: Update FPDI to 1.5.4
[moodle.git] / mod / assign / feedback / editpdf / fpdi / fpdf_tpl.php
CommitLineData
858d457d
AG
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//
19
20if (!class_exists('fpdi_bridge')) {
21 require_once('fpdi_bridge.php');
22}
23
24/**
25 * Class FPDF_TPL
26 */
27class FPDF_TPL extends fpdi_bridge
28{
29 /**
30 * Array of template data
31 *
32 * @var array
33 */
34 protected $_tpls = array();
35
36 /**
37 * Current Template-Id
38 *
39 * @var int
40 */
41 public $tpl = 0;
42
43 /**
44 * "In Template"-Flag
45 *
46 * @var boolean
47 */
48 protected $_inTpl = false;
49
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";
56
57 /**
58 * Resources used by templates and pages
59 *
60 * @var array
61 */
62 protected $_res = array();
63
64 /**
65 * Last used template data
66 *
67 * @var array
68 */
69 public $lastUsedTemplateData = array();
70
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 }
95
96 if ($this->page <= 0) {
97 throw new LogicException("You have to add at least a page first!");
98 }
99
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;
108
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 );
132
133 $this->SetAutoPageBreak(false);
134
135 // Define own high and width to calculate correct positions
136 $this->h = $h;
137 $this->w = $w;
138
139 $this->_inTpl = true;
140 $this->SetXY($x + $this->lMargin, $y + $this->tMargin);
141 $this->SetRightMargin($this->w - $w + $this->rMargin);
142
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 }
150
151 return $this->tpl;
152 }
153
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 }
167
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']);
178
179 $this->FontFamily = $tpl['o_FontFamily'];
180 $this->FontStyle = $tpl['o_FontStyle'];
181 $this->FontSizePt = $tpl['o_FontSizePt'];
182 $this->FontSize = $tpl['o_FontSize'];
183
184 $fontKey = $this->FontFamily . $this->FontStyle;
185 if ($fontKey)
186 $this->CurrentFont =& $this->fonts[$fontKey];
187
188 return $this->tpl;
189 } else {
190 return false;
191 }
192 }
193
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 }
218
219 if (!isset($this->_tpls[$tplIdx])) {
220 throw new InvalidArgumentException('Template does not exist!');
221 }
222
223 if ($this->_inTpl) {
224 $this->_res['tpl'][$this->tpl]['tpls'][$tplIdx] =& $this->_tpls[$tplIdx];
225 }
226
227 $tpl = $this->_tpls[$tplIdx];
228 $_w = $tpl['w'];
229 $_h = $tpl['h'];
230
231 if ($x == null) {
232 $x = 0;
233 }
234
235 if ($y == null) {
236 $y = 0;
237 }
238
239 $x += $tpl['x'];
240 $y += $tpl['y'];
241
242 $wh = $this->getTemplateSize($tplIdx, $w, $h);
243 $w = $wh['w'];
244 $h = $wh['h'];
245
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 );
257
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));
262
263 $this->lastUsedTemplateData = $tplData;
264
265 return array('w' => $w, 'h' => $h);
266 }
267
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;
282
283 $tpl = $this->_tpls[$tplIdx];
284 $_w = $tpl['w'];
285 $_h = $tpl['h'];
286
287 if ($w == 0 && $h == 0) {
288 $w = $_w;
289 $h = $_h;
290 }
291
292 if ($w == 0)
293 $w = $h * $_w / $_h;
294 if($h == 0)
295 $h = $w * $_h / $_w;
296
297 return array("w" => $w, "h" => $h);
298 }
299
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 }
314
315 parent::SetFont($family, $style, $size);
316
317 $fontkey = $this->FontFamily . $this->FontStyle;
318
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 }
325
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 }
344
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 }
351
352 return $ret;
353 }
354
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 }
371
372 if ($this->_inTpl) {
373 throw new LogicException('Adding pages in templates is not possible!');
374 }
375
376 parent::AddPage($orientation, $format);
377 }
378
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 }
393
394 if ($this->_inTpl) {
395 throw new LogicException('Using links in templates is not posible!');
396 }
397
398 parent::Link($x, $y, $w, $h, $link);
399 }
400
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 }
415
416 if ($this->_inTpl) {
417 throw new LogicException('Adding links in templates is not possible!');
418 }
419
420 return parent::AddLink();
421 }
422
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 }
437
438 if ($this->_inTpl) {
439 throw new LogicException('Setting links in templates is not possible!');
440 }
441
442 parent::SetLink($link, $y, $page);
443 }
444
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);
452
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 ));
469
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 }
475
476 $this->_out('/Resources ');
477 $this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
478
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 <<');
483
484 foreach($res['fonts'] as $font) {
485 $this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
486 }
487
488 $this->_out('>>');
489 }
490
491 if(isset($res['images']) || isset($res['tpls'])) {
492 $this->_out('/XObject <<');
493
494 if (isset($res['images'])) {
495 foreach($res['images'] as $image)
496 $this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
497 }
498
499 if (isset($res['tpls'])) {
500 foreach($res['tpls'] as $i => $_tpl)
501 $this->_out($this->tplPrefix . $i . ' ' . $_tpl['n'] . ' 0 R');
502 }
503
504 $this->_out('>>');
505 }
506 }
507
508 $this->_out('>>');
509
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 }
516
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 }
527
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();
536
537 foreach($this->_tpls as $tplIdx => $tpl) {
538 $this->_out(sprintf('%s%d %d 0 R', $this->tplPrefix, $tplIdx, $tpl['n']));
539 }
540 }
541
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 }
557}