PluginProbe
MBE eShip / trunk
MBE eShip vtrunk
2.8.1 trunk 1.0.0 1.1.0 1.1.3 1.2.1 1.2.2 1.3.0 1.4.0 1.4.1 1.5.0 1.5.1 1.5.2 1.6.0 1.7.0 1.7.1 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.1.0 2.1.1 2.1.2 2.2.1 All 37 releases
mail-boxes-etc / lib / dompdf / src / Adapter / CPDF.php

CPDF.php in MBE eShip trunk, at lib/dompdf/src/Adapter/CPDF.php

945 lines 26.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package dompdf
4 * @link https://github.com/dompdf/dompdf
5 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
6 */
7
8 // FIXME: Need to sanity check inputs to this class
9 namespace Dompdf\Adapter;
10
11 use Dompdf\Canvas;
12 use Dompdf\Dompdf;
13 use Dompdf\Exception;
14 use Dompdf\FontMetrics;
15 use Dompdf\Helpers;
16 use Dompdf\Image\Cache;
17 use FontLib\Exception\FontNotFoundException;
18
19 /**
20 * PDF rendering interface
21 *
22 * Dompdf\Adapter\CPDF provides a simple stateless interface to the stateful one
23 * provided by the Cpdf class.
24 *
25 * Unless otherwise mentioned, all dimensions are in points (1/72 in). The
26 * coordinate origin is in the top left corner, and y values increase
27 * downwards.
28 *
29 * See {@link http://www.ros.co.nz/pdf/} for more complete documentation
30 * on the underlying {@link Cpdf} class.
31 *
32 * @package dompdf
33 */
34 class CPDF implements Canvas
35 {
36
37 /**
38 * Dimensions of paper sizes in points
39 *
40 * @var array
41 */
42 static $PAPER_SIZES = [
43 "4a0" => [0.0, 0.0, 4767.87, 6740.79],
44 "2a0" => [0.0, 0.0, 3370.39, 4767.87],
45 "a0" => [0.0, 0.0, 2383.94, 3370.39],
46 "a1" => [0.0, 0.0, 1683.78, 2383.94],
47 "a2" => [0.0, 0.0, 1190.55, 1683.78],
48 "a3" => [0.0, 0.0, 841.89, 1190.55],
49 "a4" => [0.0, 0.0, 595.28, 841.89],
50 "a5" => [0.0, 0.0, 419.53, 595.28],
51 "a6" => [0.0, 0.0, 297.64, 419.53],
52 "a7" => [0.0, 0.0, 209.76, 297.64],
53 "a8" => [0.0, 0.0, 147.40, 209.76],
54 "a9" => [0.0, 0.0, 104.88, 147.40],
55 "a10" => [0.0, 0.0, 73.70, 104.88],
56 "b0" => [0.0, 0.0, 2834.65, 4008.19],
57 "b1" => [0.0, 0.0, 2004.09, 2834.65],
58 "b2" => [0.0, 0.0, 1417.32, 2004.09],
59 "b3" => [0.0, 0.0, 1000.63, 1417.32],
60 "b4" => [0.0, 0.0, 708.66, 1000.63],
61 "b5" => [0.0, 0.0, 498.90, 708.66],
62 "b6" => [0.0, 0.0, 354.33, 498.90],
63 "b7" => [0.0, 0.0, 249.45, 354.33],
64 "b8" => [0.0, 0.0, 175.75, 249.45],
65 "b9" => [0.0, 0.0, 124.72, 175.75],
66 "b10" => [0.0, 0.0, 87.87, 124.72],
67 "c0" => [0.0, 0.0, 2599.37, 3676.54],
68 "c1" => [0.0, 0.0, 1836.85, 2599.37],
69 "c2" => [0.0, 0.0, 1298.27, 1836.85],
70 "c3" => [0.0, 0.0, 918.43, 1298.27],
71 "c4" => [0.0, 0.0, 649.13, 918.43],
72 "c5" => [0.0, 0.0, 459.21, 649.13],
73 "c6" => [0.0, 0.0, 323.15, 459.21],
74 "c7" => [0.0, 0.0, 229.61, 323.15],
75 "c8" => [0.0, 0.0, 161.57, 229.61],
76 "c9" => [0.0, 0.0, 113.39, 161.57],
77 "c10" => [0.0, 0.0, 79.37, 113.39],
78 "ra0" => [0.0, 0.0, 2437.80, 3458.27],
79 "ra1" => [0.0, 0.0, 1729.13, 2437.80],
80 "ra2" => [0.0, 0.0, 1218.90, 1729.13],
81 "ra3" => [0.0, 0.0, 864.57, 1218.90],
82 "ra4" => [0.0, 0.0, 609.45, 864.57],
83 "sra0" => [0.0, 0.0, 2551.18, 3628.35],
84 "sra1" => [0.0, 0.0, 1814.17, 2551.18],
85 "sra2" => [0.0, 0.0, 1275.59, 1814.17],
86 "sra3" => [0.0, 0.0, 907.09, 1275.59],
87 "sra4" => [0.0, 0.0, 637.80, 907.09],
88 "letter" => [0.0, 0.0, 612.00, 792.00],
89 "half-letter" => [0.0, 0.0, 396.00, 612.00],
90 "legal" => [0.0, 0.0, 612.00, 1008.00],
91 "ledger" => [0.0, 0.0, 1224.00, 792.00],
92 "tabloid" => [0.0, 0.0, 792.00, 1224.00],
93 "executive" => [0.0, 0.0, 521.86, 756.00],
94 "folio" => [0.0, 0.0, 612.00, 936.00],
95 "commercial #10 envelope" => [0.0, 0.0, 684.00, 297.00],
96 "catalog #10 1/2 envelope" => [0.0, 0.0, 648.00, 864.00],
97 "8.5x11" => [0.0, 0.0, 612.00, 792.00],
98 "8.5x14" => [0.0, 0.0, 612.00, 1008.00],
99 "11x17" => [0.0, 0.0, 792.00, 1224.00],
100 ];
101
102 /**
103 * The Dompdf object
104 *
105 * @var Dompdf
106 */
107 protected $_dompdf;
108
109 /**
110 * Instance of Cpdf class
111 *
112 * @var \Dompdf\Cpdf
113 */
114 protected $_pdf;
115
116 /**
117 * PDF width, in points
118 *
119 * @var float
120 */
121 protected $_width;
122
123 /**
124 * PDF height, in points
125 *
126 * @var float
127 */
128 protected $_height;
129
130 /**
131 * Current page number
132 *
133 * @var int
134 */
135 protected $_page_number;
136
137 /**
138 * Total number of pages
139 *
140 * @var int
141 */
142 protected $_page_count;
143
144 /**
145 * Array of pages for accessing after rendering is initially complete
146 *
147 * @var array
148 */
149 protected $_pages;
150
151 /**
152 * Currently-applied opacity level (0 - 1)
153 *
154 * @var float
155 */
156 protected $_current_opacity = 1;
157
158 public function __construct($paper = "letter", $orientation = "portrait", ?Dompdf $dompdf = null)
159 {
160 if (is_array($paper)) {
161 $size = array_map("floatval", $paper);
162 } else {
163 $paper = strtolower($paper);
164 $size = self::$PAPER_SIZES[$paper] ?? self::$PAPER_SIZES["letter"];
165 }
166
167 if (strtolower($orientation) === "landscape") {
168 [$size[2], $size[3]] = [$size[3], $size[2]];
169 }
170
171 if ($dompdf === null) {
172 $this->_dompdf = new Dompdf();
173 } else {
174 $this->_dompdf = $dompdf;
175 }
176
177 $this->_pdf = new \Dompdf\Cpdf(
178 $size,
179 true,
180 $this->_dompdf->getOptions()->getFontCache(),
181 $this->_dompdf->getOptions()->getTempDir()
182 );
183
184 $this->_pdf->addInfo("Producer", sprintf("%s + CPDF", $this->_dompdf->version));
185 $time = substr_replace(date('YmdHisO'), '\'', -2, 0) . '\'';
186 $this->_pdf->addInfo("CreationDate", "D:$time");
187 $this->_pdf->addInfo("ModDate", "D:$time");
188
189 $this->_width = $size[2] - $size[0];
190 $this->_height = $size[3] - $size[1];
191
192 $this->_page_number = $this->_page_count = 1;
193
194 $this->_pages = [$this->_pdf->getFirstPageId()];
195 }
196
197 public function get_dompdf()
198 {
199 return $this->_dompdf;
200 }
201
202 /**
203 * Returns the Cpdf instance
204 *
205 * @return \Dompdf\Cpdf
206 */
207 public function get_cpdf()
208 {
209 return $this->_pdf;
210 }
211
212 public function add_info(string $label, string $value): void
213 {
214 $this->_pdf->addInfo($label, $value);
215 }
216
217 /**
218 * Opens a new 'object'
219 *
220 * While an object is open, all drawing actions are recorded in the object,
221 * as opposed to being drawn on the current page. Objects can be added
222 * later to a specific page or to several pages.
223 *
224 * The return value is an integer ID for the new object.
225 *
226 * @see CPDF::close_object()
227 * @see CPDF::add_object()
228 *
229 * @return int
230 */
231 public function open_object()
232 {
233 $ret = $this->_pdf->openObject();
234 $this->_pdf->saveState();
235 return $ret;
236 }
237
238 /**
239 * Reopens an existing 'object'
240 *
241 * @see CPDF::open_object()
242 * @param int $object the ID of a previously opened object
243 */
244 public function reopen_object($object)
245 {
246 $this->_pdf->reopenObject($object);
247 $this->_pdf->saveState();
248 }
249
250 /**
251 * Closes the current 'object'
252 *
253 * @see CPDF::open_object()
254 */
255 public function close_object()
256 {
257 $this->_pdf->restoreState();
258 $this->_pdf->closeObject();
259 }
260
261 /**
262 * Adds a specified 'object' to the document
263 *
264 * $object int specifying an object created with {@link
265 * CPDF::open_object()}. $where can be one of:
266 * - 'add' add to current page only
267 * - 'all' add to every page from the current one onwards
268 * - 'odd' add to all odd numbered pages from now on
269 * - 'even' add to all even numbered pages from now on
270 * - 'next' add the object to the next page only
271 * - 'nextodd' add to all odd numbered pages from the next one
272 * - 'nexteven' add to all even numbered pages from the next one
273 *
274 * @see Cpdf::addObject()
275 *
276 * @param int $object
277 * @param string $where
278 */
279 public function add_object($object, $where = 'all')
280 {
281 $this->_pdf->addObject($object, $where);
282 }
283
284 /**
285 * Stops the specified 'object' from appearing in the document.
286 *
287 * The object will stop being displayed on the page following the current
288 * one.
289 *
290 * @param int $object
291 */
292 public function stop_object($object)
293 {
294 $this->_pdf->stopObject($object);
295 }
296
297 /**
298 * Serialize the pdf object's current state for retrieval later
299 */
300 public function serialize_object($id)
301 {
302 return $this->_pdf->serializeObject($id);
303 }
304
305 public function reopen_serialized_object($obj)
306 {
307 return $this->_pdf->restoreSerializedObject($obj);
308 }
309
310 //........................................................................
311
312 public function get_width()
313 {
314 return $this->_width;
315 }
316
317 public function get_height()
318 {
319 return $this->_height;
320 }
321
322 public function get_page_number()
323 {
324 return $this->_page_number;
325 }
326
327 public function get_page_count()
328 {
329 return $this->_page_count;
330 }
331
332 /**
333 * Sets the current page number
334 *
335 * @param int $num
336 */
337 public function set_page_number($num)
338 {
339 $this->_page_number = $num;
340 }
341
342 public function set_page_count($count)
343 {
344 $this->_page_count = $count;
345 }
346
347 /**
348 * Sets the stroke color
349 *
350 * See {@link Style::set_color()} for the format of the color array.
351 *
352 * @param array $color
353 */
354 protected function _set_stroke_color($color)
355 {
356 $this->_pdf->setStrokeColor($color);
357 $alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
358 $alpha *= $this->_current_opacity;
359 $this->_set_line_transparency("Normal", $alpha);
360 }
361
362 /**
363 * Sets the fill colour
364 *
365 * See {@link Style::set_color()} for the format of the colour array.
366 *
367 * @param array $color
368 */
369 protected function _set_fill_color($color)
370 {
371 $this->_pdf->setColor($color);
372 $alpha = isset($color["alpha"]) ? $color["alpha"] : 1;
373 $alpha *= $this->_current_opacity;
374 $this->_set_fill_transparency("Normal", $alpha);
375 }
376
377 /**
378 * Sets line transparency
379 * @see Cpdf::setLineTransparency()
380 *
381 * Valid blend modes are (case-sensitive):
382 *
383 * Normal, Multiply, Screen, Overlay, Darken, Lighten,
384 * ColorDodge, ColorBurn, HardLight, SoftLight, Difference,
385 * Exclusion
386 *
387 * @param string $mode the blending mode to use
388 * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
389 */
390 protected function _set_line_transparency($mode, $opacity)
391 {
392 $this->_pdf->setLineTransparency($mode, $opacity);
393 }
394
395 /**
396 * Sets fill transparency
397 * @see Cpdf::setFillTransparency()
398 *
399 * Valid blend modes are (case-sensitive):
400 *
401 * Normal, Multiply, Screen, Overlay, Darken, Lighten,
402 * ColorDogde, ColorBurn, HardLight, SoftLight, Difference,
403 * Exclusion
404 *
405 * @param string $mode the blending mode to use
406 * @param float $opacity 0.0 fully transparent, 1.0 fully opaque
407 */
408 protected function _set_fill_transparency($mode, $opacity)
409 {
410 $this->_pdf->setFillTransparency($mode, $opacity);
411 }
412
413 /**
414 * Sets the line style
415 *
416 * @see Cpdf::setLineStyle()
417 *
418 * @param float $width
419 * @param string $cap
420 * @param string $join
421 * @param array $dash
422 */
423 protected function _set_line_style($width, $cap, $join, $dash)
424 {
425 $this->_pdf->setLineStyle($width, $cap, $join, $dash);
426 }
427
428 public function set_opacity(float $opacity, string $mode = "Normal"): void
429 {
430 $this->_set_line_transparency($mode, $opacity);
431 $this->_set_fill_transparency($mode, $opacity);
432 $this->_current_opacity = $opacity;
433 }
434
435 public function set_default_view($view, $options = [])
436 {
437 array_unshift($options, $view);
438 call_user_func_array([$this->_pdf, "openHere"], $options);
439 }
440
441 /**
442 * Remaps y coords from 4th to 1st quadrant
443 *
444 * @param float $y
445 * @return float
446 */
447 protected function y($y)
448 {
449 return $this->_height - $y;
450 }
451
452 public function line($x1, $y1, $x2, $y2, $color, $width, $style = [], $cap = "butt")
453 {
454 $this->_set_stroke_color($color);
455 $this->_set_line_style($width, $cap, "", $style);
456
457 $this->_pdf->line($x1, $this->y($y1),
458 $x2, $this->y($y2));
459 $this->_set_line_transparency("Normal", $this->_current_opacity);
460 }
461
462 public function arc($x, $y, $r1, $r2, $astart, $aend, $color, $width, $style = [], $cap = "butt")
463 {
464 $this->_set_stroke_color($color);
465 $this->_set_line_style($width, $cap, "", $style);
466
467 $this->_pdf->ellipse($x, $this->y($y), $r1, $r2, 0, 8, $astart, $aend, false, false, true, false);
468 $this->_set_line_transparency("Normal", $this->_current_opacity);
469 }
470
471 public function rectangle($x1, $y1, $w, $h, $color, $width, $style = [], $cap = "butt")
472 {
473 $this->_set_stroke_color($color);
474 $this->_set_line_style($width, $cap, "", $style);
475 $this->_pdf->rectangle($x1, $this->y($y1) - $h, $w, $h);
476 $this->_set_line_transparency("Normal", $this->_current_opacity);
477 }
478
479 public function filled_rectangle($x1, $y1, $w, $h, $color)
480 {
481 $this->_set_fill_color($color);
482 $this->_pdf->filledRectangle($x1, $this->y($y1) - $h, $w, $h);
483 $this->_set_fill_transparency("Normal", $this->_current_opacity);
484 }
485
486 public function clipping_rectangle($x1, $y1, $w, $h)
487 {
488 $this->_pdf->clippingRectangle($x1, $this->y($y1) - $h, $w, $h);
489 }
490
491 public function clipping_roundrectangle($x1, $y1, $w, $h, $rTL, $rTR, $rBR, $rBL)
492 {
493 $this->_pdf->clippingRectangleRounded($x1, $this->y($y1) - $h, $w, $h, $rTL, $rTR, $rBR, $rBL);
494 }
495
496 public function clipping_polygon(array $points): void
497 {
498 // Adjust y values
499 for ($i = 1; $i < count($points); $i += 2) {
500 $points[$i] = $this->y($points[$i]);
501 }
502
503 $this->_pdf->clippingPolygon($points);
504 }
505
506 public function clipping_end()
507 {
508 $this->_pdf->clippingEnd();
509 }
510
511 public function save()
512 {
513 $this->_pdf->saveState();
514 }
515
516 public function restore()
517 {
518 $this->_pdf->restoreState();
519 }
520
521 public function rotate($angle, $x, $y)
522 {
523 $this->_pdf->rotate($angle, $x, $y);
524 }
525
526 public function skew($angle_x, $angle_y, $x, $y)
527 {
528 $this->_pdf->skew($angle_x, $angle_y, $x, $y);
529 }
530
531 public function scale($s_x, $s_y, $x, $y)
532 {
533 $this->_pdf->scale($s_x, $s_y, $x, $y);
534 }
535
536 public function translate($t_x, $t_y)
537 {
538 $this->_pdf->translate($t_x, $t_y);
539 }
540
541 public function transform($a, $b, $c, $d, $e, $f)
542 {
543 $this->_pdf->transform([$a, $b, $c, $d, $e, $f]);
544 }
545
546 public function polygon($points, $color, $width = null, $style = [], $fill = false)
547 {
548 $this->_set_fill_color($color);
549 $this->_set_stroke_color($color);
550
551 if (!$fill && isset($width)) {
552 $this->_set_line_style($width, "square", "miter", $style);
553 }
554
555 // Adjust y values
556 for ($i = 1; $i < count($points); $i += 2) {
557 $points[$i] = $this->y($points[$i]);
558 }
559
560 $this->_pdf->polygon($points, $fill);
561
562 $this->_set_fill_transparency("Normal", $this->_current_opacity);
563 $this->_set_line_transparency("Normal", $this->_current_opacity);
564 }
565
566 public function circle($x, $y, $r, $color, $width = null, $style = [], $fill = false)
567 {
568 $this->_set_fill_color($color);
569 $this->_set_stroke_color($color);
570
571 if (!$fill && isset($width)) {
572 $this->_set_line_style($width, "round", "round", $style);
573 }
574
575 $this->_pdf->ellipse($x, $this->y($y), $r, 0, 0, 8, 0, 360, 1, $fill);
576
577 $this->_set_fill_transparency("Normal", $this->_current_opacity);
578 $this->_set_line_transparency("Normal", $this->_current_opacity);
579 }
580
581 /**
582 * Convert image to a PNG image
583 *
584 * @param string $image_url
585 * @param string $type
586 *
587 * @return string|null The url of the newly converted image
588 */
589 protected function _convert_to_png($image_url, $type)
590 {
591 $filename = Cache::getTempImage($image_url);
592
593 if ($filename !== null && file_exists($filename)) {
594 return $filename;
595 }
596
597 $func_name = "imagecreatefrom$type";
598
599 set_error_handler([Helpers::class, "record_warnings"]);
600
601 if (!function_exists($func_name)) {
602 if (!method_exists(Helpers::class, $func_name)) {
603 throw new Exception("Function $func_name() not found. Cannot convert $type image: $image_url. Please install the image PHP extension.");
604 }
605 $func_name = [Helpers::class, $func_name];
606 }
607
608 try {
609 $im = call_user_func($func_name, $image_url);
610
611 if ($im) {
612 imageinterlace($im, false);
613
614 $tmp_dir = $this->_dompdf->getOptions()->getTempDir();
615 $tmp_name = @tempnam($tmp_dir, "{$type}_dompdf_img_");
616 @unlink($tmp_name);
617 $filename = "$tmp_name.png";
618
619 imagepng($im, $filename);
620 imagedestroy($im);
621 } else {
622 $filename = null;
623 }
624 } finally {
625 restore_error_handler();
626 }
627
628 if ($filename !== null) {
629 Cache::addTempImage($image_url, $filename);
630 }
631
632 return $filename;
633 }
634
635 public function image($img, $x, $y, $w, $h, $resolution = "normal")
636 {
637 [$width, $height, $type] = Helpers::dompdf_getimagesize($img, $this->get_dompdf()->getHttpContext());
638
639 $debug_png = $this->_dompdf->getOptions()->getDebugPng();
640
641 if ($debug_png) {
642 print "[image:$img|$width|$height|$type]";
643 }
644
645 switch ($type) {
646 case "jpeg":
647 if ($debug_png) {
648 print '!!!jpg!!!';
649 }
650 $this->_pdf->addJpegFromFile($img, $x, $this->y($y) - $h, $w, $h);
651 break;
652
653 case "webp":
654 /** @noinspection PhpMissingBreakStatementInspection */
655 case "gif":
656 /** @noinspection PhpMissingBreakStatementInspection */
657 case "bmp":
658 if ($debug_png) print "!!!{$type}!!!";
659 $img = $this->_convert_to_png($img, $type);
660 if ($img === null) {
661 if ($debug_png) print '!!!conversion to PDF failed!!!';
662 $this->image(Cache::$broken_image, $x, $y, $w, $h, $resolution);
663 break;
664 }
665
666 case "png":
667 if ($debug_png) print '!!!png!!!';
668
669 $this->_pdf->addPngFromFile($img, $x, $this->y($y) - $h, $w, $h);
670 break;
671
672 case "svg":
673 if ($debug_png) print '!!!SVG!!!';
674
675 $this->_pdf->addSvgFromFile($img, $x, $this->y($y) - $h, $w, $h);
676 break;
677
678 default:
679 if ($debug_png) print '!!!unknown!!!';
680 }
681 }
682
683 public function select($x, $y, $w, $h, $font, $size, $color = [0, 0, 0], $opts = [])
684 {
685 $pdf = $this->_pdf;
686
687 $font .= ".afm";
688 $pdf->selectFont($font);
689
690 if (!isset($pdf->acroFormId)) {
691 $pdf->addForm();
692 }
693
694 $ft = \Dompdf\Cpdf::ACROFORM_FIELD_CHOICE;
695 $ff = \Dompdf\Cpdf::ACROFORM_FIELD_CHOICE_COMBO;
696
697 $id = $pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
698 $pdf->setFormFieldOpt($id, $opts);
699 }
700
701 public function textarea($x, $y, $w, $h, $font, $size, $color = [0, 0, 0])
702 {
703 $pdf = $this->_pdf;
704
705 $font .= ".afm";
706 $pdf->selectFont($font);
707
708 if (!isset($pdf->acroFormId)) {
709 $pdf->addForm();
710 }
711
712 $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
713 $ff = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT_MULTILINE;
714
715 $pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
716 }
717
718 public function input($x, $y, $w, $h, $type, $font, $size, $color = [0, 0, 0])
719 {
720 $pdf = $this->_pdf;
721
722 $font .= ".afm";
723 $pdf->selectFont($font);
724
725 if (!isset($pdf->acroFormId)) {
726 $pdf->addForm();
727 }
728
729 $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
730 $ff = 0;
731
732 switch ($type) {
733 case 'text':
734 $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
735 break;
736 case 'password':
737 $ft = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT;
738 $ff = \Dompdf\Cpdf::ACROFORM_FIELD_TEXT_PASSWORD;
739 break;
740 case 'submit':
741 $ft = \Dompdf\Cpdf::ACROFORM_FIELD_BUTTON;
742 break;
743 }
744
745 $pdf->addFormField($ft, rand(), $x, $this->y($y) - $h, $x + $w, $this->y($y), $ff, $size, $color);
746 }
747
748 public function text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
749 {
750 $pdf = $this->_pdf;
751
752 $this->_set_fill_color($color);
753
754 $is_font_subsetting = $this->_dompdf->getOptions()->getIsFontSubsettingEnabled();
755 $pdf->selectFont($font . '.afm', '', true, $is_font_subsetting);
756
757 $pdf->addText($x, $this->y($y) - $pdf->getFontHeight($size), $size, $text, $angle, $word_space, $char_space);
758
759 $this->_set_fill_transparency("Normal", $this->_current_opacity);
760 }
761
762 public function javascript($code)
763 {
764 $this->_pdf->addJavascript($code);
765 }
766
767 //........................................................................
768
769 public function add_named_dest($anchorname)
770 {
771 $this->_pdf->addDestination($anchorname, "Fit");
772 }
773
774 public function add_link($url, $x, $y, $width, $height)
775 {
776 $y = $this->y($y) - $height;
777
778 if (strpos($url, '#') === 0) {
779 // Local link
780 $name = substr($url, 1);
781 if ($name) {
782 $this->_pdf->addInternalLink($name, $x, $y, $x + $width, $y + $height);
783 }
784 } else {
785 $this->_pdf->addLink($url, $x, $y, $x + $width, $y + $height);
786 }
787 }
788
789 /**
790 * @throws FontNotFoundException
791 */
792 public function get_text_width($text, $font, $size, $word_spacing = 0.0, $char_spacing = 0.0)
793 {
794 $this->_pdf->selectFont($font, '', true, $this->_dompdf->getOptions()->getIsFontSubsettingEnabled());
795 return $this->_pdf->getTextWidth($size, $text, $word_spacing, $char_spacing);
796 }
797
798 /**
799 * @throws FontNotFoundException
800 */
801 public function get_font_height($font, $size)
802 {
803 $options = $this->_dompdf->getOptions();
804 $this->_pdf->selectFont($font, '', true, $options->getIsFontSubsettingEnabled());
805
806 return $this->_pdf->getFontHeight($size) * $options->getFontHeightRatio();
807 }
808
809 /*function get_font_x_height($font, $size) {
810 $this->_pdf->selectFont($font);
811 $ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
812 return $this->_pdf->getFontXHeight($size) * $ratio;
813 }*/
814
815 /**
816 * @throws FontNotFoundException
817 */
818 public function get_font_baseline($font, $size)
819 {
820 $ratio = $this->_dompdf->getOptions()->getFontHeightRatio();
821 return $this->get_font_height($font, $size) / $ratio;
822 }
823
824 /**
825 * Processes a callback or script on every page.
826 *
827 * The callback function receives the four parameters `int $pageNumber`,
828 * `int $pageCount`, `Canvas $canvas`, and `FontMetrics $fontMetrics`, in
829 * that order. If a script is passed as string, the variables `$PAGE_NUM`,
830 * `$PAGE_COUNT`, `$pdf`, and `$fontMetrics` are available instead. Passing
831 * a script as string is deprecated and will be removed in a future version.
832 *
833 * This function can be used to add page numbers to all pages after the
834 * first one, for example.
835 *
836 * @param callable|string $callback The callback function or PHP script to process on every page
837 */
838 public function page_script($callback): void
839 {
840 if (is_string($callback)) {
841 $this->processPageScript(function (
842 int $PAGE_NUM,
843 int $PAGE_COUNT,
844 self $pdf,
845 FontMetrics $fontMetrics
846 ) use ($callback) {
847 eval($callback);
848 });
849 return;
850 }
851
852 $this->processPageScript($callback);
853 }
854
855 public function page_text($x, $y, $text, $font, $size, $color = [0, 0, 0], $word_space = 0.0, $char_space = 0.0, $angle = 0.0)
856 {
857 $this->processPageScript(function (int $pageNumber, int $pageCount) use ($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle) {
858 $text = str_replace(
859 ["{PAGE_NUM}", "{PAGE_COUNT}"],
860 [$pageNumber, $pageCount],
861 $text
862 );
863 $this->text($x, $y, $text, $font, $size, $color, $word_space, $char_space, $angle);
864 });
865 }
866
867 public function page_line($x1, $y1, $x2, $y2, $color, $width, $style = [])
868 {
869 $this->processPageScript(function () use ($x1, $y1, $x2, $y2, $color, $width, $style) {
870 $this->line($x1, $y1, $x2, $y2, $color, $width, $style);
871 });
872 }
873
874 /**
875 * @return int
876 */
877 public function new_page()
878 {
879 $this->_page_number++;
880 $this->_page_count++;
881
882 $ret = $this->_pdf->newPage();
883 $this->_pages[] = $ret;
884 return $ret;
885 }
886
887 protected function processPageScript(callable $callback): void
888 {
889 $pageNumber = 1;
890
891 foreach ($this->_pages as $pid) {
892 $this->reopen_object($pid);
893
894 $fontMetrics = $this->_dompdf->getFontMetrics();
895 $callback($pageNumber, $this->_page_count, $this, $fontMetrics);
896
897 $this->close_object();
898 $pageNumber++;
899 }
900 }
901
902 public function stream($filename = "document.pdf", $options = [])
903 {
904 if (headers_sent()) {
905 die("Unable to stream pdf: headers already sent");
906 }
907
908 if (!isset($options["compress"])) $options["compress"] = true;
909 if (!isset($options["Attachment"])) $options["Attachment"] = true;
910
911 $debug = !$options['compress'];
912 $tmp = ltrim($this->_pdf->output($debug));
913
914 header("Cache-Control: private");
915 header("Content-Type: application/pdf");
916 header("Content-Length: " . mb_strlen($tmp, "8bit"));
917
918 $filename = str_replace(["\n", "'"], "", basename($filename, ".pdf")) . ".pdf";
919 $attachment = $options["Attachment"] ? "attachment" : "inline";
920 header(Helpers::buildContentDispositionHeader($attachment, $filename));
921
922 echo $tmp;
923 flush();
924 }
925
926 public function output($options = [])
927 {
928 if (!isset($options["compress"])) $options["compress"] = true;
929
930 $debug = !$options['compress'];
931
932 return $this->_pdf->output($debug);
933 }
934
935 /**
936 * Returns logging messages generated by the Cpdf class
937 *
938 * @return string
939 */
940 public function get_messages()
941 {
942 return $this->_pdf->messages;
943 }
944 }
945