1<?php
2//============================================================+
3// File name   : tcpdf.php
4// Version     : 6.4.4
5// Begin       : 2002-08-03
6// Last Update : 2021-12-31
7// Author      : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
8// License     : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
9// -------------------------------------------------------------------
10// Copyright (C) 2002-2021 Nicola Asuni - Tecnick.com LTD
11//
12// This file is part of TCPDF software library.
13//
14// TCPDF is free software: you can redistribute it and/or modify it
15// under the terms of the GNU Lesser General Public License as
16// published by the Free Software Foundation, either version 3 of the
17// License, or (at your option) any later version.
18//
19// TCPDF is distributed in the hope that it will be useful, but
20// WITHOUT ANY WARRANTY; without even the implied warranty of
21// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22// See the GNU Lesser General Public License for more details.
23//
24// You should have received a copy of the License
25// along with TCPDF. If not, see
26// <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>.
27//
28// See LICENSE.TXT file for more information.
29// -------------------------------------------------------------------
30//
31// Description :
32//   This is a PHP class for generating PDF documents without requiring external extensions.
33//
34// NOTE:
35//   This class was originally derived in 2002 from the Public
36//   Domain FPDF class by Olivier Plathey (http://www.fpdf.org),
37//   but now is almost entirely rewritten and contains thousands of
38//   new lines of code and hundreds new features.
39//
40// Main features:
41//  * no external libraries are required for the basic functions;
42//  * all standard page formats, custom page formats, custom margins and units of measure;
43//  * UTF-8 Unicode and Right-To-Left languages;
44//  * TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;
45//  * font subsetting;
46//  * methods to publish some XHTML + CSS code, Javascript and Forms;
47//  * images, graphic (geometric figures) and transformation methods;
48//  * supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImageMagick (http://www.imagemagick.org/www/formats.html)
49//  * 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extension, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417;
50//  * JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;
51//  * automatic page header and footer management;
52//  * document encryption up to 256 bit and digital signature certifications;
53//  * transactions to UNDO commands;
54//  * PDF annotations, including links, text and file attachments;
55//  * text rendering modes (fill, stroke and clipping);
56//  * multiple columns mode;
57//  * no-write page regions;
58//  * bookmarks, named destinations and table of content;
59//  * text hyphenation;
60//  * text stretching and spacing (tracking);
61//  * automatic page break, line break and text alignments including justification;
62//  * automatic page numbering and page groups;
63//  * move and delete pages;
64//  * page compression (requires php-zlib extension);
65//  * XOBject Templates;
66//  * Layers and object visibility.
67//	* PDF/A-1b support
68//============================================================+
69
70/**
71 * @file
72 * This is a PHP class for generating PDF documents without requiring external extensions.<br>
73 * TCPDF project (http://www.tcpdf.org) was originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br>
74 * <h3>TCPDF main features are:</h3>
75 * <ul>
76 * <li>no external libraries are required for the basic functions;</li>
77 * <li>all standard page formats, custom page formats, custom margins and units of measure;</li>
78 * <li>UTF-8 Unicode and Right-To-Left languages;</li>
79 * <li>TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;</li>
80 * <li>font subsetting;</li>
81 * <li>methods to publish some XHTML + CSS code, Javascript and Forms;</li>
82 * <li>images, graphic (geometric figures) and transformation methods;
83 * <li>supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImageMagick (http://www.imagemagick.org/www/formats.html)</li>
84 * <li>1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extension, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417;</li>
85 * <li>JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li>
86 * <li>automatic page header and footer management;</li>
87 * <li>document encryption up to 256 bit and digital signature certifications;</li>
88 * <li>transactions to UNDO commands;</li>
89 * <li>PDF annotations, including links, text and file attachments;</li>
90 * <li>text rendering modes (fill, stroke and clipping);</li>
91 * <li>multiple columns mode;</li>
92 * <li>no-write page regions;</li>
93 * <li>bookmarks, named destinations and table of content;</li>
94 * <li>text hyphenation;</li>
95 * <li>text stretching and spacing (tracking);</li>
96 * <li>automatic page break, line break and text alignments including justification;</li>
97 * <li>automatic page numbering and page groups;</li>
98 * <li>move and delete pages;</li>
99 * <li>page compression (requires php-zlib extension);</li>
100 * <li>XOBject Templates;</li>
101 * <li>Layers and object visibility;</li>
102 * <li>PDF/A-1b support.</li>
103 * </ul>
104 * Tools to encode your unicode fonts are on fonts/utils directory.</p>
105 * @package com.tecnick.tcpdf
106 * @author Nicola Asuni
107 * @version 6.3.2
108 */
109
110// TCPDF configuration
111require_once(dirname(__FILE__).'/tcpdf_autoconfig.php');
112// TCPDF static font methods and data
113require_once(dirname(__FILE__).'/include/tcpdf_font_data.php');
114// TCPDF static font methods and data
115require_once(dirname(__FILE__).'/include/tcpdf_fonts.php');
116// TCPDF static color methods and data
117require_once(dirname(__FILE__).'/include/tcpdf_colors.php');
118// TCPDF static image methods and data
119require_once(dirname(__FILE__).'/include/tcpdf_images.php');
120// TCPDF static methods and data
121require_once(dirname(__FILE__).'/include/tcpdf_static.php');
122
123// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
124
125/**
126 * @class TCPDF
127 * PHP class for generating PDF documents without requiring external extensions.
128 * TCPDF project (http://www.tcpdf.org) has been originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br>
129 * @package com.tecnick.tcpdf
130 * @brief PHP class for generating PDF documents without requiring external extensions.
131 * @version 6.3.2
132 * @author Nicola Asuni - info@tecnick.com
133 * @IgnoreAnnotation("protected")
134 * @IgnoreAnnotation("public")
135 * @IgnoreAnnotation("pre")
136 */
137class TCPDF {
138
139	// Protected properties
140
141	/**
142	 * Current page number.
143	 * @protected
144	 */
145	protected $page;
146
147	/**
148	 * Current object number.
149	 * @protected
150	 */
151	protected $n;
152
153	/**
154	 * Array of object offsets.
155	 * @protected
156	 */
157	protected $offsets = array();
158
159	/**
160	 * Array of object IDs for each page.
161	 * @protected
162	 */
163	protected $pageobjects = array();
164
165	/**
166	 * Buffer holding in-memory PDF.
167	 * @protected
168	 */
169	protected $buffer;
170
171	/**
172	 * Array containing pages.
173	 * @protected
174	 */
175	protected $pages = array();
176
177	/**
178	 * Current document state.
179	 * @protected
180	 */
181	protected $state;
182
183	/**
184	 * Compression flag.
185	 * @protected
186	 */
187	protected $compress;
188
189	/**
190	 * Current page orientation (P = Portrait, L = Landscape).
191	 * @protected
192	 */
193	protected $CurOrientation;
194
195	/**
196	 * Page dimensions.
197	 * @protected
198	 */
199	protected $pagedim = array();
200
201	/**
202	 * Scale factor (number of points in user unit).
203	 * @protected
204	 */
205	protected $k;
206
207	/**
208	 * Width of page format in points.
209	 * @protected
210	 */
211	protected $fwPt;
212
213	/**
214	 * Height of page format in points.
215	 * @protected
216	 */
217	protected $fhPt;
218
219	/**
220	 * Current width of page in points.
221	 * @protected
222	 */
223	protected $wPt;
224
225	/**
226	 * Current height of page in points.
227	 * @protected
228	 */
229	protected $hPt;
230
231	/**
232	 * Current width of page in user unit.
233	 * @protected
234	 */
235	protected $w;
236
237	/**
238	 * Current height of page in user unit.
239	 * @protected
240	 */
241	protected $h;
242
243	/**
244	 * Left margin.
245	 * @protected
246	 */
247	protected $lMargin;
248
249	/**
250	 * Right margin.
251	 * @protected
252	 */
253	protected $rMargin;
254
255	/**
256	 * Cell left margin (used by regions).
257	 * @protected
258	 */
259	protected $clMargin;
260
261	/**
262	 * Cell right margin (used by regions).
263	 * @protected
264	 */
265	protected $crMargin;
266
267	/**
268	 * Top margin.
269	 * @protected
270	 */
271	protected $tMargin;
272
273	/**
274	 * Page break margin.
275	 * @protected
276	 */
277	protected $bMargin;
278
279	/**
280	 * Array of cell internal paddings ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
281	 * @since 5.9.000 (2010-10-03)
282	 * @protected
283	 */
284	protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
285
286	/**
287	 * Array of cell margins ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
288	 * @since 5.9.000 (2010-10-04)
289	 * @protected
290	 */
291	protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
292
293	/**
294	 * Current horizontal position in user unit for cell positioning.
295	 * @protected
296	 */
297	protected $x;
298
299	/**
300	 * Current vertical position in user unit for cell positioning.
301	 * @protected
302	 */
303	protected $y;
304
305	/**
306	 * Height of last cell printed.
307	 * @protected
308	 */
309	protected $lasth;
310
311	/**
312	 * Line width in user unit.
313	 * @protected
314	 */
315	protected $LineWidth;
316
317	/**
318	 * Array of standard font names.
319	 * @protected
320	 */
321	protected $CoreFonts;
322
323	/**
324	 * Array of used fonts.
325	 * @protected
326	 */
327	protected $fonts = array();
328
329	/**
330	 * Array of font files.
331	 * @protected
332	 */
333	protected $FontFiles = array();
334
335	/**
336	 * Array of encoding differences.
337	 * @protected
338	 */
339	protected $diffs = array();
340
341	/**
342	 * Array of used images.
343	 * @protected
344	 */
345	protected $images = array();
346
347	/**
348	 * Depth of the svg tag, to keep track if the svg tag is a subtag or the root tag.
349	 * @protected
350	 */
351	protected $svg_tag_depth = 0;
352
353	/**
354	 * Array of Annotations in pages.
355	 * @protected
356	 */
357	protected $PageAnnots = array();
358
359	/**
360	 * Array of internal links.
361	 * @protected
362	 */
363	protected $links = array();
364
365	/**
366	 * Current font family.
367	 * @protected
368	 */
369	protected $FontFamily;
370
371	/**
372	 * Current font style.
373	 * @protected
374	 */
375	protected $FontStyle;
376
377	/**
378	 * Current font ascent (distance between font top and baseline).
379	 * @protected
380	 * @since 2.8.000 (2007-03-29)
381	 */
382	protected $FontAscent;
383
384	/**
385	 * Current font descent (distance between font bottom and baseline).
386	 * @protected
387	 * @since 2.8.000 (2007-03-29)
388	 */
389	protected $FontDescent;
390
391	/**
392	 * Underlining flag.
393	 * @protected
394	 */
395	protected $underline;
396
397	/**
398	 * Overlining flag.
399	 * @protected
400	 */
401	protected $overline;
402
403	/**
404	 * Current font info.
405	 * @protected
406	 */
407	protected $CurrentFont;
408
409	/**
410	 * Current font size in points.
411	 * @protected
412	 */
413	protected $FontSizePt;
414
415	/**
416	 * Current font size in user unit.
417	 * @protected
418	 */
419	protected $FontSize;
420
421	/**
422	 * Commands for drawing color.
423	 * @protected
424	 */
425	protected $DrawColor;
426
427	/**
428	 * Commands for filling color.
429	 * @protected
430	 */
431	protected $FillColor;
432
433	/**
434	 * Commands for text color.
435	 * @protected
436	 */
437	protected $TextColor;
438
439	/**
440	 * Indicates whether fill and text colors are different.
441	 * @protected
442	 */
443	protected $ColorFlag;
444
445	/**
446	 * Automatic page breaking.
447	 * @protected
448	 */
449	protected $AutoPageBreak;
450
451	/**
452	 * Threshold used to trigger page breaks.
453	 * @protected
454	 */
455	protected $PageBreakTrigger;
456
457	/**
458	 * Flag set when processing page header.
459	 * @protected
460	 */
461	protected $InHeader = false;
462
463	/**
464	 * Flag set when processing page footer.
465	 * @protected
466	 */
467	protected $InFooter = false;
468
469	/**
470	 * Zoom display mode.
471	 * @protected
472	 */
473	protected $ZoomMode;
474
475	/**
476	 * Layout display mode.
477	 * @protected
478	 */
479	protected $LayoutMode;
480
481	/**
482	 * If true set the document information dictionary in Unicode.
483	 * @protected
484	 */
485	protected $docinfounicode = true;
486
487	/**
488	 * Document title.
489	 * @protected
490	 */
491	protected $title = '';
492
493	/**
494	 * Document subject.
495	 * @protected
496	 */
497	protected $subject = '';
498
499	/**
500	 * Document author.
501	 * @protected
502	 */
503	protected $author = '';
504
505	/**
506	 * Document keywords.
507	 * @protected
508	 */
509	protected $keywords = '';
510
511	/**
512	 * Document creator.
513	 * @protected
514	 */
515	protected $creator = '';
516
517	/**
518	 * Starting page number.
519	 * @protected
520	 */
521	protected $starting_page_number = 1;
522
523	/**
524	 * The right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image.
525	 * @since 2002-07-31
526	 * @author Nicola Asuni
527	 * @protected
528	 */
529	protected $img_rb_x;
530
531	/**
532	 * The right-bottom corner Y coordinate of last inserted image.
533	 * @since 2002-07-31
534	 * @author Nicola Asuni
535	 * @protected
536	 */
537	protected $img_rb_y;
538
539	/**
540	 * Adjusting factor to convert pixels to user units.
541	 * @since 2004-06-14
542	 * @author Nicola Asuni
543	 * @protected
544	 */
545	protected $imgscale = 1;
546
547	/**
548	 * Boolean flag set to true when the input text is unicode (require unicode fonts).
549	 * @since 2005-01-02
550	 * @author Nicola Asuni
551	 * @protected
552	 */
553	protected $isunicode = false;
554
555	/**
556	 * PDF version.
557	 * @since 1.5.3
558	 * @protected
559	 */
560	protected $PDFVersion = '1.7';
561
562	/**
563	 * ID of the stored default header template (-1 = not set).
564	 * @protected
565	 */
566	protected $header_xobjid = false;
567
568	/**
569	 * If true reset the Header Xobject template at each page
570	 * @protected
571	 */
572	protected $header_xobj_autoreset = false;
573
574	/**
575	 * Minimum distance between header and top page margin.
576	 * @protected
577	 */
578	protected $header_margin;
579
580	/**
581	 * Minimum distance between footer and bottom page margin.
582	 * @protected
583	 */
584	protected $footer_margin;
585
586	/**
587	 * Original left margin value.
588	 * @protected
589	 * @since 1.53.0.TC013
590	 */
591	protected $original_lMargin;
592
593	/**
594	 * Original right margin value.
595	 * @protected
596	 * @since 1.53.0.TC013
597	 */
598	protected $original_rMargin;
599
600	/**
601	 * Default font used on page header.
602	 * @protected
603	 * @var array<int,string|float|null>
604	 * @phpstan-var array{0: string, 1: string, 2: float|null}
605	 */
606	protected $header_font;
607
608	/**
609	 * Default font used on page footer.
610	 * @protected
611	 * @var array<int,string|float|null>
612	 * @phpstan-var array{0: string, 1: string, 2: float|null}
613	 */
614	protected $footer_font;
615
616	/**
617	 * Language templates.
618	 * @protected
619	 */
620	protected $l;
621
622	/**
623	 * Barcode to print on page footer (only if set).
624	 * @protected
625	 */
626	protected $barcode = false;
627
628	/**
629	 * Boolean flag to print/hide page header.
630	 * @protected
631	 */
632	protected $print_header = true;
633
634	/**
635	 * Boolean flag to print/hide page footer.
636	 * @protected
637	 */
638	protected $print_footer = true;
639
640	/**
641	 * Header image logo.
642	 * @protected
643	 */
644	protected $header_logo = '';
645
646	/**
647	 * Width of header image logo in user units.
648	 * @protected
649	 */
650	protected $header_logo_width = 30;
651
652	/**
653	 * Title to be printed on default page header.
654	 * @protected
655	 */
656	protected $header_title = '';
657
658	/**
659	 * String to print on page header after title.
660	 * @protected
661	 */
662	protected $header_string = '';
663
664	/**
665	 * Color for header text (RGB array).
666	 * @since 5.9.174 (2012-07-25)
667	 * @protected
668	 * @var int[]
669	 * @phpstan-var array{0: int, 1: int, 2: int}
670	 */
671	protected $header_text_color = array(0,0,0);
672
673	/**
674	 * Color for header line (RGB array).
675	 * @since 5.9.174 (2012-07-25)
676	 * @protected
677	 * @var int[]
678	 * @phpstan-var array{0: int, 1: int, 2: int}
679	 */
680	protected $header_line_color = array(0,0,0);
681
682	/**
683	 * Color for footer text (RGB array).
684	 * @since 5.9.174 (2012-07-25)
685	 * @protected
686	 * @var int[]
687	 * @phpstan-var array{0: int, 1: int, 2: int}
688	 */
689	protected $footer_text_color = array(0,0,0);
690
691	/**
692	 * Color for footer line (RGB array).
693	 * @since 5.9.174 (2012-07-25)
694	 * @protected
695	 * @var int[]
696	 * @phpstan-var array{0: int, 1: int, 2: int}
697	 */
698	protected $footer_line_color = array(0,0,0);
699
700	/**
701	 * Text shadow data array.
702	 * @since 5.9.174 (2012-07-25)
703	 * @protected
704	 */
705	protected $txtshadow = array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal');
706
707	/**
708	 * Default number of columns for html table.
709	 * @protected
710	 */
711	protected $default_table_columns = 4;
712
713	// variables for html parser
714
715	/**
716	 * HTML PARSER: array to store current link and rendering styles.
717	 * @protected
718	 */
719	protected $HREF = array();
720
721	/**
722	 * List of available fonts on filesystem.
723	 * @protected
724	 */
725	protected $fontlist = array();
726
727	/**
728	 * Current foreground color.
729	 * @protected
730	 */
731	protected $fgcolor;
732
733	/**
734	 * HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
735	 * @protected
736	 */
737	protected $listordered = array();
738
739	/**
740	 * HTML PARSER: array count list items on nested lists.
741	 * @protected
742	 */
743	protected $listcount = array();
744
745	/**
746	 * HTML PARSER: current list nesting level.
747	 * @protected
748	 */
749	protected $listnum = 0;
750
751	/**
752	 * HTML PARSER: indent amount for lists.
753	 * @protected
754	 */
755	protected $listindent = 0;
756
757	/**
758	 * HTML PARSER: current list indententation level.
759	 * @protected
760	 */
761	protected $listindentlevel = 0;
762
763	/**
764	 * Current background color.
765	 * @protected
766	 */
767	protected $bgcolor;
768
769	/**
770	 * Temporary font size in points.
771	 * @protected
772	 */
773	protected $tempfontsize = 10;
774
775	/**
776	 * Spacer string for LI tags.
777	 * @protected
778	 */
779	protected $lispacer = '';
780
781	/**
782	 * Default encoding.
783	 * @protected
784	 * @since 1.53.0.TC010
785	 */
786	protected $encoding = 'UTF-8';
787
788	/**
789	 * Boolean flag to indicate if the document language is Right-To-Left.
790	 * @protected
791	 * @since 2.0.000
792	 */
793	protected $rtl = false;
794
795	/**
796	 * Boolean flag used to force RTL or LTR string direction.
797	 * @protected
798	 * @since 2.0.000
799	 */
800	protected $tmprtl = false;
801
802	// --- Variables used for document encryption:
803
804	/**
805	 * IBoolean flag indicating whether document is protected.
806	 * @protected
807	 * @since 2.0.000 (2008-01-02)
808	 */
809	protected $encrypted;
810
811	/**
812	 * Array containing encryption settings.
813	 * @protected
814	 * @since 5.0.005 (2010-05-11)
815	 */
816	protected $encryptdata = array();
817
818	/**
819	 * Last RC4 key encrypted (cached for optimisation).
820	 * @protected
821	 * @since 2.0.000 (2008-01-02)
822	 */
823	protected $last_enc_key;
824
825	/**
826	 * Last RC4 computed key.
827	 * @protected
828	 * @since 2.0.000 (2008-01-02)
829	 */
830	protected $last_enc_key_c;
831
832	/**
833	 * File ID (used on document trailer).
834	 * @protected
835	 * @since 5.0.005 (2010-05-12)
836	 */
837	protected $file_id;
838
839	// --- bookmark ---
840
841	/**
842	 * Outlines for bookmark.
843	 * @protected
844	 * @since 2.1.002 (2008-02-12)
845	 */
846	protected $outlines = array();
847
848	/**
849	 * Outline root for bookmark.
850	 * @protected
851	 * @since 2.1.002 (2008-02-12)
852	 */
853	protected $OutlineRoot;
854
855	// --- javascript and form ---
856
857	/**
858	 * Javascript code.
859	 * @protected
860	 * @since 2.1.002 (2008-02-12)
861	 */
862	protected $javascript = '';
863
864	/**
865	 * Javascript counter.
866	 * @protected
867	 * @since 2.1.002 (2008-02-12)
868	 */
869	protected $n_js;
870
871	/**
872	 * line through state
873	 * @protected
874	 * @since 2.8.000 (2008-03-19)
875	 */
876	protected $linethrough;
877
878	/**
879	 * Array with additional document-wide usage rights for the document.
880	 * @protected
881	 * @since 5.8.014 (2010-08-23)
882	 */
883	protected $ur = array();
884
885	/**
886	 * DPI (Dot Per Inch) Document Resolution (do not change).
887	 * @protected
888	 * @since 3.0.000 (2008-03-27)
889	 */
890	protected $dpi = 72;
891
892	/**
893	 * Array of page numbers were a new page group was started (the page numbers are the keys of the array).
894	 * @protected
895	 * @since 3.0.000 (2008-03-27)
896	 */
897	protected $newpagegroup = array();
898
899	/**
900	 * Array that contains the number of pages in each page group.
901	 * @protected
902	 * @since 3.0.000 (2008-03-27)
903	 */
904	protected $pagegroups = array();
905
906	/**
907	 * Current page group number.
908	 * @protected
909	 * @since 3.0.000 (2008-03-27)
910	 */
911	protected $currpagegroup = 0;
912
913	/**
914	 * Array of transparency objects and parameters.
915	 * @protected
916	 * @since 3.0.000 (2008-03-27)
917	 */
918	protected $extgstates;
919
920	/**
921	 * Set the default JPEG compression quality (1-100).
922	 * @protected
923	 * @since 3.0.000 (2008-03-27)
924	 */
925	protected $jpeg_quality;
926
927	/**
928	 * Default cell height ratio.
929	 * @protected
930	 * @since 3.0.014 (2008-05-23)
931	 * @var float
932	 */
933	protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
934
935	/**
936	 * PDF viewer preferences.
937	 * @protected
938	 * @since 3.1.000 (2008-06-09)
939	 */
940	protected $viewer_preferences;
941
942	/**
943	 * A name object specifying how the document should be displayed when opened.
944	 * @protected
945	 * @since 3.1.000 (2008-06-09)
946	 */
947	protected $PageMode;
948
949	/**
950	 * Array for storing gradient information.
951	 * @protected
952	 * @since 3.1.000 (2008-06-09)
953	 */
954	protected $gradients = array();
955
956	/**
957	 * Array used to store positions inside the pages buffer (keys are the page numbers).
958	 * @protected
959	 * @since 3.2.000 (2008-06-26)
960	 */
961	protected $intmrk = array();
962
963	/**
964	 * Array used to store positions inside the pages buffer (keys are the page numbers).
965	 * @protected
966	 * @since 5.7.000 (2010-08-03)
967	 */
968	protected $bordermrk = array();
969
970	/**
971	 * Array used to store page positions to track empty pages (keys are the page numbers).
972	 * @protected
973	 * @since 5.8.007 (2010-08-18)
974	 */
975	protected $emptypagemrk = array();
976
977	/**
978	 * Array used to store content positions inside the pages buffer (keys are the page numbers).
979	 * @protected
980	 * @since 4.6.021 (2009-07-20)
981	 */
982	protected $cntmrk = array();
983
984	/**
985	 * Array used to store footer positions of each page.
986	 * @protected
987	 * @since 3.2.000 (2008-07-01)
988	 */
989	protected $footerpos = array();
990
991	/**
992	 * Array used to store footer length of each page.
993	 * @protected
994	 * @since 4.0.014 (2008-07-29)
995	 */
996	protected $footerlen = array();
997
998	/**
999	 * Boolean flag to indicate if a new line is created.
1000	 * @protected
1001	 * @since 3.2.000 (2008-07-01)
1002	 */
1003	protected $newline = true;
1004
1005	/**
1006	 * End position of the latest inserted line.
1007	 * @protected
1008	 * @since 3.2.000 (2008-07-01)
1009	 */
1010	protected $endlinex = 0;
1011
1012	/**
1013	 * PDF string for width value of the last line.
1014	 * @protected
1015	 * @since 4.0.006 (2008-07-16)
1016	 */
1017	protected $linestyleWidth = '';
1018
1019	/**
1020	 * PDF string for CAP value of the last line.
1021	 * @protected
1022	 * @since 4.0.006 (2008-07-16)
1023	 */
1024	protected $linestyleCap = '0 J';
1025
1026	/**
1027	 * PDF string for join value of the last line.
1028	 * @protected
1029	 * @since 4.0.006 (2008-07-16)
1030	 */
1031	protected $linestyleJoin = '0 j';
1032
1033	/**
1034	 * PDF string for dash value of the last line.
1035	 * @protected
1036	 * @since 4.0.006 (2008-07-16)
1037	 */
1038	protected $linestyleDash = '[] 0 d';
1039
1040	/**
1041	 * Boolean flag to indicate if marked-content sequence is open.
1042	 * @protected
1043	 * @since 4.0.013 (2008-07-28)
1044	 */
1045	protected $openMarkedContent = false;
1046
1047	/**
1048	 * Count the latest inserted vertical spaces on HTML.
1049	 * @protected
1050	 * @since 4.0.021 (2008-08-24)
1051	 */
1052	protected $htmlvspace = 0;
1053
1054	/**
1055	 * Array of Spot colors.
1056	 * @protected
1057	 * @since 4.0.024 (2008-09-12)
1058	 */
1059	protected $spot_colors = array();
1060
1061	/**
1062	 * Symbol used for HTML unordered list items.
1063	 * @protected
1064	 * @since 4.0.028 (2008-09-26)
1065	 */
1066	protected $lisymbol = '';
1067
1068	/**
1069	 * String used to mark the beginning and end of EPS image blocks.
1070	 * @protected
1071	 * @since 4.1.000 (2008-10-18)
1072	 */
1073	protected $epsmarker = 'x#!#EPS#!#x';
1074
1075	/**
1076	 * Array of transformation matrix.
1077	 * @protected
1078	 * @since 4.2.000 (2008-10-29)
1079	 */
1080	protected $transfmatrix = array();
1081
1082	/**
1083	 * Current key for transformation matrix.
1084	 * @protected
1085	 * @since 4.8.005 (2009-09-17)
1086	 */
1087	protected $transfmatrix_key = 0;
1088
1089	/**
1090	 * Booklet mode for double-sided pages.
1091	 * @protected
1092	 * @since 4.2.000 (2008-10-29)
1093	 */
1094	protected $booklet = false;
1095
1096	/**
1097	 * Epsilon value used for float calculations.
1098	 * @protected
1099	 * @since 4.2.000 (2008-10-29)
1100	 */
1101	protected $feps = 0.005;
1102
1103	/**
1104	 * Array used for custom vertical spaces for HTML tags.
1105	 * @protected
1106	 * @since 4.2.001 (2008-10-30)
1107	 */
1108	protected $tagvspaces = array();
1109
1110	/**
1111	 * HTML PARSER: custom indent amount for lists. Negative value means disabled.
1112	 * @protected
1113	 * @since 4.2.007 (2008-11-12)
1114	 */
1115	protected $customlistindent = -1;
1116
1117	/**
1118	 * Boolean flag to indicate if the border of the cell sides that cross the page should be removed.
1119	 * @protected
1120	 * @since 4.2.010 (2008-11-14)
1121	 */
1122	protected $opencell = true;
1123
1124	/**
1125	 * Array of files to embedd.
1126	 * @protected
1127	 * @since 4.4.000 (2008-12-07)
1128	 */
1129	protected $embeddedfiles = array();
1130
1131	/**
1132	 * Boolean flag to indicate if we are inside a PRE tag.
1133	 * @protected
1134	 * @since 4.4.001 (2008-12-08)
1135	 */
1136	protected $premode = false;
1137
1138	/**
1139	 * Array used to store positions of graphics transformation blocks inside the page buffer.
1140	 * keys are the page numbers
1141	 * @protected
1142	 * @since 4.4.002 (2008-12-09)
1143	 */
1144	protected $transfmrk = array();
1145
1146	/**
1147	 * Default color for html links.
1148	 * @protected
1149	 * @since 4.4.003 (2008-12-09)
1150	 */
1151	protected $htmlLinkColorArray = array(0, 0, 255);
1152
1153	/**
1154	 * Default font style to add to html links.
1155	 * @protected
1156	 * @since 4.4.003 (2008-12-09)
1157	 */
1158	protected $htmlLinkFontStyle = 'U';
1159
1160	/**
1161	 * Counts the number of pages.
1162	 * @protected
1163	 * @since 4.5.000 (2008-12-31)
1164	 */
1165	protected $numpages = 0;
1166
1167	/**
1168	 * Array containing page lengths in bytes.
1169	 * @protected
1170	 * @since 4.5.000 (2008-12-31)
1171	 */
1172	protected $pagelen = array();
1173
1174	/**
1175	 * Counts the number of pages.
1176	 * @protected
1177	 * @since 4.5.000 (2008-12-31)
1178	 */
1179	protected $numimages = 0;
1180
1181	/**
1182	 * Store the image keys.
1183	 * @protected
1184	 * @since 4.5.000 (2008-12-31)
1185	 */
1186	protected $imagekeys = array();
1187
1188	/**
1189	 * Length of the buffer in bytes.
1190	 * @protected
1191	 * @since 4.5.000 (2008-12-31)
1192	 */
1193	protected $bufferlen = 0;
1194
1195	/**
1196	 * Counts the number of fonts.
1197	 * @protected
1198	 * @since 4.5.000 (2009-01-02)
1199	 */
1200	protected $numfonts = 0;
1201
1202	/**
1203	 * Store the font keys.
1204	 * @protected
1205	 * @since 4.5.000 (2009-01-02)
1206	 */
1207	protected $fontkeys = array();
1208
1209	/**
1210	 * Store the font object IDs.
1211	 * @protected
1212	 * @since 4.8.001 (2009-09-09)
1213	 */
1214	protected $font_obj_ids = array();
1215
1216	/**
1217	 * Store the fage status (true when opened, false when closed).
1218	 * @protected
1219	 * @since 4.5.000 (2009-01-02)
1220	 */
1221	protected $pageopen = array();
1222
1223	/**
1224	 * Default monospace font.
1225	 * @protected
1226	 * @since 4.5.025 (2009-03-10)
1227	 */
1228	protected $default_monospaced_font = 'courier';
1229
1230	/**
1231	 * Cloned copy of the current class object.
1232	 * @protected
1233	 * @since 4.5.029 (2009-03-19)
1234	 */
1235	protected $objcopy;
1236
1237	/**
1238	 * Array used to store the lengths of cache files.
1239	 * @protected
1240	 * @since 4.5.029 (2009-03-19)
1241	 */
1242	protected $cache_file_length = array();
1243
1244	/**
1245	 * Table header content to be repeated on each new page.
1246	 * @protected
1247	 * @since 4.5.030 (2009-03-20)
1248	 */
1249	protected $thead = '';
1250
1251	/**
1252	 * Margins used for table header.
1253	 * @protected
1254	 * @since 4.5.030 (2009-03-20)
1255	 */
1256	protected $theadMargins = array();
1257
1258	/**
1259	 * Boolean flag to enable document digital signature.
1260	 * @protected
1261	 * @since 4.6.005 (2009-04-24)
1262	 */
1263	protected $sign = false;
1264
1265	/**
1266	 * Digital signature data.
1267	 * @protected
1268	 * @since 4.6.005 (2009-04-24)
1269	 */
1270	protected $signature_data = array();
1271
1272	/**
1273	 * Digital signature max length.
1274	 * @protected
1275	 * @since 4.6.005 (2009-04-24)
1276	 */
1277	protected $signature_max_length = 11742;
1278
1279	/**
1280	 * Data for digital signature appearance.
1281	 * @protected
1282	 * @since 5.3.011 (2010-06-16)
1283	 */
1284	protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
1285
1286	/**
1287	 * Array of empty digital signature appearances.
1288	 * @protected
1289	 * @since 5.9.101 (2011-07-06)
1290	 */
1291	protected $empty_signature_appearance = array();
1292
1293	/**
1294	 * Boolean flag to enable document timestamping with TSA.
1295	 * @protected
1296	 * @since 6.0.085 (2014-06-19)
1297	 */
1298	protected $tsa_timestamp = false;
1299
1300	/**
1301	 * Timestamping data.
1302	 * @protected
1303	 * @since 6.0.085 (2014-06-19)
1304	 */
1305	protected $tsa_data = array();
1306
1307	/**
1308	 * Regular expression used to find blank characters (required for word-wrapping).
1309	 * @protected
1310	 * @since 4.6.006 (2009-04-28)
1311	 */
1312	protected $re_spaces = '/[^\S\xa0]/';
1313
1314	/**
1315	 * Array of $re_spaces parts.
1316	 * @protected
1317	 * @since 5.5.011 (2010-07-09)
1318	 */
1319	protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
1320
1321	/**
1322	 * Digital signature object ID.
1323	 * @protected
1324	 * @since 4.6.022 (2009-06-23)
1325	 */
1326	protected $sig_obj_id = 0;
1327
1328	/**
1329	 * ID of page objects.
1330	 * @protected
1331	 * @since 4.7.000 (2009-08-29)
1332	 */
1333	protected $page_obj_id = array();
1334
1335	/**
1336	 * List of form annotations IDs.
1337	 * @protected
1338	 * @since 4.8.000 (2009-09-07)
1339	 */
1340	protected $form_obj_id = array();
1341
1342	/**
1343	 * Deafult Javascript field properties. Possible values are described on official Javascript for Acrobat API reference. Annotation options can be directly specified using the 'aopt' entry.
1344	 * @protected
1345	 * @since 4.8.000 (2009-09-07)
1346	 */
1347	protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1348
1349	/**
1350	 * Javascript objects array.
1351	 * @protected
1352	 * @since 4.8.000 (2009-09-07)
1353	 */
1354	protected $js_objects = array();
1355
1356	/**
1357	 * Current form action (used during XHTML rendering).
1358	 * @protected
1359	 * @since 4.8.000 (2009-09-07)
1360	 */
1361	protected $form_action = '';
1362
1363	/**
1364	 * Current form encryption type (used during XHTML rendering).
1365	 * @protected
1366	 * @since 4.8.000 (2009-09-07)
1367	 */
1368	protected $form_enctype = 'application/x-www-form-urlencoded';
1369
1370	/**
1371	 * Current method to submit forms.
1372	 * @protected
1373	 * @since 4.8.000 (2009-09-07)
1374	 */
1375	protected $form_mode = 'post';
1376
1377	/**
1378	 * List of fonts used on form fields (fontname => fontkey).
1379	 * @protected
1380	 * @since 4.8.001 (2009-09-09)
1381	 */
1382	protected $annotation_fonts = array();
1383
1384	/**
1385	 * List of radio buttons parent objects.
1386	 * @protected
1387	 * @since 4.8.001 (2009-09-09)
1388	 */
1389	protected $radiobutton_groups = array();
1390
1391	/**
1392	 * List of radio group objects IDs.
1393	 * @protected
1394	 * @since 4.8.001 (2009-09-09)
1395	 */
1396	protected $radio_groups = array();
1397
1398	/**
1399	 * Text indentation value (used for text-indent CSS attribute).
1400	 * @protected
1401	 * @since 4.8.006 (2009-09-23)
1402	 */
1403	protected $textindent = 0;
1404
1405	/**
1406	 * Store page number when startTransaction() is called.
1407	 * @protected
1408	 * @since 4.8.006 (2009-09-23)
1409	 */
1410	protected $start_transaction_page = 0;
1411
1412	/**
1413	 * Store Y position when startTransaction() is called.
1414	 * @protected
1415	 * @since 4.9.001 (2010-03-28)
1416	 */
1417	protected $start_transaction_y = 0;
1418
1419	/**
1420	 * True when we are printing the thead section on a new page.
1421	 * @protected
1422	 * @since 4.8.027 (2010-01-25)
1423	 */
1424	protected $inthead = false;
1425
1426	/**
1427	 * Array of column measures (width, space, starting Y position).
1428	 * @protected
1429	 * @since 4.9.001 (2010-03-28)
1430	 */
1431	protected $columns = array();
1432
1433	/**
1434	 * Number of colums.
1435	 * @protected
1436	 * @since 4.9.001 (2010-03-28)
1437	 */
1438	protected $num_columns = 1;
1439
1440	/**
1441	 * Current column number.
1442	 * @protected
1443	 * @since 4.9.001 (2010-03-28)
1444	 */
1445	protected $current_column = 0;
1446
1447	/**
1448	 * Starting page for columns.
1449	 * @protected
1450	 * @since 4.9.001 (2010-03-28)
1451	 */
1452	protected $column_start_page = 0;
1453
1454	/**
1455	 * Maximum page and column selected.
1456	 * @protected
1457	 * @since 5.8.000 (2010-08-11)
1458	 */
1459	protected $maxselcol = array('page' => 0, 'column' => 0);
1460
1461	/**
1462	 * Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding.
1463	 * @protected
1464	 * @since 5.8.000 (2010-08-11)
1465	 */
1466	protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
1467
1468	/**
1469	 * Text rendering mode: 0 = Fill text; 1 = Stroke text; 2 = Fill, then stroke text; 3 = Neither fill nor stroke text (invisible); 4 = Fill text and add to path for clipping; 5 = Stroke text and add to path for clipping; 6 = Fill, then stroke text and add to path for clipping; 7 = Add text to path for clipping.
1470	 * @protected
1471	 * @since 4.9.008 (2010-04-03)
1472	 */
1473	protected $textrendermode = 0;
1474
1475	/**
1476	 * Text stroke width in doc units.
1477	 * @protected
1478	 * @since 4.9.008 (2010-04-03)
1479	 */
1480	protected $textstrokewidth = 0;
1481
1482	/**
1483	 * Current stroke color.
1484	 * @protected
1485	 * @since 4.9.008 (2010-04-03)
1486	 */
1487	protected $strokecolor;
1488
1489	/**
1490	 * Default unit of measure for document.
1491	 * @protected
1492	 * @since 5.0.000 (2010-04-22)
1493	 */
1494	protected $pdfunit = 'mm';
1495
1496	/**
1497	 * Boolean flag true when we are on TOC (Table Of Content) page.
1498	 * @protected
1499	 */
1500	protected $tocpage = false;
1501
1502	/**
1503	 * Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library.
1504	 * @protected
1505	 * @since 5.0.000 (2010-04-26)
1506	 */
1507	protected $rasterize_vector_images = false;
1508
1509	/**
1510	 * Boolean flag: if true enables font subsetting by default.
1511	 * @protected
1512	 * @since 5.3.002 (2010-06-07)
1513	 */
1514	protected $font_subsetting = true;
1515
1516	/**
1517	 * Array of default graphic settings.
1518	 * @protected
1519	 * @since 5.5.008 (2010-07-02)
1520	 */
1521	protected $default_graphic_vars = array();
1522
1523	/**
1524	 * Array of XObjects.
1525	 * @protected
1526	 * @since 5.8.014 (2010-08-23)
1527	 */
1528	protected $xobjects = array();
1529
1530	/**
1531	 * Boolean value true when we are inside an XObject.
1532	 * @protected
1533	 * @since 5.8.017 (2010-08-24)
1534	 */
1535	protected $inxobj = false;
1536
1537	/**
1538	 * Current XObject ID.
1539	 * @protected
1540	 * @since 5.8.017 (2010-08-24)
1541	 */
1542	protected $xobjid = '';
1543
1544	/**
1545	 * Percentage of character stretching.
1546	 * @protected
1547	 * @since 5.9.000 (2010-09-29)
1548	 */
1549	protected $font_stretching = 100;
1550
1551	/**
1552	 * Increases or decreases the space between characters in a text by the specified amount (tracking).
1553	 * @protected
1554	 * @since 5.9.000 (2010-09-29)
1555	 */
1556	protected $font_spacing = 0;
1557
1558	/**
1559	 * Array of no-write regions.
1560	 * ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right)
1561	 * @protected
1562	 * @since 5.9.003 (2010-10-14)
1563	 */
1564	protected $page_regions = array();
1565
1566	/**
1567	 * Boolean value true when page region check is active.
1568	 * @protected
1569	 */
1570	protected $check_page_regions = true;
1571
1572	/**
1573	 * Array of PDF layers data.
1574	 * @protected
1575	 * @since 5.9.102 (2011-07-13)
1576	 */
1577	protected $pdflayers = array();
1578
1579	/**
1580	 * A dictionary of names and corresponding destinations (Dests key on document Catalog).
1581	 * @protected
1582	 * @since 5.9.097 (2011-06-23)
1583	 */
1584	protected $dests = array();
1585
1586	/**
1587	 * Object ID for Named Destinations
1588	 * @protected
1589	 * @since 5.9.097 (2011-06-23)
1590	 */
1591	protected $n_dests;
1592
1593	/**
1594	 * Embedded Files Names
1595	 * @protected
1596	 * @since 5.9.204 (2013-01-23)
1597	 */
1598	protected $efnames = array();
1599
1600	/**
1601	 * Directory used for the last SVG image.
1602	 * @protected
1603	 * @since 5.0.000 (2010-05-05)
1604	 */
1605	protected $svgdir = '';
1606
1607	/**
1608	 *  Deafult unit of measure for SVG.
1609	 * @protected
1610	 * @since 5.0.000 (2010-05-02)
1611	 */
1612	protected $svgunit = 'px';
1613
1614	/**
1615	 * Array of SVG gradients.
1616	 * @protected
1617	 * @since 5.0.000 (2010-05-02)
1618	 */
1619	protected $svggradients = array();
1620
1621	/**
1622	 * ID of last SVG gradient.
1623	 * @protected
1624	 * @since 5.0.000 (2010-05-02)
1625	 */
1626	protected $svggradientid = 0;
1627
1628	/**
1629	 * Boolean value true when in SVG defs group.
1630	 * @protected
1631	 * @since 5.0.000 (2010-05-02)
1632	 */
1633	protected $svgdefsmode = false;
1634
1635	/**
1636	 * Array of SVG defs.
1637	 * @protected
1638	 * @since 5.0.000 (2010-05-02)
1639	 */
1640	protected $svgdefs = array();
1641
1642	/**
1643	 * Boolean value true when in SVG clipPath tag.
1644	 * @protected
1645	 * @since 5.0.000 (2010-04-26)
1646	 */
1647	protected $svgclipmode = false;
1648
1649	/**
1650	 * Array of SVG clipPath commands.
1651	 * @protected
1652	 * @since 5.0.000 (2010-05-02)
1653	 */
1654	protected $svgclippaths = array();
1655
1656	/**
1657	 * Array of SVG clipPath tranformation matrix.
1658	 * @protected
1659	 * @since 5.8.022 (2010-08-31)
1660	 */
1661	protected $svgcliptm = array();
1662
1663	/**
1664	 * ID of last SVG clipPath.
1665	 * @protected
1666	 * @since 5.0.000 (2010-05-02)
1667	 */
1668	protected $svgclipid = 0;
1669
1670	/**
1671	 * SVG text.
1672	 * @protected
1673	 * @since 5.0.000 (2010-05-02)
1674	 */
1675	protected $svgtext = '';
1676
1677	/**
1678	 * SVG text properties.
1679	 * @protected
1680	 * @since 5.8.013 (2010-08-23)
1681	 */
1682	protected $svgtextmode = array();
1683
1684	/**
1685	 * Array of SVG properties.
1686	 * @protected
1687	 * @since 5.0.000 (2010-05-02)
1688	 */
1689	protected $svgstyles = array(array(
1690		'alignment-baseline' => 'auto',
1691		'baseline-shift' => 'baseline',
1692		'clip' => 'auto',
1693		'clip-path' => 'none',
1694		'clip-rule' => 'nonzero',
1695		'color' => 'black',
1696		'color-interpolation' => 'sRGB',
1697		'color-interpolation-filters' => 'linearRGB',
1698		'color-profile' => 'auto',
1699		'color-rendering' => 'auto',
1700		'cursor' => 'auto',
1701		'direction' => 'ltr',
1702		'display' => 'inline',
1703		'dominant-baseline' => 'auto',
1704		'enable-background' => 'accumulate',
1705		'fill' => 'black',
1706		'fill-opacity' => 1,
1707		'fill-rule' => 'nonzero',
1708		'filter' => 'none',
1709		'flood-color' => 'black',
1710		'flood-opacity' => 1,
1711		'font' => '',
1712		'font-family' => 'helvetica',
1713		'font-size' => 'medium',
1714		'font-size-adjust' => 'none',
1715		'font-stretch' => 'normal',
1716		'font-style' => 'normal',
1717		'font-variant' => 'normal',
1718		'font-weight' => 'normal',
1719		'glyph-orientation-horizontal' => '0deg',
1720		'glyph-orientation-vertical' => 'auto',
1721		'image-rendering' => 'auto',
1722		'kerning' => 'auto',
1723		'letter-spacing' => 'normal',
1724		'lighting-color' => 'white',
1725		'marker' => '',
1726		'marker-end' => 'none',
1727		'marker-mid' => 'none',
1728		'marker-start' => 'none',
1729		'mask' => 'none',
1730		'opacity' => 1,
1731		'overflow' => 'auto',
1732		'pointer-events' => 'visiblePainted',
1733		'shape-rendering' => 'auto',
1734		'stop-color' => 'black',
1735		'stop-opacity' => 1,
1736		'stroke' => 'none',
1737		'stroke-dasharray' => 'none',
1738		'stroke-dashoffset' => 0,
1739		'stroke-linecap' => 'butt',
1740		'stroke-linejoin' => 'miter',
1741		'stroke-miterlimit' => 4,
1742		'stroke-opacity' => 1,
1743		'stroke-width' => 1,
1744		'text-anchor' => 'start',
1745		'text-decoration' => 'none',
1746		'text-rendering' => 'auto',
1747		'unicode-bidi' => 'normal',
1748		'visibility' => 'visible',
1749		'word-spacing' => 'normal',
1750		'writing-mode' => 'lr-tb',
1751		'text-color' => 'black',
1752		'transfmatrix' => array(1, 0, 0, 1, 0, 0)
1753		));
1754
1755	/**
1756	 * If true force sRGB color profile for all document.
1757	 * @protected
1758	 * @since 5.9.121 (2011-09-28)
1759	 */
1760	protected $force_srgb = false;
1761
1762	/**
1763	 * If true set the document to PDF/A mode.
1764	 * @protected
1765	 * @since 5.9.121 (2011-09-27)
1766	 */
1767	protected $pdfa_mode = false;
1768
1769	/**
1770	 * version of PDF/A mode (1 - 3).
1771	 * @protected
1772	 * @since 6.2.26 (2019-03-12)
1773	 */
1774	protected $pdfa_version = 1;
1775
1776	/**
1777	 * Document creation date-time
1778	 * @protected
1779	 * @since 5.9.152 (2012-03-22)
1780	 */
1781	protected $doc_creation_timestamp;
1782
1783	/**
1784	 * Document modification date-time
1785	 * @protected
1786	 * @since 5.9.152 (2012-03-22)
1787	 */
1788	protected $doc_modification_timestamp;
1789
1790	/**
1791	 * Custom XMP data.
1792	 * @protected
1793	 * @since 5.9.128 (2011-10-06)
1794	 */
1795	protected $custom_xmp = '';
1796
1797	/**
1798	 * Custom XMP RDF data.
1799	 * @protected
1800	 * @since 6.3.0 (2019-09-19)
1801	 */
1802	protected $custom_xmp_rdf = '';
1803
1804	/**
1805	 * Overprint mode array.
1806	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
1807	 * @protected
1808	 * @since 5.9.152 (2012-03-23)
1809	 * @var array<string,bool|int>
1810	 */
1811	protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0);
1812
1813	/**
1814	 * Alpha mode array.
1815	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
1816	 * @protected
1817	 * @since 5.9.152 (2012-03-23)
1818	 */
1819	protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false);
1820
1821	/**
1822	 * Define the page boundaries boxes to be set on document.
1823	 * @protected
1824	 * @since 5.9.152 (2012-03-23)
1825	 */
1826	protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
1827
1828	/**
1829	 * If true print TCPDF meta link.
1830	 * @protected
1831	 * @since 5.9.152 (2012-03-23)
1832	 */
1833	protected $tcpdflink = true;
1834
1835	/**
1836	 * Cache array for computed GD gamma values.
1837	 * @protected
1838	 * @since 5.9.1632 (2012-06-05)
1839	 */
1840	protected $gdgammacache = array();
1841
1842    /**
1843     * Cache array for file content
1844     * @protected
1845     * @var array
1846     * @since 6.3.5 (2020-09-28)
1847     */
1848	protected $fileContentCache = array();
1849
1850	/**
1851	 * Whether to allow local file path in image html tags, when prefixed with file://
1852	 *
1853	 * @var bool
1854	 * @protected
1855	 * @since 6.4 (2020-07-23)
1856	 */
1857	protected $allowLocalFiles = false;
1858
1859	//------------------------------------------------------------
1860	// METHODS
1861	//------------------------------------------------------------
1862
1863	/**
1864	 * This is the class constructor.
1865	 * It allows to set up the page format, the orientation and the measure unit used in all the methods (except for the font sizes).
1866	 *
1867	 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li><li>'' (empty string) for automatic orientation</li></ul>
1868	 * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.
1869	 * @param mixed $format The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
1870	 * @param boolean $unicode TRUE means that the input text is unicode (default = true)
1871	 * @param string $encoding Charset encoding (used only when converting back html entities); default is UTF-8.
1872	 * @param boolean $diskcache DEPRECATED FEATURE
1873	 * @param false|integer $pdfa If not false, set the document to PDF/A mode and the good version (1 or 3).
1874	 * @public
1875	 * @see getPageSizeFromFormat(), setPageFormat()
1876	 */
1877	public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) {
1878		// set file ID for trailer
1879		$serformat = (is_array($format) ? json_encode($format) : $format);
1880		$this->file_id = md5(TCPDF_STATIC::getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding));
1881		$this->font_obj_ids = array();
1882		$this->page_obj_id = array();
1883		$this->form_obj_id = array();
1884
1885		// set pdf/a mode
1886		if ($pdfa != false) {
1887			$this->pdfa_mode = true;
1888			$this->pdfa_version = $pdfa;  // 1 or 3
1889		} else
1890			$this->pdfa_mode = false;
1891
1892		$this->force_srgb = false;
1893		// set language direction
1894		$this->rtl = false;
1895		$this->tmprtl = false;
1896		// some checks
1897		$this->_dochecks();
1898		// initialization of properties
1899		$this->isunicode = $unicode;
1900		$this->page = 0;
1901		$this->transfmrk[0] = array();
1902		$this->pagedim = array();
1903		$this->n = 2;
1904		$this->buffer = '';
1905		$this->pages = array();
1906		$this->state = 0;
1907		$this->fonts = array();
1908		$this->FontFiles = array();
1909		$this->diffs = array();
1910		$this->images = array();
1911		$this->links = array();
1912		$this->gradients = array();
1913		$this->InFooter = false;
1914		$this->lasth = 0;
1915		$this->FontFamily = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
1916		$this->FontStyle = '';
1917		$this->FontSizePt = 12;
1918		$this->underline = false;
1919		$this->overline = false;
1920		$this->linethrough = false;
1921		$this->DrawColor = '0 G';
1922		$this->FillColor = '0 g';
1923		$this->TextColor = '0 g';
1924		$this->ColorFlag = false;
1925		$this->pdflayers = array();
1926		// encryption values
1927		$this->encrypted = false;
1928		$this->last_enc_key = '';
1929		// standard Unicode fonts
1930		$this->CoreFonts = array(
1931			'courier'=>'Courier',
1932			'courierB'=>'Courier-Bold',
1933			'courierI'=>'Courier-Oblique',
1934			'courierBI'=>'Courier-BoldOblique',
1935			'helvetica'=>'Helvetica',
1936			'helveticaB'=>'Helvetica-Bold',
1937			'helveticaI'=>'Helvetica-Oblique',
1938			'helveticaBI'=>'Helvetica-BoldOblique',
1939			'times'=>'Times-Roman',
1940			'timesB'=>'Times-Bold',
1941			'timesI'=>'Times-Italic',
1942			'timesBI'=>'Times-BoldItalic',
1943			'symbol'=>'Symbol',
1944			'zapfdingbats'=>'ZapfDingbats'
1945		);
1946		// set scale factor
1947		$this->setPageUnit($unit);
1948		// set page format and orientation
1949		$this->setPageFormat($format, $orientation);
1950		// page margins (1 cm)
1951		$margin = 28.35 / $this->k;
1952		$this->setMargins($margin, $margin);
1953		$this->clMargin = $this->lMargin;
1954		$this->crMargin = $this->rMargin;
1955		// internal cell padding
1956		$cpadding = $margin / 10;
1957		$this->setCellPaddings($cpadding, 0, $cpadding, 0);
1958		// cell margins
1959		$this->setCellMargins(0, 0, 0, 0);
1960		// line width (0.2 mm)
1961		$this->LineWidth = 0.57 / $this->k;
1962		$this->linestyleWidth = sprintf('%F w', ($this->LineWidth * $this->k));
1963		$this->linestyleCap = '0 J';
1964		$this->linestyleJoin = '0 j';
1965		$this->linestyleDash = '[] 0 d';
1966		// automatic page break
1967		$this->setAutoPageBreak(true, (2 * $margin));
1968		// full width display mode
1969		$this->setDisplayMode('fullwidth');
1970		// compression
1971		$this->setCompression();
1972		// set default PDF version number
1973		$this->setPDFVersion();
1974		$this->tcpdflink = true;
1975		$this->encoding = $encoding;
1976		$this->HREF = array();
1977		$this->getFontsList();
1978		$this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1979		$this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0);
1980		$this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1981		$this->extgstates = array();
1982		$this->setTextShadow();
1983		// signature
1984		$this->sign = false;
1985		$this->tsa_timestamp = false;
1986		$this->tsa_data = array();
1987		$this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0', 'name' => 'Signature');
1988		$this->empty_signature_appearance = array();
1989		// user's rights
1990		$this->ur['enabled'] = false;
1991		$this->ur['document'] = '/FullSave';
1992		$this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
1993		$this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1994		$this->ur['signature'] = '/Modify';
1995		$this->ur['ef'] = '/Create/Delete/Modify/Import';
1996		$this->ur['formex'] = '';
1997		// set default JPEG quality
1998		$this->jpeg_quality = 75;
1999		// initialize some settings
2000		TCPDF_FONTS::utf8Bidi(array(), '', false, $this->isunicode, $this->CurrentFont);
2001		// set default font
2002		$this->setFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
2003		$this->setHeaderFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt));
2004		$this->setFooterFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt));
2005		// check if PCRE Unicode support is enabled
2006		if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
2007			// PCRE unicode support is turned ON
2008			// \s     : any whitespace character
2009			// \p{Z}  : any separator
2010			// \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words.
2011			// \xa0   : Unicode Character 'NO-BREAK SPACE' (U+00A0)
2012			//$this->setSpacesRE('/(?!\xa0)[\s\p{Z}\p{Lo}]/u');
2013			$this->setSpacesRE('/(?!\xa0)[\s\p{Z}]/u');
2014		} else {
2015			// PCRE unicode support is turned OFF
2016			$this->setSpacesRE('/[^\S\xa0]/');
2017		}
2018		$this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
2019		// set document creation and modification timestamp
2020		$this->doc_creation_timestamp = time();
2021		$this->doc_modification_timestamp = $this->doc_creation_timestamp;
2022		// get default graphic vars
2023		$this->default_graphic_vars = $this->getGraphicVars();
2024		$this->header_xobj_autoreset = false;
2025		$this->custom_xmp = '';
2026		$this->custom_xmp_rdf = '';
2027		// Call cleanup method after script execution finishes or exit() is called.
2028		// NOTE: This will not be executed if the process is killed with a SIGTERM or SIGKILL signal.
2029		register_shutdown_function(array($this, '_destroy'), true);
2030	}
2031
2032	/**
2033	 * Default destructor.
2034	 * @public
2035	 * @since 1.53.0.TC016
2036	 */
2037	public function __destruct() {
2038		// cleanup
2039		$this->_destroy(true);
2040	}
2041
2042	/**
2043	 * Set the units of measure for the document.
2044	 * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.
2045	 * @public
2046	 * @since 3.0.015 (2008-06-06)
2047	 */
2048	public function setPageUnit($unit) {
2049		$unit = strtolower($unit);
2050		//Set scale factor
2051		switch ($unit) {
2052			// points
2053			case 'px':
2054			case 'pt': {
2055				$this->k = 1;
2056				break;
2057			}
2058			// millimeters
2059			case 'mm': {
2060				$this->k = $this->dpi / 25.4;
2061				break;
2062			}
2063			// centimeters
2064			case 'cm': {
2065				$this->k = $this->dpi / 2.54;
2066				break;
2067			}
2068			// inches
2069			case 'in': {
2070				$this->k = $this->dpi;
2071				break;
2072			}
2073			// unsupported unit
2074			default : {
2075				$this->Error('Incorrect unit: '.$unit);
2076				break;
2077			}
2078		}
2079		$this->pdfunit = $unit;
2080		if (isset($this->CurOrientation)) {
2081			$this->setPageOrientation($this->CurOrientation);
2082		}
2083	}
2084
2085	/**
2086	 * Change the format of the current page
2087	 * @param mixed $format The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() documentation or an array of two numbers (width, height) or an array containing the following measures and options:<ul>
2088	 * <li>['format'] = page format name (one of the above);</li>
2089	 * <li>['Rotate'] : The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.</li>
2090	 * <li>['PZ'] : The page's preferred zoom (magnification) factor.</li>
2091	 * <li>['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed:</li>
2092	 * <li>['MediaBox']['llx'] : lower-left x coordinate</li>
2093	 * <li>['MediaBox']['lly'] : lower-left y coordinate</li>
2094	 * <li>['MediaBox']['urx'] : upper-right x coordinate</li>
2095	 * <li>['MediaBox']['ury'] : upper-right y coordinate</li>
2096	 * <li>['CropBox'] : the visible region of default user space:</li>
2097	 * <li>['CropBox']['llx'] : lower-left x coordinate</li>
2098	 * <li>['CropBox']['lly'] : lower-left y coordinate</li>
2099	 * <li>['CropBox']['urx'] : upper-right x coordinate</li>
2100	 * <li>['CropBox']['ury'] : upper-right y coordinate</li>
2101	 * <li>['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment:</li>
2102	 * <li>['BleedBox']['llx'] : lower-left x coordinate</li>
2103	 * <li>['BleedBox']['lly'] : lower-left y coordinate</li>
2104	 * <li>['BleedBox']['urx'] : upper-right x coordinate</li>
2105	 * <li>['BleedBox']['ury'] : upper-right y coordinate</li>
2106	 * <li>['TrimBox'] : the intended dimensions of the finished page after trimming:</li>
2107	 * <li>['TrimBox']['llx'] : lower-left x coordinate</li>
2108	 * <li>['TrimBox']['lly'] : lower-left y coordinate</li>
2109	 * <li>['TrimBox']['urx'] : upper-right x coordinate</li>
2110	 * <li>['TrimBox']['ury'] : upper-right y coordinate</li>
2111	 * <li>['ArtBox'] : the extent of the page's meaningful content:</li>
2112	 * <li>['ArtBox']['llx'] : lower-left x coordinate</li>
2113	 * <li>['ArtBox']['lly'] : lower-left y coordinate</li>
2114	 * <li>['ArtBox']['urx'] : upper-right x coordinate</li>
2115	 * <li>['ArtBox']['ury'] : upper-right y coordinate</li>
2116	 * <li>['BoxColorInfo'] :specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for each of the possible page boundaries other than the MediaBox:</li>
2117	 * <li>['BoxColorInfo'][BOXTYPE]['C'] : an array of three numbers in the range 0-255, representing the components in the DeviceRGB colour space.</li>
2118	 * <li>['BoxColorInfo'][BOXTYPE]['W'] : the guideline width in default user units</li>
2119	 * <li>['BoxColorInfo'][BOXTYPE]['S'] : the guideline style: S = Solid; D = Dashed</li>
2120	 * <li>['BoxColorInfo'][BOXTYPE]['D'] : dash array defining a pattern of dashes and gaps to be used in drawing dashed guidelines</li>
2121	 * <li>['trans'] : the style and duration of the visual transition to use when moving from another page to the given page during a presentation</li>
2122	 * <li>['trans']['Dur'] : The page's display duration (also called its advance timing): the maximum length of time, in seconds, that the page shall be displayed during presentations before the viewer application shall automatically advance to the next page.</li>
2123	 * <li>['trans']['S'] : transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li>
2124	 * <li>['trans']['D'] : The duration of the transition effect, in seconds.</li>
2125	 * <li>['trans']['Dm'] : (Split and Blinds transition styles only) The dimension in which the specified transition effect shall occur: H = Horizontal, V = Vertical. Default value: H.</li>
2126	 * <li>['trans']['M'] : (Split, Box and Fly transition styles only) The direction of motion for the specified transition effect: I = Inward from the edges of the page, O = Outward from the center of the pageDefault value: I.</li>
2127	 * <li>['trans']['Di'] : (Wipe, Glitter, Fly, Cover, Uncover and Push transition styles only) The direction in which the specified transition effect shall moves, expressed in degrees counterclockwise starting from a left-to-right direction. If the value is a number, it shall be one of: 0 = Left to right, 90 = Bottom to top (Wipe only), 180 = Right to left (Wipe only), 270 = Top to bottom, 315 = Top-left to bottom-right (Glitter only). If the value is a name, it shall be None, which is relevant only for the Fly transition when the value of SS is not 1.0. Default value: 0.</li>
2128	 * <li>['trans']['SS'] : (Fly transition style only) The starting or ending scale at which the changes shall be drawn. If M specifies an inward transition, the scale of the changes drawn shall progress from SS to 1.0 over the course of the transition. If M specifies an outward transition, the scale of the changes drawn shall progress from 1.0 to SS over the course of the transition. Default: 1.0.</li>
2129	 * <li>['trans']['B'] : (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li>
2130	 * </ul>
2131	 * @param string $orientation page orientation. Possible values are (case insensitive):<ul>
2132	 * <li>P or Portrait (default)</li>
2133	 * <li>L or Landscape</li>
2134	 * <li>'' (empty string) for automatic orientation</li>
2135	 * </ul>
2136	 * @protected
2137	 * @since 3.0.015 (2008-06-06)
2138	 * @see getPageSizeFromFormat()
2139	 */
2140	protected function setPageFormat($format, $orientation='P') {
2141		if (!empty($format) AND isset($this->pagedim[$this->page])) {
2142			// remove inherited values
2143			unset($this->pagedim[$this->page]);
2144		}
2145		if (is_string($format)) {
2146			// get page measures from format name
2147			$pf = TCPDF_STATIC::getPageSizeFromFormat($format);
2148			$this->fwPt = $pf[0];
2149			$this->fhPt = $pf[1];
2150		} else {
2151			// the boundaries of the physical medium on which the page shall be displayed or printed
2152			if (isset($format['MediaBox'])) {
2153				$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', $format['MediaBox']['llx'], $format['MediaBox']['lly'], $format['MediaBox']['urx'], $format['MediaBox']['ury'], false, $this->k, $this->pagedim);
2154				$this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k);
2155				$this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k);
2156			} else {
2157				if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) {
2158					$pf = array(($format[0] * $this->k), ($format[1] * $this->k));
2159				} else {
2160					if (!isset($format['format'])) {
2161						// default value
2162						$format['format'] = 'A4';
2163					}
2164					$pf = TCPDF_STATIC::getPageSizeFromFormat($format['format']);
2165				}
2166				$this->fwPt = $pf[0];
2167				$this->fhPt = $pf[1];
2168				$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2169			}
2170			// the visible region of default user space
2171			if (isset($format['CropBox'])) {
2172				$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'CropBox', $format['CropBox']['llx'], $format['CropBox']['lly'], $format['CropBox']['urx'], $format['CropBox']['ury'], false, $this->k, $this->pagedim);
2173			}
2174			// the region to which the contents of the page shall be clipped when output in a production environment
2175			if (isset($format['BleedBox'])) {
2176				$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'BleedBox', $format['BleedBox']['llx'], $format['BleedBox']['lly'], $format['BleedBox']['urx'], $format['BleedBox']['ury'], false, $this->k, $this->pagedim);
2177			}
2178			// the intended dimensions of the finished page after trimming
2179			if (isset($format['TrimBox'])) {
2180				$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'TrimBox', $format['TrimBox']['llx'], $format['TrimBox']['lly'], $format['TrimBox']['urx'], $format['TrimBox']['ury'], false, $this->k, $this->pagedim);
2181			}
2182			// the page's meaningful content (including potential white space)
2183			if (isset($format['ArtBox'])) {
2184				$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'ArtBox', $format['ArtBox']['llx'], $format['ArtBox']['lly'], $format['ArtBox']['urx'], $format['ArtBox']['ury'], false, $this->k, $this->pagedim);
2185			}
2186			// specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
2187			if (isset($format['BoxColorInfo'])) {
2188				$this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo'];
2189			}
2190			if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) {
2191				// The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2192				$this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']);
2193			}
2194			if (isset($format['PZ'])) {
2195				// The page's preferred zoom (magnification) factor
2196				$this->pagedim[$this->page]['PZ'] = floatval($format['PZ']);
2197			}
2198			if (isset($format['trans'])) {
2199				// The style and duration of the visual transition to use when moving from another page to the given page during a presentation
2200				if (isset($format['trans']['Dur'])) {
2201					// The page's display duration
2202					$this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']);
2203				}
2204				$stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
2205				if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
2206					// The transition style that shall be used when moving to this page from another during a presentation
2207					$this->pagedim[$this->page]['trans']['S'] = $format['trans']['S'];
2208					$valid_effect = array('Split', 'Blinds');
2209					$valid_vals = array('H', 'V');
2210					if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
2211						$this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm'];
2212					}
2213					$valid_effect = array('Split', 'Box', 'Fly');
2214					$valid_vals = array('I', 'O');
2215					if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
2216						$this->pagedim[$this->page]['trans']['M'] = $format['trans']['M'];
2217					}
2218					$valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
2219					if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
2220						if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
2221							OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
2222							OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
2223							$this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']);
2224						}
2225					}
2226					if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
2227						$this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']);
2228					}
2229					if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
2230						$this->pagedim[$this->page]['trans']['B'] = 'true';
2231					}
2232				} else {
2233					$this->pagedim[$this->page]['trans']['S'] = 'R';
2234				}
2235				if (isset($format['trans']['D'])) {
2236					// The duration of the transition effect, in seconds
2237					$this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']);
2238				} else {
2239					$this->pagedim[$this->page]['trans']['D'] = 1;
2240				}
2241			}
2242		}
2243		$this->setPageOrientation($orientation);
2244	}
2245
2246	/**
2247	 * Set page orientation.
2248	 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li><li>'' (empty string) for automatic orientation</li></ul>
2249	 * @param boolean|null $autopagebreak Boolean indicating if auto-page-break mode should be on or off.
2250	 * @param float|null $bottommargin bottom margin of the page.
2251	 * @public
2252	 * @since 3.0.015 (2008-06-06)
2253	 */
2254	public function setPageOrientation($orientation, $autopagebreak=null, $bottommargin=null) {
2255		if (!isset($this->pagedim[$this->page]['MediaBox'])) {
2256			// the boundaries of the physical medium on which the page shall be displayed or printed
2257			$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2258		}
2259		if (!isset($this->pagedim[$this->page]['CropBox'])) {
2260			// the visible region of default user space
2261			$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'CropBox', $this->pagedim[$this->page]['MediaBox']['llx'], $this->pagedim[$this->page]['MediaBox']['lly'], $this->pagedim[$this->page]['MediaBox']['urx'], $this->pagedim[$this->page]['MediaBox']['ury'], true, $this->k, $this->pagedim);
2262		}
2263		if (!isset($this->pagedim[$this->page]['BleedBox'])) {
2264			// the region to which the contents of the page shall be clipped when output in a production environment
2265			$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'BleedBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2266		}
2267		if (!isset($this->pagedim[$this->page]['TrimBox'])) {
2268			// the intended dimensions of the finished page after trimming
2269			$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'TrimBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2270		}
2271		if (!isset($this->pagedim[$this->page]['ArtBox'])) {
2272			// the page's meaningful content (including potential white space)
2273			$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'ArtBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2274		}
2275		if (!isset($this->pagedim[$this->page]['Rotate'])) {
2276			// The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2277			$this->pagedim[$this->page]['Rotate'] = 0;
2278		}
2279		if (!isset($this->pagedim[$this->page]['PZ'])) {
2280			// The page's preferred zoom (magnification) factor
2281			$this->pagedim[$this->page]['PZ'] = 1;
2282		}
2283		if ($this->fwPt > $this->fhPt) {
2284			// landscape
2285			$default_orientation = 'L';
2286		} else {
2287			// portrait
2288			$default_orientation = 'P';
2289		}
2290		$valid_orientations = array('P', 'L');
2291		if (empty($orientation)) {
2292			$orientation = $default_orientation;
2293		} else {
2294			$orientation = strtoupper($orientation[0]);
2295		}
2296		if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
2297			$this->CurOrientation = $orientation;
2298			$this->wPt = $this->fhPt;
2299			$this->hPt = $this->fwPt;
2300		} else {
2301			$this->CurOrientation = $default_orientation;
2302			$this->wPt = $this->fwPt;
2303			$this->hPt = $this->fhPt;
2304		}
2305		if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
2306			// swap X and Y coordinates (change page orientation)
2307			$this->pagedim = TCPDF_STATIC::swapPageBoxCoordinates($this->page, $this->pagedim);
2308		}
2309		$this->w = ($this->wPt / $this->k);
2310		$this->h = ($this->hPt / $this->k);
2311		if (TCPDF_STATIC::empty_string($autopagebreak)) {
2312			if (isset($this->AutoPageBreak)) {
2313				$autopagebreak = $this->AutoPageBreak;
2314			} else {
2315				$autopagebreak = true;
2316			}
2317		}
2318		if (TCPDF_STATIC::empty_string($bottommargin)) {
2319			if (isset($this->bMargin)) {
2320				$bottommargin = $this->bMargin;
2321			} else {
2322				// default value = 2 cm
2323				$bottommargin = 2 * 28.35 / $this->k;
2324			}
2325		}
2326		$this->setAutoPageBreak($autopagebreak, $bottommargin);
2327		// store page dimensions
2328		$this->pagedim[$this->page]['w'] = $this->wPt;
2329		$this->pagedim[$this->page]['h'] = $this->hPt;
2330		$this->pagedim[$this->page]['wk'] = $this->w;
2331		$this->pagedim[$this->page]['hk'] = $this->h;
2332		$this->pagedim[$this->page]['tm'] = $this->tMargin;
2333		$this->pagedim[$this->page]['bm'] = $bottommargin;
2334		$this->pagedim[$this->page]['lm'] = $this->lMargin;
2335		$this->pagedim[$this->page]['rm'] = $this->rMargin;
2336		$this->pagedim[$this->page]['pb'] = $autopagebreak;
2337		$this->pagedim[$this->page]['or'] = $this->CurOrientation;
2338		$this->pagedim[$this->page]['olm'] = $this->original_lMargin;
2339		$this->pagedim[$this->page]['orm'] = $this->original_rMargin;
2340	}
2341
2342	/**
2343	 * Set regular expression to detect withespaces or word separators.
2344	 * The pattern delimiter must be the forward-slash character "/".
2345	 * Some example patterns are:
2346	 * <pre>
2347	 * Non-Unicode or missing PCRE unicode support: "/[^\S\xa0]/"
2348	 * Unicode and PCRE unicode support: "/(?!\xa0)[\s\p{Z}]/u"
2349	 * Unicode and PCRE unicode support in Chinese mode: "/(?!\xa0)[\s\p{Z}\p{Lo}]/u"
2350	 * if PCRE unicode support is turned ON ("\P" is the negate class of "\p"):
2351	 *      \s     : any whitespace character
2352	 *      \p{Z}  : any separator
2353	 *      \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words.
2354	 *      \xa0   : Unicode Character 'NO-BREAK SPACE' (U+00A0)
2355	 * </pre>
2356	 * @param string $re regular expression (leave empty for default).
2357	 * @public
2358	 * @since 4.6.016 (2009-06-15)
2359	 */
2360	public function setSpacesRE($re='/[^\S\xa0]/') {
2361		$this->re_spaces = $re;
2362		$re_parts = explode('/', $re);
2363		// get pattern parts
2364		$this->re_space = array();
2365		if (isset($re_parts[1]) AND !empty($re_parts[1])) {
2366			$this->re_space['p'] = $re_parts[1];
2367		} else {
2368			$this->re_space['p'] = '[\s]';
2369		}
2370		// set pattern modifiers
2371		if (isset($re_parts[2]) AND !empty($re_parts[2])) {
2372			$this->re_space['m'] = $re_parts[2];
2373		} else {
2374			$this->re_space['m'] = '';
2375		}
2376	}
2377
2378	/**
2379	 * Enable or disable Right-To-Left language mode
2380	 * @param boolean $enable if true enable Right-To-Left language mode.
2381	 * @param boolean $resetx if true reset the X position on direction change.
2382	 * @public
2383	 * @since 2.0.000 (2008-01-03)
2384	 */
2385	public function setRTL($enable, $resetx=true) {
2386		$enable = $enable ? true : false;
2387		$resetx = ($resetx AND ($enable != $this->rtl));
2388		$this->rtl = $enable;
2389		$this->tmprtl = false;
2390		if ($resetx) {
2391			$this->Ln(0);
2392		}
2393	}
2394
2395	/**
2396	 * Return the RTL status
2397	 * @return bool
2398	 * @public
2399	 * @since 4.0.012 (2008-07-24)
2400	 */
2401	public function getRTL() {
2402		return $this->rtl;
2403	}
2404
2405	/**
2406	 * Force temporary RTL language direction
2407	 * @param false|string $mode can be false, 'L' for LTR or 'R' for RTL
2408	 * @public
2409	 * @since 2.1.000 (2008-01-09)
2410	 */
2411	public function setTempRTL($mode) {
2412		$newmode = false;
2413		switch (strtoupper($mode)) {
2414			case 'LTR':
2415			case 'L': {
2416				if ($this->rtl) {
2417					$newmode = 'L';
2418				}
2419				break;
2420			}
2421			case 'RTL':
2422			case 'R': {
2423				if (!$this->rtl) {
2424					$newmode = 'R';
2425				}
2426				break;
2427			}
2428			case false:
2429			default: {
2430				$newmode = false;
2431				break;
2432			}
2433		}
2434		$this->tmprtl = $newmode;
2435	}
2436
2437	/**
2438	 * Return the current temporary RTL status
2439	 * @return bool
2440	 * @public
2441	 * @since 4.8.014 (2009-11-04)
2442	 */
2443	public function isRTLTextDir() {
2444		return ($this->rtl OR ($this->tmprtl == 'R'));
2445	}
2446
2447	/**
2448	 * Set the last cell height.
2449	 * @param float $h cell height.
2450	 * @author Nicola Asuni
2451	 * @public
2452	 * @since 1.53.0.TC034
2453	 */
2454	public function setLastH($h) {
2455		$this->lasth = $h;
2456	}
2457
2458	/**
2459	 * Return the cell height
2460	 * @param int $fontsize Font size in internal units
2461	 * @param boolean $padding If true add cell padding
2462	 * @public
2463	 * @return float
2464	 */
2465	public function getCellHeight($fontsize, $padding=TRUE) {
2466		$height = ($fontsize * $this->cell_height_ratio);
2467		if ($padding) {
2468			$height += ($this->cell_padding['T'] + $this->cell_padding['B']);
2469		}
2470		return round($height, 6);
2471	}
2472
2473	/**
2474	 * Reset the last cell height.
2475	 * @public
2476	 * @since 5.9.000 (2010-10-03)
2477	 */
2478	public function resetLastH() {
2479		$this->lasth = $this->getCellHeight($this->FontSize);
2480	}
2481
2482	/**
2483	 * Get the last cell height.
2484	 * @return float last cell height
2485	 * @public
2486	 * @since 4.0.017 (2008-08-05)
2487	 */
2488	public function getLastH() {
2489		return $this->lasth;
2490	}
2491
2492	/**
2493	 * Set the adjusting factor to convert pixels to user units.
2494	 * @param float $scale adjusting factor to convert pixels to user units.
2495	 * @author Nicola Asuni
2496	 * @public
2497	 * @since 1.5.2
2498	 */
2499	public function setImageScale($scale) {
2500		$this->imgscale = $scale;
2501	}
2502
2503	/**
2504	 * Returns the adjusting factor to convert pixels to user units.
2505	 * @return float adjusting factor to convert pixels to user units.
2506	 * @author Nicola Asuni
2507	 * @public
2508	 * @since 1.5.2
2509	 */
2510	public function getImageScale() {
2511		return $this->imgscale;
2512	}
2513
2514	/**
2515	 * Returns an array of page dimensions:
2516	 * <ul><li>$this->pagedim[$this->page]['w'] = page width in points</li><li>$this->pagedim[$this->page]['h'] = height in points</li><li>$this->pagedim[$this->page]['wk'] = page width in user units</li><li>$this->pagedim[$this->page]['hk'] = page height in user units</li><li>$this->pagedim[$this->page]['tm'] = top margin</li><li>$this->pagedim[$this->page]['bm'] = bottom margin</li><li>$this->pagedim[$this->page]['lm'] = left margin</li><li>$this->pagedim[$this->page]['rm'] = right margin</li><li>$this->pagedim[$this->page]['pb'] = auto page break</li><li>$this->pagedim[$this->page]['or'] = page orientation</li><li>$this->pagedim[$this->page]['olm'] = original left margin</li><li>$this->pagedim[$this->page]['orm'] = original right margin</li><li>$this->pagedim[$this->page]['Rotate'] = The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.</li><li>$this->pagedim[$this->page]['PZ'] = The page's preferred zoom (magnification) factor.</li><li>$this->pagedim[$this->page]['trans'] : the style and duration of the visual transition to use when moving from another page to the given page during a presentation<ul><li>$this->pagedim[$this->page]['trans']['Dur'] = The page's display duration (also called its advance timing): the maximum length of time, in seconds, that the page shall be displayed during presentations before the viewer application shall automatically advance to the next page.</li><li>$this->pagedim[$this->page]['trans']['S'] = transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li><li>$this->pagedim[$this->page]['trans']['D'] = The duration of the transition effect, in seconds.</li><li>$this->pagedim[$this->page]['trans']['Dm'] = (Split and Blinds transition styles only) The dimension in which the specified transition effect shall occur: H = Horizontal, V = Vertical. Default value: H.</li><li>$this->pagedim[$this->page]['trans']['M'] = (Split, Box and Fly transition styles only) The direction of motion for the specified transition effect: I = Inward from the edges of the page, O = Outward from the center of the pageDefault value: I.</li><li>$this->pagedim[$this->page]['trans']['Di'] = (Wipe, Glitter, Fly, Cover, Uncover and Push transition styles only) The direction in which the specified transition effect shall moves, expressed in degrees counterclockwise starting from a left-to-right direction. If the value is a number, it shall be one of: 0 = Left to right, 90 = Bottom to top (Wipe only), 180 = Right to left (Wipe only), 270 = Top to bottom, 315 = Top-left to bottom-right (Glitter only). If the value is a name, it shall be None, which is relevant only for the Fly transition when the value of SS is not 1.0. Default value: 0.</li><li>$this->pagedim[$this->page]['trans']['SS'] = (Fly transition style only) The starting or ending scale at which the changes shall be drawn. If M specifies an inward transition, the scale of the changes drawn shall progress from SS to 1.0 over the course of the transition. If M specifies an outward transition, the scale of the changes drawn shall progress from 1.0 to SS over the course of the transition. Default: 1.0. </li><li>$this->pagedim[$this->page]['trans']['B'] = (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li></ul></li><li>$this->pagedim[$this->page]['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed<ul><li>$this->pagedim[$this->page]['MediaBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['CropBox'] : the visible region of default user space<ul><li>$this->pagedim[$this->page]['CropBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment<ul><li>$this->pagedim[$this->page]['BleedBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['TrimBox'] : the intended dimensions of the finished page after trimming<ul><li>$this->pagedim[$this->page]['TrimBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['ArtBox'] : the extent of the page's meaningful content<ul><li>$this->pagedim[$this->page]['ArtBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['ury'] = upper-right y coordinate in points</li></ul></li></ul>
2517	 * @param int|null $pagenum page number (empty = current page)
2518	 * @return array of page dimensions.
2519	 * @author Nicola Asuni
2520	 * @public
2521	 * @since 4.5.027 (2009-03-16)
2522	 */
2523	public function getPageDimensions($pagenum=null) {
2524		if (empty($pagenum)) {
2525			$pagenum = $this->page;
2526		}
2527		return $this->pagedim[$pagenum];
2528	}
2529
2530	/**
2531	 * Returns the page width in units.
2532	 * @param int|null $pagenum page number (empty = current page)
2533	 * @return int page width.
2534	 * @author Nicola Asuni
2535	 * @public
2536	 * @since 1.5.2
2537	 * @see getPageDimensions()
2538	 */
2539	public function getPageWidth($pagenum=null) {
2540		if (empty($pagenum)) {
2541			return $this->w;
2542		}
2543		return $this->pagedim[$pagenum]['w'];
2544	}
2545
2546	/**
2547	 * Returns the page height in units.
2548	 * @param int|null $pagenum page number (empty = current page)
2549	 * @return int page height.
2550	 * @author Nicola Asuni
2551	 * @public
2552	 * @since 1.5.2
2553	 * @see getPageDimensions()
2554	 */
2555	public function getPageHeight($pagenum=null) {
2556		if (empty($pagenum)) {
2557			return $this->h;
2558		}
2559		return $this->pagedim[$pagenum]['h'];
2560	}
2561
2562	/**
2563	 * Returns the page break margin.
2564	 * @param int|null $pagenum page number (empty = current page)
2565	 * @return int page break margin.
2566	 * @author Nicola Asuni
2567	 * @public
2568	 * @since 1.5.2
2569	 * @see getPageDimensions()
2570	 */
2571	public function getBreakMargin($pagenum=null) {
2572		if (empty($pagenum)) {
2573			return $this->bMargin;
2574		}
2575		return $this->pagedim[$pagenum]['bm'];
2576	}
2577
2578	/**
2579	 * Returns the scale factor (number of points in user unit).
2580	 * @return int scale factor.
2581	 * @author Nicola Asuni
2582	 * @public
2583	 * @since 1.5.2
2584	 */
2585	public function getScaleFactor() {
2586		return $this->k;
2587	}
2588
2589	/**
2590	 * Defines the left, top and right margins.
2591	 * @param float $left Left margin.
2592	 * @param float $top Top margin.
2593	 * @param float $right Right margin. Default value is the left one.
2594	 * @param boolean $keepmargins if true overwrites the default page margins
2595	 * @public
2596	 * @since 1.0
2597	 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
2598	 */
2599	public function setMargins($left, $top, $right=null, $keepmargins=false) {
2600		//Set left, top and right margins
2601		$this->lMargin = $left;
2602		$this->tMargin = $top;
2603		if ($right == -1 OR $right === null) {
2604			$right = $left;
2605		}
2606		$this->rMargin = $right;
2607		if ($keepmargins) {
2608			// overwrite original values
2609			$this->original_lMargin = $this->lMargin;
2610			$this->original_rMargin = $this->rMargin;
2611		}
2612	}
2613
2614	/**
2615	 * Defines the left margin. The method can be called before creating the first page. If the current abscissa gets out of page, it is brought back to the margin.
2616	 * @param float $margin The margin.
2617	 * @public
2618	 * @since 1.4
2619	 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
2620	 */
2621	public function setLeftMargin($margin) {
2622		//Set left margin
2623		$this->lMargin = $margin;
2624		if (($this->page > 0) AND ($this->x < $margin)) {
2625			$this->x = $margin;
2626		}
2627	}
2628
2629	/**
2630	 * Defines the top margin. The method can be called before creating the first page.
2631	 * @param float $margin The margin.
2632	 * @public
2633	 * @since 1.5
2634	 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
2635	 */
2636	public function setTopMargin($margin) {
2637		//Set top margin
2638		$this->tMargin = $margin;
2639		if (($this->page > 0) AND ($this->y < $margin)) {
2640			$this->y = $margin;
2641		}
2642	}
2643
2644	/**
2645	 * Defines the right margin. The method can be called before creating the first page.
2646	 * @param float $margin The margin.
2647	 * @public
2648	 * @since 1.5
2649	 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
2650	 */
2651	public function setRightMargin($margin) {
2652		$this->rMargin = $margin;
2653		if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
2654			$this->x = $this->w - $margin;
2655		}
2656	}
2657
2658	/**
2659	 * Set the same internal Cell padding for top, right, bottom, left-
2660	 * @param float $pad internal padding.
2661	 * @public
2662	 * @since 2.1.000 (2008-01-09)
2663	 * @see getCellPaddings(), setCellPaddings()
2664	 */
2665	public function setCellPadding($pad) {
2666		if ($pad >= 0) {
2667			$this->cell_padding['L'] = $pad;
2668			$this->cell_padding['T'] = $pad;
2669			$this->cell_padding['R'] = $pad;
2670			$this->cell_padding['B'] = $pad;
2671		}
2672	}
2673
2674	/**
2675	 * Set the internal Cell paddings.
2676	 * @param float|null $left left padding
2677	 * @param float|null $top top padding
2678	 * @param float|null $right right padding
2679	 * @param float|null $bottom bottom padding
2680	 * @public
2681	 * @since 5.9.000 (2010-10-03)
2682	 * @see getCellPaddings(), SetCellPadding()
2683	 */
2684	public function setCellPaddings($left=null, $top=null, $right=null, $bottom=null) {
2685		if (!TCPDF_STATIC::empty_string($left) AND ($left >= 0)) {
2686			$this->cell_padding['L'] = $left;
2687		}
2688		if (!TCPDF_STATIC::empty_string($top) AND ($top >= 0)) {
2689			$this->cell_padding['T'] = $top;
2690		}
2691		if (!TCPDF_STATIC::empty_string($right) AND ($right >= 0)) {
2692			$this->cell_padding['R'] = $right;
2693		}
2694		if (!TCPDF_STATIC::empty_string($bottom) AND ($bottom >= 0)) {
2695			$this->cell_padding['B'] = $bottom;
2696		}
2697	}
2698
2699	/**
2700	 * Get the internal Cell padding array.
2701	 * @return array of padding values
2702	 * @public
2703	 * @since 5.9.000 (2010-10-03)
2704	 * @see setCellPaddings(), SetCellPadding()
2705	 */
2706	public function getCellPaddings() {
2707		return $this->cell_padding;
2708	}
2709
2710	/**
2711	 * Set the internal Cell margins.
2712	 * @param float|null $left left margin
2713	 * @param float|null $top top margin
2714	 * @param float|null $right right margin
2715	 * @param float|null $bottom bottom margin
2716	 * @public
2717	 * @since 5.9.000 (2010-10-03)
2718	 * @see getCellMargins()
2719	 */
2720	public function setCellMargins($left=null, $top=null, $right=null, $bottom=null) {
2721		if (!TCPDF_STATIC::empty_string($left) AND ($left >= 0)) {
2722			$this->cell_margin['L'] = $left;
2723		}
2724		if (!TCPDF_STATIC::empty_string($top) AND ($top >= 0)) {
2725			$this->cell_margin['T'] = $top;
2726		}
2727		if (!TCPDF_STATIC::empty_string($right) AND ($right >= 0)) {
2728			$this->cell_margin['R'] = $right;
2729		}
2730		if (!TCPDF_STATIC::empty_string($bottom) AND ($bottom >= 0)) {
2731			$this->cell_margin['B'] = $bottom;
2732		}
2733	}
2734
2735	/**
2736	 * Get the internal Cell margin array.
2737	 * @return array of margin values
2738	 * @public
2739	 * @since 5.9.000 (2010-10-03)
2740	 * @see setCellMargins()
2741	 */
2742	public function getCellMargins() {
2743		return $this->cell_margin;
2744	}
2745
2746	/**
2747	 * Adjust the internal Cell padding array to take account of the line width.
2748	 * @param string|array|int $brd Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
2749	 * @return void|array array of adjustments
2750	 * @public
2751	 * @since 5.9.000 (2010-10-03)
2752	 */
2753	protected function adjustCellPadding($brd=0) {
2754		if (empty($brd)) {
2755			return;
2756		}
2757		if (is_string($brd)) {
2758			// convert string to array
2759			$slen = strlen($brd);
2760			$newbrd = array();
2761			for ($i = 0; $i < $slen; ++$i) {
2762				$newbrd[$brd[$i]] = true;
2763			}
2764			$brd = $newbrd;
2765		} elseif (
2766			($brd === 1)
2767			|| ($brd === true)
2768			|| (is_numeric($brd) && ((int)$brd > 0))
2769		) {
2770			$brd = array('LRTB' => true);
2771		}
2772		if (!is_array($brd)) {
2773			return;
2774		}
2775		// store current cell padding
2776		$cp = $this->cell_padding;
2777		// select border mode
2778		if (isset($brd['mode'])) {
2779			$mode = $brd['mode'];
2780			unset($brd['mode']);
2781		} else {
2782			$mode = 'normal';
2783		}
2784		// process borders
2785		foreach ($brd as $border => $style) {
2786			$line_width = $this->LineWidth;
2787			if (is_array($style) && isset($style['width'])) {
2788				// get border width
2789				$line_width = $style['width'];
2790			}
2791			$adj = 0; // line width inside the cell
2792			switch ($mode) {
2793				case 'ext': {
2794					$adj = 0;
2795					break;
2796				}
2797				case 'int': {
2798					$adj = $line_width;
2799					break;
2800				}
2801				case 'normal':
2802				default: {
2803					$adj = ($line_width / 2);
2804					break;
2805				}
2806			}
2807			// correct internal cell padding if required to avoid overlap between text and lines
2808			if (
2809				is_numeric($this->cell_padding['T'])
2810				&& ($this->cell_padding['T'] < $adj)
2811				&& (strpos($border, 'T') !== false)
2812			) {
2813				$this->cell_padding['T'] = $adj;
2814			}
2815			if (
2816				is_numeric($this->cell_padding['R'])
2817				&& ($this->cell_padding['R'] < $adj)
2818				&& (strpos($border, 'R') !== false)
2819			) {
2820				$this->cell_padding['R'] = $adj;
2821			}
2822			if (
2823				is_numeric($this->cell_padding['B'])
2824				&& ($this->cell_padding['B'] < $adj)
2825				&& (strpos($border, 'B') !== false)
2826			) {
2827				$this->cell_padding['B'] = $adj;
2828			}
2829			if (
2830				is_numeric($this->cell_padding['L'])
2831				&& ($this->cell_padding['L'] < $adj)
2832				&& (strpos($border, 'L') !== false)
2833			) {
2834				$this->cell_padding['L'] = $adj;
2835			}
2836
2837		}
2838
2839		return array(
2840			'T' => ($this->cell_padding['T'] - $cp['T']),
2841			'R' => ($this->cell_padding['R'] - $cp['R']),
2842			'B' => ($this->cell_padding['B'] - $cp['B']),
2843			'L' => ($this->cell_padding['L'] - $cp['L']),
2844		);
2845	}
2846
2847	/**
2848	 * Enables or disables the automatic page breaking mode. When enabling, the second parameter is the distance from the bottom of the page that defines the triggering limit. By default, the mode is on and the margin is 2 cm.
2849	 * @param boolean $auto Boolean indicating if mode should be on or off.
2850	 * @param float $margin Distance from the bottom of the page.
2851	 * @public
2852	 * @since 1.0
2853	 * @see Cell(), MultiCell(), AcceptPageBreak()
2854	 */
2855	public function setAutoPageBreak($auto, $margin=0) {
2856		$this->AutoPageBreak = $auto ? true : false;
2857		$this->bMargin = $margin;
2858		$this->PageBreakTrigger = $this->h - $margin;
2859	}
2860
2861	/**
2862	 * Return the auto-page-break mode (true or false).
2863	 * @return bool auto-page-break mode
2864	 * @public
2865	 * @since 5.9.088
2866	 */
2867	public function getAutoPageBreak() {
2868		return $this->AutoPageBreak;
2869	}
2870
2871	/**
2872	 * Defines the way the document is to be displayed by the viewer.
2873	 * @param mixed $zoom The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. <ul><li>fullpage: displays the entire page on screen </li><li>fullwidth: uses maximum width of window</li><li>real: uses real size (equivalent to 100% zoom)</li><li>default: uses viewer default mode</li></ul>
2874	 * @param string $layout The page layout. Possible values are:<ul><li>SinglePage Display one page at a time</li><li>OneColumn Display the pages in one column</li><li>TwoColumnLeft Display the pages in two columns, with odd-numbered pages on the left</li><li>TwoColumnRight Display the pages in two columns, with odd-numbered pages on the right</li><li>TwoPageLeft (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the left</li><li>TwoPageRight (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the right</li></ul>
2875	 * @param string $mode A name object specifying how the document should be displayed when opened:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>FullScreen Full-screen mode, with no menu bar, window controls, or any other window visible</li><li>UseOC (PDF 1.5) Optional content group panel visible</li><li>UseAttachments (PDF 1.6) Attachments panel visible</li></ul>
2876	 * @public
2877	 * @since 1.2
2878	 */
2879	public function setDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
2880		if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
2881			$this->ZoomMode = $zoom;
2882		} else {
2883			$this->Error('Incorrect zoom display mode: '.$zoom);
2884		}
2885		$this->LayoutMode = TCPDF_STATIC::getPageLayoutMode($layout);
2886		$this->PageMode = TCPDF_STATIC::getPageMode($mode);
2887	}
2888
2889	/**
2890	 * Activates or deactivates page compression. When activated, the internal representation of each page is compressed, which leads to a compression ratio of about 2 for the resulting document. Compression is on by default.
2891	 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
2892	 * @param boolean $compress Boolean indicating if compression must be enabled.
2893	 * @public
2894	 * @since 1.4
2895	 */
2896	public function setCompression($compress=true) {
2897		$this->compress = false;
2898		if (function_exists('gzcompress')) {
2899			if ($compress) {
2900				if ( !$this->pdfa_mode) {
2901					$this->compress = true;
2902				}
2903			}
2904		}
2905	}
2906
2907	/**
2908	 * Set flag to force sRGB_IEC61966-2.1 black scaled ICC color profile for the whole document.
2909	 * @param boolean $mode If true force sRGB output intent.
2910	 * @public
2911	 * @since 5.9.121 (2011-09-28)
2912	 */
2913	public function setSRGBmode($mode=false) {
2914		$this->force_srgb = $mode ? true : false;
2915	}
2916
2917	/**
2918	 * Turn on/off Unicode mode for document information dictionary (meta tags).
2919	 * This has effect only when unicode mode is set to false.
2920	 * @param boolean $unicode if true set the meta information in Unicode
2921	 * @since 5.9.027 (2010-12-01)
2922	 * @public
2923	 */
2924	public function setDocInfoUnicode($unicode=true) {
2925		$this->docinfounicode = $unicode ? true : false;
2926	}
2927
2928	/**
2929	 * Defines the title of the document.
2930	 * @param string $title The title.
2931	 * @public
2932	 * @since 1.2
2933	 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
2934	 */
2935	public function setTitle($title) {
2936		$this->title = $title;
2937	}
2938
2939	/**
2940	 * Defines the subject of the document.
2941	 * @param string $subject The subject.
2942	 * @public
2943	 * @since 1.2
2944	 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
2945	 */
2946	public function setSubject($subject) {
2947		$this->subject = $subject;
2948	}
2949
2950	/**
2951	 * Defines the author of the document.
2952	 * @param string $author The name of the author.
2953	 * @public
2954	 * @since 1.2
2955	 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
2956	 */
2957	public function setAuthor($author) {
2958		$this->author = $author;
2959	}
2960
2961	/**
2962	 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
2963	 * @param string $keywords The list of keywords.
2964	 * @public
2965	 * @since 1.2
2966	 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
2967	 */
2968	public function setKeywords($keywords) {
2969		$this->keywords = $keywords;
2970	}
2971
2972	/**
2973	 * Defines the creator of the document. This is typically the name of the application that generates the PDF.
2974	 * @param string $creator The name of the creator.
2975	 * @public
2976	 * @since 1.2
2977	 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
2978	 */
2979	public function setCreator($creator) {
2980		$this->creator = $creator;
2981	}
2982
2983	/**
2984	 * Whether to allow local file path in image html tags, when prefixed with file://
2985	 *
2986	 * @param bool $allowLocalFiles true, when local files should be allowed. Otherwise false.
2987	 * @public
2988	 * @since 6.4
2989	 */
2990	public function setAllowLocalFiles($allowLocalFiles) {
2991		$this->allowLocalFiles = (bool) $allowLocalFiles;
2992	}
2993
2994
2995	/**
2996	 * Throw an exception or print an error message and die if the K_TCPDF_PARSER_THROW_EXCEPTION_ERROR constant is set to true.
2997	 * @param string $msg The error message
2998	 * @public
2999	 * @since 1.0
3000	 */
3001	public function Error($msg) {
3002		// unset all class variables
3003		$this->_destroy(true);
3004		if (defined('K_TCPDF_THROW_EXCEPTION_ERROR') AND !K_TCPDF_THROW_EXCEPTION_ERROR) {
3005			die('<strong>TCPDF ERROR: </strong>'.$msg);
3006		} else {
3007			throw new Exception('TCPDF ERROR: '.$msg);
3008		}
3009	}
3010
3011	/**
3012	 * This method begins the generation of the PDF document.
3013	 * It is not necessary to call it explicitly because AddPage() does it automatically.
3014	 * Note: no page is created by this method
3015	 * @public
3016	 * @since 1.0
3017	 * @see AddPage(), Close()
3018	 */
3019	public function Open() {
3020		$this->state = 1;
3021	}
3022
3023	/**
3024	 * Terminates the PDF document.
3025	 * It is not necessary to call this method explicitly because Output() does it automatically.
3026	 * If the document contains no page, AddPage() is called to prevent from getting an invalid document.
3027	 * @public
3028	 * @since 1.0
3029	 * @see Open(), Output()
3030	 */
3031	public function Close() {
3032		if ($this->state == 3) {
3033			return;
3034		}
3035		if ($this->page == 0) {
3036			$this->AddPage();
3037		}
3038		$this->endLayer();
3039		if ($this->tcpdflink) {
3040			// save current graphic settings
3041			$gvars = $this->getGraphicVars();
3042			$this->setEqualColumns();
3043			$this->lastpage(true);
3044			$this->setAutoPageBreak(false);
3045			$this->x = 0;
3046			$this->y = $this->h - (1 / $this->k);
3047			$this->lMargin = 0;
3048			$this->_outSaveGraphicsState();
3049			$font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
3050			$this->setFont($font, '', 1);
3051			$this->setTextRenderingMode(0, false, false);
3052			$msg = "\x50\x6f\x77\x65\x72\x65\x64\x20\x62\x79\x20\x54\x43\x50\x44\x46\x20\x28\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29";
3053			$lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
3054			$this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
3055			$this->_outRestoreGraphicsState();
3056			// restore graphic settings
3057			$this->setGraphicVars($gvars);
3058		}
3059		// close page
3060		$this->endPage();
3061		// close document
3062		$this->_enddoc();
3063		// unset all class variables (except critical ones)
3064		$this->_destroy(false);
3065	}
3066
3067	/**
3068	 * Move pointer at the specified document page and update page dimensions.
3069	 * @param int $pnum page number (1 ... numpages)
3070	 * @param boolean $resetmargins if true reset left, right, top margins and Y position.
3071	 * @public
3072	 * @since 2.1.000 (2008-01-07)
3073	 * @see getPage(), lastpage(), getNumPages()
3074	 */
3075	public function setPage($pnum, $resetmargins=false) {
3076		if (($pnum == $this->page) AND ($this->state == 2)) {
3077			return;
3078		}
3079		if (($pnum > 0) AND ($pnum <= $this->numpages)) {
3080			$this->state = 2;
3081			// save current graphic settings
3082			//$gvars = $this->getGraphicVars();
3083			$oldpage = $this->page;
3084			$this->page = $pnum;
3085			$this->wPt = $this->pagedim[$this->page]['w'];
3086			$this->hPt = $this->pagedim[$this->page]['h'];
3087			$this->w = $this->pagedim[$this->page]['wk'];
3088			$this->h = $this->pagedim[$this->page]['hk'];
3089			$this->tMargin = $this->pagedim[$this->page]['tm'];
3090			$this->bMargin = $this->pagedim[$this->page]['bm'];
3091			$this->original_lMargin = $this->pagedim[$this->page]['olm'];
3092			$this->original_rMargin = $this->pagedim[$this->page]['orm'];
3093			$this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
3094			$this->CurOrientation = $this->pagedim[$this->page]['or'];
3095			$this->setAutoPageBreak($this->AutoPageBreak, $this->bMargin);
3096			// restore graphic settings
3097			//$this->setGraphicVars($gvars);
3098			if ($resetmargins) {
3099				$this->lMargin = $this->pagedim[$this->page]['olm'];
3100				$this->rMargin = $this->pagedim[$this->page]['orm'];
3101				$this->setY($this->tMargin);
3102			} else {
3103				// account for booklet mode
3104				if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3105					$deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
3106					$this->lMargin += $deltam;
3107					$this->rMargin -= $deltam;
3108				}
3109			}
3110		} else {
3111			$this->Error('Wrong page number on setPage() function: '.$pnum);
3112		}
3113	}
3114
3115	/**
3116	 * Reset pointer to the last document page.
3117	 * @param boolean $resetmargins if true reset left, right, top margins and Y position.
3118	 * @public
3119	 * @since 2.0.000 (2008-01-04)
3120	 * @see setPage(), getPage(), getNumPages()
3121	 */
3122	public function lastPage($resetmargins=false) {
3123		$this->setPage($this->getNumPages(), $resetmargins);
3124	}
3125
3126	/**
3127	 * Get current document page number.
3128	 * @return int page number
3129	 * @public
3130	 * @since 2.1.000 (2008-01-07)
3131	 * @see setPage(), lastpage(), getNumPages()
3132	 */
3133	public function getPage() {
3134		return $this->page;
3135	}
3136
3137	/**
3138	 * Get the total number of insered pages.
3139	 * @return int number of pages
3140	 * @public
3141	 * @since 2.1.000 (2008-01-07)
3142	 * @see setPage(), getPage(), lastpage()
3143	 */
3144	public function getNumPages() {
3145		return $this->numpages;
3146	}
3147
3148	/**
3149	 * Adds a new TOC (Table Of Content) page to the document.
3150	 * @param string $orientation page orientation.
3151	 * @param mixed $format The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
3152	 * @param boolean $keepmargins if true overwrites the default page margins with the current margins
3153	 * @public
3154	 * @since 5.0.001 (2010-05-06)
3155	 * @see AddPage(), startPage(), endPage(), endTOCPage()
3156	 */
3157	public function addTOCPage($orientation='', $format='', $keepmargins=false) {
3158		$this->AddPage($orientation, $format, $keepmargins, true);
3159	}
3160
3161	/**
3162	 * Terminate the current TOC (Table Of Content) page
3163	 * @public
3164	 * @since 5.0.001 (2010-05-06)
3165	 * @see AddPage(), startPage(), endPage(), addTOCPage()
3166	 */
3167	public function endTOCPage() {
3168		$this->endPage(true);
3169	}
3170
3171	/**
3172	 * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer (if enabled). Then the page is added, the current position set to the top-left corner according to the left and top margins (or top-right if in RTL mode), and Header() is called to display the header (if enabled).
3173	 * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.
3174	 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3175	 * @param mixed $format The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
3176	 * @param boolean $keepmargins if true overwrites the default page margins with the current margins
3177	 * @param boolean $tocpage if true set the tocpage state to true (the added page will be used to display Table Of Content).
3178	 * @public
3179	 * @since 1.0
3180	 * @see startPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3181	 */
3182	public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
3183		if ($this->inxobj) {
3184			// we are inside an XObject template
3185			return;
3186		}
3187		if (!isset($this->original_lMargin) OR $keepmargins) {
3188			$this->original_lMargin = $this->lMargin;
3189		}
3190		if (!isset($this->original_rMargin) OR $keepmargins) {
3191			$this->original_rMargin = $this->rMargin;
3192		}
3193		// terminate previous page
3194		$this->endPage();
3195		// start new page
3196		$this->startPage($orientation, $format, $tocpage);
3197	}
3198
3199	/**
3200	 * Terminate the current page
3201	 * @param boolean $tocpage if true set the tocpage state to false (end the page used to display Table Of Content).
3202	 * @public
3203	 * @since 4.2.010 (2008-11-14)
3204	 * @see AddPage(), startPage(), addTOCPage(), endTOCPage()
3205	 */
3206	public function endPage($tocpage=false) {
3207		// check if page is already closed
3208		if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
3209			return;
3210		}
3211		// print page footer
3212		$this->setFooter();
3213		// close page
3214		$this->_endpage();
3215		// mark page as closed
3216		$this->pageopen[$this->page] = false;
3217		if ($tocpage) {
3218			$this->tocpage = false;
3219		}
3220	}
3221
3222	/**
3223	 * Starts a new page to the document. The page must be closed using the endPage() function.
3224	 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
3225	 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3226	 * @param mixed $format The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
3227	 * @param boolean $tocpage if true the page is designated to contain the Table-Of-Content.
3228	 * @since 4.2.010 (2008-11-14)
3229	 * @see AddPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3230	 * @public
3231	 */
3232	public function startPage($orientation='', $format='', $tocpage=false) {
3233		if ($tocpage) {
3234			$this->tocpage = true;
3235		}
3236		// move page numbers of documents to be attached
3237		if ($this->tocpage) {
3238			// move reference to unexistent pages (used for page attachments)
3239			// adjust outlines
3240			$tmpoutlines = $this->outlines;
3241			foreach ($tmpoutlines as $key => $outline) {
3242				if (!$outline['f'] AND ($outline['p'] > $this->numpages)) {
3243					$this->outlines[$key]['p'] = ($outline['p'] + 1);
3244				}
3245			}
3246			// adjust dests
3247			$tmpdests = $this->dests;
3248			foreach ($tmpdests as $key => $dest) {
3249				if (!$dest['f'] AND ($dest['p'] > $this->numpages)) {
3250					$this->dests[$key]['p'] = ($dest['p'] + 1);
3251				}
3252			}
3253			// adjust links
3254			$tmplinks = $this->links;
3255			foreach ($tmplinks as $key => $link) {
3256				if (!$link['f'] AND ($link['p'] > $this->numpages)) {
3257					$this->links[$key]['p'] = ($link['p'] + 1);
3258				}
3259			}
3260		}
3261		if ($this->numpages > $this->page) {
3262			// this page has been already added
3263			$this->setPage($this->page + 1);
3264			$this->setY($this->tMargin);
3265			return;
3266		}
3267		// start a new page
3268		if ($this->state == 0) {
3269			$this->Open();
3270		}
3271		++$this->numpages;
3272		$this->swapMargins($this->booklet);
3273		// save current graphic settings
3274		$gvars = $this->getGraphicVars();
3275		// start new page
3276		$this->_beginpage($orientation, $format);
3277		// mark page as open
3278		$this->pageopen[$this->page] = true;
3279		// restore graphic settings
3280		$this->setGraphicVars($gvars);
3281		// mark this point
3282		$this->setPageMark();
3283		// print page header
3284		$this->setHeader();
3285		// restore graphic settings
3286		$this->setGraphicVars($gvars);
3287		// mark this point
3288		$this->setPageMark();
3289		// print table header (if any)
3290		$this->setTableHeader();
3291		// set mark for empty page check
3292		$this->emptypagemrk[$this->page]= $this->pagelen[$this->page];
3293	}
3294
3295	/**
3296	 * Set start-writing mark on current page stream used to put borders and fills.
3297	 * Borders and fills are always created after content and inserted on the position marked by this method.
3298	 * This function must be called after calling Image() function for a background image.
3299	 * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
3300	 * @public
3301	 * @since 4.0.016 (2008-07-30)
3302	 */
3303	public function setPageMark() {
3304		$this->intmrk[$this->page] = $this->pagelen[$this->page];
3305		$this->bordermrk[$this->page] = $this->intmrk[$this->page];
3306		$this->setContentMark();
3307	}
3308
3309	/**
3310	 * Set start-writing mark on selected page.
3311	 * Borders and fills are always created after content and inserted on the position marked by this method.
3312	 * @param int $page page number (default is the current page)
3313	 * @protected
3314	 * @since 4.6.021 (2009-07-20)
3315	 */
3316	protected function setContentMark($page=0) {
3317		if ($page <= 0) {
3318			$page = $this->page;
3319		}
3320		if (isset($this->footerlen[$page])) {
3321			$this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
3322		} else {
3323			$this->cntmrk[$page] = $this->pagelen[$page];
3324		}
3325	}
3326
3327	/**
3328	 * Set header data.
3329	 * @param string $ln header image logo
3330	 * @param int $lw header image logo width in mm
3331	 * @param string $ht string to print as title on document header
3332	 * @param string $hs string to print on document header
3333	 * @param int[] $tc RGB array color for text.
3334	 * @param int[] $lc RGB array color for line.
3335	 * @public
3336	 */
3337	public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) {
3338		$this->header_logo = $ln;
3339		$this->header_logo_width = $lw;
3340		$this->header_title = $ht;
3341		$this->header_string = $hs;
3342		$this->header_text_color = $tc;
3343		$this->header_line_color = $lc;
3344	}
3345
3346	/**
3347	 * Set footer data.
3348	 * @param int[] $tc RGB array color for text.
3349	 * @param int[] $lc RGB array color for line.
3350	 * @public
3351	 */
3352	public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) {
3353		$this->footer_text_color = $tc;
3354		$this->footer_line_color = $lc;
3355	}
3356
3357	/**
3358	 * Returns header data:
3359	 * <ul><li>$ret['logo'] = logo image</li><li>$ret['logo_width'] = width of the image logo in user units</li><li>$ret['title'] = header title</li><li>$ret['string'] = header description string</li></ul>
3360	 * @return array<string,mixed>
3361	 * @public
3362	 * @since 4.0.012 (2008-07-24)
3363	 */
3364	public function getHeaderData() {
3365		$ret = array();
3366		$ret['logo'] = $this->header_logo;
3367		$ret['logo_width'] = $this->header_logo_width;
3368		$ret['title'] = $this->header_title;
3369		$ret['string'] = $this->header_string;
3370		$ret['text_color'] = $this->header_text_color;
3371		$ret['line_color'] = $this->header_line_color;
3372		return $ret;
3373	}
3374
3375	/**
3376	 * Set header margin.
3377	 * (minimum distance between header and top page margin)
3378	 * @param int $hm distance in user units
3379	 * @public
3380	 */
3381	public function setHeaderMargin($hm=10) {
3382		$this->header_margin = $hm;
3383	}
3384
3385	/**
3386	 * Returns header margin in user units.
3387	 * @return float
3388	 * @since 4.0.012 (2008-07-24)
3389	 * @public
3390	 */
3391	public function getHeaderMargin() {
3392		return $this->header_margin;
3393	}
3394
3395	/**
3396	 * Set footer margin.
3397	 * (minimum distance between footer and bottom page margin)
3398	 * @param int $fm distance in user units
3399	 * @public
3400	 */
3401	public function setFooterMargin($fm=10) {
3402		$this->footer_margin = $fm;
3403	}
3404
3405	/**
3406	 * Returns footer margin in user units.
3407	 * @return float
3408	 * @since 4.0.012 (2008-07-24)
3409	 * @public
3410	 */
3411	public function getFooterMargin() {
3412		return $this->footer_margin;
3413	}
3414	/**
3415	 * Set a flag to print page header.
3416	 * @param boolean $val set to true to print the page header (default), false otherwise.
3417	 * @public
3418	 */
3419	public function setPrintHeader($val=true) {
3420		$this->print_header = $val ? true : false;
3421	}
3422
3423	/**
3424	 * Set a flag to print page footer.
3425	 * @param boolean $val set to true to print the page footer (default), false otherwise.
3426	 * @public
3427	 */
3428	public function setPrintFooter($val=true) {
3429		$this->print_footer = $val ? true : false;
3430	}
3431
3432	/**
3433	 * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
3434	 * @return float
3435	 * @public
3436	 */
3437	public function getImageRBX() {
3438		return $this->img_rb_x;
3439	}
3440
3441	/**
3442	 * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
3443	 * @return float
3444	 * @public
3445	 */
3446	public function getImageRBY() {
3447		return $this->img_rb_y;
3448	}
3449
3450	/**
3451	 * Reset the xobject template used by Header() method.
3452	 * @public
3453	 */
3454	public function resetHeaderTemplate() {
3455		$this->header_xobjid = false;
3456	}
3457
3458	/**
3459	 * Set a flag to automatically reset the xobject template used by Header() method at each page.
3460	 * @param boolean $val set to true to reset Header xobject template at each page, false otherwise.
3461	 * @public
3462	 */
3463	public function setHeaderTemplateAutoreset($val=true) {
3464		$this->header_xobj_autoreset = $val ? true : false;
3465	}
3466
3467	/**
3468	 * This method is used to render the page header.
3469	 * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3470	 * @public
3471	 */
3472	public function Header() {
3473		if ($this->header_xobjid === false) {
3474			// start a new XObject Template
3475			$this->header_xobjid = $this->startTemplate($this->w, $this->tMargin);
3476			$headerfont = $this->getHeaderFont();
3477			$headerdata = $this->getHeaderData();
3478			$this->y = $this->header_margin;
3479			if ($this->rtl) {
3480				$this->x = $this->w - $this->original_rMargin;
3481			} else {
3482				$this->x = $this->original_lMargin;
3483			}
3484			if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
3485				$imgtype = TCPDF_IMAGES::getImageFileType(K_PATH_IMAGES.$headerdata['logo']);
3486				if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
3487					$this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3488				} elseif ($imgtype == 'svg') {
3489					$this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3490				} else {
3491					$this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3492				}
3493				$imgy = $this->getImageRBY();
3494			} else {
3495				$imgy = $this->y;
3496			}
3497			$cell_height = $this->getCellHeight($headerfont[2] / $this->k);
3498			// set starting margin for text data cell
3499			if ($this->getRTL()) {
3500				$header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1);
3501			} else {
3502				$header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1);
3503			}
3504			$cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1);
3505			$this->setTextColorArray($this->header_text_color);
3506			// header title
3507			$this->setFont($headerfont[0], 'B', $headerfont[2] + 1);
3508			$this->setX($header_x);
3509			$this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
3510			// header string
3511			$this->setFont($headerfont[0], $headerfont[1], $headerfont[2]);
3512			$this->setX($header_x);
3513			$this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false);
3514			// print an ending header line
3515			$this->setLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color']));
3516			$this->setY((2.835 / $this->k) + max($imgy, $this->y));
3517			if ($this->rtl) {
3518				$this->setX($this->original_rMargin);
3519			} else {
3520				$this->setX($this->original_lMargin);
3521			}
3522			$this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C');
3523			$this->endTemplate();
3524		}
3525		// print header template
3526		$x = 0;
3527		$dx = 0;
3528		if (!$this->header_xobj_autoreset AND $this->booklet AND (($this->page % 2) == 0)) {
3529			// adjust margins for booklet mode
3530			$dx = ($this->original_lMargin - $this->original_rMargin);
3531		}
3532		if ($this->rtl) {
3533			$x = $this->w + $dx;
3534		} else {
3535			$x = 0 + $dx;
3536		}
3537		$this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false);
3538		if ($this->header_xobj_autoreset) {
3539			// reset header xobject template at each page
3540			$this->header_xobjid = false;
3541		}
3542	}
3543
3544	/**
3545	 * This method is used to render the page footer.
3546	 * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3547	 * @public
3548	 */
3549	public function Footer() {
3550		$cur_y = $this->y;
3551		$this->setTextColorArray($this->footer_text_color);
3552		//set style for cell border
3553		$line_width = (0.85 / $this->k);
3554		$this->setLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color));
3555		//print document barcode
3556		$barcode = $this->getBarcode();
3557		if (!empty($barcode)) {
3558			$this->Ln($line_width);
3559			$barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3);
3560			$style = array(
3561				'position' => $this->rtl?'R':'L',
3562				'align' => $this->rtl?'R':'L',
3563				'stretch' => false,
3564				'fitwidth' => true,
3565				'cellfitalign' => '',
3566				'border' => false,
3567				'padding' => 0,
3568				'fgcolor' => array(0,0,0),
3569				'bgcolor' => false,
3570				'text' => false
3571			);
3572			$this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, '');
3573		}
3574		$w_page = isset($this->l['w_page']) ? $this->l['w_page'].' ' : '';
3575		if (empty($this->pagegroups)) {
3576			$pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
3577		} else {
3578			$pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
3579		}
3580		$this->setY($cur_y);
3581		//Print page number
3582		if ($this->getRTL()) {
3583			$this->setX($this->original_rMargin);
3584			$this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
3585		} else {
3586			$this->setX($this->original_lMargin);
3587			$this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R');
3588		}
3589	}
3590
3591	/**
3592	 * This method is used to render the page header.
3593	 * @protected
3594	 * @since 4.0.012 (2008-07-24)
3595	 */
3596	protected function setHeader() {
3597		if (!$this->print_header OR ($this->state != 2)) {
3598			return;
3599		}
3600		$this->InHeader = true;
3601		$this->setGraphicVars($this->default_graphic_vars);
3602		$temp_thead = $this->thead;
3603		$temp_theadMargins = $this->theadMargins;
3604		$lasth = $this->lasth;
3605		$newline = $this->newline;
3606		$this->_outSaveGraphicsState();
3607		$this->rMargin = $this->original_rMargin;
3608		$this->lMargin = $this->original_lMargin;
3609		$this->setCellPadding(0);
3610		//set current position
3611		if ($this->rtl) {
3612			$this->setXY($this->original_rMargin, $this->header_margin);
3613		} else {
3614			$this->setXY($this->original_lMargin, $this->header_margin);
3615		}
3616		$this->setFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
3617		$this->Header();
3618		//restore position
3619		if ($this->rtl) {
3620			$this->setXY($this->original_rMargin, $this->tMargin);
3621		} else {
3622			$this->setXY($this->original_lMargin, $this->tMargin);
3623		}
3624		$this->_outRestoreGraphicsState();
3625		$this->lasth = $lasth;
3626		$this->thead = $temp_thead;
3627		$this->theadMargins = $temp_theadMargins;
3628		$this->newline = $newline;
3629		$this->InHeader = false;
3630	}
3631
3632	/**
3633	 * This method is used to render the page footer.
3634	 * @protected
3635	 * @since 4.0.012 (2008-07-24)
3636	 */
3637	protected function setFooter() {
3638		if ($this->state != 2) {
3639			return;
3640		}
3641		$this->InFooter = true;
3642		// save current graphic settings
3643		$gvars = $this->getGraphicVars();
3644		// mark this point
3645		$this->footerpos[$this->page] = $this->pagelen[$this->page];
3646		$this->_out("\n");
3647		if ($this->print_footer) {
3648			$this->setGraphicVars($this->default_graphic_vars);
3649			$this->current_column = 0;
3650			$this->num_columns = 1;
3651			$temp_thead = $this->thead;
3652			$temp_theadMargins = $this->theadMargins;
3653			$lasth = $this->lasth;
3654			$this->_outSaveGraphicsState();
3655			$this->rMargin = $this->original_rMargin;
3656			$this->lMargin = $this->original_lMargin;
3657			$this->setCellPadding(0);
3658			//set current position
3659			$footer_y = $this->h - $this->footer_margin;
3660			if ($this->rtl) {
3661				$this->setXY($this->original_rMargin, $footer_y);
3662			} else {
3663				$this->setXY($this->original_lMargin, $footer_y);
3664			}
3665			$this->setFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
3666			$this->Footer();
3667			//restore position
3668			if ($this->rtl) {
3669				$this->setXY($this->original_rMargin, $this->tMargin);
3670			} else {
3671				$this->setXY($this->original_lMargin, $this->tMargin);
3672			}
3673			$this->_outRestoreGraphicsState();
3674			$this->lasth = $lasth;
3675			$this->thead = $temp_thead;
3676			$this->theadMargins = $temp_theadMargins;
3677		}
3678		// restore graphic settings
3679		$this->setGraphicVars($gvars);
3680		$this->current_column = $gvars['current_column'];
3681		$this->num_columns = $gvars['num_columns'];
3682		// calculate footer length
3683		$this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
3684		$this->InFooter = false;
3685	}
3686
3687	/**
3688	 * Check if we are on the page body (excluding page header and footer).
3689	 * @return bool true if we are not in page header nor in page footer, false otherwise.
3690	 * @protected
3691	 * @since 5.9.091 (2011-06-15)
3692	 */
3693	protected function inPageBody() {
3694		return (($this->InHeader === false) AND ($this->InFooter === false));
3695	}
3696
3697	/**
3698	 * This method is used to render the table header on new page (if any).
3699	 * @protected
3700	 * @since 4.5.030 (2009-03-25)
3701	 */
3702	protected function setTableHeader() {
3703		if ($this->num_columns > 1) {
3704			// multi column mode
3705			return;
3706		}
3707		if (isset($this->theadMargins['top'])) {
3708			// restore the original top-margin
3709			$this->tMargin = $this->theadMargins['top'];
3710			$this->pagedim[$this->page]['tm'] = $this->tMargin;
3711			$this->y = $this->tMargin;
3712		}
3713		if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
3714			// set margins
3715			$prev_lMargin = $this->lMargin;
3716			$prev_rMargin = $this->rMargin;
3717			$prev_cell_padding = $this->cell_padding;
3718			$this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']);
3719			$this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']);
3720			$this->cell_padding = $this->theadMargins['cell_padding'];
3721			if ($this->rtl) {
3722				$this->x = $this->w - $this->rMargin;
3723			} else {
3724				$this->x = $this->lMargin;
3725			}
3726			// account for special "cell" mode
3727			if ($this->theadMargins['cell']) {
3728				if ($this->rtl) {
3729					$this->x -= $this->cell_padding['R'];
3730				} else {
3731					$this->x += $this->cell_padding['L'];
3732				}
3733			}
3734			$gvars = $this->getGraphicVars();
3735			if (!empty($this->theadMargins['gvars'])) {
3736				// set the correct graphic style
3737				$this->setGraphicVars($this->theadMargins['gvars']);
3738				$this->rMargin = $gvars['rMargin'];
3739				$this->lMargin = $gvars['lMargin'];
3740			}
3741			// print table header
3742			$this->writeHTML($this->thead, false, false, false, false, '');
3743			$this->setGraphicVars($gvars);
3744			// set new top margin to skip the table headers
3745			if (!isset($this->theadMargins['top'])) {
3746				$this->theadMargins['top'] = $this->tMargin;
3747			}
3748			// store end of header position
3749			if (!isset($this->columns[0]['th'])) {
3750				$this->columns[0]['th'] = array();
3751			}
3752			$this->columns[0]['th']['\''.$this->page.'\''] = $this->y;
3753			$this->tMargin = $this->y;
3754			$this->pagedim[$this->page]['tm'] = $this->tMargin;
3755			$this->lasth = 0;
3756			$this->lMargin = $prev_lMargin;
3757			$this->rMargin = $prev_rMargin;
3758			$this->cell_padding = $prev_cell_padding;
3759		}
3760	}
3761
3762	/**
3763	 * Returns the current page number.
3764	 * @return int page number
3765	 * @public
3766	 * @since 1.0
3767	 * @see getAliasNbPages()
3768	 */
3769	public function PageNo() {
3770		return $this->page;
3771	}
3772
3773	/**
3774	 * Returns the array of spot colors.
3775	 * @return array Spot colors array.
3776	 * @public
3777	 * @since 6.0.038 (2013-09-30)
3778	 */
3779	public function getAllSpotColors() {
3780		return $this->spot_colors;
3781	}
3782
3783	/**
3784	 * Defines a new spot color.
3785	 * It can be expressed in RGB components or gray scale.
3786	 * The method can be called before the first page is created and the value is retained from page to page.
3787	 * @param string $name Full name of the spot color.
3788	 * @param float $c Cyan color for CMYK. Value between 0 and 100.
3789	 * @param float $m Magenta color for CMYK. Value between 0 and 100.
3790	 * @param float $y Yellow color for CMYK. Value between 0 and 100.
3791	 * @param float $k Key (Black) color for CMYK. Value between 0 and 100.
3792	 * @public
3793	 * @since 4.0.024 (2008-09-12)
3794	 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3795	 */
3796	public function AddSpotColor($name, $c, $m, $y, $k) {
3797		if (!isset($this->spot_colors[$name])) {
3798			$i = (1 + count($this->spot_colors));
3799			$this->spot_colors[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i);
3800		}
3801	}
3802
3803	/**
3804	 * Set the spot color for the specified type ('draw', 'fill', 'text').
3805	 * @param string $type Type of object affected by this color: ('draw', 'fill', 'text').
3806	 * @param string $name Name of the spot color.
3807	 * @param float $tint Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3808	 * @return string PDF color command.
3809	 * @public
3810	 * @since 5.9.125 (2011-10-03)
3811	 */
3812	public function setSpotColor($type, $name, $tint=100) {
3813		$spotcolor = TCPDF_COLORS::getSpotColor($name, $this->spot_colors);
3814		if ($spotcolor === false) {
3815			$this->Error('Undefined spot color: '.$name.', you must add it using the AddSpotColor() method.');
3816		}
3817		$tint = (max(0, min(100, $tint)) / 100);
3818		$pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']);
3819		switch ($type) {
3820			case 'draw': {
3821				$pdfcolor .= sprintf('CS %F SCN', $tint);
3822				$this->DrawColor = $pdfcolor;
3823				$this->strokecolor = $spotcolor;
3824				break;
3825			}
3826			case 'fill': {
3827				$pdfcolor .= sprintf('cs %F scn', $tint);
3828				$this->FillColor = $pdfcolor;
3829				$this->bgcolor = $spotcolor;
3830				break;
3831			}
3832			case 'text': {
3833				$pdfcolor .= sprintf('cs %F scn', $tint);
3834				$this->TextColor = $pdfcolor;
3835				$this->fgcolor = $spotcolor;
3836				break;
3837			}
3838		}
3839		$this->ColorFlag = ($this->FillColor != $this->TextColor);
3840		if ($this->state == 2) {
3841			$this->_out($pdfcolor);
3842		}
3843		if ($this->inxobj) {
3844			// we are inside an XObject template
3845			$this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name];
3846		}
3847		return $pdfcolor;
3848	}
3849
3850	/**
3851	 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
3852	 * @param string $name Name of the spot color.
3853	 * @param float $tint Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3854	 * @public
3855	 * @since 4.0.024 (2008-09-12)
3856	 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3857	 */
3858	public function setDrawSpotColor($name, $tint=100) {
3859		$this->setSpotColor('draw', $name, $tint);
3860	}
3861
3862	/**
3863	 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
3864	 * @param string $name Name of the spot color.
3865	 * @param float $tint Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3866	 * @public
3867	 * @since 4.0.024 (2008-09-12)
3868	 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
3869	 */
3870	public function setFillSpotColor($name, $tint=100) {
3871		$this->setSpotColor('fill', $name, $tint);
3872	}
3873
3874	/**
3875	 * Defines the spot color used for text.
3876	 * @param string $name Name of the spot color.
3877	 * @param int $tint Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3878	 * @public
3879	 * @since 4.0.024 (2008-09-12)
3880	 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
3881	 */
3882	public function setTextSpotColor($name, $tint=100) {
3883		$this->setSpotColor('text', $name, $tint);
3884	}
3885
3886	/**
3887	 * Set the color array for the specified type ('draw', 'fill', 'text').
3888	 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3889	 * The method can be called before the first page is created and the value is retained from page to page.
3890	 * @param string $type Type of object affected by this color: ('draw', 'fill', 'text').
3891	 * @param array $color Array of colors (1=gray, 3=RGB, 4=CMYK or 5=spotcolor=CMYK+name values).
3892	 * @param boolean $ret If true do not send the PDF command.
3893	 * @return string The PDF command or empty string.
3894	 * @public
3895	 * @since 3.1.000 (2008-06-11)
3896	 */
3897	public function setColorArray($type, $color, $ret=false) {
3898		if (is_array($color)) {
3899			$color = array_values($color);
3900			// component: grey, RGB red or CMYK cyan
3901			$c = isset($color[0]) ? $color[0] : -1;
3902			// component: RGB green or CMYK magenta
3903			$m = isset($color[1]) ? $color[1] : -1;
3904			// component: RGB blue or CMYK yellow
3905			$y = isset($color[2]) ? $color[2] : -1;
3906			// component: CMYK black
3907			$k = isset($color[3]) ? $color[3] : -1;
3908			// color name
3909			$name = isset($color[4]) ? $color[4] : '';
3910			if ($c >= 0) {
3911				return $this->setColor($type, $c, $m, $y, $k, $ret, $name);
3912			}
3913		}
3914		return '';
3915	}
3916
3917	/**
3918	 * Defines the color used for all drawing operations (lines, rectangles and cell borders).
3919	 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3920	 * The method can be called before the first page is created and the value is retained from page to page.
3921	 * @param array $color Array of colors (1, 3 or 4 values).
3922	 * @param boolean $ret If true do not send the PDF command.
3923	 * @return string the PDF command
3924	 * @public
3925	 * @since 3.1.000 (2008-06-11)
3926	 * @see SetDrawColor()
3927	 */
3928	public function setDrawColorArray($color, $ret=false) {
3929		return $this->setColorArray('draw', $color, $ret);
3930	}
3931
3932	/**
3933	 * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
3934	 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3935	 * The method can be called before the first page is created and the value is retained from page to page.
3936	 * @param array $color Array of colors (1, 3 or 4 values).
3937	 * @param boolean $ret If true do not send the PDF command.
3938	 * @public
3939	 * @since 3.1.000 (2008-6-11)
3940	 * @see SetFillColor()
3941	 */
3942	public function setFillColorArray($color, $ret=false) {
3943		return $this->setColorArray('fill', $color, $ret);
3944	}
3945
3946	/**
3947	 * Defines the color used for text. It can be expressed in RGB components or gray scale.
3948	 * The method can be called before the first page is created and the value is retained from page to page.
3949	 * @param array $color Array of colors (1, 3 or 4 values).
3950	 * @param boolean $ret If true do not send the PDF command.
3951	 * @public
3952	 * @since 3.1.000 (2008-6-11)
3953	 * @see SetFillColor()
3954	 */
3955	public function setTextColorArray($color, $ret=false) {
3956		return $this->setColorArray('text', $color, $ret);
3957	}
3958
3959	/**
3960	 * Defines the color used by the specified type ('draw', 'fill', 'text').
3961	 * @param string $type Type of object affected by this color: ('draw', 'fill', 'text').
3962	 * @param float $col1 GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3963	 * @param float $col2 GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3964	 * @param float $col3 BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3965	 * @param float $col4 KEY (BLACK) color for CMYK (0-100).
3966	 * @param boolean $ret If true do not send the command.
3967	 * @param string $name spot color name (if any)
3968	 * @return string The PDF command or empty string.
3969	 * @public
3970	 * @since 5.9.125 (2011-10-03)
3971	 */
3972	public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3973		// set default values
3974		if (!is_numeric($col1)) {
3975			$col1 = 0;
3976		}
3977		if (!is_numeric($col2)) {
3978			$col2 = -1;
3979		}
3980		if (!is_numeric($col3)) {
3981			$col3 = -1;
3982		}
3983		if (!is_numeric($col4)) {
3984			$col4 = -1;
3985		}
3986		// set color by case
3987		$suffix = '';
3988		if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
3989			// Grey scale
3990			$col1 = max(0, min(255, $col1));
3991			$intcolor = array('G' => $col1);
3992			$pdfcolor = sprintf('%F ', ($col1 / 255));
3993			$suffix = 'g';
3994		} elseif ($col4 == -1) {
3995			// RGB
3996			$col1 = max(0, min(255, $col1));
3997			$col2 = max(0, min(255, $col2));
3998			$col3 = max(0, min(255, $col3));
3999			$intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
4000			$pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255));
4001			$suffix = 'rg';
4002		} else {
4003			$col1 = max(0, min(100, $col1));
4004			$col2 = max(0, min(100, $col2));
4005			$col3 = max(0, min(100, $col3));
4006			$col4 = max(0, min(100, $col4));
4007			if (empty($name)) {
4008				// CMYK
4009				$intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
4010				$pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
4011				$suffix = 'k';
4012			} else {
4013				// SPOT COLOR
4014				$intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
4015				$this->AddSpotColor($name, $col1, $col2, $col3, $col4);
4016				$pdfcolor = $this->setSpotColor($type, $name, 100);
4017			}
4018		}
4019		switch ($type) {
4020			case 'draw': {
4021				$pdfcolor .= strtoupper($suffix);
4022				$this->DrawColor = $pdfcolor;
4023				$this->strokecolor = $intcolor;
4024				break;
4025			}
4026			case 'fill': {
4027				$pdfcolor .= $suffix;
4028				$this->FillColor = $pdfcolor;
4029				$this->bgcolor = $intcolor;
4030				break;
4031			}
4032			case 'text': {
4033				$pdfcolor .= $suffix;
4034				$this->TextColor = $pdfcolor;
4035				$this->fgcolor = $intcolor;
4036				break;
4037			}
4038		}
4039		$this->ColorFlag = ($this->FillColor != $this->TextColor);
4040		if (($type != 'text') AND ($this->state == 2) AND $type !== 0) {
4041			if (!$ret) {
4042				$this->_out($pdfcolor);
4043			}
4044			return $pdfcolor;
4045		}
4046		return '';
4047	}
4048
4049	/**
4050	 * Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
4051	 * @param float $col1 GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
4052	 * @param float $col2 GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
4053	 * @param float $col3 BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
4054	 * @param float $col4 KEY (BLACK) color for CMYK (0-100).
4055	 * @param boolean $ret If true do not send the command.
4056	 * @param string $name spot color name (if any)
4057	 * @return string the PDF command
4058	 * @public
4059	 * @since 1.3
4060	 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
4061	 */
4062	public function setDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4063		return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name);
4064	}
4065
4066	/**
4067	 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
4068	 * @param float $col1 GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
4069	 * @param float $col2 GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
4070	 * @param float $col3 BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
4071	 * @param float $col4 KEY (BLACK) color for CMYK (0-100).
4072	 * @param boolean $ret If true do not send the command.
4073	 * @param string $name Spot color name (if any).
4074	 * @return string The PDF command.
4075	 * @public
4076	 * @since 1.3
4077	 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
4078	 */
4079	public function setFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4080		return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name);
4081	}
4082
4083	/**
4084	 * Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
4085	 * @param float $col1 GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
4086	 * @param float $col2 GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
4087	 * @param float $col3 BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
4088	 * @param float $col4 KEY (BLACK) color for CMYK (0-100).
4089	 * @param boolean $ret If true do not send the command.
4090	 * @param string $name Spot color name (if any).
4091	 * @return string Empty string.
4092	 * @public
4093	 * @since 1.3
4094	 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
4095	 */
4096	public function setTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4097		return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name);
4098	}
4099
4100	/**
4101	 * Returns the length of a string in user unit. A font must be selected.<br>
4102	 * @param string $s The string whose length is to be computed
4103	 * @param string $fontname Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
4104	 * @param string $fontstyle Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line-through</li><li>O: overline</li></ul> or any combination. The default value is regular.
4105	 * @param float $fontsize Font size in points. The default value is the current size.
4106	 * @param boolean $getarray if true returns an array of characters widths, if false returns the total length.
4107	 * @return float[]|float total string length or array of characted widths
4108	 * @author Nicola Asuni
4109	 * @public
4110	 * @since 1.2
4111	 */
4112	public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4113		return $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont), $s, $this->tmprtl, $this->isunicode, $this->CurrentFont), $fontname, $fontstyle, $fontsize, $getarray);
4114	}
4115
4116	/**
4117	 * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br>
4118	 * @param array $sa The array of chars whose total length is to be computed
4119	 * @param string $fontname Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
4120	 * @param string $fontstyle Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line through</li><li>O: overline</li></ul> or any combination. The default value is regular.
4121	 * @param float $fontsize Font size in points. The default value is the current size.
4122	 * @param boolean $getarray if true returns an array of characters widths, if false returns the total length.
4123	 * @return float[]|float total string length or array of characted widths
4124	 * @author Nicola Asuni
4125	 * @public
4126	 * @since 2.4.000 (2008-03-06)
4127	 */
4128	public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4129		// store current values
4130		if (!TCPDF_STATIC::empty_string($fontname)) {
4131			$prev_FontFamily = $this->FontFamily;
4132			$prev_FontStyle = $this->FontStyle;
4133			$prev_FontSizePt = $this->FontSizePt;
4134			$this->setFont($fontname, $fontstyle, $fontsize, '', 'default', false);
4135		}
4136		// convert UTF-8 array to Latin1 if required
4137		if ($this->isunicode AND (!$this->isUnicodeFont())) {
4138			$sa = TCPDF_FONTS::UTF8ArrToLatin1Arr($sa);
4139		}
4140		$w = 0; // total width
4141		$wa = array(); // array of characters widths
4142		foreach ($sa as $ck => $char) {
4143			// character width
4144			$cw = $this->GetCharWidth($char, isset($sa[($ck + 1)]));
4145			$wa[] = $cw;
4146			$w += $cw;
4147		}
4148		// restore previous values
4149		if (!TCPDF_STATIC::empty_string($fontname)) {
4150			$this->setFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
4151		}
4152		if ($getarray) {
4153			return $wa;
4154		}
4155		return $w;
4156	}
4157
4158	/**
4159	 * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking).
4160	 * @param int $char The char code whose length is to be returned
4161	 * @param boolean $notlast If false ignore the font-spacing.
4162	 * @return float char width
4163	 * @author Nicola Asuni
4164	 * @public
4165	 * @since 2.4.000 (2008-03-06)
4166	 */
4167	public function GetCharWidth($char, $notlast=true) {
4168		// get raw width
4169		$chw = $this->getRawCharWidth($char);
4170		if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) {
4171			// increase/decrease font spacing
4172			$chw += $this->font_spacing;
4173		}
4174		if ($this->font_stretching != 100) {
4175			// fixed stretching mode
4176			$chw *= ($this->font_stretching / 100);
4177		}
4178		return $chw;
4179	}
4180
4181	/**
4182	 * Returns the length of the char in user unit for the current font.
4183	 * @param int $char The char code whose length is to be returned
4184	 * @return float char width
4185	 * @author Nicola Asuni
4186	 * @public
4187	 * @since 5.9.000 (2010-09-28)
4188	 */
4189	public function getRawCharWidth($char) {
4190		if ($char == 173) {
4191			// SHY character will not be printed
4192			return (0);
4193		}
4194		if (isset($this->CurrentFont['cw'][intval($char)])) {
4195			$w = $this->CurrentFont['cw'][intval($char)];
4196		} elseif (isset($this->CurrentFont['dw'])) {
4197			// default width
4198			$w = $this->CurrentFont['dw'];
4199		} elseif (isset($this->CurrentFont['cw'][32])) {
4200			// default width
4201			$w = $this->CurrentFont['cw'][32];
4202		} else {
4203			$w = 600;
4204		}
4205		return $this->getAbsFontMeasure($w);
4206	}
4207
4208	/**
4209	 * Returns the numbero of characters in a string.
4210	 * @param string $s The input string.
4211	 * @return int number of characters
4212	 * @public
4213	 * @since 2.0.0001 (2008-01-07)
4214	 */
4215	public function GetNumChars($s) {
4216		if ($this->isUnicodeFont()) {
4217			return count(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont));
4218		}
4219		return strlen($s);
4220	}
4221
4222	/**
4223	 * Fill the list of available fonts ($this->fontlist).
4224	 * @protected
4225	 * @since 4.0.013 (2008-07-28)
4226	 */
4227	protected function getFontsList() {
4228		if (($fontsdir = opendir(TCPDF_FONTS::_getfontpath())) !== false) {
4229			while (($file = readdir($fontsdir)) !== false) {
4230				if (substr($file, -4) == '.php') {
4231					array_push($this->fontlist, strtolower(basename($file, '.php')));
4232				}
4233			}
4234			closedir($fontsdir);
4235		}
4236	}
4237
4238	/**
4239	 * Imports a TrueType, Type1, core, or CID0 font and makes it available.
4240	 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
4241	 * The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by K_PATH_FONTS if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated.
4242	 * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
4243	 * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul>
4244	 * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4245	 * @return array|false array containing the font data, or false in case of error.
4246	 * @param mixed $subset if true embedd only a subset of the font (stores only the information related to the used characters); if false embedd full font; if 'default' uses the default value set using setFontSubsetting(). This option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font.
4247	 * @public
4248	 * @since 1.5
4249	 * @see SetFont(), setFontSubsetting()
4250	 */
4251	public function AddFont($family, $style='', $fontfile='', $subset='default') {
4252		if ($subset === 'default') {
4253			$subset = $this->font_subsetting;
4254		}
4255		if ($this->pdfa_mode) {
4256			$subset = false;
4257		}
4258		if (TCPDF_STATIC::empty_string($family)) {
4259			if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
4260				$family = $this->FontFamily;
4261			} else {
4262				$this->Error('Empty font family');
4263			}
4264		}
4265		// move embedded styles on $style
4266		if (substr($family, -1) == 'I') {
4267			$style .= 'I';
4268			$family = substr($family, 0, -1);
4269		}
4270		if (substr($family, -1) == 'B') {
4271			$style .= 'B';
4272			$family = substr($family, 0, -1);
4273		}
4274		// normalize family name
4275		$family = strtolower($family);
4276		if ((!$this->isunicode) AND ($family == 'arial')) {
4277			$family = 'helvetica';
4278		}
4279		if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
4280			$style = '';
4281		}
4282		if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) {
4283			// all fonts must be embedded
4284			$family = 'pdfa'.$family;
4285		}
4286		$tempstyle = strtoupper($style);
4287		$style = '';
4288		// underline
4289		if (strpos($tempstyle, 'U') !== false) {
4290			$this->underline = true;
4291		} else {
4292			$this->underline = false;
4293		}
4294		// line-through (deleted)
4295		if (strpos($tempstyle, 'D') !== false) {
4296			$this->linethrough = true;
4297		} else {
4298			$this->linethrough = false;
4299		}
4300		// overline
4301		if (strpos($tempstyle, 'O') !== false) {
4302			$this->overline = true;
4303		} else {
4304			$this->overline = false;
4305		}
4306		// bold
4307		if (strpos($tempstyle, 'B') !== false) {
4308			$style .= 'B';
4309		}
4310		// oblique
4311		if (strpos($tempstyle, 'I') !== false) {
4312			$style .= 'I';
4313		}
4314		$bistyle = $style;
4315		$fontkey = $family.$style;
4316		$font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : '');
4317		$fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
4318		// check if the font has been already added
4319		$fb = $this->getFontBuffer($fontkey);
4320		if ($fb !== false) {
4321			if ($this->inxobj) {
4322				// we are inside an XObject template
4323				$this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i'];
4324			}
4325			return $fontdata;
4326		}
4327		// get specified font directory (if any)
4328		$fontdir = false;
4329		if (!TCPDF_STATIC::empty_string($fontfile)) {
4330			$fontdir = dirname($fontfile);
4331			if (TCPDF_STATIC::empty_string($fontdir) OR ($fontdir == '.')) {
4332				$fontdir = '';
4333			} else {
4334				$fontdir .= '/';
4335			}
4336		}
4337		// true when the font style variation is missing
4338		$missing_style = false;
4339		// search and include font file
4340		if (TCPDF_STATIC::empty_string($fontfile) OR (!@TCPDF_STATIC::file_exists($fontfile))) {
4341			// build a standard filenames for specified font
4342			$tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
4343			$fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);
4344			if (TCPDF_STATIC::empty_string($fontfile)) {
4345				$missing_style = true;
4346				// try to remove the style part
4347				$tmp_fontfile = str_replace(' ', '', $family).'.php';
4348				$fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);
4349			}
4350		}
4351		// include font file
4352		if (!TCPDF_STATIC::empty_string($fontfile) AND (@TCPDF_STATIC::file_exists($fontfile))) {
4353			include($fontfile);
4354		} else {
4355			$this->Error('Could not include font definition file: '.$family.'');
4356		}
4357		// check font parameters
4358		if ((!isset($type)) OR (!isset($cw))) {
4359			$this->Error('The font definition file has a bad format: '.$fontfile.'');
4360		}
4361		// SET default parameters
4362		if (!isset($file) OR TCPDF_STATIC::empty_string($file)) {
4363			$file = '';
4364		}
4365		if (!isset($enc) OR TCPDF_STATIC::empty_string($enc)) {
4366			$enc = '';
4367		}
4368		if (!isset($cidinfo) OR TCPDF_STATIC::empty_string($cidinfo)) {
4369			$cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0);
4370			$cidinfo['uni2cid'] = array();
4371		}
4372		if (!isset($ctg) OR TCPDF_STATIC::empty_string($ctg)) {
4373			$ctg = '';
4374		}
4375		if (!isset($desc) OR TCPDF_STATIC::empty_string($desc)) {
4376			$desc = array();
4377		}
4378		if (!isset($up) OR TCPDF_STATIC::empty_string($up)) {
4379			$up = -100;
4380		}
4381		if (!isset($ut) OR TCPDF_STATIC::empty_string($ut)) {
4382			$ut = 50;
4383		}
4384		if (!isset($cw) OR TCPDF_STATIC::empty_string($cw)) {
4385			$cw = array();
4386		}
4387		if (!isset($dw) OR TCPDF_STATIC::empty_string($dw)) {
4388			// set default width
4389			if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
4390				$dw = $desc['MissingWidth'];
4391			} elseif (isset($cw[32])) {
4392				$dw = $cw[32];
4393			} else {
4394				$dw = 600;
4395			}
4396		}
4397		++$this->numfonts;
4398		if ($type == 'core') {
4399			$name = $this->CoreFonts[$fontkey];
4400			$subset = false;
4401		} elseif (($type == 'TrueType') OR ($type == 'Type1')) {
4402			$subset = false;
4403		} elseif ($type == 'TrueTypeUnicode') {
4404			$enc = 'Identity-H';
4405		} elseif ($type == 'cidfont0') {
4406			if ($this->pdfa_mode) {
4407				$this->Error('All fonts must be embedded in PDF/A mode!');
4408			}
4409		} else {
4410			$this->Error('Unknow font type: '.$type.'');
4411		}
4412		// set name if unset
4413		if (!isset($name) OR empty($name)) {
4414			$name = $fontkey;
4415		}
4416		// create artificial font style variations if missing (only works with non-embedded fonts)
4417		if (($type != 'core') AND $missing_style) {
4418			// style variations
4419			$styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
4420			$name .= $styles[$bistyle];
4421			// artificial bold
4422			if (strpos($bistyle, 'B') !== false) {
4423				if (isset($desc['StemV'])) {
4424					// from normal to bold
4425					$desc['StemV'] = round($desc['StemV'] * 1.75);
4426				} else {
4427					// bold
4428					$desc['StemV'] = 123;
4429				}
4430			}
4431			// artificial italic
4432			if (strpos($bistyle, 'I') !== false) {
4433				if (isset($desc['ItalicAngle'])) {
4434					$desc['ItalicAngle'] -= 11;
4435				} else {
4436					$desc['ItalicAngle'] = -11;
4437				}
4438				if (isset($desc['Flags'])) {
4439					$desc['Flags'] |= 64; //bit 7
4440				} else {
4441					$desc['Flags'] = 64;
4442				}
4443			}
4444		}
4445		// check if the array of characters bounding boxes is defined
4446		if (!isset($cbbox)) {
4447			$cbbox = array();
4448		}
4449		// initialize subsetchars
4450		$subsetchars = array_fill(0, 255, true);
4451		$this->setFontBuffer($fontkey, array('fontkey' => $fontkey, 'i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'cbbox' => $cbbox, 'dw' => $dw, 'enc' => $enc, 'cidinfo' => $cidinfo, 'file' => $file, 'ctg' => $ctg, 'subset' => $subset, 'subsetchars' => $subsetchars));
4452		if ($this->inxobj) {
4453			// we are inside an XObject template
4454			$this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts;
4455		}
4456		if (isset($diff) AND (!empty($diff))) {
4457			//Search existing encodings
4458			$d = 0;
4459			$nb = count($this->diffs);
4460			for ($i=1; $i <= $nb; ++$i) {
4461				if ($this->diffs[$i] == $diff) {
4462					$d = $i;
4463					break;
4464				}
4465			}
4466			if ($d == 0) {
4467				$d = $nb + 1;
4468				$this->diffs[$d] = $diff;
4469			}
4470			$this->setFontSubBuffer($fontkey, 'diff', $d);
4471		}
4472		if (!TCPDF_STATIC::empty_string($file)) {
4473			if (!isset($this->FontFiles[$file])) {
4474				if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
4475					$this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4476				} elseif ($type != 'core') {
4477					$this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4478				}
4479			} else {
4480				// update fontkeys that are sharing this font file
4481				$this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset);
4482				if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) {
4483					$this->FontFiles[$file]['fontkeys'][] = $fontkey;
4484				}
4485			}
4486		}
4487		return $fontdata;
4488	}
4489
4490	/**
4491	 * Sets the font used to print character strings.
4492	 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
4493	 * The method can be called before the first page is created and the font is retained from page to page.
4494	 * If you just wish to change the current font size, it is simpler to call SetFontSize().
4495	 * Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:<ul><li>They are in the current directory (the one where the running script lies)</li><li>They are in one of the directories defined by the include_path parameter</li><li>They are in the directory defined by the K_PATH_FONTS constant</li></ul><br />
4496	 * @param string $family Family font. It can be either a name defined by AddFont() or one of the standard Type1 families (case insensitive):<ul><li>times (Times-Roman)</li><li>timesb (Times-Bold)</li><li>timesi (Times-Italic)</li><li>timesbi (Times-BoldItalic)</li><li>helvetica (Helvetica)</li><li>helveticab (Helvetica-Bold)</li><li>helveticai (Helvetica-Oblique)</li><li>helveticabi (Helvetica-BoldOblique)</li><li>courier (Courier)</li><li>courierb (Courier-Bold)</li><li>courieri (Courier-Oblique)</li><li>courierbi (Courier-BoldOblique)</li><li>symbol (Symbol)</li><li>zapfdingbats (ZapfDingbats)</li></ul> It is also possible to pass an empty string. In that case, the current family is retained.
4497	 * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line through</li><li>O: overline</li></ul> or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined.
4498	 * @param float|null $size Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12
4499	 * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4500	 * @param mixed $subset if true embedd only a subset of the font (stores only the information related to the used characters); if false embedd full font; if 'default' uses the default value set using setFontSubsetting(). This option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font.
4501	 * @param boolean $out if true output the font size command, otherwise only set the font properties.
4502	 * @author Nicola Asuni
4503	 * @public
4504	 * @since 1.0
4505	 * @see AddFont(), SetFontSize()
4506	 */
4507	public function setFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) {
4508		//Select a font; size given in points
4509		if ($size === null) {
4510			$size = $this->FontSizePt;
4511		}
4512		if ($size < 0) {
4513			$size = 0;
4514		}
4515		// try to add font (if not already added)
4516		$fontdata = $this->AddFont($family, $style, $fontfile, $subset);
4517		$this->FontFamily = $fontdata['family'];
4518		$this->FontStyle = $fontdata['style'];
4519		if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
4520			// save subset chars of the previous font
4521			$this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
4522		}
4523		$this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
4524		$this->setFontSize($size, $out);
4525	}
4526
4527	/**
4528	 * Defines the size of the current font.
4529	 * @param float $size The font size in points.
4530	 * @param boolean $out if true output the font size command, otherwise only set the font properties.
4531	 * @public
4532	 * @since 1.0
4533	 * @see SetFont()
4534	 */
4535	public function setFontSize($size, $out=true) {
4536		$size = (float)$size;
4537		// font size in points
4538		$this->FontSizePt = $size;
4539		// font size in user units
4540		$this->FontSize = $size / $this->k;
4541		// calculate some font metrics
4542		if (isset($this->CurrentFont['desc']['FontBBox'])) {
4543			$bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4544			$font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
4545		} else {
4546			$font_height = $size * 1.219;
4547		}
4548		if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
4549			$font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000);
4550		}
4551		if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) {
4552			$font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000);
4553		}
4554		if (!isset($font_ascent) AND !isset($font_descent)) {
4555			// core font
4556			$font_ascent = 0.76 * $font_height;
4557			$font_descent = $font_height - $font_ascent;
4558		} elseif (!isset($font_descent)) {
4559			$font_descent = $font_height - $font_ascent;
4560		} elseif (!isset($font_ascent)) {
4561			$font_ascent = $font_height - $font_descent;
4562		}
4563		$this->FontAscent = ($font_ascent / $this->k);
4564		$this->FontDescent = ($font_descent / $this->k);
4565		if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i'])) AND ($this->state == 2)) {
4566			$this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4567		}
4568	}
4569
4570	/**
4571	 * Returns the bounding box of the current font in user units.
4572	 * @return array
4573	 * @public
4574	 * @since 5.9.152 (2012-03-23)
4575	 */
4576	public function getFontBBox() {
4577		$fbbox = array();
4578		if (isset($this->CurrentFont['desc']['FontBBox'])) {
4579			$tmpbbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4580			$fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox);
4581		} else {
4582			// Find max width
4583			if (isset($this->CurrentFont['desc']['MaxWidth'])) {
4584				$maxw = $this->getAbsFontMeasure(intval($this->CurrentFont['desc']['MaxWidth']));
4585			} else {
4586				$maxw = 0;
4587				if (isset($this->CurrentFont['desc']['MissingWidth'])) {
4588					$maxw = max($maxw, $this->CurrentFont['desc']['MissingWidth']);
4589				}
4590				if (isset($this->CurrentFont['desc']['AvgWidth'])) {
4591					$maxw = max($maxw, $this->CurrentFont['desc']['AvgWidth']);
4592				}
4593				if (isset($this->CurrentFont['dw'])) {
4594					$maxw = max($maxw, $this->CurrentFont['dw']);
4595				}
4596				foreach ($this->CurrentFont['cw'] as $char => $w) {
4597					$maxw = max($maxw, $w);
4598				}
4599				if ($maxw == 0) {
4600					$maxw = 600;
4601				}
4602				$maxw = $this->getAbsFontMeasure($maxw);
4603			}
4604			$fbbox = array(0, (0 - $this->FontDescent), $maxw, $this->FontAscent);
4605		}
4606		return $fbbox;
4607	}
4608
4609	/**
4610	 * Convert a relative font measure into absolute value.
4611	 * @param int $s Font measure.
4612	 * @return float Absolute measure.
4613	 * @since 5.9.186 (2012-09-13)
4614	 */
4615	public function getAbsFontMeasure($s) {
4616		return ($s * $this->FontSize / 1000);
4617	}
4618
4619	/**
4620	 * Returns the glyph bounding box of the specified character in the current font in user units.
4621	 * @param int $char Input character code.
4622	 * @return false|array array(xMin, yMin, xMax, yMax) or FALSE if not defined.
4623	 * @since 5.9.186 (2012-09-13)
4624	 */
4625	public function getCharBBox($char) {
4626		$c = intval($char);
4627		if (isset($this->CurrentFont['cw'][$c])) {
4628			// glyph is defined ... use zero width & height for glyphs without outlines
4629			$result = array(0,0,0,0);
4630			if (isset($this->CurrentFont['cbbox'][$c])) {
4631				$result = $this->CurrentFont['cbbox'][$c];
4632			}
4633			return array_map(array($this,'getAbsFontMeasure'), $result);
4634		}
4635		return false;
4636	}
4637
4638	/**
4639	 * Return the font descent value
4640	 * @param string $font font name
4641	 * @param string $style font style
4642	 * @param float $size The size (in points)
4643	 * @return int font descent
4644	 * @public
4645	 * @author Nicola Asuni
4646	 * @since 4.9.003 (2010-03-30)
4647	 */
4648	public function getFontDescent($font, $style='', $size=0) {
4649		$fontdata = $this->AddFont($font, $style);
4650		$fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4651		if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
4652			$descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
4653		} else {
4654			$descent = (1.219 * 0.24 * $size);
4655		}
4656		return ($descent / $this->k);
4657	}
4658
4659	/**
4660	 * Return the font ascent value.
4661	 * @param string $font font name
4662	 * @param string $style font style
4663	 * @param float $size The size (in points)
4664	 * @return int font ascent
4665	 * @public
4666	 * @author Nicola Asuni
4667	 * @since 4.9.003 (2010-03-30)
4668	 */
4669	public function getFontAscent($font, $style='', $size=0) {
4670		$fontdata = $this->AddFont($font, $style);
4671		$fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4672		if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
4673			$ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
4674		} else {
4675			$ascent = 1.219 * 0.76 * $size;
4676		}
4677		return ($ascent / $this->k);
4678	}
4679
4680	/**
4681	 * Return true in the character is present in the specified font.
4682	 * @param mixed $char Character to check (integer value or string)
4683	 * @param string $font Font name (family name).
4684	 * @param string $style Font style.
4685	 * @return bool true if the char is defined, false otherwise.
4686	 * @public
4687	 * @since 5.9.153 (2012-03-28)
4688	 */
4689	public function isCharDefined($char, $font='', $style='') {
4690		if (is_string($char)) {
4691			// get character code
4692			$char = TCPDF_FONTS::UTF8StringToArray($char, $this->isunicode, $this->CurrentFont);
4693			$char = $char[0];
4694		}
4695		if (TCPDF_STATIC::empty_string($font)) {
4696			if (TCPDF_STATIC::empty_string($style)) {
4697				return (isset($this->CurrentFont['cw'][intval($char)]));
4698			}
4699			$font = $this->FontFamily;
4700		}
4701		$fontdata = $this->AddFont($font, $style);
4702		$fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4703		return (isset($fontinfo['cw'][intval($char)]));
4704	}
4705
4706	/**
4707	 * Replace missing font characters on selected font with specified substitutions.
4708	 * @param string $text Text to process.
4709	 * @param string $font Font name (family name).
4710	 * @param string $style Font style.
4711	 * @param array $subs Array of possible character substitutions. The key is the character to check (integer value) and the value is a single intege value or an array of possible substitutes.
4712	 * @return string Processed text.
4713	 * @public
4714	 * @since 5.9.153 (2012-03-28)
4715	 */
4716	public function replaceMissingChars($text, $font='', $style='', $subs=array()) {
4717		if (empty($subs)) {
4718			return $text;
4719		}
4720		if (TCPDF_STATIC::empty_string($font)) {
4721			$font = $this->FontFamily;
4722		}
4723		$fontdata = $this->AddFont($font, $style);
4724		$fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4725		$uniarr = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
4726		foreach ($uniarr as $k => $chr) {
4727			if (!isset($fontinfo['cw'][$chr])) {
4728				// this character is missing on the selected font
4729				if (isset($subs[$chr])) {
4730					// we have available substitutions
4731					if (is_array($subs[$chr])) {
4732						foreach($subs[$chr] as $s) {
4733							if (isset($fontinfo['cw'][$s])) {
4734								$uniarr[$k] = $s;
4735								break;
4736							}
4737						}
4738					} elseif (isset($fontinfo['cw'][$subs[$chr]])) {
4739						$uniarr[$k] = $subs[$chr];
4740					}
4741				}
4742			}
4743		}
4744		return TCPDF_FONTS::UniArrSubString(TCPDF_FONTS::UTF8ArrayToUniArray($uniarr, $this->isunicode));
4745	}
4746
4747	/**
4748	 * Defines the default monospaced font.
4749	 * @param string $font Font name.
4750	 * @public
4751	 * @since 4.5.025
4752	 */
4753	public function setDefaultMonospacedFont($font) {
4754		$this->default_monospaced_font = $font;
4755	}
4756
4757	/**
4758	 * Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.<br />
4759	 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
4760	 * @public
4761	 * @since 1.5
4762	 * @see Cell(), Write(), Image(), Link(), SetLink()
4763	 */
4764	public function AddLink() {
4765		// create a new internal link
4766		$n = count($this->links) + 1;
4767		$this->links[$n] = array('p' => 0, 'y' => 0, 'f' => false);
4768		return $n;
4769	}
4770
4771	/**
4772	 * Defines the page and position a link points to.
4773	 * @param int $link The link identifier returned by AddLink()
4774	 * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
4775	 * @param int|string $page Number of target page; -1 indicates the current page (default value). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages.
4776	 * @public
4777	 * @since 1.5
4778	 * @see AddLink()
4779	 */
4780	public function setLink($link, $y=0, $page=-1) {
4781		$fixed = false;
4782		if (!empty($page) AND (substr($page, 0, 1) == '*')) {
4783			$page = intval(substr($page, 1));
4784			// this page number will not be changed when moving/add/deleting pages
4785			$fixed = true;
4786		}
4787		if ($page < 0) {
4788			$page = $this->page;
4789		}
4790		if ($y == -1) {
4791			$y = $this->y;
4792		}
4793		$this->links[$link] = array('p' => $page, 'y' => $y, 'f' => $fixed);
4794	}
4795
4796	/**
4797	 * Puts a link on a rectangular area of the page.
4798	 * Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image.
4799	 * @param float $x Abscissa of the upper-left corner of the rectangle
4800	 * @param float $y Ordinate of the upper-left corner of the rectangle
4801	 * @param float $w Width of the rectangle
4802	 * @param float $h Height of the rectangle
4803	 * @param mixed $link URL or identifier returned by AddLink()
4804	 * @param int $spaces number of spaces on the text to link
4805	 * @public
4806	 * @since 1.5
4807	 * @see AddLink(), Annotation(), Cell(), Write(), Image()
4808	 */
4809	public function Link($x, $y, $w, $h, $link, $spaces=0) {
4810		$this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
4811	}
4812
4813	/**
4814	 * Puts a markup annotation on a rectangular area of the page.
4815	 * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!
4816	 * @param float $x Abscissa of the upper-left corner of the rectangle
4817	 * @param float $y Ordinate of the upper-left corner of the rectangle
4818	 * @param float $w Width of the rectangle
4819	 * @param float $h Height of the rectangle
4820	 * @param string $text annotation text or alternate content
4821	 * @param array $opt array of options (see section 8.4 of PDF reference 1.7).
4822	 * @param int $spaces number of spaces on the text to link
4823	 * @public
4824	 * @since 4.0.018 (2008-08-06)
4825	 */
4826	public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
4827		if ($this->inxobj) {
4828			// store parameters for later use on template
4829			$this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
4830			return;
4831		}
4832		if ($x === '') {
4833			$x = $this->x;
4834		}
4835		if ($y === '') {
4836			$y = $this->y;
4837		}
4838		// check page for no-write regions and adapt page margins if necessary
4839		list($x, $y) = $this->checkPageRegions($h, $x, $y);
4840		// recalculate coordinates to account for graphic transformations
4841		if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) {
4842			for ($i=$this->transfmatrix_key; $i > 0; --$i) {
4843				$maxid = count($this->transfmatrix[$i]) - 1;
4844				for ($j=$maxid; $j >= 0; --$j) {
4845					$ctm = $this->transfmatrix[$i][$j];
4846					if (isset($ctm['a'])) {
4847						$x = $x * $this->k;
4848						$y = ($this->h - $y) * $this->k;
4849						$w = $w * $this->k;
4850						$h = $h * $this->k;
4851						// top left
4852						$xt = $x;
4853						$yt = $y;
4854						$x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4855						$y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4856						// top right
4857						$xt = $x + $w;
4858						$yt = $y;
4859						$x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4860						$y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4861						// bottom left
4862						$xt = $x;
4863						$yt = $y - $h;
4864						$x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4865						$y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4866						// bottom right
4867						$xt = $x + $w;
4868						$yt = $y - $h;
4869						$x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4870						$y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4871						// new coordinates (rectangle area)
4872						$x = min($x1, $x2, $x3, $x4);
4873						$y = max($y1, $y2, $y3, $y4);
4874						$w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
4875						$h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
4876						$x = $x / $this->k;
4877						$y = $this->h - ($y / $this->k);
4878					}
4879				}
4880			}
4881		}
4882		if ($this->page <= 0) {
4883			$page = 1;
4884		} else {
4885			$page = $this->page;
4886		}
4887		if (!isset($this->PageAnnots[$page])) {
4888			$this->PageAnnots[$page] = array();
4889		}
4890		$this->PageAnnots[$page][] = array('n' => ++$this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
4891		if (!$this->pdfa_mode || ($this->pdfa_mode && $this->pdfa_version == 3)) {
4892			if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!TCPDF_STATIC::empty_string($opt['FS']))
4893				AND (@TCPDF_STATIC::file_exists($opt['FS']) OR TCPDF_STATIC::isValidURL($opt['FS']))
4894				AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
4895				$this->embeddedfiles[basename($opt['FS'])] = array('f' => ++$this->n, 'n' => ++$this->n, 'file' => $opt['FS']);
4896			}
4897		}
4898		// Add widgets annotation's icons
4899		if (isset($opt['mk']['i']) AND @TCPDF_STATIC::file_exists($opt['mk']['i'])) {
4900			$this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
4901		}
4902		if (isset($opt['mk']['ri']) AND @TCPDF_STATIC::file_exists($opt['mk']['ri'])) {
4903			$this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4904		}
4905		if (isset($opt['mk']['ix']) AND @TCPDF_STATIC::file_exists($opt['mk']['ix'])) {
4906			$this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4907		}
4908	}
4909
4910	/**
4911	 * Embedd the attached files.
4912	 * @since 4.4.000 (2008-12-07)
4913	 * @protected
4914	 * @see Annotation()
4915	 */
4916	protected function _putEmbeddedFiles() {
4917		if ($this->pdfa_mode && $this->pdfa_version != 3)  {
4918			// embedded files are not allowed in PDF/A mode version 1 and 2
4919			return;
4920		}
4921		reset($this->embeddedfiles);
4922		foreach ($this->embeddedfiles as $filename => $filedata) {
4923		    $data = $this->getCachedFileContents($filedata['file']);
4924			if ($data !== FALSE) {
4925				$rawsize = strlen($data);
4926				if ($rawsize > 0) {
4927					// update name tree
4928					$this->efnames[$filename] = $filedata['f'].' 0 R';
4929					// embedded file specification object
4930					$out = $this->_getobj($filedata['f'])."\n";
4931					$out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']);
4932					$out .= ' /UF '.$this->_datastring($filename, $filedata['f']);
4933					$out .= ' /AFRelationship /Source';
4934					$out .= ' /EF <</F '.$filedata['n'].' 0 R>> >>';
4935					$out .= "\n".'endobj';
4936					$this->_out($out);
4937					// embedded file object
4938					$filter = '';
4939					if ($this->compress) {
4940						$data = gzcompress($data);
4941						$filter = ' /Filter /FlateDecode';
4942					}
4943
4944					if ($this->pdfa_version == 3) {
4945						$filter = ' /Subtype /text#2Fxml';
4946					}
4947
4948					$stream = $this->_getrawstream($data, $filedata['n']);
4949					$out = $this->_getobj($filedata['n'])."\n";
4950					$out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>';
4951					$out .= ' stream'."\n".$stream."\n".'endstream';
4952					$out .= "\n".'endobj';
4953					$this->_out($out);
4954				}
4955			}
4956		}
4957	}
4958
4959	/**
4960	 * Prints a text cell at the specified position.
4961	 * This method allows to place a string precisely on the page.
4962	 * @param float $x Abscissa of the cell origin
4963	 * @param float $y Ordinate of the cell origin
4964	 * @param string $txt String to print
4965	 * @param int $fstroke outline size in user units (0 = disable)
4966	 * @param boolean $fclip if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
4967	 * @param boolean $ffill if true fills the text
4968	 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
4969	 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
4970	 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
4971	 * @param boolean $fill Indicates if the cell background must be painted (true) or transparent (false).
4972	 * @param mixed $link URL or identifier returned by AddLink().
4973	 * @param int $stretch font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
4974	 * @param boolean $ignore_min_height if true ignore automatic minimum height value.
4975	 * @param string $calign cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li><li>B : cell bottom</li></ul>
4976	 * @param string $valign text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
4977	 * @param boolean $rtloff if true uses the page top-left corner as origin of axis for $x and $y initial position.
4978	 * @public
4979	 * @since 1.0
4980	 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()
4981	 */
4982	public function Text($x, $y, $txt, $fstroke=0, $fclip=false, $ffill=true, $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M', $rtloff=false) {
4983		$textrendermode = $this->textrendermode;
4984		$textstrokewidth = $this->textstrokewidth;
4985		$this->setTextRenderingMode($fstroke, $ffill, $fclip);
4986		$this->setXY($x, $y, $rtloff);
4987		$this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
4988		// restore previous rendering mode
4989		$this->textrendermode = $textrendermode;
4990		$this->textstrokewidth = $textstrokewidth;
4991	}
4992
4993	/**
4994	 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
4995	 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
4996	 * This method is called automatically and should not be called directly by the application.
4997	 * @return bool
4998	 * @public
4999	 * @since 1.4
5000	 * @see SetAutoPageBreak()
5001	 */
5002	public function AcceptPageBreak() {
5003		if ($this->num_columns > 1) {
5004			// multi column mode
5005			if ($this->current_column < ($this->num_columns - 1)) {
5006				// go to next column
5007				$this->selectColumn($this->current_column + 1);
5008			} elseif ($this->AutoPageBreak) {
5009				// add a new page
5010				$this->AddPage();
5011				// set first column
5012				$this->selectColumn(0);
5013			}
5014			// avoid page breaking from checkPageBreak()
5015			return false;
5016		}
5017		return $this->AutoPageBreak;
5018	}
5019
5020	/**
5021	 * Add page if needed.
5022	 * @param float $h Cell height. Default value: 0.
5023	 * @param float|null $y starting y position, leave empty for current position.
5024	 * @param bool  $addpage if true add a page, otherwise only return the true/false state
5025	 * @return bool true in case of page break, false otherwise.
5026	 * @since 3.2.000 (2008-07-01)
5027	 * @protected
5028	 */
5029	protected function checkPageBreak($h=0, $y=null, $addpage=true) {
5030		if (TCPDF_STATIC::empty_string($y)) {
5031			$y = $this->y;
5032		}
5033		$current_page = $this->page;
5034		if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) {
5035			if ($addpage) {
5036				//Automatic page break
5037				$x = $this->x;
5038				$this->AddPage($this->CurOrientation);
5039				$this->y = $this->tMargin;
5040				$oldpage = $this->page - 1;
5041				if ($this->rtl) {
5042					if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
5043						$this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
5044					} else {
5045						$this->x = $x;
5046					}
5047				} else {
5048					if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
5049						$this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
5050					} else {
5051						$this->x = $x;
5052					}
5053				}
5054			}
5055			return true;
5056		}
5057		if ($current_page != $this->page) {
5058			// account for columns mode
5059			return true;
5060		}
5061		return false;
5062	}
5063
5064	/**
5065	 * Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
5066	 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
5067	 * @param float $w Cell width. If 0, the cell extends up to the right margin.
5068	 * @param float $h Cell height. Default value: 0.
5069	 * @param string $txt String to print. Default value: empty string.
5070	 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5071	 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
5072	 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
5073	 * @param boolean $fill Indicates if the cell background must be painted (true) or transparent (false).
5074	 * @param mixed $link URL or identifier returned by AddLink().
5075	 * @param int $stretch font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
5076	 * @param boolean $ignore_min_height if true ignore automatic minimum height value.
5077	 * @param string $calign cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul>
5078	 * @param string $valign text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
5079	 * @public
5080	 * @since 1.0
5081	 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
5082	 */
5083	public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') {
5084		$prev_cell_margin = $this->cell_margin;
5085		$prev_cell_padding = $this->cell_padding;
5086		$this->adjustCellPadding($border);
5087		if (!$ignore_min_height) {
5088			$min_cell_height = $this->getCellHeight($this->FontSize);
5089			if ($h < $min_cell_height) {
5090				$h = $min_cell_height;
5091			}
5092		}
5093		$this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']);
5094		// apply text shadow if enabled
5095		if ($this->txtshadow['enabled']) {
5096			// save data
5097			$x = $this->x;
5098			$y = $this->y;
5099			$bc = $this->bgcolor;
5100			$fc = $this->fgcolor;
5101			$sc = $this->strokecolor;
5102			$alpha = $this->alpha;
5103			// print shadow
5104			$this->x += $this->txtshadow['depth_w'];
5105			$this->y += $this->txtshadow['depth_h'];
5106			$this->setFillColorArray($this->txtshadow['color']);
5107			$this->setTextColorArray($this->txtshadow['color']);
5108			$this->setDrawColorArray($this->txtshadow['color']);
5109			if ($this->txtshadow['opacity'] != $alpha['CA']) {
5110				$this->setAlpha($this->txtshadow['opacity'], $this->txtshadow['blend_mode']);
5111			}
5112			if ($this->state == 2) {
5113				$this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5114			}
5115			//restore data
5116			$this->x = $x;
5117			$this->y = $y;
5118			$this->setFillColorArray($bc);
5119			$this->setTextColorArray($fc);
5120			$this->setDrawColorArray($sc);
5121			if ($this->txtshadow['opacity'] != $alpha['CA']) {
5122				$this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']);
5123			}
5124		}
5125		if ($this->state == 2) {
5126			$this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5127		}
5128		$this->cell_padding = $prev_cell_padding;
5129		$this->cell_margin = $prev_cell_margin;
5130	}
5131
5132	/**
5133	 * Returns the PDF string code to print a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
5134	 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
5135	 * @param float $w Cell width. If 0, the cell extends up to the right margin.
5136	 * @param float $h Cell height. Default value: 0.
5137	 * @param string $txt String to print. Default value: empty string.
5138	 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5139	 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
5140	 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
5141	 * @param boolean $fill Indicates if the cell background must be painted (true) or transparent (false).
5142	 * @param mixed $link URL or identifier returned by AddLink().
5143	 * @param int $stretch font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
5144	 * @param boolean $ignore_min_height if true ignore automatic minimum height value.
5145	 * @param string $calign cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul>
5146	 * @param string $valign text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>M : middle</li><li>B : bottom</li></ul>
5147	 * @return string containing cell code
5148	 * @protected
5149	 * @since 1.0
5150	 * @see Cell()
5151	 */
5152	protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') {
5153		// replace 'NO-BREAK SPACE' (U+00A0) character with a simple space
5154		$txt = str_replace(TCPDF_FONTS::unichr(160, $this->isunicode), ' ', $txt);
5155		$prev_cell_margin = $this->cell_margin;
5156		$prev_cell_padding = $this->cell_padding;
5157		$txt = TCPDF_STATIC::removeSHY($txt, $this->isunicode);
5158		$rs = ''; //string to be returned
5159		$this->adjustCellPadding($border);
5160		if (!$ignore_min_height) {
5161			$min_cell_height = $this->getCellHeight($this->FontSize);
5162			if ($h < $min_cell_height) {
5163				$h = $min_cell_height;
5164			}
5165		}
5166		$k = $this->k;
5167		// check page for no-write regions and adapt page margins if necessary
5168		list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
5169		if ($this->rtl) {
5170			$x = $this->x - $this->cell_margin['R'];
5171		} else {
5172			$x = $this->x + $this->cell_margin['L'];
5173		}
5174		$y = $this->y + $this->cell_margin['T'];
5175		$prev_font_stretching = $this->font_stretching;
5176		$prev_font_spacing = $this->font_spacing;
5177		// cell vertical alignment
5178		switch ($calign) {
5179			case 'A': {
5180				// font top
5181				switch ($valign) {
5182					case 'T': {
5183						// top
5184						$y -= $this->cell_padding['T'];
5185						break;
5186					}
5187					case 'B': {
5188						// bottom
5189						$y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent);
5190						break;
5191					}
5192					default:
5193					case 'C':
5194					case 'M': {
5195						// center
5196						$y -= (($h - $this->FontAscent - $this->FontDescent) / 2);
5197						break;
5198					}
5199				}
5200				break;
5201			}
5202			case 'L': {
5203				// font baseline
5204				switch ($valign) {
5205					case 'T': {
5206						// top
5207						$y -= ($this->cell_padding['T'] + $this->FontAscent);
5208						break;
5209					}
5210					case 'B': {
5211						// bottom
5212						$y -= ($h - $this->cell_padding['B'] - $this->FontDescent);
5213						break;
5214					}
5215					default:
5216					case 'C':
5217					case 'M': {
5218						// center
5219						$y -= (($h + $this->FontAscent - $this->FontDescent) / 2);
5220						break;
5221					}
5222				}
5223				break;
5224			}
5225			case 'D': {
5226				// font bottom
5227				switch ($valign) {
5228					case 'T': {
5229						// top
5230						$y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent);
5231						break;
5232					}
5233					case 'B': {
5234						// bottom
5235						$y -= ($h - $this->cell_padding['B']);
5236						break;
5237					}
5238					default:
5239					case 'C':
5240					case 'M': {
5241						// center
5242						$y -= (($h + $this->FontAscent + $this->FontDescent) / 2);
5243						break;
5244					}
5245				}
5246				break;
5247			}
5248			case 'B': {
5249				// cell bottom
5250				$y -= $h;
5251				break;
5252			}
5253			case 'C':
5254			case 'M': {
5255				// cell center
5256				$y -= ($h / 2);
5257				break;
5258			}
5259			default:
5260			case 'T': {
5261				// cell top
5262				break;
5263			}
5264		}
5265		// text vertical alignment
5266		switch ($valign) {
5267			case 'T': {
5268				// top
5269				$yt = $y + $this->cell_padding['T'];
5270				break;
5271			}
5272			case 'B': {
5273				// bottom
5274				$yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent;
5275				break;
5276			}
5277			default:
5278			case 'C':
5279			case 'M': {
5280				// center
5281				$yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2);
5282				break;
5283			}
5284		}
5285		$basefonty = $yt + $this->FontAscent;
5286		if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5287			if ($this->rtl) {
5288				$w = $x - $this->lMargin;
5289			} else {
5290				$w = $this->w - $this->rMargin - $x;
5291			}
5292		}
5293		$s = '';
5294		// fill and borders
5295		if (is_string($border) AND (strlen($border) == 4)) {
5296			// full border
5297			$border = 1;
5298		}
5299		if ($fill OR ($border == 1)) {
5300			if ($fill) {
5301				$op = ($border == 1) ? 'B' : 'f';
5302			} else {
5303				$op = 'S';
5304			}
5305			if ($this->rtl) {
5306				$xk = (($x - $w) * $k);
5307			} else {
5308				$xk = ($x * $k);
5309			}
5310			$s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op);
5311		}
5312		// draw borders
5313		$s .= $this->getCellBorder($x, $y, $w, $h, $border);
5314		if ($txt != '') {
5315			$txt2 = $txt;
5316			if ($this->isunicode) {
5317				if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5318					$txt2 = TCPDF_FONTS::UTF8ToLatin1($txt2, $this->isunicode, $this->CurrentFont);
5319				} else {
5320					$unicode = TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont); // array of UTF-8 unicode values
5321					$unicode = TCPDF_FONTS::utf8Bidi($unicode, '', $this->tmprtl, $this->isunicode, $this->CurrentFont);
5322					// replace thai chars (if any)
5323					if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) {
5324						// number of chars
5325						$numchars = count($unicode);
5326						// po pla, for far, for fan
5327						$longtail = array(0x0e1b, 0x0e1d, 0x0e1f);
5328						// do chada, to patak
5329						$lowtail = array(0x0e0e, 0x0e0f);
5330						// mai hun arkad, sara i, sara ii, sara ue, sara uee
5331						$upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37);
5332						// mai ek, mai tho, mai tri, mai chattawa, karan
5333						$tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c);
5334						// sara u, sara uu, pinthu
5335						$lowvowel = array(0x0e38, 0x0e39, 0x0e3a);
5336						$output = array();
5337						for ($i = 0; $i < $numchars; $i++) {
5338							if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) {
5339								$ch0 = $unicode[$i];
5340								$ch1 = ($i > 0) ? $unicode[($i - 1)] : 0;
5341								$ch2 = ($i > 1) ? $unicode[($i - 2)] : 0;
5342								$chn = ($i < ($numchars - 1)) ? $unicode[($i + 1)] : 0;
5343								if (in_array($ch0, $tonemark)) {
5344									if ($chn == 0x0e33) {
5345										// sara um
5346										if (in_array($ch1, $longtail)) {
5347											// tonemark at upper left
5348											$output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5349										} else {
5350											// tonemark at upper right (normal position)
5351											$output[] = $ch0;
5352										}
5353									} elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) {
5354										// tonemark at lower left
5355										$output[] = $this->replaceChar($ch0, (0xf705 + $ch0 - 0x0e48));
5356									} elseif (in_array($ch1, $upvowel)) {
5357										if (in_array($ch2, $longtail)) {
5358											// tonemark at upper left
5359											$output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5360										} else {
5361											// tonemark at upper right (normal position)
5362											$output[] = $ch0;
5363										}
5364									} else {
5365										// tonemark at lower right
5366										$output[] = $this->replaceChar($ch0, (0xf70a + $ch0 - 0x0e48));
5367									}
5368								} elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) {
5369									// add lower left nikhahit and sara aa
5370									if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) {
5371										$output[] = 0xf711;
5372										$this->CurrentFont['subsetchars'][0xf711] = true;
5373										$output[] = 0x0e32;
5374										$this->CurrentFont['subsetchars'][0x0e32] = true;
5375									} else {
5376										$output[] = $ch0;
5377									}
5378								} elseif (in_array($ch1, $longtail)) {
5379									if ($ch0 == 0x0e31) {
5380										// lower left mai hun arkad
5381										$output[] = $this->replaceChar($ch0, 0xf710);
5382									} elseif (in_array($ch0, $upvowel)) {
5383										// lower left
5384										$output[] = $this->replaceChar($ch0, (0xf701 + $ch0 - 0x0e34));
5385									} elseif ($ch0 == 0x0e47) {
5386										// lower left mai tai koo
5387										$output[] = $this->replaceChar($ch0, 0xf712);
5388									} else {
5389										// normal character
5390										$output[] = $ch0;
5391									}
5392								} elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) {
5393									// lower vowel
5394									$output[] = $this->replaceChar($ch0, (0xf718 + $ch0 - 0x0e38));
5395								} elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) {
5396									// yo ying without lower part
5397									$output[] = $this->replaceChar($ch0, 0xf70f);
5398								} elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) {
5399									// tho santan without lower part
5400									$output[] = $this->replaceChar($ch0, 0xf700);
5401								} else {
5402									$output[] = $ch0;
5403								}
5404							} else {
5405								// non-thai character
5406								$output[] = $unicode[$i];
5407							}
5408						}
5409						$unicode = $output;
5410						// update font subsetchars
5411						$this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
5412					} // end of K_THAI_TOPCHARS
5413					$txt2 = TCPDF_FONTS::arrUTF8ToUTF16BE($unicode, false);
5414				}
5415			}
5416			$txt2 = TCPDF_STATIC::_escape($txt2);
5417			// get current text width (considering general font stretching and spacing)
5418			$txwidth = $this->GetStringWidth($txt);
5419			$width = $txwidth;
5420			// check for stretch mode
5421			if ($stretch > 0) {
5422				// calculate ratio between cell width and text width
5423				if ($width <= 0) {
5424					$ratio = 1;
5425				} else {
5426					$ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width);
5427				}
5428				// check if stretching is required
5429				if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) {
5430					// the text will be stretched to fit cell width
5431					if ($stretch > 2) {
5432						// set new character spacing
5433						$this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100));
5434					} else {
5435						// set new horizontal stretching
5436						$this->font_stretching *= $ratio;
5437					}
5438					// recalculate text width (the text fills the entire cell)
5439					$width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5440					// reset alignment
5441					$align = '';
5442				}
5443			}
5444			if ($this->font_stretching != 100) {
5445				// apply font stretching
5446				$rs .= sprintf('BT %F Tz ET ', $this->font_stretching);
5447			}
5448			if ($this->font_spacing != 0) {
5449				// increase/decrease font spacing
5450				$rs .= sprintf('BT %F Tc ET ', ($this->font_spacing * $this->k));
5451			}
5452			if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5453				$s .= 'q '.$this->TextColor.' ';
5454			}
5455			// rendering mode
5456			$s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, ($this->textstrokewidth * $this->k));
5457			// count number of spaces
5458			$ns = substr_count($txt, chr(32));
5459			// Justification
5460			$spacewidth = 0;
5461			if (($align == 'J') AND ($ns > 0)) {
5462				if ($this->isUnicodeFont()) {
5463					// get string width without spaces
5464					$width = $this->GetStringWidth(str_replace(' ', '', $txt));
5465					// calculate average space width
5466					$spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / ($this->FontSize?$this->FontSize:1);
5467					if ($this->font_stretching != 100) {
5468						// word spacing is affected by stretching
5469						$spacewidth /= ($this->font_stretching / 100);
5470					}
5471					// set word position to be used with TJ operator
5472					$txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2);
5473					$unicode_justification = true;
5474				} else {
5475					// get string width
5476					$width = $txwidth;
5477					// new space width
5478					$spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k;
5479					if ($this->font_stretching != 100) {
5480						// word spacing (Tw) is affected by stretching
5481						$spacewidth /= ($this->font_stretching / 100);
5482					}
5483					// set word spacing
5484					$rs .= sprintf('BT %F Tw ET ', $spacewidth);
5485				}
5486				$width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5487			}
5488			// replace carriage return characters
5489			$txt2 = str_replace("\r", ' ', $txt2);
5490			switch ($align) {
5491				case 'C': {
5492					$dx = ($w - $width) / 2;
5493					break;
5494				}
5495				case 'R': {
5496					if ($this->rtl) {
5497						$dx = $this->cell_padding['R'];
5498					} else {
5499						$dx = $w - $width - $this->cell_padding['R'];
5500					}
5501					break;
5502				}
5503				case 'L': {
5504					if ($this->rtl) {
5505						$dx = $w - $width - $this->cell_padding['L'];
5506					} else {
5507						$dx = $this->cell_padding['L'];
5508					}
5509					break;
5510				}
5511				case 'J':
5512				default: {
5513					if ($this->rtl) {
5514						$dx = $this->cell_padding['R'];
5515					} else {
5516						$dx = $this->cell_padding['L'];
5517					}
5518					break;
5519				}
5520			}
5521			if ($this->rtl) {
5522				$xdx = $x - $dx - $width;
5523			} else {
5524				$xdx = $x + $dx;
5525			}
5526			$xdk = $xdx * $k;
5527			// print text
5528			$s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
5529			if (isset($uniblock)) {
5530				// print overlapping characters as separate string
5531				$xshift = 0; // horizontal shift
5532				$ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k);
5533				$spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1));
5534				foreach ($uniblock as $uk => $uniarr) {
5535					if (($uk % 2) == 0) {
5536						// x space to skip
5537						if ($spacewidth != 0) {
5538							// justification shift
5539							$xshift += (count(array_keys($uniarr, 32)) * $spw);
5540						}
5541						$xshift += $this->GetArrStringWidth($uniarr); // + shift justification
5542					} else {
5543						// character to print
5544						$topchr = TCPDF_FONTS::arrUTF8ToUTF16BE($uniarr, false);
5545						$topchr = TCPDF_STATIC::_escape($topchr);
5546						$s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr);
5547					}
5548				}
5549			}
5550			if ($this->underline) {
5551				$s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
5552			}
5553			if ($this->linethrough) {
5554				$s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
5555			}
5556			if ($this->overline) {
5557				$s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
5558			}
5559			if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5560				$s .= ' Q';
5561			}
5562			if ($link) {
5563				$this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns);
5564			}
5565		}
5566		// output cell
5567		if ($s) {
5568			// output cell
5569			$rs .= $s;
5570			if ($this->font_spacing != 0) {
5571				// reset font spacing mode
5572				$rs .= ' BT 0 Tc ET';
5573			}
5574			if ($this->font_stretching != 100) {
5575				// reset font stretching mode
5576				$rs .= ' BT 100 Tz ET';
5577			}
5578		}
5579		// reset word spacing
5580		if (!$this->isUnicodeFont() AND ($align == 'J')) {
5581			$rs .= ' BT 0 Tw ET';
5582		}
5583		// reset stretching and spacing
5584		$this->font_stretching = $prev_font_stretching;
5585		$this->font_spacing = $prev_font_spacing;
5586		$this->lasth = $h;
5587		if ($ln > 0) {
5588			//Go to the beginning of the next line
5589			$this->y = $y + $h + $this->cell_margin['B'];
5590			if ($ln == 1) {
5591				if ($this->rtl) {
5592					$this->x = $this->w - $this->rMargin;
5593				} else {
5594					$this->x = $this->lMargin;
5595				}
5596			}
5597		} else {
5598			// go left or right by case
5599			if ($this->rtl) {
5600				$this->x = $x - $w - $this->cell_margin['L'];
5601			} else {
5602				$this->x = $x + $w + $this->cell_margin['R'];
5603			}
5604		}
5605		$gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
5606		$rs = $gstyles.$rs;
5607		$this->cell_padding = $prev_cell_padding;
5608		$this->cell_margin = $prev_cell_margin;
5609		return $rs;
5610	}
5611
5612	/**
5613	 * Replace a char if is defined on the current font.
5614	 * @param int $oldchar Integer code (unicode) of the character to replace.
5615	 * @param int $newchar Integer code (unicode) of the new character.
5616	 * @return int the replaced char or the old char in case the new char i not defined
5617	 * @protected
5618	 * @since 5.9.167 (2012-06-22)
5619	 */
5620	protected function replaceChar($oldchar, $newchar) {
5621		if ($this->isCharDefined($newchar)) {
5622			// add the new char on the subset list
5623			$this->CurrentFont['subsetchars'][$newchar] = true;
5624			// return the new character
5625			return $newchar;
5626		}
5627		// return the old char
5628		return $oldchar;
5629	}
5630
5631	/**
5632	 * Returns the code to draw the cell border
5633	 * @param float $x X coordinate.
5634	 * @param float $y Y coordinate.
5635	 * @param float $w Cell width.
5636	 * @param float $h Cell height.
5637	 * @param string|array|int $brd Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5638	 * @return string containing cell border code
5639	 * @protected
5640	 * @see SetLineStyle()
5641	 * @since 5.7.000 (2010-08-02)
5642	 */
5643	protected function getCellBorder($x, $y, $w, $h, $brd) {
5644		$s = ''; // string to be returned
5645		if (empty($brd)) {
5646			return $s;
5647		}
5648		if ($brd == 1) {
5649			$brd = array('LRTB' => true);
5650		}
5651		// calculate coordinates for border
5652		$k = $this->k;
5653		if ($this->rtl) {
5654			$xeL = ($x - $w) * $k;
5655			$xeR = $x * $k;
5656		} else {
5657			$xeL = $x * $k;
5658			$xeR = ($x + $w) * $k;
5659		}
5660		$yeL = (($this->h - ($y + $h)) * $k);
5661		$yeT = (($this->h - $y) * $k);
5662		$xeT = $xeL;
5663		$xeB = $xeR;
5664		$yeR = $yeT;
5665		$yeB = $yeL;
5666		if (is_string($brd)) {
5667			// convert string to array
5668			$slen = strlen($brd);
5669			$newbrd = array();
5670			for ($i = 0; $i < $slen; ++$i) {
5671				$newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
5672			}
5673			$brd = $newbrd;
5674		}
5675		if (isset($brd['mode'])) {
5676			$mode = $brd['mode'];
5677			unset($brd['mode']);
5678		} else {
5679			$mode = 'normal';
5680		}
5681		foreach ($brd as $border => $style) {
5682			if (is_array($style) AND !empty($style)) {
5683				// apply border style
5684				$prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' ';
5685				$s .= $this->setLineStyle($style, true)."\n";
5686			}
5687			switch ($mode) {
5688				case 'ext': {
5689					$off = (($this->LineWidth / 2) * $k);
5690					$xL = $xeL - $off;
5691					$xR = $xeR + $off;
5692					$yT = $yeT + $off;
5693					$yL = $yeL - $off;
5694					$xT = $xL;
5695					$xB = $xR;
5696					$yR = $yT;
5697					$yB = $yL;
5698					$w += $this->LineWidth;
5699					$h += $this->LineWidth;
5700					break;
5701				}
5702				case 'int': {
5703					$off = ($this->LineWidth / 2) * $k;
5704					$xL = $xeL + $off;
5705					$xR = $xeR - $off;
5706					$yT = $yeT - $off;
5707					$yL = $yeL + $off;
5708					$xT = $xL;
5709					$xB = $xR;
5710					$yR = $yT;
5711					$yB = $yL;
5712					$w -= $this->LineWidth;
5713					$h -= $this->LineWidth;
5714					break;
5715				}
5716				case 'normal':
5717				default: {
5718					$xL = $xeL;
5719					$xT = $xeT;
5720					$xB = $xeB;
5721					$xR = $xeR;
5722					$yL = $yeL;
5723					$yT = $yeT;
5724					$yB = $yeB;
5725					$yR = $yeR;
5726					break;
5727				}
5728			}
5729			// draw borders by case
5730			if (strlen($border) == 4) {
5731				$s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k));
5732			} elseif (strlen($border) == 3) {
5733				if (strpos($border,'B') === false) { // LTR
5734					$s .= sprintf('%F %F m ', $xL, $yL);
5735					$s .= sprintf('%F %F l ', $xT, $yT);
5736					$s .= sprintf('%F %F l ', $xR, $yR);
5737					$s .= sprintf('%F %F l ', $xB, $yB);
5738					$s .= 'S ';
5739				} elseif (strpos($border,'L') === false) { // TRB
5740					$s .= sprintf('%F %F m ', $xT, $yT);
5741					$s .= sprintf('%F %F l ', $xR, $yR);
5742					$s .= sprintf('%F %F l ', $xB, $yB);
5743					$s .= sprintf('%F %F l ', $xL, $yL);
5744					$s .= 'S ';
5745				} elseif (strpos($border,'T') === false) { // RBL
5746					$s .= sprintf('%F %F m ', $xR, $yR);
5747					$s .= sprintf('%F %F l ', $xB, $yB);
5748					$s .= sprintf('%F %F l ', $xL, $yL);
5749					$s .= sprintf('%F %F l ', $xT, $yT);
5750					$s .= 'S ';
5751				} elseif (strpos($border,'R') === false) { // BLT
5752					$s .= sprintf('%F %F m ', $xB, $yB);
5753					$s .= sprintf('%F %F l ', $xL, $yL);
5754					$s .= sprintf('%F %F l ', $xT, $yT);
5755					$s .= sprintf('%F %F l ', $xR, $yR);
5756					$s .= 'S ';
5757				}
5758			} elseif (strlen($border) == 2) {
5759				if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
5760					$s .= sprintf('%F %F m ', $xL, $yL);
5761					$s .= sprintf('%F %F l ', $xT, $yT);
5762					$s .= sprintf('%F %F l ', $xR, $yR);
5763					$s .= 'S ';
5764				} elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
5765					$s .= sprintf('%F %F m ', $xT, $yT);
5766					$s .= sprintf('%F %F l ', $xR, $yR);
5767					$s .= sprintf('%F %F l ', $xB, $yB);
5768					$s .= 'S ';
5769				} elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
5770					$s .= sprintf('%F %F m ', $xR, $yR);
5771					$s .= sprintf('%F %F l ', $xB, $yB);
5772					$s .= sprintf('%F %F l ', $xL, $yL);
5773					$s .= 'S ';
5774				} elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
5775					$s .= sprintf('%F %F m ', $xB, $yB);
5776					$s .= sprintf('%F %F l ', $xL, $yL);
5777					$s .= sprintf('%F %F l ', $xT, $yT);
5778					$s .= 'S ';
5779				} elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
5780					$s .= sprintf('%F %F m ', $xL, $yL);
5781					$s .= sprintf('%F %F l ', $xT, $yT);
5782					$s .= 'S ';
5783					$s .= sprintf('%F %F m ', $xR, $yR);
5784					$s .= sprintf('%F %F l ', $xB, $yB);
5785					$s .= 'S ';
5786				} elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
5787					$s .= sprintf('%F %F m ', $xT, $yT);
5788					$s .= sprintf('%F %F l ', $xR, $yR);
5789					$s .= 'S ';
5790					$s .= sprintf('%F %F m ', $xB, $yB);
5791					$s .= sprintf('%F %F l ', $xL, $yL);
5792					$s .= 'S ';
5793				}
5794			} else { // strlen($border) == 1
5795				if (strpos($border,'L') !== false) { // L
5796					$s .= sprintf('%F %F m ', $xL, $yL);
5797					$s .= sprintf('%F %F l ', $xT, $yT);
5798					$s .= 'S ';
5799				} elseif (strpos($border,'T') !== false) { // T
5800					$s .= sprintf('%F %F m ', $xT, $yT);
5801					$s .= sprintf('%F %F l ', $xR, $yR);
5802					$s .= 'S ';
5803				} elseif (strpos($border,'R') !== false) { // R
5804					$s .= sprintf('%F %F m ', $xR, $yR);
5805					$s .= sprintf('%F %F l ', $xB, $yB);
5806					$s .= 'S ';
5807				} elseif (strpos($border,'B') !== false) { // B
5808					$s .= sprintf('%F %F m ', $xB, $yB);
5809					$s .= sprintf('%F %F l ', $xL, $yL);
5810					$s .= 'S ';
5811				}
5812			}
5813			if (is_array($style) AND !empty($style)) {
5814				// reset border style to previous value
5815				$s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n";
5816			}
5817		}
5818		return $s;
5819	}
5820
5821	/**
5822	 * This method allows printing text with line breaks.
5823	 * They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.<br />
5824	 * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
5825	 * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
5826	 * @param float $h Cell minimum height. The cell extends automatically if needed.
5827	 * @param string $txt String to print
5828	 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5829	 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value when $ishtml=false)</li></ul>
5830	 * @param boolean $fill Indicates if the cell background must be painted (true) or transparent (false).
5831	 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul>
5832	 * @param float|null $x x position in user units
5833	 * @param float|null $y y position in user units
5834	 * @param boolean $reseth if true reset the last cell height (default true).
5835	 * @param int $stretch font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
5836	 * @param boolean $ishtml INTERNAL USE ONLY -- set to true if $txt is HTML content (default = false). Never set this parameter to true, use instead writeHTMLCell() or writeHTML() methods.
5837	 * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width.
5838	 * @param float $maxh maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature. This feature works only when $ishtml=false.
5839	 * @param string $valign Vertical alignment of text (requires $maxh = $h > 0). Possible values are:<ul><li>T: TOP</li><li>M: middle</li><li>B: bottom</li></ul>. This feature works only when $ishtml=false and the cell must fit in a single page.
5840	 * @param boolean $fitcell if true attempt to fit all the text within the cell by reducing the font size (do not work in HTML mode). $maxh must be greater than 0 and equal to $h.
5841	 * @return int Return the number of cells or 1 for html mode.
5842	 * @public
5843	 * @since 1.3
5844	 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
5845	 */
5846	public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false, $ln=1, $x=null, $y=null, $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $valign='T', $fitcell=false) {
5847		$prev_cell_margin = $this->cell_margin;
5848		$prev_cell_padding = $this->cell_padding;
5849		// adjust internal padding
5850		$this->adjustCellPadding($border);
5851		$mc_padding = $this->cell_padding;
5852		$mc_margin = $this->cell_margin;
5853		$this->cell_padding['T'] = 0;
5854		$this->cell_padding['B'] = 0;
5855		$this->setCellMargins(0, 0, 0, 0);
5856		if (TCPDF_STATIC::empty_string($this->lasth) OR $reseth) {
5857			// reset row height
5858			$this->resetLastH();
5859		}
5860		if (!TCPDF_STATIC::empty_string($y)) {
5861			$this->setY($y); // set y in order to convert negative y values to positive ones
5862		}
5863		$y = $this->GetY();
5864		$resth = 0;
5865		if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) {
5866			// spit cell in more pages/columns
5867			$newh = ($this->PageBreakTrigger - $y);
5868			$resth = ($h - $newh); // cell to be printed on the next page/column
5869			$h = $newh;
5870		}
5871		// get current page number
5872		$startpage = $this->page;
5873		// get current column
5874		$startcolumn = $this->current_column;
5875		if (!TCPDF_STATIC::empty_string($x)) {
5876			$this->setX($x);
5877		} else {
5878			$x = $this->GetX();
5879		}
5880		// check page for no-write regions and adapt page margins if necessary
5881		list($x, $y) = $this->checkPageRegions(0, $x, $y);
5882		// apply margins
5883		$oy = $y + $mc_margin['T'];
5884		if ($this->rtl) {
5885			$ox = ($this->w - $x - $mc_margin['R']);
5886		} else {
5887			$ox = ($x + $mc_margin['L']);
5888		}
5889		$this->x = $ox;
5890		$this->y = $oy;
5891		// set width
5892		if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5893			if ($this->rtl) {
5894				$w = ($this->x - $this->lMargin - $mc_margin['L']);
5895			} else {
5896				$w = ($this->w - $this->x - $this->rMargin - $mc_margin['R']);
5897			}
5898		}
5899		// store original margin values
5900		$lMargin = $this->lMargin;
5901		$rMargin = $this->rMargin;
5902		if ($this->rtl) {
5903			$this->rMargin = ($this->w - $this->x);
5904			$this->lMargin = ($this->x - $w);
5905		} else {
5906			$this->lMargin = ($this->x);
5907			$this->rMargin = ($this->w - $this->x - $w);
5908		}
5909		$this->clMargin = $this->lMargin;
5910		$this->crMargin = $this->rMargin;
5911		if ($autopadding) {
5912			// add top padding
5913			$this->y += $mc_padding['T'];
5914		}
5915		if ($ishtml) { // ******* Write HTML text
5916			$this->writeHTML($txt, true, false, $reseth, true, $align);
5917			$nl = 1;
5918		} else { // ******* Write simple text
5919			$prev_FontSizePt = $this->FontSizePt;
5920			if ($fitcell) {
5921				// ajust height values
5922				$tobottom = ($this->h - $this->y - $this->bMargin - $this->cell_padding['T'] - $this->cell_padding['B']);
5923				$h = $maxh = max(min($h, $tobottom), min($maxh, $tobottom));
5924			}
5925			// vertical alignment
5926			if ($maxh > 0) {
5927				// get text height
5928				$text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5929				if ($fitcell AND ($text_height > $maxh) AND ($this->FontSizePt > 1)) {
5930					// try to reduce font size to fit text on cell (use a quick search algorithm)
5931					$fmin = 1;
5932					$fmax = $this->FontSizePt;
5933					$diff_epsilon = (1 / $this->k); // one point (min resolution)
5934					$maxit = (2 * min(100, max(10, intval($fmax)))); // max number of iterations
5935					while ($maxit >= 0) {
5936						$fmid = (($fmax + $fmin) / 2);
5937						$this->setFontSize($fmid, false);
5938						$this->resetLastH();
5939						$text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5940						$diff = ($maxh - $text_height);
5941						if ($diff >= 0) {
5942							if ($diff <= $diff_epsilon) {
5943								break;
5944							}
5945							$fmin = $fmid;
5946						} else {
5947							$fmax = $fmid;
5948						}
5949						--$maxit;
5950					}
5951					if ($maxit < 0) {
5952						// premature exit, we get the minimum font value to fit the cell
5953						$this->setFontSize($fmin);
5954						$this->resetLastH();
5955						$text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5956					} else {
5957						$this->setFontSize($fmid);
5958						$this->resetLastH();
5959					}
5960				}
5961				if ($text_height < $maxh) {
5962					if ($valign == 'M') {
5963						// text vertically centered
5964						$this->y += (($maxh - $text_height) / 2);
5965					} elseif ($valign == 'B') {
5966						// text vertically aligned on bottom
5967						$this->y += ($maxh - $text_height);
5968					}
5969				}
5970			}
5971			$nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
5972			if ($fitcell) {
5973				// restore font size
5974				$this->setFontSize($prev_FontSizePt);
5975			}
5976		}
5977		if ($autopadding) {
5978			// add bottom padding
5979			$this->y += $mc_padding['B'];
5980		}
5981		// Get end-of-text Y position
5982		$currentY = $this->y;
5983		// get latest page number
5984		$endpage = $this->page;
5985		if ($resth > 0) {
5986			$skip = ($endpage - $startpage);
5987			$tmpresth = $resth;
5988			while ($tmpresth > 0) {
5989				if ($skip <= 0) {
5990					// add a page (or trig AcceptPageBreak() for multicolumn mode)
5991					$this->checkPageBreak($this->PageBreakTrigger + 1);
5992				}
5993				if ($this->num_columns > 1) {
5994					$tmpresth -= ($this->h - $this->y - $this->bMargin);
5995				} else {
5996					$tmpresth -= ($this->h - $this->tMargin - $this->bMargin);
5997				}
5998				--$skip;
5999			}
6000			$currentY = $this->y;
6001			$endpage = $this->page;
6002		}
6003		// get latest column
6004		$endcolumn = $this->current_column;
6005		if ($this->num_columns == 0) {
6006			$this->num_columns = 1;
6007		}
6008		// disable page regions check
6009		$check_page_regions = $this->check_page_regions;
6010		$this->check_page_regions = false;
6011		// get border modes
6012		$border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
6013		$border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
6014		$border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
6015		// design borders around HTML cells.
6016		for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
6017			$ccode = '';
6018			$this->setPage($page);
6019			if ($this->num_columns < 2) {
6020				// single-column mode
6021				$this->setX($x);
6022				$this->y = $this->tMargin;
6023			}
6024			// account for margin changes
6025			if ($page > $startpage) {
6026				if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
6027					$this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
6028				} elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
6029					$this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
6030				}
6031			}
6032			if ($startpage == $endpage) {
6033				// single page
6034				for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
6035					if ($column != $this->current_column) {
6036						$this->selectColumn($column);
6037					}
6038					if ($this->rtl) {
6039						$this->x -= $mc_margin['R'];
6040					} else {
6041						$this->x += $mc_margin['L'];
6042					}
6043					if ($startcolumn == $endcolumn) { // single column
6044						$cborder = $border;
6045						$h = max($h, ($currentY - $oy));
6046						$this->y = $oy;
6047					} elseif ($column == $startcolumn) { // first column
6048						$cborder = $border_start;
6049						$this->y = $oy;
6050						$h = $this->h - $this->y - $this->bMargin;
6051					} elseif ($column == $endcolumn) { // end column
6052						$cborder = $border_end;
6053						$h = $currentY - $this->y;
6054						if ($resth > $h) {
6055							$h = $resth;
6056						}
6057					} else { // middle column
6058						$cborder = $border_middle;
6059						$h = $this->h - $this->y - $this->bMargin;
6060						$resth -= $h;
6061					}
6062					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6063				} // end for each column
6064			} elseif ($page == $startpage) { // first page
6065				for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
6066					if ($column != $this->current_column) {
6067						$this->selectColumn($column);
6068					}
6069					if ($this->rtl) {
6070						$this->x -= $mc_margin['R'];
6071					} else {
6072						$this->x += $mc_margin['L'];
6073					}
6074					if ($column == $startcolumn) { // first column
6075						$cborder = $border_start;
6076						$this->y = $oy;
6077						$h = $this->h - $this->y - $this->bMargin;
6078					} else { // middle column
6079						$cborder = $border_middle;
6080						$h = $this->h - $this->y - $this->bMargin;
6081						$resth -= $h;
6082					}
6083					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6084				} // end for each column
6085			} elseif ($page == $endpage) { // last page
6086				for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
6087					if ($column != $this->current_column) {
6088						$this->selectColumn($column);
6089					}
6090					if ($this->rtl) {
6091						$this->x -= $mc_margin['R'];
6092					} else {
6093						$this->x += $mc_margin['L'];
6094					}
6095					if ($column == $endcolumn) {
6096						// end column
6097						$cborder = $border_end;
6098						$h = $currentY - $this->y;
6099						if ($resth > $h) {
6100							$h = $resth;
6101						}
6102					} else {
6103						// middle column
6104						$cborder = $border_middle;
6105						$h = $this->h - $this->y - $this->bMargin;
6106						$resth -= $h;
6107					}
6108					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6109				} // end for each column
6110			} else { // middle page
6111				for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
6112					$this->selectColumn($column);
6113					if ($this->rtl) {
6114						$this->x -= $mc_margin['R'];
6115					} else {
6116						$this->x += $mc_margin['L'];
6117					}
6118					$cborder = $border_middle;
6119					$h = $this->h - $this->y - $this->bMargin;
6120					$resth -= $h;
6121					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6122				} // end for each column
6123			}
6124			if ($cborder OR $fill) {
6125				$offsetlen = strlen($ccode);
6126				// draw border and fill
6127				if ($this->inxobj) {
6128					// we are inside an XObject template
6129					if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
6130						$pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
6131						$pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
6132						$this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
6133					} else {
6134						$pagemark = $this->xobjects[$this->xobjid]['intmrk'];
6135						$this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
6136					}
6137					$pagebuff = $this->xobjects[$this->xobjid]['outdata'];
6138					$pstart = substr($pagebuff, 0, $pagemark);
6139					$pend = substr($pagebuff, $pagemark);
6140					$this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
6141				} else {
6142					if (end($this->transfmrk[$this->page]) !== false) {
6143						$pagemarkkey = key($this->transfmrk[$this->page]);
6144						$pagemark = $this->transfmrk[$this->page][$pagemarkkey];
6145						$this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
6146					} elseif ($this->InFooter) {
6147						$pagemark = $this->footerpos[$this->page];
6148						$this->footerpos[$this->page] += $offsetlen;
6149					} else {
6150						$pagemark = $this->intmrk[$this->page];
6151						$this->intmrk[$this->page] += $offsetlen;
6152					}
6153					$pagebuff = $this->getPageBuffer($this->page);
6154					$pstart = substr($pagebuff, 0, $pagemark);
6155					$pend = substr($pagebuff, $pagemark);
6156					$this->setPageBuffer($this->page, $pstart.$ccode.$pend);
6157				}
6158			}
6159		} // end for each page
6160		// restore page regions check
6161		$this->check_page_regions = $check_page_regions;
6162		// Get end-of-cell Y position
6163		$currentY = $this->GetY();
6164		// restore previous values
6165		if ($this->num_columns > 1) {
6166			$this->selectColumn();
6167		} else {
6168			// restore original margins
6169			$this->lMargin = $lMargin;
6170			$this->rMargin = $rMargin;
6171			if ($this->page > $startpage) {
6172				// check for margin variations between pages (i.e. booklet mode)
6173				$dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']);
6174				$dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']);
6175				if (($dl != 0) OR ($dr != 0)) {
6176					$this->lMargin += $dl;
6177					$this->rMargin += $dr;
6178				}
6179			}
6180		}
6181		if ($ln > 0) {
6182			//Go to the beginning of the next line
6183			$this->setY($currentY + $mc_margin['B']);
6184			if ($ln == 2) {
6185				$this->setX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6186			}
6187		} else {
6188			// go left or right by case
6189			$this->setPage($startpage);
6190			$this->y = $y;
6191			$this->setX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6192		}
6193		$this->setContentMark();
6194		$this->cell_padding = $prev_cell_padding;
6195		$this->cell_margin = $prev_cell_margin;
6196		$this->clMargin = $this->lMargin;
6197		$this->crMargin = $this->rMargin;
6198		return $nl;
6199	}
6200
6201	/**
6202	 * This method return the estimated number of lines for print a simple text string using Multicell() method.
6203	 * @param string $txt String for calculating his height
6204	 * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
6205	 * @param boolean $reseth if true reset the last cell height (default false).
6206	 * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width (default true).
6207	 * @param array|null $cellpadding Internal cell padding, if empty uses default cell padding.
6208	 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
6209	 * @return float Return the minimal height needed for multicell method for printing the $txt param.
6210	 * @author Alexander Escalona Fern\E1ndez, Nicola Asuni
6211	 * @public
6212	 * @since 4.5.011
6213	 */
6214	public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding=null, $border=0) {
6215		if ($txt === NULL) {
6216			return 0;
6217		}
6218		if ($txt === '') {
6219			// empty string
6220			return 1;
6221		}
6222		// adjust internal padding
6223		$prev_cell_padding = $this->cell_padding;
6224		$prev_lasth = $this->lasth;
6225		if (is_array($cellpadding)) {
6226			$this->cell_padding = $cellpadding;
6227		}
6228		$this->adjustCellPadding($border);
6229		if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
6230			if ($this->rtl) {
6231				$w = $this->x - $this->lMargin;
6232			} else {
6233				$w = $this->w - $this->rMargin - $this->x;
6234			}
6235		}
6236		$wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6237		if ($reseth) {
6238			// reset row height
6239			$this->resetLastH();
6240		}
6241		$lines = 1;
6242		$sum = 0;
6243		$chars = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont), $txt, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6244		$charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
6245		$length = count($chars);
6246		$lastSeparator = -1;
6247		for ($i = 0; $i < $length; ++$i) {
6248			$c = $chars[$i];
6249			$charWidth = $charsWidth[$i];
6250			if (($c != 160)
6251					AND (($c == 173)
6252						OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6253						OR (($c == 45)
6254							AND ($i > 0) AND ($i < ($length - 1))
6255							AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i - 1)], $this->isunicode))
6256							AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6257						)
6258					)
6259				) {
6260				$lastSeparator = $i;
6261			}
6262			if ((($sum + $charWidth) > $wmax) OR ($c == 10)) {
6263				++$lines;
6264				if ($c == 10) {
6265					$lastSeparator = -1;
6266					$sum = 0;
6267				} elseif ($lastSeparator != -1) {
6268					$i = $lastSeparator;
6269					$lastSeparator = -1;
6270					$sum = 0;
6271				} else {
6272					$sum = $charWidth;
6273				}
6274			} else {
6275				$sum += $charWidth;
6276			}
6277		}
6278		if ($chars[($length - 1)] == 10) {
6279			--$lines;
6280		}
6281		$this->cell_padding = $prev_cell_padding;
6282		$this->lasth = $prev_lasth;
6283		return $lines;
6284	}
6285
6286	/**
6287	 * This method return the estimated height needed for printing a simple text string using the Multicell() method.
6288	 * Generally, if you want to know the exact height for a block of content you can use the following alternative technique:
6289	 * @pre
6290	 *  // store current object
6291	 *  $pdf->startTransaction();
6292	 *  // store starting values
6293	 *  $start_y = $pdf->GetY();
6294	 *  $start_page = $pdf->getPage();
6295	 *  // call your printing functions with your parameters
6296	 *  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6297	 *  $pdf->MultiCell($w=0, $h=0, $txt, $border=1, $align='L', $fill=false, $ln=1, $x=null, $y=null, $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0);
6298	 *  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6299	 *  // get the new Y
6300	 *  $end_y = $pdf->GetY();
6301	 *  $end_page = $pdf->getPage();
6302	 *  // calculate height
6303	 *  $height = 0;
6304	 *  if ($end_page == $start_page) {
6305	 *  	$height = $end_y - $start_y;
6306	 *  } else {
6307	 *  	for ($page=$start_page; $page <= $end_page; ++$page) {
6308	 *  		$this->setPage($page);
6309	 *  		if ($page == $start_page) {
6310	 *  			// first page
6311	 *  			$height += $this->h - $start_y - $this->bMargin;
6312	 *  		} elseif ($page == $end_page) {
6313	 *  			// last page
6314	 *  			$height += $end_y - $this->tMargin;
6315	 *  		} else {
6316	 *  			$height += $this->h - $this->tMargin - $this->bMargin;
6317	 *  		}
6318	 *  	}
6319	 *  }
6320	 *  // restore previous object
6321	 *  $pdf = $pdf->rollbackTransaction();
6322	 *
6323	 * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
6324	 * @param string $txt String for calculating his height
6325	 * @param boolean $reseth if true reset the last cell height (default false).
6326	 * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width (default true).
6327	 * @param array|null $cellpadding Internal cell padding, if empty uses default cell padding.
6328	 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
6329	 * @return float Return the minimal height needed for multicell method for printing the $txt param.
6330	 * @author Nicola Asuni, Alexander Escalona Fern\E1ndez
6331	 * @public
6332	 */
6333	public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding=null, $border=0) {
6334		// adjust internal padding
6335		$prev_cell_padding = $this->cell_padding;
6336		$prev_lasth = $this->lasth;
6337		if (is_array($cellpadding)) {
6338			$this->cell_padding = $cellpadding;
6339		}
6340		$this->adjustCellPadding($border);
6341		$lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
6342		$height = $this->getCellHeight(($lines * $this->FontSize), $autopadding);
6343		$this->cell_padding = $prev_cell_padding;
6344		$this->lasth = $prev_lasth;
6345		return $height;
6346	}
6347
6348	/**
6349	 * This method prints text from the current position.<br />
6350	 * @param float $h Line height
6351	 * @param string $txt String to print
6352	 * @param mixed $link URL or identifier returned by AddLink()
6353	 * @param boolean $fill Indicates if the cell background must be painted (true) or transparent (false).
6354	 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
6355	 * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
6356	 * @param int $stretch font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
6357	 * @param boolean $firstline if true prints only the first line and return the remaining string.
6358	 * @param boolean $firstblock if true the string is the starting of a line.
6359	 * @param float $maxh maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature.
6360	 * @param float $wadj first line width will be reduced by this amount (used in HTML mode).
6361	 * @param array|null $margin margin array of the parent container
6362	 * @return mixed Return the number of cells or the remaining string if $firstline = true.
6363	 * @public
6364	 * @since 1.5
6365	 */
6366	public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin=null) {
6367		// check page for no-write regions and adapt page margins if necessary
6368		list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
6369		if (strlen($txt) == 0) {
6370			// fix empty text
6371			$txt = ' ';
6372		}
6373		if (!is_array($margin)) {
6374			// set default margins
6375			$margin = $this->cell_margin;
6376		}
6377		// remove carriage returns
6378		$s = str_replace("\r", '', $txt);
6379		// check if string contains arabic text
6380		if (preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_ARABIC, $s)) {
6381			$arabic = true;
6382		} else {
6383			$arabic = false;
6384		}
6385		// check if string contains RTL text
6386		if ($arabic OR ($this->tmprtl == 'R') OR preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $s)) {
6387			$rtlmode = true;
6388		} else {
6389			$rtlmode = false;
6390		}
6391		// get a char width
6392		$chrwidth = $this->GetCharWidth(46); // dot character
6393		// get array of unicode values
6394		$chars = TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont);
6395		// calculate maximum width for a single character on string
6396		$chrw = $this->GetArrStringWidth($chars, '', '', 0, true);
6397		array_walk($chrw, array($this, 'getRawCharWidth'));
6398		$maxchwidth = max($chrw);
6399		// get array of chars
6400		$uchars = TCPDF_FONTS::UTF8ArrayToUniArray($chars, $this->isunicode);
6401		// get the number of characters
6402		$nb = count($chars);
6403		// replacement for SHY character (minus symbol)
6404		$shy_replacement = 45;
6405		$shy_replacement_char = TCPDF_FONTS::unichr($shy_replacement, $this->isunicode);
6406		// widht for SHY replacement
6407		$shy_replacement_width = $this->GetCharWidth($shy_replacement);
6408		// page width
6409		$pw = $w = $this->w - $this->lMargin - $this->rMargin;
6410		// calculate remaining line width ($w)
6411		if ($this->rtl) {
6412			$w = $this->x - $this->lMargin;
6413		} else {
6414			$w = $this->w - $this->rMargin - $this->x;
6415		}
6416		// max column width
6417		$wmax = ($w - $wadj);
6418		if (!$firstline) {
6419			$wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']);
6420		}
6421		if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) {
6422			// the maximum width character do not fit on column
6423			return '';
6424		}
6425		// minimum row height
6426		$row_height = max($h, $this->getCellHeight($this->FontSize));
6427		// max Y
6428		$maxy = $this->y + $maxh - max($row_height, $h);
6429		$start_page = $this->page;
6430		$i = 0; // character position
6431		$j = 0; // current starting position
6432		$sep = -1; // position of the last blank space
6433		$prevsep = $sep; // previous separator
6434		$shy = false; // true if the last blank is a soft hypen (SHY)
6435		$prevshy = $shy; // previous shy mode
6436		$l = 0; // current string length
6437		$nl = 0; //number of lines
6438		$linebreak = false;
6439		$pc = 0; // previous character
6440		// for each character
6441		while ($i < $nb) {
6442			if (($maxh > 0) AND ($this->y > $maxy) ) {
6443				break;
6444			}
6445			//Get the current character
6446			$c = $chars[$i];
6447			if ($c == 10) { // 10 = "\n" = new line
6448				//Explicit line break
6449				if ($align == 'J') {
6450					if ($this->rtl) {
6451						$talign = 'R';
6452					} else {
6453						$talign = 'L';
6454					}
6455				} else {
6456					$talign = $align;
6457				}
6458				$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6459				if ($firstline) {
6460					$startx = $this->x;
6461					$tmparr = array_slice($chars, $j, ($i - $j));
6462					if ($rtlmode) {
6463						$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6464					}
6465					$linew = $this->GetArrStringWidth($tmparr);
6466					unset($tmparr);
6467					if ($this->rtl) {
6468						$this->endlinex = $startx - $linew;
6469					} else {
6470						$this->endlinex = $startx + $linew;
6471					}
6472					$w = $linew;
6473					$tmpcellpadding = $this->cell_padding;
6474					if ($maxh == 0) {
6475						$this->setCellPadding(0);
6476					}
6477				}
6478				if ($firstblock AND $this->isRTLTextDir()) {
6479					$tmpstr = $this->stringRightTrim($tmpstr);
6480				}
6481				// Skip newlines at the beginning of a page or column
6482				if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) {
6483					$this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
6484				}
6485				unset($tmpstr);
6486				if ($firstline) {
6487					$this->cell_padding = $tmpcellpadding;
6488					return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6489				}
6490				++$nl;
6491				$j = $i + 1;
6492				$l = 0;
6493				$sep = -1;
6494				$prevsep = $sep;
6495				$shy = false;
6496				// account for margin changes
6497				if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6498					$this->AcceptPageBreak();
6499					if ($this->rtl) {
6500						$this->x -= $margin['R'];
6501					} else {
6502						$this->x += $margin['L'];
6503					}
6504					$this->lMargin += $margin['L'];
6505					$this->rMargin += $margin['R'];
6506				}
6507				$w = $this->getRemainingWidth();
6508				$wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']);
6509			} else {
6510				// 160 is the non-breaking space.
6511				// 173 is SHY (Soft Hypen).
6512				// \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
6513				// \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
6514				// \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
6515				if (($c != 160)
6516					AND (($c == 173)
6517						OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6518						OR (($c == 45)
6519							AND ($i < ($nb - 1))
6520							AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($pc, $this->isunicode))
6521							AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6522						)
6523					)
6524				) {
6525					// update last blank space position
6526					$prevsep = $sep;
6527					$sep = $i;
6528					// check if is a SHY
6529					if (($c == 173) OR ($c == 45)) {
6530						$prevshy = $shy;
6531						$shy = true;
6532						if ($pc == 45) {
6533							$tmp_shy_replacement_width = 0;
6534							$tmp_shy_replacement_char = '';
6535						} else {
6536							$tmp_shy_replacement_width = $shy_replacement_width;
6537							$tmp_shy_replacement_char = $shy_replacement_char;
6538						}
6539					} else {
6540						$shy = false;
6541					}
6542				}
6543				// update string length
6544				if ($this->isUnicodeFont() AND ($arabic)) {
6545					// with bidirectional algorithm some chars may be changed affecting the line length
6546					// *** very slow ***
6547					$l = $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl, $this->isunicode, $this->CurrentFont));
6548				} else {
6549					$l += $this->GetCharWidth($c, ($i+1 < $nb));
6550				}
6551				if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) >= $wmax))) {
6552					if (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) {
6553						$sep = $prevsep;
6554						$shy = $prevshy;
6555					}
6556					// we have reached the end of column
6557					if ($sep == -1) {
6558						// check if the line was already started
6559						if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth)))
6560							OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) {
6561							// print a void cell and go to next line
6562							$this->Cell($w, $h, '', 0, 1);
6563							$linebreak = true;
6564							if ($firstline) {
6565								return (TCPDF_FONTS::UniArrSubString($uchars, $j));
6566							}
6567						} else {
6568							// truncate the word because do not fit on column
6569							$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6570							if ($firstline) {
6571								$startx = $this->x;
6572								$tmparr = array_slice($chars, $j, ($i - $j));
6573								if ($rtlmode) {
6574									$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6575								}
6576								$linew = $this->GetArrStringWidth($tmparr);
6577								unset($tmparr);
6578								if ($this->rtl) {
6579									$this->endlinex = $startx - $linew;
6580								} else {
6581									$this->endlinex = $startx + $linew;
6582								}
6583								$w = $linew;
6584								$tmpcellpadding = $this->cell_padding;
6585								if ($maxh == 0) {
6586									$this->setCellPadding(0);
6587								}
6588							}
6589							if ($firstblock AND $this->isRTLTextDir()) {
6590								$tmpstr = $this->stringRightTrim($tmpstr);
6591							}
6592							$this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6593							unset($tmpstr);
6594							if ($firstline) {
6595								$this->cell_padding = $tmpcellpadding;
6596								return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6597							}
6598							$j = $i;
6599							--$i;
6600						}
6601					} else {
6602						// word wrapping
6603						if ($this->rtl AND (!$firstblock) AND ($sep < $i)) {
6604							$endspace = 1;
6605						} else {
6606							$endspace = 0;
6607						}
6608						// check the length of the next string
6609						$strrest = TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace));
6610						$nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $this->stringTrim($strrest));
6611						if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
6612							// truncate the word because do not fit on a full page width
6613							$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6614							if ($firstline) {
6615								$startx = $this->x;
6616								$tmparr = array_slice($chars, $j, ($i - $j));
6617								if ($rtlmode) {
6618									$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6619								}
6620								$linew = $this->GetArrStringWidth($tmparr);
6621								unset($tmparr);
6622								if ($this->rtl) {
6623									$this->endlinex = ($startx - $linew);
6624								} else {
6625									$this->endlinex = ($startx + $linew);
6626								}
6627								$w = $linew;
6628								$tmpcellpadding = $this->cell_padding;
6629								if ($maxh == 0) {
6630									$this->setCellPadding(0);
6631								}
6632							}
6633							if ($firstblock AND $this->isRTLTextDir()) {
6634								$tmpstr = $this->stringRightTrim($tmpstr);
6635							}
6636							$this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6637							unset($tmpstr);
6638							if ($firstline) {
6639								$this->cell_padding = $tmpcellpadding;
6640								return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6641							}
6642							$j = $i;
6643							--$i;
6644						} else {
6645							// word wrapping
6646							if ($shy) {
6647								// add hypen (minus symbol) at the end of the line
6648								$shy_width = $tmp_shy_replacement_width;
6649								if ($this->rtl) {
6650									$shy_char_left = $tmp_shy_replacement_char;
6651									$shy_char_right = '';
6652								} else {
6653									$shy_char_left = '';
6654									$shy_char_right = $tmp_shy_replacement_char;
6655								}
6656							} else {
6657								$shy_width = 0;
6658								$shy_char_left = '';
6659								$shy_char_right = '';
6660							}
6661							$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, ($sep + $endspace));
6662							if ($firstline) {
6663								$startx = $this->x;
6664								$tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
6665								if ($rtlmode) {
6666									$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6667								}
6668								$linew = $this->GetArrStringWidth($tmparr);
6669								unset($tmparr);
6670								if ($this->rtl) {
6671									$this->endlinex = $startx - $linew - $shy_width;
6672								} else {
6673									$this->endlinex = $startx + $linew + $shy_width;
6674								}
6675								$w = $linew;
6676								$tmpcellpadding = $this->cell_padding;
6677								if ($maxh == 0) {
6678									$this->setCellPadding(0);
6679								}
6680							}
6681							// print the line
6682							if ($firstblock AND $this->isRTLTextDir()) {
6683								$tmpstr = $this->stringRightTrim($tmpstr);
6684							}
6685							$this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
6686							unset($tmpstr);
6687							if ($firstline) {
6688								if ($chars[$sep] == 45) {
6689									$endspace += 1;
6690								}
6691								// return the remaining text
6692								$this->cell_padding = $tmpcellpadding;
6693								return (TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace)));
6694							}
6695							$i = $sep;
6696							$sep = -1;
6697							$shy = false;
6698							$j = ($i + 1);
6699						}
6700					}
6701					// account for margin changes
6702					if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6703						$this->AcceptPageBreak();
6704						if ($this->rtl) {
6705							$this->x -= $margin['R'];
6706						} else {
6707							$this->x += $margin['L'];
6708						}
6709						$this->lMargin += $margin['L'];
6710						$this->rMargin += $margin['R'];
6711					}
6712					$w = $this->getRemainingWidth();
6713					$wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6714					if ($linebreak) {
6715						$linebreak = false;
6716					} else {
6717						++$nl;
6718						$l = 0;
6719					}
6720				}
6721			}
6722			// save last character
6723			$pc = $c;
6724			++$i;
6725		} // end while i < nb
6726		// print last substring (if any)
6727		if ($l > 0) {
6728			switch ($align) {
6729				case 'J':
6730				case 'C': {
6731					break;
6732				}
6733				case 'L': {
6734					if (!$this->rtl) {
6735						$w = $l;
6736					}
6737					break;
6738				}
6739				case 'R': {
6740					if ($this->rtl) {
6741						$w = $l;
6742					}
6743					break;
6744				}
6745				default: {
6746					$w = $l;
6747					break;
6748				}
6749			}
6750			$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $nb);
6751			if ($firstline) {
6752				$startx = $this->x;
6753				$tmparr = array_slice($chars, $j, ($nb - $j));
6754				if ($rtlmode) {
6755					$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6756				}
6757				$linew = $this->GetArrStringWidth($tmparr);
6758				unset($tmparr);
6759				if ($this->rtl) {
6760					$this->endlinex = $startx - $linew;
6761				} else {
6762					$this->endlinex = $startx + $linew;
6763				}
6764				$w = $linew;
6765				$tmpcellpadding = $this->cell_padding;
6766				if ($maxh == 0) {
6767					$this->setCellPadding(0);
6768				}
6769			}
6770			if ($firstblock AND $this->isRTLTextDir()) {
6771				$tmpstr = $this->stringRightTrim($tmpstr);
6772			}
6773			$this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
6774			unset($tmpstr);
6775			if ($firstline) {
6776				$this->cell_padding = $tmpcellpadding;
6777				return (TCPDF_FONTS::UniArrSubString($uchars, $nb));
6778			}
6779			++$nl;
6780		}
6781		if ($firstline) {
6782			return '';
6783		}
6784		return $nl;
6785	}
6786
6787	/**
6788	 * Returns the remaining width between the current position and margins.
6789	 * @return float Return the remaining width
6790	 * @protected
6791	 */
6792	protected function getRemainingWidth() {
6793		list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y);
6794		if ($this->rtl) {
6795			return ($this->x - $this->lMargin);
6796		} else {
6797			return ($this->w - $this->rMargin - $this->x);
6798		}
6799	}
6800
6801	/**
6802	 * Set the block dimensions accounting for page breaks and page/column fitting
6803	 * @param float $w width
6804	 * @param float $h height
6805	 * @param float $x X coordinate
6806	 * @param float $y Y coodiante
6807	 * @param boolean $fitonpage if true the block is resized to not exceed page dimensions.
6808	 * @return array array($w, $h, $x, $y)
6809	 * @protected
6810	 * @since 5.5.009 (2010-07-05)
6811	 */
6812	protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
6813		if ($w <= 0) {
6814			// set maximum width
6815			$w = ($this->w - $this->lMargin - $this->rMargin);
6816			if ($w <= 0) {
6817				$w = 1;
6818			}
6819		}
6820		if ($h <= 0) {
6821			// set maximum height
6822			$h = ($this->PageBreakTrigger - $this->tMargin);
6823			if ($h <= 0) {
6824				$h = 1;
6825			}
6826		}
6827		// resize the block to be vertically contained on a single page or single column
6828		if ($fitonpage OR $this->AutoPageBreak) {
6829			$ratio_wh = ($w / $h);
6830			if ($h > ($this->PageBreakTrigger - $this->tMargin)) {
6831				$h = $this->PageBreakTrigger - $this->tMargin;
6832				$w = ($h * $ratio_wh);
6833			}
6834			// resize the block to be horizontally contained on a single page or single column
6835			if ($fitonpage) {
6836				$maxw = ($this->w - $this->lMargin - $this->rMargin);
6837				if ($w > $maxw) {
6838					$w = $maxw;
6839					$h = ($w / $ratio_wh);
6840				}
6841			}
6842		}
6843		// Check whether we need a new page or new column first as this does not fit
6844		$prev_x = $this->x;
6845		$prev_y = $this->y;
6846		if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) {
6847			$y = $this->y;
6848			if ($this->rtl) {
6849				$x += ($prev_x - $this->x);
6850			} else {
6851				$x += ($this->x - $prev_x);
6852			}
6853			$this->newline = true;
6854		}
6855		// resize the block to be contained on the remaining available page or column space
6856		if ($fitonpage) {
6857			$ratio_wh = ($w / $h);
6858			if (($y + $h) > $this->PageBreakTrigger) {
6859				$h = $this->PageBreakTrigger - $y;
6860				$w = ($h * $ratio_wh);
6861			}
6862			if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
6863				$w = $this->w - $this->rMargin - $x;
6864				$h = ($w / $ratio_wh);
6865			} elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
6866				$w = $x - $this->lMargin;
6867				$h = ($w / $ratio_wh);
6868			}
6869		}
6870		return array($w, $h, $x, $y);
6871	}
6872
6873	/**
6874	 * Puts an image in the page.
6875	 * The upper-left corner must be given.
6876	 * The dimensions can be specified in different ways:<ul>
6877	 * <li>explicit width and height (expressed in user unit)</li>
6878	 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
6879	 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
6880	 * Supported formats are JPEG and PNG images whitout GD library and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;
6881	 * The format can be specified explicitly or inferred from the file extension.<br />
6882	 * It is possible to put a link on the image.<br />
6883	 * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
6884	 * @param string $file Name of the file containing the image or a '@' character followed by the image data string. To link an image without embedding it on the document, set an asterisk character before the URL (i.e.: '*http://www.example.com/image.jpg').
6885	 * @param float|null $x Abscissa of the upper-left corner (LTR) or upper-right corner (RTL).
6886	 * @param float|null $y Ordinate of the upper-left corner (LTR) or upper-right corner (RTL).
6887	 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
6888	 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
6889	 * @param string $type Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension.
6890	 * @param mixed $link URL or identifier returned by AddLink().
6891	 * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
6892	 * @param mixed $resize If true resize (reduce) the image to fit $w and $h (requires GD or ImageMagick library); if false do not resize; if 2 force resize in all cases (upscaling and downscaling).
6893	 * @param int $dpi dot-per-inch resolution used on resize
6894	 * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
6895	 * @param boolean $ismask true if this image is a mask, false otherwise
6896	 * @param mixed $imgmask image object returned by this function or false
6897	 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
6898	 * @param mixed $fitbox If not false scale image dimensions proportionally to fit within the ($w, $h) box. $fitbox can be true or a 2 characters string indicating the image alignment inside the box. The first character indicate the horizontal alignment (L = left, C = center, R = right) the second character indicate the vertical algnment (T = top, M = middle, B = bottom).
6899	 * @param boolean $hidden If true do not display the image.
6900	 * @param boolean $fitonpage If true the image is resized to not exceed page dimensions.
6901	 * @param boolean $alt If true the image will be added as alternative and not directly printed (the ID of the image will be returned).
6902	 * @param array $altimgs Array of alternate images IDs. Each alternative image must be an array with two values: an integer representing the image ID (the value returned by the Image method) and a boolean value to indicate if the image is the default for printing.
6903	 * @return mixed|false image information
6904	 * @public
6905	 * @since 1.1
6906	 */
6907	public function Image($file, $x=null, $y=null, $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false, $hidden=false, $fitonpage=false, $alt=false, $altimgs=array()) {
6908		if ($this->state != 2) {
6909			return false;
6910		}
6911		if (TCPDF_STATIC::empty_string($x)) {
6912			$x = $this->x;
6913		}
6914		if (TCPDF_STATIC::empty_string($y)) {
6915			$y = $this->y;
6916		}
6917		// check page for no-write regions and adapt page margins if necessary
6918		list($x, $y) = $this->checkPageRegions($h, $x, $y);
6919		$exurl = ''; // external streams
6920		$imsize = FALSE;
6921
6922        // Make sure the file variable is not empty or null because accessing $file[0] later
6923        // results in error when running PHP 7.4
6924        if (empty($file)) {
6925            return false;
6926        }
6927		// check if we are passing an image as file or string
6928		if ($file[0] === '@') {
6929			// image from string
6930			$imgdata = substr($file, 1);
6931		} else { // image file
6932			if ($file[0] === '*') {
6933				// image as external stream
6934				$file = substr($file, 1);
6935				$exurl = $file;
6936			}
6937			// check if file exist and it is valid
6938			if (!@$this->fileExists($file)) {
6939				return false;
6940			}
6941            if (false !== $info = $this->getImageBuffer($file)) {
6942                $imsize = array($info['w'], $info['h']);
6943            } elseif (($imsize = @getimagesize($file)) === FALSE && strpos($file, '__tcpdf_'.$this->file_id.'_img') === FALSE){
6944                $imgdata = $this->getCachedFileContents($file);
6945            }
6946		}
6947		if (!empty($imgdata)) {
6948			// copy image to cache
6949			$original_file = $file;
6950			$file = TCPDF_STATIC::getObjFilename('img', $this->file_id);
6951			$fp = TCPDF_STATIC::fopenLocal($file, 'w');
6952			if (!$fp) {
6953				$this->Error('Unable to write file: '.$file);
6954			}
6955			fwrite($fp, $imgdata);
6956			fclose($fp);
6957			unset($imgdata);
6958			$imsize = @getimagesize($file);
6959			if ($imsize === FALSE) {
6960				unlink($file);
6961				$file = $original_file;
6962			}
6963		}
6964		if ($imsize === FALSE) {
6965			if (($w > 0) AND ($h > 0)) {
6966				// get measures from specified data
6967				$pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6968				$ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6969				$imsize = array($pw, $ph);
6970			} else {
6971				$this->Error('[Image] Unable to get the size of the image: '.$file);
6972			}
6973		}
6974		// file hash
6975		$filehash = md5($file);
6976		// get original image width and height in pixels
6977		list($pixw, $pixh) = $imsize;
6978		// calculate image width and height on document
6979		if (($w <= 0) AND ($h <= 0)) {
6980			// convert image size to document unit
6981			$w = $this->pixelsToUnits($pixw);
6982			$h = $this->pixelsToUnits($pixh);
6983		} elseif ($w <= 0) {
6984			$w = $h * $pixw / $pixh;
6985		} elseif ($h <= 0) {
6986			$h = $w * $pixh / $pixw;
6987		} elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
6988			if (strlen($fitbox) !== 2) {
6989				// set default alignment
6990				$fitbox = '--';
6991			}
6992			// scale image dimensions proportionally to fit within the ($w, $h) box
6993			if ((($w * $pixh) / ($h * $pixw)) < 1) {
6994				// store current height
6995				$oldh = $h;
6996				// calculate new height
6997				$h = $w * $pixh / $pixw;
6998				// height difference
6999				$hdiff = ($oldh - $h);
7000				// vertical alignment
7001				switch (strtoupper($fitbox[1])) {
7002					case 'T': {
7003						break;
7004					}
7005					case 'M': {
7006						$y += ($hdiff / 2);
7007						break;
7008					}
7009					case 'B': {
7010						$y += $hdiff;
7011						break;
7012					}
7013				}
7014			} else {
7015				// store current width
7016				$oldw = $w;
7017				// calculate new width
7018				$w = $h * $pixw / $pixh;
7019				// width difference
7020				$wdiff = ($oldw - $w);
7021				// horizontal alignment
7022				switch (strtoupper($fitbox[0])) {
7023					case 'L': {
7024						if ($this->rtl) {
7025							$x -= $wdiff;
7026						}
7027						break;
7028					}
7029					case 'C': {
7030						if ($this->rtl) {
7031							$x -= ($wdiff / 2);
7032						} else {
7033							$x += ($wdiff / 2);
7034						}
7035						break;
7036					}
7037					case 'R': {
7038						if (!$this->rtl) {
7039							$x += $wdiff;
7040						}
7041						break;
7042					}
7043				}
7044			}
7045		}
7046		// fit the image on available space
7047		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
7048		// calculate new minimum dimensions in pixels
7049		$neww = round($w * $this->k * $dpi / $this->dpi);
7050		$newh = round($h * $this->k * $dpi / $this->dpi);
7051		// check if resize is necessary (resize is used only to reduce the image)
7052		$newsize = ($neww * $newh);
7053		$pixsize = ($pixw * $pixh);
7054		if (intval($resize) == 2) {
7055			$resize = true;
7056		} elseif ($newsize >= $pixsize) {
7057			$resize = false;
7058		}
7059		// check if image has been already added on document
7060		$newimage = true;
7061		if (in_array($file, $this->imagekeys)) {
7062			$newimage = false;
7063			// get existing image data
7064			$info = $this->getImageBuffer($file);
7065			if (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE) {
7066				// check if the newer image is larger
7067				$oldsize = ($info['w'] * $info['h']);
7068				if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7069					$newimage = true;
7070				}
7071			}
7072		} elseif (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE)) {
7073			// create temp image file (without alpha channel)
7074			$tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash;
7075			// create temp alpha file
7076			$tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash;
7077			// check for cached images
7078			if (in_array($tempfile_plain, $this->imagekeys)) {
7079				// get existing image data
7080				$info = $this->getImageBuffer($tempfile_plain);
7081				// check if the newer image is larger
7082				$oldsize = ($info['w'] * $info['h']);
7083				if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7084					$newimage = true;
7085				} else {
7086					$newimage = false;
7087					// embed mask image
7088					$imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7089					// embed image, masked with previously embedded mask
7090					return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7091				}
7092			}
7093		}
7094		if ($newimage) {
7095			//First use of image, get info
7096			$type = strtolower($type);
7097			if ($type == '') {
7098				$type = TCPDF_IMAGES::getImageFileType($file, $imsize);
7099			} elseif ($type == 'jpg') {
7100				$type = 'jpeg';
7101			}
7102			$mqr = TCPDF_STATIC::get_mqr();
7103			TCPDF_STATIC::set_mqr(false);
7104			// Specific image handlers (defined on TCPDF_IMAGES CLASS)
7105			$mtd = '_parse'.$type;
7106			// GD image handler function
7107			$gdfunction = 'imagecreatefrom'.$type;
7108			$info = false;
7109			if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
7110				// TCPDF image functions
7111				$info = TCPDF_IMAGES::$mtd($file);
7112				if (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE)
7113					AND (($info === 'pngalpha') OR (isset($info['trns']) AND !empty($info['trns'])))) {
7114					return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
7115				}
7116			}
7117			if (($info === false) AND function_exists($gdfunction)) {
7118				try {
7119					// GD library
7120					$img = $gdfunction($file);
7121					if ($img !== false) {
7122						if ($resize) {
7123							$imgr = imagecreatetruecolor($neww, $newh);
7124							if (($type == 'gif') OR ($type == 'png')) {
7125								$imgr = TCPDF_IMAGES::setGDImageTransparency($imgr, $img);
7126							}
7127							imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
7128							$img = $imgr;
7129						}
7130						if (($type == 'gif') OR ($type == 'png')) {
7131							$info = TCPDF_IMAGES::_toPNG($img, TCPDF_STATIC::getObjFilename('img', $this->file_id));
7132						} else {
7133							$info = TCPDF_IMAGES::_toJPEG($img, $this->jpeg_quality, TCPDF_STATIC::getObjFilename('img', $this->file_id));
7134						}
7135					}
7136				} catch(Exception $e) {
7137					$info = false;
7138				}
7139			}
7140			if (($info === false) AND extension_loaded('imagick')) {
7141				try {
7142					// ImageMagick library
7143					$img = new Imagick();
7144					if ($type == 'svg') {
7145						if ($file[0] === '@') {
7146							// image from string
7147							$svgimg = substr($file, 1);
7148						} else {
7149							// get SVG file content
7150                            $svgimg = $this->getCachedFileContents($file);
7151						}
7152						if ($svgimg !== FALSE) {
7153							// get width and height
7154							$regs = array();
7155							if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) {
7156								$svgtag = $regs[1];
7157								$tmp = array();
7158								if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7159									$ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7160									$owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit;
7161									$svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
7162								} else {
7163									$ow = $w;
7164								}
7165								$tmp = array();
7166								if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7167									$oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7168									$ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit;
7169									$svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
7170								} else {
7171									$oh = $h;
7172								}
7173								$tmp = array();
7174								if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
7175									$vbw = ($ow * $this->imgscale * $this->k);
7176									$vbh = ($oh * $this->imgscale * $this->k);
7177									$vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh);
7178									$svgtag = $vbox.$svgtag;
7179								}
7180								$svgimg = preg_replace('/<svg([^\>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
7181							}
7182							$img->readImageBlob($svgimg);
7183						}
7184					} else {
7185						$img->readImage($file);
7186					}
7187					if ($resize) {
7188						$img->resizeImage($neww, $newh, 10, 1, false);
7189					}
7190					$img->setCompressionQuality($this->jpeg_quality);
7191					$img->setImageFormat('jpeg');
7192					$tempname = TCPDF_STATIC::getObjFilename('img', $this->file_id);
7193					$img->writeImage($tempname);
7194					$info = TCPDF_IMAGES::_parsejpeg($tempname);
7195					unlink($tempname);
7196					$img->destroy();
7197				} catch(Exception $e) {
7198					$info = false;
7199				}
7200			}
7201			if ($info === false) {
7202				// unable to process image
7203				return false;
7204			}
7205			TCPDF_STATIC::set_mqr($mqr);
7206			if ($ismask) {
7207				// force grayscale
7208				$info['cs'] = 'DeviceGray';
7209			}
7210			if ($imgmask !== false) {
7211				$info['masked'] = $imgmask;
7212			}
7213			if (!empty($exurl)) {
7214				$info['exurl'] = $exurl;
7215			}
7216			// array of alternative images
7217			$info['altimgs'] = $altimgs;
7218			// add image to document
7219			$info['i'] = $this->setImageBuffer($file, $info);
7220		}
7221		// set alignment
7222		$this->img_rb_x = $x + $w;
7223		$this->img_rb_y = $y + $h;
7224
7225		// set alignment
7226		if ($palign == 'L') {
7227			$ximg = $this->lMargin;
7228		} elseif ($palign == 'C') {
7229			$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7230		} elseif ($palign == 'R') {
7231			$ximg = $this->w - $this->rMargin - $w;
7232		} else {
7233			$ximg = $x;
7234		}
7235
7236		if ($ismask OR $hidden) {
7237			// image is not displayed
7238			return $info['i'];
7239		}
7240		$xkimg = $ximg * $this->k;
7241		if (!$alt) {
7242			// only non-alternative immages will be set
7243			$this->_out(sprintf('q %F 0 0 %F %F %F cm /I%u Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i']));
7244		}
7245		if (!empty($border)) {
7246			$bx = $this->x;
7247			$by = $this->y;
7248			$this->x = $ximg;
7249			if ($this->rtl) {
7250				$this->x += $w;
7251			}
7252			$this->y = $y;
7253			$this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
7254			$this->x = $bx;
7255			$this->y = $by;
7256		}
7257		if ($link) {
7258			$this->Link($ximg, $y, $w, $h, $link, 0);
7259		}
7260		// set pointer to align the next text/objects
7261		switch($align) {
7262			case 'T': {
7263				$this->y = $y;
7264				$this->x = $this->img_rb_x;
7265				break;
7266			}
7267			case 'M': {
7268				$this->y = $y + round($h/2);
7269				$this->x = $this->img_rb_x;
7270				break;
7271			}
7272			case 'B': {
7273				$this->y = $this->img_rb_y;
7274				$this->x = $this->img_rb_x;
7275				break;
7276			}
7277			case 'N': {
7278				$this->setY($this->img_rb_y);
7279				break;
7280			}
7281			default:{
7282				break;
7283			}
7284		}
7285		$this->endlinex = $this->img_rb_x;
7286		if ($this->inxobj) {
7287			// we are inside an XObject template
7288			$this->xobjects[$this->xobjid]['images'][] = $info['i'];
7289		}
7290		return $info['i'];
7291	}
7292
7293	/**
7294	 * Extract info from a PNG image with alpha channel using the Imagick or GD library.
7295	 * @param string $file Name of the file containing the image.
7296	 * @param float $x Abscissa of the upper-left corner.
7297	 * @param float $y Ordinate of the upper-left corner.
7298	 * @param float $wpx Original width of the image in pixels.
7299	 * @param float $hpx original height of the image in pixels.
7300	 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
7301	 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
7302	 * @param string $type Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension.
7303	 * @param mixed $link URL or identifier returned by AddLink().
7304	 * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
7305	 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).
7306	 * @param int $dpi dot-per-inch resolution used on resize
7307	 * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
7308	 * @param string $filehash File hash used to build unique file names.
7309	 * @author Nicola Asuni
7310	 * @protected
7311	 * @since 4.3.007 (2008-12-04)
7312	 * @see Image()
7313	 */
7314	protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
7315		// create temp images
7316		if (empty($filehash)) {
7317			$filehash = md5($file);
7318		}
7319		// create temp image file (without alpha channel)
7320		$tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash;
7321		// create temp alpha file
7322		$tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash;
7323		$parsed = false;
7324		$parse_error = '';
7325		// ImageMagick extension
7326		if (($parsed === false) AND extension_loaded('imagick')) {
7327			try {
7328				// ImageMagick library
7329				$img = new Imagick();
7330				$img->readImage($file);
7331				// clone image object
7332				$imga = TCPDF_STATIC::objclone($img);
7333				// extract alpha channel
7334				if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) {
7335					$img->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT);
7336				} else {
7337					$img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
7338					$img->negateImage(true);
7339				}
7340				$img->setImageFormat('png');
7341				$img->writeImage($tempfile_alpha);
7342				// remove alpha channel
7343				if (method_exists($imga, 'setImageMatte')) {
7344					$imga->setImageMatte(false);
7345				} else {
7346					$imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
7347				}
7348				$imga->setImageFormat('png');
7349				$imga->writeImage($tempfile_plain);
7350				$parsed = true;
7351			} catch (Exception $e) {
7352				// Imagemagick fails, try with GD
7353				$parse_error = 'Imagick library error: '.$e->getMessage();
7354			}
7355		}
7356		// GD extension
7357		if (($parsed === false) AND function_exists('imagecreatefrompng')) {
7358			try {
7359				// generate images
7360				$img = imagecreatefrompng($file);
7361				$imgalpha = imagecreate($wpx, $hpx);
7362				// generate gray scale palette (0 -> 255)
7363				for ($c = 0; $c < 256; ++$c) {
7364					ImageColorAllocate($imgalpha, $c, $c, $c);
7365				}
7366				// extract alpha channel
7367				for ($xpx = 0; $xpx < $wpx; ++$xpx) {
7368					for ($ypx = 0; $ypx < $hpx; ++$ypx) {
7369						$color = imagecolorat($img, $xpx, $ypx);
7370						// get and correct gamma color
7371						$alpha = $this->getGDgamma($img, $color);
7372						imagesetpixel($imgalpha, (int) $xpx, (int) $ypx, (int) $alpha);
7373					}
7374				}
7375				imagepng($imgalpha, $tempfile_alpha);
7376				imagedestroy($imgalpha);
7377				// extract image without alpha channel
7378				$imgplain = imagecreatetruecolor($wpx, $hpx);
7379				imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
7380				imagepng($imgplain, $tempfile_plain);
7381				imagedestroy($imgplain);
7382				$parsed = true;
7383			} catch (Exception $e) {
7384				// GD fails
7385				$parse_error = 'GD library error: '.$e->getMessage();
7386			}
7387		}
7388		if ($parsed === false) {
7389			if (empty($parse_error)) {
7390				$this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
7391			} else {
7392				$this->Error($parse_error);
7393			}
7394		}
7395		// embed mask image
7396		$imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7397		// embed image, masked with previously embedded mask
7398		$this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7399	}
7400
7401	/**
7402	 * Get the GD-corrected PNG gamma value from alpha color
7403	 * @param resource $img GD image Resource ID.
7404	 * @param int $c alpha color
7405	 * @protected
7406	 * @since 4.3.007 (2008-12-04)
7407	 */
7408	protected function getGDgamma($img, $c) {
7409		if (!isset($this->gdgammacache['#'.$c])) {
7410			$colors = imagecolorsforindex($img, $c);
7411			// GD alpha is only 7 bit (0 -> 127)
7412			$this->gdgammacache['#'.$c] = (int) (((127 - $colors['alpha']) / 127) * 255);
7413			// correct gamma
7414			$this->gdgammacache['#'.$c] = (int) (pow(($this->gdgammacache['#'.$c] / 255), 2.2) * 255);
7415			// store the latest values on cache to improve performances
7416			if (count($this->gdgammacache) > 8) {
7417				// remove one element from the cache array
7418				array_shift($this->gdgammacache);
7419			}
7420		}
7421		return $this->gdgammacache['#'.$c];
7422	}
7423
7424	/**
7425	 * Performs a line break.
7426	 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
7427	 * @param float|null $h The height of the break. By default, the value equals the height of the last printed cell.
7428	 * @param boolean $cell if true add the current left (or right o for RTL) padding to the X coordinate
7429	 * @public
7430	 * @since 1.0
7431	 * @see Cell()
7432	 */
7433	public function Ln($h=null, $cell=false) {
7434		if (($this->num_columns > 1) AND ($this->y == $this->columns[$this->current_column]['y']) AND isset($this->columns[$this->current_column]['x']) AND ($this->x == $this->columns[$this->current_column]['x'])) {
7435			// revove vertical space from the top of the column
7436			return;
7437		}
7438		if ($cell) {
7439			if ($this->rtl) {
7440				$cellpadding = $this->cell_padding['R'];
7441			} else {
7442				$cellpadding = $this->cell_padding['L'];
7443			}
7444		} else {
7445			$cellpadding = 0;
7446		}
7447		if ($this->rtl) {
7448			$this->x = $this->w - $this->rMargin - $cellpadding;
7449		} else {
7450			$this->x = $this->lMargin + $cellpadding;
7451		}
7452		if (TCPDF_STATIC::empty_string($h)) {
7453			$h = $this->lasth;
7454		}
7455		$this->y += $h;
7456		$this->newline = true;
7457	}
7458
7459	/**
7460	 * Returns the relative X value of current position.
7461	 * The value is relative to the left border for LTR languages and to the right border for RTL languages.
7462	 * @return float
7463	 * @public
7464	 * @since 1.2
7465	 * @see SetX(), GetY(), SetY()
7466	 */
7467	public function GetX() {
7468		//Get x position
7469		if ($this->rtl) {
7470			return ($this->w - $this->x);
7471		} else {
7472			return $this->x;
7473		}
7474	}
7475
7476	/**
7477	 * Returns the absolute X value of current position.
7478	 * @return float
7479	 * @public
7480	 * @since 1.2
7481	 * @see SetX(), GetY(), SetY()
7482	 */
7483	public function GetAbsX() {
7484		return $this->x;
7485	}
7486
7487	/**
7488	 * Returns the ordinate of the current position.
7489	 * @return float
7490	 * @public
7491	 * @since 1.0
7492	 * @see SetY(), GetX(), SetX()
7493	 */
7494	public function GetY() {
7495		return $this->y;
7496	}
7497
7498	/**
7499	 * Defines the abscissa of the current position.
7500	 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
7501	 * @param float $x The value of the abscissa in user units.
7502	 * @param boolean $rtloff if true always uses the page top-left corner as origin of axis.
7503	 * @public
7504	 * @since 1.2
7505	 * @see GetX(), GetY(), SetY(), SetXY()
7506	 */
7507	public function setX($x, $rtloff=false) {
7508		$x = floatval($x);
7509		if (!$rtloff AND $this->rtl) {
7510			if ($x >= 0) {
7511				$this->x = $this->w - $x;
7512			} else {
7513				$this->x = abs($x);
7514			}
7515		} else {
7516			if ($x >= 0) {
7517				$this->x = $x;
7518			} else {
7519				$this->x = $this->w + $x;
7520			}
7521		}
7522		if ($this->x < 0) {
7523			$this->x = 0;
7524		}
7525		if ($this->x > $this->w) {
7526			$this->x = $this->w;
7527		}
7528	}
7529
7530	/**
7531	 * Moves the current abscissa back to the left margin and sets the ordinate.
7532	 * If the passed value is negative, it is relative to the bottom of the page.
7533	 * @param float $y The value of the ordinate in user units.
7534	 * @param bool $resetx if true (default) reset the X position.
7535	 * @param boolean $rtloff if true always uses the page top-left corner as origin of axis.
7536	 * @public
7537	 * @since 1.0
7538	 * @see GetX(), GetY(), SetY(), SetXY()
7539	 */
7540	public function setY($y, $resetx=true, $rtloff=false) {
7541		$y = floatval($y);
7542		if ($resetx) {
7543			//reset x
7544			if (!$rtloff AND $this->rtl) {
7545				$this->x = $this->w - $this->rMargin;
7546			} else {
7547				$this->x = $this->lMargin;
7548			}
7549		}
7550		if ($y >= 0) {
7551			$this->y = $y;
7552		} else {
7553			$this->y = $this->h + $y;
7554		}
7555		if ($this->y < 0) {
7556			$this->y = 0;
7557		}
7558		if ($this->y > $this->h) {
7559			$this->y = $this->h;
7560		}
7561	}
7562
7563	/**
7564	 * Defines the abscissa and ordinate of the current position.
7565	 * If the passed values are negative, they are relative respectively to the right and bottom of the page.
7566	 * @param float $x The value of the abscissa.
7567	 * @param float $y The value of the ordinate.
7568	 * @param boolean $rtloff if true always uses the page top-left corner as origin of axis.
7569	 * @public
7570	 * @since 1.2
7571	 * @see SetX(), SetY()
7572	 */
7573	public function setXY($x, $y, $rtloff=false) {
7574		$this->setY($y, false, $rtloff);
7575		$this->setX($x, $rtloff);
7576	}
7577
7578	/**
7579	 * Set the absolute X coordinate of the current pointer.
7580	 * @param float $x The value of the abscissa in user units.
7581	 * @public
7582	 * @since 5.9.186 (2012-09-13)
7583	 * @see setAbsX(), setAbsY(), SetAbsXY()
7584	 */
7585	public function setAbsX($x) {
7586		$this->x = floatval($x);
7587	}
7588
7589	/**
7590	 * Set the absolute Y coordinate of the current pointer.
7591	 * @param float $y (float) The value of the ordinate in user units.
7592	 * @public
7593	 * @since 5.9.186 (2012-09-13)
7594	 * @see setAbsX(), setAbsY(), SetAbsXY()
7595	 */
7596	public function setAbsY($y) {
7597		$this->y = floatval($y);
7598	}
7599
7600	/**
7601	 * Set the absolute X and Y coordinates of the current pointer.
7602	 * @param float $x The value of the abscissa in user units.
7603	 * @param float $y (float) The value of the ordinate in user units.
7604	 * @public
7605	 * @since 5.9.186 (2012-09-13)
7606	 * @see setAbsX(), setAbsY(), SetAbsXY()
7607	 */
7608	public function setAbsXY($x, $y) {
7609		$this->setAbsX($x);
7610		$this->setAbsY($y);
7611	}
7612
7613	/**
7614	 * Send the document to a given destination: string, local file or browser.
7615	 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
7616	 * The method first calls Close() if necessary to terminate the document.
7617	 * @param string $name The name of the file when saved. Note that special characters are removed and blanks characters are replaced with the underscore character.
7618	 * @param string $dest Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser (default). The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local server file with the name given by name.</li><li>S: return the document as a string (name is ignored).</li><li>FI: equivalent to F + I option</li><li>FD: equivalent to F + D option</li><li>E: return the document as base64 mime multi-part email attachment (RFC 2045)</li></ul>
7619	 * @return string
7620	 * @public
7621	 * @since 1.0
7622	 * @see Close()
7623	 */
7624	public function Output($name='doc.pdf', $dest='I') {
7625		//Output PDF to some destination
7626		//Finish document if necessary
7627		if ($this->state < 3) {
7628			$this->Close();
7629		}
7630		//Normalize parameters
7631		if (is_bool($dest)) {
7632			$dest = $dest ? 'D' : 'F';
7633		}
7634		$dest = strtoupper($dest);
7635		if ($dest[0] != 'F') {
7636			$name = preg_replace('/[\s]+/', '_', $name);
7637			$name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
7638		}
7639		if ($this->sign) {
7640			// *** apply digital signature to the document ***
7641			// get the document content
7642			$pdfdoc = $this->getBuffer();
7643			// remove last newline
7644			$pdfdoc = substr($pdfdoc, 0, -1);
7645			// remove filler space
7646			$byterange_string_len = strlen(TCPDF_STATIC::$byterange_string);
7647			// define the ByteRange
7648			$byte_range = array();
7649			$byte_range[0] = 0;
7650			$byte_range[1] = strpos($pdfdoc, TCPDF_STATIC::$byterange_string) + $byterange_string_len + 10;
7651			$byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
7652			$byte_range[3] = strlen($pdfdoc) - $byte_range[2];
7653			$pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
7654			// replace the ByteRange
7655			$byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
7656			$byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
7657			$pdfdoc = str_replace(TCPDF_STATIC::$byterange_string, $byterange, $pdfdoc);
7658			// write the document to a temporary folder
7659			$tempdoc = TCPDF_STATIC::getObjFilename('doc', $this->file_id);
7660			$f = TCPDF_STATIC::fopenLocal($tempdoc, 'wb');
7661			if (!$f) {
7662				$this->Error('Unable to create temporary file: '.$tempdoc);
7663			}
7664			$pdfdoc_length = strlen($pdfdoc);
7665			fwrite($f, $pdfdoc, $pdfdoc_length);
7666			fclose($f);
7667			// get digital signature via openssl library
7668			$tempsign = TCPDF_STATIC::getObjFilename('sig', $this->file_id);
7669			if (empty($this->signature_data['extracerts'])) {
7670				openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
7671			} else {
7672				openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED, $this->signature_data['extracerts']);
7673			}
7674			// read signature
7675			$signature = file_get_contents($tempsign);
7676			// extract signature
7677			$signature = substr($signature, $pdfdoc_length);
7678			$signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
7679			$tmparr = explode("\n\n", $signature);
7680			$signature = $tmparr[1];
7681			// decode signature
7682			$signature = base64_decode(trim($signature));
7683			// add TSA timestamp to signature
7684			$signature = $this->applyTSA($signature);
7685			// convert signature to hex
7686			$signature = current(unpack('H*', $signature));
7687			$signature = str_pad($signature, $this->signature_max_length, '0');
7688			// Add signature to the document
7689			$this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
7690			$this->bufferlen = strlen($this->buffer);
7691		}
7692		switch($dest) {
7693			case 'I': {
7694				// Send PDF to the standard output
7695				if (ob_get_contents()) {
7696					$this->Error('Some data has already been output, can\'t send PDF file');
7697				}
7698				if (php_sapi_name() != 'cli') {
7699					// send output to a browser
7700					header('Content-Type: application/pdf');
7701					if (headers_sent()) {
7702						$this->Error('Some data has already been output to browser, can\'t send PDF file');
7703					}
7704					header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7705					//header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7706					header('Pragma: public');
7707					header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7708					header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7709					header('Content-Disposition: inline; filename="'.basename($name).'"');
7710					TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7711				} else {
7712					echo $this->getBuffer();
7713				}
7714				break;
7715			}
7716			case 'D': {
7717				// download PDF as file
7718				if (ob_get_contents()) {
7719					$this->Error('Some data has already been output, can\'t send PDF file');
7720				}
7721				header('Content-Description: File Transfer');
7722				if (headers_sent()) {
7723					$this->Error('Some data has already been output to browser, can\'t send PDF file');
7724				}
7725				header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7726				//header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7727				header('Pragma: public');
7728				header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7729				header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7730				// force download dialog
7731				if (strpos(php_sapi_name(), 'cgi') === false) {
7732					header('Content-Type: application/force-download');
7733					header('Content-Type: application/octet-stream', false);
7734					header('Content-Type: application/download', false);
7735					header('Content-Type: application/pdf', false);
7736				} else {
7737					header('Content-Type: application/pdf');
7738				}
7739				// use the Content-Disposition header to supply a recommended filename
7740				header('Content-Disposition: attachment; filename="'.basename($name).'"');
7741				header('Content-Transfer-Encoding: binary');
7742				TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7743				break;
7744			}
7745			case 'F':
7746			case 'FI':
7747			case 'FD': {
7748				// save PDF to a local file
7749				$f = TCPDF_STATIC::fopenLocal($name, 'wb');
7750				if (!$f) {
7751					$this->Error('Unable to create output file: '.$name);
7752				}
7753				fwrite($f, $this->getBuffer(), $this->bufferlen);
7754				fclose($f);
7755				if ($dest == 'FI') {
7756					// send headers to browser
7757					header('Content-Type: application/pdf');
7758					header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7759					//header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7760					header('Pragma: public');
7761					header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7762					header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7763					header('Content-Disposition: inline; filename="'.basename($name).'"');
7764					TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7765				} elseif ($dest == 'FD') {
7766					// send headers to browser
7767					if (ob_get_contents()) {
7768						$this->Error('Some data has already been output, can\'t send PDF file');
7769					}
7770					header('Content-Description: File Transfer');
7771					if (headers_sent()) {
7772						$this->Error('Some data has already been output to browser, can\'t send PDF file');
7773					}
7774					header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7775					header('Pragma: public');
7776					header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7777					header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7778					// force download dialog
7779					if (strpos(php_sapi_name(), 'cgi') === false) {
7780						header('Content-Type: application/force-download');
7781						header('Content-Type: application/octet-stream', false);
7782						header('Content-Type: application/download', false);
7783						header('Content-Type: application/pdf', false);
7784					} else {
7785						header('Content-Type: application/pdf');
7786					}
7787					// use the Content-Disposition header to supply a recommended filename
7788					header('Content-Disposition: attachment; filename="'.basename($name).'"');
7789					header('Content-Transfer-Encoding: binary');
7790					TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7791				}
7792				break;
7793			}
7794			case 'E': {
7795				// return PDF as base64 mime multi-part email attachment (RFC 2045)
7796				$retval = 'Content-Type: application/pdf;'."\r\n";
7797				$retval .= ' name="'.$name.'"'."\r\n";
7798				$retval .= 'Content-Transfer-Encoding: base64'."\r\n";
7799				$retval .= 'Content-Disposition: attachment;'."\r\n";
7800				$retval .= ' filename="'.$name.'"'."\r\n\r\n";
7801				$retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
7802				return $retval;
7803			}
7804			case 'S': {
7805				// returns PDF as a string
7806				return $this->getBuffer();
7807			}
7808			default: {
7809				$this->Error('Incorrect output destination: '.$dest);
7810			}
7811		}
7812		return '';
7813	}
7814
7815	protected static $cleaned_ids = array();
7816	/**
7817	 * Unset all class variables except the following critical variables.
7818	 * @param boolean $destroyall if true destroys all class variables, otherwise preserves critical variables.
7819	 * @param boolean $preserve_objcopy if true preserves the objcopy variable
7820	 * @public
7821	 * @since 4.5.016 (2009-02-24)
7822	 */
7823	public function _destroy($destroyall=false, $preserve_objcopy=false) {
7824		if (isset(self::$cleaned_ids[$this->file_id])) {
7825			$destroyall = false;
7826		}
7827		if ($destroyall AND !$preserve_objcopy && isset($this->file_id)) {
7828			self::$cleaned_ids[$this->file_id] = true;
7829			// remove all temporary files
7830			if ($handle = @opendir(K_PATH_CACHE)) {
7831				while ( false !== ( $file_name = readdir( $handle ) ) ) {
7832					if (strpos($file_name, '__tcpdf_'.$this->file_id.'_') === 0) {
7833						unlink(K_PATH_CACHE.$file_name);
7834					}
7835				}
7836				closedir($handle);
7837			}
7838			if (isset($this->imagekeys)) {
7839				foreach($this->imagekeys as $file) {
7840					if (strpos($file, K_PATH_CACHE) === 0 && TCPDF_STATIC::file_exists($file)) {
7841						@unlink($file);
7842					}
7843				}
7844			}
7845		}
7846		$preserve = array(
7847			'file_id',
7848			'state',
7849			'bufferlen',
7850			'buffer',
7851			'cached_files',
7852			'imagekeys',
7853			'sign',
7854			'signature_data',
7855			'signature_max_length',
7856			'byterange_string',
7857			'tsa_timestamp',
7858			'tsa_data'
7859		);
7860		foreach (array_keys(get_object_vars($this)) as $val) {
7861			if ($destroyall OR !in_array($val, $preserve)) {
7862				if ((!$preserve_objcopy OR ($val != 'objcopy')) AND ($val != 'file_id') AND isset($this->$val)) {
7863					unset($this->$val);
7864				}
7865			}
7866		}
7867	}
7868
7869	/**
7870	 * Check for locale-related bug
7871	 * @protected
7872	 */
7873	protected function _dochecks() {
7874		//Check for locale-related bug
7875		if (1.1 == 1) {
7876			$this->Error('Don\'t alter the locale before including class file');
7877		}
7878		//Check for decimal separator
7879		if (sprintf('%.1F', 1.0) != '1.0') {
7880			setlocale(LC_NUMERIC, 'C');
7881		}
7882	}
7883
7884	/**
7885	 * Return an array containing variations for the basic page number alias.
7886	 * @param string $a Base alias.
7887	 * @return array of page number aliases
7888	 * @protected
7889	 */
7890	protected function getInternalPageNumberAliases($a= '') {
7891		$alias = array();
7892		// build array of Unicode + ASCII variants (the order is important)
7893		$alias = array('u' => array(), 'a' => array());
7894		$u = '{'.$a.'}';
7895		$alias['u'][] = TCPDF_STATIC::_escape($u);
7896		if ($this->isunicode) {
7897			$alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($u, $this->isunicode, $this->CurrentFont));
7898			$alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($u, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7899			$alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($a, $this->isunicode, $this->CurrentFont));
7900			$alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($a, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7901		}
7902		$alias['a'][] = TCPDF_STATIC::_escape($a);
7903		return $alias;
7904	}
7905
7906	/**
7907	 * Return an array containing all internal page aliases.
7908	 * @return array of page number aliases
7909	 * @protected
7910	 */
7911	protected function getAllInternalPageNumberAliases() {
7912		$basic_alias = array(TCPDF_STATIC::$alias_tot_pages, TCPDF_STATIC::$alias_num_page, TCPDF_STATIC::$alias_group_tot_pages, TCPDF_STATIC::$alias_group_num_page, TCPDF_STATIC::$alias_right_shift);
7913		$pnalias = array();
7914		foreach($basic_alias as $k => $a) {
7915			$pnalias[$k] = $this->getInternalPageNumberAliases($a);
7916		}
7917		return $pnalias;
7918	}
7919
7920	/**
7921	 * Replace right shift page number aliases with spaces to correct right alignment.
7922	 * This works perfectly only when using monospaced fonts.
7923	 * @param string $page Page content.
7924	 * @param array $aliases Array of page aliases.
7925	 * @param int $diff initial difference to add.
7926	 * @return string replaced page content.
7927	 * @protected
7928	 */
7929	protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
7930		foreach ($aliases as $type => $alias) {
7931			foreach ($alias as $a) {
7932				// find position of compensation factor
7933				$startnum = (strpos($a, ':') + 1);
7934				$a = substr($a, 0, $startnum);
7935				if (($pos = strpos($page, $a)) !== false) {
7936					// end of alias
7937					$endnum = strpos($page, '}', $pos);
7938					// string to be replaced
7939					$aa = substr($page, $pos, ($endnum - $pos + 1));
7940					// get compensation factor
7941					$ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum));
7942					$ratio = preg_replace('/[^0-9\.]/', '', $ratio);
7943					$ratio = floatval($ratio);
7944					if ($type == 'u') {
7945						$chrdiff = floor(($diff + 12) * $ratio);
7946						$shift = str_repeat(' ', $chrdiff);
7947						$shift = TCPDF_FONTS::UTF8ToUTF16BE($shift, false, $this->isunicode, $this->CurrentFont);
7948					} else {
7949						$chrdiff = floor(($diff + 11) * $ratio);
7950						$shift = str_repeat(' ', $chrdiff);
7951					}
7952					$page = str_replace($aa, $shift, $page);
7953				}
7954			}
7955		}
7956		return $page;
7957	}
7958
7959	/**
7960	 * Set page boxes to be included on page descriptions.
7961	 * @param array $boxes Array of page boxes to set on document: ('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox').
7962	 * @protected
7963	 */
7964	protected function setPageBoxTypes($boxes) {
7965		$this->page_boxes = array();
7966		foreach ($boxes as $box) {
7967			if (in_array($box, TCPDF_STATIC::$pageboxes)) {
7968				$this->page_boxes[] = $box;
7969			}
7970		}
7971	}
7972
7973	/**
7974	 * Output pages (and replace page number aliases).
7975	 * @protected
7976	 */
7977	protected function _putpages() {
7978		$filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
7979		// get internal aliases for page numbers
7980		$pnalias = $this->getAllInternalPageNumberAliases();
7981		$num_pages = $this->numpages;
7982		$ptpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $num_pages - 1));
7983		$ptpu = TCPDF_FONTS::UTF8ToUTF16BE($ptpa, false, $this->isunicode, $this->CurrentFont);
7984		$ptp_num_chars = $this->GetNumChars($ptpa);
7985		$pagegroupnum = 0;
7986		$groupnum = 0;
7987		$ptgu = 1;
7988		$ptga = 1;
7989		$ptg_num_chars = 1;
7990		for ($n = 1; $n <= $num_pages; ++$n) {
7991			// get current page
7992			$temppage = $this->getPageBuffer($n);
7993			$pagelen = strlen($temppage);
7994			// set replacements for total pages number
7995			$pnpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $n - 1));
7996			$pnpu = TCPDF_FONTS::UTF8ToUTF16BE($pnpa, false, $this->isunicode, $this->CurrentFont);
7997			$pnp_num_chars = $this->GetNumChars($pnpa);
7998			$pdiff = 0; // difference used for right shift alignment of page numbers
7999			$gdiff = 0; // difference used for right shift alignment of page group numbers
8000			if (!empty($this->pagegroups)) {
8001				if (isset($this->newpagegroup[$n])) {
8002					$pagegroupnum = 0;
8003					++$groupnum;
8004					$ptga = TCPDF_STATIC::formatPageNumber($this->pagegroups[$groupnum]);
8005					$ptgu = TCPDF_FONTS::UTF8ToUTF16BE($ptga, false, $this->isunicode, $this->CurrentFont);
8006					$ptg_num_chars = $this->GetNumChars($ptga);
8007				}
8008				++$pagegroupnum;
8009				$pnga = TCPDF_STATIC::formatPageNumber($pagegroupnum);
8010				$pngu = TCPDF_FONTS::UTF8ToUTF16BE($pnga, false, $this->isunicode, $this->CurrentFont);
8011				$png_num_chars = $this->GetNumChars($pnga);
8012				// replace page numbers
8013				$replace = array();
8014				$replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
8015				$replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
8016				$replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
8017				$replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
8018				list($temppage, $gdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $gdiff);
8019			}
8020			// replace page numbers
8021			$replace = array();
8022			$replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
8023			$replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
8024			$replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
8025			$replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
8026			list($temppage, $pdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $pdiff);
8027			// replace right shift alias
8028			$temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
8029			// replace EPS marker
8030			$temppage = str_replace($this->epsmarker, '', $temppage);
8031			//Page
8032			$this->page_obj_id[$n] = $this->_newobj();
8033			$out = '<<';
8034			$out .= ' /Type /Page';
8035			$out .= ' /Parent 1 0 R';
8036			if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
8037				$out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp);
8038			}
8039			$out .= ' /Resources 2 0 R';
8040			foreach ($this->page_boxes as $box) {
8041				$out .= ' /'.$box;
8042				$out .= sprintf(' [%F %F %F %F]', $this->pagedim[$n][$box]['llx'], $this->pagedim[$n][$box]['lly'], $this->pagedim[$n][$box]['urx'], $this->pagedim[$n][$box]['ury']);
8043			}
8044			if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
8045				$out .= ' /BoxColorInfo <<';
8046				foreach ($this->page_boxes as $box) {
8047					if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
8048						$out .= ' /'.$box.' <<';
8049						if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
8050							$color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
8051							$out .= ' /C [';
8052							$out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
8053							$out .= ' ]';
8054						}
8055						if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
8056							$out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
8057						}
8058						if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
8059							$out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
8060						}
8061						if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
8062							$dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
8063							$out .= ' /D [';
8064							foreach ($dashes as $dash) {
8065								$out .= sprintf(' %F', ($dash * $this->k));
8066							}
8067							$out .= ' ]';
8068						}
8069						$out .= ' >>';
8070					}
8071				}
8072				$out .= ' >>';
8073			}
8074			$out .= ' /Contents '.($this->n + 1).' 0 R';
8075			$out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
8076			if (!$this->pdfa_mode || $this->pdfa_version >= 2) {
8077				$out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
8078			}
8079			if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
8080				// page transitions
8081				if (isset($this->pagedim[$n]['trans']['Dur'])) {
8082					$out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
8083				}
8084				$out .= ' /Trans <<';
8085				$out .= ' /Type /Trans';
8086				if (isset($this->pagedim[$n]['trans']['S'])) {
8087					$out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
8088				}
8089				if (isset($this->pagedim[$n]['trans']['D'])) {
8090					$out .= ' /D '.$this->pagedim[$n]['trans']['D'];
8091				}
8092				if (isset($this->pagedim[$n]['trans']['Dm'])) {
8093					$out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
8094				}
8095				if (isset($this->pagedim[$n]['trans']['M'])) {
8096					$out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
8097				}
8098				if (isset($this->pagedim[$n]['trans']['Di'])) {
8099					$out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
8100				}
8101				if (isset($this->pagedim[$n]['trans']['SS'])) {
8102					$out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
8103				}
8104				if (isset($this->pagedim[$n]['trans']['B'])) {
8105					$out .= ' /B '.$this->pagedim[$n]['trans']['B'];
8106				}
8107				$out .= ' >>';
8108			}
8109			$out .= $this->_getannotsrefs($n);
8110			$out .= ' /PZ '.$this->pagedim[$n]['PZ'];
8111			$out .= ' >>';
8112			$out .= "\n".'endobj';
8113			$this->_out($out);
8114			//Page content
8115			$p = ($this->compress) ? gzcompress($temppage) : $temppage;
8116			$this->_newobj();
8117			$p = $this->_getrawstream($p);
8118			$this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
8119		}
8120		//Pages root
8121		$out = $this->_getobj(1)."\n";
8122		$out .= '<< /Type /Pages /Kids [';
8123		foreach($this->page_obj_id as $page_obj) {
8124			$out .= ' '.$page_obj.' 0 R';
8125		}
8126		$out .= ' ] /Count '.$num_pages.' >>';
8127		$out .= "\n".'endobj';
8128		$this->_out($out);
8129	}
8130
8131	/**
8132	 * Get references to page annotations.
8133	 * @param int $n page number
8134	 * @return string
8135	 * @protected
8136	 * @author Nicola Asuni
8137	 * @since 5.0.010 (2010-05-17)
8138	 */
8139	protected function _getannotsrefs($n) {
8140		if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
8141			return '';
8142		}
8143		$out = ' /Annots [';
8144		if (isset($this->PageAnnots[$n])) {
8145			foreach ($this->PageAnnots[$n] as $key => $val) {
8146				if (!in_array($val['n'], $this->radio_groups)) {
8147					$out .= ' '.$val['n'].' 0 R';
8148				}
8149			}
8150			// add radiobutton groups
8151			if (isset($this->radiobutton_groups[$n])) {
8152				foreach ($this->radiobutton_groups[$n] as $key => $data) {
8153					if (isset($data['n'])) {
8154						$out .= ' '.$data['n'].' 0 R';
8155					}
8156				}
8157			}
8158		}
8159		if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
8160			// set reference for signature object
8161			$out .= ' '.$this->sig_obj_id.' 0 R';
8162		}
8163		if (!empty($this->empty_signature_appearance)) {
8164			foreach ($this->empty_signature_appearance as $esa) {
8165				if ($esa['page'] == $n) {
8166					// set reference for empty signature objects
8167					$out .= ' '.$esa['objid'].' 0 R';
8168				}
8169			}
8170		}
8171		$out .= ' ]';
8172		return $out;
8173	}
8174
8175	/**
8176	 * Output annotations objects for all pages.
8177	 * !!! THIS METHOD IS NOT YET COMPLETED !!!
8178	 * See section 12.5 of PDF 32000_2008 reference.
8179	 * @protected
8180	 * @author Nicola Asuni
8181	 * @since 4.0.018 (2008-08-06)
8182	 */
8183	protected function _putannotsobjs() {
8184		// reset object counter
8185		for ($n=1; $n <= $this->numpages; ++$n) {
8186			if (isset($this->PageAnnots[$n])) {
8187				// set page annotations
8188				foreach ($this->PageAnnots[$n] as $key => $pl) {
8189					$annot_obj_id = $this->PageAnnots[$n][$key]['n'];
8190					// create annotation object for grouping radiobuttons
8191					if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
8192						$radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n'];
8193						$annots = '<<';
8194						$annots .= ' /Type /Annot';
8195						$annots .= ' /Subtype /Widget';
8196						$annots .= ' /Rect [0 0 0 0]';
8197						if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) {
8198							// read only
8199							$annots .= ' /F 68';
8200							$annots .= ' /Ff 49153';
8201						} else {
8202							$annots .= ' /F 4'; // default print for PDF/A
8203							$annots .= ' /Ff 49152';
8204						}
8205						$annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
8206						if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8207							$annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id);
8208						}
8209						$annots .= ' /FT /Btn';
8210						$annots .= ' /Kids [';
8211						$defval = '';
8212						foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) {
8213							if (isset($data['kid'])) {
8214								$annots .= ' '.$data['kid'].' 0 R';
8215								if ($data['def'] !== 'Off') {
8216									$defval = $data['def'];
8217								}
8218							}
8219						}
8220						$annots .= ' ]';
8221						if (!empty($defval)) {
8222							$annots .= ' /V /'.$defval;
8223						}
8224						$annots .= ' >>';
8225						$this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
8226						$this->form_obj_id[] = $radio_button_obj_id;
8227						// store object id to be used on Parent entry of Kids
8228						$this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id;
8229					}
8230					$formfield = false;
8231					$pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
8232					$a = $pl['x'] * $this->k;
8233					$b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k);
8234					$c = $pl['w'] * $this->k;
8235					$d = $pl['h'] * $this->k;
8236					$rect = sprintf('%F %F %F %F', $a, $b, $a+$c, $b+$d);
8237					// create new annotation object
8238					$annots = '<</Type /Annot';
8239					$annots .= ' /Subtype /'.$pl['opt']['subtype'];
8240					$annots .= ' /Rect ['.$rect.']';
8241					$ft = array('Btn', 'Tx', 'Ch', 'Sig');
8242					if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
8243						$annots .= ' /FT /'.$pl['opt']['ft'];
8244						$formfield = true;
8245					}
8246					if ($pl['opt']['subtype'] !== 'Link') {
8247						$annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
8248					}
8249					$annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
8250					$annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
8251					$annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp);
8252					if (isset($pl['opt']['f'])) {
8253						$fval = 0;
8254						if (is_array($pl['opt']['f'])) {
8255							foreach ($pl['opt']['f'] as $f) {
8256								switch (strtolower($f)) {
8257									case 'invisible': {
8258										$fval += 1 << 0;
8259										break;
8260									}
8261									case 'hidden': {
8262										$fval += 1 << 1;
8263										break;
8264									}
8265									case 'print': {
8266										$fval += 1 << 2;
8267										break;
8268									}
8269									case 'nozoom': {
8270										$fval += 1 << 3;
8271										break;
8272									}
8273									case 'norotate': {
8274										$fval += 1 << 4;
8275										break;
8276									}
8277									case 'noview': {
8278										$fval += 1 << 5;
8279										break;
8280									}
8281									case 'readonly': {
8282										$fval += 1 << 6;
8283										break;
8284									}
8285									case 'locked': {
8286										$fval += 1 << 8;
8287										break;
8288									}
8289									case 'togglenoview': {
8290										$fval += 1 << 9;
8291										break;
8292									}
8293									case 'lockedcontents': {
8294										$fval += 1 << 10;
8295										break;
8296									}
8297									default: {
8298										break;
8299									}
8300								}
8301							}
8302						} else {
8303							$fval = intval($pl['opt']['f']);
8304						}
8305					} else {
8306						$fval = 4;
8307					}
8308					if ($this->pdfa_mode) {
8309						// force print flag for PDF/A mode
8310						$fval |= 4;
8311					}
8312					$annots .= ' /F '.intval($fval);
8313					if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
8314						$annots .= ' /AS /'.$pl['opt']['as'];
8315					}
8316					if (isset($pl['opt']['ap'])) {
8317						// appearance stream
8318						$annots .= ' /AP <<';
8319						if (is_array($pl['opt']['ap'])) {
8320							foreach ($pl['opt']['ap'] as $apmode => $apdef) {
8321								// $apmode can be: n = normal; r = rollover; d = down;
8322								$annots .= ' /'.strtoupper($apmode);
8323								if (is_array($apdef)) {
8324									$annots .= ' <<';
8325									foreach ($apdef as $apstate => $stream) {
8326										// reference to XObject that define the appearance for this mode-state
8327										$apsobjid = $this->_putAPXObject($c, $d, $stream);
8328										$annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
8329									}
8330									$annots .= ' >>';
8331								} else {
8332									// reference to XObject that define the appearance for this mode
8333									$apsobjid = $this->_putAPXObject($c, $d, $apdef);
8334									$annots .= ' '.$apsobjid.' 0 R';
8335								}
8336							}
8337						} else {
8338							$annots .= $pl['opt']['ap'];
8339						}
8340						$annots .= ' >>';
8341					}
8342					if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
8343						$annots .= ' /BS <<';
8344						$annots .= ' /Type /Border';
8345						if (isset($pl['opt']['bs']['w'])) {
8346							$annots .= ' /W '.intval($pl['opt']['bs']['w']);
8347						}
8348						$bstyles = array('S', 'D', 'B', 'I', 'U');
8349						if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
8350							$annots .= ' /S /'.$pl['opt']['bs']['s'];
8351						}
8352						if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
8353							$annots .= ' /D [';
8354							foreach ($pl['opt']['bs']['d'] as $cord) {
8355								$annots .= ' '.intval($cord);
8356							}
8357							$annots .= ']';
8358						}
8359						$annots .= ' >>';
8360					} else {
8361						$annots .= ' /Border [';
8362						if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
8363							$annots .= intval($pl['opt']['border'][0]).' ';
8364							$annots .= intval($pl['opt']['border'][1]).' ';
8365							$annots .= intval($pl['opt']['border'][2]);
8366							if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
8367								$annots .= ' [';
8368								foreach ($pl['opt']['border'][3] as $dash) {
8369									$annots .= intval($dash).' ';
8370								}
8371								$annots .= ']';
8372							}
8373						} else {
8374							$annots .= '0 0 0';
8375						}
8376						$annots .= ']';
8377					}
8378					if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
8379						$annots .= ' /BE <<';
8380						$bstyles = array('S', 'C');
8381						if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) {
8382							$annots .= ' /S /'.$pl['opt']['bs']['s'];
8383						} else {
8384							$annots .= ' /S /S';
8385						}
8386						if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
8387							$annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']);
8388						}
8389						$annots .= '>>';
8390					}
8391					if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
8392						$annots .= ' /C '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['c']);
8393					}
8394					//$annots .= ' /StructParent ';
8395					//$annots .= ' /OC ';
8396					$markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
8397					if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
8398						// this is a markup type
8399						if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8400							$annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
8401						}
8402						//$annots .= ' /Popup ';
8403						if (isset($pl['opt']['ca'])) {
8404							$annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca']));
8405						}
8406						if (isset($pl['opt']['rc'])) {
8407							$annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8408						}
8409						$annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp);
8410						//$annots .= ' /IRT ';
8411						if (isset($pl['opt']['subj'])) {
8412							$annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
8413						}
8414						//$annots .= ' /RT ';
8415						//$annots .= ' /IT ';
8416						//$annots .= ' /ExData ';
8417					}
8418					$lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
8419					// Annotation types
8420					switch (strtolower($pl['opt']['subtype'])) {
8421						case 'text': {
8422							if (isset($pl['opt']['open'])) {
8423								$annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
8424							}
8425							$iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
8426							if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8427								$annots .= ' /Name /'.$pl['opt']['name'];
8428							} else {
8429								$annots .= ' /Name /Note';
8430							}
8431							$hasStateModel = isset($pl['opt']['statemodel']);
8432							$hasState = isset($pl['opt']['state']);
8433							$statemodels = array('Marked', 'Review');
8434							if (!$hasStateModel && !$hasState) {
8435								break;
8436							}
8437							if ($hasStateModel AND in_array($pl['opt']['statemodel'], $statemodels)) {
8438								$annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8439							} else {
8440								$pl['opt']['statemodel'] = 'Marked';
8441								$annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8442							}
8443							if ($pl['opt']['statemodel'] == 'Marked') {
8444								$states = array('Accepted', 'Unmarked');
8445							} else {
8446								$states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
8447							}
8448							if ($hasState AND in_array($pl['opt']['state'], $states)) {
8449								$annots .= ' /State /'.$pl['opt']['state'];
8450							} else {
8451								if ($pl['opt']['statemodel'] == 'Marked') {
8452									$annots .= ' /State /Unmarked';
8453								} else {
8454									$annots .= ' /State /None';
8455								}
8456							}
8457							break;
8458						}
8459						case 'link': {
8460							if (is_string($pl['txt']) && !empty($pl['txt'])) {
8461								if ($pl['txt'][0] == '#') {
8462									// internal destination
8463									$annots .= ' /A <</S /GoTo /D /'.TCPDF_STATIC::encodeNameObject(substr($pl['txt'], 1)).'>>';
8464								} elseif ($pl['txt'][0] == '%') {
8465									// embedded PDF file
8466									$filename = basename(substr($pl['txt'], 1));
8467									$annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
8468								} elseif ($pl['txt'][0] == '*') {
8469									// embedded generic file
8470									$filename = basename(substr($pl['txt'], 1));
8471									$jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});';
8472									$annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>';
8473								} else {
8474									$parsedUrl = parse_url($pl['txt']);
8475									if (empty($parsedUrl['scheme']) AND (!empty($parsedUrl['path']) && strtolower(substr($parsedUrl['path'], -4)) == '.pdf')) {
8476										// relative link to a PDF file
8477										$dest = '[0 /Fit]'; // default page 0
8478										if (!empty($parsedUrl['fragment'])) {
8479											// check for named destination
8480											$tmp = explode('=', $parsedUrl['fragment']);
8481											$dest = '('.((count($tmp) == 2) ? $tmp[1] : $tmp[0]).')';
8482										}
8483										$annots .= ' /A <</S /GoToR /D '.$dest.' /F '.$this->_datastring($this->unhtmlentities($parsedUrl['path']), $annot_obj_id).' /NewWindow true>>';
8484									} else {
8485										// external URI link
8486										$annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
8487									}
8488								}
8489							} elseif (isset($this->links[$pl['txt']])) {
8490								// internal link ID
8491								$l = $this->links[$pl['txt']];
8492								if (isset($this->page_obj_id[($l['p'])])) {
8493									$annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l['p'])], ($this->pagedim[$l['p']]['h'] - ($l['y'] * $this->k)));
8494								}
8495							}
8496							$hmodes = array('N', 'I', 'O', 'P');
8497							if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
8498								$annots .= ' /H /'.$pl['opt']['h'];
8499							} else {
8500								$annots .= ' /H /I';
8501							}
8502							//$annots .= ' /PA ';
8503							//$annots .= ' /Quadpoints ';
8504							break;
8505						}
8506						case 'freetext': {
8507							if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8508								$annots .= ' /DA ('.$pl['opt']['da'].')';
8509							}
8510							if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8511								$annots .= ' /Q '.intval($pl['opt']['q']);
8512							}
8513							if (isset($pl['opt']['rc'])) {
8514								$annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8515							}
8516							if (isset($pl['opt']['ds'])) {
8517								$annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
8518							}
8519							if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
8520								$annots .= ' /CL [';
8521								foreach ($pl['opt']['cl'] as $cl) {
8522									$annots .= sprintf('%F ', $cl * $this->k);
8523								}
8524								$annots .= ']';
8525							}
8526							$tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
8527							if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
8528								$annots .= ' /IT /'.$pl['opt']['it'];
8529							}
8530							if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
8531								$l = $pl['opt']['rd'][0] * $this->k;
8532								$r = $pl['opt']['rd'][1] * $this->k;
8533								$t = $pl['opt']['rd'][2] * $this->k;
8534								$b = $pl['opt']['rd'][3] * $this->k;
8535								$annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']';
8536							}
8537							if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
8538								$annots .= ' /LE /'.$pl['opt']['le'];
8539							}
8540							break;
8541						}
8542						case 'line': {
8543							break;
8544						}
8545						case 'square': {
8546							break;
8547						}
8548						case 'circle': {
8549							break;
8550						}
8551						case 'polygon': {
8552							break;
8553						}
8554						case 'polyline': {
8555							break;
8556						}
8557						case 'highlight': {
8558							break;
8559						}
8560						case 'underline': {
8561							break;
8562						}
8563						case 'squiggly': {
8564							break;
8565						}
8566						case 'strikeout': {
8567							break;
8568						}
8569						case 'stamp': {
8570							break;
8571						}
8572						case 'caret': {
8573							break;
8574						}
8575						case 'ink': {
8576							break;
8577						}
8578						case 'popup': {
8579							break;
8580						}
8581						case 'fileattachment': {
8582							if ($this->pdfa_mode && $this->pdfa_version != 3) {
8583								// embedded files are not allowed in PDF/A mode version 1 and 2
8584								break;
8585							}
8586							if (!isset($pl['opt']['fs'])) {
8587								break;
8588							}
8589							$filename = basename($pl['opt']['fs']);
8590							if (isset($this->embeddedfiles[$filename]['f'])) {
8591								$annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R';
8592								$iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
8593								if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8594									$annots .= ' /Name /'.$pl['opt']['name'];
8595								} else {
8596									$annots .= ' /Name /PushPin';
8597								}
8598								// index (zero-based) of the annotation in the Annots array of this page
8599								$this->embeddedfiles[$filename]['a'] = $key;
8600							}
8601							break;
8602						}
8603						case 'sound': {
8604							if (!isset($pl['opt']['fs'])) {
8605								break;
8606							}
8607							$filename = basename($pl['opt']['fs']);
8608							if (isset($this->embeddedfiles[$filename]['f'])) {
8609								// ... TO BE COMPLETED ...
8610								// /R /C /B /E /CO /CP
8611								$annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R';
8612								$iconsapp = array('Speaker', 'Mic');
8613								if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8614									$annots .= ' /Name /'.$pl['opt']['name'];
8615								} else {
8616									$annots .= ' /Name /Speaker';
8617								}
8618							}
8619							break;
8620						}
8621						case 'movie': {
8622							break;
8623						}
8624						case 'widget': {
8625							$hmode = array('N', 'I', 'O', 'P', 'T');
8626							if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
8627								$annots .= ' /H /'.$pl['opt']['h'];
8628							}
8629							if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
8630								$annots .= ' /MK <<';
8631								if (isset($pl['opt']['mk']['r'])) {
8632									$annots .= ' /R '.$pl['opt']['mk']['r'];
8633								}
8634								if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
8635									$annots .= ' /BC '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bc']);
8636								}
8637								if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
8638									$annots .= ' /BG '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bg']);
8639								}
8640								if (isset($pl['opt']['mk']['ca'])) {
8641									$annots .= ' /CA '.$pl['opt']['mk']['ca'];
8642								}
8643								if (isset($pl['opt']['mk']['rc'])) {
8644									$annots .= ' /RC '.$pl['opt']['mk']['rc'];
8645								}
8646								if (isset($pl['opt']['mk']['ac'])) {
8647									$annots .= ' /AC '.$pl['opt']['mk']['ac'];
8648								}
8649								if (isset($pl['opt']['mk']['i'])) {
8650									$info = $this->getImageBuffer($pl['opt']['mk']['i']);
8651									if ($info !== false) {
8652										$annots .= ' /I '.$info['n'].' 0 R';
8653									}
8654								}
8655								if (isset($pl['opt']['mk']['ri'])) {
8656									$info = $this->getImageBuffer($pl['opt']['mk']['ri']);
8657									if ($info !== false) {
8658										$annots .= ' /RI '.$info['n'].' 0 R';
8659									}
8660								}
8661								if (isset($pl['opt']['mk']['ix'])) {
8662									$info = $this->getImageBuffer($pl['opt']['mk']['ix']);
8663									if ($info !== false) {
8664										$annots .= ' /IX '.$info['n'].' 0 R';
8665									}
8666								}
8667								if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
8668									$annots .= ' /IF <<';
8669									$if_sw = array('A', 'B', 'S', 'N');
8670									if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
8671										$annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
8672									}
8673									$if_s = array('A', 'P');
8674									if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
8675										$annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
8676									}
8677									if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
8678										$annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
8679									}
8680									if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
8681										$annots .= ' /FB true';
8682									}
8683									$annots .= '>>';
8684								}
8685								if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
8686									$annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
8687								}
8688								$annots .= '>>';
8689							} // end MK
8690							// --- Entries for field dictionaries ---
8691							if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
8692								// set parent
8693								$annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
8694							}
8695							if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8696								$annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
8697							}
8698							if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8699								$annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
8700							}
8701							if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
8702								$annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
8703							}
8704							if (isset($pl['opt']['ff'])) {
8705								if (is_array($pl['opt']['ff'])) {
8706									// array of bit settings
8707									$flag = 0;
8708									foreach($pl['opt']['ff'] as $val) {
8709										$flag += 1 << ($val - 1);
8710									}
8711								} else {
8712									$flag = intval($pl['opt']['ff']);
8713								}
8714								$annots .= ' /Ff '.$flag;
8715							}
8716							if (isset($pl['opt']['maxlen'])) {
8717								$annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
8718							}
8719							if (isset($pl['opt']['v'])) {
8720								$annots .= ' /V';
8721								if (is_array($pl['opt']['v'])) {
8722									foreach ($pl['opt']['v'] AS $optval) {
8723										if (is_float($optval)) {
8724											$optval = sprintf('%F', $optval);
8725										}
8726										$annots .= ' '.$optval;
8727									}
8728								} else {
8729									$annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
8730								}
8731							}
8732							if (isset($pl['opt']['dv'])) {
8733								$annots .= ' /DV';
8734								if (is_array($pl['opt']['dv'])) {
8735									foreach ($pl['opt']['dv'] AS $optval) {
8736										if (is_float($optval)) {
8737											$optval = sprintf('%F', $optval);
8738										}
8739										$annots .= ' '.$optval;
8740									}
8741								} else {
8742									$annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
8743								}
8744							}
8745							if (isset($pl['opt']['rv'])) {
8746								$annots .= ' /RV';
8747								if (is_array($pl['opt']['rv'])) {
8748									foreach ($pl['opt']['rv'] AS $optval) {
8749										if (is_float($optval)) {
8750											$optval = sprintf('%F', $optval);
8751										}
8752										$annots .= ' '.$optval;
8753									}
8754								} else {
8755									$annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
8756								}
8757							}
8758							if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
8759								$annots .= ' /A << '.$pl['opt']['a'].' >>';
8760							}
8761							if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
8762								$annots .= ' /AA << '.$pl['opt']['aa'].' >>';
8763							}
8764							if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8765								$annots .= ' /DA ('.$pl['opt']['da'].')';
8766							}
8767							if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8768								$annots .= ' /Q '.intval($pl['opt']['q']);
8769							}
8770							if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
8771								$annots .= ' /Opt [';
8772								foreach($pl['opt']['opt'] AS $copt) {
8773									if (is_array($copt)) {
8774										$annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
8775									} else {
8776										$annots .= ' '.$this->_textstring($copt, $annot_obj_id);
8777									}
8778								}
8779								$annots .= ']';
8780							}
8781							if (isset($pl['opt']['ti'])) {
8782								$annots .= ' /TI '.intval($pl['opt']['ti']);
8783							}
8784							if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
8785								$annots .= ' /I [';
8786								foreach($pl['opt']['i'] AS $copt) {
8787									$annots .= intval($copt).' ';
8788								}
8789								$annots .= ']';
8790							}
8791							break;
8792						}
8793						case 'screen': {
8794							break;
8795						}
8796						case 'printermark': {
8797							break;
8798						}
8799						case 'trapnet': {
8800							break;
8801						}
8802						case 'watermark': {
8803							break;
8804						}
8805						case '3d': {
8806							break;
8807						}
8808						default: {
8809							break;
8810						}
8811					}
8812					$annots .= '>>';
8813					// create new annotation object
8814					$this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
8815					if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
8816						// store reference of form object
8817						$this->form_obj_id[] = $annot_obj_id;
8818					}
8819				}
8820			}
8821		} // end for each page
8822	}
8823
8824	/**
8825	 * Put appearance streams XObject used to define annotation's appearance states.
8826	 * @param int $w annotation width
8827	 * @param int $h annotation height
8828	 * @param string $stream appearance stream
8829	 * @return int object ID
8830	 * @protected
8831	 * @since 4.8.001 (2009-09-09)
8832	 */
8833	protected function _putAPXObject($w=0, $h=0, $stream='') {
8834		$stream = trim($stream);
8835		$out = $this->_getobj()."\n";
8836		$this->xobjects['AX'.$this->n] = array('n' => $this->n);
8837		$out .= '<<';
8838		$out .= ' /Type /XObject';
8839		$out .= ' /Subtype /Form';
8840		$out .= ' /FormType 1';
8841		if ($this->compress) {
8842			$stream = gzcompress($stream);
8843			$out .= ' /Filter /FlateDecode';
8844		}
8845		$rect = sprintf('%F %F', $w, $h);
8846		$out .= ' /BBox [0 0 '.$rect.']';
8847		$out .= ' /Matrix [1 0 0 1 0 0]';
8848		$out .= ' /Resources 2 0 R';
8849		$stream = $this->_getrawstream($stream);
8850		$out .= ' /Length '.strlen($stream);
8851		$out .= ' >>';
8852		$out .= ' stream'."\n".$stream."\n".'endstream';
8853		$out .= "\n".'endobj';
8854		$this->_out($out);
8855		return $this->n;
8856	}
8857
8858	/**
8859	 * Output fonts.
8860	 * @author Nicola Asuni
8861	 * @protected
8862	 */
8863	protected function _putfonts() {
8864		$nf = $this->n;
8865		foreach ($this->diffs as $diff) {
8866			//Encodings
8867			$this->_newobj();
8868			$this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
8869		}
8870		$mqr = TCPDF_STATIC::get_mqr();
8871		TCPDF_STATIC::set_mqr(false);
8872		foreach ($this->FontFiles as $file => $info) {
8873			// search and get font file to embedd
8874			$fontfile = TCPDF_FONTS::getFontFullPath($file, $info['fontdir']);
8875			if (!TCPDF_STATIC::empty_string($fontfile)) {
8876				$font = file_get_contents($fontfile);
8877				$compressed = (substr($file, -2) == '.z');
8878				if ((!$compressed) AND (isset($info['length2']))) {
8879					$header = (ord($font[0]) == 128);
8880					if ($header) {
8881						// strip first binary header
8882						$font = substr($font, 6);
8883					}
8884					if ($header AND (ord($font[$info['length1']]) == 128)) {
8885						// strip second binary header
8886						$font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
8887					}
8888				} elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
8889					if ($compressed) {
8890						// uncompress font
8891						$font = gzuncompress($font);
8892					}
8893					// merge subset characters
8894					$subsetchars = array(); // used chars
8895					foreach ($info['fontkeys'] as $fontkey) {
8896						$fontinfo = $this->getFontBuffer($fontkey);
8897						$subsetchars += $fontinfo['subsetchars'];
8898					}
8899					// rebuild a font subset
8900					$font = TCPDF_FONTS::_getTrueTypeFontSubset($font, $subsetchars);
8901					// calculate new font length
8902					$info['length1'] = strlen($font);
8903					if ($compressed) {
8904						// recompress font
8905						$font = gzcompress($font);
8906					}
8907				}
8908				$this->_newobj();
8909				$this->FontFiles[$file]['n'] = $this->n;
8910				$stream = $this->_getrawstream($font);
8911				$out = '<< /Length '.strlen($stream);
8912				if ($compressed) {
8913					$out .= ' /Filter /FlateDecode';
8914				}
8915				$out .= ' /Length1 '.$info['length1'];
8916				if (isset($info['length2'])) {
8917					$out .= ' /Length2 '.$info['length2'].' /Length3 0';
8918				}
8919				$out .= ' >>';
8920				$out .= ' stream'."\n".$stream."\n".'endstream';
8921				$out .= "\n".'endobj';
8922				$this->_out($out);
8923			}
8924		}
8925		TCPDF_STATIC::set_mqr($mqr);
8926		foreach ($this->fontkeys as $k) {
8927			//Font objects
8928			$font = $this->getFontBuffer($k);
8929			$type = $font['type'];
8930			$name = $font['name'];
8931			if ($type == 'core') {
8932				// standard core font
8933				$out = $this->_getobj($this->font_obj_ids[$k])."\n";
8934				$out .= '<</Type /Font';
8935				$out .= ' /Subtype /Type1';
8936				$out .= ' /BaseFont /'.$name;
8937				$out .= ' /Name /F'.$font['i'];
8938				if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
8939					$out .= ' /Encoding /WinAnsiEncoding';
8940				}
8941				if ($k == 'helvetica') {
8942					// add default font for annotations
8943					$this->annotation_fonts[$k] = $font['i'];
8944				}
8945				$out .= ' >>';
8946				$out .= "\n".'endobj';
8947				$this->_out($out);
8948			} elseif (($type == 'Type1') OR ($type == 'TrueType')) {
8949				// additional Type1 or TrueType font
8950				$out = $this->_getobj($this->font_obj_ids[$k])."\n";
8951				$out .= '<</Type /Font';
8952				$out .= ' /Subtype /'.$type;
8953				$out .= ' /BaseFont /'.$name;
8954				$out .= ' /Name /F'.$font['i'];
8955				$out .= ' /FirstChar 32 /LastChar 255';
8956				$out .= ' /Widths '.($this->n + 1).' 0 R';
8957				$out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
8958				if ($font['enc']) {
8959					if (isset($font['diff'])) {
8960						$out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
8961					} else {
8962						$out .= ' /Encoding /WinAnsiEncoding';
8963					}
8964				}
8965				$out .= ' >>';
8966				$out .= "\n".'endobj';
8967				$this->_out($out);
8968				// Widths
8969				$this->_newobj();
8970				$s = '[';
8971				for ($i = 32; $i < 256; ++$i) {
8972					if (isset($font['cw'][$i])) {
8973						$s .= $font['cw'][$i].' ';
8974					} else {
8975						$s .= $font['dw'].' ';
8976					}
8977				}
8978				$s .= ']';
8979				$s .= "\n".'endobj';
8980				$this->_out($s);
8981				//Descriptor
8982				$this->_newobj();
8983				$s = '<</Type /FontDescriptor /FontName /'.$name;
8984				foreach ($font['desc'] as $fdk => $fdv) {
8985					if (is_float($fdv)) {
8986						$fdv = sprintf('%F', $fdv);
8987					}
8988					$s .= ' /'.$fdk.' '.$fdv.'';
8989				}
8990				if (!TCPDF_STATIC::empty_string($font['file'])) {
8991					$s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
8992				}
8993				$s .= '>>';
8994				$s .= "\n".'endobj';
8995				$this->_out($s);
8996			} else {
8997				// additional types
8998				$mtd = '_put'.strtolower($type);
8999				if (!method_exists($this, $mtd)) {
9000					$this->Error('Unsupported font type: '.$type);
9001				}
9002				$this->$mtd($font);
9003			}
9004		}
9005	}
9006
9007	/**
9008	 * Adds unicode fonts.<br>
9009	 * Based on PDF Reference 1.3 (section 5)
9010	 * @param array $font font data
9011	 * @protected
9012	 * @author Nicola Asuni
9013	 * @since 1.52.0.TC005 (2005-01-05)
9014	 */
9015	protected function _puttruetypeunicode($font) {
9016		$fontname = '';
9017		if ($font['subset']) {
9018			// change name for font subsetting
9019			$subtag = sprintf('%06u', $font['i']);
9020			$subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
9021			$fontname .= $subtag.'+';
9022		}
9023		$fontname .= $font['name'];
9024		// Type0 Font
9025		// A composite font composed of other fonts, organized hierarchically
9026		$out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
9027		$out .= '<< /Type /Font';
9028		$out .= ' /Subtype /Type0';
9029		$out .= ' /BaseFont /'.$fontname;
9030		$out .= ' /Name /F'.$font['i'];
9031		$out .= ' /Encoding /'.$font['enc'];
9032		$out .= ' /ToUnicode '.($this->n + 1).' 0 R';
9033		$out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
9034		$out .= ' >>';
9035		$out .= "\n".'endobj';
9036		$this->_out($out);
9037		// ToUnicode map for Identity-H
9038		$stream = TCPDF_FONT_DATA::$uni_identity_h;
9039		// ToUnicode Object
9040		$this->_newobj();
9041		$stream = ($this->compress) ? gzcompress($stream) : $stream;
9042		$filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9043		$stream = $this->_getrawstream($stream);
9044		$this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
9045		// CIDFontType2
9046		// A CIDFont whose glyph descriptions are based on TrueType font technology
9047		$oid = $this->_newobj();
9048		$out = '<< /Type /Font';
9049		$out .= ' /Subtype /CIDFontType2';
9050		$out .= ' /BaseFont /'.$fontname;
9051		// A dictionary containing entries that define the character collection of the CIDFont.
9052		$cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9053		$cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9054		$cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9055		$out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
9056		$out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
9057		$out .= ' /DW '.$font['dw']; // default width
9058		$out .= "\n".TCPDF_FONTS::_putfontwidths($font, 0);
9059		if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
9060			$out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
9061		}
9062		$out .= ' >>';
9063		$out .= "\n".'endobj';
9064		$this->_out($out);
9065		// Font descriptor
9066		// A font descriptor describing the CIDFont default metrics other than its glyph widths
9067		$this->_newobj();
9068		$out = '<< /Type /FontDescriptor';
9069		$out .= ' /FontName /'.$fontname;
9070		foreach ($font['desc'] as $key => $value) {
9071			if (is_float($value)) {
9072				$value = sprintf('%F', $value);
9073			}
9074			$out .= ' /'.$key.' '.$value;
9075		}
9076		$fontdir = false;
9077		if (!TCPDF_STATIC::empty_string($font['file'])) {
9078			// A stream containing a TrueType font
9079			$out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
9080			$fontdir = $this->FontFiles[$font['file']]['fontdir'];
9081		}
9082		$out .= ' >>';
9083		$out .= "\n".'endobj';
9084		$this->_out($out);
9085		if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
9086			$this->_newobj();
9087			// Embed CIDToGIDMap
9088			// A specification of the mapping from CIDs to glyph indices
9089			// search and get CTG font file to embedd
9090			$ctgfile = strtolower($font['ctg']);
9091			// search and get ctg font file to embedd
9092			$fontfile = TCPDF_FONTS::getFontFullPath($ctgfile, $fontdir);
9093			if (TCPDF_STATIC::empty_string($fontfile)) {
9094				$this->Error('Font file not found: '.$ctgfile);
9095			}
9096			$stream = $this->_getrawstream(file_get_contents($fontfile));
9097			$out = '<< /Length '.strlen($stream).'';
9098			if (substr($fontfile, -2) == '.z') { // check file extension
9099				// Decompresses data encoded using the public-domain
9100				// zlib/deflate compression method, reproducing the
9101				// original text or binary data
9102				$out .= ' /Filter /FlateDecode';
9103			}
9104			$out .= ' >>';
9105			$out .= ' stream'."\n".$stream."\n".'endstream';
9106			$out .= "\n".'endobj';
9107			$this->_out($out);
9108		}
9109	}
9110
9111	/**
9112	 * Output CID-0 fonts.
9113	 * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format
9114	 * @param array $font font data
9115	 * @protected
9116	 * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
9117	 * @since 3.2.000 (2008-06-23)
9118	 */
9119	protected function _putcidfont0($font) {
9120		$cidoffset = 0;
9121		if (!isset($font['cw'][1])) {
9122			$cidoffset = 31;
9123		}
9124		if (isset($font['cidinfo']['uni2cid'])) {
9125			// convert unicode to cid.
9126			$uni2cid = $font['cidinfo']['uni2cid'];
9127			$cw = array();
9128			foreach ($font['cw'] as $uni => $width) {
9129				if (isset($uni2cid[$uni])) {
9130					$cw[($uni2cid[$uni] + $cidoffset)] = $width;
9131				} elseif ($uni < 256) {
9132					$cw[$uni] = $width;
9133				} // else unknown character
9134			}
9135			$font = array_merge($font, array('cw' => $cw));
9136		}
9137		$name = $font['name'];
9138		$enc = $font['enc'];
9139		if ($enc) {
9140			$longname = $name.'-'.$enc;
9141		} else {
9142			$longname = $name;
9143		}
9144		$out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
9145		$out .= '<</Type /Font';
9146		$out .= ' /Subtype /Type0';
9147		$out .= ' /BaseFont /'.$longname;
9148		$out .= ' /Name /F'.$font['i'];
9149		if ($enc) {
9150			$out .= ' /Encoding /'.$enc;
9151		}
9152		$out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
9153		$out .= ' >>';
9154		$out .= "\n".'endobj';
9155		$this->_out($out);
9156		$oid = $this->_newobj();
9157		$out = '<</Type /Font';
9158		$out .= ' /Subtype /CIDFontType0';
9159		$out .= ' /BaseFont /'.$name;
9160		$cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9161		$cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9162		$cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9163		$out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
9164		$out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
9165		$out .= ' /DW '.$font['dw'];
9166		$out .= "\n".TCPDF_FONTS::_putfontwidths($font, $cidoffset);
9167		$out .= ' >>';
9168		$out .= "\n".'endobj';
9169		$this->_out($out);
9170		$this->_newobj();
9171		$s = '<</Type /FontDescriptor /FontName /'.$name;
9172		foreach ($font['desc'] as $k => $v) {
9173			if ($k != 'Style') {
9174				if (is_float($v)) {
9175					$v = sprintf('%F', $v);
9176				}
9177				$s .= ' /'.$k.' '.$v.'';
9178			}
9179		}
9180		$s .= '>>';
9181		$s .= "\n".'endobj';
9182		$this->_out($s);
9183	}
9184
9185	/**
9186	 * Output images.
9187	 * @protected
9188	 */
9189	protected function _putimages() {
9190		$filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9191		foreach ($this->imagekeys as $file) {
9192			$info = $this->getImageBuffer($file);
9193			// set object for alternate images array
9194			if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
9195				$altoid = $this->_newobj();
9196				$out = '[';
9197				foreach ($info['altimgs'] as $altimage) {
9198					if (isset($this->xobjects['I'.$altimage[0]]['n'])) {
9199						$out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R';
9200						$out .= ' /DefaultForPrinting';
9201						if ($altimage[1] === true) {
9202							$out .= ' true';
9203						} else {
9204							$out .= ' false';
9205						}
9206						$out .= ' >>';
9207					}
9208				}
9209				$out .= ' ]';
9210				$out .= "\n".'endobj';
9211				$this->_out($out);
9212			}
9213			// set image object
9214			$oid = $this->_newobj();
9215			$this->xobjects['I'.$info['i']] = array('n' => $oid);
9216			$this->setImageSubBuffer($file, 'n', $this->n);
9217			$out = '<</Type /XObject';
9218			$out .= ' /Subtype /Image';
9219			$out .= ' /Width '.$info['w'];
9220			$out .= ' /Height '.$info['h'];
9221			if (array_key_exists('masked', $info)) {
9222				$out .= ' /SMask '.($this->n - 1).' 0 R';
9223			}
9224			// set color space
9225			$icc = false;
9226			if (isset($info['icc']) AND ($info['icc'] !== false)) {
9227				// ICC Colour Space
9228				$icc = true;
9229				$out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]';
9230			} elseif ($info['cs'] == 'Indexed') {
9231				// Indexed Colour Space
9232				$out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
9233			} else {
9234				// Device Colour Space
9235				$out .= ' /ColorSpace /'.$info['cs'];
9236			}
9237			if ($info['cs'] == 'DeviceCMYK') {
9238				$out .= ' /Decode [1 0 1 0 1 0 1 0]';
9239			}
9240			$out .= ' /BitsPerComponent '.$info['bpc'];
9241			if (isset($altoid) AND ($altoid > 0)) {
9242				// reference to alternate images dictionary
9243				$out .= ' /Alternates '.$altoid.' 0 R';
9244			}
9245			if (isset($info['exurl']) AND !empty($info['exurl'])) {
9246				// external stream
9247				$out .= ' /Length 0';
9248				$out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
9249				if (isset($info['f'])) {
9250					$out .= ' /FFilter /'.$info['f'];
9251				}
9252				$out .= ' >>';
9253				$out .= ' stream'."\n".'endstream';
9254			} else {
9255				if (isset($info['f'])) {
9256					$out .= ' /Filter /'.$info['f'];
9257				}
9258				if (isset($info['parms'])) {
9259					$out .= ' '.$info['parms'];
9260				}
9261				if (isset($info['trns']) AND is_array($info['trns'])) {
9262					$trns = '';
9263					$count_info = count($info['trns']);
9264					if ($info['cs'] == 'Indexed') {
9265						$maxval =(pow(2, $info['bpc']) - 1);
9266						for ($i = 0; $i < $count_info; ++$i) {
9267							if (($info['trns'][$i] != 0) AND ($info['trns'][$i] != $maxval)) {
9268								// this is not a binary type mask @TODO: create a SMask
9269								$trns = '';
9270								break;
9271							} elseif (empty($trns) AND ($info['trns'][$i] == 0)) {
9272								// store the first fully transparent value
9273								$trns .= $i.' '.$i.' ';
9274							}
9275						}
9276					} else {
9277						// grayscale or RGB
9278						for ($i = 0; $i < $count_info; ++$i) {
9279							if ($info['trns'][$i] == 0) {
9280								$trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
9281							}
9282						}
9283					}
9284					// Colour Key Masking
9285					if (!empty($trns)) {
9286						$out .= ' /Mask ['.$trns.']';
9287					}
9288				}
9289				$stream = $this->_getrawstream($info['data']);
9290				$out .= ' /Length '.strlen($stream).' >>';
9291				$out .= ' stream'."\n".$stream."\n".'endstream';
9292			}
9293			$out .= "\n".'endobj';
9294			$this->_out($out);
9295			if ($icc) {
9296				// ICC colour profile
9297				$this->_newobj();
9298				$icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
9299				$icc = $this->_getrawstream($icc);
9300				$this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9301			} elseif ($info['cs'] == 'Indexed') {
9302				// colour palette
9303				$this->_newobj();
9304				$pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
9305				$pal = $this->_getrawstream($pal);
9306				$this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
9307			}
9308		}
9309	}
9310
9311	/**
9312	 * Output Form XObjects Templates.
9313	 * @author Nicola Asuni
9314	 * @since 5.8.017 (2010-08-24)
9315	 * @protected
9316	 * @see startTemplate(), endTemplate(), printTemplate()
9317	 */
9318	protected function _putxobjects() {
9319		foreach ($this->xobjects as $key => $data) {
9320			if (isset($data['outdata'])) {
9321				$stream = str_replace($this->epsmarker, '', trim($data['outdata']));
9322				$out = $this->_getobj($data['n'])."\n";
9323				$out .= '<<';
9324				$out .= ' /Type /XObject';
9325				$out .= ' /Subtype /Form';
9326				$out .= ' /FormType 1';
9327				if ($this->compress) {
9328					$stream = gzcompress($stream);
9329					$out .= ' /Filter /FlateDecode';
9330				}
9331				$out .= sprintf(' /BBox [%F %F %F %F]', ($data['x'] * $this->k), (-$data['y'] * $this->k), (($data['w'] + $data['x']) * $this->k), (($data['h'] - $data['y']) * $this->k));
9332				$out .= ' /Matrix [1 0 0 1 0 0]';
9333				$out .= ' /Resources <<';
9334				$out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9335				if (!$this->pdfa_mode || $this->pdfa_version >= 2) {
9336					// transparency
9337					if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
9338						$out .= ' /ExtGState <<';
9339						foreach ($data['extgstates'] as $k => $extgstate) {
9340							if (isset($this->extgstates[$k]['name'])) {
9341								$out .= ' /'.$this->extgstates[$k]['name'];
9342							} else {
9343								$out .= ' /GS'.$k;
9344							}
9345							$out .= ' '.$this->extgstates[$k]['n'].' 0 R';
9346						}
9347						$out .= ' >>';
9348					}
9349					if (isset($data['gradients']) AND !empty($data['gradients'])) {
9350						$gp = '';
9351						$gs = '';
9352						foreach ($data['gradients'] as $id => $grad) {
9353							// gradient patterns
9354							$gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R';
9355							// gradient shadings
9356							$gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R';
9357						}
9358						$out .= ' /Pattern <<'.$gp.' >>';
9359						$out .= ' /Shading <<'.$gs.' >>';
9360					}
9361				}
9362				// spot colors
9363				if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
9364					$out .= ' /ColorSpace <<';
9365					foreach ($data['spot_colors'] as $name => $color) {
9366						$out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R';
9367					}
9368					$out .= ' >>';
9369				}
9370				// fonts
9371				if (!empty($data['fonts'])) {
9372					$out .= ' /Font <<';
9373					foreach ($data['fonts'] as $fontkey => $fontid) {
9374						$out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9375					}
9376					$out .= ' >>';
9377				}
9378				// images or nested xobjects
9379				if (!empty($data['images']) OR !empty($data['xobjects'])) {
9380					$out .= ' /XObject <<';
9381					foreach ($data['images'] as $imgid) {
9382						$out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
9383					}
9384					foreach ($data['xobjects'] as $sub_id => $sub_objid) {
9385						$out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
9386					}
9387					$out .= ' >>';
9388				}
9389				$out .= ' >>'; //end resources
9390				if (isset($data['group']) AND ($data['group'] !== false)) {
9391					// set transparency group
9392					$out .= ' /Group << /Type /Group /S /Transparency';
9393					if (is_array($data['group'])) {
9394						if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
9395							$out .= ' /CS /'.$data['group']['CS'];
9396						}
9397						if (isset($data['group']['I'])) {
9398							$out .= ' /I /'.($data['group']['I']===true?'true':'false');
9399						}
9400						if (isset($data['group']['K'])) {
9401							$out .= ' /K /'.($data['group']['K']===true?'true':'false');
9402						}
9403					}
9404					$out .= ' >>';
9405				}
9406				$stream = $this->_getrawstream($stream, $data['n']);
9407				$out .= ' /Length '.strlen($stream);
9408				$out .= ' >>';
9409				$out .= ' stream'."\n".$stream."\n".'endstream';
9410				$out .= "\n".'endobj';
9411				$this->_out($out);
9412			}
9413		}
9414	}
9415
9416	/**
9417	 * Output Spot Colors Resources.
9418	 * @protected
9419	 * @since 4.0.024 (2008-09-12)
9420	 */
9421	protected function _putspotcolors() {
9422		foreach ($this->spot_colors as $name => $color) {
9423			$this->_newobj();
9424			$this->spot_colors[$name]['n'] = $this->n;
9425			$out = '[/Separation /'.str_replace(' ', '#20', $name);
9426			$out .= ' /DeviceCMYK <<';
9427			$out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
9428			$out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
9429			$out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
9430			$out .= "\n".'endobj';
9431			$this->_out($out);
9432		}
9433	}
9434
9435	/**
9436	 * Return XObjects Dictionary.
9437	 * @return string XObjects dictionary
9438	 * @protected
9439	 * @since 5.8.014 (2010-08-23)
9440	 */
9441	protected function _getxobjectdict() {
9442		$out = '';
9443		foreach ($this->xobjects as $id => $objid) {
9444			$out .= ' /'.$id.' '.$objid['n'].' 0 R';
9445		}
9446		return $out;
9447	}
9448
9449	/**
9450	 * Output Resources Dictionary.
9451	 * @protected
9452	 */
9453	protected function _putresourcedict() {
9454		$out = $this->_getobj(2)."\n";
9455		$out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9456		$out .= ' /Font <<';
9457		foreach ($this->fontkeys as $fontkey) {
9458			$font = $this->getFontBuffer($fontkey);
9459			$out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
9460		}
9461		$out .= ' >>';
9462		$out .= ' /XObject <<';
9463		$out .= $this->_getxobjectdict();
9464		$out .= ' >>';
9465		// layers
9466		if (!empty($this->pdflayers)) {
9467			$out .= ' /Properties <<';
9468			foreach ($this->pdflayers as $layer) {
9469				$out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
9470			}
9471			$out .= ' >>';
9472		}
9473		if (!$this->pdfa_mode || $this->pdfa_version >= 2) {
9474			// transparency
9475			if (isset($this->extgstates) AND !empty($this->extgstates)) {
9476				$out .= ' /ExtGState <<';
9477				foreach ($this->extgstates as $k => $extgstate) {
9478					if (isset($extgstate['name'])) {
9479						$out .= ' /'.$extgstate['name'];
9480					} else {
9481						$out .= ' /GS'.$k;
9482					}
9483					$out .= ' '.$extgstate['n'].' 0 R';
9484				}
9485				$out .= ' >>';
9486			}
9487			if (isset($this->gradients) AND !empty($this->gradients)) {
9488				$gp = '';
9489				$gs = '';
9490				foreach ($this->gradients as $id => $grad) {
9491					// gradient patterns
9492					$gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
9493					// gradient shadings
9494					$gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
9495				}
9496				$out .= ' /Pattern <<'.$gp.' >>';
9497				$out .= ' /Shading <<'.$gs.' >>';
9498			}
9499		}
9500		// spot colors
9501		if (isset($this->spot_colors) AND !empty($this->spot_colors)) {
9502			$out .= ' /ColorSpace <<';
9503			foreach ($this->spot_colors as $color) {
9504				$out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
9505			}
9506			$out .= ' >>';
9507		}
9508		$out .= ' >>';
9509		$out .= "\n".'endobj';
9510		$this->_out($out);
9511	}
9512
9513	/**
9514	 * Output Resources.
9515	 * @protected
9516	 */
9517	protected function _putresources() {
9518		$this->_putextgstates();
9519		$this->_putocg();
9520		$this->_putfonts();
9521		$this->_putimages();
9522		$this->_putspotcolors();
9523		$this->_putshaders();
9524		$this->_putxobjects();
9525		$this->_putresourcedict();
9526		$this->_putdests();
9527		$this->_putEmbeddedFiles();
9528		$this->_putannotsobjs();
9529		$this->_putjavascript();
9530		$this->_putbookmarks();
9531		$this->_putencryption();
9532	}
9533
9534	/**
9535	 * Adds some Metadata information (Document Information Dictionary)
9536	 * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference)
9537	 * @return int object id
9538	 * @protected
9539	 */
9540	protected function _putinfo() {
9541		$oid = $this->_newobj();
9542		$out = '<<';
9543		// store current isunicode value
9544		$prev_isunicode = $this->isunicode;
9545		if ($this->docinfounicode) {
9546			$this->isunicode = true;
9547		}
9548		if (!TCPDF_STATIC::empty_string($this->title)) {
9549			// The document's title.
9550			$out .= ' /Title '.$this->_textstring($this->title, $oid);
9551		}
9552		if (!TCPDF_STATIC::empty_string($this->author)) {
9553			// The name of the person who created the document.
9554			$out .= ' /Author '.$this->_textstring($this->author, $oid);
9555		}
9556		if (!TCPDF_STATIC::empty_string($this->subject)) {
9557			// The subject of the document.
9558			$out .= ' /Subject '.$this->_textstring($this->subject, $oid);
9559		}
9560		if (!TCPDF_STATIC::empty_string($this->keywords)) {
9561			// Keywords associated with the document.
9562			$out .= ' /Keywords '.$this->_textstring($this->keywords, $oid);
9563		}
9564		if (!TCPDF_STATIC::empty_string($this->creator)) {
9565			// If the document was converted to PDF from another format, the name of the conforming product that created the original document from which it was converted.
9566			$out .= ' /Creator '.$this->_textstring($this->creator, $oid);
9567		}
9568		// restore previous isunicode value
9569		$this->isunicode = $prev_isunicode;
9570		// default producer
9571		$out .= ' /Producer '.$this->_textstring(TCPDF_STATIC::getTCPDFProducer(), $oid);
9572		// The date and time the document was created, in human-readable form
9573		$out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp);
9574		// The date and time the document was most recently modified, in human-readable form
9575		$out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp);
9576		// A name object indicating whether the document has been modified to include trapping information
9577		$out .= ' /Trapped /False';
9578		$out .= ' >>';
9579		$out .= "\n".'endobj';
9580		$this->_out($out);
9581		return $oid;
9582	}
9583
9584	/**
9585	 * Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag.
9586	 * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method!
9587	 * @param string $xmp Custom XMP data.
9588	 * @since 5.9.128 (2011-10-06)
9589	 * @public
9590	 */
9591	public function setExtraXMP($xmp) {
9592		$this->custom_xmp = $xmp;
9593	}
9594
9595	/**
9596	 * Set additional XMP data to be added on the default XMP data just before the end of "rdf:RDF" tag.
9597	 * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method!
9598	 * @param string $xmp Custom XMP RDF data.
9599	 * @since 6.3.0 (2019-09-19)
9600	 * @public
9601	 */
9602	public function setExtraXMPRDF($xmp) {
9603		$this->custom_xmp_rdf = $xmp;
9604	}
9605
9606	/**
9607	 * Put XMP data object and return ID.
9608	 * @return int The object ID.
9609	 * @since 5.9.121 (2011-09-28)
9610	 * @protected
9611	 */
9612	protected function _putXMP() {
9613		$oid = $this->_newobj();
9614		// store current isunicode value
9615		$prev_isunicode = $this->isunicode;
9616		$this->isunicode = true;
9617		$prev_encrypted = $this->encrypted;
9618		$this->encrypted = false;
9619		// set XMP data
9620		$xmp = '<?xpacket begin="'.TCPDF_FONTS::unichr(0xfeff, $this->isunicode).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
9621		$xmp .= '<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.1-c043 52.372728, 2009/01/18-15:08:04">'."\n";
9622		$xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
9623		$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
9624		$xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
9625		$xmp .= "\t\t\t".'<dc:title>'."\n";
9626		$xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9627		$xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->title).'</rdf:li>'."\n";
9628		$xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9629		$xmp .= "\t\t\t".'</dc:title>'."\n";
9630		$xmp .= "\t\t\t".'<dc:creator>'."\n";
9631		$xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
9632		$xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->author).'</rdf:li>'."\n";
9633		$xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
9634		$xmp .= "\t\t\t".'</dc:creator>'."\n";
9635		$xmp .= "\t\t\t".'<dc:description>'."\n";
9636		$xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9637		$xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->subject).'</rdf:li>'."\n";
9638		$xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9639		$xmp .= "\t\t\t".'</dc:description>'."\n";
9640		$xmp .= "\t\t\t".'<dc:subject>'."\n";
9641		$xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9642		$xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->keywords).'</rdf:li>'."\n";
9643		$xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9644		$xmp .= "\t\t\t".'</dc:subject>'."\n";
9645		$xmp .= "\t\t".'</rdf:Description>'."\n";
9646		// convert doc creation date format
9647		$dcdate = TCPDF_STATIC::getFormattedDate($this->doc_creation_timestamp);
9648		$doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2);
9649		$doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2);
9650		$doccreationdate .= substr($dcdate, 14, 3).':'.substr($dcdate, 18, 2);
9651		$doccreationdate = TCPDF_STATIC::_escapeXML($doccreationdate);
9652		// convert doc modification date format
9653		$dmdate = TCPDF_STATIC::getFormattedDate($this->doc_modification_timestamp);
9654		$docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2);
9655		$docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2);
9656		$docmoddate .= substr($dmdate, 14, 3).':'.substr($dmdate, 18, 2);
9657		$docmoddate = TCPDF_STATIC::_escapeXML($docmoddate);
9658		$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
9659		$xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n";
9660		$xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n";
9661		$xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n";
9662		$xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n";
9663		$xmp .= "\t\t".'</rdf:Description>'."\n";
9664		$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
9665		$xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC::_escapeXML($this->keywords).'</pdf:Keywords>'."\n";
9666		$xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC::_escapeXML(TCPDF_STATIC::getTCPDFProducer()).'</pdf:Producer>'."\n";
9667		$xmp .= "\t\t".'</rdf:Description>'."\n";
9668		$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
9669		$uuid = 'uuid:'.substr($this->file_id, 0, 8).'-'.substr($this->file_id, 8, 4).'-'.substr($this->file_id, 12, 4).'-'.substr($this->file_id, 16, 4).'-'.substr($this->file_id, 20, 12);
9670		$xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
9671		$xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
9672		$xmp .= "\t\t".'</rdf:Description>'."\n";
9673		if ($this->pdfa_mode) {
9674			$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
9675			$xmp .= "\t\t\t".'<pdfaid:part>'.$this->pdfa_version.'</pdfaid:part>'."\n";
9676			$xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
9677			$xmp .= "\t\t".'</rdf:Description>'."\n";
9678		}
9679		// XMP extension schemas
9680		$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/" xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#" xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">'."\n";
9681		$xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
9682		$xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9683		$xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9684		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
9685		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
9686		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
9687		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9688		$xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9689		$xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9690		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9691		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Adobe PDF Schema</pdfaProperty:description>'."\n";
9692		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9693		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9694		$xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9695		$xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9696		$xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9697		$xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9698		$xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9699		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
9700		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
9701		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
9702		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9703		$xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9704		$xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9705		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9706		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
9707		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9708		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9709		$xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9710		$xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9711		$xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9712		$xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9713		$xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9714		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
9715		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
9716		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
9717		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9718		$xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9719		$xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9720		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9721		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
9722		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
9723		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
9724		$xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9725		$xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9726		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9727		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
9728		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
9729		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9730		$xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9731		$xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9732		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9733		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
9734		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
9735		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9736		$xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9737		$xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9738		$xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9739		$xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9740		$xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9741		$xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
9742		$xmp .= "\t\t".'</rdf:Description>'."\n";
9743		$xmp .= $this->custom_xmp_rdf;
9744		$xmp .= "\t".'</rdf:RDF>'."\n";
9745		$xmp .= $this->custom_xmp;
9746		$xmp .= '</x:xmpmeta>'."\n";
9747		$xmp .= '<?xpacket end="w"?>';
9748		$out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
9749		// restore previous isunicode value
9750		$this->isunicode = $prev_isunicode;
9751		$this->encrypted = $prev_encrypted;
9752		$this->_out($out);
9753		return $oid;
9754	}
9755
9756	/**
9757	 * Output Catalog.
9758	 * @return int object id
9759	 * @protected
9760	 */
9761	protected function _putcatalog() {
9762		// put XMP
9763		$xmpobj = $this->_putXMP();
9764		// if required, add standard sRGB ICC colour profile
9765		if ($this->pdfa_mode OR $this->force_srgb) {
9766			$iccobj = $this->_newobj();
9767			$icc = file_get_contents(dirname(__FILE__).'/include/sRGB.icc');
9768			$filter = '';
9769			if ($this->compress) {
9770				$filter = ' /Filter /FlateDecode';
9771				$icc = gzcompress($icc);
9772			}
9773			$icc = $this->_getrawstream($icc);
9774			$this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9775		}
9776		// start catalog
9777		$oid = $this->_newobj();
9778		$out = '<< /Type /Catalog';
9779		$out .= ' /Version /'.$this->PDFVersion;
9780		//$out .= ' /Extensions <<>>';
9781		$out .= ' /Pages 1 0 R';
9782		//$out .= ' /PageLabels ' //...;
9783		$out .= ' /Names <<';
9784		if ((!$this->pdfa_mode) AND !empty($this->n_js)) {
9785			$out .= ' /JavaScript '.$this->n_js;
9786		}
9787		if (!empty($this->efnames)) {
9788			$out .= ' /EmbeddedFiles <</Names [';
9789			foreach ($this->efnames AS $fn => $fref) {
9790				$out .= ' '.$this->_datastring($fn).' '.$fref;
9791			}
9792			$out .= ' ]>>';
9793		}
9794		$out .= ' >>';
9795		if (!empty($this->dests)) {
9796			$out .= ' /Dests '.($this->n_dests).' 0 R';
9797		}
9798		$out .= $this->_putviewerpreferences();
9799		if (isset($this->LayoutMode) AND (!TCPDF_STATIC::empty_string($this->LayoutMode))) {
9800			$out .= ' /PageLayout /'.$this->LayoutMode;
9801		}
9802		if (isset($this->PageMode) AND (!TCPDF_STATIC::empty_string($this->PageMode))) {
9803			$out .= ' /PageMode /'.$this->PageMode;
9804		}
9805		if (count($this->outlines) > 0) {
9806			$out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
9807			$out .= ' /PageMode /UseOutlines';
9808		}
9809		//$out .= ' /Threads []';
9810		if ($this->ZoomMode == 'fullpage') {
9811			$out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
9812		} elseif ($this->ZoomMode == 'fullwidth') {
9813			$out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
9814		} elseif ($this->ZoomMode == 'real') {
9815			$out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
9816		} elseif (!is_string($this->ZoomMode)) {
9817			$out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100));
9818		}
9819		//$out .= ' /AA <<>>';
9820		//$out .= ' /URI <<>>';
9821		$out .= ' /Metadata '.$xmpobj.' 0 R';
9822		//$out .= ' /StructTreeRoot <<>>';
9823		//$out .= ' /MarkInfo <<>>';
9824		if (isset($this->l['a_meta_language'])) {
9825			$out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
9826		}
9827		//$out .= ' /SpiderInfo <<>>';
9828		// set OutputIntent to sRGB IEC61966-2.1 if required
9829		if ($this->pdfa_mode OR $this->force_srgb) {
9830			$out .= ' /OutputIntents [<<';
9831			$out .= ' /Type /OutputIntent';
9832			$out .= ' /S /GTS_PDFA1';
9833			$out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9834			$out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9835			$out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
9836			$out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9837			$out .= ' /DestOutputProfile '.$iccobj.' 0 R';
9838			$out .= ' >>]';
9839		}
9840		//$out .= ' /PieceInfo <<>>';
9841		if (!empty($this->pdflayers)) {
9842			$lyrobjs = '';
9843			$lyrobjs_off = '';
9844			$lyrobjs_lock = '';
9845			foreach ($this->pdflayers as $layer) {
9846				$layer_obj_ref = ' '.$layer['objid'].' 0 R';
9847				$lyrobjs .= $layer_obj_ref;
9848				if ($layer['view'] === false) {
9849					$lyrobjs_off .= $layer_obj_ref;
9850				}
9851				if ($layer['lock']) {
9852					$lyrobjs_lock .= $layer_obj_ref;
9853				}
9854			}
9855			$out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
9856			$out .= ' /D <<';
9857			$out .= ' /Name '.$this->_textstring('Layers', $oid);
9858			$out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
9859			$out .= ' /BaseState /ON';
9860			$out .= ' /OFF ['.$lyrobjs_off.']';
9861			$out .= ' /Locked ['.$lyrobjs_lock.']';
9862			$out .= ' /Intent /View';
9863			$out .= ' /AS [';
9864			$out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
9865			$out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
9866			$out .= ' ]';
9867			$out .= ' /Order ['.$lyrobjs.']';
9868			$out .= ' /ListMode /AllPages';
9869			//$out .= ' /RBGroups ['..']';
9870			//$out .= ' /Locked ['..']';
9871			$out .= ' >>';
9872			$out .= ' >>';
9873		}
9874		// AcroForm
9875		if (!empty($this->form_obj_id)
9876			OR ($this->sign AND isset($this->signature_data['cert_type']))
9877			OR !empty($this->empty_signature_appearance)) {
9878			$out .= ' /AcroForm <<';
9879			$objrefs = '';
9880			if ($this->sign AND isset($this->signature_data['cert_type'])) {
9881				// set reference for signature object
9882				$objrefs .= $this->sig_obj_id.' 0 R';
9883			}
9884			if (!empty($this->empty_signature_appearance)) {
9885				foreach ($this->empty_signature_appearance as $esa) {
9886					// set reference for empty signature objects
9887					$objrefs .= ' '.$esa['objid'].' 0 R';
9888				}
9889			}
9890			if (!empty($this->form_obj_id)) {
9891				foreach($this->form_obj_id as $objid) {
9892					$objrefs .= ' '.$objid.' 0 R';
9893				}
9894			}
9895			$out .= ' /Fields ['.$objrefs.']';
9896			// It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
9897			if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
9898				$out .= ' /NeedAppearances false';
9899			}
9900			if ($this->sign AND isset($this->signature_data['cert_type'])) {
9901				if ($this->signature_data['cert_type'] > 0) {
9902					$out .= ' /SigFlags 3';
9903				} else {
9904					$out .= ' /SigFlags 1';
9905				}
9906			}
9907			//$out .= ' /CO ';
9908			if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
9909				$out .= ' /DR <<';
9910				$out .= ' /Font <<';
9911				foreach ($this->annotation_fonts as $fontkey => $fontid) {
9912					$out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9913				}
9914				$out .= ' >> >>';
9915			}
9916			$font = $this->getFontBuffer('helvetica');
9917			$out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
9918			$out .= ' /Q '.(($this->rtl)?'2':'0');
9919			//$out .= ' /XFA ';
9920			$out .= ' >>';
9921			// signatures
9922			if ($this->sign AND isset($this->signature_data['cert_type'])
9923				AND (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A'))) {
9924				if ($this->signature_data['cert_type'] > 0) {
9925					$out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
9926				} else {
9927					$out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
9928				}
9929			}
9930		}
9931		//$out .= ' /Legal <<>>';
9932		//$out .= ' /Requirements []';
9933		//$out .= ' /Collection <<>>';
9934		//$out .= ' /NeedsRendering true';
9935		$out .= ' >>';
9936		$out .= "\n".'endobj';
9937		$this->_out($out);
9938		return $oid;
9939	}
9940
9941	/**
9942	 * Output viewer preferences.
9943	 * @return string for viewer preferences
9944	 * @author Nicola asuni
9945	 * @since 3.1.000 (2008-06-09)
9946	 * @protected
9947	 */
9948	protected function _putviewerpreferences() {
9949		$vp = $this->viewer_preferences;
9950		$out = ' /ViewerPreferences <<';
9951		if ($this->rtl) {
9952			$out .= ' /Direction /R2L';
9953		} else {
9954			$out .= ' /Direction /L2R';
9955		}
9956		if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) {
9957			$out .= ' /HideToolbar true';
9958		}
9959		if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) {
9960			$out .= ' /HideMenubar true';
9961		}
9962		if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) {
9963			$out .= ' /HideWindowUI true';
9964		}
9965		if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) {
9966			$out .= ' /FitWindow true';
9967		}
9968		if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) {
9969			$out .= ' /CenterWindow true';
9970		}
9971		if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) {
9972			$out .= ' /DisplayDocTitle true';
9973		}
9974		if (isset($vp['NonFullScreenPageMode'])) {
9975			$out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode'];
9976		}
9977		if (isset($vp['ViewArea'])) {
9978			$out .= ' /ViewArea /'.$vp['ViewArea'];
9979		}
9980		if (isset($vp['ViewClip'])) {
9981			$out .= ' /ViewClip /'.$vp['ViewClip'];
9982		}
9983		if (isset($vp['PrintArea'])) {
9984			$out .= ' /PrintArea /'.$vp['PrintArea'];
9985		}
9986		if (isset($vp['PrintClip'])) {
9987			$out .= ' /PrintClip /'.$vp['PrintClip'];
9988		}
9989		if (isset($vp['PrintScaling'])) {
9990			$out .= ' /PrintScaling /'.$vp['PrintScaling'];
9991		}
9992		if (isset($vp['Duplex']) AND (!TCPDF_STATIC::empty_string($vp['Duplex']))) {
9993			$out .= ' /Duplex /'.$vp['Duplex'];
9994		}
9995		if (isset($vp['PickTrayByPDFSize'])) {
9996			if ($vp['PickTrayByPDFSize']) {
9997				$out .= ' /PickTrayByPDFSize true';
9998			} else {
9999				$out .= ' /PickTrayByPDFSize false';
10000			}
10001		}
10002		if (isset($vp['PrintPageRange'])) {
10003			$PrintPageRangeNum = '';
10004			foreach ($vp['PrintPageRange'] as $k => $v) {
10005				$PrintPageRangeNum .= ' '.($v - 1).'';
10006			}
10007			$out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
10008		}
10009		if (isset($vp['NumCopies'])) {
10010			$out .= ' /NumCopies '.intval($vp['NumCopies']);
10011		}
10012		$out .= ' >>';
10013		return $out;
10014	}
10015
10016	/**
10017	 * Output PDF File Header (7.5.2).
10018	 * @protected
10019	 */
10020	protected function _putheader() {
10021		$this->_out('%PDF-'.$this->PDFVersion);
10022		$this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
10023	}
10024
10025	/**
10026	 * Output end of document (EOF).
10027	 * @protected
10028	 */
10029	protected function _enddoc() {
10030		if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
10031			// save subset chars of the previous font
10032			$this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
10033		}
10034		$this->state = 1;
10035		$this->_putheader();
10036		$this->_putpages();
10037		$this->_putresources();
10038		// empty signature fields
10039		if (!empty($this->empty_signature_appearance)) {
10040			foreach ($this->empty_signature_appearance as $key => $esa) {
10041				// widget annotation for empty signature
10042				$out = $this->_getobj($esa['objid'])."\n";
10043				$out .= '<< /Type /Annot';
10044				$out .= ' /Subtype /Widget';
10045				$out .= ' /Rect ['.$esa['rect'].']';
10046				$out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page
10047				$out .= ' /F 4';
10048				$out .= ' /FT /Sig';
10049				$signame = $esa['name'].sprintf(' [%03d]', ($key + 1));
10050				$out .= ' /T '.$this->_textstring($signame, $esa['objid']);
10051				$out .= ' /Ff 0';
10052				$out .= ' >>';
10053				$out .= "\n".'endobj';
10054				$this->_out($out);
10055			}
10056		}
10057		// Signature
10058		if ($this->sign AND isset($this->signature_data['cert_type'])) {
10059			// widget annotation for signature
10060			$out = $this->_getobj($this->sig_obj_id)."\n";
10061			$out .= '<< /Type /Annot';
10062			$out .= ' /Subtype /Widget';
10063			$out .= ' /Rect ['.$this->signature_appearance['rect'].']';
10064			$out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
10065			$out .= ' /F 4';
10066			$out .= ' /FT /Sig';
10067			$out .= ' /T '.$this->_textstring($this->signature_appearance['name'], $this->sig_obj_id);
10068			$out .= ' /Ff 0';
10069			$out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
10070			$out .= ' >>';
10071			$out .= "\n".'endobj';
10072			$this->_out($out);
10073			// signature
10074			$this->_putsignature();
10075		}
10076		// Info
10077		$objid_info = $this->_putinfo();
10078		// Catalog
10079		$objid_catalog = $this->_putcatalog();
10080		// Cross-ref
10081		$o = $this->bufferlen;
10082		// XREF section
10083		$this->_out('xref');
10084		$this->_out('0 '.($this->n + 1));
10085		$this->_out('0000000000 65535 f ');
10086		$freegen = ($this->n + 2);
10087		for ($i=1; $i <= $this->n; ++$i) {
10088			if (!isset($this->offsets[$i]) AND ($i > 1)) {
10089				$this->_out(sprintf('0000000000 %05d f ', $freegen));
10090				++$freegen;
10091			} else {
10092				$this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
10093			}
10094		}
10095		// TRAILER
10096		$out = 'trailer'."\n";
10097		$out .= '<<';
10098		$out .= ' /Size '.($this->n + 1);
10099		$out .= ' /Root '.$objid_catalog.' 0 R';
10100		$out .= ' /Info '.$objid_info.' 0 R';
10101		if ($this->encrypted) {
10102			$out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
10103		}
10104		$out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
10105		$out .= ' >>';
10106		$this->_out($out);
10107		$this->_out('startxref');
10108		$this->_out($o);
10109		$this->_out('%%EOF');
10110		$this->state = 3; // end-of-doc
10111	}
10112
10113	/**
10114	 * Initialize a new page.
10115	 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
10116	 * @param mixed $format The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
10117	 * @protected
10118	 * @see getPageSizeFromFormat(), setPageFormat()
10119	 */
10120	protected function _beginpage($orientation='', $format='') {
10121		++$this->page;
10122		$this->pageobjects[$this->page] = array();
10123		$this->setPageBuffer($this->page, '');
10124		// initialize array for graphics tranformation positions inside a page buffer
10125		$this->transfmrk[$this->page] = array();
10126		$this->state = 2;
10127		if (TCPDF_STATIC::empty_string($orientation)) {
10128			if (isset($this->CurOrientation)) {
10129				$orientation = $this->CurOrientation;
10130			} elseif ($this->fwPt > $this->fhPt) {
10131				// landscape
10132				$orientation = 'L';
10133			} else {
10134				// portrait
10135				$orientation = 'P';
10136			}
10137		}
10138		if (TCPDF_STATIC::empty_string($format)) {
10139			$this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
10140			$this->setPageOrientation($orientation);
10141		} else {
10142			$this->setPageFormat($format, $orientation);
10143		}
10144		if ($this->rtl) {
10145			$this->x = $this->w - $this->rMargin;
10146		} else {
10147			$this->x = $this->lMargin;
10148		}
10149		$this->y = $this->tMargin;
10150		if (isset($this->newpagegroup[$this->page])) {
10151			// start a new group
10152			$this->currpagegroup = $this->newpagegroup[$this->page];
10153			$this->pagegroups[$this->currpagegroup] = 1;
10154		} elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
10155			++$this->pagegroups[$this->currpagegroup];
10156		}
10157	}
10158
10159	/**
10160	 * Mark end of page.
10161	 * @protected
10162	 */
10163	protected function _endpage() {
10164		$this->setVisibility('all');
10165		$this->state = 1;
10166	}
10167
10168	/**
10169	 * Begin a new object and return the object number.
10170	 * @return int object number
10171	 * @protected
10172	 */
10173	protected function _newobj() {
10174		$this->_out($this->_getobj());
10175		return $this->n;
10176	}
10177
10178	/**
10179	 * Return the starting object string for the selected object ID.
10180	 * @param int|null $objid Object ID (leave empty to get a new ID).
10181	 * @return string the starting object string
10182	 * @protected
10183	 * @since 5.8.009 (2010-08-20)
10184	 */
10185	protected function _getobj($objid=null) {
10186		if (TCPDF_STATIC::empty_string($objid)) {
10187			++$this->n;
10188			$objid = $this->n;
10189		}
10190		$this->offsets[$objid] = $this->bufferlen;
10191		$this->pageobjects[$this->page][] = $objid;
10192		return $objid.' 0 obj';
10193	}
10194
10195	/**
10196	 * Underline text.
10197	 * @param int $x X coordinate
10198	 * @param int $y Y coordinate
10199	 * @param string $txt text to underline
10200	 * @protected
10201	 */
10202	protected function _dounderline($x, $y, $txt) {
10203		$w = $this->GetStringWidth($txt);
10204		return $this->_dounderlinew($x, $y, $w);
10205	}
10206
10207	/**
10208	 * Underline for rectangular text area.
10209	 * @param int $x X coordinate
10210	 * @param int $y Y coordinate
10211	 * @param int $w width to underline
10212	 * @protected
10213	 * @since 4.8.008 (2009-09-29)
10214	 */
10215	protected function _dounderlinew($x, $y, $w) {
10216		$linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10217		return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
10218	}
10219
10220	/**
10221	 * Line through text.
10222	 * @param int $x X coordinate
10223	 * @param int $y Y coordinate
10224	 * @param string $txt text to linethrough
10225	 * @protected
10226	 */
10227	protected function _dolinethrough($x, $y, $txt) {
10228		$w = $this->GetStringWidth($txt);
10229		return $this->_dolinethroughw($x, $y, $w);
10230	}
10231
10232	/**
10233	 * Line through for rectangular text area.
10234	 * @param int $x X coordinate
10235	 * @param int $y Y coordinate
10236	 * @param int $w line length (width)
10237	 * @protected
10238	 * @since 4.9.008 (2009-09-29)
10239	 */
10240	protected function _dolinethroughw($x, $y, $w) {
10241		$linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10242		return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew);
10243	}
10244
10245	/**
10246	 * Overline text.
10247	 * @param int $x X coordinate
10248	 * @param int $y Y coordinate
10249	 * @param string $txt text to overline
10250	 * @protected
10251	 * @since 4.9.015 (2010-04-19)
10252	 */
10253	protected function _dooverline($x, $y, $txt) {
10254		$w = $this->GetStringWidth($txt);
10255		return $this->_dooverlinew($x, $y, $w);
10256	}
10257
10258	/**
10259	 * Overline for rectangular text area.
10260	 * @param int $x X coordinate
10261	 * @param int $y Y coordinate
10262	 * @param int $w width to overline
10263	 * @protected
10264	 * @since 4.9.015 (2010-04-19)
10265	 */
10266	protected function _dooverlinew($x, $y, $w) {
10267		$linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10268		return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
10269
10270	}
10271
10272	/**
10273	 * Format a data string for meta information
10274	 * @param string $s data string to escape.
10275	 * @param int $n object ID
10276	 * @return string escaped string.
10277	 * @protected
10278	 */
10279	protected function _datastring($s, $n=0) {
10280		if ($n == 0) {
10281			$n = $this->n;
10282		}
10283		$s = $this->_encrypt_data($n, $s);
10284		return '('. TCPDF_STATIC::_escape($s).')';
10285	}
10286
10287	/**
10288	 * Set the document creation timestamp
10289	 * @param mixed $time Document creation timestamp in seconds or date-time string.
10290	 * @public
10291	 * @since 5.9.152 (2012-03-23)
10292	 */
10293	public function setDocCreationTimestamp($time) {
10294		if (is_string($time)) {
10295			$time = TCPDF_STATIC::getTimestamp($time);
10296		}
10297		$this->doc_creation_timestamp = intval($time);
10298	}
10299
10300	/**
10301	 * Set the document modification timestamp
10302	 * @param mixed $time Document modification timestamp in seconds or date-time string.
10303	 * @public
10304	 * @since 5.9.152 (2012-03-23)
10305	 */
10306	public function setDocModificationTimestamp($time) {
10307		if (is_string($time)) {
10308			$time = TCPDF_STATIC::getTimestamp($time);
10309		}
10310		$this->doc_modification_timestamp = intval($time);
10311	}
10312
10313	/**
10314	 * Returns document creation timestamp in seconds.
10315	 * @return int Creation timestamp in seconds.
10316	 * @public
10317	 * @since 5.9.152 (2012-03-23)
10318	 */
10319	public function getDocCreationTimestamp() {
10320		return $this->doc_creation_timestamp;
10321	}
10322
10323	/**
10324	 * Returns document modification timestamp in seconds.
10325	 * @return int Modfication timestamp in seconds.
10326	 * @public
10327	 * @since 5.9.152 (2012-03-23)
10328	 */
10329	public function getDocModificationTimestamp() {
10330		return $this->doc_modification_timestamp;
10331	}
10332
10333	/**
10334	 * Returns a formatted date for meta information
10335	 * @param int $n Object ID.
10336	 * @param int $timestamp Timestamp to convert.
10337	 * @return string escaped date string.
10338	 * @protected
10339	 * @since 4.6.028 (2009-08-25)
10340	 */
10341	protected function _datestring($n=0, $timestamp=0) {
10342		if ((empty($timestamp)) OR ($timestamp < 0)) {
10343			$timestamp = $this->doc_creation_timestamp;
10344		}
10345		return $this->_datastring('D:'.TCPDF_STATIC::getFormattedDate($timestamp), $n);
10346	}
10347
10348	/**
10349	 * Format a text string for meta information
10350	 * @param string $s string to escape.
10351	 * @param int $n object ID
10352	 * @return string escaped string.
10353	 * @protected
10354	 */
10355	protected function _textstring($s, $n=0) {
10356		if ($this->isunicode) {
10357			//Convert string to UTF-16BE
10358			$s = TCPDF_FONTS::UTF8ToUTF16BE($s, true, $this->isunicode, $this->CurrentFont);
10359		}
10360		return $this->_datastring($s, $n);
10361	}
10362
10363	/**
10364	 * get raw output stream.
10365	 * @param string $s string to output.
10366	 * @param int $n object reference for encryption mode
10367	 * @protected
10368	 * @author Nicola Asuni
10369	 * @since 5.5.000 (2010-06-22)
10370	 */
10371	protected function _getrawstream($s, $n=0) {
10372		if ($n <= 0) {
10373			// default to current object
10374			$n = $this->n;
10375		}
10376		return $this->_encrypt_data($n, $s);
10377	}
10378
10379	/**
10380	 * Output a string to the document.
10381	 * @param string $s string to output.
10382	 * @protected
10383	 */
10384	protected function _out($s) {
10385		if ($this->state == 2) {
10386			if ($this->inxobj) {
10387				// we are inside an XObject template
10388				$this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
10389			} elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
10390				// puts data before page footer
10391				$pagebuff = $this->getPageBuffer($this->page);
10392				$page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
10393				$footer = substr($pagebuff, -$this->footerlen[$this->page]);
10394				$this->setPageBuffer($this->page, $page.$s."\n".$footer);
10395				// update footer position
10396				$this->footerpos[$this->page] += strlen($s."\n");
10397			} else {
10398				// set page data
10399				$this->setPageBuffer($this->page, $s."\n", true);
10400			}
10401		} elseif ($this->state > 0) {
10402			// set general data
10403			$this->setBuffer($s."\n");
10404		}
10405	}
10406
10407	/**
10408	 * Set header font.
10409	 * @param array<int,string|float|null> $font Array describing the basic font parameters: (family, style, size).
10410	 * @phpstan-param array{0: string, 1: string, 2: float|null} $font
10411	 * @public
10412	 * @since 1.1
10413	 */
10414	public function setHeaderFont($font) {
10415		$this->header_font = $font;
10416	}
10417
10418	/**
10419	 * Get header font.
10420	 * @return array<int,string|float|null> Array describing the basic font parameters: (family, style, size).
10421	 * @phpstan-return array{0: string, 1: string, 2: float|null}
10422	 * @public
10423	 * @since 4.0.012 (2008-07-24)
10424	 */
10425	public function getHeaderFont() {
10426		return $this->header_font;
10427	}
10428
10429	/**
10430	 * Set footer font.
10431	 * @param array<int,string|float|null> $font Array describing the basic font parameters: (family, style, size).
10432	 * @phpstan-param array{0: string, 1: string, 2: float|null} $font
10433	 * @public
10434	 * @since 1.1
10435	 */
10436	public function setFooterFont($font) {
10437		$this->footer_font = $font;
10438	}
10439
10440	/**
10441	 * Get Footer font.
10442	 * @return array<int,string|float|null> Array describing the basic font parameters: (family, style, size).
10443	 * @phpstan-return array{0: string, 1: string, 2: float|null} $font
10444	 * @public
10445	 * @since 4.0.012 (2008-07-24)
10446	 */
10447	public function getFooterFont() {
10448		return $this->footer_font;
10449	}
10450
10451	/**
10452	 * Set language array.
10453	 * @param array $language
10454	 * @public
10455	 * @since 1.1
10456	 */
10457	public function setLanguageArray($language) {
10458		$this->l = $language;
10459		if (isset($this->l['a_meta_dir'])) {
10460			$this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
10461		} else {
10462			$this->rtl = false;
10463		}
10464	}
10465
10466	/**
10467	 * Returns the PDF data.
10468	 * @public
10469	 */
10470	public function getPDFData() {
10471		if ($this->state < 3) {
10472			$this->Close();
10473		}
10474		return $this->buffer;
10475	}
10476
10477	/**
10478	 * Output anchor link.
10479	 * @param string $url link URL or internal link (i.e.: &lt;a href="#23,4.5"&gt;link to page 23 at 4.5 Y position&lt;/a&gt;)
10480	 * @param string $name link name
10481	 * @param boolean $fill Indicates if the cell background must be painted (true) or transparent (false).
10482	 * @param boolean $firstline if true prints only the first line and return the remaining string.
10483	 * @param array|null $color array of RGB text color
10484	 * @param string $style font style (U, D, B, I)
10485	 * @param boolean $firstblock if true the string is the starting of a line.
10486	 * @return int the number of cells used or the remaining text if $firstline = true;
10487	 * @public
10488	 */
10489	public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color=null, $style=-1, $firstblock=false) {
10490		if (isset($url[1]) AND ($url[0] == '#') AND is_numeric($url[1])) {
10491			// convert url to internal link
10492			$lnkdata = explode(',', $url);
10493			if (isset($lnkdata[0]) ) {
10494				$page = substr($lnkdata[0], 1);
10495				if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
10496					$lnky = floatval($lnkdata[1]);
10497				} else {
10498					$lnky = 0;
10499				}
10500				$url = $this->AddLink();
10501				$this->setLink($url, $lnky, $page);
10502			}
10503		}
10504		// store current settings
10505		$prevcolor = $this->fgcolor;
10506		$prevstyle = $this->FontStyle;
10507		if (empty($color)) {
10508			$this->setTextColorArray($this->htmlLinkColorArray);
10509		} else {
10510			$this->setTextColorArray($color);
10511		}
10512		if ($style == -1) {
10513			$this->setFont('', $this->FontStyle.$this->htmlLinkFontStyle);
10514		} else {
10515			$this->setFont('', $this->FontStyle.$style);
10516		}
10517		$ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
10518		// restore settings
10519		$this->setFont('', $prevstyle);
10520		$this->setTextColorArray($prevcolor);
10521		return $ret;
10522	}
10523
10524	/**
10525	 * Converts pixels to User's Units.
10526	 * @param int $px pixels
10527	 * @return float value in user's unit
10528	 * @public
10529	 * @see setImageScale(), getImageScale()
10530	 */
10531	public function pixelsToUnits($px) {
10532		return ($px / ($this->imgscale * $this->k));
10533	}
10534
10535	/**
10536	 * Reverse function for htmlentities.
10537	 * Convert entities in UTF-8.
10538	 * @param string $text_to_convert Text to convert.
10539	 * @return string converted text string
10540	 * @public
10541	 */
10542	public function unhtmlentities($text_to_convert) {
10543		return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
10544	}
10545
10546	// ENCRYPTION METHODS ----------------------------------
10547
10548	/**
10549	 * Compute encryption key depending on object number where the encrypted data is stored.
10550	 * This is used for all strings and streams without crypt filter specifier.
10551	 * @param int $n object number
10552	 * @return int object key
10553	 * @protected
10554	 * @author Nicola Asuni
10555	 * @since 2.0.000 (2008-01-02)
10556	 */
10557	protected function _objectkey($n) {
10558		$objkey = $this->encryptdata['key'].pack('VXxx', $n);
10559		if ($this->encryptdata['mode'] == 2) { // AES-128
10560			// AES padding
10561			$objkey .= "\x73\x41\x6C\x54"; // sAlT
10562		}
10563		$objkey = substr(TCPDF_STATIC::_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
10564		$objkey = substr($objkey, 0, 16);
10565		return $objkey;
10566	}
10567
10568	/**
10569	 * Encrypt the input string.
10570	 * @param int $n object number
10571	 * @param string $s data string to encrypt
10572	 * @return string encrypted string
10573	 * @protected
10574	 * @author Nicola Asuni
10575	 * @since 5.0.005 (2010-05-11)
10576	 */
10577	protected function _encrypt_data($n, $s) {
10578		if (!$this->encrypted) {
10579			return $s;
10580		}
10581		switch ($this->encryptdata['mode']) {
10582			case 0:   // RC4-40
10583			case 1: { // RC4-128
10584				$s = TCPDF_STATIC::_RC4($this->_objectkey($n), $s, $this->last_enc_key, $this->last_enc_key_c);
10585				break;
10586			}
10587			case 2: { // AES-128
10588				$s = TCPDF_STATIC::_AES($this->_objectkey($n), $s);
10589				break;
10590			}
10591			case 3: { // AES-256
10592				$s = TCPDF_STATIC::_AES($this->encryptdata['key'], $s);
10593				break;
10594			}
10595		}
10596		return $s;
10597	}
10598
10599	/**
10600	 * Put encryption on PDF document.
10601	 * @protected
10602	 * @author Nicola Asuni
10603	 * @since 2.0.000 (2008-01-02)
10604	 */
10605	protected function _putencryption() {
10606		if (!$this->encrypted) {
10607			return;
10608		}
10609		$this->encryptdata['objid'] = $this->_newobj();
10610		$out = '<<';
10611		if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
10612			$this->encryptdata['Filter'] = 'Standard';
10613		}
10614		$out .= ' /Filter /'.$this->encryptdata['Filter'];
10615		if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
10616			$out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
10617		}
10618		if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
10619			$this->encryptdata['V'] = 1;
10620		}
10621		// V is a code specifying the algorithm to be used in encrypting and decrypting the document
10622		$out .= ' /V '.$this->encryptdata['V'];
10623		if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
10624			// The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
10625			$out .= ' /Length '.$this->encryptdata['Length'];
10626		} else {
10627			$out .= ' /Length 40';
10628		}
10629		if ($this->encryptdata['V'] >= 4) {
10630			if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
10631				$this->encryptdata['StmF'] = 'Identity';
10632			}
10633			if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
10634				// The name of the crypt filter that shall be used when decrypting all strings in the document.
10635				$this->encryptdata['StrF'] = 'Identity';
10636			}
10637			// A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
10638			if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
10639				$out .= ' /CF <<';
10640				$out .= ' /'.$this->encryptdata['StmF'].' <<';
10641				$out .= ' /Type /CryptFilter';
10642				if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
10643					// The method used
10644					$out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
10645					if ($this->encryptdata['pubkey']) {
10646						$out .= ' /Recipients [';
10647						foreach ($this->encryptdata['Recipients'] as $rec) {
10648							$out .= ' <'.$rec.'>';
10649						}
10650						$out .= ' ]';
10651						if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
10652							$out .= ' /EncryptMetadata false';
10653						} else {
10654							$out .= ' /EncryptMetadata true';
10655						}
10656					}
10657				} else {
10658					$out .= ' /CFM /None';
10659				}
10660				if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
10661					// The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
10662					$out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
10663				} else {
10664					$out .= ' /AuthEvent /DocOpen';
10665				}
10666				if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
10667					// The bit length of the encryption key.
10668					$out .= ' /Length '.$this->encryptdata['CF']['Length'];
10669				}
10670				$out .= ' >> >>';
10671			}
10672			// The name of the crypt filter that shall be used by default when decrypting streams.
10673			$out .= ' /StmF /'.$this->encryptdata['StmF'];
10674			// The name of the crypt filter that shall be used when decrypting all strings in the document.
10675			$out .= ' /StrF /'.$this->encryptdata['StrF'];
10676			if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
10677				// The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
10678				$out .= ' /EFF /'.$this->encryptdata[''];
10679			}
10680		}
10681		// Additional encryption dictionary entries for the standard security handler
10682		if ($this->encryptdata['pubkey']) {
10683			if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
10684				$out .= ' /Recipients [';
10685				foreach ($this->encryptdata['Recipients'] as $rec) {
10686					$out .= ' <'.$rec.'>';
10687				}
10688				$out .= ' ]';
10689			}
10690		} else {
10691			$out .= ' /R';
10692			if ($this->encryptdata['V'] == 5) { // AES-256
10693				$out .= ' 5';
10694				$out .= ' /OE ('.TCPDF_STATIC::_escape($this->encryptdata['OE']).')';
10695				$out .= ' /UE ('.TCPDF_STATIC::_escape($this->encryptdata['UE']).')';
10696				$out .= ' /Perms ('.TCPDF_STATIC::_escape($this->encryptdata['perms']).')';
10697			} elseif ($this->encryptdata['V'] == 4) { // AES-128
10698				$out .= ' 4';
10699			} elseif ($this->encryptdata['V'] < 2) { // RC-40
10700				$out .= ' 2';
10701			} else { // RC-128
10702				$out .= ' 3';
10703			}
10704			$out .= ' /O ('.TCPDF_STATIC::_escape($this->encryptdata['O']).')';
10705			$out .= ' /U ('.TCPDF_STATIC::_escape($this->encryptdata['U']).')';
10706			$out .= ' /P '.$this->encryptdata['P'];
10707			if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
10708				$out .= ' /EncryptMetadata false';
10709			} else {
10710				$out .= ' /EncryptMetadata true';
10711			}
10712		}
10713		$out .= ' >>';
10714		$out .= "\n".'endobj';
10715		$this->_out($out);
10716	}
10717
10718	/**
10719	 * Compute U value (used for encryption)
10720	 * @return string U value
10721	 * @protected
10722	 * @since 2.0.000 (2008-01-02)
10723	 * @author Nicola Asuni
10724	 */
10725	protected function _Uvalue() {
10726		if ($this->encryptdata['mode'] == 0) { // RC4-40
10727			return TCPDF_STATIC::_RC4($this->encryptdata['key'], TCPDF_STATIC::$enc_padding, $this->last_enc_key, $this->last_enc_key_c);
10728		} elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
10729			$tmp = TCPDF_STATIC::_md5_16(TCPDF_STATIC::$enc_padding.$this->encryptdata['fileid']);
10730			$enc = TCPDF_STATIC::_RC4($this->encryptdata['key'], $tmp, $this->last_enc_key, $this->last_enc_key_c);
10731			$len = strlen($tmp);
10732			for ($i = 1; $i <= 19; ++$i) {
10733				$ek = '';
10734				for ($j = 0; $j < $len; ++$j) {
10735					$ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i);
10736				}
10737				$enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10738			}
10739			$enc .= str_repeat("\x00", 16);
10740			return substr($enc, 0, 32);
10741		} elseif ($this->encryptdata['mode'] == 3) { // AES-256
10742			$seed = TCPDF_STATIC::_md5_16(TCPDF_STATIC::getRandomSeed());
10743			// User Validation Salt
10744			$this->encryptdata['UVS'] = substr($seed, 0, 8);
10745			// User Key Salt
10746			$this->encryptdata['UKS'] = substr($seed, 8, 16);
10747			return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
10748		}
10749	}
10750
10751	/**
10752	 * Compute UE value (used for encryption)
10753	 * @return string UE value
10754	 * @protected
10755	 * @since 5.9.006 (2010-10-19)
10756	 * @author Nicola Asuni
10757	 */
10758	protected function _UEvalue() {
10759		$hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
10760		return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']);
10761	}
10762
10763	/**
10764	 * Compute O value (used for encryption)
10765	 * @return string O value
10766	 * @protected
10767	 * @since 2.0.000 (2008-01-02)
10768	 * @author Nicola Asuni
10769	 */
10770	protected function _Ovalue() {
10771		if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
10772			$tmp = TCPDF_STATIC::_md5_16($this->encryptdata['owner_password']);
10773			if ($this->encryptdata['mode'] > 0) {
10774				for ($i = 0; $i < 50; ++$i) {
10775					$tmp = TCPDF_STATIC::_md5_16($tmp);
10776				}
10777			}
10778			$owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
10779			$enc = TCPDF_STATIC::_RC4($owner_key, $this->encryptdata['user_password'], $this->last_enc_key, $this->last_enc_key_c);
10780			if ($this->encryptdata['mode'] > 0) {
10781				$len = strlen($owner_key);
10782				for ($i = 1; $i <= 19; ++$i) {
10783					$ek = '';
10784					for ($j = 0; $j < $len; ++$j) {
10785						$ek .= chr(ord($owner_key[$j]) ^ $i);
10786					}
10787					$enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10788				}
10789			}
10790			return $enc;
10791		} elseif ($this->encryptdata['mode'] == 3) { // AES-256
10792			$seed = TCPDF_STATIC::_md5_16(TCPDF_STATIC::getRandomSeed());
10793			// Owner Validation Salt
10794			$this->encryptdata['OVS'] = substr($seed, 0, 8);
10795			// Owner Key Salt
10796			$this->encryptdata['OKS'] = substr($seed, 8, 16);
10797			return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
10798		}
10799	}
10800
10801	/**
10802	 * Compute OE value (used for encryption)
10803	 * @return string OE value
10804	 * @protected
10805	 * @since 5.9.006 (2010-10-19)
10806	 * @author Nicola Asuni
10807	 */
10808	protected function _OEvalue() {
10809		$hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
10810		return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']);
10811	}
10812
10813	/**
10814	 * Convert password for AES-256 encryption mode
10815	 * @param string $password password
10816	 * @return string password
10817	 * @protected
10818	 * @since 5.9.006 (2010-10-19)
10819	 * @author Nicola Asuni
10820	 */
10821	protected function _fixAES256Password($password) {
10822		$psw = ''; // password to be returned
10823		$psw_array = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($password, $this->isunicode, $this->CurrentFont), $password, $this->rtl, $this->isunicode, $this->CurrentFont);
10824		foreach ($psw_array as $c) {
10825			$psw .= TCPDF_FONTS::unichr($c, $this->isunicode);
10826		}
10827		return substr($psw, 0, 127);
10828	}
10829
10830	/**
10831	 * Compute encryption key
10832	 * @protected
10833	 * @since 2.0.000 (2008-01-02)
10834	 * @author Nicola Asuni
10835	 */
10836	protected function _generateencryptionkey() {
10837		$keybytelen = ($this->encryptdata['Length'] / 8);
10838		if (!$this->encryptdata['pubkey']) { // standard mode
10839			if ($this->encryptdata['mode'] == 3) { // AES-256
10840				// generate 256 bit random key
10841				$this->encryptdata['key'] = substr(hash('sha256', TCPDF_STATIC::getRandomSeed(), true), 0, $keybytelen);
10842				// truncate passwords
10843				$this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
10844				$this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
10845				// Compute U value
10846				$this->encryptdata['U'] = $this->_Uvalue();
10847				// Compute UE value
10848				$this->encryptdata['UE'] = $this->_UEvalue();
10849				// Compute O value
10850				$this->encryptdata['O'] = $this->_Ovalue();
10851				// Compute OE value
10852				$this->encryptdata['OE'] = $this->_OEvalue();
10853				// Compute P value
10854				$this->encryptdata['P'] = $this->encryptdata['protection'];
10855				// Computing the encryption dictionary's Perms (permissions) value
10856				$perms = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
10857				$perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
10858				if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
10859					$perms .= 'F';
10860				} else {
10861					$perms .= 'T';
10862				}
10863				$perms .= 'adb'; // bytes 9-11
10864				$perms .= 'nick'; // bytes 12-15
10865				$this->encryptdata['perms'] = TCPDF_STATIC::_AESnopad($this->encryptdata['key'], $perms);
10866			} else { // RC4-40, RC4-128, AES-128
10867				// Pad passwords
10868				$this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10869				$this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10870				// Compute O value
10871				$this->encryptdata['O'] = $this->_Ovalue();
10872				// get default permissions (reverse byte order)
10873				$permissions = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']);
10874				// Compute encryption key
10875				$tmp = TCPDF_STATIC::_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
10876				if ($this->encryptdata['mode'] > 0) {
10877					for ($i = 0; $i < 50; ++$i) {
10878						$tmp = TCPDF_STATIC::_md5_16(substr($tmp, 0, $keybytelen));
10879					}
10880				}
10881				$this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
10882				// Compute U value
10883				$this->encryptdata['U'] = $this->_Uvalue();
10884				// Compute P value
10885				$this->encryptdata['P'] = $this->encryptdata['protection'];
10886			}
10887		} else { // Public-Key mode
10888			// random 20-byte seed
10889			$seed = sha1(TCPDF_STATIC::getRandomSeed(), true);
10890			$recipient_bytes = '';
10891			foreach ($this->encryptdata['pubkeys'] as $pubkey) {
10892				// for each public certificate
10893				if (isset($pubkey['p'])) {
10894					$pkprotection = TCPDF_STATIC::getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
10895				} else {
10896					$pkprotection = $this->encryptdata['protection'];
10897				}
10898				// get default permissions (reverse byte order)
10899				$pkpermissions = TCPDF_STATIC::getEncPermissionsString($pkprotection);
10900				// envelope data
10901				$envelope = $seed.$pkpermissions;
10902				// write the envelope data to a temporary file
10903				$tempkeyfile = TCPDF_STATIC::getObjFilename('key', $this->file_id);
10904				$f = TCPDF_STATIC::fopenLocal($tempkeyfile, 'wb');
10905				if (!$f) {
10906					$this->Error('Unable to create temporary key file: '.$tempkeyfile);
10907				}
10908				$envelope_length = strlen($envelope);
10909				fwrite($f, $envelope, $envelope_length);
10910				fclose($f);
10911				$tempencfile = TCPDF_STATIC::getObjFilename('enc', $this->file_id);
10912				if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) {
10913					$this->Error('Unable to encrypt the file: '.$tempkeyfile);
10914				}
10915				// read encryption signature
10916				$signature = file_get_contents($tempencfile, false, null, $envelope_length);
10917				// extract signature
10918				$signature = substr($signature, strpos($signature, 'Content-Disposition'));
10919				$tmparr = explode("\n\n", $signature);
10920				$signature = trim($tmparr[1]);
10921				unset($tmparr);
10922				// decode signature
10923				$signature = base64_decode($signature);
10924				// convert signature to hex
10925				$hexsignature = current(unpack('H*', $signature));
10926				// store signature on recipients array
10927				$this->encryptdata['Recipients'][] = $hexsignature;
10928				// The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
10929				$recipient_bytes .= $signature;
10930			}
10931			// calculate encryption key
10932			if ($this->encryptdata['mode'] == 3) { // AES-256
10933				$this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
10934			} else { // RC4-40, RC4-128, AES-128
10935				$this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
10936			}
10937		}
10938	}
10939
10940	/**
10941	 * Set document protection
10942	 * Remark: the protection against modification is for people who have the full Acrobat product.
10943	 * If you don't set any password, the document will open as usual. If you set a user password, the PDF viewer will ask for it before displaying the document. The master password, if different from the user one, can be used to get full access.
10944	 * Note: protecting a document requires to encrypt it, which increases the processing time a lot. This can cause a PHP time-out in some cases, especially if the document contains images or fonts.
10945	 * @param array $permissions the set of permissions (specify the ones you want to block):<ul><li>print : Print the document;</li><li>modify : Modify the contents of the document by operations other than those controlled by 'fill-forms', 'extract' and 'assemble';</li><li>copy : Copy or otherwise extract text and graphics from the document;</li><li>annot-forms : Add or modify text annotations, fill in interactive form fields, and, if 'modify' is also set, create or modify interactive form fields (including signature fields);</li><li>fill-forms : Fill in existing interactive form fields (including signature fields), even if 'annot-forms' is not specified;</li><li>extract : Extract text and graphics (in support of accessibility to users with disabilities or for other purposes);</li><li>assemble : Assemble the document (insert, rotate, or delete pages and create bookmarks or thumbnail images), even if 'modify' is not set;</li><li>print-high : Print the document to a representation from which a faithful digital copy of the PDF content could be generated. When this is not set, printing is limited to a low-level representation of the appearance, possibly of degraded quality.</li><li>owner : (inverted logic - only for public-key) when set permits change of encryption and enables all other permissions.</li></ul>
10946	 * @param string $user_pass user password. Empty by default.
10947	 * @param string|null $owner_pass owner password. If not specified, a random value is used.
10948	 * @param int $mode encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
10949	 * @param array|null $pubkeys array of recipients containing public-key certificates ('c') and permissions ('p'). For example: array(array('c' => 'file://../examples/data/cert/tcpdf.crt', 'p' => array('print')))
10950	 * @public
10951	 * @since 2.0.000 (2008-01-02)
10952	 * @author Nicola Asuni
10953	 */
10954	public function setProtection($permissions=array('print', 'modify', 'copy', 'annot-forms', 'fill-forms', 'extract', 'assemble', 'print-high'), $user_pass='', $owner_pass=null, $mode=0, $pubkeys=null) {
10955		if ($this->pdfa_mode) {
10956			// encryption is not allowed in PDF/A mode
10957			return;
10958		}
10959		$this->encryptdata['protection'] = TCPDF_STATIC::getUserPermissionCode($permissions, $mode);
10960		if (($pubkeys !== null) AND (is_array($pubkeys))) {
10961			// public-key mode
10962			$this->encryptdata['pubkeys'] = $pubkeys;
10963			if ($mode == 0) {
10964				// public-Key Security requires at least 128 bit
10965				$mode = 1;
10966			}
10967			if (!function_exists('openssl_pkcs7_encrypt')) {
10968				$this->Error('Public-Key Security requires openssl library.');
10969			}
10970			// Set Public-Key filter (available are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
10971			$this->encryptdata['pubkey'] = true;
10972			$this->encryptdata['Filter'] = 'Adobe.PubSec';
10973			$this->encryptdata['StmF'] = 'DefaultCryptFilter';
10974			$this->encryptdata['StrF'] = 'DefaultCryptFilter';
10975		} else {
10976			// standard mode (password mode)
10977			$this->encryptdata['pubkey'] = false;
10978			$this->encryptdata['Filter'] = 'Standard';
10979			$this->encryptdata['StmF'] = 'StdCF';
10980			$this->encryptdata['StrF'] = 'StdCF';
10981		}
10982		if ($mode > 1) { // AES
10983			if (!extension_loaded('openssl') && !extension_loaded('mcrypt')) {
10984				$this->Error('AES encryption requires openssl or mcrypt extension (http://www.php.net/manual/en/mcrypt.requirements.php).');
10985			}
10986			if (extension_loaded('openssl') && !in_array('aes-256-cbc', openssl_get_cipher_methods())) {
10987				$this->Error('AES encryption requires openssl/aes-256-cbc cypher.');
10988			}
10989			if (extension_loaded('mcrypt') && mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
10990				$this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
10991			}
10992			if (($mode == 3) AND !function_exists('hash')) {
10993				// the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
10994				$this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
10995			}
10996		}
10997		if ($owner_pass === null) {
10998			$owner_pass = md5(TCPDF_STATIC::getRandomSeed());
10999		}
11000		$this->encryptdata['user_password'] = $user_pass;
11001		$this->encryptdata['owner_password'] = $owner_pass;
11002		$this->encryptdata['mode'] = $mode;
11003		switch ($mode) {
11004			case 0: { // RC4 40 bit
11005				$this->encryptdata['V'] = 1;
11006				$this->encryptdata['Length'] = 40;
11007				$this->encryptdata['CF']['CFM'] = 'V2';
11008				break;
11009			}
11010			case 1: { // RC4 128 bit
11011				$this->encryptdata['V'] = 2;
11012				$this->encryptdata['Length'] = 128;
11013				$this->encryptdata['CF']['CFM'] = 'V2';
11014				if ($this->encryptdata['pubkey']) {
11015					$this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
11016					$this->encryptdata['Recipients'] = array();
11017				}
11018				break;
11019			}
11020			case 2: { // AES 128 bit
11021				$this->encryptdata['V'] = 4;
11022				$this->encryptdata['Length'] = 128;
11023				$this->encryptdata['CF']['CFM'] = 'AESV2';
11024				$this->encryptdata['CF']['Length'] = 128;
11025				if ($this->encryptdata['pubkey']) {
11026					$this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
11027					$this->encryptdata['Recipients'] = array();
11028				}
11029				break;
11030			}
11031			case 3: { // AES 256 bit
11032				$this->encryptdata['V'] = 5;
11033				$this->encryptdata['Length'] = 256;
11034				$this->encryptdata['CF']['CFM'] = 'AESV3';
11035				$this->encryptdata['CF']['Length'] = 256;
11036				if ($this->encryptdata['pubkey']) {
11037					$this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
11038					$this->encryptdata['Recipients'] = array();
11039				}
11040				break;
11041			}
11042		}
11043		$this->encrypted = true;
11044		$this->encryptdata['fileid'] = TCPDF_STATIC::convertHexStringToString($this->file_id);
11045		$this->_generateencryptionkey();
11046	}
11047
11048	// END OF ENCRYPTION FUNCTIONS -------------------------
11049
11050	// START TRANSFORMATIONS SECTION -----------------------
11051
11052	/**
11053	 * Starts a 2D tranformation saving current graphic state.
11054	 * This function must be called before scaling, mirroring, translation, rotation and skewing.
11055	 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
11056	 * @public
11057	 * @since 2.1.000 (2008-01-07)
11058	 * @see StartTransform(), StopTransform()
11059	 */
11060	public function StartTransform() {
11061		if ($this->state != 2) {
11062			return;
11063		}
11064		$this->_outSaveGraphicsState();
11065		if ($this->inxobj) {
11066			// we are inside an XObject template
11067			$this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
11068		} else {
11069			$this->transfmrk[$this->page][] = $this->pagelen[$this->page];
11070		}
11071		++$this->transfmatrix_key;
11072		$this->transfmatrix[$this->transfmatrix_key] = array();
11073	}
11074
11075	/**
11076	 * Stops a 2D tranformation restoring previous graphic state.
11077	 * This function must be called after scaling, mirroring, translation, rotation and skewing.
11078	 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
11079	 * @public
11080	 * @since 2.1.000 (2008-01-07)
11081	 * @see StartTransform(), StopTransform()
11082	 */
11083	public function StopTransform() {
11084		if ($this->state != 2) {
11085			return;
11086		}
11087		$this->_outRestoreGraphicsState();
11088		if (isset($this->transfmatrix[$this->transfmatrix_key])) {
11089			array_pop($this->transfmatrix[$this->transfmatrix_key]);
11090			--$this->transfmatrix_key;
11091		}
11092		if ($this->inxobj) {
11093			// we are inside an XObject template
11094			array_pop($this->xobjects[$this->xobjid]['transfmrk']);
11095		} else {
11096			array_pop($this->transfmrk[$this->page]);
11097		}
11098	}
11099	/**
11100	 * Horizontal Scaling.
11101	 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
11102	 * @param int $x abscissa of the scaling center. Default is current x position
11103	 * @param int $y ordinate of the scaling center. Default is current y position
11104	 * @public
11105	 * @since 2.1.000 (2008-01-07)
11106	 * @see StartTransform(), StopTransform()
11107	 */
11108	public function ScaleX($s_x, $x='', $y='') {
11109		$this->Scale($s_x, 100, $x, $y);
11110	}
11111
11112	/**
11113	 * Vertical Scaling.
11114	 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
11115	 * @param int $x abscissa of the scaling center. Default is current x position
11116	 * @param int $y ordinate of the scaling center. Default is current y position
11117	 * @public
11118	 * @since 2.1.000 (2008-01-07)
11119	 * @see StartTransform(), StopTransform()
11120	 */
11121	public function ScaleY($s_y, $x='', $y='') {
11122		$this->Scale(100, $s_y, $x, $y);
11123	}
11124
11125	/**
11126	 * Vertical and horizontal proportional Scaling.
11127	 * @param float $s scaling factor for width and height as percent. 0 is not allowed.
11128	 * @param int $x abscissa of the scaling center. Default is current x position
11129	 * @param int $y ordinate of the scaling center. Default is current y position
11130	 * @public
11131	 * @since 2.1.000 (2008-01-07)
11132	 * @see StartTransform(), StopTransform()
11133	 */
11134	public function ScaleXY($s, $x='', $y='') {
11135		$this->Scale($s, $s, $x, $y);
11136	}
11137
11138	/**
11139	 * Vertical and horizontal non-proportional Scaling.
11140	 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
11141	 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
11142	 * @param float|null $x abscissa of the scaling center. Default is current x position
11143	 * @param float|null $y ordinate of the scaling center. Default is current y position
11144	 * @public
11145	 * @since 2.1.000 (2008-01-07)
11146	 * @see StartTransform(), StopTransform()
11147	 */
11148	public function Scale($s_x, $s_y, $x=null, $y=null) {
11149		if (TCPDF_STATIC::empty_string($x)) {
11150			$x = $this->x;
11151		}
11152		if (TCPDF_STATIC::empty_string($y)) {
11153			$y = $this->y;
11154		}
11155		if (($s_x == 0) OR ($s_y == 0)) {
11156			$this->Error('Please do not use values equal to zero for scaling');
11157		}
11158		$y = ($this->h - $y) * $this->k;
11159		$x *= $this->k;
11160		//calculate elements of transformation matrix
11161		$s_x /= 100;
11162		$s_y /= 100;
11163		$tm = array();
11164		$tm[0] = $s_x;
11165		$tm[1] = 0;
11166		$tm[2] = 0;
11167		$tm[3] = $s_y;
11168		$tm[4] = $x * (1 - $s_x);
11169		$tm[5] = $y * (1 - $s_y);
11170		//scale the coordinate system
11171		$this->Transform($tm);
11172	}
11173
11174	/**
11175	 * Horizontal Mirroring.
11176	 * @param float|null $x abscissa of the point. Default is current x position
11177	 * @public
11178	 * @since 2.1.000 (2008-01-07)
11179	 * @see StartTransform(), StopTransform()
11180	 */
11181	public function MirrorH($x=null) {
11182		$this->Scale(-100, 100, $x);
11183	}
11184
11185	/**
11186	 * Verical Mirroring.
11187	 * @param float|null $y ordinate of the point. Default is current y position
11188	 * @public
11189	 * @since 2.1.000 (2008-01-07)
11190	 * @see StartTransform(), StopTransform()
11191	 */
11192	public function MirrorV($y=null) {
11193		$this->Scale(100, -100, null, $y);
11194	}
11195
11196	/**
11197	 * Point reflection mirroring.
11198	 * @param float|null $x abscissa of the point. Default is current x position
11199	 * @param float|null $y ordinate of the point. Default is current y position
11200	 * @public
11201	 * @since 2.1.000 (2008-01-07)
11202	 * @see StartTransform(), StopTransform()
11203	 */
11204	public function MirrorP($x=null,$y=null) {
11205		$this->Scale(-100, -100, $x, $y);
11206	}
11207
11208	/**
11209	 * Reflection against a straight line through point (x, y) with the gradient angle (angle).
11210	 * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line).
11211	 * @param float|null $x abscissa of the point. Default is current x position
11212	 * @param float|null $y ordinate of the point. Default is current y position
11213	 * @public
11214	 * @since 2.1.000 (2008-01-07)
11215	 * @see StartTransform(), StopTransform()
11216	 */
11217	public function MirrorL($angle=0, $x=null,$y=null) {
11218		$this->Scale(-100, 100, $x, $y);
11219		$this->Rotate(-2*($angle-90), $x, $y);
11220	}
11221
11222	/**
11223	 * Translate graphic object horizontally.
11224	 * @param int $t_x movement to the right (or left for RTL)
11225	 * @public
11226	 * @since 2.1.000 (2008-01-07)
11227	 * @see StartTransform(), StopTransform()
11228	 */
11229	public function TranslateX($t_x) {
11230		$this->Translate($t_x, 0);
11231	}
11232
11233	/**
11234	 * Translate graphic object vertically.
11235	 * @param int $t_y movement to the bottom
11236	 * @public
11237	 * @since 2.1.000 (2008-01-07)
11238	 * @see StartTransform(), StopTransform()
11239	 */
11240	public function TranslateY($t_y) {
11241		$this->Translate(0, $t_y);
11242	}
11243
11244	/**
11245	 * Translate graphic object horizontally and vertically.
11246	 * @param int $t_x movement to the right
11247	 * @param int $t_y movement to the bottom
11248	 * @public
11249	 * @since 2.1.000 (2008-01-07)
11250	 * @see StartTransform(), StopTransform()
11251	 */
11252	public function Translate($t_x, $t_y) {
11253		//calculate elements of transformation matrix
11254		$tm = array();
11255		$tm[0] = 1;
11256		$tm[1] = 0;
11257		$tm[2] = 0;
11258		$tm[3] = 1;
11259		$tm[4] = $t_x * $this->k;
11260		$tm[5] = -$t_y * $this->k;
11261		//translate the coordinate system
11262		$this->Transform($tm);
11263	}
11264
11265	/**
11266	 * Rotate object.
11267	 * @param float $angle angle in degrees for counter-clockwise rotation
11268	 * @param float|null $x abscissa of the rotation center. Default is current x position
11269	 * @param float|null $y ordinate of the rotation center. Default is current y position
11270	 * @public
11271	 * @since 2.1.000 (2008-01-07)
11272	 * @see StartTransform(), StopTransform()
11273	 */
11274	public function Rotate($angle, $x=null, $y=null) {
11275		if (TCPDF_STATIC::empty_string($x)) {
11276			$x = $this->x;
11277		}
11278		if (TCPDF_STATIC::empty_string($y)) {
11279			$y = $this->y;
11280		}
11281		$y = ($this->h - $y) * $this->k;
11282		$x *= $this->k;
11283		//calculate elements of transformation matrix
11284		$tm = array();
11285		$tm[0] = cos(deg2rad($angle));
11286		$tm[1] = sin(deg2rad($angle));
11287		$tm[2] = -$tm[1];
11288		$tm[3] = $tm[0];
11289		$tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
11290		$tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
11291		//rotate the coordinate system around ($x,$y)
11292		$this->Transform($tm);
11293	}
11294
11295	/**
11296	 * Skew horizontally.
11297	 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
11298	 * @param float|null $x abscissa of the skewing center. default is current x position
11299	 * @param float|null $y ordinate of the skewing center. default is current y position
11300	 * @public
11301	 * @since 2.1.000 (2008-01-07)
11302	 * @see StartTransform(), StopTransform()
11303	 */
11304	public function SkewX($angle_x, $x=null, $y=null) {
11305		$this->Skew($angle_x, 0, $x, $y);
11306	}
11307
11308	/**
11309	 * Skew vertically.
11310	 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
11311	 * @param float|null $x abscissa of the skewing center. default is current x position
11312	 * @param float|null $y ordinate of the skewing center. default is current y position
11313	 * @public
11314	 * @since 2.1.000 (2008-01-07)
11315	 * @see StartTransform(), StopTransform()
11316	 */
11317	public function SkewY($angle_y, $x=null, $y=null) {
11318		$this->Skew(0, $angle_y, $x, $y);
11319	}
11320
11321	/**
11322	 * Skew.
11323	 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
11324	 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
11325	 * @param float|null $x abscissa of the skewing center. default is current x position
11326	 * @param float|null $y ordinate of the skewing center. default is current y position
11327	 * @public
11328	 * @since 2.1.000 (2008-01-07)
11329	 * @see StartTransform(), StopTransform()
11330	 */
11331	public function Skew($angle_x, $angle_y, $x=null, $y=null) {
11332		if (TCPDF_STATIC::empty_string($x)) {
11333			$x = $this->x;
11334		}
11335		if (TCPDF_STATIC::empty_string($y)) {
11336			$y = $this->y;
11337		}
11338		if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
11339			$this->Error('Please use values between -90 and +90 degrees for Skewing.');
11340		}
11341		$x *= $this->k;
11342		$y = ($this->h - $y) * $this->k;
11343		//calculate elements of transformation matrix
11344		$tm = array();
11345		$tm[0] = 1;
11346		$tm[1] = tan(deg2rad($angle_y));
11347		$tm[2] = tan(deg2rad($angle_x));
11348		$tm[3] = 1;
11349		$tm[4] = -$tm[2] * $y;
11350		$tm[5] = -$tm[1] * $x;
11351		//skew the coordinate system
11352		$this->Transform($tm);
11353	}
11354
11355	/**
11356	 * Apply graphic transformations.
11357	 * @param array $tm transformation matrix
11358	 * @protected
11359	 * @since 2.1.000 (2008-01-07)
11360	 * @see StartTransform(), StopTransform()
11361	 */
11362	protected function Transform($tm) {
11363		if ($this->state != 2) {
11364			return;
11365		}
11366		$this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
11367		// add tranformation matrix
11368		$this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
11369		// update transformation mark
11370		if ($this->inxobj) {
11371			// we are inside an XObject template
11372			if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
11373				$key = key($this->xobjects[$this->xobjid]['transfmrk']);
11374				$this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
11375			}
11376		} elseif (end($this->transfmrk[$this->page]) !== false) {
11377			$key = key($this->transfmrk[$this->page]);
11378			$this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
11379		}
11380	}
11381
11382	// END TRANSFORMATIONS SECTION -------------------------
11383
11384	// START GRAPHIC FUNCTIONS SECTION ---------------------
11385	// The following section is based on the code provided by David Hernandez Sanz
11386
11387	/**
11388	 * Defines the line width. By default, the value equals 0.2 mm. The method can be called before the first page is created and the value is retained from page to page.
11389	 * @param float $width The width.
11390	 * @public
11391	 * @since 1.0
11392	 * @see Line(), Rect(), Cell(), MultiCell()
11393	 */
11394	public function setLineWidth($width) {
11395		//Set line width
11396		$this->LineWidth = $width;
11397		$this->linestyleWidth = sprintf('%F w', ($width * $this->k));
11398		if ($this->state == 2) {
11399			$this->_out($this->linestyleWidth);
11400		}
11401	}
11402
11403	/**
11404	 * Returns the current the line width.
11405	 * @return int Line width
11406	 * @public
11407	 * @since 2.1.000 (2008-01-07)
11408	 * @see Line(), SetLineWidth()
11409	 */
11410	public function GetLineWidth() {
11411		return $this->LineWidth;
11412	}
11413
11414	/**
11415	 * Set line style.
11416	 * @param array $style Line style. Array with keys among the following:
11417	 * <ul>
11418	 *	 <li>width (float): Width of the line in user units.</li>
11419	 *	 <li>cap (string): Type of cap to put on the line. Possible values are:
11420	 * butt, round, square. The difference between "square" and "butt" is that
11421	 * "square" projects a flat end past the end of the line.</li>
11422	 *	 <li>join (string): Type of join. Possible values are: miter, round,
11423	 * bevel.</li>
11424	 *	 <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
11425	 * series of length values, which are the lengths of the on and off dashes.
11426	 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
11427	 * 1 off, 2 on, 1 off, ...</li>
11428	 *	 <li>phase (integer): Modifier on the dash pattern which is used to shift
11429	 * the point at which the pattern starts.</li>
11430	 *	 <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName).</li>
11431	 * </ul>
11432	 * @param boolean $ret if true do not send the command.
11433	 * @return string the PDF command
11434	 * @public
11435	 * @since 2.1.000 (2008-01-08)
11436	 */
11437	public function setLineStyle($style, $ret=false) {
11438		$s = ''; // string to be returned
11439		if (!is_array($style)) {
11440			return $s;
11441		}
11442		if (isset($style['width'])) {
11443			$this->LineWidth = $style['width'];
11444			$this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k));
11445			$s .= $this->linestyleWidth.' ';
11446		}
11447		if (isset($style['cap'])) {
11448			$ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
11449			if (isset($ca[$style['cap']])) {
11450				$this->linestyleCap = $ca[$style['cap']].' J';
11451				$s .= $this->linestyleCap.' ';
11452			}
11453		}
11454		if (isset($style['join'])) {
11455			$ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
11456			if (isset($ja[$style['join']])) {
11457				$this->linestyleJoin = $ja[$style['join']].' j';
11458				$s .= $this->linestyleJoin.' ';
11459			}
11460		}
11461		if (isset($style['dash'])) {
11462			$dash_string = '';
11463			if ($style['dash']) {
11464				if (preg_match('/^.+,/', $style['dash']) > 0) {
11465					$tab = explode(',', $style['dash']);
11466				} else {
11467					$tab = array($style['dash']);
11468				}
11469				$dash_string = '';
11470				foreach ($tab as $i => $v) {
11471					if ($i) {
11472						$dash_string .= ' ';
11473					}
11474					$dash_string .= sprintf('%F', $v);
11475				}
11476			}
11477			if (!isset($style['phase']) OR !$style['dash']) {
11478				$style['phase'] = 0;
11479			}
11480			$this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']);
11481			$s .= $this->linestyleDash.' ';
11482		}
11483		if (isset($style['color'])) {
11484			$s .= $this->setDrawColorArray($style['color'], true).' ';
11485		}
11486		if (!$ret AND ($this->state == 2)) {
11487			$this->_out($s);
11488		}
11489		return $s;
11490	}
11491
11492	/**
11493	 * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment.
11494	 * @param float $x Abscissa of point.
11495	 * @param float $y Ordinate of point.
11496	 * @protected
11497	 * @since 2.1.000 (2008-01-08)
11498	 */
11499	protected function _outPoint($x, $y) {
11500		if ($this->state == 2) {
11501			$this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k)));
11502		}
11503	}
11504
11505	/**
11506	 * Append a straight line segment from the current point to the point (x, y).
11507	 * The new current point shall be (x, y).
11508	 * @param float $x Abscissa of end point.
11509	 * @param float $y Ordinate of end point.
11510	 * @protected
11511	 * @since 2.1.000 (2008-01-08)
11512	 */
11513	protected function _outLine($x, $y) {
11514		if ($this->state == 2) {
11515			$this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k)));
11516		}
11517	}
11518
11519	/**
11520	 * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space.
11521	 * @param float $x Abscissa of upper-left corner.
11522	 * @param float $y Ordinate of upper-left corner.
11523	 * @param float $w Width.
11524	 * @param float $h Height.
11525	 * @param string $op options
11526	 * @protected
11527	 * @since 2.1.000 (2008-01-08)
11528	 */
11529	protected function _outRect($x, $y, $w, $h, $op) {
11530		if ($this->state == 2) {
11531			$this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k), (($this->h - $y) * $this->k), ($w * $this->k), (-$h * $this->k), $op));
11532		}
11533	}
11534
11535	/**
11536	 * Append a cubic Bezier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x2, y2) as the Bezier control points.
11537	 * The new current point shall be (x3, y3).
11538	 * @param float $x1 Abscissa of control point 1.
11539	 * @param float $y1 Ordinate of control point 1.
11540	 * @param float $x2 Abscissa of control point 2.
11541	 * @param float $y2 Ordinate of control point 2.
11542	 * @param float $x3 Abscissa of end point.
11543	 * @param float $y3 Ordinate of end point.
11544	 * @protected
11545	 * @since 2.1.000 (2008-01-08)
11546	 */
11547	protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
11548		if ($this->state == 2) {
11549			$this->_out(sprintf('%F %F %F %F %F %F c', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11550		}
11551	}
11552
11553	/**
11554	 * Append a cubic Bezier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using the current point and (x2, y2) as the Bezier control points.
11555	 * The new current point shall be (x3, y3).
11556	 * @param float $x2 Abscissa of control point 2.
11557	 * @param float $y2 Ordinate of control point 2.
11558	 * @param float $x3 Abscissa of end point.
11559	 * @param float $y3 Ordinate of end point.
11560	 * @protected
11561	 * @since 4.9.019 (2010-04-26)
11562	 */
11563	protected function _outCurveV($x2, $y2, $x3, $y3) {
11564		if ($this->state == 2) {
11565			$this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11566		}
11567	}
11568
11569	/**
11570	 * Append a cubic Bezier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x3, y3) as the Bezier control points.
11571	 * The new current point shall be (x3, y3).
11572	 * @param float $x1 Abscissa of control point 1.
11573	 * @param float $y1 Ordinate of control point 1.
11574	 * @param float $x3 Abscissa of end point.
11575	 * @param float $y3 Ordinate of end point.
11576	 * @protected
11577	 * @since 2.1.000 (2008-01-08)
11578	 */
11579	protected function _outCurveY($x1, $y1, $x3, $y3) {
11580		if ($this->state == 2) {
11581			$this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11582		}
11583	}
11584
11585	/**
11586	 * Draws a line between two points.
11587	 * @param float $x1 Abscissa of first point.
11588	 * @param float $y1 Ordinate of first point.
11589	 * @param float $x2 Abscissa of second point.
11590	 * @param float $y2 Ordinate of second point.
11591	 * @param array $style Line style. Array like for SetLineStyle(). Default value: default line style (empty array).
11592	 * @public
11593	 * @since 1.0
11594	 * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
11595	 */
11596	public function Line($x1, $y1, $x2, $y2, $style=array()) {
11597		if ($this->state != 2) {
11598			return;
11599		}
11600		if (is_array($style)) {
11601			$this->setLineStyle($style);
11602		}
11603		$this->_outPoint($x1, $y1);
11604		$this->_outLine($x2, $y2);
11605		$this->_out('S');
11606	}
11607
11608	/**
11609	 * Draws a rectangle.
11610	 * @param float $x Abscissa of upper-left corner.
11611	 * @param float $y Ordinate of upper-left corner.
11612	 * @param float $w Width.
11613	 * @param float $h Height.
11614	 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
11615	 * @param array $border_style Border style of rectangle. Array with keys among the following:
11616	 * <ul>
11617	 *	 <li>all: Line style of all borders. Array like for SetLineStyle().</li>
11618	 *	 <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for SetLineStyle().</li>
11619	 * </ul>
11620	 * If a key is not present or is null, the correspondent border is not drawn. Default value: default line style (empty array).
11621	 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11622	 * @public
11623	 * @since 1.0
11624	 * @see SetLineStyle()
11625	 */
11626	public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
11627		if ($this->state != 2) {
11628			return;
11629		}
11630		if (empty($style)) {
11631			$style = 'S';
11632		}
11633		if (!(strpos($style, 'F') === false) AND !empty($fill_color)) {
11634			// set background color
11635			$this->setFillColorArray($fill_color);
11636		}
11637		if (!empty($border_style)) {
11638			if (isset($border_style['all']) AND !empty($border_style['all'])) {
11639				//set global style for border
11640				$this->setLineStyle($border_style['all']);
11641				$border_style = array();
11642			} else {
11643				// remove stroke operator from style
11644				$opnostroke = array('S' => '', 'D' => '', 's' => '', 'd' => '', 'B' => 'F', 'FD' => 'F', 'DF' => 'F', 'B*' => 'F*', 'F*D' => 'F*', 'DF*' => 'F*', 'b' => 'f', 'fd' => 'f', 'df' => 'f', 'b*' => 'f*', 'f*d' => 'f*', 'df*' => 'f*' );
11645				if (isset($opnostroke[$style])) {
11646					$style = $opnostroke[$style];
11647				}
11648			}
11649		}
11650		if (!empty($style)) {
11651			$op = TCPDF_STATIC::getPathPaintOperator($style);
11652			$this->_outRect($x, $y, $w, $h, $op);
11653		}
11654		if (!empty($border_style)) {
11655			$border_style2 = array();
11656			foreach ($border_style as $line => $value) {
11657				$length = strlen($line);
11658				for ($i = 0; $i < $length; ++$i) {
11659					$border_style2[$line[$i]] = $value;
11660				}
11661			}
11662			$border_style = $border_style2;
11663			if (isset($border_style['L']) AND $border_style['L']) {
11664				$this->Line($x, $y, $x, $y + $h, $border_style['L']);
11665			}
11666			if (isset($border_style['T']) AND $border_style['T']) {
11667				$this->Line($x, $y, $x + $w, $y, $border_style['T']);
11668			}
11669			if (isset($border_style['R']) AND $border_style['R']) {
11670				$this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
11671			}
11672			if (isset($border_style['B']) AND $border_style['B']) {
11673				$this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
11674			}
11675		}
11676	}
11677
11678	/**
11679	 * Draws a Bezier curve.
11680	 * The Bezier curve is a tangent to the line between the control points at
11681	 * either end of the curve.
11682	 * @param float $x0 Abscissa of start point.
11683	 * @param float $y0 Ordinate of start point.
11684	 * @param float $x1 Abscissa of control point 1.
11685	 * @param float $y1 Ordinate of control point 1.
11686	 * @param float $x2 Abscissa of control point 2.
11687	 * @param float $y2 Ordinate of control point 2.
11688	 * @param float $x3 Abscissa of end point.
11689	 * @param float $y3 Ordinate of end point.
11690	 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
11691	 * @param array $line_style Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
11692	 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11693	 * @public
11694	 * @see SetLineStyle()
11695	 * @since 2.1.000 (2008-01-08)
11696	 */
11697	public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
11698		if ($this->state != 2) {
11699			return;
11700		}
11701		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11702			$this->setFillColorArray($fill_color);
11703		}
11704		$op = TCPDF_STATIC::getPathPaintOperator($style);
11705		if ($line_style) {
11706			$this->setLineStyle($line_style);
11707		}
11708		$this->_outPoint($x0, $y0);
11709		$this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11710		$this->_out($op);
11711	}
11712
11713	/**
11714	 * Draws a poly-Bezier curve.
11715	 * Each Bezier curve segment is a tangent to the line between the control points at
11716	 * either end of the curve.
11717	 * @param float $x0 Abscissa of start point.
11718	 * @param float $y0 Ordinate of start point.
11719	 * @param float[] $segments An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
11720	 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
11721	 * @param array $line_style Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
11722	 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11723	 * @public
11724	 * @see SetLineStyle()
11725	 * @since 3.0008 (2008-05-12)
11726	 */
11727	public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
11728		if ($this->state != 2) {
11729			return;
11730		}
11731		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11732			$this->setFillColorArray($fill_color);
11733		}
11734		$op = TCPDF_STATIC::getPathPaintOperator($style);
11735		if ($op == 'f') {
11736			$line_style = array();
11737		}
11738		if ($line_style) {
11739			$this->setLineStyle($line_style);
11740		}
11741		$this->_outPoint($x0, $y0);
11742		foreach ($segments as $segment) {
11743			list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
11744			$this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11745		}
11746		$this->_out($op);
11747	}
11748
11749	/**
11750	 * Draws an ellipse.
11751	 * An ellipse is formed from n Bezier curves.
11752	 * @param float $x0 Abscissa of center point.
11753	 * @param float $y0 Ordinate of center point.
11754	 * @param float $rx Horizontal radius.
11755	 * @param float $ry Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
11756	 * @param float $angle Angle oriented (anti-clockwise). Default value: 0.
11757	 * @param float $astart Angle start of draw line. Default value: 0.
11758	 * @param float $afinish Angle finish of draw line. Default value: 360.
11759	 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
11760	 * @param array $line_style Line style of ellipse. Array like for SetLineStyle(). Default value: default line style (empty array).
11761	 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11762	 * @param integer $nc Number of curves used to draw a 90 degrees portion of ellipse.
11763	 * @author Nicola Asuni
11764	 * @public
11765	 * @since 2.1.000 (2008-01-08)
11766	 */
11767	public function Ellipse($x0, $y0, $rx, $ry=0, $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11768		if ($this->state != 2) {
11769			return;
11770		}
11771		if (TCPDF_STATIC::empty_string($ry) OR ($ry == 0)) {
11772			$ry = $rx;
11773		}
11774		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11775			$this->setFillColorArray($fill_color);
11776		}
11777		$op = TCPDF_STATIC::getPathPaintOperator($style);
11778		if ($op == 'f') {
11779			$line_style = array();
11780		}
11781		if ($line_style) {
11782			$this->setLineStyle($line_style);
11783		}
11784		$this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
11785		$this->_out($op);
11786	}
11787
11788	/**
11789	 * Append an elliptical arc to the current path.
11790	 * An ellipse is formed from n Bezier curves.
11791	 * @param float $xc Abscissa of center point.
11792	 * @param float $yc Ordinate of center point.
11793	 * @param float $rx Horizontal radius.
11794	 * @param float $ry Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
11795	 * @param float $xang Angle between the X-axis and the major axis of the ellipse. Default value: 0.
11796	 * @param float $angs Angle start of draw line. Default value: 0.
11797	 * @param float $angf Angle finish of draw line. Default value: 360.
11798	 * @param boolean $pie if true do not mark the border point (used to draw pie sectors).
11799	 * @param integer $nc Number of curves used to draw a 90 degrees portion of ellipse.
11800	 * @param boolean $startpoint if true output a starting point.
11801	 * @param boolean $ccw if true draws in counter-clockwise.
11802	 * @param boolean $svg if true the angles are in svg mode (already calculated).
11803	 * @return array bounding box coordinates (x min, y min, x max, y max)
11804	 * @author Nicola Asuni
11805	 * @protected
11806	 * @since 4.9.019 (2010-04-26)
11807	 */
11808	protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
11809		if (($rx <= 0) OR ($ry < 0)) {
11810			return;
11811		}
11812		$k = $this->k;
11813		if ($nc < 2) {
11814			$nc = 2;
11815		}
11816		$xmin = 2147483647;
11817		$ymin = 2147483647;
11818		$xmax = 0;
11819		$ymax = 0;
11820		if ($pie) {
11821			// center of the arc
11822			$this->_outPoint($xc, $yc);
11823		}
11824		$xang = deg2rad((float) $xang);
11825		$angs = deg2rad((float) $angs);
11826		$angf = deg2rad((float) $angf);
11827		if ($svg) {
11828			$as = $angs;
11829			$af = $angf;
11830		} else {
11831			$as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
11832			$af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
11833		}
11834		if ($as < 0) {
11835			$as += (2 * M_PI);
11836		}
11837		if ($af < 0) {
11838			$af += (2 * M_PI);
11839		}
11840		if ($ccw AND ($as > $af)) {
11841			// reverse rotation
11842			$as -= (2 * M_PI);
11843		} elseif (!$ccw AND ($as < $af)) {
11844			// reverse rotation
11845			$af -= (2 * M_PI);
11846		}
11847		$total_angle = ($af - $as);
11848		if ($nc < 2) {
11849			$nc = 2;
11850		}
11851		// total arcs to draw
11852		$nc *= (2 * abs($total_angle) / M_PI);
11853		$nc = round($nc) + 1;
11854		// angle of each arc
11855		$arcang = ($total_angle / $nc);
11856		// center point in PDF coordinates
11857		$x0 = $xc;
11858		$y0 = ($this->h - $yc);
11859		// starting angle
11860		$ang = $as;
11861		$alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
11862		$cos_xang = cos($xang);
11863		$sin_xang = sin($xang);
11864		$cos_ang = cos($ang);
11865		$sin_ang = sin($ang);
11866		// first arc point
11867		$px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11868		$py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11869		// first Bezier control point
11870		$qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11871		$qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11872		if ($pie) {
11873			// line from center to arc starting point
11874			$this->_outLine($px1, $this->h - $py1);
11875		} elseif ($startpoint) {
11876			// arc starting point
11877			$this->_outPoint($px1, $this->h - $py1);
11878		}
11879		// draw arcs
11880		for ($i = 1; $i <= $nc; ++$i) {
11881			// starting angle
11882			$ang = $as + ($i * $arcang);
11883			if ($i == $nc) {
11884				$ang = $af;
11885			}
11886			$cos_ang = cos($ang);
11887			$sin_ang = sin($ang);
11888			// second arc point
11889			$px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11890			$py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11891			// second Bezier control point
11892			$qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11893			$qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11894			// draw arc
11895			$cx1 = ($px1 + $qx1);
11896			$cy1 = ($this->h - ($py1 + $qy1));
11897			$cx2 = ($px2 - $qx2);
11898			$cy2 = ($this->h - ($py2 - $qy2));
11899			$cx3 = $px2;
11900			$cy3 = ($this->h - $py2);
11901			$this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
11902			// get bounding box coordinates
11903			$xmin = min($xmin, $cx1, $cx2, $cx3);
11904			$ymin = min($ymin, $cy1, $cy2, $cy3);
11905			$xmax = max($xmax, $cx1, $cx2, $cx3);
11906			$ymax = max($ymax, $cy1, $cy2, $cy3);
11907			// move to next point
11908			$px1 = $px2;
11909			$py1 = $py2;
11910			$qx1 = $qx2;
11911			$qy1 = $qy2;
11912		}
11913		if ($pie) {
11914			$this->_outLine($xc, $yc);
11915			// get bounding box coordinates
11916			$xmin = min($xmin, $xc);
11917			$ymin = min($ymin, $yc);
11918			$xmax = max($xmax, $xc);
11919			$ymax = max($ymax, $yc);
11920		}
11921		return array($xmin, $ymin, $xmax, $ymax);
11922	}
11923
11924	/**
11925	 * Draws a circle.
11926	 * A circle is formed from n Bezier curves.
11927	 * @param float $x0 Abscissa of center point.
11928	 * @param float $y0 Ordinate of center point.
11929	 * @param float $r Radius.
11930	 * @param float $angstr Angle start of draw line. Default value: 0.
11931	 * @param float $angend Angle finish of draw line. Default value: 360.
11932	 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
11933	 * @param array $line_style Line style of circle. Array like for SetLineStyle(). Default value: default line style (empty array).
11934	 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
11935	 * @param integer $nc Number of curves used to draw a 90 degrees portion of circle.
11936	 * @public
11937	 * @since 2.1.000 (2008-01-08)
11938	 */
11939	public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11940		$this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
11941	}
11942
11943	/**
11944	 * Draws a polygonal line
11945	 * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
11946	 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
11947	 * @param array $line_style Line style of polygon. Array with keys among the following:
11948	 * <ul>
11949	 *	 <li>all: Line style of all lines. Array like for SetLineStyle().</li>
11950	 *	 <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
11951	 * </ul>
11952	 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
11953	 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11954	 * @since 4.8.003 (2009-09-15)
11955	 * @public
11956	 */
11957	public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
11958		$this->Polygon($p, $style, $line_style, $fill_color, false);
11959	}
11960
11961	/**
11962	 * Draws a polygon.
11963	 * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
11964	 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
11965	 * @param array $line_style Line style of polygon. Array with keys among the following:
11966	 * <ul>
11967	 *	 <li>all: Line style of all lines. Array like for SetLineStyle().</li>
11968	 *	 <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
11969	 * </ul>
11970	 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
11971	 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11972	 * @param boolean $closed if true the polygon is closes, otherwise will remain open
11973	 * @public
11974	 * @since 2.1.000 (2008-01-08)
11975	 */
11976	public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
11977		if ($this->state != 2) {
11978			return;
11979		}
11980		$nc = count($p); // number of coordinates
11981		$np = $nc / 2; // number of points
11982		if ($closed) {
11983			// close polygon by adding the first 2 points at the end (one line)
11984			for ($i = 0; $i < 4; ++$i) {
11985				$p[$nc + $i] = $p[$i];
11986			}
11987			// copy style for the last added line
11988			if (isset($line_style[0])) {
11989				$line_style[$np] = $line_style[0];
11990			}
11991			$nc += 4;
11992		}
11993		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11994			$this->setFillColorArray($fill_color);
11995		}
11996		$op = TCPDF_STATIC::getPathPaintOperator($style);
11997		if ($op == 'f') {
11998			$line_style = array();
11999		}
12000		$draw = true;
12001		if ($line_style) {
12002			if (isset($line_style['all'])) {
12003				$this->setLineStyle($line_style['all']);
12004			} else {
12005				$draw = false;
12006				if ($op == 'B') {
12007					// draw fill
12008					$op = 'f';
12009					$this->_outPoint($p[0], $p[1]);
12010					for ($i = 2; $i < $nc; $i = $i + 2) {
12011						$this->_outLine($p[$i], $p[$i + 1]);
12012					}
12013					$this->_out($op);
12014				}
12015				// draw outline
12016				$this->_outPoint($p[0], $p[1]);
12017				for ($i = 2; $i < $nc; $i = $i + 2) {
12018					$line_num = ($i / 2) - 1;
12019					if (isset($line_style[$line_num])) {
12020						if ($line_style[$line_num] != 0) {
12021							if (is_array($line_style[$line_num])) {
12022								$this->_out('S');
12023								$this->setLineStyle($line_style[$line_num]);
12024								$this->_outPoint($p[$i - 2], $p[$i - 1]);
12025								$this->_outLine($p[$i], $p[$i + 1]);
12026								$this->_out('S');
12027								$this->_outPoint($p[$i], $p[$i + 1]);
12028							} else {
12029								$this->_outLine($p[$i], $p[$i + 1]);
12030							}
12031						}
12032					} else {
12033						$this->_outLine($p[$i], $p[$i + 1]);
12034					}
12035				}
12036				$this->_out($op);
12037			}
12038		}
12039		if ($draw) {
12040			$this->_outPoint($p[0], $p[1]);
12041			for ($i = 2; $i < $nc; $i = $i + 2) {
12042				$this->_outLine($p[$i], $p[$i + 1]);
12043			}
12044			$this->_out($op);
12045		}
12046	}
12047
12048	/**
12049	 * Draws a regular polygon.
12050	 * @param float $x0 Abscissa of center point.
12051	 * @param float $y0 Ordinate of center point.
12052	 * @param float $r Radius of inscribed circle.
12053	 * @param integer $ns Number of sides.
12054	 * @param float $angle Angle oriented (anti-clockwise). Default value: 0.
12055	 * @param boolean $draw_circle Draw inscribed circle or not. Default value: false.
12056	 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
12057	 * @param array $line_style Line style of polygon sides. Array with keys among the following:
12058	 * <ul>
12059	 *	 <li>all: Line style of all sides. Array like for SetLineStyle().</li>
12060	 *	 <li>0 to ($ns - 1): Line style of each side. Array like for SetLineStyle().</li>
12061	 * </ul>
12062	 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
12063	 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
12064	 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
12065	 * <ul>
12066	 *	 <li>D or empty string: Draw (default).</li>
12067	 *	 <li>F: Fill.</li>
12068	 *	 <li>DF or FD: Draw and fill.</li>
12069	 *	 <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
12070	 *	 <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
12071	 * </ul>
12072	 * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
12073	 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
12074	 * @public
12075	 * @since 2.1.000 (2008-01-08)
12076	 */
12077	public function RegularPolygon($x0, $y0, $r, $ns, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
12078		if (3 > $ns) {
12079			$ns = 3;
12080		}
12081		if ($draw_circle) {
12082			$this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12083		}
12084		$p = array();
12085		for ($i = 0; $i < $ns; ++$i) {
12086			$a = $angle + ($i * 360 / $ns);
12087			$a_rad = deg2rad((float) $a);
12088			$p[] = $x0 + ($r * sin($a_rad));
12089			$p[] = $y0 + ($r * cos($a_rad));
12090		}
12091		$this->Polygon($p, $style, $line_style, $fill_color);
12092	}
12093
12094	/**
12095	 * Draws a star polygon
12096	 * @param float $x0 Abscissa of center point.
12097	 * @param float $y0 Ordinate of center point.
12098	 * @param float $r Radius of inscribed circle.
12099	 * @param integer $nv Number of vertices.
12100	 * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon).
12101	 * @param float $angle Angle oriented (anti-clockwise). Default value: 0.
12102	 * @param boolean $draw_circle Draw inscribed circle or not. Default value is false.
12103	 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
12104	 * @param array $line_style Line style of polygon sides. Array with keys among the following:
12105	 * <ul>
12106	 *	 <li>all: Line style of all sides. Array like for
12107	 * SetLineStyle().</li>
12108	 *	 <li>0 to (n - 1): Line style of each side. Array like for SetLineStyle().</li>
12109	 * </ul>
12110	 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
12111	 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
12112	 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
12113	 * <ul>
12114	 *	 <li>D or empty string: Draw (default).</li>
12115	 *	 <li>F: Fill.</li>
12116	 *	 <li>DF or FD: Draw and fill.</li>
12117	 *	 <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
12118	 *	 <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
12119	 * </ul>
12120	 * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
12121	 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
12122	 * @public
12123	 * @since 2.1.000 (2008-01-08)
12124	 */
12125	public function StarPolygon($x0, $y0, $r, $nv, $ng, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
12126		if ($nv < 2) {
12127			$nv = 2;
12128		}
12129		if ($draw_circle) {
12130			$this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12131		}
12132		$p2 = array();
12133		$visited = array();
12134		for ($i = 0; $i < $nv; ++$i) {
12135			$a = $angle + ($i * 360 / $nv);
12136			$a_rad = deg2rad((float) $a);
12137			$p2[] = $x0 + ($r * sin($a_rad));
12138			$p2[] = $y0 + ($r * cos($a_rad));
12139			$visited[] = false;
12140		}
12141		$p = array();
12142		$i = 0;
12143		do {
12144			$p[] = $p2[$i * 2];
12145			$p[] = $p2[($i * 2) + 1];
12146			$visited[$i] = true;
12147			$i += $ng;
12148			$i %= $nv;
12149		} while (!$visited[$i]);
12150		$this->Polygon($p, $style, $line_style, $fill_color);
12151	}
12152
12153	/**
12154	 * Draws a rounded rectangle.
12155	 * @param float $x Abscissa of upper-left corner.
12156	 * @param float $y Ordinate of upper-left corner.
12157	 * @param float $w Width.
12158	 * @param float $h Height.
12159	 * @param float $r the radius of the circle used to round off the corners of the rectangle.
12160	 * @param string $round_corner Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top right, bottom right, bottom left and top left. Default value: all rounded corner ("1111").
12161	 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
12162	 * @param array $border_style Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12163	 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
12164	 * @public
12165	 * @since 2.1.000 (2008-01-08)
12166	 */
12167	public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12168		$this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
12169	}
12170
12171	/**
12172	 * Draws a rounded rectangle.
12173	 * @param float $x Abscissa of upper-left corner.
12174	 * @param float $y Ordinate of upper-left corner.
12175	 * @param float $w Width.
12176	 * @param float $h Height.
12177	 * @param float $rx the x-axis radius of the ellipse used to round off the corners of the rectangle.
12178	 * @param float $ry the y-axis radius of the ellipse used to round off the corners of the rectangle.
12179	 * @param string $round_corner Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top right, bottom right, bottom left and top left. Default value: all rounded corner ("1111").
12180	 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
12181	 * @param array $border_style Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12182	 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
12183	 * @public
12184	 * @since 4.9.019 (2010-04-22)
12185	 */
12186	public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12187		if ($this->state != 2) {
12188			return;
12189		}
12190		if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
12191			// Not rounded
12192			$this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
12193			return;
12194		}
12195		// Rounded
12196		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12197			$this->setFillColorArray($fill_color);
12198		}
12199		$op = TCPDF_STATIC::getPathPaintOperator($style);
12200		if ($op == 'f') {
12201			$border_style = array();
12202		}
12203		if ($border_style) {
12204			$this->setLineStyle($border_style);
12205		}
12206		$MyArc = 4 / 3 * (sqrt(2) - 1);
12207		$this->_outPoint($x + $rx, $y);
12208		$xc = $x + $w - $rx;
12209		$yc = $y + $ry;
12210		$this->_outLine($xc, $y);
12211		if ($round_corner[0]) {
12212			$this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
12213		} else {
12214			$this->_outLine($x + $w, $y);
12215		}
12216		$xc = $x + $w - $rx;
12217		$yc = $y + $h - $ry;
12218		$this->_outLine($x + $w, $yc);
12219		if ($round_corner[1]) {
12220			$this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
12221		} else {
12222			$this->_outLine($x + $w, $y + $h);
12223		}
12224		$xc = $x + $rx;
12225		$yc = $y + $h - $ry;
12226		$this->_outLine($xc, $y + $h);
12227		if ($round_corner[2]) {
12228			$this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
12229		} else {
12230			$this->_outLine($x, $y + $h);
12231		}
12232		$xc = $x + $rx;
12233		$yc = $y + $ry;
12234		$this->_outLine($x, $yc);
12235		if ($round_corner[3]) {
12236			$this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
12237		} else {
12238			$this->_outLine($x, $y);
12239			$this->_outLine($x + $rx, $y);
12240		}
12241		$this->_out($op);
12242	}
12243
12244	/**
12245	 * Draws a grahic arrow.
12246	 * @param float $x0 Abscissa of first point.
12247	 * @param float $y0 Ordinate of first point.
12248	 * @param float $x1 Abscissa of second point.
12249	 * @param float $y1 Ordinate of second point.
12250	 * @param int $head_style (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead)
12251	 * @param float $arm_size length of arrowhead arms
12252	 * @param int $arm_angle angle between an arm and the shaft
12253	 * @author Piotr Galecki, Nicola Asuni, Andy Meier
12254	 * @since 4.6.018 (2009-07-10)
12255	 */
12256	public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
12257		// getting arrow direction angle
12258		// 0 deg angle is when both arms go along X axis. angle grows clockwise.
12259		$dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
12260		if ($dir_angle < 0) {
12261			$dir_angle += (2 * M_PI);
12262		}
12263		$arm_angle = deg2rad($arm_angle);
12264		$sx1 = $x1;
12265		$sy1 = $y1;
12266		if ($head_style > 0) {
12267			// calculate the stopping point for the arrow shaft
12268			$sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
12269			$sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
12270		}
12271		// main arrow line / shaft
12272		$this->Line($x0, $y0, $sx1, $sy1);
12273		// left arrowhead arm tip
12274		$x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
12275		$y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
12276		// right arrowhead arm tip
12277		$x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
12278		$y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
12279		$mode = 'D';
12280		$style = array();
12281		switch ($head_style) {
12282			case 0: {
12283				// draw only arrowhead arms
12284				$mode = 'D';
12285				$style = array(1, 1, 0);
12286				break;
12287			}
12288			case 1: {
12289				// draw closed arrowhead, but no fill
12290				$mode = 'D';
12291				break;
12292			}
12293			case 2: {
12294				// closed and filled arrowhead
12295				$mode = 'DF';
12296				break;
12297			}
12298			case 3: {
12299				// filled arrowhead
12300				$mode = 'F';
12301				break;
12302			}
12303		}
12304		$this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
12305	}
12306
12307	// END GRAPHIC FUNCTIONS SECTION -----------------------
12308
12309	/**
12310	 * Add a Named Destination.
12311	 * NOTE: destination names are unique, so only last entry will be saved.
12312	 * @param string $name Destination name.
12313	 * @param float $y Y position in user units of the destiantion on the selected page (default = -1 = current position; 0 = page start;).
12314	 * @param int|string $page Target page number (leave empty for current page). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages.
12315	 * @param float $x X position in user units of the destiantion on the selected page (default = -1 = current position;).
12316	 * @return string|false Stripped named destination identifier or false in case of error.
12317	 * @public
12318	 * @author Christian Deligant, Nicola Asuni
12319	 * @since 5.9.097 (2011-06-23)
12320	 */
12321	public function setDestination($name, $y=-1, $page='', $x=-1) {
12322		// remove unsupported characters
12323		$name = TCPDF_STATIC::encodeNameObject($name);
12324		if (TCPDF_STATIC::empty_string($name)) {
12325			return false;
12326		}
12327		if ($y == -1) {
12328			$y = $this->GetY();
12329		} elseif ($y < 0) {
12330			$y = 0;
12331		} elseif ($y > $this->h) {
12332			$y = $this->h;
12333		}
12334		if ($x == -1) {
12335			$x = $this->GetX();
12336		} elseif ($x < 0) {
12337			$x = 0;
12338		} elseif ($x > $this->w) {
12339			$x = $this->w;
12340		}
12341		$fixed = false;
12342		if (!empty($page) AND (substr($page, 0, 1) == '*')) {
12343			$page = intval(substr($page, 1));
12344			// this page number will not be changed when moving/add/deleting pages
12345			$fixed = true;
12346		}
12347		if (empty($page)) {
12348			$page = $this->PageNo();
12349			if (empty($page)) {
12350				return;
12351			}
12352		}
12353		$this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed);
12354		return $name;
12355	}
12356
12357	/**
12358	 * Return the Named Destination array.
12359	 * @return array Named Destination array.
12360	 * @public
12361	 * @author Nicola Asuni
12362	 * @since 5.9.097 (2011-06-23)
12363	 */
12364	public function getDestination() {
12365		return $this->dests;
12366	}
12367
12368	/**
12369	 * Insert Named Destinations.
12370	 * @protected
12371	 * @author Johannes G\FCntert, Nicola Asuni
12372	 * @since 5.9.098 (2011-06-23)
12373	 */
12374	protected function _putdests() {
12375		if (empty($this->dests)) {
12376			return;
12377		}
12378		$this->n_dests = $this->_newobj();
12379		$out = ' <<';
12380		foreach($this->dests as $name => $o) {
12381			$out .= ' /'.$name.' '.sprintf('[%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
12382		}
12383		$out .= ' >>';
12384		$out .= "\n".'endobj';
12385		$this->_out($out);
12386	}
12387
12388	/**
12389	 * Adds a bookmark - alias for Bookmark().
12390	 * @param string $txt Bookmark description.
12391	 * @param int $level Bookmark level (minimum value is 0).
12392	 * @param float $y Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
12393	 * @param int|string $page Target page number (leave empty for current page). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages.
12394	 * @param string $style Font style: B = Bold, I = Italic, BI = Bold + Italic.
12395	 * @param array $color RGB color array (values from 0 to 255).
12396	 * @param float $x X position in user units of the bookmark on the selected page (default = -1 = current position;).
12397	 * @param mixed $link URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name).
12398	 * @public
12399	 */
12400	public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12401		$this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link);
12402	}
12403
12404	/**
12405	 * Adds a bookmark.
12406	 * @param string $txt Bookmark description.
12407	 * @param int $level Bookmark level (minimum value is 0).
12408	 * @param float $y Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
12409	 * @param int|string $page Target page number (leave empty for current page). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages.
12410	 * @param string $style Font style: B = Bold, I = Italic, BI = Bold + Italic.
12411	 * @param array $color RGB color array (values from 0 to 255).
12412	 * @param float $x X position in user units of the bookmark on the selected page (default = -1 = current position;).
12413	 * @param mixed $link URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name).
12414	 * @public
12415	 * @since 2.1.002 (2008-02-12)
12416	 */
12417	public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12418		if ($level < 0) {
12419			$level = 0;
12420		}
12421		if (isset($this->outlines[0])) {
12422			$lastoutline = end($this->outlines);
12423			$maxlevel = $lastoutline['l'] + 1;
12424		} else {
12425			$maxlevel = 0;
12426		}
12427		if ($level > $maxlevel) {
12428			$level = $maxlevel;
12429		}
12430		if ($y == -1) {
12431			$y = $this->GetY();
12432		} elseif ($y < 0) {
12433			$y = 0;
12434		} elseif ($y > $this->h) {
12435			$y = $this->h;
12436		}
12437		if ($x == -1) {
12438			$x = $this->GetX();
12439		} elseif ($x < 0) {
12440			$x = 0;
12441		} elseif ($x > $this->w) {
12442			$x = $this->w;
12443		}
12444		$fixed = false;
12445		$pageAsString = (string) $page;
12446		if ($pageAsString && $pageAsString[0] == '*') {
12447			$page = intval(substr($page, 1));
12448			// this page number will not be changed when moving/add/deleting pages
12449			$fixed = true;
12450		}
12451		if (empty($page)) {
12452			$page = $this->PageNo();
12453			if (empty($page)) {
12454				return;
12455			}
12456		}
12457		$this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed, 's' => strtoupper($style), 'c' => $color, 'u' => $link);
12458	}
12459
12460	/**
12461	 * Sort bookmarks for page and key.
12462	 * @protected
12463	 * @since 5.9.119 (2011-09-19)
12464	 */
12465	protected function sortBookmarks() {
12466		// get sorting columns
12467		$outline_p = array();
12468		$outline_y = array();
12469		foreach ($this->outlines as $key => $row) {
12470			$outline_p[$key] = $row['p'];
12471			$outline_k[$key] = $key;
12472		}
12473		// sort outlines by page and original position
12474		array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
12475	}
12476
12477	/**
12478	 * Create a bookmark PDF string.
12479	 * @protected
12480	 * @author Olivier Plathey, Nicola Asuni
12481	 * @since 2.1.002 (2008-02-12)
12482	 */
12483	protected function _putbookmarks() {
12484		$nb = count($this->outlines);
12485		if ($nb == 0) {
12486			return;
12487		}
12488		// sort bookmarks
12489		$this->sortBookmarks();
12490		$lru = array();
12491		$level = 0;
12492		foreach ($this->outlines as $i => $o) {
12493			if ($o['l'] > 0) {
12494				$parent = $lru[($o['l'] - 1)];
12495				//Set parent and last pointers
12496				$this->outlines[$i]['parent'] = $parent;
12497				$this->outlines[$parent]['last'] = $i;
12498				if ($o['l'] > $level) {
12499					//Level increasing: set first pointer
12500					$this->outlines[$parent]['first'] = $i;
12501				}
12502			} else {
12503				$this->outlines[$i]['parent'] = $nb;
12504			}
12505			if (($o['l'] <= $level) AND ($i > 0)) {
12506				//Set prev and next pointers
12507				$prev = $lru[$o['l']];
12508				$this->outlines[$prev]['next'] = $i;
12509				$this->outlines[$i]['prev'] = $prev;
12510			}
12511			$lru[$o['l']] = $i;
12512			$level = $o['l'];
12513		}
12514		//Outline items
12515		$n = $this->n + 1;
12516		$nltags = '/<br[\s]?\/>|<\/(blockquote|dd|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|p|pre|ul|tcpdf|table|tr|td)>/si';
12517		foreach ($this->outlines as $i => $o) {
12518			$oid = $this->_newobj();
12519			// covert HTML title to string
12520			$title = preg_replace($nltags, "\n", $o['t']);
12521			$title = preg_replace("/[\r]+/si", '', $title);
12522			$title = preg_replace("/[\n]+/si", "\n", $title);
12523			$title = strip_tags($title);
12524			$title = $this->stringTrim($title);
12525			$out = '<</Title '.$this->_textstring($title, $oid);
12526			$out .= ' /Parent '.($n + $o['parent']).' 0 R';
12527			if (isset($o['prev'])) {
12528				$out .= ' /Prev '.($n + $o['prev']).' 0 R';
12529			}
12530			if (isset($o['next'])) {
12531				$out .= ' /Next '.($n + $o['next']).' 0 R';
12532			}
12533			if (isset($o['first'])) {
12534				$out .= ' /First '.($n + $o['first']).' 0 R';
12535			}
12536			if (isset($o['last'])) {
12537				$out .= ' /Last '.($n + $o['last']).' 0 R';
12538			}
12539			if (isset($o['u']) AND !empty($o['u'])) {
12540				// link
12541				if (is_string($o['u'])) {
12542					if ($o['u'][0] == '#') {
12543						// internal destination
12544						$out .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($o['u'], 1));
12545					} elseif ($o['u'][0] == '%') {
12546						// embedded PDF file
12547						$filename = basename(substr($o['u'], 1));
12548						$out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
12549					} elseif ($o['u'][0] == '*') {
12550						// embedded generic file
12551						$filename = basename(substr($o['u'], 1));
12552						$jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});';
12553						$out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>';
12554					} else {
12555						// external URI link
12556						$out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>';
12557					}
12558				} elseif (isset($this->links[$o['u']])) {
12559					// internal link ID
12560					$l = $this->links[$o['u']];
12561					if (isset($this->page_obj_id[($l['p'])])) {
12562						$out .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l['p'])], ($this->pagedim[$l['p']]['h'] - ($l['y'] * $this->k)));
12563					}
12564				}
12565			} elseif (isset($this->page_obj_id[($o['p'])])) {
12566				// link to a page
12567				$out .= ' '.sprintf('/Dest [%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
12568			}
12569			// set font style
12570			$style = 0;
12571			if (!empty($o['s'])) {
12572				// bold
12573				if (strpos($o['s'], 'B') !== false) {
12574					$style |= 2;
12575				}
12576				// oblique
12577				if (strpos($o['s'], 'I') !== false) {
12578					$style |= 1;
12579				}
12580			}
12581			$out .= sprintf(' /F %d', $style);
12582			// set bookmark color
12583			if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
12584				$color = array_values($o['c']);
12585				$out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
12586			} else {
12587				// black
12588				$out .= ' /C [0.0 0.0 0.0]';
12589			}
12590			$out .= ' /Count 0'; // normally closed item
12591			$out .= ' >>';
12592			$out .= "\n".'endobj';
12593			$this->_out($out);
12594		}
12595		//Outline root
12596		$this->OutlineRoot = $this->_newobj();
12597		$this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj');
12598	}
12599
12600	// --- JAVASCRIPT ------------------------------------------------------
12601
12602	/**
12603	 * Adds a javascript
12604	 * @param string $script Javascript code
12605	 * @public
12606	 * @author Johannes G\FCntert, Nicola Asuni
12607	 * @since 2.1.002 (2008-02-12)
12608	 */
12609	public function IncludeJS($script) {
12610		$this->javascript .= $script;
12611	}
12612
12613	/**
12614	 * Adds a javascript object and return object ID
12615	 * @param string $script Javascript code
12616	 * @param boolean $onload if true executes this object when opening the document
12617	 * @return int internal object ID
12618	 * @public
12619	 * @author Nicola Asuni
12620	 * @since 4.8.000 (2009-09-07)
12621	 */
12622	public function addJavascriptObject($script, $onload=false) {
12623		if ($this->pdfa_mode) {
12624			// javascript is not allowed in PDF/A mode
12625			return false;
12626		}
12627		++$this->n;
12628		$this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload);
12629		return $this->n;
12630	}
12631
12632	/**
12633	 * Create a javascript PDF string.
12634	 * @protected
12635	 * @author Johannes G\FCntert, Nicola Asuni
12636	 * @since 2.1.002 (2008-02-12)
12637	 */
12638	protected function _putjavascript() {
12639		if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) {
12640			return;
12641		}
12642		if (strpos($this->javascript, 'this.addField') > 0) {
12643			if (!$this->ur['enabled']) {
12644				//$this->setUserRights();
12645			}
12646			// the following two lines are used to avoid form fields duplication after saving
12647			// The addField method only works when releasing user rights (UR3)
12648			$jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
12649			$jsb = "getField('tcpdfdocsaved').value='saved';";
12650			$this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
12651		}
12652		// name tree for javascript
12653		$this->n_js = '<< /Names [';
12654		if (!empty($this->javascript)) {
12655			$this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
12656		}
12657		if (!empty($this->js_objects)) {
12658			foreach ($this->js_objects as $key => $val) {
12659				if ($val['onload']) {
12660					$this->n_js .= ' (JS'.$key.') '.$key.' 0 R';
12661				}
12662			}
12663		}
12664		$this->n_js .= ' ] >>';
12665		// default Javascript object
12666		if (!empty($this->javascript)) {
12667			$obj_id = $this->_newobj();
12668			$out = '<< /S /JavaScript';
12669			$out .= ' /JS '.$this->_textstring($this->javascript, $obj_id);
12670			$out .= ' >>';
12671			$out .= "\n".'endobj';
12672			$this->_out($out);
12673		}
12674		// additional Javascript objects
12675		if (!empty($this->js_objects)) {
12676			foreach ($this->js_objects as $key => $val) {
12677				$out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
12678				$this->_out($out);
12679			}
12680		}
12681	}
12682
12683	/**
12684	 * Adds a javascript form field.
12685	 * @param string $type field type
12686	 * @param string $name field name
12687	 * @param int $x horizontal position
12688	 * @param int $y vertical position
12689	 * @param int $w width
12690	 * @param int $h height
12691	 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12692	 * @protected
12693	 * @author Denis Van Nuffelen, Nicola Asuni
12694	 * @since 2.1.002 (2008-02-12)
12695	 */
12696	protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
12697		if ($this->rtl) {
12698			$x = $x - $w;
12699		}
12700		// the followind avoid fields duplication after saving the document
12701		$this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {";
12702		$k = $this->k;
12703		$this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%u,[%F,%F,%F,%F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n";
12704		$this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
12705		foreach($prop as $key => $val) {
12706			if (strcmp(substr($key, -5), 'Color') == 0) {
12707				$val = TCPDF_COLORS::_JScolor($val);
12708			} else {
12709				$val = "'".$val."'";
12710			}
12711			$this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
12712		}
12713		if ($this->rtl) {
12714			$this->x -= $w;
12715		} else {
12716			$this->x += $w;
12717		}
12718		$this->javascript .= '}';
12719	}
12720
12721	// --- FORM FIELDS -----------------------------------------------------
12722
12723
12724
12725	/**
12726	 * Set default properties for form fields.
12727	 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12728	 * @public
12729	 * @author Nicola Asuni
12730	 * @since 4.8.000 (2009-09-06)
12731	 */
12732	public function setFormDefaultProp($prop=array()) {
12733		$this->default_form_prop = $prop;
12734	}
12735
12736	/**
12737	 * Return the default properties for form fields.
12738	 * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12739	 * @public
12740	 * @author Nicola Asuni
12741	 * @since 4.8.000 (2009-09-06)
12742	 */
12743	public function getFormDefaultProp() {
12744		return $this->default_form_prop;
12745	}
12746
12747	/**
12748	 * Creates a text field
12749	 * @param string $name field name
12750	 * @param float $w Width of the rectangle
12751	 * @param float $h Height of the rectangle
12752	 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12753	 * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
12754	 * @param float|null $x Abscissa of the upper-left corner of the rectangle
12755	 * @param float|null $y Ordinate of the upper-left corner of the rectangle
12756	 * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12757	 * @public
12758	 * @author Nicola Asuni
12759	 * @since 4.8.000 (2009-09-07)
12760	 */
12761	public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x=null, $y=null, $js=false) {
12762		if (TCPDF_STATIC::empty_string($x)) {
12763			$x = $this->x;
12764		}
12765		if (TCPDF_STATIC::empty_string($y)) {
12766			$y = $this->y;
12767		}
12768		// check page for no-write regions and adapt page margins if necessary
12769		list($x, $y) = $this->checkPageRegions($h, $x, $y);
12770		if ($js) {
12771			$this->_addfield('text', $name, $x, $y, $w, $h, $prop);
12772			return;
12773		}
12774		// get default style
12775		$prop = array_merge($this->getFormDefaultProp(), $prop);
12776		// get annotation data
12777		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12778		// set default appearance stream
12779		$this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12780		$fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12781		$popt['da'] = $fontstyle;
12782		// build appearance stream
12783		$popt['ap'] = array();
12784		$popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12785		$text = '';
12786		if (isset($prop['value']) AND !empty($prop['value'])) {
12787			$text = $prop['value'];
12788		} elseif (isset($opt['v']) AND !empty($opt['v'])) {
12789			$text = $opt['v'];
12790		}
12791		$tmpid = $this->startTemplate($w, $h, false);
12792		$align = '';
12793		if (isset($popt['q'])) {
12794			switch ($popt['q']) {
12795				case 0: {
12796					$align = 'L';
12797					break;
12798				}
12799				case 1: {
12800					$align = 'C';
12801					break;
12802				}
12803				case 2: {
12804					$align = 'R';
12805					break;
12806				}
12807				default: {
12808					$align = '';
12809					break;
12810				}
12811			}
12812		}
12813		$this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12814		$this->endTemplate();
12815		--$this->n;
12816		$popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12817		unset($this->xobjects[$tmpid]);
12818		$popt['ap']['n'] .= 'Q EMC';
12819		// merge options
12820		$opt = array_merge($popt, $opt);
12821		// remove some conflicting options
12822		unset($opt['bs']);
12823		// set remaining annotation data
12824		$opt['Subtype'] = 'Widget';
12825		$opt['ft'] = 'Tx';
12826		$opt['t'] = $name;
12827		// Additional annotation's parameters (check _putannotsobj() method):
12828		//$opt['f']
12829		//$opt['as']
12830		//$opt['bs']
12831		//$opt['be']
12832		//$opt['c']
12833		//$opt['border']
12834		//$opt['h']
12835		//$opt['mk'];
12836		//$opt['mk']['r']
12837		//$opt['mk']['bc'];
12838		//$opt['mk']['bg'];
12839		unset($opt['mk']['ca']);
12840		unset($opt['mk']['rc']);
12841		unset($opt['mk']['ac']);
12842		unset($opt['mk']['i']);
12843		unset($opt['mk']['ri']);
12844		unset($opt['mk']['ix']);
12845		unset($opt['mk']['if']);
12846		//$opt['mk']['if']['sw'];
12847		//$opt['mk']['if']['s'];
12848		//$opt['mk']['if']['a'];
12849		//$opt['mk']['if']['fb'];
12850		unset($opt['mk']['tp']);
12851		//$opt['tu']
12852		//$opt['tm']
12853		//$opt['ff']
12854		//$opt['v']
12855		//$opt['dv']
12856		//$opt['a']
12857		//$opt['aa']
12858		//$opt['q']
12859		$this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12860		if ($this->rtl) {
12861			$this->x -= $w;
12862		} else {
12863			$this->x += $w;
12864		}
12865	}
12866
12867	/**
12868	 * Creates a RadioButton field.
12869	 * @param string $name Field name.
12870	 * @param int $w Width of the radio button.
12871	 * @param array $prop Javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12872	 * @param array $opt Annotation parameters. Possible values are described on official PDF32000_2008 reference.
12873	 * @param string $onvalue Value to be returned if selected.
12874	 * @param boolean $checked Define the initial state.
12875	 * @param float|null $x Abscissa of the upper-left corner of the rectangle
12876	 * @param float|null $y Ordinate of the upper-left corner of the rectangle
12877	 * @param boolean $js If true put the field using JavaScript (requires Acrobat Writer to be rendered).
12878	 * @public
12879	 * @author Nicola Asuni
12880	 * @since 4.8.000 (2009-09-07)
12881	 */
12882	public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x=null, $y=null, $js=false) {
12883		if (TCPDF_STATIC::empty_string($x)) {
12884			$x = $this->x;
12885		}
12886		if (TCPDF_STATIC::empty_string($y)) {
12887			$y = $this->y;
12888		}
12889		// check page for no-write regions and adapt page margins if necessary
12890		list($x, $y) = $this->checkPageRegions($w, $x, $y);
12891		if ($js) {
12892			$this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
12893			return;
12894		}
12895		if (TCPDF_STATIC::empty_string($onvalue)) {
12896			$onvalue = 'On';
12897		}
12898		if ($checked) {
12899			$defval = $onvalue;
12900		} else {
12901			$defval = 'Off';
12902		}
12903		// set font
12904		$font = 'zapfdingbats';
12905		if ($this->pdfa_mode) {
12906			// all fonts must be embedded
12907			$font = 'pdfa'.$font;
12908		}
12909		$this->AddFont($font);
12910		$tmpfont = $this->getFontBuffer($font);
12911		// set data for parent group
12912		if (!isset($this->radiobutton_groups[$this->page])) {
12913			$this->radiobutton_groups[$this->page] = array();
12914		}
12915		if (!isset($this->radiobutton_groups[$this->page][$name])) {
12916			$this->radiobutton_groups[$this->page][$name] = array();
12917			++$this->n;
12918			$this->radiobutton_groups[$this->page][$name]['n'] = $this->n;
12919			$this->radio_groups[] = $this->n;
12920		}
12921		$kid = ($this->n + 1);
12922		// save object ID to be added on Kids entry on parent object
12923		$this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval);
12924		// get default style
12925		$prop = array_merge($this->getFormDefaultProp(), $prop);
12926		$prop['NoToggleToOff'] = 'true';
12927		$prop['Radio'] = 'true';
12928		$prop['borderStyle'] = 'inset';
12929		// get annotation data
12930		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12931		// set additional default options
12932		$this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
12933		$fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
12934		$popt['da'] = $fontstyle;
12935		// build appearance stream
12936		$popt['ap'] = array();
12937		$popt['ap']['n'] = array();
12938		$fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k);
12939		$fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
12940		$popt['ap']['n'][$onvalue] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(108).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
12941		$popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(109).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
12942		if (!isset($popt['mk'])) {
12943			$popt['mk'] = array();
12944		}
12945		$popt['mk']['ca'] = '(l)';
12946		// merge options
12947		$opt = array_merge($popt, $opt);
12948		// set remaining annotation data
12949		$opt['Subtype'] = 'Widget';
12950		$opt['ft'] = 'Btn';
12951		if ($checked) {
12952			$opt['v'] = array('/'.$onvalue);
12953			$opt['as'] = $onvalue;
12954		} else {
12955			$opt['as'] = 'Off';
12956		}
12957		// store readonly flag
12958		if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) {
12959			$this->radiobutton_groups[$this->page][$name]['#readonly#'] = false;
12960		}
12961		$this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64);
12962		$this->Annotation($x, $y, $w, $w, $name, $opt, 0);
12963		if ($this->rtl) {
12964			$this->x -= $w;
12965		} else {
12966			$this->x += $w;
12967		}
12968	}
12969
12970	/**
12971	 * Creates a List-box field
12972	 * @param string $name field name
12973	 * @param int $w width
12974	 * @param int $h height
12975	 * @param array $values array containing the list of values.
12976	 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12977	 * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
12978	 * @param float|null $x Abscissa of the upper-left corner of the rectangle
12979	 * @param float|null $y Ordinate of the upper-left corner of the rectangle
12980	 * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12981	 * @public
12982	 * @author Nicola Asuni
12983	 * @since 4.8.000 (2009-09-07)
12984	 */
12985	public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x=null, $y=null, $js=false) {
12986		if (TCPDF_STATIC::empty_string($x)) {
12987			$x = $this->x;
12988		}
12989		if (TCPDF_STATIC::empty_string($y)) {
12990			$y = $this->y;
12991		}
12992		// check page for no-write regions and adapt page margins if necessary
12993		list($x, $y) = $this->checkPageRegions($h, $x, $y);
12994		if ($js) {
12995			$this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
12996			$s = '';
12997			foreach ($values as $value) {
12998				if (is_array($value)) {
12999					$s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
13000				} else {
13001					$s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
13002				}
13003			}
13004			$this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
13005			return;
13006		}
13007		// get default style
13008		$prop = array_merge($this->getFormDefaultProp(), $prop);
13009		// get annotation data
13010		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13011		// set additional default values
13012		$this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13013		$fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13014		$popt['da'] = $fontstyle;
13015		// build appearance stream
13016		$popt['ap'] = array();
13017		$popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13018		$text = '';
13019		foreach($values as $item) {
13020			if (is_array($item)) {
13021				$text .= $item[1]."\n";
13022			} else {
13023				$text .= $item."\n";
13024			}
13025		}
13026		$tmpid = $this->startTemplate($w, $h, false);
13027		$this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13028		$this->endTemplate();
13029		--$this->n;
13030		$popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13031		unset($this->xobjects[$tmpid]);
13032		$popt['ap']['n'] .= 'Q EMC';
13033		// merge options
13034		$opt = array_merge($popt, $opt);
13035		// set remaining annotation data
13036		$opt['Subtype'] = 'Widget';
13037		$opt['ft'] = 'Ch';
13038		$opt['t'] = $name;
13039		$opt['opt'] = $values;
13040		unset($opt['mk']['ca']);
13041		unset($opt['mk']['rc']);
13042		unset($opt['mk']['ac']);
13043		unset($opt['mk']['i']);
13044		unset($opt['mk']['ri']);
13045		unset($opt['mk']['ix']);
13046		unset($opt['mk']['if']);
13047		unset($opt['mk']['tp']);
13048		$this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13049		if ($this->rtl) {
13050			$this->x -= $w;
13051		} else {
13052			$this->x += $w;
13053		}
13054	}
13055
13056	/**
13057	 * Creates a Combo-box field
13058	 * @param string $name field name
13059	 * @param int $w width
13060	 * @param int $h height
13061	 * @param array $values array containing the list of values.
13062	 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13063	 * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
13064	 * @param float|null $x Abscissa of the upper-left corner of the rectangle
13065	 * @param float|null $y Ordinate of the upper-left corner of the rectangle
13066	 * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13067	 * @public
13068	 * @author Nicola Asuni
13069	 * @since 4.8.000 (2009-09-07)
13070	 */
13071	public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x=null, $y=null, $js=false) {
13072		if (TCPDF_STATIC::empty_string($x)) {
13073			$x = $this->x;
13074		}
13075		if (TCPDF_STATIC::empty_string($y)) {
13076			$y = $this->y;
13077		}
13078		// check page for no-write regions and adapt page margins if necessary
13079		list($x, $y) = $this->checkPageRegions($h, $x, $y);
13080		if ($js) {
13081			$this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
13082			$s = '';
13083			foreach ($values as $value) {
13084				if (is_array($value)) {
13085					$s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
13086				} else {
13087					$s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
13088				}
13089			}
13090			$this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
13091			return;
13092		}
13093		// get default style
13094		$prop = array_merge($this->getFormDefaultProp(), $prop);
13095		$prop['Combo'] = true;
13096		// get annotation data
13097		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13098		// set additional default options
13099		$this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13100		$fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13101		$popt['da'] = $fontstyle;
13102		// build appearance stream
13103		$popt['ap'] = array();
13104		$popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13105		$text = '';
13106		foreach($values as $item) {
13107			if (is_array($item)) {
13108				$text .= $item[1]."\n";
13109			} else {
13110				$text .= $item."\n";
13111			}
13112		}
13113		$tmpid = $this->startTemplate($w, $h, false);
13114		$this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13115		$this->endTemplate();
13116		--$this->n;
13117		$popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13118		unset($this->xobjects[$tmpid]);
13119		$popt['ap']['n'] .= 'Q EMC';
13120		// merge options
13121		$opt = array_merge($popt, $opt);
13122		// set remaining annotation data
13123		$opt['Subtype'] = 'Widget';
13124		$opt['ft'] = 'Ch';
13125		$opt['t'] = $name;
13126		$opt['opt'] = $values;
13127		unset($opt['mk']['ca']);
13128		unset($opt['mk']['rc']);
13129		unset($opt['mk']['ac']);
13130		unset($opt['mk']['i']);
13131		unset($opt['mk']['ri']);
13132		unset($opt['mk']['ix']);
13133		unset($opt['mk']['if']);
13134		unset($opt['mk']['tp']);
13135		$this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13136		if ($this->rtl) {
13137			$this->x -= $w;
13138		} else {
13139			$this->x += $w;
13140		}
13141	}
13142
13143	/**
13144	 * Creates a CheckBox field
13145	 * @param string $name field name
13146	 * @param int $w width
13147	 * @param boolean $checked define the initial state.
13148	 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13149	 * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
13150	 * @param string $onvalue value to be returned if selected.
13151	 * @param float|null $x Abscissa of the upper-left corner of the rectangle
13152	 * @param float|null $y Ordinate of the upper-left corner of the rectangle
13153	 * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13154	 * @public
13155	 * @author Nicola Asuni
13156	 * @since 4.8.000 (2009-09-07)
13157	 */
13158	public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x=null, $y=null, $js=false) {
13159		if (TCPDF_STATIC::empty_string($x)) {
13160			$x = $this->x;
13161		}
13162		if (TCPDF_STATIC::empty_string($y)) {
13163			$y = $this->y;
13164		}
13165		// check page for no-write regions and adapt page margins if necessary
13166		list($x, $y) = $this->checkPageRegions($w, $x, $y);
13167		if ($js) {
13168			$this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
13169			return;
13170		}
13171		if (!isset($prop['value'])) {
13172			$prop['value'] = array('Yes');
13173		}
13174		// get default style
13175		$prop = array_merge($this->getFormDefaultProp(), $prop);
13176		$prop['borderStyle'] = 'inset';
13177		// get annotation data
13178		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13179		// set additional default options
13180		$font = 'zapfdingbats';
13181		if ($this->pdfa_mode) {
13182			// all fonts must be embedded
13183			$font = 'pdfa'.$font;
13184		}
13185		$this->AddFont($font);
13186		$tmpfont = $this->getFontBuffer($font);
13187		$this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
13188		$fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
13189		$popt['da'] = $fontstyle;
13190		// build appearance stream
13191		$popt['ap'] = array();
13192		$popt['ap']['n'] = array();
13193		$fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k);
13194		$fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
13195		$popt['ap']['n']['Yes'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(110).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
13196		$popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(111).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
13197		// merge options
13198		$opt = array_merge($popt, $opt);
13199		// set remaining annotation data
13200		$opt['Subtype'] = 'Widget';
13201		$opt['ft'] = 'Btn';
13202		$opt['t'] = $name;
13203		if (TCPDF_STATIC::empty_string($onvalue)) {
13204			$onvalue = 'Yes';
13205		}
13206		$opt['opt'] = array($onvalue);
13207		if ($checked) {
13208			$opt['v'] = array('/Yes');
13209			$opt['as'] = 'Yes';
13210		} else {
13211			$opt['v'] = array('/Off');
13212			$opt['as'] = 'Off';
13213		}
13214		$this->Annotation($x, $y, $w, $w, $name, $opt, 0);
13215		if ($this->rtl) {
13216			$this->x -= $w;
13217		} else {
13218			$this->x += $w;
13219		}
13220	}
13221
13222	/**
13223	 * Creates a button field
13224	 * @param string $name field name
13225	 * @param int $w width
13226	 * @param int $h height
13227	 * @param string $caption caption.
13228	 * @param mixed $action action triggered by pressing the button. Use a string to specify a javascript action. Use an array to specify a form action options as on section 12.7.5 of PDF32000_2008.
13229	 * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13230	 * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
13231	 * @param float|null $x Abscissa of the upper-left corner of the rectangle
13232	 * @param float|null $y Ordinate of the upper-left corner of the rectangle
13233	 * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13234	 * @public
13235	 * @author Nicola Asuni
13236	 * @since 4.8.000 (2009-09-07)
13237	 */
13238	public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x=null, $y=null, $js=false) {
13239		if (TCPDF_STATIC::empty_string($x)) {
13240			$x = $this->x;
13241		}
13242		if (TCPDF_STATIC::empty_string($y)) {
13243			$y = $this->y;
13244		}
13245		// check page for no-write regions and adapt page margins if necessary
13246		list($x, $y) = $this->checkPageRegions($h, $x, $y);
13247		if ($js) {
13248			$this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
13249			$this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
13250			$this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
13251			$this->javascript .= 'f'.$name.".highlight='push';\n";
13252			$this->javascript .= 'f'.$name.".print=false;\n";
13253			return;
13254		}
13255		// get default style
13256		$prop = array_merge($this->getFormDefaultProp(), $prop);
13257		$prop['Pushbutton'] = 'true';
13258		$prop['highlight'] = 'push';
13259		$prop['display'] = 'display.noPrint';
13260		// get annotation data
13261		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13262		$this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13263		$fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13264		$popt['da'] = $fontstyle;
13265		// build appearance stream
13266		$popt['ap'] = array();
13267		$popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13268		$tmpid = $this->startTemplate($w, $h, false);
13269		$bw = (2 / $this->k); // border width
13270		$border = array(
13271			'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13272			'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
13273			'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13274			'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
13275		$this->setFillColor(204);
13276		$this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
13277		$this->endTemplate();
13278		--$this->n;
13279		$popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13280		unset($this->xobjects[$tmpid]);
13281		$popt['ap']['n'] .= 'Q EMC';
13282		// set additional default options
13283		if (!isset($popt['mk'])) {
13284			$popt['mk'] = array();
13285		}
13286		$ann_obj_id = ($this->n + 1);
13287		if (!empty($action) AND !is_array($action)) {
13288			$ann_obj_id = ($this->n + 2);
13289		}
13290		$popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
13291		$popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
13292		$popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
13293		// merge options
13294		$opt = array_merge($popt, $opt);
13295		// set remaining annotation data
13296		$opt['Subtype'] = 'Widget';
13297		$opt['ft'] = 'Btn';
13298		$opt['t'] = $caption;
13299		$opt['v'] = $name;
13300		if (!empty($action)) {
13301			if (is_array($action)) {
13302				// form action options as on section 12.7.5 of PDF32000_2008.
13303				$opt['aa'] = '/D <<';
13304				$bmode = array('SubmitForm', 'ResetForm', 'ImportData');
13305				foreach ($action AS $key => $val) {
13306					if (($key == 'S') AND in_array($val, $bmode)) {
13307						$opt['aa'] .= ' /S /'.$val;
13308					} elseif (($key == 'F') AND (!empty($val))) {
13309						$opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
13310					} elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
13311						$opt['aa'] .= ' /Fields [';
13312						foreach ($val AS $field) {
13313							$opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
13314						}
13315						$opt['aa'] .= ']';
13316					} elseif (($key == 'Flags')) {
13317						$ff = 0;
13318						if (is_array($val)) {
13319							foreach ($val AS $flag) {
13320								switch ($flag) {
13321									case 'Include/Exclude': {
13322										$ff += 1 << 0;
13323										break;
13324									}
13325									case 'IncludeNoValueFields': {
13326										$ff += 1 << 1;
13327										break;
13328									}
13329									case 'ExportFormat': {
13330										$ff += 1 << 2;
13331										break;
13332									}
13333									case 'GetMethod': {
13334										$ff += 1 << 3;
13335										break;
13336									}
13337									case 'SubmitCoordinates': {
13338										$ff += 1 << 4;
13339										break;
13340									}
13341									case 'XFDF': {
13342										$ff += 1 << 5;
13343										break;
13344									}
13345									case 'IncludeAppendSaves': {
13346										$ff += 1 << 6;
13347										break;
13348									}
13349									case 'IncludeAnnotations': {
13350										$ff += 1 << 7;
13351										break;
13352									}
13353									case 'SubmitPDF': {
13354										$ff += 1 << 8;
13355										break;
13356									}
13357									case 'CanonicalFormat': {
13358										$ff += 1 << 9;
13359										break;
13360									}
13361									case 'ExclNonUserAnnots': {
13362										$ff += 1 << 10;
13363										break;
13364									}
13365									case 'ExclFKey': {
13366										$ff += 1 << 11;
13367										break;
13368									}
13369									case 'EmbedForm': {
13370										$ff += 1 << 13;
13371										break;
13372									}
13373								}
13374							}
13375						} else {
13376							$ff = intval($val);
13377						}
13378						$opt['aa'] .= ' /Flags '.$ff;
13379					}
13380				}
13381				$opt['aa'] .= ' >>';
13382			} else {
13383				// Javascript action or raw action command
13384				$js_obj_id = $this->addJavascriptObject($action);
13385				$opt['aa'] = '/D '.$js_obj_id.' 0 R';
13386			}
13387		}
13388		$this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13389		if ($this->rtl) {
13390			$this->x -= $w;
13391		} else {
13392			$this->x += $w;
13393		}
13394	}
13395
13396	// --- END FORMS FIELDS ------------------------------------------------
13397
13398	/**
13399	 * Add certification signature (DocMDP or UR3)
13400	 * You can set only one signature type
13401	 * @protected
13402	 * @author Nicola Asuni
13403	 * @since 4.6.008 (2009-05-07)
13404	 */
13405	protected function _putsignature() {
13406		if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
13407			return;
13408		}
13409		$sigobjid = ($this->sig_obj_id + 1);
13410		$out = $this->_getobj($sigobjid)."\n";
13411		$out .= '<< /Type /Sig';
13412		$out .= ' /Filter /Adobe.PPKLite';
13413		$out .= ' /SubFilter /adbe.pkcs7.detached';
13414		$out .= ' '.TCPDF_STATIC::$byterange_string;
13415		$out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>';
13416		if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
13417			$out .= ' /Reference ['; // array of signature reference dictionaries
13418			$out .= ' << /Type /SigRef';
13419			if ($this->signature_data['cert_type'] > 0) {
13420				$out .= ' /TransformMethod /DocMDP';
13421				$out .= ' /TransformParams <<';
13422				$out .= ' /Type /TransformParams';
13423				$out .= ' /P '.$this->signature_data['cert_type'];
13424				$out .= ' /V /1.2';
13425			} else {
13426				$out .= ' /TransformMethod /UR3';
13427				$out .= ' /TransformParams <<';
13428				$out .= ' /Type /TransformParams';
13429				$out .= ' /V /2.2';
13430				if (!TCPDF_STATIC::empty_string($this->ur['document'])) {
13431					$out .= ' /Document['.$this->ur['document'].']';
13432				}
13433				if (!TCPDF_STATIC::empty_string($this->ur['form'])) {
13434					$out .= ' /Form['.$this->ur['form'].']';
13435				}
13436				if (!TCPDF_STATIC::empty_string($this->ur['signature'])) {
13437					$out .= ' /Signature['.$this->ur['signature'].']';
13438				}
13439				if (!TCPDF_STATIC::empty_string($this->ur['annots'])) {
13440					$out .= ' /Annots['.$this->ur['annots'].']';
13441				}
13442				if (!TCPDF_STATIC::empty_string($this->ur['ef'])) {
13443					$out .= ' /EF['.$this->ur['ef'].']';
13444				}
13445				if (!TCPDF_STATIC::empty_string($this->ur['formex'])) {
13446					$out .= ' /FormEX['.$this->ur['formex'].']';
13447				}
13448			}
13449			$out .= ' >>'; // close TransformParams
13450			// optional digest data (values must be calculated and replaced later)
13451			//$out .= ' /Data ********** 0 R';
13452			//$out .= ' /DigestMethod/MD5';
13453			//$out .= ' /DigestLocation[********** 34]';
13454			//$out .= ' /DigestValue<********************************>';
13455			$out .= ' >>';
13456			$out .= ' ]'; // end of reference
13457		}
13458		if (isset($this->signature_data['info']['Name']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Name'])) {
13459			$out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid);
13460		}
13461		if (isset($this->signature_data['info']['Location']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Location'])) {
13462			$out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid);
13463		}
13464		if (isset($this->signature_data['info']['Reason']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Reason'])) {
13465			$out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid);
13466		}
13467		if (isset($this->signature_data['info']['ContactInfo']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['ContactInfo'])) {
13468			$out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid);
13469		}
13470		$out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp);
13471		$out .= ' >>';
13472		$out .= "\n".'endobj';
13473		$this->_out($out);
13474	}
13475
13476	/**
13477	 * Set User's Rights for PDF Reader
13478	 * WARNING: This is experimental and currently do not work.
13479	 * Check the PDF Reference 8.7.1 Transform Methods,
13480	 * Table 8.105 Entries in the UR transform parameters dictionary
13481	 * @param boolean $enable if true enable user's rights on PDF reader
13482	 * @param string $document Names specifying additional document-wide usage rights for the document. The only defined value is "/FullSave", which permits a user to save the document along with modified form and/or annotation data.
13483	 * @param string $annots Names specifying additional annotation-related usage rights for the document. Valid names in PDF 1.5 and later are /Create/Delete/Modify/Copy/Import/Export, which permit the user to perform the named operation on annotations.
13484	 * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
13485	 * @param string $signature Names specifying additional signature-related usage rights for the document. The only defined value is /Modify, which permits a user to apply a digital signature to an existing signature form field or clear a signed signature form field.
13486	 * @param string $ef Names specifying additional usage rights for named embedded files in the document. Valid names are /Create/Delete/Modify/Import, which permit the user to perform the named operation on named embedded files
13487	 Names specifying additional embedded-files-related usage rights for the document.
13488	 * @param string $formex Names specifying additional form-field-related usage rights. The only valid name is BarcodePlaintext, which permits text form field data to be encoded as a plaintext two-dimensional barcode.
13489	 * @public
13490	 * @author Nicola Asuni
13491	 * @since 2.9.000 (2008-03-26)
13492	 */
13493	public function setUserRights(
13494			$enable=true,
13495			$document='/FullSave',
13496			$annots='/Create/Delete/Modify/Copy/Import/Export',
13497			$form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
13498			$signature='/Modify',
13499			$ef='/Create/Delete/Modify/Import',
13500			$formex='') {
13501		$this->ur['enabled'] = $enable;
13502		$this->ur['document'] = $document;
13503		$this->ur['annots'] = $annots;
13504		$this->ur['form'] = $form;
13505		$this->ur['signature'] = $signature;
13506		$this->ur['ef'] = $ef;
13507		$this->ur['formex'] = $formex;
13508		if (!$this->sign) {
13509			$this->setSignature('', '', '', '', 0, array());
13510		}
13511	}
13512
13513	/**
13514	 * Enable document signature (requires the OpenSSL Library).
13515	 * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
13516	 * To create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13517	 * To export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13518	 * To convert pfx certificate to pem: openssl pkcs12 -in tcpdf.pfx -out tcpdf.crt -nodes
13519	 * @param mixed $signing_cert signing certificate (string or filename prefixed with 'file://')
13520	 * @param mixed $private_key private key (string or filename prefixed with 'file://')
13521	 * @param string $private_key_password password
13522	 * @param string $extracerts specifies the name of a file containing a bunch of extra certificates to include in the signature which can for example be used to help the recipient to verify the certificate that you used.
13523	 * @param int $cert_type The access permissions granted for this document. Valid values shall be: 1 = No changes to the document shall be permitted; any change to the document shall invalidate the signature; 2 = Permitted changes shall be filling in forms, instantiating page templates, and signing; other changes shall invalidate the signature; 3 = Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature.
13524	 * @param array $info array of option information: Name, Location, Reason, ContactInfo.
13525	 * @param string $approval Enable approval signature eg. for PDF incremental update
13526	 * @public
13527	 * @author Nicola Asuni
13528	 * @since 4.6.005 (2009-04-24)
13529	 */
13530	public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='') {
13531		// to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13532		// to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13533		// to convert pfx certificate to pem: openssl
13534		//     OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
13535		$this->sign = true;
13536		++$this->n;
13537		$this->sig_obj_id = $this->n; // signature widget
13538		++$this->n; // signature object ($this->sig_obj_id + 1)
13539		$this->signature_data = array();
13540		if (strlen($signing_cert) == 0) {
13541			$this->Error('Please provide a certificate file and password!');
13542		}
13543		if (strlen($private_key) == 0) {
13544			$private_key = $signing_cert;
13545		}
13546		$this->signature_data['signcert'] = $signing_cert;
13547		$this->signature_data['privkey'] = $private_key;
13548		$this->signature_data['password'] = $private_key_password;
13549		$this->signature_data['extracerts'] = $extracerts;
13550		$this->signature_data['cert_type'] = $cert_type;
13551		$this->signature_data['info'] = $info;
13552		$this->signature_data['approval'] = $approval;
13553	}
13554
13555	/**
13556	 * Set the digital signature appearance (a cliccable rectangle area to get signature properties)
13557	 * @param float $x Abscissa of the upper-left corner.
13558	 * @param float $y Ordinate of the upper-left corner.
13559	 * @param float $w Width of the signature area.
13560	 * @param float $h Height of the signature area.
13561	 * @param int $page option page number (if < 0 the current page is used).
13562	 * @param string $name Name of the signature.
13563	 * @public
13564	 * @author Nicola Asuni
13565	 * @since 5.3.011 (2010-06-17)
13566	 */
13567	public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13568		$this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13569	}
13570
13571	/**
13572	 * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties)
13573	 * @param float $x Abscissa of the upper-left corner.
13574	 * @param float $y Ordinate of the upper-left corner.
13575	 * @param float $w Width of the signature area.
13576	 * @param float $h Height of the signature area.
13577	 * @param int $page option page number (if < 0 the current page is used).
13578	 * @param string $name Name of the signature.
13579	 * @public
13580	 * @author Nicola Asuni
13581	 * @since 5.9.101 (2011-07-06)
13582	 */
13583	public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13584		++$this->n;
13585		$this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13586	}
13587
13588	/**
13589	 * Get the array that defines the signature appearance (page and rectangle coordinates).
13590	 * @param float $x Abscissa of the upper-left corner.
13591	 * @param float $y Ordinate of the upper-left corner.
13592	 * @param float $w Width of the signature area.
13593	 * @param float $h Height of the signature area.
13594	 * @param int $page option page number (if < 0 the current page is used).
13595	 * @param string $name Name of the signature.
13596	 * @return array Array defining page and rectangle coordinates of signature appearance.
13597	 * @protected
13598	 * @author Nicola Asuni
13599	 * @since 5.9.101 (2011-07-06)
13600	 */
13601	protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13602		$sigapp = array();
13603		if (($page < 1) OR ($page > $this->numpages)) {
13604			$sigapp['page'] = $this->page;
13605		} else {
13606			$sigapp['page'] = intval($page);
13607		}
13608		if (empty($name)) {
13609			$sigapp['name'] = 'Signature';
13610		} else {
13611			$sigapp['name'] = $name;
13612		}
13613		$a = $x * $this->k;
13614		$b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k);
13615		$c = $w * $this->k;
13616		$d = $h * $this->k;
13617		$sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d));
13618		return $sigapp;
13619	}
13620
13621	/**
13622	 * Enable document timestamping (requires the OpenSSL Library).
13623	 * The trusted timestamping improve document security that means that no one should be able to change the document once it has been recorded.
13624	 * Use with digital signature only!
13625	 * @param string $tsa_host Time Stamping Authority (TSA) server (prefixed with 'https://')
13626	 * @param string $tsa_username Specifies the username for TSA authorization (optional) OR specifies the TSA authorization PEM file (see: example_66.php, optional)
13627	 * @param string $tsa_password Specifies the password for TSA authorization (optional)
13628	 * @param string $tsa_cert Specifies the location of TSA certificate for authorization (optional for cURL)
13629	 * @public
13630	 * @author Richard Stockinger
13631	 * @since 6.0.090 (2014-06-16)
13632	 */
13633	public function setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='') {
13634		$this->tsa_data = array();
13635		if (!function_exists('curl_init')) {
13636			$this->Error('Please enable cURL PHP extension!');
13637		}
13638		if (strlen($tsa_host) == 0) {
13639			$this->Error('Please specify the host of Time Stamping Authority (TSA)!');
13640		}
13641		$this->tsa_data['tsa_host'] = $tsa_host;
13642		if (is_file($tsa_username)) {
13643			$this->tsa_data['tsa_auth'] = $tsa_username;
13644		} else {
13645			$this->tsa_data['tsa_username'] = $tsa_username;
13646		}
13647		$this->tsa_data['tsa_password'] = $tsa_password;
13648		$this->tsa_data['tsa_cert'] = $tsa_cert;
13649		$this->tsa_timestamp = true;
13650	}
13651
13652	/**
13653	 * NOT YET IMPLEMENTED
13654	 * Request TSA for a timestamp
13655	 * @param string $signature Digital signature as binary string
13656	 * @return string Timestamped digital signature
13657	 * @protected
13658	 * @author Richard Stockinger
13659	 * @since 6.0.090 (2014-06-16)
13660	 */
13661	protected function applyTSA($signature) {
13662		if (!$this->tsa_timestamp) {
13663			return $signature;
13664		}
13665		//@TODO: implement this feature
13666		return $signature;
13667	}
13668
13669	/**
13670	 * Create a new page group.
13671	 * NOTE: call this function before calling AddPage()
13672	 * @param int|null $page starting group page (leave empty for next page).
13673	 * @public
13674	 * @since 3.0.000 (2008-03-27)
13675	 */
13676	public function startPageGroup($page=null) {
13677		if (empty($page)) {
13678			$page = $this->page + 1;
13679		}
13680		$this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1;
13681	}
13682
13683	/**
13684	 * Set the starting page number.
13685	 * @param int $num Starting page number.
13686	 * @since 5.9.093 (2011-06-16)
13687	 * @public
13688	 */
13689	public function setStartingPageNumber($num=1) {
13690		$this->starting_page_number = max(0, intval($num));
13691	}
13692
13693	/**
13694	 * Returns the string alias used right align page numbers.
13695	 * If the current font is unicode type, the returned string wil contain an additional open curly brace.
13696	 * @return string
13697	 * @since 5.9.099 (2011-06-27)
13698	 * @public
13699	 */
13700	public function getAliasRightShift() {
13701		// calculate aproximatively the ratio between widths of aliases and replacements.
13702		$ref = '{'.TCPDF_STATIC::$alias_right_shift.'}{'.TCPDF_STATIC::$alias_tot_pages.'}{'.TCPDF_STATIC::$alias_num_page.'}';
13703		$rep = str_repeat(' ', $this->GetNumChars($ref));
13704		$wrep = $this->GetStringWidth($rep);
13705		if ($wrep > 0) {
13706			$wdiff = max(1, ($this->GetStringWidth($ref) / $wrep));
13707		} else {
13708			$wdiff = 1;
13709		}
13710		$sdiff = sprintf('%F', $wdiff);
13711		$alias = TCPDF_STATIC::$alias_right_shift.$sdiff.'}';
13712		if ($this->isUnicodeFont()) {
13713			$alias = '{'.$alias;
13714		}
13715		return $alias;
13716	}
13717
13718	/**
13719	 * Returns the string alias used for the total number of pages.
13720	 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13721	 * This alias will be replaced by the total number of pages in the document.
13722	 * @return string
13723	 * @since 4.0.018 (2008-08-08)
13724	 * @public
13725	 */
13726	public function getAliasNbPages() {
13727		if ($this->isUnicodeFont()) {
13728			return '{'.TCPDF_STATIC::$alias_tot_pages.'}';
13729		}
13730		return TCPDF_STATIC::$alias_tot_pages;
13731	}
13732
13733	/**
13734	 * Returns the string alias used for the page number.
13735	 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13736	 * This alias will be replaced by the page number.
13737	 * @return string
13738	 * @since 4.5.000 (2009-01-02)
13739	 * @public
13740	 */
13741	public function getAliasNumPage() {
13742		if ($this->isUnicodeFont()) {
13743			return '{'.TCPDF_STATIC::$alias_num_page.'}';
13744		}
13745		return TCPDF_STATIC::$alias_num_page;
13746	}
13747
13748	/**
13749	 * Return the alias for the total number of pages in the current page group.
13750	 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13751	 * This alias will be replaced by the total number of pages in this group.
13752	 * @return string alias of the current page group
13753	 * @public
13754	 * @since 3.0.000 (2008-03-27)
13755	 */
13756	public function getPageGroupAlias() {
13757		if ($this->isUnicodeFont()) {
13758			return '{'.TCPDF_STATIC::$alias_group_tot_pages.'}';
13759		}
13760		return TCPDF_STATIC::$alias_group_tot_pages;
13761	}
13762
13763	/**
13764	 * Return the alias for the page number on the current page group.
13765	 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13766	 * This alias will be replaced by the page number (relative to the belonging group).
13767	 * @return string alias of the current page group
13768	 * @public
13769	 * @since 4.5.000 (2009-01-02)
13770	 */
13771	public function getPageNumGroupAlias() {
13772		if ($this->isUnicodeFont()) {
13773			return '{'.TCPDF_STATIC::$alias_group_num_page.'}';
13774		}
13775		return TCPDF_STATIC::$alias_group_num_page;
13776	}
13777
13778	/**
13779	 * Return the current page in the group.
13780	 * @return int current page in the group
13781	 * @public
13782	 * @since 3.0.000 (2008-03-27)
13783	 */
13784	public function getGroupPageNo() {
13785		return $this->pagegroups[$this->currpagegroup];
13786	}
13787
13788	/**
13789	 * Returns the current group page number formatted as a string.
13790	 * @public
13791	 * @since 4.3.003 (2008-11-18)
13792	 * @see PaneNo(), formatPageNumber()
13793	 */
13794	public function getGroupPageNoFormatted() {
13795		return TCPDF_STATIC::formatPageNumber($this->getGroupPageNo());
13796	}
13797
13798	/**
13799	 * Returns the current page number formatted as a string.
13800	 * @public
13801	 * @since 4.2.005 (2008-11-06)
13802	 * @see PaneNo(), formatPageNumber()
13803	 */
13804	public function PageNoFormatted() {
13805		return TCPDF_STATIC::formatPageNumber($this->PageNo());
13806	}
13807
13808	/**
13809	 * Put pdf layers.
13810	 * @protected
13811	 * @since 3.0.000 (2008-03-27)
13812	 */
13813	protected function _putocg() {
13814		if (empty($this->pdflayers)) {
13815			return;
13816		}
13817		foreach ($this->pdflayers as $key => $layer) {
13818			 $this->pdflayers[$key]['objid'] = $this->_newobj();
13819			 $out = '<< /Type /OCG';
13820			 $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']);
13821			 $out .= ' /Usage <<';
13822			 if (isset($layer['print']) AND ($layer['print'] !== NULL)) {
13823				$out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>';
13824			 }
13825			 $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>';
13826			 $out .= ' >> >>';
13827			 $out .= "\n".'endobj';
13828			 $this->_out($out);
13829		}
13830	}
13831
13832	/**
13833	 * Start a new pdf layer.
13834	 * @param string $name Layer name (only a-z letters and numbers). Leave empty for automatic name.
13835	 * @param boolean|null $print Set to TRUE to print this layer, FALSE to not print and NULL to not set this option
13836	 * @param boolean $view Set to true to view this layer.
13837	 * @param boolean $lock If true lock the layer
13838	 * @public
13839	 * @since 5.9.102 (2011-07-13)
13840	 */
13841	public function startLayer($name='', $print=true, $view=true, $lock=true) {
13842		if ($this->state != 2) {
13843			return;
13844		}
13845		$layer = sprintf('LYR%03d', (count($this->pdflayers) + 1));
13846		if (empty($name)) {
13847			$name = $layer;
13848		} else {
13849			$name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
13850		}
13851		$this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view, 'lock' => $lock);
13852		$this->openMarkedContent = true;
13853		$this->_out('/OC /'.$layer.' BDC');
13854	}
13855
13856	/**
13857	 * End the current PDF layer.
13858	 * @public
13859	 * @since 5.9.102 (2011-07-13)
13860	 */
13861	public function endLayer() {
13862		if ($this->state != 2) {
13863			return;
13864		}
13865		if ($this->openMarkedContent) {
13866			// close existing open marked-content layer
13867			$this->_out('EMC');
13868			$this->openMarkedContent = false;
13869		}
13870	}
13871
13872	/**
13873	 * Set the visibility of the successive elements.
13874	 * This can be useful, for instance, to put a background
13875	 * image or color that will show on screen but won't print.
13876	 * @param string $v visibility mode. Legal values are: all, print, screen or view.
13877	 * @public
13878	 * @since 3.0.000 (2008-03-27)
13879	 */
13880	public function setVisibility($v) {
13881		if ($this->state != 2) {
13882			return;
13883		}
13884		$this->endLayer();
13885		switch($v) {
13886			case 'print': {
13887				$this->startLayer('Print', true, false);
13888				break;
13889			}
13890			case 'view':
13891			case 'screen': {
13892				$this->startLayer('View', false, true);
13893				break;
13894			}
13895			case 'all': {
13896				$this->_out('');
13897				break;
13898			}
13899			default: {
13900				$this->Error('Incorrect visibility: '.$v);
13901				break;
13902			}
13903		}
13904	}
13905
13906	/**
13907	 * Add transparency parameters to the current extgstate
13908	 * @param array $parms parameters
13909	 * @return int|void the number of extgstates
13910	 * @protected
13911	 * @since 3.0.000 (2008-03-27)
13912	 */
13913	protected function addExtGState($parms) {
13914		if ($this->pdfa_mode || $this->pdfa_version >= 2) {
13915			// transparencies are not allowed in PDF/A mode
13916			return;
13917		}
13918		// check if this ExtGState already exist
13919		foreach ($this->extgstates as $i => $ext) {
13920			if ($ext['parms'] == $parms) {
13921				if ($this->inxobj) {
13922					// we are inside an XObject template
13923					$this->xobjects[$this->xobjid]['extgstates'][$i] = $ext;
13924				}
13925				// return reference to existing ExtGState
13926				return $i;
13927			}
13928		}
13929		$n = (count($this->extgstates) + 1);
13930		$this->extgstates[$n] = array('parms' => $parms);
13931		if ($this->inxobj) {
13932			// we are inside an XObject template
13933			$this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n];
13934		}
13935		return $n;
13936	}
13937
13938	/**
13939	 * Add an extgstate
13940	 * @param int $gs extgstate
13941	 * @protected
13942	 * @since 3.0.000 (2008-03-27)
13943	 */
13944	protected function setExtGState($gs) {
13945		if (($this->pdfa_mode && $this->pdfa_version < 2) OR ($this->state != 2)) {
13946			// transparency is not allowed in PDF/A-1 mode
13947			return;
13948		}
13949		$this->_out(sprintf('/GS%d gs', $gs));
13950	}
13951
13952	/**
13953	 * Put extgstates for object transparency
13954	 * @protected
13955	 * @since 3.0.000 (2008-03-27)
13956	 */
13957	protected function _putextgstates() {
13958		foreach ($this->extgstates as $i => $ext) {
13959			$this->extgstates[$i]['n'] = $this->_newobj();
13960			$out = '<< /Type /ExtGState';
13961			foreach ($ext['parms'] as $k => $v) {
13962				if (is_float($v)) {
13963					$v = sprintf('%F', $v);
13964				} elseif ($v === true) {
13965					$v = 'true';
13966				} elseif ($v === false) {
13967					$v = 'false';
13968				}
13969				$out .= ' /'.$k.' '.$v;
13970			}
13971			$out .= ' >>';
13972			$out .= "\n".'endobj';
13973			$this->_out($out);
13974		}
13975	}
13976
13977	/**
13978	 * Set overprint mode for stroking (OP) and non-stroking (op) painting operations.
13979	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
13980	 * @param boolean $stroking If true apply overprint for stroking operations.
13981	 * @param boolean|null $nonstroking If true apply overprint for painting operations other than stroking.
13982	 * @param integer $mode Overprint mode: (0 = each source colour component value replaces the value previously painted for the corresponding device colorant; 1 = a tint value of 0.0 for a source colour component shall leave the corresponding component of the previously painted colour unchanged).
13983	 * @public
13984	 * @since 5.9.152 (2012-03-23)
13985	 */
13986	public function setOverprint($stroking=true, $nonstroking=null, $mode=0) {
13987		if ($this->state != 2) {
13988			return;
13989		}
13990		$stroking = $stroking ? true : false;
13991		if (TCPDF_STATIC::empty_string($nonstroking)) {
13992			// default value if not set
13993			$nonstroking = $stroking;
13994		} else {
13995			$nonstroking = $nonstroking ? true : false;
13996		}
13997		if (($mode != 0) AND ($mode != 1)) {
13998			$mode = 0;
13999		}
14000		$this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode);
14001		$gs = $this->addExtGState($this->overprint);
14002		$this->setExtGState($gs);
14003	}
14004
14005	/**
14006	 * Get the overprint mode array (OP, op, OPM).
14007	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
14008	 * @return array<string,bool|int>
14009	 * @public
14010	 * @since 5.9.152 (2012-03-23)
14011	 */
14012	public function getOverprint() {
14013		return $this->overprint;
14014	}
14015
14016	/**
14017	 * Set alpha for stroking (CA) and non-stroking (ca) operations.
14018	 * @param float $stroking Alpha value for stroking operations: real value from 0 (transparent) to 1 (opaque).
14019	 * @param string $bm blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity
14020	 * @param float|null $nonstroking Alpha value for non-stroking operations: real value from 0 (transparent) to 1 (opaque).
14021	 * @param boolean $ais
14022	 * @public
14023	 * @since 3.0.000 (2008-03-27)
14024	 */
14025	public function setAlpha($stroking=1, $bm='Normal', $nonstroking=null, $ais=false) {
14026		if ($this->pdfa_mode && $this->pdfa_version < 2) {
14027			// transparency is not allowed in PDF/A-1 mode
14028			return;
14029		}
14030		$stroking = floatval($stroking);
14031		if (TCPDF_STATIC::empty_string($nonstroking)) {
14032			// default value if not set
14033			$nonstroking = $stroking;
14034		} else {
14035			$nonstroking = floatval($nonstroking);
14036		}
14037		if ($bm[0] == '/') {
14038			// remove trailing slash
14039			$bm = substr($bm, 1);
14040		}
14041		if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
14042			$bm = 'Normal';
14043		}
14044		$ais = $ais ? true : false;
14045		$this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais);
14046		$gs = $this->addExtGState($this->alpha);
14047		$this->setExtGState($gs);
14048	}
14049
14050	/**
14051	 * Get the alpha mode array (CA, ca, BM, AIS).
14052	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
14053	 * @return array<string,bool|string>
14054	 * @public
14055	 * @since 5.9.152 (2012-03-23)
14056	 */
14057	public function getAlpha() {
14058		return $this->alpha;
14059	}
14060
14061	/**
14062	 * Set the default JPEG compression quality (1-100)
14063	 * @param int $quality JPEG quality, integer between 1 and 100
14064	 * @public
14065	 * @since 3.0.000 (2008-03-27)
14066	 */
14067	public function setJPEGQuality($quality) {
14068		if (($quality < 1) OR ($quality > 100)) {
14069			$quality = 75;
14070		}
14071		$this->jpeg_quality = intval($quality);
14072	}
14073
14074	/**
14075	 * Set the default number of columns in a row for HTML tables.
14076	 * @param int $cols number of columns
14077	 * @public
14078	 * @since 3.0.014 (2008-06-04)
14079	 */
14080	public function setDefaultTableColumns($cols=4) {
14081		$this->default_table_columns = intval($cols);
14082	}
14083
14084	/**
14085	 * Set the height of the cell (line height) respect the font height.
14086	 * @param float $h cell proportion respect font height (typical value = 1.25).
14087	 * @public
14088	 * @since 3.0.014 (2008-06-04)
14089	 */
14090	public function setCellHeightRatio($h) {
14091		$this->cell_height_ratio = $h;
14092	}
14093
14094	/**
14095	 * return the height of cell repect font height.
14096	 * @public
14097	 * @return float
14098	 * @since 4.0.012 (2008-07-24)
14099	 */
14100	public function getCellHeightRatio() {
14101		return $this->cell_height_ratio;
14102	}
14103
14104	/**
14105	 * Set the PDF version (check PDF reference for valid values).
14106	 * @param string $version PDF document version.
14107	 * @public
14108	 * @since 3.1.000 (2008-06-09)
14109	 */
14110	public function setPDFVersion($version='1.7') {
14111		if ($this->pdfa_mode && $this->pdfa_version == 1 ) {
14112			// PDF/A-1 mode
14113			$this->PDFVersion = '1.4';
14114		} elseif ($this->pdfa_mode && $this->pdfa_version >= 2 ) {
14115            // PDF/A-2 mode
14116            $this->PDFVersion = '1.7';
14117        } else {
14118			$this->PDFVersion = $version;
14119		}
14120	}
14121
14122	/**
14123	 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
14124	 * (see Section 8.1 of PDF reference, "Viewer Preferences").
14125	 * <ul><li>HideToolbar boolean (Optional) A flag specifying whether to hide the viewer application's tool bars when the document is active. Default value: false.</li><li>HideMenubar boolean (Optional) A flag specifying whether to hide the viewer application's menu bar when the document is active. Default value: false.</li><li>HideWindowUI boolean (Optional) A flag specifying whether to hide user interface elements in the document's window (such as scroll bars and navigation controls), leaving only the document's contents displayed. Default value: false.</li><li>FitWindow boolean (Optional) A flag specifying whether to resize the document's window to fit the size of the first displayed page. Default value: false.</li><li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li><li>DisplayDocTitle boolean (Optional; PDF 1.4) A flag specifying whether the window's title bar should display the document title taken from the Title entry of the document information dictionary (see Section 10.2.1, "Document Information Dictionary"). If false, the title bar should instead display the name of the PDF file containing the document. Default value: false.</li><li>NonFullScreenPageMode name (Optional) The document's page mode, specifying how to display the document on exiting full-screen mode:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>UseOC Optional content group panel visible</li></ul>This entry is meaningful only if the value of the PageMode entry in the catalog dictionary (see Section 3.6.1, "Document Catalog") is FullScreen; it is ignored otherwise. Default value: UseNone.</li><li>ViewArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be displayed when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>ViewClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be rendered when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintScaling name (Optional; PDF 1.6) The page scaling option to be selected when a print dialog is displayed for this document. Valid values are: <ul><li>None, which indicates that the print dialog should reflect no page scaling</li><li>AppDefault (default), which indicates that applications should use the current print scaling</li></ul></li><li>Duplex name (Optional; PDF 1.7) The paper handling option to use when printing the file from the print dialog. The following values are valid:<ul><li>Simplex - Print single-sided</li><li>DuplexFlipShortEdge - Duplex and flip on the short edge of the sheet</li><li>DuplexFlipLongEdge - Duplex and flip on the long edge of the sheet</li></ul>Default value: none</li><li>PickTrayByPDFSize boolean (Optional; PDF 1.7) A flag specifying whether the PDF page size is used to select the input paper tray. This setting influences only the preset values used to populate the print dialog presented by a PDF viewer application. If PickTrayByPDFSize is true, the check box in the print dialog associated with input paper tray is checked. Note: This setting has no effect on Mac OS systems, which do not provide the ability to pick the input tray by size.</li><li>PrintPageRange array (Optional; PDF 1.7) The page numbers used to initialize the print dialog box when the file is printed. The first page of the PDF file is denoted by 1. Each pair consists of the first and last pages in the sub-range. An odd number of integers causes this entry to be ignored. Negative numbers cause the entire array to be ignored. Default value: as defined by PDF viewer application</li><li>NumCopies integer (Optional; PDF 1.7) The number of copies to be printed when the print dialog is opened for this file. Supported values are the integers 2 through 5. Values outside this range are ignored. Default value: as defined by PDF viewer application, but typically 1</li></ul>
14126	 * @param array $preferences array of options.
14127	 * @author Nicola Asuni
14128	 * @public
14129	 * @since 3.1.000 (2008-06-09)
14130	 */
14131	public function setViewerPreferences($preferences) {
14132		$this->viewer_preferences = $preferences;
14133	}
14134
14135	/**
14136	 * Paints color transition registration bars
14137	 * @param float $x abscissa of the top left corner of the rectangle.
14138	 * @param float $y ordinate of the top left corner of the rectangle.
14139	 * @param float $w width of the rectangle.
14140	 * @param float $h height of the rectangle.
14141	 * @param boolean $transition if true prints tcolor transitions to white.
14142	 * @param boolean $vertical if true prints bar vertically.
14143	 * @param string $colors colors to print separated by comma. Valid values are: A,W,R,G,B,C,M,Y,K,RGB,CMYK,ALL,ALLSPOT,<SPOT_COLOR_NAME>. Where: A = grayscale black, W = grayscale white, R = RGB red, G RGB green, B RGB blue, C = CMYK cyan, M = CMYK magenta, Y = CMYK yellow, K = CMYK key/black, RGB = RGB registration color, CMYK = CMYK registration color, ALL = Spot registration color, ALLSPOT = print all defined spot colors, <SPOT_COLOR_NAME> = name of the spot color to print.
14144	 * @author Nicola Asuni
14145	 * @since 4.9.000 (2010-03-26)
14146	 * @public
14147	 */
14148	public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
14149		if (strpos($colors, 'ALLSPOT') !== false) {
14150			// expand spot colors
14151			$spot_colors = '';
14152			foreach ($this->spot_colors as $spot_color_name => $v) {
14153				$spot_colors .= ','.$spot_color_name;
14154			}
14155			if (!empty($spot_colors)) {
14156				$spot_colors = substr($spot_colors, 1);
14157				$colors = str_replace('ALLSPOT', $spot_colors, $colors);
14158			} else {
14159				$colors = str_replace('ALLSPOT', 'NONE', $colors);
14160			}
14161		}
14162		$bars = explode(',', $colors);
14163		$numbars = count($bars); // number of bars to print
14164		if ($numbars <= 0) {
14165			return;
14166		}
14167		// set bar measures
14168		if ($vertical) {
14169			$coords = array(0, 0, 0, 1);
14170			$wb = $w / $numbars; // bar width
14171			$hb = $h; // bar height
14172			$xd = $wb; // delta x
14173			$yd = 0; // delta y
14174		} else {
14175			$coords = array(1, 0, 0, 0);
14176			$wb = $w; // bar width
14177			$hb = $h / $numbars; // bar height
14178			$xd = 0; // delta x
14179			$yd = $hb; // delta y
14180		}
14181		$xb = $x;
14182		$yb = $y;
14183		foreach ($bars as $col) {
14184			switch ($col) {
14185				// set transition colors
14186				case 'A': { // BLACK (GRAYSCALE)
14187					$col_a = array(255);
14188					$col_b = array(0);
14189					break;
14190				}
14191				case 'W': { // WHITE (GRAYSCALE)
14192					$col_a = array(0);
14193					$col_b = array(255);
14194					break;
14195				}
14196				case 'R': { // RED (RGB)
14197					$col_a = array(255,255,255);
14198					$col_b = array(255,0,0);
14199					break;
14200				}
14201				case 'G': { // GREEN (RGB)
14202					$col_a = array(255,255,255);
14203					$col_b = array(0,255,0);
14204					break;
14205				}
14206				case 'B': { // BLUE (RGB)
14207					$col_a = array(255,255,255);
14208					$col_b = array(0,0,255);
14209					break;
14210				}
14211				case 'C': { // CYAN (CMYK)
14212					$col_a = array(0,0,0,0);
14213					$col_b = array(100,0,0,0);
14214					break;
14215				}
14216				case 'M': { // MAGENTA (CMYK)
14217					$col_a = array(0,0,0,0);
14218					$col_b = array(0,100,0,0);
14219					break;
14220				}
14221				case 'Y': { // YELLOW (CMYK)
14222					$col_a = array(0,0,0,0);
14223					$col_b = array(0,0,100,0);
14224					break;
14225				}
14226				case 'K': { // KEY - BLACK (CMYK)
14227					$col_a = array(0,0,0,0);
14228					$col_b = array(0,0,0,100);
14229					break;
14230				}
14231				case 'RGB': { // BLACK REGISTRATION (RGB)
14232					$col_a = array(255,255,255);
14233					$col_b = array(0,0,0);
14234					break;
14235				}
14236				case 'CMYK': { // BLACK REGISTRATION (CMYK)
14237					$col_a = array(0,0,0,0);
14238					$col_b = array(100,100,100,100);
14239					break;
14240				}
14241				case 'ALL': { // SPOT COLOR REGISTRATION
14242					$col_a = array(0,0,0,0,'None');
14243					$col_b = array(100,100,100,100,'All');
14244					break;
14245				}
14246				case 'NONE': { // SKIP THIS COLOR
14247					$col_a = array(0,0,0,0,'None');
14248					$col_b = array(0,0,0,0,'None');
14249					break;
14250				}
14251				default: { // SPECIFIC SPOT COLOR NAME
14252					$col_a = array(0,0,0,0,'None');
14253					$col_b = TCPDF_COLORS::getSpotColor($col, $this->spot_colors);
14254					if ($col_b === false) {
14255						// in case of error defaults to the registration color
14256						$col_b = array(100,100,100,100,'All');
14257					}
14258					break;
14259				}
14260			}
14261			if ($col != 'NONE') {
14262				if ($transition) {
14263					// color gradient
14264					$this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
14265				} else {
14266					$this->setFillColorArray($col_b);
14267					// colored rectangle
14268					$this->Rect($xb, $yb, $wb, $hb, 'F', array());
14269				}
14270				$xb += $xd;
14271				$yb += $yd;
14272			}
14273		}
14274	}
14275
14276	/**
14277	 * Paints crop marks.
14278	 * @param float $x abscissa of the crop mark center.
14279	 * @param float $y ordinate of the crop mark center.
14280	 * @param float $w width of the crop mark.
14281	 * @param float $h height of the crop mark.
14282	 * @param string $type type of crop mark, one symbol per type separated by comma: T = TOP, F = BOTTOM, L = LEFT, R = RIGHT, TL = A = TOP-LEFT, TR = B = TOP-RIGHT, BL = C = BOTTOM-LEFT, BR = D = BOTTOM-RIGHT.
14283	 * @param array $color crop mark color (default spot registration color).
14284	 * @author Nicola Asuni
14285	 * @since 4.9.000 (2010-03-26)
14286	 * @public
14287	 */
14288	public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(100,100,100,100,'All')) {
14289		$this->setLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
14290		$type = strtoupper($type);
14291		$type = preg_replace('/[^A-Z\-\,]*/', '', $type);
14292		// split type in single components
14293		$type = str_replace('-', ',', $type);
14294		$type = str_replace('TL', 'T,L', $type);
14295		$type = str_replace('TR', 'T,R', $type);
14296		$type = str_replace('BL', 'F,L', $type);
14297		$type = str_replace('BR', 'F,R', $type);
14298		$type = str_replace('A', 'T,L', $type);
14299		$type = str_replace('B', 'T,R', $type);
14300		$type = str_replace('T,RO', 'BO', $type);
14301		$type = str_replace('C', 'F,L', $type);
14302		$type = str_replace('D', 'F,R', $type);
14303		$crops = explode(',', strtoupper($type));
14304		// remove duplicates
14305		$crops = array_unique($crops);
14306		$dw = ($w / 4); // horizontal space to leave before the intersection point
14307		$dh = ($h / 4); // vertical space to leave before the intersection point
14308		foreach ($crops as $crop) {
14309			switch ($crop) {
14310				case 'T':
14311				case 'TOP': {
14312					$x1 = $x;
14313					$y1 = ($y - $h);
14314					$x2 = $x;
14315					$y2 = ($y - $dh);
14316					break;
14317				}
14318				case 'F':
14319				case 'BOTTOM': {
14320					$x1 = $x;
14321					$y1 = ($y + $dh);
14322					$x2 = $x;
14323					$y2 = ($y + $h);
14324					break;
14325				}
14326				case 'L':
14327				case 'LEFT': {
14328					$x1 = ($x - $w);
14329					$y1 = $y;
14330					$x2 = ($x - $dw);
14331					$y2 = $y;
14332					break;
14333				}
14334				case 'R':
14335				case 'RIGHT': {
14336					$x1 = ($x + $dw);
14337					$y1 = $y;
14338					$x2 = ($x + $w);
14339					$y2 = $y;
14340					break;
14341				}
14342			}
14343			$this->Line($x1, $y1, $x2, $y2);
14344		}
14345	}
14346
14347	/**
14348	 * Paints a registration mark
14349	 * @param float $x abscissa of the registration mark center.
14350	 * @param float $y ordinate of the registration mark center.
14351	 * @param float $r radius of the crop mark.
14352	 * @param boolean $double if true print two concentric crop marks.
14353	 * @param array $cola crop mark color (default spot registration color 'All').
14354	 * @param array $colb second crop mark color (default spot registration color 'None').
14355	 * @author Nicola Asuni
14356	 * @since 4.9.000 (2010-03-26)
14357	 * @public
14358	 */
14359	public function registrationMark($x, $y, $r, $double=false, $cola=array(100,100,100,100,'All'), $colb=array(0,0,0,0,'None')) {
14360		$line_style = array('width' => max((0.5 / $this->k),($r / 30)), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
14361		$this->setFillColorArray($cola);
14362		$this->PieSector($x, $y, $r, 90, 180, 'F');
14363		$this->PieSector($x, $y, $r, 270, 360, 'F');
14364		$this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14365		if ($double) {
14366			$ri = $r * 0.5;
14367			$this->setFillColorArray($colb);
14368			$this->PieSector($x, $y, $ri, 90, 180, 'F');
14369			$this->PieSector($x, $y, $ri, 270, 360, 'F');
14370			$this->setFillColorArray($cola);
14371			$this->PieSector($x, $y, $ri, 0, 90, 'F');
14372			$this->PieSector($x, $y, $ri, 180, 270, 'F');
14373			$this->Circle($x, $y, $ri, 0, 360, 'C', $line_style, array(), 8);
14374		}
14375	}
14376
14377	/**
14378	 * Paints a CMYK registration mark
14379	 * @param float $x abscissa of the registration mark center.
14380	 * @param float $y ordinate of the registration mark center.
14381	 * @param float $r radius of the crop mark.
14382	 * @author Nicola Asuni
14383	 * @since 6.0.038 (2013-09-30)
14384	 * @public
14385	 */
14386	public function registrationMarkCMYK($x, $y, $r) {
14387		// line width
14388		$lw = max((0.5 / $this->k),($r / 8));
14389		// internal radius
14390		$ri = ($r * 0.6);
14391		// external radius
14392		$re = ($r * 1.3);
14393		// Cyan
14394		$this->setFillColorArray(array(100,0,0,0));
14395		$this->PieSector($x, $y, $ri, 270, 360, 'F');
14396		// Magenta
14397		$this->setFillColorArray(array(0,100,0,0));
14398		$this->PieSector($x, $y, $ri, 0, 90, 'F');
14399		// Yellow
14400		$this->setFillColorArray(array(0,0,100,0));
14401		$this->PieSector($x, $y, $ri, 90, 180, 'F');
14402		// Key - black
14403		$this->setFillColorArray(array(0,0,0,100));
14404		$this->PieSector($x, $y, $ri, 180, 270, 'F');
14405		// registration color
14406		$line_style = array('width' => $lw, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(100,100,100,100,'All'));
14407		$this->setFillColorArray(array(100,100,100,100,'All'));
14408		// external circle
14409		$this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14410		// cross lines
14411		$this->Line($x, ($y - $re), $x, ($y - $ri));
14412		$this->Line($x, ($y + $ri), $x, ($y + $re));
14413		$this->Line(($x - $re), $y, ($x - $ri), $y);
14414		$this->Line(($x + $ri), $y, ($x + $re), $y);
14415	}
14416
14417	/**
14418	 * Paints a linear colour gradient.
14419	 * @param float $x abscissa of the top left corner of the rectangle.
14420	 * @param float $y ordinate of the top left corner of the rectangle.
14421	 * @param float $w width of the rectangle.
14422	 * @param float $h height of the rectangle.
14423	 * @param array $col1 first color (Grayscale, RGB or CMYK components).
14424	 * @param array $col2 second color (Grayscale, RGB or CMYK components).
14425	 * @param array $coords array of the form (x1, y1, x2, y2) which defines the gradient vector (see linear_gradient_coords.jpg). The default value is from left to right (x1=0, y1=0, x2=1, y2=0).
14426	 * @author Andreas W\FCrmser, Nicola Asuni
14427	 * @since 3.1.000 (2008-06-09)
14428	 * @public
14429	 */
14430	public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
14431		$this->Clip($x, $y, $w, $h);
14432		$this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14433	}
14434
14435	/**
14436	 * Paints a radial colour gradient.
14437	 * @param float $x abscissa of the top left corner of the rectangle.
14438	 * @param float $y ordinate of the top left corner of the rectangle.
14439	 * @param float $w width of the rectangle.
14440	 * @param float $h height of the rectangle.
14441	 * @param array $col1 first color (Grayscale, RGB or CMYK components).
14442	 * @param array $col2 second color (Grayscale, RGB or CMYK components).
14443	 * @param array $coords array of the form (fx, fy, cx, cy, r) where (fx, fy) is the starting point of the gradient with color1, (cx, cy) is the center of the circle with color2, and r is the radius of the circle (see radial_gradient_coords.jpg). (fx, fy) should be inside the circle, otherwise some areas will not be defined.
14444	 * @author Andreas W\FCrmser, Nicola Asuni
14445	 * @since 3.1.000 (2008-06-09)
14446	 * @public
14447	 */
14448	public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
14449		$this->Clip($x, $y, $w, $h);
14450		$this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14451	}
14452
14453	/**
14454	 * Paints a coons patch mesh.
14455	 * @param float $x abscissa of the top left corner of the rectangle.
14456	 * @param float $y ordinate of the top left corner of the rectangle.
14457	 * @param float $w width of the rectangle.
14458	 * @param float $h height of the rectangle.
14459	 * @param array $col1 first color (lower left corner) (RGB components).
14460	 * @param array $col2 second color (lower right corner) (RGB components).
14461	 * @param array $col3 third color (upper right corner) (RGB components).
14462	 * @param array $col4 fourth color (upper left corner) (RGB components).
14463	 * @param array $coords <ul><li>for one patch mesh: array(float x1, float y1, .... float x12, float y12): 12 pairs of coordinates (normally from 0 to 1) which specify the Bezier control points that define the patch. First pair is the lower left edge point, next is its right control point (control point 2). Then the other points are defined in the order: control point 1, edge point, control point 2 going counter-clockwise around the patch. Last (x12, y12) is the first edge point's left control point (control point 1).</li><li>for two or more patch meshes: array[number of patches]: arrays with the following keys for each patch: f: where to put that patch (0 = first patch, 1, 2, 3 = right, top and left of precedent patch - I didn't figure this out completely - just try and error ;-) points: 12 pairs of coordinates of the Bezier control points as above for the first patch, 8 pairs of coordinates for the following patches, ignoring the coordinates already defined by the precedent patch (I also didn't figure out the order of these - also: try and see what's happening) colors: must be 4 colors for the first patch, 2 colors for the following patches</li></ul>
14464	 * @param array $coords_min minimum value used by the coordinates. If a coordinate's value is smaller than this it will be cut to coords_min. default: 0
14465	 * @param array $coords_max maximum value used by the coordinates. If a coordinate's value is greater than this it will be cut to coords_max. default: 1
14466	 * @param boolean $antialias A flag indicating whether to filter the shading function to prevent aliasing artifacts.
14467	 * @author Andreas W\FCrmser, Nicola Asuni
14468	 * @since 3.1.000 (2008-06-09)
14469	 * @public
14470	 */
14471	public function CoonsPatchMesh($x, $y, $w, $h, $col1=array(), $col2=array(), $col3=array(), $col4=array(), $coords=array(0.00,0.0,0.33,0.00,0.67,0.00,1.00,0.00,1.00,0.33,1.00,0.67,1.00,1.00,0.67,1.00,0.33,1.00,0.00,1.00,0.00,0.67,0.00,0.33), $coords_min=0, $coords_max=1, $antialias=false) {
14472		if (($this->pdfa_mode && $this->pdfa_version < 2) OR ($this->state != 2)) {
14473			return;
14474		}
14475		$this->Clip($x, $y, $w, $h);
14476		$n = count($this->gradients) + 1;
14477		$this->gradients[$n] = array();
14478		$this->gradients[$n]['type'] = 6; //coons patch mesh
14479		$this->gradients[$n]['coords'] = array();
14480		$this->gradients[$n]['antialias'] = $antialias;
14481		$this->gradients[$n]['colors'] = array();
14482		$this->gradients[$n]['transparency'] = false;
14483		//check the coords array if it is the simple array or the multi patch array
14484		if (!isset($coords[0]['f'])) {
14485			//simple array -> convert to multi patch array
14486			if (!isset($col1[1])) {
14487				$col1[1] = $col1[2] = $col1[0];
14488			}
14489			if (!isset($col2[1])) {
14490				$col2[1] = $col2[2] = $col2[0];
14491			}
14492			if (!isset($col3[1])) {
14493				$col3[1] = $col3[2] = $col3[0];
14494			}
14495			if (!isset($col4[1])) {
14496				$col4[1] = $col4[2] = $col4[0];
14497			}
14498			$patch_array[0]['f'] = 0;
14499			$patch_array[0]['points'] = $coords;
14500			$patch_array[0]['colors'][0]['r'] = $col1[0];
14501			$patch_array[0]['colors'][0]['g'] = $col1[1];
14502			$patch_array[0]['colors'][0]['b'] = $col1[2];
14503			$patch_array[0]['colors'][1]['r'] = $col2[0];
14504			$patch_array[0]['colors'][1]['g'] = $col2[1];
14505			$patch_array[0]['colors'][1]['b'] = $col2[2];
14506			$patch_array[0]['colors'][2]['r'] = $col3[0];
14507			$patch_array[0]['colors'][2]['g'] = $col3[1];
14508			$patch_array[0]['colors'][2]['b'] = $col3[2];
14509			$patch_array[0]['colors'][3]['r'] = $col4[0];
14510			$patch_array[0]['colors'][3]['g'] = $col4[1];
14511			$patch_array[0]['colors'][3]['b'] = $col4[2];
14512		} else {
14513			//multi patch array
14514			$patch_array = $coords;
14515		}
14516		$bpcd = 65535; //16 bits per coordinate
14517		//build the data stream
14518		$this->gradients[$n]['stream'] = '';
14519		$count_patch = count($patch_array);
14520		for ($i=0; $i < $count_patch; ++$i) {
14521			$this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
14522			$count_points = count($patch_array[$i]['points']);
14523			for ($j=0; $j < $count_points; ++$j) {
14524				//each point as 16 bit
14525				$patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
14526				if ($patch_array[$i]['points'][$j] < 0) {
14527					$patch_array[$i]['points'][$j] = 0;
14528				}
14529				if ($patch_array[$i]['points'][$j] > $bpcd) {
14530					$patch_array[$i]['points'][$j] = $bpcd;
14531				}
14532				$this->gradients[$n]['stream'] .= chr((int) floor($patch_array[$i]['points'][$j] / 256));
14533				$this->gradients[$n]['stream'] .= chr((int) floor(intval($patch_array[$i]['points'][$j]) % 256));
14534			}
14535			$count_cols = count($patch_array[$i]['colors']);
14536			for ($j=0; $j < $count_cols; ++$j) {
14537				//each color component as 8 bit
14538				$this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
14539				$this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
14540				$this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
14541			}
14542		}
14543		//paint the gradient
14544		$this->_out('/Sh'.$n.' sh');
14545		//restore previous Graphic State
14546		$this->_outRestoreGraphicsState();
14547		if ($this->inxobj) {
14548			// we are inside an XObject template
14549			$this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14550		}
14551	}
14552
14553	/**
14554	 * Set a rectangular clipping area.
14555	 * @param float $x abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
14556	 * @param float $y ordinate of the top left corner of the rectangle.
14557	 * @param float $w width of the rectangle.
14558	 * @param float $h height of the rectangle.
14559	 * @author Andreas W\FCrmser, Nicola Asuni
14560	 * @since 3.1.000 (2008-06-09)
14561	 * @protected
14562	 */
14563	protected function Clip($x, $y, $w, $h) {
14564		if ($this->state != 2) {
14565			 return;
14566		}
14567		if ($this->rtl) {
14568			$x = $this->w - $x - $w;
14569		}
14570		//save current Graphic State
14571		$s = 'q';
14572		//set clipping area
14573		$s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
14574		//set up transformation matrix for gradient
14575		$s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
14576		$this->_out($s);
14577	}
14578
14579	/**
14580	 * Output gradient.
14581	 * @param int $type type of gradient (1 Function-based shading; 2 Axial shading; 3 Radial shading; 4 Free-form Gouraud-shaded triangle mesh; 5 Lattice-form Gouraud-shaded triangle mesh; 6 Coons patch mesh; 7 Tensor-product patch mesh). (Not all types are currently supported)
14582	 * @param array $coords array of coordinates.
14583	 * @param array $stops array gradient color components: color = array of GRAY, RGB or CMYK color components; offset = (0 to 1) represents a location along the gradient vector; exponent = exponent of the exponential interpolation function (default = 1).
14584	 * @param array $background An array of colour components appropriate to the colour space, specifying a single background colour value.
14585	 * @param boolean $antialias A flag indicating whether to filter the shading function to prevent aliasing artifacts.
14586	 * @author Nicola Asuni
14587	 * @since 3.1.000 (2008-06-09)
14588	 * @public
14589	 */
14590	public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
14591		if (($this->pdfa_mode && $this->pdfa_version < 2) OR ($this->state != 2)) {
14592			return;
14593		}
14594		$n = count($this->gradients) + 1;
14595		$this->gradients[$n] = array();
14596		$this->gradients[$n]['type'] = $type;
14597		$this->gradients[$n]['coords'] = $coords;
14598		$this->gradients[$n]['antialias'] = $antialias;
14599		$this->gradients[$n]['colors'] = array();
14600		$this->gradients[$n]['transparency'] = false;
14601		// color space
14602		$numcolspace = count($stops[0]['color']);
14603		$bcolor = array_values($background);
14604		switch($numcolspace) {
14605			case 5:   // SPOT
14606			case 4: { // CMYK
14607				$this->gradients[$n]['colspace'] = 'DeviceCMYK';
14608				if (!empty($background)) {
14609					$this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
14610				}
14611				break;
14612			}
14613			case 3: { // RGB
14614				$this->gradients[$n]['colspace'] = 'DeviceRGB';
14615				if (!empty($background)) {
14616					$this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
14617				}
14618				break;
14619			}
14620			case 1: { // GRAY SCALE
14621				$this->gradients[$n]['colspace'] = 'DeviceGray';
14622				if (!empty($background)) {
14623					$this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255);
14624				}
14625				break;
14626			}
14627		}
14628		$num_stops = count($stops);
14629		$last_stop_id = $num_stops - 1;
14630		foreach ($stops as $key => $stop) {
14631			$this->gradients[$n]['colors'][$key] = array();
14632			// offset represents a location along the gradient vector
14633			if (isset($stop['offset'])) {
14634				$this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
14635			} else {
14636				if ($key == 0) {
14637					$this->gradients[$n]['colors'][$key]['offset'] = 0;
14638				} elseif ($key == $last_stop_id) {
14639					$this->gradients[$n]['colors'][$key]['offset'] = 1;
14640				} else {
14641					$offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
14642					$this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
14643				}
14644			}
14645			if (isset($stop['opacity'])) {
14646				$this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
14647				if ((!($this->pdfa_mode && $this->pdfa_version < 2)) AND ($stop['opacity'] < 1)) {
14648					$this->gradients[$n]['transparency'] = true;
14649				}
14650			} else {
14651				$this->gradients[$n]['colors'][$key]['opacity'] = 1;
14652			}
14653			// exponent for the exponential interpolation function
14654			if (isset($stop['exponent'])) {
14655				$this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
14656			} else {
14657				$this->gradients[$n]['colors'][$key]['exponent'] = 1;
14658			}
14659			// set colors
14660			$color = array_values($stop['color']);
14661			switch($numcolspace) {
14662				case 5:   // SPOT
14663				case 4: { // CMYK
14664					$this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
14665					break;
14666				}
14667				case 3: { // RGB
14668					$this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255);
14669					break;
14670				}
14671				case 1: { // GRAY SCALE
14672					$this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255);
14673					break;
14674				}
14675			}
14676		}
14677		if ($this->gradients[$n]['transparency']) {
14678			// paint luminosity gradient
14679			$this->_out('/TGS'.$n.' gs');
14680		}
14681		//paint the gradient
14682		$this->_out('/Sh'.$n.' sh');
14683		//restore previous Graphic State
14684		$this->_outRestoreGraphicsState();
14685		if ($this->inxobj) {
14686			// we are inside an XObject template
14687			$this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14688		}
14689	}
14690
14691	/**
14692	 * Output gradient shaders.
14693	 * @author Nicola Asuni
14694	 * @since 3.1.000 (2008-06-09)
14695	 * @protected
14696	 */
14697	function _putshaders() {
14698		if ($this->pdfa_mode && $this->pdfa_version < 2) {
14699			return;
14700		}
14701		$idt = count($this->gradients); //index for transparency gradients
14702		foreach ($this->gradients as $id => $grad) {
14703			if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
14704				$fc = $this->_newobj();
14705				$out = '<<';
14706				$out .= ' /FunctionType 3';
14707				$out .= ' /Domain [0 1]';
14708				$functions = '';
14709				$bounds = '';
14710				$encode = '';
14711				$i = 1;
14712				$num_cols = count($grad['colors']);
14713				$lastcols = $num_cols - 1;
14714				for ($i = 1; $i < $num_cols; ++$i) {
14715					$functions .= ($fc + $i).' 0 R ';
14716					if ($i < $lastcols) {
14717						$bounds .= sprintf('%F ', $grad['colors'][$i]['offset']);
14718					}
14719					$encode .= '0 1 ';
14720				}
14721				$out .= ' /Functions ['.trim($functions).']';
14722				$out .= ' /Bounds ['.trim($bounds).']';
14723				$out .= ' /Encode ['.trim($encode).']';
14724				$out .= ' >>';
14725				$out .= "\n".'endobj';
14726				$this->_out($out);
14727				for ($i = 1; $i < $num_cols; ++$i) {
14728					$this->_newobj();
14729					$out = '<<';
14730					$out .= ' /FunctionType 2';
14731					$out .= ' /Domain [0 1]';
14732					$out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
14733					$out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
14734					$out .= ' /N '.$grad['colors'][$i]['exponent'];
14735					$out .= ' >>';
14736					$out .= "\n".'endobj';
14737					$this->_out($out);
14738				}
14739				// set transparency functions
14740				if ($grad['transparency']) {
14741					$ft = $this->_newobj();
14742					$out = '<<';
14743					$out .= ' /FunctionType 3';
14744					$out .= ' /Domain [0 1]';
14745					$functions = '';
14746					$i = 1;
14747					$num_cols = count($grad['colors']);
14748					for ($i = 1; $i < $num_cols; ++$i) {
14749						$functions .= ($ft + $i).' 0 R ';
14750					}
14751					$out .= ' /Functions ['.trim($functions).']';
14752					$out .= ' /Bounds ['.trim($bounds).']';
14753					$out .= ' /Encode ['.trim($encode).']';
14754					$out .= ' >>';
14755					$out .= "\n".'endobj';
14756					$this->_out($out);
14757					for ($i = 1; $i < $num_cols; ++$i) {
14758						$this->_newobj();
14759						$out = '<<';
14760						$out .= ' /FunctionType 2';
14761						$out .= ' /Domain [0 1]';
14762						$out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
14763						$out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
14764						$out .= ' /N '.$grad['colors'][$i]['exponent'];
14765						$out .= ' >>';
14766						$out .= "\n".'endobj';
14767						$this->_out($out);
14768					}
14769				}
14770			}
14771			// set shading object
14772			$this->_newobj();
14773			$out = '<< /ShadingType '.$grad['type'];
14774			if (isset($grad['colspace'])) {
14775				$out .= ' /ColorSpace /'.$grad['colspace'];
14776			} else {
14777				$out .= ' /ColorSpace /DeviceRGB';
14778			}
14779			if (isset($grad['background']) AND !empty($grad['background'])) {
14780				$out .= ' /Background ['.$grad['background'].']';
14781			}
14782			if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
14783				$out .= ' /AntiAlias true';
14784			}
14785			if ($grad['type'] == 2) {
14786				$out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
14787				$out .= ' /Domain [0 1]';
14788				$out .= ' /Function '.$fc.' 0 R';
14789				$out .= ' /Extend [true true]';
14790				$out .= ' >>';
14791			} elseif ($grad['type'] == 3) {
14792				//x0, y0, r0, x1, y1, r1
14793				//at this this time radius of inner circle is 0
14794				$out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
14795				$out .= ' /Domain [0 1]';
14796				$out .= ' /Function '.$fc.' 0 R';
14797				$out .= ' /Extend [true true]';
14798				$out .= ' >>';
14799			} elseif ($grad['type'] == 6) {
14800				$out .= ' /BitsPerCoordinate 16';
14801				$out .= ' /BitsPerComponent 8';
14802				$out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
14803				$out .= ' /BitsPerFlag 8';
14804				$stream = $this->_getrawstream($grad['stream']);
14805				$out .= ' /Length '.strlen($stream);
14806				$out .= ' >>';
14807				$out .= ' stream'."\n".$stream."\n".'endstream';
14808			}
14809			$out .= "\n".'endobj';
14810			$this->_out($out);
14811			if ($grad['transparency']) {
14812				$shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
14813				$shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
14814			}
14815			$this->gradients[$id]['id'] = $this->n;
14816			// set pattern object
14817			$this->_newobj();
14818			$out = '<< /Type /Pattern /PatternType 2';
14819			$out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
14820			$out .= ' >>';
14821			$out .= "\n".'endobj';
14822			$this->_out($out);
14823			$this->gradients[$id]['pattern'] = $this->n;
14824			// set shading and pattern for transparency mask
14825			if ($grad['transparency']) {
14826				// luminosity pattern
14827				$idgs = $id + $idt;
14828				$this->_newobj();
14829				$this->_out($shading_transparency);
14830				$this->gradients[$idgs]['id'] = $this->n;
14831				$this->_newobj();
14832				$out = '<< /Type /Pattern /PatternType 2';
14833				$out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
14834				$out .= ' >>';
14835				$out .= "\n".'endobj';
14836				$this->_out($out);
14837				$this->gradients[$idgs]['pattern'] = $this->n;
14838				// luminosity XObject
14839				$oid = $this->_newobj();
14840				$this->xobjects['LX'.$oid] = array('n' => $oid);
14841				$filter = '';
14842				$stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
14843				if ($this->compress) {
14844					$filter = ' /Filter /FlateDecode';
14845					$stream = gzcompress($stream);
14846				}
14847				$stream = $this->_getrawstream($stream);
14848				$out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
14849				$out .= ' /Length '.strlen($stream);
14850				$rect = sprintf('%F %F', $this->wPt, $this->hPt);
14851				$out .= ' /BBox [0 0 '.$rect.']';
14852				$out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
14853				$out .= ' /Resources <<';
14854				$out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
14855				$out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
14856				$out .= ' >>';
14857				$out .= ' >> ';
14858				$out .= ' stream'."\n".$stream."\n".'endstream';
14859				$out .= "\n".'endobj';
14860				$this->_out($out);
14861				// SMask
14862				$this->_newobj();
14863				$out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj';
14864				$this->_out($out);
14865				// ExtGState
14866				$this->_newobj();
14867				$out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj';
14868				$this->_out($out);
14869				$this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
14870			}
14871		}
14872	}
14873
14874	/**
14875	 * Draw the sector of a circle.
14876	 * It can be used for instance to render pie charts.
14877	 * @param float $xc abscissa of the center.
14878	 * @param float $yc ordinate of the center.
14879	 * @param float $r radius.
14880	 * @param float $a start angle (in degrees).
14881	 * @param float $b end angle (in degrees).
14882	 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
14883	 * @param float $cw indicates whether to go clockwise (default: true).
14884	 * @param float $o origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). Default: 90.
14885	 * @author Maxime Delorme, Nicola Asuni
14886	 * @since 3.1.000 (2008-06-09)
14887	 * @public
14888	 */
14889	public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
14890		$this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
14891	}
14892
14893	/**
14894	 * Draw the sector of an ellipse.
14895	 * It can be used for instance to render pie charts.
14896	 * @param float $xc abscissa of the center.
14897	 * @param float $yc ordinate of the center.
14898	 * @param float $rx the x-axis radius.
14899	 * @param float $ry the y-axis radius.
14900	 * @param float $a start angle (in degrees).
14901	 * @param float $b end angle (in degrees).
14902	 * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
14903	 * @param float $cw indicates whether to go clockwise.
14904	 * @param float $o origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock).
14905	 * @param integer $nc Number of curves used to draw a 90 degrees portion of arc.
14906	 * @author Maxime Delorme, Nicola Asuni
14907	 * @since 3.1.000 (2008-06-09)
14908	 * @public
14909	 */
14910	public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
14911		if ($this->state != 2) {
14912			 return;
14913		}
14914		if ($this->rtl) {
14915			$xc = ($this->w - $xc);
14916		}
14917		$op = TCPDF_STATIC::getPathPaintOperator($style);
14918		if ($op == 'f') {
14919			$line_style = array();
14920		}
14921		if ($cw) {
14922			$d = $b;
14923			$b = (360 - $a + $o);
14924			$a = (360 - $d + $o);
14925		} else {
14926			$b += $o;
14927			$a += $o;
14928		}
14929		$this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
14930		$this->_out($op);
14931	}
14932
14933	/**
14934	 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
14935	 * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
14936	 * Only vector drawing is supported, not text or bitmap.
14937	 * Although the script was successfully tested with various AI format versions, best results are probably achieved with files that were exported in the AI3 format (tested with Illustrator CS2, Freehand MX and Photoshop CS2).
14938	 * @param string $file Name of the file containing the image or a '@' character followed by the EPS/AI data string.
14939	 * @param float|null $x Abscissa of the upper-left corner.
14940	 * @param float|null $y Ordinate of the upper-left corner.
14941	 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
14942	 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
14943	 * @param mixed $link URL or identifier returned by AddLink().
14944	 * @param boolean $useBoundingBox specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
14945	 * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
14946	 * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
14947	 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
14948	 * @param boolean $fitonpage if true the image is resized to not exceed page dimensions.
14949	 * @param boolean $fixoutvals if true remove values outside the bounding box.
14950	 * @author Valentin Schmidt, Nicola Asuni
14951	 * @since 3.1.000 (2008-06-09)
14952	 * @public
14953	 */
14954	public function ImageEps($file, $x=null, $y=null, $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
14955		if ($this->state != 2) {
14956			 return;
14957		}
14958		if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
14959			// convert EPS to raster image using GD or ImageMagick libraries
14960			return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14961		}
14962		if (TCPDF_STATIC::empty_string($x)) {
14963			$x = $this->x;
14964		}
14965		if (TCPDF_STATIC::empty_string($y)) {
14966			$y = $this->y;
14967		}
14968		// check page for no-write regions and adapt page margins if necessary
14969		list($x, $y) = $this->checkPageRegions($h, $x, $y);
14970		$k = $this->k;
14971		if ($file[0] === '@') { // image from string
14972			$data = substr($file, 1);
14973		} else { // EPS/AI file
14974            $data = $this->getCachedFileContents($file);
14975		}
14976		if ($data === FALSE) {
14977			$this->Error('EPS file not found: '.$file);
14978		}
14979		$regs = array();
14980		// EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
14981		preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
14982		if (count($regs) > 1) {
14983			$version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
14984			if (strpos($version_str, 'Adobe Illustrator') !== false) {
14985				$versexp = explode(' ', $version_str);
14986				$version = (float)array_pop($versexp);
14987				if ($version >= 9) {
14988					$this->Error('This version of Adobe Illustrator file is not supported: '.$file);
14989				}
14990			}
14991		}
14992		// strip binary bytes in front of PS-header
14993		$start = strpos($data, '%!PS-Adobe');
14994		if ($start > 0) {
14995			$data = substr($data, $start);
14996		}
14997		// find BoundingBox params
14998		preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
14999		if (count($regs) > 1) {
15000			list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
15001		} else {
15002			$this->Error('No BoundingBox found in EPS/AI file: '.$file);
15003		}
15004		$start = strpos($data, '%%EndSetup');
15005		if ($start === false) {
15006			$start = strpos($data, '%%EndProlog');
15007		}
15008		if ($start === false) {
15009			$start = strpos($data, '%%BoundingBox');
15010		}
15011		$data = substr($data, $start);
15012		$end = strpos($data, '%%PageTrailer');
15013		if ($end===false) {
15014			$end = strpos($data, 'showpage');
15015		}
15016		if ($end) {
15017			$data = substr($data, 0, $end);
15018		}
15019		// calculate image width and height on document
15020		if (($w <= 0) AND ($h <= 0)) {
15021			$w = ($x2 - $x1) / $k;
15022			$h = ($y2 - $y1) / $k;
15023		} elseif ($w <= 0) {
15024			$w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
15025		} elseif ($h <= 0) {
15026			$h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
15027		}
15028		// fit the image on available space
15029		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
15030		if ($this->rasterize_vector_images) {
15031			// convert EPS to raster image using GD or ImageMagick libraries
15032			return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
15033		}
15034		// set scaling factors
15035		$scale_x = $w / (($x2 - $x1) / $k);
15036		$scale_y = $h / (($y2 - $y1) / $k);
15037		// set alignment
15038		$this->img_rb_y = $y + $h;
15039		// set alignment
15040		if ($this->rtl) {
15041			if ($palign == 'L') {
15042				$ximg = $this->lMargin;
15043			} elseif ($palign == 'C') {
15044				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15045			} elseif ($palign == 'R') {
15046				$ximg = $this->w - $this->rMargin - $w;
15047			} else {
15048				$ximg = $x - $w;
15049			}
15050			$this->img_rb_x = $ximg;
15051		} else {
15052			if ($palign == 'L') {
15053				$ximg = $this->lMargin;
15054			} elseif ($palign == 'C') {
15055				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15056			} elseif ($palign == 'R') {
15057				$ximg = $this->w - $this->rMargin - $w;
15058			} else {
15059				$ximg = $x;
15060			}
15061			$this->img_rb_x = $ximg + $w;
15062		}
15063		if ($useBoundingBox) {
15064			$dx = $ximg * $k - $x1;
15065			$dy = $y * $k - $y1;
15066		} else {
15067			$dx = $ximg * $k;
15068			$dy = $y * $k;
15069		}
15070		// save the current graphic state
15071		$this->_out('q'.$this->epsmarker);
15072		// translate
15073		$this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
15074		// scale
15075		if (isset($scale_x)) {
15076			$this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
15077		}
15078		// handle pc/unix/mac line endings
15079		$lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY);
15080		$u=0;
15081		$cnt = count($lines);
15082		for ($i=0; $i < $cnt; ++$i) {
15083			$line = $lines[$i];
15084			if (($line == '') OR ($line[0] == '%')) {
15085				continue;
15086			}
15087			$len = strlen($line);
15088			// check for spot color names
15089			$color_name = '';
15090			if (strcasecmp('x', substr(trim($line), -1)) == 0) {
15091				if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) {
15092					// extract spot color name
15093					$color_name = $matches[0];
15094					// remove color name from string
15095					$line = str_replace(' '.$color_name, '', $line);
15096					// remove pharentesis from color name
15097					$color_name = substr($color_name, 1, -1);
15098				}
15099			}
15100			$chunks = explode(' ', $line);
15101			$cmd = trim(array_pop($chunks));
15102			// RGB
15103			if (($cmd == 'Xa') OR ($cmd == 'XA')) {
15104				$b = array_pop($chunks);
15105				$g = array_pop($chunks);
15106				$r = array_pop($chunks);
15107				$this->_out(''.$r.' '.$g.' '.$b.' '.($cmd=='Xa'?'rg':'RG')); //substr($line, 0, -2).'rg' -> in EPS (AI8): c m y k r g b rg!
15108				continue;
15109			}
15110			$skip = false;
15111			if ($fixoutvals) {
15112				// check for values outside the bounding box
15113				switch ($cmd) {
15114					case 'm':
15115					case 'l':
15116					case 'L': {
15117						// skip values outside bounding box
15118						foreach ($chunks as $key => $val) {
15119							if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) {
15120								$skip = true;
15121							} elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) {
15122								$skip = true;
15123							}
15124						}
15125					}
15126				}
15127			}
15128			switch ($cmd) {
15129				case 'm':
15130				case 'l':
15131				case 'v':
15132				case 'y':
15133				case 'c':
15134				case 'k':
15135				case 'K':
15136				case 'g':
15137				case 'G':
15138				case 's':
15139				case 'S':
15140				case 'J':
15141				case 'j':
15142				case 'w':
15143				case 'M':
15144				case 'd':
15145				case 'n': {
15146					if ($skip) {
15147						break;
15148					}
15149					$this->_out($line);
15150					break;
15151				}
15152				case 'x': {// custom fill color
15153					if (empty($color_name)) {
15154						// CMYK color
15155						list($col_c, $col_m, $col_y, $col_k) = $chunks;
15156						$this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
15157					} else {
15158						// Spot Color (CMYK + tint)
15159						list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15160						$this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15161						$color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15162						$this->_out($color_cmd);
15163					}
15164					break;
15165				}
15166				case 'X': { // custom stroke color
15167					if (empty($color_name)) {
15168						// CMYK color
15169						list($col_c, $col_m, $col_y, $col_k) = $chunks;
15170						$this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
15171					} else {
15172						// Spot Color (CMYK + tint)
15173						list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15174						$this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15175						$color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15176						$this->_out($color_cmd);
15177					}
15178					break;
15179				}
15180				case 'Y':
15181				case 'N':
15182				case 'V':
15183				case 'L':
15184				case 'C': {
15185					if ($skip) {
15186						break;
15187					}
15188					$line[($len - 1)] = strtolower($cmd);
15189					$this->_out($line);
15190					break;
15191				}
15192				case 'b':
15193				case 'B': {
15194					$this->_out($cmd . '*');
15195					break;
15196				}
15197				case 'f':
15198				case 'F': {
15199					if ($u > 0) {
15200						$isU = false;
15201						$max = min(($i + 5), $cnt);
15202						for ($j = ($i + 1); $j < $max; ++$j) {
15203							$isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
15204						}
15205						if ($isU) {
15206							$this->_out('f*');
15207						}
15208					} else {
15209						$this->_out('f*');
15210					}
15211					break;
15212				}
15213				case '*u': {
15214					++$u;
15215					break;
15216				}
15217				case '*U': {
15218					--$u;
15219					break;
15220				}
15221			}
15222		}
15223		// restore previous graphic state
15224		$this->_out($this->epsmarker.'Q');
15225		if (!empty($border)) {
15226			$bx = $this->x;
15227			$by = $this->y;
15228			$this->x = $ximg;
15229			if ($this->rtl) {
15230				$this->x += $w;
15231			}
15232			$this->y = $y;
15233			$this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
15234			$this->x = $bx;
15235			$this->y = $by;
15236		}
15237		if ($link) {
15238			$this->Link($ximg, $y, $w, $h, $link, 0);
15239		}
15240		// set pointer to align the next text/objects
15241		switch($align) {
15242			case 'T':{
15243				$this->y = $y;
15244				$this->x = $this->img_rb_x;
15245				break;
15246			}
15247			case 'M':{
15248				$this->y = $y + round($h/2);
15249				$this->x = $this->img_rb_x;
15250				break;
15251			}
15252			case 'B':{
15253				$this->y = $this->img_rb_y;
15254				$this->x = $this->img_rb_x;
15255				break;
15256			}
15257			case 'N':{
15258				$this->setY($this->img_rb_y);
15259				break;
15260			}
15261			default:{
15262				break;
15263			}
15264		}
15265		$this->endlinex = $this->img_rb_x;
15266	}
15267
15268	/**
15269	 * Set document barcode.
15270	 * @param string $bc barcode
15271	 * @public
15272	 */
15273	public function setBarcode($bc='') {
15274		$this->barcode = $bc;
15275	}
15276
15277	/**
15278	 * Get current barcode.
15279	 * @return string
15280	 * @public
15281	 * @since 4.0.012 (2008-07-24)
15282	 */
15283	public function getBarcode() {
15284		return $this->barcode;
15285	}
15286
15287	/**
15288	 * Print a Linear Barcode.
15289	 * @param string $code code to print
15290	 * @param string $type type of barcode (see tcpdf_barcodes_1d.php for supported formats).
15291	 * @param float|null $x x position in user units (null = current x position)
15292	 * @param float|null $y y position in user units (null = current y position)
15293	 * @param float|null $w width in user units (null = remaining page width)
15294	 * @param float|null $h height in user units (null = remaining page height)
15295	 * @param float|null $xres width of the smallest bar in user units (null = default value = 0.4mm)
15296	 * @param array $style array of options:<ul>
15297	 * <li>boolean $style['border'] if true prints a border</li>
15298	 * <li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li>
15299	 * <li>int $style['hpadding'] horizontal padding in user units (set to 'auto' for automatic padding)</li>
15300	 * <li>int $style['vpadding'] vertical padding in user units (set to 'auto' for automatic padding)</li>
15301	 * <li>array $style['fgcolor'] color array for bars and text</li>
15302	 * <li>mixed $style['bgcolor'] color array for background (set to false for transparent)</li>
15303	 * <li>boolean $style['text'] if true prints text below the barcode</li>
15304	 * <li>string $style['label'] override default label</li>
15305	 * <li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li>
15306	 * <li>int $style['stretchtext']: 0 = disabled; 1 = horizontal scaling only if necessary; 2 = forced horizontal scaling; 3 = character spacing only if necessary; 4 = forced character spacing.</li>
15307	 * <li>string $style['position'] horizontal position of the containing barcode cell on the page: L = left margin; C = center; R = right margin.</li>
15308	 * <li>string $style['align'] horizontal position of the barcode on the containing rectangle: L = left; C = center; R = right.</li>
15309	 * <li>string $style['stretch'] if true stretch the barcode to best fit the available width, otherwise uses $xres resolution for a single bar.</li>
15310	 * <li>string $style['fitwidth'] if true reduce the width to fit the barcode width + padding. When this option is enabled the 'stretch' option is automatically disabled.</li>
15311	 * <li>string $style['cellfitalign'] this option works only when 'fitwidth' is true and 'position' is unset or empty. Set the horizontal position of the containing barcode cell inside the specified rectangle: L = left; C = center; R = right.</li></ul>
15312	 * @param string $align Indicates the alignment of the pointer next to barcode insertion relative to barcode height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
15313	 * @author Nicola Asuni
15314	 * @since 3.1.000 (2008-06-09)
15315	 * @public
15316	 */
15317	public function write1DBarcode($code, $type, $x=null, $y=null, $w=null, $h=null, $xres=null, $style=array(), $align='') {
15318		if (TCPDF_STATIC::empty_string(trim($code))) {
15319			return;
15320		}
15321		require_once(dirname(__FILE__).'/tcpdf_barcodes_1d.php');
15322		// save current graphic settings
15323		$gvars = $this->getGraphicVars();
15324		// create new barcode object
15325		$barcodeobj = new TCPDFBarcode($code, $type);
15326		$arrcode = $barcodeobj->getBarcodeArray();
15327		if (empty($arrcode) OR ($arrcode['maxw'] <= 0)) {
15328			$this->Error('Error in 1D barcode string');
15329		}
15330		if ($arrcode['maxh'] <= 0) {
15331			$arrcode['maxh'] = 1;
15332		}
15333		// set default values
15334		if (!isset($style['position'])) {
15335			$style['position'] = '';
15336		} elseif ($style['position'] == 'S') {
15337			// keep this for backward compatibility
15338			$style['position'] = '';
15339			$style['stretch'] = true;
15340		}
15341		if (!isset($style['fitwidth'])) {
15342			if (!isset($style['stretch'])) {
15343				$style['fitwidth'] = true;
15344			} else {
15345				$style['fitwidth'] = false;
15346			}
15347		}
15348		if ($style['fitwidth']) {
15349			// disable stretch
15350			$style['stretch'] = false;
15351		}
15352		if (!isset($style['stretch'])) {
15353			if (($w === '') OR ($w <= 0)) {
15354				$style['stretch'] = false;
15355			} else {
15356				$style['stretch'] = true;
15357			}
15358		}
15359		if (!isset($style['fgcolor'])) {
15360			$style['fgcolor'] = array(0,0,0); // default black
15361		}
15362		if (!isset($style['bgcolor'])) {
15363			$style['bgcolor'] = false; // default transparent
15364		}
15365		if (!isset($style['border'])) {
15366			$style['border'] = false;
15367		}
15368		$fontsize = 0;
15369		if (!isset($style['text'])) {
15370			$style['text'] = false;
15371		}
15372		if ($style['text'] AND isset($style['font'])) {
15373			if (isset($style['fontsize'])) {
15374				$fontsize = $style['fontsize'];
15375			}
15376			$this->setFont($style['font'], '', $fontsize);
15377		}
15378		if (!isset($style['stretchtext'])) {
15379			$style['stretchtext'] = 4;
15380		}
15381		if (TCPDF_STATIC::empty_string($x)) {
15382			$x = $this->x;
15383		}
15384		if (TCPDF_STATIC::empty_string($y)) {
15385			$y = $this->y;
15386		}
15387		// check page for no-write regions and adapt page margins if necessary
15388		list($x, $y) = $this->checkPageRegions($h, $x, $y);
15389		if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
15390			if ($this->rtl) {
15391				$w = $x - $this->lMargin;
15392			} else {
15393				$w = $this->w - $this->rMargin - $x;
15394			}
15395		}
15396		// padding
15397		if (!isset($style['padding'])) {
15398			$padding = 0;
15399		} elseif ($style['padding'] === 'auto') {
15400			$padding = 10 * ($w / ($arrcode['maxw'] + 20));
15401		} else {
15402			$padding = floatval($style['padding']);
15403		}
15404		// horizontal padding
15405		if (!isset($style['hpadding'])) {
15406			$hpadding = $padding;
15407		} elseif ($style['hpadding'] === 'auto') {
15408			$hpadding = 10 * ($w / ($arrcode['maxw'] + 20));
15409		} else {
15410			$hpadding = floatval($style['hpadding']);
15411		}
15412		// vertical padding
15413		if (!isset($style['vpadding'])) {
15414			$vpadding = $padding;
15415		} elseif ($style['vpadding'] === 'auto') {
15416			$vpadding = ($hpadding / 2);
15417		} else {
15418			$vpadding = floatval($style['vpadding']);
15419		}
15420		// calculate xres (single bar width)
15421		$max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
15422		if ($style['stretch']) {
15423			$xres = $max_xres;
15424		} else {
15425			if (TCPDF_STATIC::empty_string($xres)) {
15426				$xres = (0.141 * $this->k); // default bar width = 0.4 mm
15427			}
15428			if ($xres > $max_xres) {
15429				// correct xres to fit on $w
15430				$xres = $max_xres;
15431			}
15432			if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
15433				OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
15434				$hpadding = 10 * $xres;
15435				if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
15436					$vpadding = ($hpadding / 2);
15437				}
15438			}
15439		}
15440		if ($style['fitwidth']) {
15441			$wold = $w;
15442			$w = (($arrcode['maxw'] * $xres) + (2 * $hpadding));
15443			if (isset($style['cellfitalign'])) {
15444				switch ($style['cellfitalign']) {
15445					case 'L': {
15446						if ($this->rtl) {
15447							$x -= ($wold - $w);
15448						}
15449						break;
15450					}
15451					case 'R': {
15452						if (!$this->rtl) {
15453							$x += ($wold - $w);
15454						}
15455						break;
15456					}
15457					case 'C': {
15458						if ($this->rtl) {
15459							$x -= (($wold - $w) / 2);
15460						} else {
15461							$x += (($wold - $w) / 2);
15462						}
15463						break;
15464					}
15465					default : {
15466						break;
15467					}
15468				}
15469			}
15470		}
15471		$text_height = $this->getCellHeight($fontsize / $this->k);
15472		// height
15473		if (TCPDF_STATIC::empty_string($h) OR ($h <= 0)) {
15474			// set default height
15475			$h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height;
15476		}
15477		$barh = $h - $text_height - (2 * $vpadding);
15478		if ($barh <=0) {
15479			// try to reduce font or padding to fit barcode on available height
15480			if ($text_height > $h) {
15481				$fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio));
15482				$text_height = $this->getCellHeight($fontsize / $this->k);
15483				$this->setFont($style['font'], '', $fontsize);
15484			}
15485			if ($vpadding > 0) {
15486				$vpadding = (($h - $text_height) / 4);
15487			}
15488			$barh = $h - $text_height - (2 * $vpadding);
15489		}
15490		// fit the barcode on available space
15491		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15492		// set alignment
15493		$this->img_rb_y = $y + $h;
15494		// set alignment
15495		if ($this->rtl) {
15496			if ($style['position'] == 'L') {
15497				$xpos = $this->lMargin;
15498			} elseif ($style['position'] == 'C') {
15499				$xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15500			} elseif ($style['position'] == 'R') {
15501				$xpos = $this->w - $this->rMargin - $w;
15502			} else {
15503				$xpos = $x - $w;
15504			}
15505			$this->img_rb_x = $xpos;
15506		} else {
15507			if ($style['position'] == 'L') {
15508				$xpos = $this->lMargin;
15509			} elseif ($style['position'] == 'C') {
15510				$xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15511			} elseif ($style['position'] == 'R') {
15512				$xpos = $this->w - $this->rMargin - $w;
15513			} else {
15514				$xpos = $x;
15515			}
15516			$this->img_rb_x = $xpos + $w;
15517		}
15518		$xpos_rect = $xpos;
15519		if (!isset($style['align'])) {
15520			$style['align'] = 'C';
15521		}
15522		switch ($style['align']) {
15523			case 'L': {
15524				$xpos = $xpos_rect + $hpadding;
15525				break;
15526			}
15527			case 'R': {
15528				$xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding;
15529				break;
15530			}
15531			case 'C':
15532			default : {
15533				$xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2);
15534				break;
15535			}
15536		}
15537		$xpos_text = $xpos;
15538		// barcode is always printed in LTR direction
15539		$tempRTL = $this->rtl;
15540		$this->rtl = false;
15541		// print background color
15542		if ($style['bgcolor']) {
15543			$this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15544		} elseif ($style['border']) {
15545			$this->Rect($xpos_rect, $y, $w, $h, 'D');
15546		}
15547		// set foreground color
15548		$this->setDrawColorArray($style['fgcolor']);
15549		$this->setTextColorArray($style['fgcolor']);
15550		// print bars
15551		foreach ($arrcode['bcode'] as $k => $v) {
15552			$bw = ($v['w'] * $xres);
15553			if ($v['t']) {
15554				// draw a vertical bar
15555				$ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']);
15556				$this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
15557			}
15558			$xpos += $bw;
15559		}
15560		// print text
15561		if ($style['text']) {
15562			if (isset($style['label']) AND !TCPDF_STATIC::empty_string($style['label'])) {
15563				$label = $style['label'];
15564			} else {
15565				$label = $code;
15566			}
15567			$txtwidth = ($arrcode['maxw'] * $xres);
15568			if ($this->GetStringWidth($label) > $txtwidth) {
15569				$style['stretchtext'] = 2;
15570			}
15571			// print text
15572			$this->x = $xpos_text;
15573			$this->y = $y + $vpadding + $barh;
15574			$cellpadding = $this->cell_padding;
15575			$this->setCellPadding(0);
15576			$this->Cell($txtwidth, 0, $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
15577			$this->cell_padding = $cellpadding;
15578		}
15579		// restore original direction
15580		$this->rtl = $tempRTL;
15581		// restore previous settings
15582		$this->setGraphicVars($gvars);
15583		// set pointer to align the next text/objects
15584		switch($align) {
15585			case 'T':{
15586				$this->y = $y;
15587				$this->x = $this->img_rb_x;
15588				break;
15589			}
15590			case 'M':{
15591				$this->y = $y + round($h / 2);
15592				$this->x = $this->img_rb_x;
15593				break;
15594			}
15595			case 'B':{
15596				$this->y = $this->img_rb_y;
15597				$this->x = $this->img_rb_x;
15598				break;
15599			}
15600			case 'N':{
15601				$this->setY($this->img_rb_y);
15602				break;
15603			}
15604			default:{
15605				break;
15606			}
15607		}
15608		$this->endlinex = $this->img_rb_x;
15609	}
15610
15611	/**
15612	 * Print 2D Barcode.
15613	 * @param string $code code to print
15614	 * @param string $type type of barcode (see tcpdf_barcodes_2d.php for supported formats).
15615	 * @param float|null $x x position in user units
15616	 * @param float|null $y y position in user units
15617	 * @param float|null $w width in user units
15618	 * @param float|null $h height in user units
15619	 * @param array $style array of options:<ul>
15620	 * <li>boolean $style['border'] if true prints a border around the barcode</li>
15621	 * <li>int $style['padding'] padding to leave around the barcode in barcode units (set to 'auto' for automatic padding)</li>
15622	 * <li>int $style['hpadding'] horizontal padding in barcode units (set to 'auto' for automatic padding)</li>
15623	 * <li>int $style['vpadding'] vertical padding in barcode units (set to 'auto' for automatic padding)</li>
15624	 * <li>int $style['module_width'] width of a single module in points</li>
15625	 * <li>int $style['module_height'] height of a single module in points</li>
15626	 * <li>array $style['fgcolor'] color array for bars and text</li>
15627	 * <li>mixed $style['bgcolor'] color array for background or false for transparent</li>
15628	 * <li>string $style['position'] barcode position on the page: L = left margin; C = center; R = right margin; S = stretch</li>
15629	 * @param string $align Indicates the alignment of the pointer next to barcode insertion relative to barcode height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
15630	 * @param boolean $distort if true distort the barcode to fit width and height, otherwise preserve aspect ratio
15631	 * @author Nicola Asuni
15632	 * @since 4.5.037 (2009-04-07)
15633	 * @public
15634	 */
15635	public function write2DBarcode($code, $type, $x=null, $y=null, $w=null, $h=null, $style=array(), $align='', $distort=false) {
15636		if (TCPDF_STATIC::empty_string(trim($code))) {
15637			return;
15638		}
15639		require_once(dirname(__FILE__).'/tcpdf_barcodes_2d.php');
15640		// save current graphic settings
15641		$gvars = $this->getGraphicVars();
15642		// create new barcode object
15643		$barcodeobj = new TCPDF2DBarcode($code, $type);
15644		$arrcode = $barcodeobj->getBarcodeArray();
15645		if (empty($arrcode) OR !isset($arrcode['num_rows']) OR ($arrcode['num_rows'] == 0) OR !isset($arrcode['num_cols']) OR ($arrcode['num_cols'] == 0)) {
15646			$this->Error('Error in 2D barcode string');
15647		}
15648		// set default values
15649		if (!isset($style['position'])) {
15650			$style['position'] = '';
15651		}
15652		if (!isset($style['fgcolor'])) {
15653			$style['fgcolor'] = array(0,0,0); // default black
15654		}
15655		if (!isset($style['bgcolor'])) {
15656			$style['bgcolor'] = false; // default transparent
15657		}
15658		if (!isset($style['border'])) {
15659			$style['border'] = false;
15660		}
15661		// padding
15662		if (!isset($style['padding'])) {
15663			$style['padding'] = 0;
15664		} elseif ($style['padding'] === 'auto') {
15665			$style['padding'] = 4;
15666		}
15667		if (!isset($style['hpadding'])) {
15668			$style['hpadding'] = $style['padding'];
15669		} elseif ($style['hpadding'] === 'auto') {
15670			$style['hpadding'] = 4;
15671		}
15672		if (!isset($style['vpadding'])) {
15673			$style['vpadding'] = $style['padding'];
15674		} elseif ($style['vpadding'] === 'auto') {
15675			$style['vpadding'] = 4;
15676		}
15677		$hpad = (2 * $style['hpadding']);
15678		$vpad = (2 * $style['vpadding']);
15679		// cell (module) dimension
15680		if (!isset($style['module_width'])) {
15681			$style['module_width'] = 1; // width of a single module in points
15682		}
15683		if (!isset($style['module_height'])) {
15684			$style['module_height'] = 1; // height of a single module in points
15685		}
15686		if (TCPDF_STATIC::empty_string($x)) {
15687			$x = $this->x;
15688		}
15689		if (TCPDF_STATIC::empty_string($y)) {
15690			$y = $this->y;
15691		}
15692		// check page for no-write regions and adapt page margins if necessary
15693		list($x, $y) = $this->checkPageRegions($h, $x, $y);
15694		// number of barcode columns and rows
15695		$rows = $arrcode['num_rows'];
15696		$cols = $arrcode['num_cols'];
15697		if (($rows <= 0) || ($cols <= 0)){
15698			$this->Error('Error in 2D barcode string');
15699		}
15700		// module width and height
15701		$mw = $style['module_width'];
15702		$mh = $style['module_height'];
15703		if (($mw <= 0) OR ($mh <= 0)) {
15704			$this->Error('Error in 2D barcode string');
15705		}
15706		// get max dimensions
15707		if ($this->rtl) {
15708			$maxw = $x - $this->lMargin;
15709		} else {
15710			$maxw = $this->w - $this->rMargin - $x;
15711		}
15712		$maxh = ($this->h - $this->tMargin - $this->bMargin);
15713		$ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad));
15714		$ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad));
15715		if (!$distort) {
15716			if (($maxw * $ratioHW) > $maxh) {
15717				$maxw = $maxh * $ratioWH;
15718			}
15719			if (($maxh * $ratioWH) > $maxw) {
15720				$maxh = $maxw * $ratioHW;
15721			}
15722		}
15723		// set maximum dimensions
15724		if ($w > $maxw) {
15725			$w = $maxw;
15726		}
15727		if ($h > $maxh) {
15728			$h = $maxh;
15729		}
15730		// set dimensions
15731		if ((TCPDF_STATIC::empty_string($w) OR ($w <= 0)) AND (TCPDF_STATIC::empty_string($h) OR ($h <= 0))) {
15732			$w = ($cols + $hpad) * ($mw / $this->k);
15733			$h = ($rows + $vpad) * ($mh / $this->k);
15734		} elseif (($w === '') OR ($w <= 0)) {
15735			$w = $h * $ratioWH;
15736		} elseif (($h === '') OR ($h <= 0)) {
15737			$h = $w * $ratioHW;
15738		}
15739		// barcode size (excluding padding)
15740		$bw = ($w * $cols) / ($cols + $hpad);
15741		$bh = ($h * $rows) / ($rows + $vpad);
15742		// dimension of single barcode cell unit
15743		$cw = $bw / $cols;
15744		$ch = $bh / $rows;
15745		if (!$distort) {
15746			if (($cw / $ch) > ($mw / $mh)) {
15747				// correct horizontal distortion
15748				$cw = $ch * $mw / $mh;
15749				$bw = $cw * $cols;
15750				$style['hpadding'] = ($w - $bw) / (2 * $cw);
15751			} else {
15752				// correct vertical distortion
15753				$ch = $cw * $mh / $mw;
15754				$bh = $ch * $rows;
15755				$style['vpadding'] = ($h - $bh) / (2 * $ch);
15756			}
15757		}
15758		// fit the barcode on available space
15759		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15760		// set alignment
15761		$this->img_rb_y = $y + $h;
15762		// set alignment
15763		if ($this->rtl) {
15764			if ($style['position'] == 'L') {
15765				$xpos = $this->lMargin;
15766			} elseif ($style['position'] == 'C') {
15767				$xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15768			} elseif ($style['position'] == 'R') {
15769				$xpos = $this->w - $this->rMargin - $w;
15770			} else {
15771				$xpos = $x - $w;
15772			}
15773			$this->img_rb_x = $xpos;
15774		} else {
15775			if ($style['position'] == 'L') {
15776				$xpos = $this->lMargin;
15777			} elseif ($style['position'] == 'C') {
15778				$xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15779			} elseif ($style['position'] == 'R') {
15780				$xpos = $this->w - $this->rMargin - $w;
15781			} else {
15782				$xpos = $x;
15783			}
15784			$this->img_rb_x = $xpos + $w;
15785		}
15786		$xstart = $xpos + ($style['hpadding'] * $cw);
15787		$ystart = $y + ($style['vpadding'] * $ch);
15788		// barcode is always printed in LTR direction
15789		$tempRTL = $this->rtl;
15790		$this->rtl = false;
15791		// print background color
15792		if ($style['bgcolor']) {
15793			$this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15794		} elseif ($style['border']) {
15795			$this->Rect($xpos, $y, $w, $h, 'D');
15796		}
15797		// set foreground color
15798		$this->setDrawColorArray($style['fgcolor']);
15799		// print barcode cells
15800		// for each row
15801		for ($r = 0; $r < $rows; ++$r) {
15802			$xr = $xstart;
15803			// for each column
15804			for ($c = 0; $c < $cols; ++$c) {
15805				if ($arrcode['bcode'][$r][$c] == 1) {
15806					// draw a single barcode cell
15807					$this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
15808				}
15809				$xr += $cw;
15810			}
15811			$ystart += $ch;
15812		}
15813		// restore original direction
15814		$this->rtl = $tempRTL;
15815		// restore previous settings
15816		$this->setGraphicVars($gvars);
15817		// set pointer to align the next text/objects
15818		switch($align) {
15819			case 'T':{
15820				$this->y = $y;
15821				$this->x = $this->img_rb_x;
15822				break;
15823			}
15824			case 'M':{
15825				$this->y = $y + round($h/2);
15826				$this->x = $this->img_rb_x;
15827				break;
15828			}
15829			case 'B':{
15830				$this->y = $this->img_rb_y;
15831				$this->x = $this->img_rb_x;
15832				break;
15833			}
15834			case 'N':{
15835				$this->setY($this->img_rb_y);
15836				break;
15837			}
15838			default:{
15839				break;
15840			}
15841		}
15842		$this->endlinex = $this->img_rb_x;
15843	}
15844
15845	/**
15846	 * Returns an array containing current margins:
15847	 * <ul>
15848			<li>$ret['left'] = left margin</li>
15849			<li>$ret['right'] = right margin</li>
15850			<li>$ret['top'] = top margin</li>
15851			<li>$ret['bottom'] = bottom margin</li>
15852			<li>$ret['header'] = header margin</li>
15853			<li>$ret['footer'] = footer margin</li>
15854			<li>$ret['cell'] = cell padding array</li>
15855			<li>$ret['padding_left'] = cell left padding</li>
15856			<li>$ret['padding_top'] = cell top padding</li>
15857			<li>$ret['padding_right'] = cell right padding</li>
15858			<li>$ret['padding_bottom'] = cell bottom padding</li>
15859	 * </ul>
15860	 * @return array containing all margins measures
15861	 * @public
15862	 * @since 3.2.000 (2008-06-23)
15863	 */
15864	public function getMargins() {
15865		$ret = array(
15866			'left' => $this->lMargin,
15867			'right' => $this->rMargin,
15868			'top' => $this->tMargin,
15869			'bottom' => $this->bMargin,
15870			'header' => $this->header_margin,
15871			'footer' => $this->footer_margin,
15872			'cell' => $this->cell_padding,
15873			'padding_left' => $this->cell_padding['L'],
15874			'padding_top' => $this->cell_padding['T'],
15875			'padding_right' => $this->cell_padding['R'],
15876			'padding_bottom' => $this->cell_padding['B']
15877		);
15878		return $ret;
15879	}
15880
15881	/**
15882	 * Returns an array containing original margins:
15883	 * <ul>
15884			<li>$ret['left'] = left margin</li>
15885			<li>$ret['right'] = right margin</li>
15886	 * </ul>
15887	 * @return array containing all margins measures
15888	 * @public
15889	 * @since 4.0.012 (2008-07-24)
15890	 */
15891	public function getOriginalMargins() {
15892		$ret = array(
15893			'left' => $this->original_lMargin,
15894			'right' => $this->original_rMargin
15895		);
15896		return $ret;
15897	}
15898
15899	/**
15900	 * Returns the current font size.
15901	 * @return float current font size
15902	 * @public
15903	 * @since 3.2.000 (2008-06-23)
15904	 */
15905	public function getFontSize() {
15906		return $this->FontSize;
15907	}
15908
15909	/**
15910	 * Returns the current font size in points unit.
15911	 * @return int current font size in points unit
15912	 * @public
15913	 * @since 3.2.000 (2008-06-23)
15914	 */
15915	public function getFontSizePt() {
15916		return $this->FontSizePt;
15917	}
15918
15919	/**
15920	 * Returns the current font family name.
15921	 * @return string current font family name
15922	 * @public
15923	 * @since 4.3.008 (2008-12-05)
15924	 */
15925	public function getFontFamily() {
15926		return $this->FontFamily;
15927	}
15928
15929	/**
15930	 * Returns the current font style.
15931	 * @return string current font style
15932	 * @public
15933	 * @since 4.3.008 (2008-12-05)
15934	 */
15935	public function getFontStyle() {
15936		return $this->FontStyle;
15937	}
15938
15939	/**
15940	 * Cleanup HTML code (requires HTML Tidy library).
15941	 * @param string $html htmlcode to fix
15942	 * @param string $default_css CSS commands to add
15943	 * @param array|null $tagvs parameters for setHtmlVSpace method
15944	 * @param array|null $tidy_options options for tidy_parse_string function
15945	 * @return string XHTML code cleaned up
15946	 * @author Nicola Asuni
15947	 * @public
15948	 * @since 5.9.017 (2010-11-16)
15949	 * @see setHtmlVSpace()
15950	 */
15951	public function fixHTMLCode($html, $default_css='', $tagvs=null, $tidy_options=null) {
15952		return TCPDF_STATIC::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces);
15953	}
15954
15955	/**
15956	 * Returns the border width from CSS property
15957	 * @param string $width border width
15958	 * @return int with in user units
15959	 * @protected
15960	 * @since 5.7.000 (2010-08-02)
15961	 */
15962	protected function getCSSBorderWidth($width) {
15963		if ($width == 'thin') {
15964			$width = (2 / $this->k);
15965		} elseif ($width == 'medium') {
15966			$width = (4 / $this->k);
15967		} elseif ($width == 'thick') {
15968			$width = (6 / $this->k);
15969		} else {
15970			$width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
15971		}
15972		return $width;
15973	}
15974
15975	/**
15976	 * Returns the border dash style from CSS property
15977	 * @param string $style border style to convert
15978	 * @return int sash style (return -1 in case of none or hidden border)
15979	 * @protected
15980	 * @since 5.7.000 (2010-08-02)
15981	 */
15982	protected function getCSSBorderDashStyle($style) {
15983		switch (strtolower($style)) {
15984			case 'none':
15985			case 'hidden': {
15986				$dash = -1;
15987				break;
15988			}
15989			case 'dotted': {
15990				$dash = 1;
15991				break;
15992			}
15993			case 'dashed': {
15994				$dash = 3;
15995				break;
15996			}
15997			case 'double':
15998			case 'groove':
15999			case 'ridge':
16000			case 'inset':
16001			case 'outset':
16002			case 'solid':
16003			default: {
16004				$dash = 0;
16005				break;
16006			}
16007		}
16008		return $dash;
16009	}
16010
16011	/**
16012	 * Returns the border style array from CSS border properties
16013	 * @param string $cssborder border properties
16014	 * @return array containing border properties
16015	 * @protected
16016	 * @since 5.7.000 (2010-08-02)
16017	 */
16018	protected function getCSSBorderStyle($cssborder) {
16019		$bprop = preg_split('/[\s]+/', trim($cssborder));
16020		$border = array(); // value to be returned
16021		switch (count($bprop)) {
16022			case 3: {
16023				$width = $bprop[0];
16024				$style = $bprop[1];
16025				$color = $bprop[2];
16026				break;
16027			}
16028			case 2: {
16029				$width = 'medium';
16030				$style = $bprop[0];
16031				$color = $bprop[1];
16032				break;
16033			}
16034			case 1: {
16035				$width = 'medium';
16036				$style = $bprop[0];
16037				$color = 'black';
16038				break;
16039			}
16040			default: {
16041				$width = 'medium';
16042				$style = 'solid';
16043				$color = 'black';
16044				break;
16045			}
16046		}
16047		if ($style == 'none') {
16048			return array();
16049		}
16050		$border['cap'] = 'square';
16051		$border['join'] = 'miter';
16052		$border['dash'] = $this->getCSSBorderDashStyle($style);
16053		if ($border['dash'] < 0) {
16054			return array();
16055		}
16056		$border['width'] = $this->getCSSBorderWidth($width);
16057		$border['color'] = TCPDF_COLORS::convertHTMLColorToDec($color, $this->spot_colors);
16058		return $border;
16059	}
16060
16061	/**
16062	 * Get the internal Cell padding from CSS attribute.
16063	 * @param string $csspadding padding properties
16064	 * @param float $width width of the containing element
16065	 * @return array of cell paddings
16066	 * @public
16067	 * @since 5.9.000 (2010-10-04)
16068	 */
16069	public function getCSSPadding($csspadding, $width=0) {
16070		$padding = preg_split('/[\s]+/', trim($csspadding));
16071		$cell_padding = array(); // value to be returned
16072		switch (count($padding)) {
16073			case 4: {
16074				$cell_padding['T'] = $padding[0];
16075				$cell_padding['R'] = $padding[1];
16076				$cell_padding['B'] = $padding[2];
16077				$cell_padding['L'] = $padding[3];
16078				break;
16079			}
16080			case 3: {
16081				$cell_padding['T'] = $padding[0];
16082				$cell_padding['R'] = $padding[1];
16083				$cell_padding['B'] = $padding[2];
16084				$cell_padding['L'] = $padding[1];
16085				break;
16086			}
16087			case 2: {
16088				$cell_padding['T'] = $padding[0];
16089				$cell_padding['R'] = $padding[1];
16090				$cell_padding['B'] = $padding[0];
16091				$cell_padding['L'] = $padding[1];
16092				break;
16093			}
16094			case 1: {
16095				$cell_padding['T'] = $padding[0];
16096				$cell_padding['R'] = $padding[0];
16097				$cell_padding['B'] = $padding[0];
16098				$cell_padding['L'] = $padding[0];
16099				break;
16100			}
16101			default: {
16102				return $this->cell_padding;
16103			}
16104		}
16105		if ($width == 0) {
16106			$width = $this->w - $this->lMargin - $this->rMargin;
16107		}
16108		$cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
16109		$cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
16110		$cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
16111		$cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
16112		return $cell_padding;
16113	}
16114
16115	/**
16116	 * Get the internal Cell margin from CSS attribute.
16117	 * @param string $cssmargin margin properties
16118	 * @param float $width width of the containing element
16119	 * @return array of cell margins
16120	 * @public
16121	 * @since 5.9.000 (2010-10-04)
16122	 */
16123	public function getCSSMargin($cssmargin, $width=0) {
16124		$margin = preg_split('/[\s]+/', trim($cssmargin));
16125		$cell_margin = array(); // value to be returned
16126		switch (count($margin)) {
16127			case 4: {
16128				$cell_margin['T'] = $margin[0];
16129				$cell_margin['R'] = $margin[1];
16130				$cell_margin['B'] = $margin[2];
16131				$cell_margin['L'] = $margin[3];
16132				break;
16133			}
16134			case 3: {
16135				$cell_margin['T'] = $margin[0];
16136				$cell_margin['R'] = $margin[1];
16137				$cell_margin['B'] = $margin[2];
16138				$cell_margin['L'] = $margin[1];
16139				break;
16140			}
16141			case 2: {
16142				$cell_margin['T'] = $margin[0];
16143				$cell_margin['R'] = $margin[1];
16144				$cell_margin['B'] = $margin[0];
16145				$cell_margin['L'] = $margin[1];
16146				break;
16147			}
16148			case 1: {
16149				$cell_margin['T'] = $margin[0];
16150				$cell_margin['R'] = $margin[0];
16151				$cell_margin['B'] = $margin[0];
16152				$cell_margin['L'] = $margin[0];
16153				break;
16154			}
16155			default: {
16156				return $this->cell_margin;
16157			}
16158		}
16159		if ($width == 0) {
16160			$width = $this->w - $this->lMargin - $this->rMargin;
16161		}
16162		$cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
16163		$cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
16164		$cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
16165		$cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
16166		return $cell_margin;
16167	}
16168
16169	/**
16170	 * Get the border-spacing from CSS attribute.
16171	 * @param string $cssbspace border-spacing CSS properties
16172	 * @param float $width width of the containing element
16173	 * @return array of border spacings
16174	 * @public
16175	 * @since 5.9.010 (2010-10-27)
16176	 */
16177	public function getCSSBorderMargin($cssbspace, $width=0) {
16178		$space = preg_split('/[\s]+/', trim($cssbspace));
16179		$border_spacing = array(); // value to be returned
16180		switch (count($space)) {
16181			case 2: {
16182				$border_spacing['H'] = $space[0];
16183				$border_spacing['V'] = $space[1];
16184				break;
16185			}
16186			case 1: {
16187				$border_spacing['H'] = $space[0];
16188				$border_spacing['V'] = $space[0];
16189				break;
16190			}
16191			default: {
16192				return array('H' => 0, 'V' => 0);
16193			}
16194		}
16195		if ($width == 0) {
16196			$width = $this->w - $this->lMargin - $this->rMargin;
16197		}
16198		$border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
16199		$border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
16200		return $border_spacing;
16201	}
16202
16203	/**
16204	 * Returns the letter-spacing value from CSS value
16205	 * @param string $spacing letter-spacing value
16206	 * @param float $parent font spacing (tracking) value of the parent element
16207	 * @return float quantity to increases or decreases the space between characters in a text.
16208	 * @protected
16209	 * @since 5.9.000 (2010-10-02)
16210	 */
16211	protected function getCSSFontSpacing($spacing, $parent=0) {
16212		$val = 0; // value to be returned
16213		$spacing = trim($spacing);
16214		switch ($spacing) {
16215			case 'normal': {
16216				$val = 0;
16217				break;
16218			}
16219			case 'inherit': {
16220				if ($parent == 'normal') {
16221					$val = 0;
16222				} else {
16223					$val = $parent;
16224				}
16225				break;
16226			}
16227			default: {
16228				$val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
16229			}
16230		}
16231		return $val;
16232	}
16233
16234	/**
16235	 * Returns the percentage of font stretching from CSS value
16236	 * @param string $stretch stretch mode
16237	 * @param float $parent stretch value of the parent element
16238	 * @return float font stretching percentage
16239	 * @protected
16240	 * @since 5.9.000 (2010-10-02)
16241	 */
16242	protected function getCSSFontStretching($stretch, $parent=100) {
16243		$val = 100; // value to be returned
16244		$stretch = trim($stretch);
16245		switch ($stretch) {
16246			case 'ultra-condensed': {
16247				$val = 40;
16248				break;
16249			}
16250			case 'extra-condensed': {
16251				$val = 55;
16252				break;
16253			}
16254			case 'condensed': {
16255				$val = 70;
16256				break;
16257			}
16258			case 'semi-condensed': {
16259				$val = 85;
16260				break;
16261			}
16262			case 'normal': {
16263				$val = 100;
16264				break;
16265			}
16266			case 'semi-expanded': {
16267				$val = 115;
16268				break;
16269			}
16270			case 'expanded': {
16271				$val = 130;
16272				break;
16273			}
16274			case 'extra-expanded': {
16275				$val = 145;
16276				break;
16277			}
16278			case 'ultra-expanded': {
16279				$val = 160;
16280				break;
16281			}
16282			case 'wider': {
16283				$val = ($parent + 10);
16284				break;
16285			}
16286			case 'narrower': {
16287				$val = ($parent - 10);
16288				break;
16289			}
16290			case 'inherit': {
16291				if ($parent == 'normal') {
16292					$val = 100;
16293				} else {
16294					$val = $parent;
16295				}
16296				break;
16297			}
16298			default: {
16299				$val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
16300			}
16301		}
16302		return $val;
16303	}
16304
16305	/**
16306	 * Convert HTML string containing font size value to points
16307	 * @param string $val String containing font size value and unit.
16308	 * @param float $refsize Reference font size in points.
16309	 * @param float $parent_size Parent font size in points.
16310	 * @param string $defaultunit Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
16311	 * @return float value in points
16312	 * @public
16313	 */
16314	public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') {
16315		$refsize = TCPDF_FONTS::getFontRefSize($refsize);
16316		$parent_size = TCPDF_FONTS::getFontRefSize($parent_size, $refsize);
16317		switch ($val) {
16318			case 'xx-small': {
16319				$size = ($refsize - 4);
16320				break;
16321			}
16322			case 'x-small': {
16323				$size = ($refsize - 3);
16324				break;
16325			}
16326			case 'small': {
16327				$size = ($refsize - 2);
16328				break;
16329			}
16330			case 'medium': {
16331				$size = $refsize;
16332				break;
16333			}
16334			case 'large': {
16335				$size = ($refsize + 2);
16336				break;
16337			}
16338			case 'x-large': {
16339				$size = ($refsize + 4);
16340				break;
16341			}
16342			case 'xx-large': {
16343				$size = ($refsize + 6);
16344				break;
16345			}
16346			case 'smaller': {
16347				$size = ($parent_size - 3);
16348				break;
16349			}
16350			case 'larger': {
16351				$size = ($parent_size + 3);
16352				break;
16353			}
16354			default: {
16355				$size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true);
16356			}
16357		}
16358		return $size;
16359	}
16360
16361	/**
16362	 * Returns the HTML DOM array.
16363	 * @param string $html html code
16364	 * @return array
16365	 * @protected
16366	 * @since 3.2.000 (2008-06-20)
16367	 */
16368	protected function getHtmlDomArray($html) {
16369		// array of CSS styles ( selector => properties).
16370		$css = array();
16371		// get CSS array defined at previous call
16372		$matches = array();
16373		if (preg_match_all('/<cssarray>([^\<]*?)<\/cssarray>/is', $html, $matches) > 0) {
16374			if (isset($matches[1][0])) {
16375				$css = array_merge($css, json_decode($this->unhtmlentities($matches[1][0]), true));
16376			}
16377			$html = preg_replace('/<cssarray>(.*?)<\/cssarray>/is', '', $html);
16378		}
16379		// extract external CSS files
16380		$matches = array();
16381		if (preg_match_all('/<link([^\>]*?)>/is', $html, $matches) > 0) {
16382			foreach ($matches[1] as $key => $link) {
16383				$type = array();
16384				if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
16385					$type = array();
16386					preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
16387					// get 'all' and 'print' media, other media types are discarded
16388					// (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16389					if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16390						$type = array();
16391						if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
16392							// read CSS data file
16393                            $cssdata = $this->getCachedFileContents(trim($type[1]));
16394							if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) {
16395								$css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16396							}
16397						}
16398					}
16399				}
16400			}
16401		}
16402		// extract style tags
16403		$matches = array();
16404		if (preg_match_all('/<style([^\>]*?)>([^\<]*?)<\/style>/is', $html, $matches) > 0) {
16405			foreach ($matches[1] as $key => $media) {
16406				$type = array();
16407				preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
16408				// get 'all' and 'print' media, other media types are discarded
16409				// (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16410				if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16411					$cssdata = $matches[2][$key];
16412					$css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16413				}
16414			}
16415		}
16416		// create a special tag to contain the CSS array (used for table content)
16417		$csstagarray = '<cssarray>'.htmlentities(json_encode($css)).'</cssarray>';
16418		// remove head and style blocks
16419		$html = preg_replace('/<head([^\>]*?)>(.*?)<\/head>/is', '', $html);
16420		$html = preg_replace('/<style([^\>]*?)>([^\<]*?)<\/style>/is', '', $html);
16421		// define block tags
16422		$blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
16423		// define self-closing tags
16424		$selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
16425		// remove all unsupported tags (the line below lists all supported tags)
16426		$html = strip_tags($html, '<marker/><a><b><blockquote><body><br><br/><dd><del><div><dl><dt><em><font><form><h1><h2><h3><h4><h5><h6><hr><hr/><i><img><input><label><li><ol><option><p><pre><s><select><small><span><strike><strong><sub><sup><table><tablehead><tcpdf><td><textarea><th><thead><tr><tt><u><ul>');
16427		//replace some blank characters
16428		$html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
16429		$html = preg_replace('/<(table|tr|td|th|tcpdf|blockquote|dd|div|dl|dt|form|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^\>]*)>[\n\r\t]+/', '<\\1\\2>', $html);
16430		$html = preg_replace('@(\r\n|\r)@', "\n", $html);
16431		$repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
16432		$html = strtr($html, $repTable);
16433		$offset = 0;
16434		while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
16435			$html_a = substr($html, 0, $offset);
16436			$html_b = substr($html, $offset, ($pos - $offset + 6));
16437			while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
16438				// preserve newlines on <pre> tag
16439				$html_b = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
16440			}
16441			while (preg_match("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) {
16442				// preserve spaces on <pre> tag
16443				$html_b = preg_replace("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2&nbsp;\\3</pre>", $html_b);
16444			}
16445			$html = $html_a.$html_b.substr($html, $pos + 6);
16446			$offset = strlen($html_a.$html_b);
16447		}
16448		$offset = 0;
16449		while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
16450			$html_a = substr($html, 0, $offset);
16451			$html_b = substr($html, $offset, ($pos - $offset + 11));
16452			while (preg_match("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
16453				// preserve newlines on <textarea> tag
16454				$html_b = preg_replace("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
16455				$html_b = preg_replace("'<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
16456			}
16457			$html = $html_a.$html_b.substr($html, $pos + 11);
16458			$offset = strlen($html_a.$html_b);
16459		}
16460		$html = preg_replace('/([\s]*)<option/si', '<option', $html);
16461		$html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
16462		$offset = 0;
16463		while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
16464			$html_a = substr($html, 0, $offset);
16465			$html_b = substr($html, $offset, ($pos - $offset + 9));
16466			while (preg_match("'<option([^\>]*)>(.*?)</option>'si", $html_b)) {
16467				$html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
16468				$html_b = preg_replace("'<option([^\>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
16469			}
16470			$html = $html_a.$html_b.substr($html, $pos + 9);
16471			$offset = strlen($html_a.$html_b);
16472		}
16473		if (preg_match("'</select'si", $html)) {
16474			$html = preg_replace("'<select([^\>]*)>'si", "<select\\1 opt=\"", $html);
16475			$html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
16476		}
16477		$html = str_replace("\n", ' ', $html);
16478		// restore textarea newlines
16479		$html = str_replace('<TBR>', "\n", $html);
16480		// remove extra spaces from code
16481		$html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
16482		$html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html);
16483		$html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
16484		$html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html);
16485		$html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|dt|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html);
16486		$html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
16487		$html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
16488		$html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html);
16489		$html = preg_replace('/<img([^\>]*)>[\s]+([^\<])/xi', '<img\\1>&nbsp;\\2', $html);
16490		$html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
16491		$html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
16492		$html = preg_replace('/<textarea([^\>]*)>([^\<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
16493		$html = preg_replace('/<li([^\>]*)><\/li>/', '<li\\1>&nbsp;</li>', $html);
16494		$html = preg_replace('/<li([^\>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1">&nbsp;</font><img', $html);
16495		$html = preg_replace('/<([^\>\/]*)>[\s]/', '<\\1>&nbsp;', $html); // preserve some spaces
16496		$html = preg_replace('/[\s]<\/([^\>]*)>/', '&nbsp;</\\1>', $html); // preserve some spaces
16497		$html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment
16498		$html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment
16499		$html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space
16500		// trim string
16501		$html = $this->stringTrim($html);
16502		// fix br tag after li
16503		$html = preg_replace('/<li><br([^\>]*)>/', '<li> <br\\1>', $html);
16504		// fix first image tag alignment
16505		$html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
16506		// pattern for generic tag
16507		$tagpattern = '/(<[^>]+>)/';
16508		// explodes the string
16509		$a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
16510		// count elements
16511		$maxel = count($a);
16512		$elkey = 0;
16513		$key = 0;
16514		// create an array of elements
16515		$dom = array();
16516		$dom[$key] = array();
16517		// set inheritable properties fot the first void element
16518		// possible inheritable properties are: azimuth, border-collapse, border-spacing, caption-side, color, cursor, direction, empty-cells, font, font-family, font-stretch, font-size, font-size-adjust, font-style, font-variant, font-weight, letter-spacing, line-height, list-style, list-style-image, list-style-position, list-style-type, orphans, page, page-break-inside, quotes, speak, speak-header, text-align, text-indent, text-transform, volume, white-space, widows, word-spacing
16519		$dom[$key]['tag'] = false;
16520		$dom[$key]['block'] = false;
16521		$dom[$key]['value'] = '';
16522		$dom[$key]['parent'] = 0;
16523		$dom[$key]['hide'] = false;
16524		$dom[$key]['fontname'] = $this->FontFamily;
16525		$dom[$key]['fontstyle'] = $this->FontStyle;
16526		$dom[$key]['fontsize'] = $this->FontSizePt;
16527		$dom[$key]['font-stretch'] = $this->font_stretching;
16528		$dom[$key]['letter-spacing'] = $this->font_spacing;
16529		$dom[$key]['stroke'] = $this->textstrokewidth;
16530		$dom[$key]['fill'] = (($this->textrendermode % 2) == 0);
16531		$dom[$key]['clip'] = ($this->textrendermode > 3);
16532		$dom[$key]['line-height'] = $this->cell_height_ratio;
16533		$dom[$key]['bgcolor'] = false;
16534		$dom[$key]['fgcolor'] = $this->fgcolor; // color
16535		$dom[$key]['strokecolor'] = $this->strokecolor;
16536		$dom[$key]['align'] = '';
16537		$dom[$key]['listtype'] = '';
16538		$dom[$key]['text-indent'] = 0;
16539		$dom[$key]['text-transform'] = '';
16540		$dom[$key]['border'] = array();
16541		$dom[$key]['dir'] = $this->rtl?'rtl':'ltr';
16542		$thead = false; // true when we are inside the THEAD tag
16543		++$key;
16544		$level = array();
16545		array_push($level, 0); // root
16546		while ($elkey < $maxel) {
16547			$dom[$key] = array();
16548			$element = $a[$elkey];
16549			$dom[$key]['elkey'] = $elkey;
16550			if (preg_match($tagpattern, $element)) {
16551				// html tag
16552				$element = substr($element, 1, -1);
16553				// get tag name
16554				preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
16555				$tagname = strtolower($tag[1]);
16556				// check if we are inside a table header
16557				if ($tagname == 'thead') {
16558					if ($element[0] == '/') {
16559						$thead = false;
16560					} else {
16561						$thead = true;
16562					}
16563					++$elkey;
16564					continue;
16565				}
16566				$dom[$key]['tag'] = true;
16567				$dom[$key]['value'] = $tagname;
16568				if (in_array($dom[$key]['value'], $blocktags)) {
16569					$dom[$key]['block'] = true;
16570				} else {
16571					$dom[$key]['block'] = false;
16572				}
16573				if ($element[0] == '/') {
16574					// *** closing html tag
16575					$dom[$key]['opening'] = false;
16576					$dom[$key]['parent'] = end($level);
16577					array_pop($level);
16578					$dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
16579					$dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
16580					$dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
16581					$dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
16582					$dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
16583					$dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
16584					$dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
16585					$dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
16586					$dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
16587					$dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
16588					$dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
16589					$dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
16590					$dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
16591					$dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
16592					$dom[$key]['text-transform'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['text-transform'];
16593					$dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
16594					if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
16595						$dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
16596					}
16597					// set the number of columns in table tag
16598					if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
16599						$dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
16600					}
16601					if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
16602						$dom[($dom[$key]['parent'])]['content'] = $csstagarray;
16603						for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
16604							$dom[($dom[$key]['parent'])]['content'] .= stripslashes($a[$dom[$i]['elkey']]);
16605						}
16606						$key = $i;
16607						// mark nested tables
16608						$dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
16609						// remove thead sections from nested tables
16610						$dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
16611						$dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
16612					}
16613					// store header rows on a new table
16614					if (
16615						($dom[$key]['value'] === 'tr')
16616						&& !empty($dom[($dom[$key]['parent'])]['thead'])
16617						&& ($dom[($dom[$key]['parent'])]['thead'] === true)
16618					) {
16619						if (TCPDF_STATIC::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
16620							$dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
16621						}
16622						for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
16623							$dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
16624						}
16625						if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
16626							$dom[($dom[$key]['parent'])]['attribute'] = array();
16627						}
16628						// header elements must be always contained in a single page
16629						$dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
16630					}
16631					if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC::empty_string($dom[($dom[$key]['parent'])]['thead']))) {
16632						// remove the nobr attributes from the table header
16633						$dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
16634						$dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
16635					}
16636				} else {
16637					// *** opening or self-closing html tag
16638					$dom[$key]['opening'] = true;
16639					$dom[$key]['parent'] = end($level);
16640					if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
16641						// self-closing tag
16642						$dom[$key]['self'] = true;
16643					} else {
16644						// opening tag
16645						array_push($level, $key);
16646						$dom[$key]['self'] = false;
16647					}
16648					// copy some values from parent
16649					$parentkey = 0;
16650					if ($key > 0) {
16651						$parentkey = $dom[$key]['parent'];
16652						$dom[$key]['hide'] = $dom[$parentkey]['hide'];
16653						$dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
16654						$dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
16655						$dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
16656						$dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
16657						$dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
16658						$dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
16659						$dom[$key]['fill'] = $dom[$parentkey]['fill'];
16660						$dom[$key]['clip'] = $dom[$parentkey]['clip'];
16661						$dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16662						$dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
16663						$dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
16664						$dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
16665						$dom[$key]['align'] = $dom[$parentkey]['align'];
16666						$dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16667						$dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16668						$dom[$key]['text-transform'] = $dom[$parentkey]['text-transform'];
16669						$dom[$key]['border'] = array();
16670						$dom[$key]['dir'] = $dom[$parentkey]['dir'];
16671					}
16672					// get attributes
16673					preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER);
16674					$dom[$key]['attribute'] = array(); // reset attribute array
16675                    foreach($attr_array[1] as $id => $name) {
16676                        $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
16677                    }
16678					if (!empty($css)) {
16679						// merge CSS style to current style
16680						list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC::getCSSdataArray($dom, $key, $css);
16681						$dom[$key]['attribute']['style'] = TCPDF_STATIC::getTagStyleFromCSSarray($dom[$key]['cssdata']);
16682					}
16683					// split style attributes
16684					if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
16685						// get style attributes
16686						preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
16687						$dom[$key]['style'] = array(); // reset style attribute array
16688                        foreach($style_array[1] as $id => $name) {
16689                            // in case of duplicate attribute the last replace the previous
16690                            $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
16691                        }
16692						// --- get some style attributes ---
16693						// text direction
16694						if (isset($dom[$key]['style']['direction'])) {
16695							$dom[$key]['dir'] = $dom[$key]['style']['direction'];
16696						}
16697						// display
16698						if (isset($dom[$key]['style']['display'])) {
16699							$dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
16700						}
16701						// font family
16702						if (isset($dom[$key]['style']['font-family'])) {
16703							$dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
16704						}
16705						// list-style-type
16706						if (isset($dom[$key]['style']['list-style-type'])) {
16707							$dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
16708							if ($dom[$key]['listtype'] == 'inherit') {
16709								$dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16710							}
16711						}
16712						// text-indent
16713						if (isset($dom[$key]['style']['text-indent'])) {
16714							$dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
16715							if ($dom[$key]['text-indent'] == 'inherit') {
16716								$dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16717							}
16718						}
16719						// text-transform
16720						if (isset($dom[$key]['style']['text-transform'])) {
16721							$dom[$key]['text-transform'] = $dom[$key]['style']['text-transform'];
16722						}
16723						// font size
16724						if (isset($dom[$key]['style']['font-size'])) {
16725							$fsize = trim($dom[$key]['style']['font-size']);
16726							$dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt');
16727						}
16728						// font-stretch
16729						if (isset($dom[$key]['style']['font-stretch'])) {
16730							$dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
16731						}
16732						// letter-spacing
16733						if (isset($dom[$key]['style']['letter-spacing'])) {
16734							$dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
16735						}
16736						// line-height (internally is the cell height ratio)
16737						if (isset($dom[$key]['style']['line-height'])) {
16738							$lineheight = trim($dom[$key]['style']['line-height']);
16739							switch ($lineheight) {
16740								// A normal line height. This is default
16741								case 'normal': {
16742									$dom[$key]['line-height'] = $dom[0]['line-height'];
16743									break;
16744								}
16745								case 'inherit': {
16746									$dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16747								}
16748								default: {
16749									if (is_numeric($lineheight)) {
16750										// convert to percentage of font height
16751										$lineheight = ($lineheight * 100).'%';
16752									}
16753									$dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
16754									if (substr($lineheight, -1) !== '%') {
16755										if ($dom[$key]['fontsize'] <= 0) {
16756											$dom[$key]['line-height'] = 1;
16757										} else {
16758											$dom[$key]['line-height'] = (($dom[$key]['line-height'] - $this->cell_padding['T'] - $this->cell_padding['B']) / $dom[$key]['fontsize']);
16759										}
16760									}
16761								}
16762							}
16763						}
16764						// font style
16765						if (isset($dom[$key]['style']['font-weight'])) {
16766							if (strtolower($dom[$key]['style']['font-weight'][0]) == 'n') {
16767								if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
16768									$dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
16769								}
16770							} elseif (strtolower($dom[$key]['style']['font-weight'][0]) == 'b') {
16771								$dom[$key]['fontstyle'] .= 'B';
16772							}
16773						}
16774						if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style'][0]) == 'i')) {
16775							$dom[$key]['fontstyle'] .= 'I';
16776						}
16777						// font color
16778						if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['color']))) {
16779							$dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors);
16780						} elseif ($dom[$key]['value'] == 'a') {
16781							$dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16782						}
16783						// background color
16784						if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['background-color']))) {
16785							$dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors);
16786						}
16787						// text-decoration
16788						if (isset($dom[$key]['style']['text-decoration'])) {
16789							$decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
16790							foreach ($decors as $dec) {
16791								$dec = trim($dec);
16792								if (!TCPDF_STATIC::empty_string($dec)) {
16793									if ($dec[0] == 'u') {
16794										// underline
16795										$dom[$key]['fontstyle'] .= 'U';
16796									} elseif ($dec[0] == 'l') {
16797										// line-through
16798										$dom[$key]['fontstyle'] .= 'D';
16799									} elseif ($dec[0] == 'o') {
16800										// overline
16801										$dom[$key]['fontstyle'] .= 'O';
16802									}
16803								}
16804							}
16805						} elseif ($dom[$key]['value'] == 'a') {
16806							$dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16807						}
16808						// check for width attribute
16809						if (isset($dom[$key]['style']['width'])) {
16810							$dom[$key]['width'] = $dom[$key]['style']['width'];
16811						}
16812						// check for height attribute
16813						if (isset($dom[$key]['style']['height'])) {
16814							$dom[$key]['height'] = $dom[$key]['style']['height'];
16815						}
16816						// check for text alignment
16817						if (isset($dom[$key]['style']['text-align'])) {
16818							$dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align'][0]);
16819						}
16820						// check for CSS border properties
16821						if (isset($dom[$key]['style']['border'])) {
16822							$borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
16823							if (!empty($borderstyle)) {
16824								$dom[$key]['border']['LTRB'] = $borderstyle;
16825							}
16826						}
16827						if (isset($dom[$key]['style']['border-color'])) {
16828							$brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
16829							if (isset($brd_colors[3])) {
16830								$dom[$key]['border']['L']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[3], $this->spot_colors);
16831							}
16832							if (isset($brd_colors[1])) {
16833								$dom[$key]['border']['R']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[1], $this->spot_colors);
16834							}
16835							if (isset($brd_colors[0])) {
16836								$dom[$key]['border']['T']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[0], $this->spot_colors);
16837							}
16838							if (isset($brd_colors[2])) {
16839								$dom[$key]['border']['B']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[2], $this->spot_colors);
16840							}
16841						}
16842						if (isset($dom[$key]['style']['border-width'])) {
16843							$brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
16844							if (isset($brd_widths[3])) {
16845								$dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
16846							}
16847							if (isset($brd_widths[1])) {
16848								$dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
16849							}
16850							if (isset($brd_widths[0])) {
16851								$dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
16852							}
16853							if (isset($brd_widths[2])) {
16854								$dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
16855							}
16856						}
16857						if (isset($dom[$key]['style']['border-style'])) {
16858							$brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
16859							if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) {
16860								$dom[$key]['border']['L']['cap'] = 'square';
16861								$dom[$key]['border']['L']['join'] = 'miter';
16862								$dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
16863								if ($dom[$key]['border']['L']['dash'] < 0) {
16864									$dom[$key]['border']['L'] = array();
16865								}
16866							}
16867							if (isset($brd_styles[1])) {
16868								$dom[$key]['border']['R']['cap'] = 'square';
16869								$dom[$key]['border']['R']['join'] = 'miter';
16870								$dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
16871								if ($dom[$key]['border']['R']['dash'] < 0) {
16872									$dom[$key]['border']['R'] = array();
16873								}
16874							}
16875							if (isset($brd_styles[0])) {
16876								$dom[$key]['border']['T']['cap'] = 'square';
16877								$dom[$key]['border']['T']['join'] = 'miter';
16878								$dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
16879								if ($dom[$key]['border']['T']['dash'] < 0) {
16880									$dom[$key]['border']['T'] = array();
16881								}
16882							}
16883							if (isset($brd_styles[2])) {
16884								$dom[$key]['border']['B']['cap'] = 'square';
16885								$dom[$key]['border']['B']['join'] = 'miter';
16886								$dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
16887								if ($dom[$key]['border']['B']['dash'] < 0) {
16888									$dom[$key]['border']['B'] = array();
16889								}
16890							}
16891						}
16892						$cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
16893						foreach ($cellside as $bsk => $bsv) {
16894							if (isset($dom[$key]['style']['border-'.$bsv])) {
16895								$borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
16896								if (!empty($borderstyle)) {
16897									$dom[$key]['border'][$bsk] = $borderstyle;
16898								}
16899							}
16900							if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
16901								$dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors);
16902							}
16903							if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
16904								$dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
16905							}
16906							if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
16907								$dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
16908								if ($dom[$key]['border'][$bsk]['dash'] < 0) {
16909									$dom[$key]['border'][$bsk] = array();
16910								}
16911							}
16912						}
16913						// check for CSS padding properties
16914						if (isset($dom[$key]['style']['padding'])) {
16915							$dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
16916						} else {
16917							$dom[$key]['padding'] = $this->cell_padding;
16918						}
16919						foreach ($cellside as $psk => $psv) {
16920							if (isset($dom[$key]['style']['padding-'.$psv])) {
16921								$dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
16922							}
16923						}
16924						// check for CSS margin properties
16925						if (isset($dom[$key]['style']['margin'])) {
16926							$dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
16927						} else {
16928							$dom[$key]['margin'] = $this->cell_margin;
16929						}
16930						foreach ($cellside as $psk => $psv) {
16931							if (isset($dom[$key]['style']['margin-'.$psv])) {
16932								$dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
16933							}
16934						}
16935						// check for CSS border-spacing properties
16936						if (isset($dom[$key]['style']['border-spacing'])) {
16937							$dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
16938						}
16939						// page-break-inside
16940						if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
16941							$dom[$key]['attribute']['nobr'] = 'true';
16942						}
16943						// page-break-before
16944						if (isset($dom[$key]['style']['page-break-before'])) {
16945							if ($dom[$key]['style']['page-break-before'] == 'always') {
16946								$dom[$key]['attribute']['pagebreak'] = 'true';
16947							} elseif ($dom[$key]['style']['page-break-before'] == 'left') {
16948								$dom[$key]['attribute']['pagebreak'] = 'left';
16949							} elseif ($dom[$key]['style']['page-break-before'] == 'right') {
16950								$dom[$key]['attribute']['pagebreak'] = 'right';
16951							}
16952						}
16953						// page-break-after
16954						if (isset($dom[$key]['style']['page-break-after'])) {
16955							if ($dom[$key]['style']['page-break-after'] == 'always') {
16956								$dom[$key]['attribute']['pagebreakafter'] = 'true';
16957							} elseif ($dom[$key]['style']['page-break-after'] == 'left') {
16958								$dom[$key]['attribute']['pagebreakafter'] = 'left';
16959							} elseif ($dom[$key]['style']['page-break-after'] == 'right') {
16960								$dom[$key]['attribute']['pagebreakafter'] = 'right';
16961							}
16962						}
16963					}
16964					if (isset($dom[$key]['attribute']['display'])) {
16965						$dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
16966					}
16967					if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
16968						$borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
16969						if (!empty($borderstyle)) {
16970							$dom[$key]['border']['LTRB'] = $borderstyle;
16971						}
16972					}
16973					// check for font tag
16974					if ($dom[$key]['value'] == 'font') {
16975						// font family
16976						if (isset($dom[$key]['attribute']['face'])) {
16977							$dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
16978						}
16979						// font size
16980						if (isset($dom[$key]['attribute']['size'])) {
16981							if ($key > 0) {
16982								if ($dom[$key]['attribute']['size'][0] == '+') {
16983									$dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
16984								} elseif ($dom[$key]['attribute']['size'][0] == '-') {
16985									$dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
16986								} else {
16987									$dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16988								}
16989							} else {
16990								$dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16991							}
16992						}
16993					}
16994					// force natural alignment for lists
16995					if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
16996						AND (!isset($dom[$key]['align']) OR TCPDF_STATIC::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
16997						if ($this->rtl) {
16998							$dom[$key]['align'] = 'R';
16999						} else {
17000							$dom[$key]['align'] = 'L';
17001						}
17002					}
17003					if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
17004						if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
17005							$dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
17006						}
17007					}
17008					if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
17009						$dom[$key]['fontstyle'] .= 'B';
17010					}
17011					if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
17012						$dom[$key]['fontstyle'] .= 'I';
17013					}
17014					if ($dom[$key]['value'] == 'u') {
17015						$dom[$key]['fontstyle'] .= 'U';
17016					}
17017					if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
17018						$dom[$key]['fontstyle'] .= 'D';
17019					}
17020					if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
17021						$dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
17022					}
17023					if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
17024						$dom[$key]['fontname'] = $this->default_monospaced_font;
17025					}
17026					if (!empty($dom[$key]['value']) AND ($dom[$key]['value'][0] == 'h') AND (intval($dom[$key]['value'][1]) > 0) AND (intval($dom[$key]['value'][1]) < 7)) {
17027						// headings h1, h2, h3, h4, h5, h6
17028						if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
17029							$headsize = (4 - intval($dom[$key]['value'][1])) * 2;
17030							$dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
17031						}
17032						if (!isset($dom[$key]['style']['font-weight'])) {
17033							$dom[$key]['fontstyle'] .= 'B';
17034						}
17035					}
17036					if (($dom[$key]['value'] == 'table')) {
17037						$dom[$key]['rows'] = 0; // number of rows
17038						$dom[$key]['trids'] = array(); // IDs of TR elements
17039						$dom[$key]['thead'] = ''; // table header rows
17040					}
17041					if (($dom[$key]['value'] == 'tr')) {
17042						$dom[$key]['cols'] = 0;
17043						if ($thead) {
17044							$dom[$key]['thead'] = true;
17045							// rows on thead block are printed as a separate table
17046						} else {
17047							$dom[$key]['thead'] = false;
17048							$parent = $dom[$key]['parent'];
17049
17050							if (!isset($dom[$parent]['rows'])) {
17051								$dom[$parent]['rows'] = 0;
17052							}
17053							// store the number of rows on table element
17054							++$dom[$parent]['rows'];
17055
17056							if (!isset($dom[$parent]['trids'])) {
17057								$dom[$parent]['trids'] = array();
17058							}
17059
17060							// store the TR elements IDs on table element
17061							array_push($dom[$parent]['trids'], $key);
17062						}
17063					}
17064					if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
17065						if (isset($dom[$key]['attribute']['colspan'])) {
17066							$colspan = intval($dom[$key]['attribute']['colspan']);
17067						} else {
17068							$colspan = 1;
17069						}
17070						$dom[$key]['attribute']['colspan'] = $colspan;
17071						$dom[($dom[$key]['parent'])]['cols'] += $colspan;
17072					}
17073					// text direction
17074					if (isset($dom[$key]['attribute']['dir'])) {
17075						$dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
17076					}
17077					// set foreground color attribute
17078					if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['color']))) {
17079						$dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors);
17080					} elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
17081						$dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
17082					}
17083					// set background color attribute
17084					if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['bgcolor']))) {
17085						$dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors);
17086					}
17087					// set stroke color attribute
17088					if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['strokecolor']))) {
17089						$dom[$key]['strokecolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors);
17090					}
17091					// check for width attribute
17092					if (isset($dom[$key]['attribute']['width'])) {
17093						$dom[$key]['width'] = $dom[$key]['attribute']['width'];
17094					}
17095					// check for height attribute
17096					if (isset($dom[$key]['attribute']['height'])) {
17097						$dom[$key]['height'] = $dom[$key]['attribute']['height'];
17098					}
17099					// check for text alignment
17100					if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
17101						$dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align'][0]);
17102					}
17103					// check for text rendering mode (the following attributes do not exist in HTML)
17104					if (isset($dom[$key]['attribute']['stroke'])) {
17105						// font stroke width
17106						$dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
17107					}
17108					if (isset($dom[$key]['attribute']['fill'])) {
17109						// font fill
17110						if ($dom[$key]['attribute']['fill'] == 'true') {
17111							$dom[$key]['fill'] = true;
17112						} else {
17113							$dom[$key]['fill'] = false;
17114						}
17115					}
17116					if (isset($dom[$key]['attribute']['clip'])) {
17117						// clipping mode
17118						if ($dom[$key]['attribute']['clip'] == 'true') {
17119							$dom[$key]['clip'] = true;
17120						} else {
17121							$dom[$key]['clip'] = false;
17122						}
17123					}
17124				} // end opening tag
17125			} else {
17126				// text
17127				$dom[$key]['tag'] = false;
17128				$dom[$key]['block'] = false;
17129				$dom[$key]['parent'] = end($level);
17130				$dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
17131				if (!empty($dom[$dom[$key]['parent']]['text-transform'])) {
17132					// text-transform for unicode requires mb_convert_case (Multibyte String Functions)
17133					if (function_exists('mb_convert_case')) {
17134						$ttm = array('capitalize' => MB_CASE_TITLE, 'uppercase' => MB_CASE_UPPER, 'lowercase' => MB_CASE_LOWER);
17135						if (isset($ttm[$dom[$dom[$key]['parent']]['text-transform']])) {
17136							$element = mb_convert_case($element, $ttm[$dom[$dom[$key]['parent']]['text-transform']], $this->encoding);
17137						}
17138					} elseif (!$this->isunicode) {
17139						switch ($dom[$dom[$key]['parent']]['text-transform']) {
17140							case 'capitalize': {
17141								$element = ucwords(strtolower($element));
17142								break;
17143							}
17144							case 'uppercase': {
17145								$element = strtoupper($element);
17146								break;
17147							}
17148							case 'lowercase': {
17149								$element = strtolower($element);
17150								break;
17151							}
17152						}
17153					}
17154					$element = preg_replace("/&NBSP;/i", "&nbsp;", $element);
17155				}
17156				$dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
17157			}
17158			++$elkey;
17159			++$key;
17160		}
17161		return $dom;
17162	}
17163
17164	/**
17165	 * Returns the string used to find spaces
17166	 * @return string
17167	 * @protected
17168	 * @author Nicola Asuni
17169	 * @since 4.8.024 (2010-01-15)
17170	 */
17171	protected function getSpaceString() {
17172		$spacestr = chr(32);
17173		if ($this->isUnicodeFont()) {
17174			$spacestr = chr(0).chr(32);
17175		}
17176		return $spacestr;
17177	}
17178
17179	/**
17180	 * Return an hash code used to ensure that the serialized data has been generated by this TCPDF instance.
17181	 * @param string $data serialized data
17182	 * @return string
17183	 * @public static
17184	 */
17185	protected function getHashForTCPDFtagParams($data) {
17186		return md5(strlen($data).$this->file_id.$data);
17187	}
17188
17189	/**
17190	 * Serialize an array of parameters to be used with TCPDF tag in HTML code.
17191	 * @param array $data parameters array
17192	 * @return string containing serialized data
17193	 * @public static
17194	 */
17195	public function serializeTCPDFtagParameters($data) {
17196		$encoded = urlencode(json_encode($data));
17197		return $this->getHashForTCPDFtagParams($encoded).$encoded;
17198	}
17199
17200	/**
17201	 * Unserialize parameters to be used with TCPDF tag in HTML code.
17202	 * @param string $data serialized data
17203	 * @return array containing unserialized data
17204	 * @protected static
17205	 */
17206	protected function unserializeTCPDFtagParameters($data) {
17207		$hash = substr($data, 0, 32);
17208		$encoded = substr($data, 32);
17209		if ($hash != $this->getHashForTCPDFtagParams($encoded)) {
17210			$this->Error('Invalid parameters');
17211		}
17212		return json_decode(urldecode($encoded), true);
17213	}
17214
17215	/**
17216	 * Prints a cell (rectangular area) with optional borders, background color and html text string.
17217	 * The upper-left corner of the cell corresponds to the current position. After the call, the current position moves to the right or to the next line.<br />
17218	 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
17219	 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
17220	 * Supported tags are: a, b, blockquote, br, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, li, ol, p, pre, small, span, strong, sub, sup, table, tcpdf, td, th, thead, tr, tt, u, ul
17221	 * NOTE: all the HTML attributes must be enclosed in double-quote.
17222	 * @param float $w Cell width. If 0, the cell extends up to the right margin.
17223	 * @param float $h Cell minimum height. The cell extends automatically if needed.
17224	 * @param float|null $x upper-left corner X coordinate
17225	 * @param float|null $y upper-left corner Y coordinate
17226	 * @param string $html html text to print. Default value: empty string.
17227	 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
17228	 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL language)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>
17229Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
17230	 * @param boolean $fill Indicates if the cell background must be painted (true) or transparent (false).
17231	 * @param boolean $reseth if true reset the last cell height (default true).
17232	 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
17233	 * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width.
17234	 * @see Multicell(), writeHTML()
17235	 * @public
17236	 */
17237	public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
17238		return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
17239	}
17240
17241	/**
17242	 * Allows to preserve some HTML formatting (limited support).<br />
17243	 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
17244	 * Supported tags are: a, b, blockquote, br, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, li, ol, p, pre, small, span, strong, sub, sup, table, tcpdf, td, th, thead, tr, tt, u, ul
17245	 * NOTE: all the HTML attributes must be enclosed in double-quote.
17246	 * @param string $html text to display
17247	 * @param boolean $ln if true add a new line after text (default = true)
17248	 * @param boolean $fill Indicates if the background must be painted (true) or transparent (false).
17249	 * @param boolean $reseth if true reset the last cell height (default false).
17250	 * @param boolean $cell if true add the current left (or right for RTL) padding to each Write (default false).
17251	 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
17252	 * @public
17253	 */
17254	public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
17255		$gvars = $this->getGraphicVars();
17256		// store current values
17257		$prev_cell_margin = $this->cell_margin;
17258		$prev_cell_padding = $this->cell_padding;
17259		$prevPage = $this->page;
17260		$prevlMargin = $this->lMargin;
17261		$prevrMargin = $this->rMargin;
17262		$curfontname = $this->FontFamily;
17263		$curfontstyle = $this->FontStyle;
17264		$curfontsize = $this->FontSizePt;
17265		$curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
17266		$curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
17267		$curfontstretcing = $this->font_stretching;
17268		$curfonttracking = $this->font_spacing;
17269		$this->newline = true;
17270		$newline = true;
17271		$startlinepage = $this->page;
17272		$minstartliney = $this->y;
17273		$maxbottomliney = 0;
17274		$startlinex = $this->x;
17275		$startliney = $this->y;
17276		$yshift = 0;
17277		$loop = 0;
17278		$curpos = 0;
17279		$this_method_vars = array();
17280		$undo = false;
17281		$fontaligned = false;
17282		$reverse_dir = false; // true when the text direction is reversed
17283		$this->premode = false;
17284		if ($this->inxobj) {
17285			// we are inside an XObject template
17286			$pask = count($this->xobjects[$this->xobjid]['annotations']);
17287		} elseif (isset($this->PageAnnots[$this->page])) {
17288			$pask = count($this->PageAnnots[$this->page]);
17289		} else {
17290			$pask = 0;
17291		}
17292		if ($this->inxobj) {
17293			// we are inside an XObject template
17294			$startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17295		} elseif (!$this->InFooter) {
17296			if (isset($this->footerlen[$this->page])) {
17297				$this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17298			} else {
17299				$this->footerpos[$this->page] = $this->pagelen[$this->page];
17300			}
17301			$startlinepos = $this->footerpos[$this->page];
17302		} else {
17303			// we are inside the footer
17304			$startlinepos = $this->pagelen[$this->page];
17305		}
17306		$lalign = $align;
17307		$plalign = $align;
17308		if ($this->rtl) {
17309			$w = $this->x - $this->lMargin;
17310		} else {
17311			$w = $this->w - $this->rMargin - $this->x;
17312		}
17313		$w -= ($this->cell_padding['L'] + $this->cell_padding['R']);
17314		if ($cell) {
17315			if ($this->rtl) {
17316				$this->x -= $this->cell_padding['R'];
17317				$this->lMargin += $this->cell_padding['L'];
17318			} else {
17319				$this->x += $this->cell_padding['L'];
17320				$this->rMargin += $this->cell_padding['R'];
17321			}
17322		}
17323		if ($this->customlistindent >= 0) {
17324			$this->listindent = $this->customlistindent;
17325		} else {
17326			$this->listindent = $this->GetStringWidth('000000');
17327		}
17328		$this->listindentlevel = 0;
17329		// save previous states
17330		$prev_cell_height_ratio = $this->cell_height_ratio;
17331		$prev_listnum = $this->listnum;
17332		$prev_listordered = $this->listordered;
17333		$prev_listcount = $this->listcount;
17334		$prev_lispacer = $this->lispacer;
17335		$this->listnum = 0;
17336		$this->listordered = array();
17337		$this->listcount = array();
17338		$this->lispacer = '';
17339		if ((TCPDF_STATIC::empty_string($this->lasth)) OR ($reseth)) {
17340			// reset row height
17341			$this->resetLastH();
17342		}
17343		$dom = $this->getHtmlDomArray($html);
17344		$maxel = count($dom);
17345		$key = 0;
17346		while ($key < $maxel) {
17347			if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND $dom[$key]['hide']) {
17348				// store the node key
17349				$hidden_node_key = $key;
17350				if ($dom[$key]['self']) {
17351					// skip just this self-closing tag
17352					++$key;
17353				} else {
17354					// skip this and all children tags
17355					while (($key < $maxel) AND (!$dom[$key]['tag'] OR $dom[$key]['opening'] OR ($dom[$key]['parent'] != $hidden_node_key))) {
17356						// skip hidden objects
17357						++$key;
17358					}
17359					++$key;
17360				}
17361			}
17362			if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
17363				// check for pagebreak
17364				if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
17365					// add a page (or trig AcceptPageBreak() for multicolumn mode)
17366					$this->checkPageBreak($this->PageBreakTrigger + 1);
17367					$this->htmlvspace = ($this->PageBreakTrigger + 1);
17368				}
17369				if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
17370					OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
17371					// add a page (or trig AcceptPageBreak() for multicolumn mode)
17372					$this->checkPageBreak($this->PageBreakTrigger + 1);
17373					$this->htmlvspace = ($this->PageBreakTrigger + 1);
17374				}
17375			}
17376			if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
17377				if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
17378					$dom[$key]['attribute']['nobr'] = false;
17379				} else {
17380					// store current object
17381					$this->startTransaction();
17382					// save this method vars
17383					$this_method_vars['html'] = $html;
17384					$this_method_vars['ln'] = $ln;
17385					$this_method_vars['fill'] = $fill;
17386					$this_method_vars['reseth'] = $reseth;
17387					$this_method_vars['cell'] = $cell;
17388					$this_method_vars['align'] = $align;
17389					$this_method_vars['gvars'] = $gvars;
17390					$this_method_vars['prevPage'] = $prevPage;
17391					$this_method_vars['prev_cell_margin'] = $prev_cell_margin;
17392					$this_method_vars['prev_cell_padding'] = $prev_cell_padding;
17393					$this_method_vars['prevlMargin'] = $prevlMargin;
17394					$this_method_vars['prevrMargin'] = $prevrMargin;
17395					$this_method_vars['curfontname'] = $curfontname;
17396					$this_method_vars['curfontstyle'] = $curfontstyle;
17397					$this_method_vars['curfontsize'] = $curfontsize;
17398					$this_method_vars['curfontascent'] = $curfontascent;
17399					$this_method_vars['curfontdescent'] = $curfontdescent;
17400					$this_method_vars['curfontstretcing'] = $curfontstretcing;
17401					$this_method_vars['curfonttracking'] = $curfonttracking;
17402					$this_method_vars['minstartliney'] = $minstartliney;
17403					$this_method_vars['maxbottomliney'] = $maxbottomliney;
17404					$this_method_vars['yshift'] = $yshift;
17405					$this_method_vars['startlinepage'] = $startlinepage;
17406					$this_method_vars['startlinepos'] = $startlinepos;
17407					$this_method_vars['startlinex'] = $startlinex;
17408					$this_method_vars['startliney'] = $startliney;
17409					$this_method_vars['newline'] = $newline;
17410					$this_method_vars['loop'] = $loop;
17411					$this_method_vars['curpos'] = $curpos;
17412					$this_method_vars['pask'] = $pask;
17413					$this_method_vars['lalign'] = $lalign;
17414					$this_method_vars['plalign'] = $plalign;
17415					$this_method_vars['w'] = $w;
17416					$this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
17417					$this_method_vars['prev_listnum'] = $prev_listnum;
17418					$this_method_vars['prev_listordered'] = $prev_listordered;
17419					$this_method_vars['prev_listcount'] = $prev_listcount;
17420					$this_method_vars['prev_lispacer'] = $prev_lispacer;
17421					$this_method_vars['fontaligned'] = $fontaligned;
17422					$this_method_vars['key'] = $key;
17423					$this_method_vars['dom'] = $dom;
17424				}
17425			}
17426			// print THEAD block
17427			if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
17428				if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC::empty_string($dom[$dom[$key]['parent']]['thead'])) {
17429					$this->inthead = true;
17430					// print table header (thead)
17431					$this->writeHTML($this->thead, false, false, false, false, '');
17432					// check if we are on a new page or on a new column
17433					if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
17434						// we are on a new page or on a new column and the total object height is less than the available vertical space.
17435						// restore previous object
17436						$this->rollbackTransaction(true);
17437						// restore previous values
17438						foreach ($this_method_vars as $vkey => $vval) {
17439							$$vkey = $vval;
17440						}
17441						// disable table header
17442						$tmp_thead = $this->thead;
17443						$this->thead = '';
17444						// add a page (or trig AcceptPageBreak() for multicolumn mode)
17445						$pre_y = $this->y;
17446						if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
17447							// fix for multicolumn mode
17448							$startliney = $this->y;
17449						}
17450						$this->start_transaction_page = $this->page;
17451						$this->start_transaction_y = $this->y;
17452						// restore table header
17453						$this->thead = $tmp_thead;
17454						// fix table border properties
17455						if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
17456							$tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
17457						} elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
17458							$tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
17459						} else {
17460							$tmp_cellspacing = 0;
17461						}
17462						$dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
17463						$dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
17464						$dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
17465						$xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']);
17466						$dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
17467						$dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
17468						// print table header (thead)
17469						$this->writeHTML($this->thead, false, false, false, false, '');
17470					}
17471				}
17472				// move $key index forward to skip THEAD block
17473				while ( ($key < $maxel) AND (!(
17474					($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
17475					OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
17476					++$key;
17477				}
17478			}
17479			if ($dom[$key]['tag'] OR ($key == 0)) {
17480				if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
17481					$dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
17482				}
17483				// vertically align image in line
17484				if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
17485					// get image height
17486					$imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], ($dom[$key]['fontsize'] / $this->k), 'px');
17487					$autolinebreak = false;
17488					if (!empty($dom[$key]['width'])) {
17489						$imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], ($dom[$key]['fontsize'] / $this->k), 'px', false);
17490						if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R']))
17491							AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L'])))
17492							OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) {
17493							// add automatic line break
17494							$autolinebreak = true;
17495							$this->Ln('', $cell);
17496							if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
17497								// go back to evaluate this line break
17498								--$key;
17499							}
17500						}
17501					}
17502					if (!$autolinebreak) {
17503						if ($this->inPageBody()) {
17504							$pre_y = $this->y;
17505							// check for page break
17506							if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
17507								// fix for multicolumn mode
17508								$startliney = $this->y;
17509							}
17510						}
17511						if ($this->page > $startlinepage) {
17512							// fix line splitted over two pages
17513							if (isset($this->footerlen[$startlinepage])) {
17514								$curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17515							}
17516							// line to be moved one page forward
17517							$pagebuff = $this->getPageBuffer($startlinepage);
17518							$linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17519							$tstart = substr($pagebuff, 0, $startlinepos);
17520							$tend = substr($this->getPageBuffer($startlinepage), $curpos);
17521							// remove line from previous page
17522							$this->setPageBuffer($startlinepage, $tstart.''.$tend);
17523							$pagebuff = $this->getPageBuffer($this->page);
17524							$tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17525							$tend = substr($pagebuff, $this->cntmrk[$this->page]);
17526							// add line start to current page
17527							$yshift = ($minstartliney - $this->y);
17528							if ($fontaligned) {
17529								$yshift += ($curfontsize / $this->k);
17530							}
17531							$try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17532							$this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17533							// shift the annotations and links
17534							if (isset($this->PageAnnots[$this->page])) {
17535								$next_pask = count($this->PageAnnots[$this->page]);
17536							} else {
17537								$next_pask = 0;
17538							}
17539							if (isset($this->PageAnnots[$startlinepage])) {
17540								foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17541									if ($pak >= $pask) {
17542										$this->PageAnnots[$this->page][] = $pac;
17543										unset($this->PageAnnots[$startlinepage][$pak]);
17544										$npak = count($this->PageAnnots[$this->page]) - 1;
17545										$this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17546									}
17547								}
17548							}
17549							$pask = $next_pask;
17550							$startlinepos = $this->cntmrk[$this->page];
17551							$startlinepage = $this->page;
17552							$startliney = $this->y;
17553							$this->newline = false;
17554						}
17555						$this->y += ($this->getCellHeight($curfontsize / $this->k) - ($curfontdescent * $this->cell_height_ratio) - $imgh);
17556						$minstartliney = min($this->y, $minstartliney);
17557						$maxbottomliney = ($startliney + $this->getCellHeight($curfontsize / $this->k));
17558					}
17559				} elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
17560					// account for different font size
17561					$pfontname = $curfontname;
17562					$pfontstyle = $curfontstyle;
17563					$pfontsize = $curfontsize;
17564					$fontname = (isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname);
17565					$fontstyle = (isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle);
17566					$fontsize = (isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize);
17567					$fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
17568					$fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
17569					if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
17570						OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17571						OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
17572						if (($key < ($maxel - 1)) AND (
17573								($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
17574								OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17575								OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize)
17576								AND ($fontsize >= 0) AND ($curfontsize >= 0)
17577								AND (($fontsize != $curfontsize) OR ($fontstyle != $curfontstyle) OR ($fontname != $curfontname)))
17578							)) {
17579							if ($this->page > $startlinepage) {
17580								// fix lines splitted over two pages
17581								if (isset($this->footerlen[$startlinepage])) {
17582									$curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17583								}
17584								// line to be moved one page forward
17585								$pagebuff = $this->getPageBuffer($startlinepage);
17586								$linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17587								$tstart = substr($pagebuff, 0, $startlinepos);
17588								$tend = substr($this->getPageBuffer($startlinepage), $curpos);
17589								// remove line start from previous page
17590								$this->setPageBuffer($startlinepage, $tstart.''.$tend);
17591								$pagebuff = $this->getPageBuffer($this->page);
17592								$tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17593								$tend = substr($pagebuff, $this->cntmrk[$this->page]);
17594								// add line start to current page
17595								$yshift = ($minstartliney - $this->y);
17596								$try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17597								$this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17598								// shift the annotations and links
17599								if (isset($this->PageAnnots[$this->page])) {
17600									$next_pask = count($this->PageAnnots[$this->page]);
17601								} else {
17602									$next_pask = 0;
17603								}
17604								if (isset($this->PageAnnots[$startlinepage])) {
17605									foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17606										if ($pak >= $pask) {
17607											$this->PageAnnots[$this->page][] = $pac;
17608											unset($this->PageAnnots[$startlinepage][$pak]);
17609											$npak = count($this->PageAnnots[$this->page]) - 1;
17610											$this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17611										}
17612									}
17613								}
17614								$pask = $next_pask;
17615								$startlinepos = $this->cntmrk[$this->page];
17616								$startlinepage = $this->page;
17617								$startliney = $this->y;
17618							}
17619							if (!isset($dom[$key]['line-height'])) {
17620								$dom[$key]['line-height'] = $this->cell_height_ratio;
17621							}
17622							if (!$dom[$key]['block']) {
17623								if (!(isset($dom[($key + 1)]) AND $dom[($key + 1)]['tag'] AND (!$dom[($key + 1)]['opening']) AND ($dom[($key + 1)]['value'] != 'li') AND $dom[$key]['tag'] AND (!$dom[$key]['opening']))) {
17624									$this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
17625								}
17626								if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
17627									$current_line_align_data = array($key, $minstartliney, $maxbottomliney);
17628									if (isset($line_align_data) AND (($line_align_data[0] == ($key - 1)) OR (($line_align_data[0] == ($key - 2)) AND (isset($dom[($key - 1)])) AND (preg_match('/^([\s]+)$/', $dom[($key - 1)]['value']) > 0)))) {
17629										$minstartliney = min($this->y, $line_align_data[1]);
17630										$maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $line_align_data[2]);
17631									} else {
17632										$minstartliney = min($this->y, $minstartliney);
17633										$maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $maxbottomliney);
17634									}
17635									$line_align_data = $current_line_align_data;
17636								}
17637							}
17638							$this->cell_height_ratio = $dom[$key]['line-height'];
17639							$fontaligned = true;
17640						}
17641						$this->setFont($fontname, $fontstyle, $fontsize);
17642						// reset row height
17643						$this->resetLastH();
17644						$curfontname = $fontname;
17645						$curfontstyle = $fontstyle;
17646						$curfontsize = $fontsize;
17647						$curfontascent = $fontascent;
17648						$curfontdescent = $fontdescent;
17649					}
17650				}
17651				// set text rendering mode
17652				$textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
17653				$textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
17654				$textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
17655				$this->setTextRenderingMode($textstroke, $textfill, $textclip);
17656				if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
17657					$this->setFontStretching($dom[$key]['font-stretch']);
17658				}
17659				if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
17660					$this->setFontSpacing($dom[$key]['letter-spacing']);
17661				}
17662				if (($plalign == 'J') AND $dom[$key]['block']) {
17663					$plalign = '';
17664				}
17665				// get current position on page buffer
17666				$curpos = $this->pagelen[$startlinepage];
17667				if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
17668					$this->setFillColorArray($dom[$key]['bgcolor']);
17669					$wfill = true;
17670				} else {
17671					$wfill = $fill | false;
17672				}
17673				if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
17674					$this->setTextColorArray($dom[$key]['fgcolor']);
17675				}
17676				if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
17677					$this->setDrawColorArray($dom[$key]['strokecolor']);
17678				}
17679				if (isset($dom[$key]['align'])) {
17680					$lalign = $dom[$key]['align'];
17681				}
17682				if (TCPDF_STATIC::empty_string($lalign)) {
17683					$lalign = $align;
17684				}
17685			}
17686			// align lines
17687			if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
17688				$newline = true;
17689				$fontaligned = false;
17690				// we are at the beginning of a new line
17691				if (isset($startlinex)) {
17692					$yshift = ($minstartliney - $startliney);
17693					if (($yshift > 0) OR ($this->page > $startlinepage)) {
17694						$yshift = 0;
17695					}
17696					$t_x = 0;
17697					// the last line must be shifted to be aligned as requested
17698					$linew = abs($this->endlinex - $startlinex);
17699					if ($this->inxobj) {
17700						// we are inside an XObject template
17701						$pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
17702						if (isset($opentagpos)) {
17703							$midpos = $opentagpos;
17704						} else {
17705							$midpos = 0;
17706						}
17707						if ($midpos > 0) {
17708							$pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
17709							$pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
17710						} else {
17711							$pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
17712							$pend = '';
17713						}
17714					} else {
17715						$pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
17716						if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17717							$this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17718							$midpos = min($opentagpos, $this->footerpos[$startlinepage]);
17719						} elseif (isset($opentagpos)) {
17720							$midpos = $opentagpos;
17721						} elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17722							$this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17723							$midpos = $this->footerpos[$startlinepage];
17724						} else {
17725							$midpos = 0;
17726						}
17727						if ($midpos > 0) {
17728							$pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
17729							$pend = substr($this->getPageBuffer($startlinepage), $midpos);
17730						} else {
17731							$pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
17732							$pend = '';
17733						}
17734					}
17735					if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
17736						// calculate shifting amount
17737						$tw = $w;
17738						if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
17739							$tw += $this->cell_padding['R'];
17740						}
17741						if ($this->lMargin != $prevlMargin) {
17742							$tw += ($prevlMargin - $this->lMargin);
17743						}
17744						if ($this->rMargin != $prevrMargin) {
17745							$tw += ($prevrMargin - $this->rMargin);
17746						}
17747						$one_space_width = $this->GetStringWidth(chr(32));
17748						$no = 0; // number of spaces on a line contained on a single block
17749						if ($this->isRTLTextDir()) { // RTL
17750							// remove left space if exist
17751							$pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
17752							if ($pos1 > 0) {
17753								$pos1 = intval($pos1);
17754								if ($this->isUnicodeFont()) {
17755									$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
17756									$spacelen = 2;
17757								} else {
17758									$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
17759									$spacelen = 1;
17760								}
17761								if ($pos1 == $pos2) {
17762									$pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
17763									if (substr($pmid, $pos1, 4) == '[()]') {
17764										$linew -= $one_space_width;
17765									} elseif ($pos1 == strpos($pmid, '[(')) {
17766										$no = 1;
17767									}
17768								}
17769							}
17770						} else { // LTR
17771							// remove right space if exist
17772							$pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
17773							if ($pos1 > 0) {
17774								$pos1 = intval($pos1);
17775								if ($this->isUnicodeFont()) {
17776									$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
17777									$spacelen = 2;
17778								} else {
17779									$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
17780									$spacelen = 1;
17781								}
17782								if ($pos1 == $pos2) {
17783									$pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
17784									$linew -= $one_space_width;
17785								}
17786							}
17787						}
17788						$mdiff = ($tw - $linew);
17789						if ($plalign == 'C') {
17790							if ($this->rtl) {
17791								$t_x = -($mdiff / 2);
17792							} else {
17793								$t_x = ($mdiff / 2);
17794							}
17795						} elseif ($plalign == 'R') {
17796							// right alignment on LTR document
17797							$t_x = $mdiff;
17798						} elseif ($plalign == 'L') {
17799							// left alignment on RTL document
17800							$t_x = -$mdiff;
17801						} elseif (($plalign == 'J') AND ($plalign == $lalign)) {
17802							// Justification
17803							if ($this->isRTLTextDir()) {
17804								// align text on the left
17805								$t_x = -$mdiff;
17806							}
17807							$ns = 0; // number of spaces
17808							$pmidtemp = $pmid;
17809							// escape special characters
17810							$pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
17811							$pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
17812							// search spaces
17813							if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
17814								$spacestr = $this->getSpaceString();
17815								$maxkk = count($lnstring[1]) - 1;
17816								for ($kk=0; $kk <= $maxkk; ++$kk) {
17817									// restore special characters
17818									$lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
17819									$lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
17820									// store number of spaces on the strings
17821									$lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
17822									// count total spaces on line
17823									$ns += $lnstring[2][$kk];
17824									$lnstring[3][$kk] = $ns;
17825								}
17826								if ($ns == 0) {
17827									$ns = 1;
17828								}
17829								// calculate additional space to add to each existing space
17830								$spacewidth = ($mdiff / ($ns - $no)) * $this->k;
17831								if ($this->FontSize <= 0) {
17832									$this->FontSize = 1;
17833								}
17834								$spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize;
17835								if ($this->font_spacing != 0) {
17836									// fixed spacing mode
17837									$osw = -1000 * $this->font_spacing / $this->FontSize;
17838									$spacewidthu += $osw;
17839								}
17840								$nsmax = $ns;
17841								$ns = 0;
17842								reset($lnstring);
17843								$offset = 0;
17844								$strcount = 0;
17845								$prev_epsposbeg = 0;
17846								$textpos = 0;
17847								if ($this->isRTLTextDir()) {
17848									$textpos = $this->wPt;
17849								}
17850								while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
17851									// check if we are inside a string section '[( ... )]'
17852									$stroffset = strpos($pmid, '[(', $offset);
17853									if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
17854										// set offset to the end of string section
17855										$offset = strpos($pmid, ')]', $stroffset);
17856										while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
17857											$offset = strpos($pmid, ')]', ($offset + 1));
17858										}
17859										if ($offset === false) {
17860											$this->Error('HTML Justification: malformed PDF code.');
17861										}
17862										continue;
17863									}
17864									if ($this->isRTLTextDir()) {
17865										$spacew = ($spacewidth * ($nsmax - $ns));
17866									} else {
17867										$spacew = ($spacewidth * $ns);
17868									}
17869									$offset = $strpiece[2][1] + strlen($strpiece[2][0]);
17870									$epsposend = strpos($pmid, $this->epsmarker.'Q', $offset);
17871									if ($epsposend !== null) {
17872										$epsposend += strlen($this->epsmarker.'Q');
17873										$epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
17874										if ($epsposbeg === null) {
17875											$epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
17876											$prev_epsposbeg = $epsposbeg;
17877										}
17878										if (($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) {
17879											// shift EPS images
17880											$trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
17881											$pmid_b = substr($pmid, 0, $epsposbeg);
17882											$pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
17883											$pmid_e = substr($pmid, $epsposend);
17884											$pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
17885											$offset = $epsposend;
17886											continue;
17887										}
17888									}
17889									$currentxpos = 0;
17890									// shift blocks of code
17891									switch ($strpiece[2][0]) {
17892										case 'Td':
17893										case 'cm':
17894										case 'm':
17895										case 'l': {
17896											// get current X position
17897											preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
17898											if (!isset($xmatches[1])) {
17899												break;
17900											}
17901											$currentxpos = $xmatches[1];
17902											$textpos = $currentxpos;
17903											if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
17904												$ns = $lnstring[3][$strcount];
17905												if ($this->isRTLTextDir()) {
17906													$spacew = ($spacewidth * ($nsmax - $ns));
17907												}
17908												++$strcount;
17909											}
17910											// justify block
17911											if (preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $pmatch) == 1) {
17912												$newpmid = sprintf('%F',(floatval($pmatch[1]) + $spacew)).' '.$pmatch[2].' x*#!#*x'.$pmatch[3].$pmatch[4];
17913												$pmid = str_replace($pmatch[0], $newpmid, $pmid);
17914												unset($pmatch, $newpmid);
17915											}
17916											break;
17917										}
17918										case 're': {
17919											// justify block
17920											if (!TCPDF_STATIC::empty_string($this->lispacer)) {
17921												$this->lispacer = '';
17922												break;
17923											}
17924											preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
17925											if (!isset($xmatches[1])) {
17926												break;
17927											}
17928											$currentxpos = $xmatches[1];
17929											$x_diff = 0;
17930											$w_diff = 0;
17931											if ($this->isRTLTextDir()) { // RTL
17932												if ($currentxpos < $textpos) {
17933													$x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
17934													$w_diff = ($spacewidth * $lnstring[2][$strcount]);
17935												} else {
17936													if ($strcount > 0) {
17937														$x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
17938														$w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17939													}
17940												}
17941											} else { // LTR
17942												if ($currentxpos > $textpos) {
17943													if ($strcount > 0) {
17944														$x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
17945													}
17946													$w_diff = ($spacewidth * $lnstring[2][$strcount]);
17947												} else {
17948													if ($strcount > 1) {
17949														$x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
17950													}
17951													if ($strcount > 0) {
17952														$w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17953													}
17954												}
17955											}
17956											if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $pmatch) == 1) {
17957												$newx = sprintf('%F',(floatval($pmatch[1]) + $x_diff));
17958												$neww = sprintf('%F',(floatval($pmatch[3]) + $w_diff));
17959												$newpmid = $newx.' '.$pmatch[2].' '.$neww.' '.$pmatch[4].' x*#!#*x'.$pmatch[5].$pmatch[6];
17960												$pmid = str_replace($pmatch[0], $newpmid, $pmid);
17961												unset($pmatch, $newpmid, $newx, $neww);
17962											}
17963											break;
17964										}
17965										case 'c': {
17966											// get current X position
17967											preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', $pmid, $xmatches);
17968											if (!isset($xmatches[1])) {
17969												break;
17970											}
17971											$currentxpos = $xmatches[1];
17972											// justify block
17973											if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$xmatches[4].')[\s]('.$xmatches[5].')[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', $pmid, $pmatch) == 1) {
17974												$newx1 = sprintf('%F',(floatval($pmatch[1]) + $spacew));
17975												$newx2 = sprintf('%F',(floatval($pmatch[3]) + $spacew));
17976												$newx3 = sprintf('%F',(floatval($pmatch[5]) + $spacew));
17977												$newpmid = $newx1.' '.$pmatch[2].' '.$newx2.' '.$pmatch[4].' '.$newx3.' '.$pmatch[6].' x*#!#*x'.$pmatch[7].$pmatch[8];
17978												$pmid = str_replace($pmatch[0], $newpmid, $pmid);
17979												unset($pmatch, $newpmid, $newx1, $newx2, $newx3);
17980											}
17981											break;
17982										}
17983									}
17984									// shift the annotations and links
17985									$cxpos = ($currentxpos / $this->k);
17986									$lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps);
17987									if ($this->inxobj) {
17988										// we are inside an XObject template
17989										foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17990											if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17991												if ($cxpos > $lmpos) {
17992													$this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k);
17993													$this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17994												} else {
17995													$this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17996												}
17997												break;
17998											}
17999										}
18000									} elseif (isset($this->PageAnnots[$this->page])) {
18001										foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
18002											if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
18003												if ($cxpos > $lmpos) {
18004													$this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
18005													$this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
18006												} else {
18007													$this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
18008												}
18009												break;
18010											}
18011										}
18012									}
18013								} // end of while
18014								// remove markers
18015								$pmid = str_replace('x*#!#*x', '', $pmid);
18016								if ($this->isUnicodeFont()) {
18017									// multibyte characters
18018									$spacew = $spacewidthu;
18019									if ($this->font_stretching != 100) {
18020										// word spacing is affected by stretching
18021										$spacew /= ($this->font_stretching / 100);
18022									}
18023									// escape special characters
18024									$pos = 0;
18025									$pmid = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmid);
18026									$pmid = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmid);
18027									if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmid, $pamatch) > 0) {
18028										foreach($pamatch[0] as $pk => $pmatch) {
18029											$replace = $pamatch[1][$pk];
18030											$replace = str_replace('#!#OP#!#', '(', $replace);
18031											$replace = str_replace('#!#CP#!#', ')', $replace);
18032											$newpmid = '[('.str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacew).' (', $replace).')]';
18033											$pos = strpos($pmid, $pmatch, $pos);
18034											if ($pos !== FALSE) {
18035												$pmid = substr_replace($pmid, $newpmid, $pos, strlen($pmatch));
18036											}
18037											++$pos;
18038										}
18039										unset($pamatch);
18040									}
18041									if ($this->inxobj) {
18042										// we are inside an XObject template
18043										$this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend;
18044									} else {
18045										$this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
18046									}
18047									$endlinepos = strlen($pstart."\n".$pmid."\n");
18048								} else {
18049									// non-unicode (single-byte characters)
18050									if ($this->font_stretching != 100) {
18051										// word spacing (Tw) is affected by stretching
18052										$spacewidth /= ($this->font_stretching / 100);
18053									}
18054									$rs = sprintf('%F Tw', $spacewidth);
18055									$pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
18056									if ($this->inxobj) {
18057										// we are inside an XObject template
18058										$this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
18059									} else {
18060										$this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
18061									}
18062									$endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
18063								}
18064							}
18065						} // end of J
18066					} // end if $startlinex
18067					if (($t_x != 0) OR ($yshift < 0)) {
18068						// shift the line
18069						$trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
18070						$pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18071						$endlinepos = strlen($pstart);
18072						if ($this->inxobj) {
18073							// we are inside an XObject template
18074							$this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
18075							foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
18076								if ($pak >= $pask) {
18077									$this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
18078									$this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
18079								}
18080							}
18081						} else {
18082							$this->setPageBuffer($startlinepage, $pstart.$pend);
18083							// shift the annotations and links
18084							if (isset($this->PageAnnots[$this->page])) {
18085								foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
18086									if ($pak >= $pask) {
18087										$this->PageAnnots[$this->page][$pak]['x'] += $t_x;
18088										$this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
18089									}
18090								}
18091							}
18092						}
18093						$this->y -= $yshift;
18094					}
18095				}
18096				$pbrk = $this->checkPageBreak($this->lasth);
18097				$this->newline = false;
18098				$startlinex = $this->x;
18099				$startliney = $this->y;
18100				if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
18101					$startliney -= ((0.3 * $this->FontSizePt) / $this->k);
18102				} elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
18103					$startliney -= (($this->FontSizePt / 0.7) / $this->k);
18104				} else {
18105					$minstartliney = $startliney;
18106					$maxbottomliney = ($this->y + $this->getCellHeight($fontsize / $this->k));
18107				}
18108				$startlinepage = $this->page;
18109				if (isset($endlinepos) AND (!$pbrk)) {
18110					$startlinepos = $endlinepos;
18111				} else {
18112					if ($this->inxobj) {
18113						// we are inside an XObject template
18114						$startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
18115					} elseif (!$this->InFooter) {
18116						if (isset($this->footerlen[$this->page])) {
18117							$this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
18118						} else {
18119							$this->footerpos[$this->page] = $this->pagelen[$this->page];
18120						}
18121						$startlinepos = $this->footerpos[$this->page];
18122					} else {
18123						$startlinepos = $this->pagelen[$this->page];
18124					}
18125				}
18126				unset($endlinepos);
18127				$plalign = $lalign;
18128				if (isset($this->PageAnnots[$this->page])) {
18129					$pask = count($this->PageAnnots[$this->page]);
18130				} else {
18131					$pask = 0;
18132				}
18133				if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
18134					AND (isset($this->emptypagemrk[$this->page]))
18135					AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) {
18136					$this->setFont($fontname, $fontstyle, $fontsize);
18137					if ($wfill) {
18138						$this->setFillColorArray($this->bgcolor);
18139					}
18140				}
18141			} // end newline
18142			if (isset($opentagpos)) {
18143				unset($opentagpos);
18144			}
18145			if ($dom[$key]['tag']) {
18146				if ($dom[$key]['opening']) {
18147					// get text indentation (if any)
18148					if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
18149						$this->textindent = $dom[$key]['text-indent'];
18150						$this->newline = true;
18151					}
18152					// table
18153					if (($dom[$key]['value'] == 'table') AND isset($dom[$key]['cols']) AND ($dom[$key]['cols'] > 0)) {
18154						// available page width
18155						if ($this->rtl) {
18156							$wtmp = $this->x - $this->lMargin;
18157						} else {
18158							$wtmp = $this->w - $this->rMargin - $this->x;
18159						}
18160						// get cell spacing
18161						if (isset($dom[$key]['attribute']['cellspacing'])) {
18162							$clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
18163							$cellspacing = array('H' => $clsp, 'V' => $clsp);
18164						} elseif (isset($dom[$key]['border-spacing'])) {
18165							$cellspacing = $dom[$key]['border-spacing'];
18166						} else {
18167							$cellspacing = array('H' => 0, 'V' => 0);
18168						}
18169						// table width
18170						if (isset($dom[$key]['width'])) {
18171							$table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
18172						} else {
18173							$table_width = $wtmp;
18174						}
18175						$table_width -= (2 * $cellspacing['H']);
18176						if (!$this->inthead) {
18177							$this->y += $cellspacing['V'];
18178						}
18179						if ($this->rtl) {
18180							$cellspacingx = -$cellspacing['H'];
18181						} else {
18182							$cellspacingx = $cellspacing['H'];
18183						}
18184						// total table width without cellspaces
18185						$table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
18186						// minimum column width
18187						$table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
18188						// array of custom column widths
18189						$table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
18190					}
18191					// table row
18192					if ($dom[$key]['value'] == 'tr') {
18193						// reset column counter
18194						$colid = 0;
18195					}
18196					// table cell
18197					if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
18198						$trid = $dom[$key]['parent'];
18199						$table_el = $dom[$trid]['parent'];
18200						if (!isset($dom[$table_el]['cols'])) {
18201							$dom[$table_el]['cols'] = $dom[$trid]['cols'];
18202						}
18203						// store border info
18204						$tdborder = 0;
18205						if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
18206							$tdborder = $dom[$key]['border'];
18207						}
18208						$colspan = intval($dom[$key]['attribute']['colspan']);
18209						if ($colspan <= 0) {
18210							$colspan = 1;
18211						}
18212						$old_cell_padding = $this->cell_padding;
18213						if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
18214							$crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
18215							$current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
18216						} elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
18217							$current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
18218						} else {
18219							$current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
18220						}
18221						$this->cell_padding = $current_cell_padding;
18222						if (isset($dom[$key]['height'])) {
18223							// minimum cell height
18224							$cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
18225						} else {
18226							$cellh = 0;
18227						}
18228						if (isset($dom[$key]['content'])) {
18229							$cell_content = $dom[$key]['content'];
18230						} else {
18231							$cell_content = '&nbsp;';
18232						}
18233						$tagtype = $dom[$key]['value'];
18234						$parentid = $key;
18235						while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
18236							// move $key index forward
18237							++$key;
18238						}
18239						if (!isset($dom[$trid]['startpage'])) {
18240							$dom[$trid]['startpage'] = $this->page;
18241						} else {
18242							$this->setPage($dom[$trid]['startpage']);
18243						}
18244						if (!isset($dom[$trid]['startcolumn'])) {
18245							$dom[$trid]['startcolumn'] = $this->current_column;
18246						} elseif ($this->current_column != $dom[$trid]['startcolumn']) {
18247							$tmpx = $this->x;
18248							$this->selectColumn($dom[$trid]['startcolumn']);
18249							$this->x = $tmpx;
18250						}
18251						if (!isset($dom[$trid]['starty'])) {
18252							$dom[$trid]['starty'] = $this->y;
18253						} else {
18254							$this->y = $dom[$trid]['starty'];
18255						}
18256						if (!isset($dom[$trid]['startx'])) {
18257							$dom[$trid]['startx'] = $this->x;
18258							$this->x += $cellspacingx;
18259						} else {
18260							$this->x += ($cellspacingx / 2);
18261						}
18262						if (isset($dom[$parentid]['attribute']['rowspan'])) {
18263							$rowspan = intval($dom[$parentid]['attribute']['rowspan']);
18264						} else {
18265							$rowspan = 1;
18266						}
18267						// skip row-spanned cells started on the previous rows
18268						if (isset($dom[$table_el]['rowspans'])) {
18269							$rsk = 0;
18270							$rskmax = count($dom[$table_el]['rowspans']);
18271							while ($rsk < $rskmax) {
18272								$trwsp = $dom[$table_el]['rowspans'][$rsk];
18273								$rsstartx = $trwsp['startx'];
18274								$rsendx = $trwsp['endx'];
18275								// account for margin changes
18276								if ($trwsp['startpage'] < $this->page) {
18277									if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
18278										$dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
18279										$rsstartx -= $dl;
18280										$rsendx -= $dl;
18281									} elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
18282										$dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
18283										$rsstartx += $dl;
18284										$rsendx += $dl;
18285									}
18286								}
18287								if (($trwsp['rowspan'] > 0)
18288									AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps))
18289									AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps))
18290									AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) {
18291									// set the starting X position of the current cell
18292									$this->x = $rsendx + $cellspacingx;
18293									// increment column indicator
18294									$colid += $trwsp['colspan'];
18295									if (($trwsp['rowspan'] == 1)
18296										AND (isset($dom[$trid]['endy']))
18297										AND (isset($dom[$trid]['endpage']))
18298										AND (isset($dom[$trid]['endcolumn']))
18299										AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
18300										AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18301										// set ending Y position for row
18302										$dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18303										$dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
18304									}
18305									$rsk = 0;
18306								} else {
18307									++$rsk;
18308								}
18309							}
18310						}
18311						if (isset($dom[$parentid]['width'])) {
18312							// user specified width
18313							$cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
18314							$tmpcw = ($cellw / $colspan);
18315							for ($i = 0; $i < $colspan; ++$i) {
18316								$table_colwidths[($colid + $i)] = $tmpcw;
18317							}
18318						} else {
18319							// inherit column width
18320							$cellw = 0;
18321							for ($i = 0; $i < $colspan; ++$i) {
18322								$cellw += (isset($table_colwidths[($colid + $i)]) ? $table_colwidths[($colid + $i)] : 0);
18323							}
18324						}
18325						$cellw += (($colspan - 1) * $cellspacing['H']);
18326						// increment column indicator
18327						$colid += $colspan;
18328						// add rowspan information to table element
18329						if ($rowspan > 1) {
18330							$trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startcolumn' => $this->current_column, 'startx' => $this->x, 'starty' => $this->y));
18331						}
18332						$cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
18333						if ($rowspan > 1) {
18334							$dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
18335						}
18336						// push background colors
18337						if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
18338							$dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
18339						}
18340						// store border info
18341						if (isset($tdborder) AND !empty($tdborder)) {
18342							$dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
18343						}
18344						$prevLastH = $this->lasth;
18345						// store some info for multicolumn mode
18346						if ($this->rtl) {
18347							$this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
18348						} else {
18349							$this->colxshift['x'] = $this->x - $this->lMargin;
18350						}
18351						$this->colxshift['s'] = $cellspacing;
18352						$this->colxshift['p'] = $current_cell_padding;
18353						// ****** write the cell content ******
18354						$this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
18355						// restore some values
18356						$this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
18357						$this->lasth = $prevLastH;
18358						$this->cell_padding = $old_cell_padding;
18359						$dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
18360						// update the end of row position
18361						if ($rowspan <= 1) {
18362							if (isset($dom[$trid]['endy'])) {
18363								if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) {
18364									$dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
18365								} elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) {
18366									$dom[$trid]['endy'] = $this->y;
18367								}
18368							} else {
18369								$dom[$trid]['endy'] = $this->y;
18370							}
18371							if (isset($dom[$trid]['endpage'])) {
18372								$dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
18373							} else {
18374								$dom[$trid]['endpage'] = $this->page;
18375							}
18376							if (isset($dom[$trid]['endcolumn'])) {
18377								$dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
18378							} else {
18379								$dom[$trid]['endcolumn'] = $this->current_column;
18380							}
18381						} else {
18382							// account for row-spanned cells
18383							$dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
18384							$dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
18385							$dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
18386							$dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column;
18387						}
18388						if (isset($dom[$table_el]['rowspans'])) {
18389							// update endy and endpage on rowspanned cells
18390							foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
18391								if ($trwsp['rowspan'] > 0) {
18392									if (isset($dom[$trid]['endpage'])) {
18393										if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18394											$dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18395										} elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
18396											$dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
18397											$dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
18398											$dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
18399										} else {
18400											$dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
18401										}
18402									}
18403								}
18404							}
18405						}
18406						$this->x += ($cellspacingx / 2);
18407					} else {
18408						// opening tag (or self-closing tag)
18409						if (!isset($opentagpos)) {
18410							if ($this->inxobj) {
18411								// we are inside an XObject template
18412								$opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
18413							} elseif (!$this->InFooter) {
18414								if (isset($this->footerlen[$this->page])) {
18415									$this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
18416								} else {
18417									$this->footerpos[$this->page] = $this->pagelen[$this->page];
18418								}
18419								$opentagpos = $this->footerpos[$this->page];
18420							}
18421						}
18422						$dom = $this->openHTMLTagHandler($dom, $key, $cell);
18423					}
18424				} else { // closing tag
18425					$prev_numpages = $this->numpages;
18426					$old_bordermrk = $this->bordermrk[$this->page];
18427					$dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
18428					if ($this->bordermrk[$this->page] > $old_bordermrk) {
18429						$startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk);
18430					}
18431					if ($prev_numpages > $this->numpages) {
18432						$startlinepage = $this->page;
18433					}
18434				}
18435			} elseif (strlen($dom[$key]['value']) > 0) {
18436				// print list-item
18437				if (!TCPDF_STATIC::empty_string($this->lispacer) AND ($this->lispacer != '^')) {
18438					$this->setFont($pfontname, $pfontstyle, $pfontsize);
18439					$this->resetLastH();
18440					$minstartliney = $this->y;
18441					$maxbottomliney = ($startliney + $this->getCellHeight($this->FontSize));
18442					if (is_numeric($pfontsize) AND ($pfontsize > 0)) {
18443						$this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
18444					}
18445					$this->setFont($curfontname, $curfontstyle, $curfontsize);
18446					$this->resetLastH();
18447					if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
18448						$pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
18449						$pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
18450						$this->y += ($this->getCellHeight(($pfontsize - $curfontsize) / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
18451						$minstartliney = min($this->y, $minstartliney);
18452						$maxbottomliney = max(($this->y + $this->getCellHeight($pfontsize / $this->k)), $maxbottomliney);
18453					}
18454				}
18455				// text
18456				$this->htmlvspace = 0;
18457				$isRTLString = preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $dom[$key]['value']) || preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_ARABIC, $dom[$key]['value']);
18458				if ((!$this->premode) AND $this->isRTLTextDir() AND !$isRTLString) {
18459					// reverse spaces order
18460					$lsp = ''; // left spaces
18461					$rsp = ''; // right spaces
18462					if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18463						$lsp = $matches[1];
18464					}
18465					if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18466						$rsp = $matches[1];
18467					}
18468					$dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
18469				}
18470				if ($newline) {
18471					if (!$this->premode) {
18472						$prelen = strlen($dom[$key]['value']);
18473						if ($this->isRTLTextDir() AND !$isRTLString) {
18474							// right trim except non-breaking space
18475							$dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
18476						} else {
18477							// left trim except non-breaking space
18478							$dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
18479						}
18480						$postlen = strlen($dom[$key]['value']);
18481						if (($postlen == 0) AND ($prelen > 0)) {
18482							$dom[$key]['trimmed_space'] = true;
18483						}
18484					}
18485					$newline = false;
18486					$firstblock = true;
18487				} else {
18488					$firstblock = false;
18489					// replace empty multiple spaces string with a single space
18490					$dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']);
18491				}
18492				$strrest = '';
18493				if ($this->rtl) {
18494					$this->x -= $this->textindent;
18495				} else {
18496					$this->x += $this->textindent;
18497				}
18498				if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
18499					$strlinelen = $this->GetStringWidth($dom[$key]['value']);
18500					if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
18501						// HTML <a> Link
18502						$hrefcolor = '';
18503						if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
18504							$hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
18505						}
18506						$hrefstyle = -1;
18507						if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
18508							$hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
18509						}
18510						$strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
18511					} else {
18512						$wadj = 0; // space to leave for block continuity
18513						if ($this->rtl) {
18514							$cwa = ($this->x - $this->lMargin);
18515						} else {
18516							$cwa = ($this->w - $this->rMargin - $this->x);
18517						}
18518						if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) {
18519							// check the next text blocks for continuity
18520							$nkey = ($key + 1);
18521							$write_block = true;
18522							$same_textdir = true;
18523							$tmp_fontname = $this->FontFamily;
18524							$tmp_fontstyle = $this->FontStyle;
18525							$tmp_fontsize = $this->FontSizePt;
18526							while ($write_block AND isset($dom[$nkey])) {
18527								if ($dom[$nkey]['tag']) {
18528									if ($dom[$nkey]['block']) {
18529										// end of block
18530										$write_block = false;
18531									}
18532									$tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
18533									$tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
18534									$tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
18535									$same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
18536								} else {
18537									$nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'+/', $this->re_space['m'], $dom[$nkey]['value']);
18538									if (isset($nextstr[0]) AND $same_textdir) {
18539										$wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
18540										if (isset($nextstr[1])) {
18541											$write_block = false;
18542										}
18543									}
18544								}
18545								++$nkey;
18546							}
18547						}
18548						if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) {
18549							$wadj = 0;
18550							$nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $dom[$key]['value']);
18551							$numblks = count($nextstr);
18552							if ($numblks > 1) {
18553								// try to split on blank spaces
18554								$wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)]));
18555							} else {
18556								// set the entire block on new line
18557								$wadj = $this->GetStringWidth($nextstr[0]);
18558							}
18559						}
18560						// check for reversed text direction
18561						if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) {
18562							// LTR text on RTL direction or RTL text on LTR direction
18563							$reverse_dir = true;
18564							$this->rtl = !$this->rtl;
18565							$revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems
18566							if ($this->rtl) {
18567								$this->x += $revshift;
18568							} else {
18569								$this->x -= $revshift;
18570							}
18571							$xws = $this->x;
18572						}
18573						// ****** write only until the end of the line and get the rest ******
18574						$strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
18575						// restore default direction
18576						if ($reverse_dir AND ($wadj == 0)) {
18577							$this->x = $xws;
18578							$this->rtl = !$this->rtl;
18579							$reverse_dir = false;
18580						}
18581					}
18582				}
18583				$this->textindent = 0;
18584				if (strlen($strrest) > 0) {
18585					// store the remaining string on the previous $key position
18586					$this->newline = true;
18587					if ($strrest == $dom[$key]['value']) {
18588						// used to avoid infinite loop
18589						++$loop;
18590					} else {
18591						$loop = 0;
18592					}
18593					$dom[$key]['value'] = $strrest;
18594					if ($cell) {
18595						if ($this->rtl) {
18596							$this->x -= $this->cell_padding['R'];
18597						} else {
18598							$this->x += $this->cell_padding['L'];
18599						}
18600					}
18601					if ($loop < 3) {
18602						--$key;
18603					}
18604				} else {
18605					$loop = 0;
18606					// add the positive font spacing of the last character (if any)
18607					 if ($this->font_spacing > 0) {
18608					 	if ($this->rtl) {
18609							$this->x -= $this->font_spacing;
18610						} else {
18611							$this->x += $this->font_spacing;
18612						}
18613					}
18614				}
18615			}
18616			++$key;
18617			if (isset($dom[$key]['tag']) AND $dom[$key]['tag'] AND (!isset($dom[$key]['opening']) OR !$dom[$key]['opening']) AND isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
18618				// check if we are on a new page or on a new column
18619				if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) {
18620					// we are on a new page or on a new column and the total object height is less than the available vertical space.
18621					// restore previous object
18622					$this->rollbackTransaction(true);
18623					// restore previous values
18624					foreach ($this_method_vars as $vkey => $vval) {
18625						$$vkey = $vval;
18626					}
18627					if (!empty($dom[$key]['thead'])) {
18628						$this->inthead = true;
18629					}
18630					// add a page (or trig AcceptPageBreak() for multicolumn mode)
18631					$pre_y = $this->y;
18632					if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
18633						$startliney = $this->y;
18634					}
18635					$undo = true; // avoid infinite loop
18636				} else {
18637					$undo = false;
18638				}
18639			}
18640		} // end for each $key
18641		// align the last line
18642		if (isset($startlinex)) {
18643			$yshift = ($minstartliney - $startliney);
18644			if (($yshift > 0) OR ($this->page > $startlinepage)) {
18645				$yshift = 0;
18646			}
18647			$t_x = 0;
18648			// the last line must be shifted to be aligned as requested
18649			$linew = abs($this->endlinex - $startlinex);
18650			if ($this->inxobj) {
18651				// we are inside an XObject template
18652				$pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
18653				if (isset($opentagpos)) {
18654					$midpos = $opentagpos;
18655				} else {
18656					$midpos = 0;
18657				}
18658				if ($midpos > 0) {
18659					$pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
18660					$pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
18661				} else {
18662					$pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
18663					$pend = '';
18664				}
18665			} else {
18666				$pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
18667				if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18668					$this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18669					$midpos = min($opentagpos, $this->footerpos[$startlinepage]);
18670				} elseif (isset($opentagpos)) {
18671					$midpos = $opentagpos;
18672				} elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18673					$this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18674					$midpos = $this->footerpos[$startlinepage];
18675				} else {
18676					$midpos = 0;
18677				}
18678				if ($midpos > 0) {
18679					$pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
18680					$pend = substr($this->getPageBuffer($startlinepage), $midpos);
18681				} else {
18682					$pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
18683					$pend = '';
18684				}
18685			}
18686			if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
18687				// calculate shifting amount
18688				$tw = $w;
18689				if ($this->lMargin != $prevlMargin) {
18690					$tw += ($prevlMargin - $this->lMargin);
18691				}
18692				if ($this->rMargin != $prevrMargin) {
18693					$tw += ($prevrMargin - $this->rMargin);
18694				}
18695				$one_space_width = $this->GetStringWidth(chr(32));
18696				$no = 0; // number of spaces on a line contained on a single block
18697				if ($this->isRTLTextDir()) { // RTL
18698					// remove left space if exist
18699					$pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
18700					if ($pos1 > 0) {
18701						$pos1 = intval($pos1);
18702						if ($this->isUnicodeFont()) {
18703							$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
18704							$spacelen = 2;
18705						} else {
18706							$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
18707							$spacelen = 1;
18708						}
18709						if ($pos1 == $pos2) {
18710							$pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
18711							if (substr($pmid, $pos1, 4) == '[()]') {
18712								$linew -= $one_space_width;
18713							} elseif ($pos1 == strpos($pmid, '[(')) {
18714								$no = 1;
18715							}
18716						}
18717					}
18718				} else { // LTR
18719					// remove right space if exist
18720					$pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
18721					if ($pos1 > 0) {
18722						$pos1 = intval($pos1);
18723						if ($this->isUnicodeFont()) {
18724							$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
18725							$spacelen = 2;
18726						} else {
18727							$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
18728							$spacelen = 1;
18729						}
18730						if ($pos1 == $pos2) {
18731							$pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
18732							$linew -= $one_space_width;
18733						}
18734					}
18735				}
18736				$mdiff = ($tw - $linew);
18737				if ($plalign == 'C') {
18738					if ($this->rtl) {
18739						$t_x = -($mdiff / 2);
18740					} else {
18741						$t_x = ($mdiff / 2);
18742					}
18743				} elseif ($plalign == 'R') {
18744					// right alignment on LTR document
18745					$t_x = $mdiff;
18746				} elseif ($plalign == 'L') {
18747					// left alignment on RTL document
18748					$t_x = -$mdiff;
18749				}
18750			} // end if startlinex
18751			if (($t_x != 0) OR ($yshift < 0)) {
18752				// shift the line
18753				$trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
18754				$pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18755				$endlinepos = strlen($pstart);
18756				if ($this->inxobj) {
18757					// we are inside an XObject template
18758					$this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
18759					foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
18760						if ($pak >= $pask) {
18761							$this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
18762							$this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
18763						}
18764					}
18765				} else {
18766					$this->setPageBuffer($startlinepage, $pstart.$pend);
18767					// shift the annotations and links
18768					if (isset($this->PageAnnots[$this->page])) {
18769						foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
18770							if ($pak >= $pask) {
18771								$this->PageAnnots[$this->page][$pak]['x'] += $t_x;
18772								$this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
18773							}
18774						}
18775					}
18776				}
18777				$this->y -= $yshift;
18778				$yshift = 0;
18779			}
18780		}
18781		// restore previous values
18782		$this->setGraphicVars($gvars);
18783		if ($this->num_columns > 1) {
18784			$this->selectColumn();
18785		} elseif ($this->page > $prevPage) {
18786			$this->lMargin = $this->pagedim[$this->page]['olm'];
18787			$this->rMargin = $this->pagedim[$this->page]['orm'];
18788		}
18789		// restore previous list state
18790		$this->cell_height_ratio = $prev_cell_height_ratio;
18791		$this->listnum = $prev_listnum;
18792		$this->listordered = $prev_listordered;
18793		$this->listcount = $prev_listcount;
18794		$this->lispacer = $prev_lispacer;
18795		if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
18796			$this->Ln($this->lasth);
18797			if (($this->y < $maxbottomliney) AND ($startlinepage == $this->page)) {
18798				$this->y = $maxbottomliney;
18799			}
18800		}
18801		unset($dom);
18802	}
18803
18804	/**
18805	 * Process opening tags.
18806	 * @param array $dom html dom array
18807	 * @param int $key current element id
18808	 * @param boolean $cell if true add the default left (or right if RTL) padding to each new line (default false).
18809	 * @return array $dom
18810	 * @protected
18811	 */
18812	protected function openHTMLTagHandler($dom, $key, $cell) {
18813		$tag = $dom[$key];
18814		$parent = $dom[($dom[$key]['parent'])];
18815		$firsttag = ($key == 1);
18816		// check for text direction attribute
18817		if (isset($tag['dir'])) {
18818			$this->setTempRTL($tag['dir']);
18819		} else {
18820			$this->tmprtl = false;
18821		}
18822		if ($tag['block']) {
18823			$hbz = 0; // distance from y to line bottom
18824			$hb = 0; // vertical space between block tags
18825			// calculate vertical space for block tags
18826			if (isset($this->tagvspaces[$tag['value']][0]['h']) && !empty($this->tagvspaces[$tag['value']][0]['h']) && ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
18827				$cur_h = $this->tagvspaces[$tag['value']][0]['h'];
18828			} elseif (isset($tag['fontsize'])) {
18829				$cur_h = $this->getCellHeight($tag['fontsize'] / $this->k);
18830			} else {
18831				$cur_h = $this->getCellHeight($this->FontSize);
18832			}
18833			if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
18834				$on = $this->tagvspaces[$tag['value']][0]['n'];
18835			} elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18836				$on = 0.6;
18837			} else {
18838				$on = 1;
18839			}
18840			if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br', 'hr')))) {
18841				$hb = 0;
18842			} else {
18843				$hb = ($on * $cur_h);
18844			}
18845			if (($this->htmlvspace <= 0) AND ($on > 0)) {
18846				if (isset($parent['fontsize'])) {
18847					$hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
18848				} else {
18849					$hbz = $this->getCellHeight($this->FontSize);
18850				}
18851			}
18852			if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) {
18853				// fix vertical space after table
18854				$hbz = 0;
18855			}
18856			// closing vertical space
18857			$hbc = 0;
18858			if (isset($this->tagvspaces[$tag['value']][1]['h']) && !empty($this->tagvspaces[$tag['value']][1]['h']) && ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
18859				$pre_h = $this->tagvspaces[$tag['value']][1]['h'];
18860			} elseif (isset($parent['fontsize'])) {
18861				$pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
18862			} else {
18863				$pre_h = $this->getCellHeight($this->FontSize);
18864			}
18865			if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
18866				$cn = $this->tagvspaces[$tag['value']][1]['n'];
18867			} elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18868				$cn = 0.6;
18869			} else {
18870				$cn = 1;
18871			}
18872			if (isset($this->tagvspaces[$tag['value']][1])) {
18873				$hbc = ($cn * $pre_h);
18874			}
18875		}
18876		// Opening tag
18877		switch($tag['value']) {
18878			case 'table': {
18879				$cp = 0;
18880				$cs = 0;
18881				$dom[$key]['rowspans'] = array();
18882				if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
18883					$this->htmlvspace = 0;
18884					// set table header
18885					if (!TCPDF_STATIC::empty_string($dom[$key]['thead'])) {
18886						// set table header
18887						$this->thead = $dom[$key]['thead'];
18888						if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
18889							$this->theadMargins = array();
18890							$this->theadMargins['cell_padding'] = $this->cell_padding;
18891							$this->theadMargins['lmargin'] = $this->lMargin;
18892							$this->theadMargins['rmargin'] = $this->rMargin;
18893							$this->theadMargins['page'] = $this->page;
18894							$this->theadMargins['cell'] = $cell;
18895							$this->theadMargins['gvars'] = $this->getGraphicVars();
18896						}
18897					}
18898				}
18899				// store current margins and page
18900				$dom[$key]['old_cell_padding'] = $this->cell_padding;
18901				if (isset($tag['attribute']['cellpadding'])) {
18902					$pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
18903					$this->setCellPadding($pad);
18904				} elseif (isset($tag['padding'])) {
18905					$this->cell_padding = $tag['padding'];
18906				}
18907				if (isset($tag['attribute']['cellspacing'])) {
18908					$cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
18909				} elseif (isset($tag['border-spacing'])) {
18910					$cs = $tag['border-spacing']['V'];
18911				}
18912				$prev_y = $this->y;
18913				if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) {
18914					$this->inthead = true;
18915					// add a page (or trig AcceptPageBreak() for multicolumn mode)
18916					$this->checkPageBreak($this->PageBreakTrigger + 1);
18917				}
18918				break;
18919			}
18920			case 'tr': {
18921				// array of columns positions
18922				$dom[$key]['cellpos'] = array();
18923				break;
18924			}
18925			case 'hr': {
18926				if ((isset($tag['height'])) AND ($tag['height'] != '')) {
18927					$hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
18928				} else {
18929					$hrHeight = $this->GetLineWidth();
18930				}
18931				$this->addHTMLVertSpace($hbz, max($hb, ($hrHeight / 2)), $cell, $firsttag);
18932				$x = $this->GetX();
18933				$y = $this->GetY();
18934				$wtmp = $this->w - $this->lMargin - $this->rMargin;
18935				if ($cell) {
18936					$wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']);
18937				}
18938				if ((isset($tag['width'])) AND ($tag['width'] != '')) {
18939					$hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
18940				} else {
18941					$hrWidth = $wtmp;
18942				}
18943				$prevlinewidth = $this->GetLineWidth();
18944				$this->setLineWidth($hrHeight);
18945
18946				$lineStyle = array();
18947                    		if (isset($tag['fgcolor'])) {
18948		                        $lineStyle['color'] = $tag['fgcolor'];
18949                    		}
18950
18951                    		if (isset($tag['fgcolor'])) {
18952                        		$lineStyle['color'] = $tag['fgcolor'];
18953                    		}
18954
18955                    		if (isset($tag['style']['cap'])) {
18956                        		$lineStyle['cap'] = $tag['style']['cap'];
18957                    		}
18958
18959                    		if (isset($tag['style']['join'])) {
18960                        		$lineStyle['join'] = $tag['style']['join'];
18961                    		}
18962
18963                    		if (isset($tag['style']['dash'])) {
18964                        		$lineStyle['dash'] = $tag['style']['dash'];
18965                    		}
18966
18967                    		if (isset($tag['style']['phase'])) {
18968                        		$lineStyle['phase'] = $tag['style']['phase'];
18969                    		}
18970
18971				$lineStyle = array_filter($lineStyle);
18972
18973				$this->Line($x, $y, $x + $hrWidth, $y, $lineStyle);
18974				$this->setLineWidth($prevlinewidth);
18975				$this->addHTMLVertSpace(max($hbc, ($hrHeight / 2)), 0, $cell, !isset($dom[($key + 1)]));
18976				break;
18977			}
18978			case 'a': {
18979				if (array_key_exists('href', $tag['attribute'])) {
18980					$this->HREF['url'] = $tag['attribute']['href'];
18981				}
18982				break;
18983			}
18984			case 'img': {
18985				if (empty($tag['attribute']['src'])) {
18986					break;
18987				}
18988				$imgsrc = $tag['attribute']['src'];
18989				if ($imgsrc[0] === '@') {
18990					// data stream
18991					$imgsrc = '@'.base64_decode(substr($imgsrc, 1));
18992					$type = '';
18993				} elseif ( $this->allowLocalFiles && substr($imgsrc, 0, 7) === 'file://') {
18994                    // get image type from a local file path
18995                    $imgsrc = substr($imgsrc, 7);
18996                    $type = TCPDF_IMAGES::getImageFileType($imgsrc);
18997                } else {
18998					if (($imgsrc[0] === '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
18999						// fix image path
19000						$findroot = strpos($imgsrc, $_SERVER['DOCUMENT_ROOT']);
19001						if (($findroot === false) OR ($findroot > 1)) {
19002							if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
19003								$imgsrc = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$imgsrc;
19004							} else {
19005								$imgsrc = $_SERVER['DOCUMENT_ROOT'].$imgsrc;
19006							}
19007						}
19008						$imgsrc = urldecode($imgsrc);
19009						$testscrtype = @parse_url($imgsrc);
19010						if (empty($testscrtype['query'])) {
19011							// convert URL to server path
19012							$imgsrc = str_replace(K_PATH_URL, K_PATH_MAIN, $imgsrc);
19013						} elseif (preg_match('|^https?://|', $imgsrc) !== 1) {
19014							// convert URL to server path
19015							$imgsrc = str_replace(K_PATH_MAIN, K_PATH_URL, $imgsrc);
19016						}
19017					}
19018					// get image type
19019					$type = TCPDF_IMAGES::getImageFileType($imgsrc);
19020				}
19021				if (!isset($tag['width'])) {
19022					$tag['width'] = 0;
19023				}
19024				if (!isset($tag['height'])) {
19025					$tag['height'] = 0;
19026				}
19027				//if (!isset($tag['attribute']['align'])) {
19028					// the only alignment supported is "bottom"
19029					// further development is required for other modes.
19030					$tag['attribute']['align'] = 'bottom';
19031				//}
19032				switch($tag['attribute']['align']) {
19033					case 'top': {
19034						$align = 'T';
19035						break;
19036					}
19037					case 'middle': {
19038						$align = 'M';
19039						break;
19040					}
19041					case 'bottom': {
19042						$align = 'B';
19043						break;
19044					}
19045					default: {
19046						$align = 'B';
19047						break;
19048					}
19049				}
19050				$prevy = $this->y;
19051				$xpos = $this->x;
19052				$imglink = '';
19053				if (isset($this->HREF['url']) AND !TCPDF_STATIC::empty_string($this->HREF['url'])) {
19054					$imglink = $this->HREF['url'];
19055					if ($imglink[0] == '#') {
19056						// convert url to internal link
19057						$lnkdata = explode(',', $imglink);
19058						if (isset($lnkdata[0])) {
19059							$page = intval(substr($lnkdata[0], 1));
19060							if (empty($page) OR ($page <= 0)) {
19061								$page = $this->page;
19062							}
19063							if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
19064								$lnky = floatval($lnkdata[1]);
19065							} else {
19066								$lnky = 0;
19067							}
19068							$imglink = $this->AddLink();
19069							$this->setLink($imglink, $lnky, $page);
19070						}
19071					}
19072				}
19073				$border = 0;
19074				if (isset($tag['border']) AND !empty($tag['border'])) {
19075					// currently only support 1 (frame) or a combination of 'LTRB'
19076					$border = $tag['border'];
19077				}
19078				$iw = '';
19079				if (isset($tag['width'])) {
19080					$iw = $this->getHTMLUnitToUnits($tag['width'], ($tag['fontsize'] / $this->k), 'px', false);
19081				}
19082				$ih = '';
19083				if (isset($tag['height'])) {
19084					$ih = $this->getHTMLUnitToUnits($tag['height'], ($tag['fontsize'] / $this->k), 'px', false);
19085				}
19086				if (($type == 'eps') OR ($type == 'ai')) {
19087					$this->ImageEps($imgsrc, $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
19088				} elseif ($type == 'svg') {
19089					$this->ImageSVG($imgsrc, $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
19090				} else {
19091					$this->Image($imgsrc, $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
19092				}
19093				switch($align) {
19094					case 'T': {
19095						$this->y = $prevy;
19096						break;
19097					}
19098					case 'M': {
19099						$this->y = (($this->img_rb_y + $prevy - ($this->getCellHeight($tag['fontsize'] / $this->k))) / 2);
19100						break;
19101					}
19102					case 'B': {
19103						$this->y = $this->img_rb_y - ($this->getCellHeight($tag['fontsize'] / $this->k) - ($this->getFontDescent($tag['fontname'], $tag['fontstyle'], $tag['fontsize']) * $this->cell_height_ratio));
19104						break;
19105					}
19106				}
19107				break;
19108			}
19109			case 'dl': {
19110				++$this->listnum;
19111				if ($this->listnum == 1) {
19112					$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19113				} else {
19114					$this->addHTMLVertSpace(0, 0, $cell, $firsttag);
19115				}
19116				break;
19117			}
19118			case 'dt': {
19119				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19120				break;
19121			}
19122			case 'dd': {
19123				if ($this->rtl) {
19124					$this->rMargin += $this->listindent;
19125				} else {
19126					$this->lMargin += $this->listindent;
19127				}
19128				++$this->listindentlevel;
19129				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19130				break;
19131			}
19132			case 'ul':
19133			case 'ol': {
19134				++$this->listnum;
19135				if ($tag['value'] == 'ol') {
19136					$this->listordered[$this->listnum] = true;
19137				} else {
19138					$this->listordered[$this->listnum] = false;
19139				}
19140				if (isset($tag['attribute']['start'])) {
19141					$this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
19142				} else {
19143					$this->listcount[$this->listnum] = 0;
19144				}
19145				if ($this->rtl) {
19146					$this->rMargin += $this->listindent;
19147					$this->x -= $this->listindent;
19148				} else {
19149					$this->lMargin += $this->listindent;
19150					$this->x += $this->listindent;
19151				}
19152				++$this->listindentlevel;
19153				if ($this->listnum == 1) {
19154					if ($key > 1) {
19155						$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19156					}
19157				} else {
19158					$this->addHTMLVertSpace(0, 0, $cell, $firsttag);
19159				}
19160				break;
19161			}
19162			case 'li': {
19163				if ($key > 2) {
19164					$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19165				}
19166				if ($this->listordered[$this->listnum]) {
19167					// ordered item
19168					if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
19169						$this->lispacer = $parent['attribute']['type'];
19170					} elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
19171						$this->lispacer = $parent['listtype'];
19172					} elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
19173						$this->lispacer = $this->lisymbol;
19174					} else {
19175						$this->lispacer = '#';
19176					}
19177					++$this->listcount[$this->listnum];
19178					if (isset($tag['attribute']['value'])) {
19179						$this->listcount[$this->listnum] = intval($tag['attribute']['value']);
19180					}
19181				} else {
19182					// unordered item
19183					if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
19184						$this->lispacer = $parent['attribute']['type'];
19185					} elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
19186						$this->lispacer = $parent['listtype'];
19187					} elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
19188						$this->lispacer = $this->lisymbol;
19189					} else {
19190						$this->lispacer = '!';
19191					}
19192				}
19193				break;
19194			}
19195			case 'blockquote': {
19196				if ($this->rtl) {
19197					$this->rMargin += $this->listindent;
19198				} else {
19199					$this->lMargin += $this->listindent;
19200				}
19201				++$this->listindentlevel;
19202				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19203				break;
19204			}
19205			case 'br': {
19206				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19207				break;
19208			}
19209			case 'div': {
19210				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19211				break;
19212			}
19213			case 'p': {
19214				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19215				break;
19216			}
19217			case 'pre': {
19218				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19219				$this->premode = true;
19220				break;
19221			}
19222			case 'sup': {
19223				$this->setXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
19224				break;
19225			}
19226			case 'sub': {
19227				$this->setXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
19228				break;
19229			}
19230			case 'h1':
19231			case 'h2':
19232			case 'h3':
19233			case 'h4':
19234			case 'h5':
19235			case 'h6': {
19236				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19237				break;
19238			}
19239			// Form fields (since 4.8.000 - 2009-09-07)
19240			case 'form': {
19241				if (isset($tag['attribute']['action'])) {
19242					$this->form_action = $tag['attribute']['action'];
19243				} else {
19244					$this->Error('Please explicitly set action attribute path!');
19245				}
19246				if (isset($tag['attribute']['enctype'])) {
19247					$this->form_enctype = $tag['attribute']['enctype'];
19248				} else {
19249					$this->form_enctype = 'application/x-www-form-urlencoded';
19250				}
19251				if (isset($tag['attribute']['method'])) {
19252					$this->form_mode = $tag['attribute']['method'];
19253				} else {
19254					$this->form_mode = 'post';
19255				}
19256				break;
19257			}
19258			case 'input': {
19259				if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19260					$name = $tag['attribute']['name'];
19261				} else {
19262					break;
19263				}
19264				$prop = array();
19265				$opt = array();
19266				if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19267					$prop['readonly'] = true;
19268				}
19269				if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19270					$value = $tag['attribute']['value'];
19271				}
19272				if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC::empty_string($tag['attribute']['maxlength'])) {
19273					$opt['maxlen'] = intval($tag['attribute']['maxlength']);
19274				}
19275				$h = $this->getCellHeight($this->FontSize);
19276				if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19277					$w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
19278				} else {
19279					$w = $h;
19280				}
19281				if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
19282					$checked = true;
19283				} else {
19284					$checked = false;
19285				}
19286				if (isset($tag['align'])) {
19287					switch ($tag['align']) {
19288						case 'C': {
19289							$opt['q'] = 1;
19290							break;
19291						}
19292						case 'R': {
19293							$opt['q'] = 2;
19294							break;
19295						}
19296						case 'L':
19297						default: {
19298							break;
19299						}
19300					}
19301				}
19302				switch ($tag['attribute']['type']) {
19303					case 'text': {
19304						if (isset($value)) {
19305							$opt['v'] = $value;
19306						}
19307						$this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19308						break;
19309					}
19310					case 'password': {
19311						if (isset($value)) {
19312							$opt['v'] = $value;
19313						}
19314						$prop['password'] = 'true';
19315						$this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19316						break;
19317					}
19318					case 'checkbox': {
19319						if (!isset($value)) {
19320							break;
19321						}
19322						$this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
19323						break;
19324					}
19325					case 'radio': {
19326						if (!isset($value)) {
19327							break;
19328						}
19329						$this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
19330						break;
19331					}
19332					case 'submit': {
19333						if (!isset($value)) {
19334							$value = 'submit';
19335						}
19336						$w = $this->GetStringWidth($value) * 1.5;
19337						$h *= 1.6;
19338						$prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19339						$action = array();
19340						$action['S'] = 'SubmitForm';
19341						$action['F'] = $this->form_action;
19342						if ($this->form_enctype != 'FDF') {
19343							$action['Flags'] = array('ExportFormat');
19344						}
19345						if ($this->form_mode == 'get') {
19346							$action['Flags'] = array('GetMethod');
19347						}
19348						$this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
19349						break;
19350					}
19351					case 'reset': {
19352						if (!isset($value)) {
19353							$value = 'reset';
19354						}
19355						$w = $this->GetStringWidth($value) * 1.5;
19356						$h *= 1.6;
19357						$prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19358						$this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
19359						break;
19360					}
19361					case 'file': {
19362						$prop['fileSelect'] = 'true';
19363						$this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19364						if (!isset($value)) {
19365							$value = '*';
19366						}
19367						$w = $this->GetStringWidth($value) * 2;
19368						$h *= 1.2;
19369						$prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19370						$jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
19371						$this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19372						break;
19373					}
19374					case 'hidden': {
19375						if (isset($value)) {
19376							$opt['v'] = $value;
19377						}
19378						$opt['f'] = array('invisible', 'hidden');
19379						$this->TextField($name, 0, 0, $prop, $opt, '', '', false);
19380						break;
19381					}
19382					case 'image': {
19383						// THIS TYPE MUST BE FIXED
19384						if (isset($tag['attribute']['src']) AND !TCPDF_STATIC::empty_string($tag['attribute']['src'])) {
19385							$img = $tag['attribute']['src'];
19386						} else {
19387							break;
19388						}
19389						$value = 'img';
19390						//$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
19391						if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19392							$jsaction = $tag['attribute']['onclick'];
19393						} else {
19394							$jsaction = '';
19395						}
19396						$this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19397						break;
19398					}
19399					case 'button': {
19400						if (!isset($value)) {
19401							$value = ' ';
19402						}
19403						$w = $this->GetStringWidth($value) * 1.5;
19404						$h *= 1.6;
19405						$prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19406						if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19407							$jsaction = $tag['attribute']['onclick'];
19408						} else {
19409							$jsaction = '';
19410						}
19411						$this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19412						break;
19413					}
19414				}
19415				break;
19416			}
19417			case 'textarea': {
19418				$prop = array();
19419				$opt = array();
19420				if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19421					$prop['readonly'] = true;
19422				}
19423				if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19424					$name = $tag['attribute']['name'];
19425				} else {
19426					break;
19427				}
19428				if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19429					$opt['v'] = $tag['attribute']['value'];
19430				}
19431				if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC::empty_string($tag['attribute']['cols'])) {
19432					$w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
19433				} else {
19434					$w = 40;
19435				}
19436				if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC::empty_string($tag['attribute']['rows'])) {
19437					$h = intval($tag['attribute']['rows']) * $this->getCellHeight($this->FontSize);
19438				} else {
19439					$h = 10;
19440				}
19441				$prop['multiline'] = 'true';
19442				$this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19443				break;
19444			}
19445			case 'select': {
19446				$h = $this->getCellHeight($this->FontSize);
19447				if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19448					$h *= ($tag['attribute']['size'] + 1);
19449				}
19450				$prop = array();
19451				$opt = array();
19452				if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19453					$name = $tag['attribute']['name'];
19454				} else {
19455					break;
19456				}
19457				$w = 0;
19458				if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC::empty_string($tag['attribute']['opt'])) {
19459					$options = explode('#!NwL!#', $tag['attribute']['opt']);
19460					$values = array();
19461					foreach ($options as $val) {
19462						if (strpos($val, '#!TaB!#') !== false) {
19463							$opts = explode('#!TaB!#', $val);
19464							$values[] = $opts;
19465							$w = max($w, $this->GetStringWidth($opts[1]));
19466						} else {
19467							$values[] = $val;
19468							$w = max($w, $this->GetStringWidth($val));
19469						}
19470					}
19471				} else {
19472					break;
19473				}
19474				$w *= 2;
19475				if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
19476					$prop['multipleSelection'] = 'true';
19477					$this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19478				} else {
19479					$this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19480				}
19481				break;
19482			}
19483			case 'tcpdf': {
19484				if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
19485					// Special tag used to call TCPDF methods
19486					if (isset($tag['attribute']['method'])) {
19487						$tcpdf_method = $tag['attribute']['method'];
19488						if (method_exists($this, $tcpdf_method)) {
19489							if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
19490								$params = $this->unserializeTCPDFtagParameters($tag['attribute']['params']);
19491								call_user_func_array(array($this, $tcpdf_method), $params);
19492							} else {
19493								$this->$tcpdf_method();
19494							}
19495							$this->newline = true;
19496						}
19497					}
19498				}
19499				break;
19500			}
19501			default: {
19502				break;
19503			}
19504		}
19505		// define tags that support borders and background colors
19506		$bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
19507		if (in_array($tag['value'], $bordertags)) {
19508			// set border
19509			$dom[$key]['borderposition'] = $this->getBorderStartPosition();
19510		}
19511		if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
19512			$pba = $dom[$key]['attribute']['pagebreakafter'];
19513			// check for pagebreak
19514			if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19515				// add a page (or trig AcceptPageBreak() for multicolumn mode)
19516				$this->checkPageBreak($this->PageBreakTrigger + 1);
19517			}
19518			if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19519				OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19520				// add a page (or trig AcceptPageBreak() for multicolumn mode)
19521				$this->checkPageBreak($this->PageBreakTrigger + 1);
19522			}
19523		}
19524		return $dom;
19525	}
19526
19527	/**
19528	 * Process closing tags.
19529	 * @param array $dom html dom array
19530	 * @param int $key current element id
19531	 * @param boolean $cell if true add the default left (or right if RTL) padding to each new line (default false).
19532	 * @param int $maxbottomliney maximum y value of current line
19533	 * @return array $dom
19534	 * @protected
19535	 */
19536	protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
19537		$tag = $dom[$key];
19538		$parent = $dom[($dom[$key]['parent'])];
19539		$lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
19540		$in_table_head = false;
19541		// maximum x position (used to draw borders)
19542		if ($this->rtl) {
19543			$xmax = $this->w;
19544		} else {
19545			$xmax = 0;
19546		}
19547		if ($tag['block']) {
19548			$hbz = 0; // distance from y to line bottom
19549			$hb = 0; // vertical space between block tags
19550			// calculate vertical space for block tags
19551			if (isset($this->tagvspaces[$tag['value']][1]['h']) && !empty($this->tagvspaces[$tag['value']][1]['h']) && ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
19552				$pre_h = $this->tagvspaces[$tag['value']][1]['h'];
19553			} elseif (isset($parent['fontsize'])) {
19554				$pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
19555			} else {
19556				$pre_h = $this->getCellHeight($this->FontSize);
19557			}
19558			if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
19559				$cn = $this->tagvspaces[$tag['value']][1]['n'];
19560			} elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
19561				$cn = 0.6;
19562			} else {
19563				$cn = 1;
19564			}
19565			if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) {
19566				$hb = 0;
19567			} else {
19568				$hb = ($cn * $pre_h);
19569			}
19570			if ($maxbottomliney > $this->PageBreakTrigger) {
19571				$hbz = $this->getCellHeight($this->FontSize);
19572			} elseif ($this->y < $maxbottomliney) {
19573				$hbz = ($maxbottomliney - $this->y);
19574			}
19575		}
19576		// Closing tag
19577		switch($tag['value']) {
19578			case 'tr': {
19579				$table_el = $dom[($dom[$key]['parent'])]['parent'];
19580				if (!isset($parent['endy'])) {
19581					$dom[($dom[$key]['parent'])]['endy'] = $this->y;
19582					$parent['endy'] = $this->y;
19583				}
19584				if (!isset($parent['endpage'])) {
19585					$dom[($dom[$key]['parent'])]['endpage'] = $this->page;
19586					$parent['endpage'] = $this->page;
19587				}
19588				if (!isset($parent['endcolumn'])) {
19589					$dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column;
19590					$parent['endcolumn'] = $this->current_column;
19591				}
19592				// update row-spanned cells
19593				if (isset($dom[$table_el]['rowspans'])) {
19594					foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19595						$dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
19596						if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19597							if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
19598								$dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
19599							} elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
19600								$dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19601								$dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19602								$dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19603							}
19604						}
19605					}
19606					// report new endy and endpage to the rowspanned cells
19607					foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19608						if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19609							$dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
19610							$dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19611							$dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
19612							$dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19613							$dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
19614							$dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19615						}
19616					}
19617					// update remaining rowspanned cells
19618					foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19619						if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19620							$dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
19621							$dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
19622							$dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
19623						}
19624					}
19625				}
19626				$prev_page = $this->page;
19627				$this->setPage($dom[($dom[$key]['parent'])]['endpage']);
19628				if ($this->num_columns > 1) {
19629					if (($prev_page < $this->page)
19630						AND ((($this->current_column == 0) AND ($dom[($dom[$key]['parent'])]['endcolumn'] == ($this->num_columns - 1)))
19631							OR ($this->current_column == $dom[($dom[$key]['parent'])]['endcolumn']))) {
19632						// page jump
19633						$this->selectColumn(0);
19634						$dom[($dom[$key]['parent'])]['endcolumn'] = 0;
19635						$dom[($dom[$key]['parent'])]['endy'] = $this->y;
19636					} else {
19637						$this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
19638						$this->y = $dom[($dom[$key]['parent'])]['endy'];
19639					}
19640				} else {
19641					$this->y = $dom[($dom[$key]['parent'])]['endy'];
19642				}
19643				if (isset($dom[$table_el]['attribute']['cellspacing'])) {
19644					$this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
19645				} elseif (isset($dom[$table_el]['border-spacing'])) {
19646					$this->y += $dom[$table_el]['border-spacing']['V'];
19647				}
19648				$this->Ln(0, $cell);
19649				if ($this->current_column == $parent['startcolumn']) {
19650					$this->x = $parent['startx'];
19651				}
19652				// account for booklet mode
19653				if ($this->page > $parent['startpage']) {
19654					if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
19655						$this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
19656					} elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
19657						$this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
19658					}
19659				}
19660				break;
19661			}
19662			case 'tablehead':
19663				// closing tag used for the thead part
19664				$in_table_head = true;
19665				$this->inthead = false;
19666			case 'table': {
19667				$table_el = $parent;
19668				// set default border
19669				if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
19670					// set default border
19671					$border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
19672				} else {
19673					$border = 0;
19674				}
19675				$default_border = $border;
19676				// fix bottom line alignment of last line before page break
19677				foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
19678					// update row-spanned cells
19679					if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19680						foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19681							if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) {
19682								$dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
19683							}
19684							if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) {
19685								$dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
19686							}
19687						}
19688					}
19689					if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
19690						$pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
19691						$dom[$prevtrkey]['endy'] = $pgendy;
19692						// update row-spanned cells
19693						if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19694							foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19695								if (($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
19696									$dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
19697									$dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
19698								}
19699							}
19700						}
19701					}
19702					$prevtrkey = $trkey;
19703					$table_el = $dom[($dom[$key]['parent'])];
19704				}
19705				// for each row
19706				if (count($table_el['trids']) > 0) {
19707					unset($xmax);
19708				}
19709				foreach ($table_el['trids'] as $j => $trkey) {
19710					$parent = $dom[$trkey];
19711					if (!isset($xmax)) {
19712						$xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
19713					}
19714					// for each cell on the row
19715					foreach ($parent['cellpos'] as $k => $cellpos) {
19716						if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
19717							$cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
19718							$cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
19719							$endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
19720							$startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
19721							$endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
19722							$startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
19723							$endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
19724						} else {
19725							$endy = $parent['endy'];
19726							$startpage = $parent['startpage'];
19727							$endpage = $parent['endpage'];
19728							$startcolumn = $parent['startcolumn'];
19729							$endcolumn = $parent['endcolumn'];
19730						}
19731						if ($this->num_columns == 0) {
19732							$this->num_columns = 1;
19733						}
19734						if (isset($cellpos['border'])) {
19735							$border = $cellpos['border'];
19736						}
19737						if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
19738							$this->setFillColorArray($cellpos['bgcolor']);
19739							$fill = true;
19740						} else {
19741							$fill = false;
19742						}
19743						$x = $cellpos['startx'];
19744						$y = $parent['starty'];
19745						$starty = $y;
19746						$w = abs($cellpos['endx'] - $cellpos['startx']);
19747						// get border modes
19748						$border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
19749						$border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
19750						$border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19751						// design borders around HTML cells.
19752						for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
19753							$ccode = '';
19754							$this->setPage($page);
19755							if ($this->num_columns < 2) {
19756								// single-column mode
19757								$this->x = $x;
19758								$this->y = $this->tMargin;
19759							}
19760							// account for margin changes
19761							if ($page > $startpage) {
19762								if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
19763									$this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
19764								} elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
19765									$this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
19766								}
19767							}
19768							if ($startpage == $endpage) { // single page
19769								$deltacol = 0;
19770								$deltath = 0;
19771								for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
19772									$this->selectColumn($column);
19773									if ($startcolumn == $endcolumn) { // single column
19774										$cborder = $border;
19775										$h = $endy - $parent['starty'];
19776										$this->y = $y;
19777										$this->x = $x;
19778									} elseif ($column == $startcolumn) { // first column
19779										$cborder = $border_start;
19780										$this->y = $starty;
19781										$this->x = $x;
19782										$h = $this->h - $this->y - $this->bMargin;
19783										if ($this->rtl) {
19784											$deltacol = $this->x + $this->rMargin - $this->w;
19785										} else {
19786											$deltacol = $this->x - $this->lMargin;
19787										}
19788									} elseif ($column == $endcolumn) { // end column
19789										$cborder = $border_end;
19790										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19791											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19792										}
19793										$this->x += $deltacol;
19794										$h = $endy - $this->y;
19795									} else { // middle column
19796										$cborder = $border_middle;
19797										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19798											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19799										}
19800										$this->x += $deltacol;
19801										$h = $this->h - $this->y - $this->bMargin;
19802									}
19803									$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19804								} // end for each column
19805							} elseif ($page == $startpage) { // first page
19806								$deltacol = 0;
19807								$deltath = 0;
19808								for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
19809									$this->selectColumn($column);
19810									if ($column == $startcolumn) { // first column
19811										$cborder = $border_start;
19812										$this->y = $starty;
19813										$this->x = $x;
19814										$h = $this->h - $this->y - $this->bMargin;
19815										if ($this->rtl) {
19816											$deltacol = $this->x + $this->rMargin - $this->w;
19817										} else {
19818											$deltacol = $this->x - $this->lMargin;
19819										}
19820									} else { // middle column
19821										$cborder = $border_middle;
19822										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19823											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19824										}
19825										$this->x += $deltacol;
19826										$h = $this->h - $this->y - $this->bMargin;
19827									}
19828									$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19829								} // end for each column
19830							} elseif ($page == $endpage) { // last page
19831								$deltacol = 0;
19832								$deltath = 0;
19833								for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
19834									$this->selectColumn($column);
19835									if ($column == $endcolumn) { // end column
19836										$cborder = $border_end;
19837										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19838											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19839										}
19840										$this->x += $deltacol;
19841										$h = $endy - $this->y;
19842									} else { // middle column
19843										$cborder = $border_middle;
19844										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19845											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19846										}
19847										$this->x += $deltacol;
19848										$h = $this->h - $this->y - $this->bMargin;
19849									}
19850									$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19851								} // end for each column
19852							} else { // middle page
19853								$deltacol = 0;
19854								$deltath = 0;
19855								for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
19856									$this->selectColumn($column);
19857									$cborder = $border_middle;
19858									if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19859										$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19860									}
19861									$this->x += $deltacol;
19862									$h = $this->h - $this->y - $this->bMargin;
19863									$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19864								} // end for each column
19865							}
19866							if (!empty($cborder) OR !empty($fill)) {
19867								$offsetlen = strlen($ccode);
19868								// draw border and fill
19869								if ($this->inxobj) {
19870									// we are inside an XObject template
19871									if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
19872										$pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
19873										$pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
19874										$this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
19875									} else {
19876										$pagemark = $this->xobjects[$this->xobjid]['intmrk'];
19877										$this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
19878									}
19879									$pagebuff = $this->xobjects[$this->xobjid]['outdata'];
19880									$pstart = substr($pagebuff, 0, $pagemark);
19881									$pend = substr($pagebuff, $pagemark);
19882									$this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
19883								} else {
19884									// draw border and fill
19885									if (end($this->transfmrk[$this->page]) !== false) {
19886										$pagemarkkey = key($this->transfmrk[$this->page]);
19887										$pagemark = $this->transfmrk[$this->page][$pagemarkkey];
19888									} elseif ($this->InFooter) {
19889										$pagemark = $this->footerpos[$this->page];
19890									} else {
19891										$pagemark = $this->intmrk[$this->page];
19892									}
19893									$pagebuff = $this->getPageBuffer($this->page);
19894									$pstart = substr($pagebuff, 0, $pagemark);
19895									$pend = substr($pagebuff, $pagemark);
19896									$this->setPageBuffer($this->page, $pstart.$ccode.$pend);
19897								}
19898							}
19899						} // end for each page
19900						// restore default border
19901						$border = $default_border;
19902					} // end for each cell on the row
19903					if (isset($table_el['attribute']['cellspacing'])) {
19904						$this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
19905					} elseif (isset($table_el['border-spacing'])) {
19906						$this->y += $table_el['border-spacing']['V'];
19907					}
19908					$this->Ln(0, $cell);
19909					$this->x = $parent['startx'];
19910					if ($endpage > $startpage) {
19911						if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
19912							$this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
19913						} elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
19914							$this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
19915						}
19916					}
19917				}
19918				if (!$in_table_head) { // we are not inside a thead section
19919					$this->cell_padding = isset($table_el['old_cell_padding']) ? $table_el['old_cell_padding'] : null;
19920					// reset row height
19921					$this->resetLastH();
19922					if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) {
19923						$plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]);
19924						if (($plendiff > 0) AND ($plendiff < 60)) {
19925							$pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff);
19926							if (substr($pagediff, 0, 5) == 'BT /F') {
19927								// the difference is only a font setting
19928								$plendiff = 0;
19929							}
19930						}
19931						if ($plendiff == 0) {
19932							// remove last blank page
19933							$this->deletePage($this->numpages);
19934						}
19935					}
19936					if (isset($this->theadMargins['top'])) {
19937						// restore top margin
19938						$this->tMargin = $this->theadMargins['top'];
19939					}
19940					if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
19941						// reset main table header
19942						$this->thead = '';
19943						$this->theadMargins = array();
19944						$this->pagedim[$this->page]['tm'] = $this->tMargin;
19945					}
19946				}
19947				$parent = $table_el;
19948				break;
19949			}
19950			case 'a': {
19951				$this->HREF = array();
19952				break;
19953			}
19954			case 'sup': {
19955				$this->setXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
19956				break;
19957			}
19958			case 'sub': {
19959				$this->setXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k));
19960				break;
19961			}
19962			case 'div': {
19963				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19964				break;
19965			}
19966			case 'blockquote': {
19967				if ($this->rtl) {
19968					$this->rMargin -= $this->listindent;
19969				} else {
19970					$this->lMargin -= $this->listindent;
19971				}
19972				--$this->listindentlevel;
19973				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19974				break;
19975			}
19976			case 'p': {
19977				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19978				break;
19979			}
19980			case 'pre': {
19981				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19982				$this->premode = false;
19983				break;
19984			}
19985			case 'dl': {
19986				--$this->listnum;
19987				if ($this->listnum <= 0) {
19988					$this->listnum = 0;
19989					$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19990				} else {
19991					$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19992				}
19993				$this->resetLastH();
19994				break;
19995			}
19996			case 'dt': {
19997				$this->lispacer = '';
19998				$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19999				break;
20000			}
20001			case 'dd': {
20002				$this->lispacer = '';
20003				if ($this->rtl) {
20004					$this->rMargin -= $this->listindent;
20005				} else {
20006					$this->lMargin -= $this->listindent;
20007				}
20008				--$this->listindentlevel;
20009				$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
20010				break;
20011			}
20012			case 'ul':
20013			case 'ol': {
20014				--$this->listnum;
20015				$this->lispacer = '';
20016				if ($this->rtl) {
20017					$this->rMargin -= $this->listindent;
20018				} else {
20019					$this->lMargin -= $this->listindent;
20020				}
20021				--$this->listindentlevel;
20022				if ($this->listnum <= 0) {
20023					$this->listnum = 0;
20024					$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
20025				} else {
20026					$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
20027				}
20028				$this->resetLastH();
20029				break;
20030			}
20031			case 'li': {
20032				$this->lispacer = '';
20033				$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
20034				break;
20035			}
20036			case 'h1':
20037			case 'h2':
20038			case 'h3':
20039			case 'h4':
20040			case 'h5':
20041			case 'h6': {
20042				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
20043				break;
20044			}
20045			// Form fields (since 4.8.000 - 2009-09-07)
20046			case 'form': {
20047				$this->form_action = '';
20048				$this->form_enctype = 'application/x-www-form-urlencoded';
20049				break;
20050			}
20051			default : {
20052				break;
20053			}
20054		}
20055		// draw border and background (if any)
20056		$this->drawHTMLTagBorder($parent, $xmax);
20057		if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
20058			$pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
20059			// check for pagebreak
20060			if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
20061				// add a page (or trig AcceptPageBreak() for multicolumn mode)
20062				$this->checkPageBreak($this->PageBreakTrigger + 1);
20063			}
20064			if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
20065				OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
20066				// add a page (or trig AcceptPageBreak() for multicolumn mode)
20067				$this->checkPageBreak($this->PageBreakTrigger + 1);
20068			}
20069		}
20070		$this->tmprtl = false;
20071		return $dom;
20072	}
20073
20074	/**
20075	 * Add vertical spaces if needed.
20076	 * @param string $hbz Distance between current y and line bottom.
20077	 * @param string $hb The height of the break.
20078	 * @param boolean $cell if true add the default left (or right if RTL) padding to each new line (default false).
20079	 * @param boolean $firsttag set to true when the tag is the first.
20080	 * @param boolean $lasttag set to true when the tag is the last.
20081	 * @protected
20082	 */
20083	protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
20084		if ($firsttag) {
20085			$this->Ln(0, $cell);
20086			$this->htmlvspace = 0;
20087			return;
20088		}
20089		if ($lasttag) {
20090			$this->Ln($hbz, $cell);
20091			$this->htmlvspace = 0;
20092			return;
20093		}
20094		if ($hb < $this->htmlvspace) {
20095			$hd = 0;
20096		} else {
20097			$hd = $hb - $this->htmlvspace;
20098			$this->htmlvspace = $hb;
20099		}
20100		$this->Ln(($hbz + $hd), $cell);
20101	}
20102
20103	/**
20104	 * Return the starting coordinates to draw an html border
20105	 * @return array containing top-left border coordinates
20106	 * @protected
20107	 * @since 5.7.000 (2010-08-03)
20108	 */
20109	protected function getBorderStartPosition() {
20110		if ($this->rtl) {
20111			$xmax = $this->lMargin;
20112		} else {
20113			$xmax = $this->w - $this->rMargin;
20114		}
20115		return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax);
20116	}
20117
20118	/**
20119	 * Draw an HTML block border and fill
20120	 * @param array $tag array of tag properties.
20121	 * @param int $xmax end X coordinate for border.
20122	 * @protected
20123	 * @since 5.7.000 (2010-08-03)
20124	 */
20125	protected function drawHTMLTagBorder($tag, $xmax) {
20126		if (!isset($tag['borderposition'])) {
20127			// nothing to draw
20128			return;
20129		}
20130		$prev_x = $this->x;
20131		$prev_y = $this->y;
20132		$prev_lasth = $this->lasth;
20133		$border = 0;
20134		$fill = false;
20135		$this->lasth = 0;
20136		if (isset($tag['border']) AND !empty($tag['border'])) {
20137			// get border style
20138			$border = $tag['border'];
20139			if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
20140				// border for table header
20141				$border = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
20142			}
20143		}
20144		if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
20145			// get background color
20146			$old_bgcolor = $this->bgcolor;
20147			$this->setFillColorArray($tag['bgcolor']);
20148			$fill = true;
20149		}
20150		if (!$border AND !$fill) {
20151			// nothing to draw
20152			return;
20153		}
20154		if (isset($tag['attribute']['cellspacing'])) {
20155			$clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
20156			$cellspacing = array('H' => $clsp, 'V' => $clsp);
20157		} elseif (isset($tag['border-spacing'])) {
20158			$cellspacing = $tag['border-spacing'];
20159		} else {
20160			$cellspacing = array('H' => 0, 'V' => 0);
20161		}
20162		if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
20163			// draw the border externally respect the sqare edge.
20164			$border['mode'] = 'ext';
20165		}
20166		if ($this->rtl) {
20167			if ($xmax >= $tag['borderposition']['x']) {
20168				$xmax = $tag['borderposition']['xmax'];
20169			}
20170			$w = ($tag['borderposition']['x'] - $xmax);
20171		} else {
20172			if ($xmax <= $tag['borderposition']['x']) {
20173				$xmax = $tag['borderposition']['xmax'];
20174			}
20175			$w = ($xmax - $tag['borderposition']['x']);
20176		}
20177		if ($w <= 0) {
20178			return;
20179		}
20180		$w += $cellspacing['H'];
20181		$startpage = $tag['borderposition']['page'];
20182		$startcolumn = $tag['borderposition']['column'];
20183		$x = $tag['borderposition']['x'];
20184		$y = $tag['borderposition']['y'];
20185		$endpage = $this->page;
20186		$starty = $tag['borderposition']['y'] - $cellspacing['V'];
20187		$currentY = $this->y;
20188		$this->x = $x;
20189		// get latest column
20190		$endcolumn = $this->current_column;
20191		if ($this->num_columns == 0) {
20192			$this->num_columns = 1;
20193		}
20194		// get border modes
20195		$border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
20196		$border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
20197		$border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
20198		// temporary disable page regions
20199		$temp_page_regions = $this->page_regions;
20200		$this->page_regions = array();
20201		// design borders around HTML cells.
20202		for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
20203			$ccode = '';
20204			$this->setPage($page);
20205			if ($this->num_columns < 2) {
20206				// single-column mode
20207				$this->x = $x;
20208				$this->y = $this->tMargin;
20209			}
20210			// account for margin changes
20211			if ($page > $startpage) {
20212				if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
20213					$this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
20214				} elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
20215					$this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
20216				}
20217			}
20218			if ($startpage == $endpage) {
20219				// single page
20220				for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
20221					$this->selectColumn($column);
20222					if ($startcolumn == $endcolumn) { // single column
20223						$cborder = $border;
20224						$h = ($currentY - $y) + $cellspacing['V'];
20225						$this->y = $starty;
20226					} elseif ($column == $startcolumn) { // first column
20227						$cborder = $border_start;
20228						$this->y = $starty;
20229						$h = $this->h - $this->y - $this->bMargin;
20230					} elseif ($column == $endcolumn) { // end column
20231						$cborder = $border_end;
20232						$h = $currentY - $this->y;
20233					} else { // middle column
20234						$cborder = $border_middle;
20235						$h = $this->h - $this->y - $this->bMargin;
20236					}
20237					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20238				} // end for each column
20239			} elseif ($page == $startpage) { // first page
20240				for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
20241					$this->selectColumn($column);
20242					if ($column == $startcolumn) { // first column
20243						$cborder = $border_start;
20244						$this->y = $starty;
20245						$h = $this->h - $this->y - $this->bMargin;
20246					} else { // middle column
20247						$cborder = $border_middle;
20248						$h = $this->h - $this->y - $this->bMargin;
20249					}
20250					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20251				} // end for each column
20252			} elseif ($page == $endpage) { // last page
20253				for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
20254					$this->selectColumn($column);
20255					if ($column == $endcolumn) {
20256						// end column
20257						$cborder = $border_end;
20258						$h = $currentY - $this->y;
20259					} else {
20260						// middle column
20261						$cborder = $border_middle;
20262						$h = $this->h - $this->y - $this->bMargin;
20263					}
20264					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20265				} // end for each column
20266			} else { // middle page
20267				for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
20268					$this->selectColumn($column);
20269					$cborder = $border_middle;
20270					$h = $this->h - $this->y - $this->bMargin;
20271					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20272				} // end for each column
20273			}
20274			if ($cborder OR $fill) {
20275				$offsetlen = strlen($ccode);
20276				// draw border and fill
20277				if ($this->inxobj) {
20278					// we are inside an XObject template
20279					if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
20280						$pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
20281						$pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
20282						$this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
20283					} else {
20284						$pagemark = $this->xobjects[$this->xobjid]['intmrk'];
20285						$this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
20286					}
20287					$pagebuff = $this->xobjects[$this->xobjid]['outdata'];
20288					$pstart = substr($pagebuff, 0, $pagemark);
20289					$pend = substr($pagebuff, $pagemark);
20290					$this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
20291				} else {
20292					if (end($this->transfmrk[$this->page]) !== false) {
20293						$pagemarkkey = key($this->transfmrk[$this->page]);
20294						$pagemark = $this->transfmrk[$this->page][$pagemarkkey];
20295					} elseif ($this->InFooter) {
20296						$pagemark = $this->footerpos[$this->page];
20297					} else {
20298						$pagemark = $this->intmrk[$this->page];
20299					}
20300					$pagebuff = $this->getPageBuffer($this->page);
20301					$pstart = substr($pagebuff, 0, $pagemark);
20302					$pend = substr($pagebuff, $pagemark);
20303					$this->setPageBuffer($this->page, $pstart.$ccode.$pend);
20304					$this->bordermrk[$this->page] += $offsetlen;
20305					$this->cntmrk[$this->page] += $offsetlen;
20306				}
20307			}
20308		} // end for each page
20309		// restore page regions
20310		$this->page_regions = $temp_page_regions;
20311		if (isset($old_bgcolor)) {
20312			// restore background color
20313			$this->setFillColorArray($old_bgcolor);
20314		}
20315		// restore pointer position
20316		$this->x = $prev_x;
20317		$this->y = $prev_y;
20318		$this->lasth = $prev_lasth;
20319	}
20320
20321	/**
20322	 * Set the default bullet to be used as LI bullet symbol
20323	 * @param string $symbol character or string to be used (legal values are: '' = automatic, '!' = auto bullet, '#' = auto numbering, 'disc', 'disc', 'circle', 'square', '1', 'decimal', 'decimal-leading-zero', 'i', 'lower-roman', 'I', 'upper-roman', 'a', 'lower-alpha', 'lower-latin', 'A', 'upper-alpha', 'upper-latin', 'lower-greek', 'img|type|width|height|image.ext')
20324	 * @public
20325	 * @since 4.0.028 (2008-09-26)
20326	 */
20327	public function setLIsymbol($symbol='!') {
20328		// check for custom image symbol
20329		if (substr($symbol, 0, 4) == 'img|') {
20330			$this->lisymbol = $symbol;
20331			return;
20332		}
20333		$symbol = strtolower($symbol);
20334		$valid_symbols = array('!', '#', 'disc', 'circle', 'square', '1', 'decimal', 'decimal-leading-zero', 'i', 'lower-roman', 'I', 'upper-roman', 'a', 'lower-alpha', 'lower-latin', 'A', 'upper-alpha', 'upper-latin', 'lower-greek');
20335		if (in_array($symbol, $valid_symbols)) {
20336			$this->lisymbol = $symbol;
20337		} else {
20338			$this->lisymbol = '';
20339		}
20340	}
20341
20342	/**
20343	 * Set the booklet mode for double-sided pages.
20344	 * @param boolean $booklet true set the booklet mode on, false otherwise.
20345	 * @param float $inner Inner page margin.
20346	 * @param float $outer Outer page margin.
20347	 * @public
20348	 * @since 4.2.000 (2008-10-29)
20349	 */
20350	public function setBooklet($booklet=true, $inner=-1, $outer=-1) {
20351		$this->booklet = $booklet;
20352		if ($inner >= 0) {
20353			$this->lMargin = $inner;
20354		}
20355		if ($outer >= 0) {
20356			$this->rMargin = $outer;
20357		}
20358	}
20359
20360	/**
20361	 * Swap the left and right margins.
20362	 * @param boolean $reverse if true swap left and right margins.
20363	 * @protected
20364	 * @since 4.2.000 (2008-10-29)
20365	 */
20366	protected function swapMargins($reverse=true) {
20367		if ($reverse) {
20368			// swap left and right margins
20369			$mtemp = $this->original_lMargin;
20370			$this->original_lMargin = $this->original_rMargin;
20371			$this->original_rMargin = $mtemp;
20372			$deltam = $this->original_lMargin - $this->original_rMargin;
20373			$this->lMargin += $deltam;
20374			$this->rMargin -= $deltam;
20375		}
20376	}
20377
20378	/**
20379	 * Set the vertical spaces for HTML tags.
20380	 * The array must have the following structure (example):
20381	 * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1)));
20382	 * The first array level contains the tag names,
20383	 * the second level contains 0 for opening tags or 1 for closing tags,
20384	 * the third level contains the vertical space unit (h) and the number spaces to add (n).
20385	 * If the h parameter is not specified, default values are used.
20386	 * @param array $tagvs array of tags and relative vertical spaces.
20387	 * @public
20388	 * @since 4.2.001 (2008-10-30)
20389	 */
20390	public function setHtmlVSpace($tagvs) {
20391		$this->tagvspaces = $tagvs;
20392	}
20393
20394	/**
20395	 * Set custom width for list indentation.
20396	 * @param float $width width of the indentation. Use negative value to disable it.
20397	 * @public
20398	 * @since 4.2.007 (2008-11-12)
20399	 */
20400	public function setListIndentWidth($width) {
20401		return $this->customlistindent = floatval($width);
20402	}
20403
20404	/**
20405	 * Set the top/bottom cell sides to be open or closed when the cell cross the page.
20406	 * @param boolean $isopen if true keeps the top/bottom border open for the cell sides that cross the page.
20407	 * @public
20408	 * @since 4.2.010 (2008-11-14)
20409	 */
20410	public function setOpenCell($isopen) {
20411		$this->opencell = $isopen;
20412	}
20413
20414	/**
20415	 * Set the color and font style for HTML links.
20416	 * @param array $color RGB array of colors
20417	 * @param string $fontstyle additional font styles to add
20418	 * @public
20419	 * @since 4.4.003 (2008-12-09)
20420	 */
20421	public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
20422		$this->htmlLinkColorArray = $color;
20423		$this->htmlLinkFontStyle = $fontstyle;
20424	}
20425
20426	/**
20427	 * Convert HTML string containing value and unit of measure to user's units or points.
20428	 * @param string $htmlval String containing values and unit.
20429	 * @param string $refsize Reference value in points.
20430	 * @param string $defaultunit Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
20431	 * @param boolean $points If true returns points, otherwise returns value in user's units.
20432	 * @return float value in user's unit or point if $points=true
20433	 * @public
20434	 * @since 4.4.004 (2008-12-10)
20435	 */
20436	public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
20437		$supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
20438		$retval = 0;
20439		$value = 0;
20440		$unit = 'px';
20441		if ($points) {
20442			$k = 1;
20443		} else {
20444			$k = $this->k;
20445		}
20446		if (in_array($defaultunit, $supportedunits)) {
20447			$unit = $defaultunit;
20448		}
20449		if (is_numeric($htmlval)) {
20450			$value = floatval($htmlval);
20451		} elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
20452			$value = floatval($mnum[1]);
20453			if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
20454				if (in_array($munit[1], $supportedunits)) {
20455					$unit = $munit[1];
20456				}
20457			}
20458		}
20459		switch ($unit) {
20460			// percentage
20461			case '%': {
20462				$retval = (($value * $refsize) / 100);
20463				break;
20464			}
20465			// relative-size
20466			case 'em': {
20467				$retval = ($value * $refsize);
20468				break;
20469			}
20470			// height of lower case 'x' (about half the font-size)
20471			case 'ex': {
20472				$retval = ($value * ($refsize / 2));
20473				break;
20474			}
20475			// absolute-size
20476			case 'in': {
20477				$retval = (($value * $this->dpi) / $k);
20478				break;
20479			}
20480			// centimeters
20481			case 'cm': {
20482				$retval = (($value / 2.54 * $this->dpi) / $k);
20483				break;
20484			}
20485			// millimeters
20486			case 'mm': {
20487				$retval = (($value / 25.4 * $this->dpi) / $k);
20488				break;
20489			}
20490			// one pica is 12 points
20491			case 'pc': {
20492				$retval = (($value * 12) / $k);
20493				break;
20494			}
20495			// points
20496			case 'pt': {
20497				$retval = ($value / $k);
20498				break;
20499			}
20500			// pixels
20501			case 'px': {
20502				$retval = $this->pixelsToUnits($value);
20503				if ($points) {
20504					$retval *= $this->k;
20505				}
20506				break;
20507			}
20508		}
20509		return $retval;
20510	}
20511
20512	/**
20513	 * Output an HTML list bullet or ordered item symbol
20514	 * @param int $listdepth list nesting level
20515	 * @param string $listtype type of list
20516	 * @param float $size current font size
20517	 * @protected
20518	 * @since 4.4.004 (2008-12-10)
20519	 */
20520	protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
20521		if ($this->state != 2) {
20522			return;
20523		}
20524		$size /= $this->k;
20525		$fill = '';
20526		$bgcolor = $this->bgcolor;
20527		$color = $this->fgcolor;
20528		$strokecolor = $this->strokecolor;
20529		$width = 0;
20530		$textitem = '';
20531		$tmpx = $this->x;
20532		$lspace = $this->GetStringWidth('  ');
20533		if ($listtype == '^') {
20534			// special symbol used for avoid justification of rect bullet
20535			$this->lispacer = '';
20536			return;
20537		} elseif ($listtype == '!') {
20538			// set default list type for unordered list
20539			$deftypes = array('disc', 'circle', 'square');
20540			$listtype = $deftypes[($listdepth - 1) % 3];
20541		} elseif ($listtype == '#') {
20542			// set default list type for ordered list
20543			$listtype = 'decimal';
20544		} elseif (substr($listtype, 0, 4) == 'img|') {
20545			// custom image type ('img|type|width|height|image.ext')
20546			$img = explode('|', $listtype);
20547			$listtype = 'img';
20548		}
20549		switch ($listtype) {
20550			// unordered types
20551			case 'none': {
20552				break;
20553			}
20554			case 'disc': {
20555				$r = $size / 6;
20556				$lspace += (2 * $r);
20557				if ($this->rtl) {
20558					$this->x += $lspace;
20559				} else {
20560					$this->x -= $lspace;
20561				}
20562				$this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8);
20563				break;
20564			}
20565			case 'circle': {
20566				$r = $size / 6;
20567				$lspace += (2 * $r);
20568				if ($this->rtl) {
20569					$this->x += $lspace;
20570				} else {
20571					$this->x -= $lspace;
20572				}
20573				$prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor;
20574				$new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
20575				$this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
20576				$this->_out($prev_line_style); // restore line settings
20577				break;
20578			}
20579			case 'square': {
20580				$l = $size / 3;
20581				$lspace += $l;
20582				if ($this->rtl) {;
20583					$this->x += $lspace;
20584				} else {
20585					$this->x -= $lspace;
20586				}
20587				$this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color);
20588				break;
20589			}
20590			case 'img': {
20591				// 1=>type, 2=>width, 3=>height, 4=>image.ext
20592				$lspace += $img[2];
20593				if ($this->rtl) {;
20594					$this->x += $lspace;
20595				} else {
20596					$this->x -= $lspace;
20597				}
20598				$imgtype = strtolower($img[1]);
20599				$prev_y = $this->y;
20600				switch ($imgtype) {
20601					case 'svg': {
20602						$this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
20603						break;
20604					}
20605					case 'ai':
20606					case 'eps': {
20607						$this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
20608						break;
20609					}
20610					default: {
20611						$this->Image($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], $img[1], '', 'T', false, 300, '', false, false, 0, false, false, false);
20612						break;
20613					}
20614				}
20615				$this->y = $prev_y;
20616				break;
20617			}
20618			// ordered types
20619			// $this->listcount[$this->listnum];
20620			// $textitem
20621			case '1':
20622			case 'decimal': {
20623				$textitem = $this->listcount[$this->listnum];
20624				break;
20625			}
20626			case 'decimal-leading-zero': {
20627				$textitem = sprintf('%02d', $this->listcount[$this->listnum]);
20628				break;
20629			}
20630			case 'i':
20631			case 'lower-roman': {
20632				$textitem = strtolower(TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]));
20633				break;
20634			}
20635			case 'I':
20636			case 'upper-roman': {
20637				$textitem = TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]);
20638				break;
20639			}
20640			case 'a':
20641			case 'lower-alpha':
20642			case 'lower-latin': {
20643				$textitem = chr(97 + $this->listcount[$this->listnum] - 1);
20644				break;
20645			}
20646			case 'A':
20647			case 'upper-alpha':
20648			case 'upper-latin': {
20649				$textitem = chr(65 + $this->listcount[$this->listnum] - 1);
20650				break;
20651			}
20652			case 'lower-greek': {
20653				$textitem = TCPDF_FONTS::unichr((945 + $this->listcount[$this->listnum] - 1), $this->isunicode);
20654				break;
20655			}
20656			/*
20657			// Types to be implemented (special handling)
20658			case 'hebrew': {
20659				break;
20660			}
20661			case 'armenian': {
20662				break;
20663			}
20664			case 'georgian': {
20665				break;
20666			}
20667			case 'cjk-ideographic': {
20668				break;
20669			}
20670			case 'hiragana': {
20671				break;
20672			}
20673			case 'katakana': {
20674				break;
20675			}
20676			case 'hiragana-iroha': {
20677				break;
20678			}
20679			case 'katakana-iroha': {
20680				break;
20681			}
20682			*/
20683			default: {
20684				$textitem = $this->listcount[$this->listnum];
20685			}
20686		}
20687		if (!TCPDF_STATIC::empty_string($textitem)) {
20688			// Check whether we need a new page or new column
20689			$prev_y = $this->y;
20690			$h = $this->getCellHeight($this->FontSize);
20691			if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) {
20692				$tmpx = $this->x;
20693			}
20694			// print ordered item
20695			if ($this->rtl) {
20696				$textitem = '.'.$textitem;
20697			} else {
20698				$textitem = $textitem.'.';
20699			}
20700			$lspace += $this->GetStringWidth($textitem);
20701			if ($this->rtl) {
20702				$this->x += $lspace;
20703			} else {
20704				$this->x -= $lspace;
20705			}
20706			$this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
20707		}
20708		$this->x = $tmpx;
20709		$this->lispacer = '^';
20710		// restore colors
20711		$this->setFillColorArray($bgcolor);
20712		$this->setDrawColorArray($strokecolor);
20713		$this->settextColorArray($color);
20714	}
20715
20716	/**
20717	 * Returns current graphic variables as array.
20718	 * @return array of graphic variables
20719	 * @protected
20720	 * @since 4.2.010 (2008-11-14)
20721	 */
20722	protected function getGraphicVars() {
20723		$grapvars = array(
20724			'FontFamily' => $this->FontFamily,
20725			'FontStyle' => $this->FontStyle,
20726			'FontSizePt' => $this->FontSizePt,
20727			'rMargin' => $this->rMargin,
20728			'lMargin' => $this->lMargin,
20729			'cell_padding' => $this->cell_padding,
20730			'cell_margin' => $this->cell_margin,
20731			'LineWidth' => $this->LineWidth,
20732			'linestyleWidth' => $this->linestyleWidth,
20733			'linestyleCap' => $this->linestyleCap,
20734			'linestyleJoin' => $this->linestyleJoin,
20735			'linestyleDash' => $this->linestyleDash,
20736			'textrendermode' => $this->textrendermode,
20737			'textstrokewidth' => $this->textstrokewidth,
20738			'DrawColor' => $this->DrawColor,
20739			'FillColor' => $this->FillColor,
20740			'TextColor' => $this->TextColor,
20741			'ColorFlag' => $this->ColorFlag,
20742			'bgcolor' => $this->bgcolor,
20743			'fgcolor' => $this->fgcolor,
20744			'htmlvspace' => $this->htmlvspace,
20745			'listindent' => $this->listindent,
20746			'listindentlevel' => $this->listindentlevel,
20747			'listnum' => $this->listnum,
20748			'listordered' => $this->listordered,
20749			'listcount' => $this->listcount,
20750			'lispacer' => $this->lispacer,
20751			'cell_height_ratio' => $this->cell_height_ratio,
20752			'font_stretching' => $this->font_stretching,
20753			'font_spacing' => $this->font_spacing,
20754			'alpha' => $this->alpha,
20755			// extended
20756			'lasth' => $this->lasth,
20757			'tMargin' => $this->tMargin,
20758			'bMargin' => $this->bMargin,
20759			'AutoPageBreak' => $this->AutoPageBreak,
20760			'PageBreakTrigger' => $this->PageBreakTrigger,
20761			'x' => $this->x,
20762			'y' => $this->y,
20763			'w' => $this->w,
20764			'h' => $this->h,
20765			'wPt' => $this->wPt,
20766			'hPt' => $this->hPt,
20767			'fwPt' => $this->fwPt,
20768			'fhPt' => $this->fhPt,
20769			'page' => $this->page,
20770			'current_column' => $this->current_column,
20771			'num_columns' => $this->num_columns
20772			);
20773		return $grapvars;
20774	}
20775
20776	/**
20777	 * Set graphic variables.
20778	 * @param array $gvars array of graphic variablesto restore
20779	 * @param boolean $extended if true restore extended graphic variables
20780	 * @protected
20781	 * @since 4.2.010 (2008-11-14)
20782	 */
20783	protected function setGraphicVars($gvars, $extended=false) {
20784		if ($this->state != 2) {
20785			 return;
20786		}
20787		$this->FontFamily = $gvars['FontFamily'];
20788		$this->FontStyle = $gvars['FontStyle'];
20789		$this->FontSizePt = $gvars['FontSizePt'];
20790		$this->rMargin = $gvars['rMargin'];
20791		$this->lMargin = $gvars['lMargin'];
20792		$this->cell_padding = $gvars['cell_padding'];
20793		$this->cell_margin = $gvars['cell_margin'];
20794		$this->LineWidth = $gvars['LineWidth'];
20795		$this->linestyleWidth = $gvars['linestyleWidth'];
20796		$this->linestyleCap = $gvars['linestyleCap'];
20797		$this->linestyleJoin = $gvars['linestyleJoin'];
20798		$this->linestyleDash = $gvars['linestyleDash'];
20799		$this->textrendermode = $gvars['textrendermode'];
20800		$this->textstrokewidth = $gvars['textstrokewidth'];
20801		$this->DrawColor = $gvars['DrawColor'];
20802		$this->FillColor = $gvars['FillColor'];
20803		$this->TextColor = $gvars['TextColor'];
20804		$this->ColorFlag = $gvars['ColorFlag'];
20805		$this->bgcolor = $gvars['bgcolor'];
20806		$this->fgcolor = $gvars['fgcolor'];
20807		$this->htmlvspace = $gvars['htmlvspace'];
20808		$this->listindent = $gvars['listindent'];
20809		$this->listindentlevel = $gvars['listindentlevel'];
20810		$this->listnum = $gvars['listnum'];
20811		$this->listordered = $gvars['listordered'];
20812		$this->listcount = $gvars['listcount'];
20813		$this->lispacer = $gvars['lispacer'];
20814		$this->cell_height_ratio = $gvars['cell_height_ratio'];
20815		$this->font_stretching = $gvars['font_stretching'];
20816		$this->font_spacing = $gvars['font_spacing'];
20817		$this->alpha = $gvars['alpha'];
20818		if ($extended) {
20819			// restore extended values
20820			$this->lasth = $gvars['lasth'];
20821			$this->tMargin = $gvars['tMargin'];
20822			$this->bMargin = $gvars['bMargin'];
20823			$this->AutoPageBreak = $gvars['AutoPageBreak'];
20824			$this->PageBreakTrigger = $gvars['PageBreakTrigger'];
20825			$this->x = $gvars['x'];
20826			$this->y = $gvars['y'];
20827			$this->w = $gvars['w'];
20828			$this->h = $gvars['h'];
20829			$this->wPt = $gvars['wPt'];
20830			$this->hPt = $gvars['hPt'];
20831			$this->fwPt = $gvars['fwPt'];
20832			$this->fhPt = $gvars['fhPt'];
20833			$this->page = $gvars['page'];
20834			$this->current_column = $gvars['current_column'];
20835			$this->num_columns = $gvars['num_columns'];
20836		}
20837		$this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
20838		if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
20839			$this->setFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
20840		}
20841	}
20842
20843	/**
20844	 * Outputs the "save graphics state" operator 'q'
20845	 * @protected
20846	 */
20847	protected function _outSaveGraphicsState() {
20848		$this->_out('q');
20849	}
20850
20851	/**
20852	 * Outputs the "restore graphics state" operator 'Q'
20853	 * @protected
20854	 */
20855	protected function _outRestoreGraphicsState() {
20856		$this->_out('Q');
20857	}
20858
20859	/**
20860	 * Set buffer content (always append data).
20861	 * @param string $data data
20862	 * @protected
20863	 * @since 4.5.000 (2009-01-02)
20864	 */
20865	protected function setBuffer($data) {
20866		$this->bufferlen += strlen($data);
20867		$this->buffer .= $data;
20868	}
20869
20870	/**
20871	 * Replace the buffer content
20872	 * @param string $data data
20873	 * @protected
20874	 * @since 5.5.000 (2010-06-22)
20875	 */
20876	protected function replaceBuffer($data) {
20877		$this->bufferlen = strlen($data);
20878		$this->buffer = $data;
20879	}
20880
20881	/**
20882	 * Get buffer content.
20883	 * @return string buffer content
20884	 * @protected
20885	 * @since 4.5.000 (2009-01-02)
20886	 */
20887	protected function getBuffer() {
20888		return $this->buffer;
20889	}
20890
20891	/**
20892	 * Set page buffer content.
20893	 * @param int $page page number
20894	 * @param string $data page data
20895	 * @param boolean $append if true append data, false replace.
20896	 * @protected
20897	 * @since 4.5.000 (2008-12-31)
20898	 */
20899	protected function setPageBuffer($page, $data, $append=false) {
20900		if ($append) {
20901			$this->pages[$page] .= $data;
20902		} else {
20903			$this->pages[$page] = $data;
20904		}
20905		if ($append AND isset($this->pagelen[$page])) {
20906			$this->pagelen[$page] += strlen($data);
20907		} else {
20908			$this->pagelen[$page] = strlen($data);
20909		}
20910	}
20911
20912	/**
20913	 * Get page buffer content.
20914	 * @param int $page page number
20915	 * @return string page buffer content or false in case of error
20916	 * @protected
20917	 * @since 4.5.000 (2008-12-31)
20918	 */
20919	protected function getPageBuffer($page) {
20920		if (isset($this->pages[$page])) {
20921			return $this->pages[$page];
20922		}
20923		return false;
20924	}
20925
20926	/**
20927	 * Set image buffer content.
20928	 * @param string $image image key
20929	 * @param array $data image data
20930	 * @return int image index number
20931	 * @protected
20932	 * @since 4.5.000 (2008-12-31)
20933	 */
20934	protected function setImageBuffer($image, $data) {
20935		if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) {
20936			$this->imagekeys[$this->numimages] = $image;
20937			$data['i'] = $this->numimages;
20938			++$this->numimages;
20939		}
20940		$this->images[$image] = $data;
20941		return $data['i'];
20942	}
20943
20944	/**
20945	 * Set image buffer content for a specified sub-key.
20946	 * @param string $image image key
20947	 * @param string $key image sub-key
20948	 * @param array $data image data
20949	 * @protected
20950	 * @since 4.5.000 (2008-12-31)
20951	 */
20952	protected function setImageSubBuffer($image, $key, $data) {
20953		if (!isset($this->images[$image])) {
20954			$this->setImageBuffer($image, array());
20955		}
20956		$this->images[$image][$key] = $data;
20957	}
20958
20959	/**
20960	 * Get image buffer content.
20961	 * @param string $image image key
20962	 * @return string|false image buffer content or false in case of error
20963	 * @protected
20964	 * @since 4.5.000 (2008-12-31)
20965	 */
20966	protected function getImageBuffer($image) {
20967		if (isset($this->images[$image])) {
20968			return $this->images[$image];
20969		}
20970		return false;
20971	}
20972
20973	/**
20974	 * Set font buffer content.
20975	 * @param string $font font key
20976	 * @param array $data font data
20977	 * @protected
20978	 * @since 4.5.000 (2009-01-02)
20979	 */
20980	protected function setFontBuffer($font, $data) {
20981		$this->fonts[$font] = $data;
20982		if (!in_array($font, $this->fontkeys)) {
20983			$this->fontkeys[] = $font;
20984			// store object ID for current font
20985			++$this->n;
20986			$this->font_obj_ids[$font] = $this->n;
20987			$this->setFontSubBuffer($font, 'n', $this->n);
20988		}
20989	}
20990
20991	/**
20992	 * Set font buffer content.
20993	 * @param string $font font key
20994	 * @param string $key font sub-key
20995	 * @param mixed $data font data
20996	 * @protected
20997	 * @since 4.5.000 (2009-01-02)
20998	 */
20999	protected function setFontSubBuffer($font, $key, $data) {
21000		if (!isset($this->fonts[$font])) {
21001			$this->setFontBuffer($font, array());
21002		}
21003		$this->fonts[$font][$key] = $data;
21004	}
21005
21006	/**
21007	 * Get font buffer content.
21008	 * @param string $font font key
21009	 * @return string|false font buffer content or false in case of error
21010	 * @protected
21011	 * @since 4.5.000 (2009-01-02)
21012	 */
21013	protected function getFontBuffer($font) {
21014		if (isset($this->fonts[$font])) {
21015			return $this->fonts[$font];
21016		}
21017		return false;
21018	}
21019
21020	/**
21021	 * Move a page to a previous position.
21022	 * @param int $frompage number of the source page
21023	 * @param int $topage number of the destination page (must be less than $frompage)
21024	 * @return bool true in case of success, false in case of error.
21025	 * @public
21026	 * @since 4.5.000 (2009-01-02)
21027	 */
21028	public function movePage($frompage, $topage) {
21029		if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
21030			return false;
21031		}
21032		if ($frompage == $this->page) {
21033			// close the page before moving it
21034			$this->endPage();
21035		}
21036		// move all page-related states
21037		$tmppage = $this->getPageBuffer($frompage);
21038		$tmppagedim = $this->pagedim[$frompage];
21039		$tmppagelen = $this->pagelen[$frompage];
21040		$tmpintmrk = $this->intmrk[$frompage];
21041		$tmpbordermrk = $this->bordermrk[$frompage];
21042		$tmpcntmrk = $this->cntmrk[$frompage];
21043		$tmppageobjects = $this->pageobjects[$frompage];
21044		if (isset($this->footerpos[$frompage])) {
21045			$tmpfooterpos = $this->footerpos[$frompage];
21046		}
21047		if (isset($this->footerlen[$frompage])) {
21048			$tmpfooterlen = $this->footerlen[$frompage];
21049		}
21050		if (isset($this->transfmrk[$frompage])) {
21051			$tmptransfmrk = $this->transfmrk[$frompage];
21052		}
21053		if (isset($this->PageAnnots[$frompage])) {
21054			$tmpannots = $this->PageAnnots[$frompage];
21055		}
21056		if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
21057			for ($i = $frompage; $i > $topage; --$i) {
21058				if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) {
21059					--$this->pagegroups[$this->newpagegroup[$i]];
21060					break;
21061				}
21062			}
21063			for ($i = $topage; $i > 0; --$i) {
21064				if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) {
21065					++$this->pagegroups[$this->newpagegroup[$i]];
21066					break;
21067				}
21068			}
21069		}
21070		for ($i = $frompage; $i > $topage; --$i) {
21071			$j = $i - 1;
21072			// shift pages down
21073			$this->setPageBuffer($i, $this->getPageBuffer($j));
21074			$this->pagedim[$i] = $this->pagedim[$j];
21075			$this->pagelen[$i] = $this->pagelen[$j];
21076			$this->intmrk[$i] = $this->intmrk[$j];
21077			$this->bordermrk[$i] = $this->bordermrk[$j];
21078			$this->cntmrk[$i] = $this->cntmrk[$j];
21079			$this->pageobjects[$i] = $this->pageobjects[$j];
21080			if (isset($this->footerpos[$j])) {
21081				$this->footerpos[$i] = $this->footerpos[$j];
21082			} elseif (isset($this->footerpos[$i])) {
21083				unset($this->footerpos[$i]);
21084			}
21085			if (isset($this->footerlen[$j])) {
21086				$this->footerlen[$i] = $this->footerlen[$j];
21087			} elseif (isset($this->footerlen[$i])) {
21088				unset($this->footerlen[$i]);
21089			}
21090			if (isset($this->transfmrk[$j])) {
21091				$this->transfmrk[$i] = $this->transfmrk[$j];
21092			} elseif (isset($this->transfmrk[$i])) {
21093				unset($this->transfmrk[$i]);
21094			}
21095			if (isset($this->PageAnnots[$j])) {
21096				$this->PageAnnots[$i] = $this->PageAnnots[$j];
21097			} elseif (isset($this->PageAnnots[$i])) {
21098				unset($this->PageAnnots[$i]);
21099			}
21100			if (isset($this->newpagegroup[$j])) {
21101				$this->newpagegroup[$i] = $this->newpagegroup[$j];
21102				unset($this->newpagegroup[$j]);
21103			}
21104			if ($this->currpagegroup == $j) {
21105				$this->currpagegroup = $i;
21106			}
21107		}
21108		$this->setPageBuffer($topage, $tmppage);
21109		$this->pagedim[$topage] = $tmppagedim;
21110		$this->pagelen[$topage] = $tmppagelen;
21111		$this->intmrk[$topage] = $tmpintmrk;
21112		$this->bordermrk[$topage] = $tmpbordermrk;
21113		$this->cntmrk[$topage] = $tmpcntmrk;
21114		$this->pageobjects[$topage] = $tmppageobjects;
21115		if (isset($tmpfooterpos)) {
21116			$this->footerpos[$topage] = $tmpfooterpos;
21117		} elseif (isset($this->footerpos[$topage])) {
21118			unset($this->footerpos[$topage]);
21119		}
21120		if (isset($tmpfooterlen)) {
21121			$this->footerlen[$topage] = $tmpfooterlen;
21122		} elseif (isset($this->footerlen[$topage])) {
21123			unset($this->footerlen[$topage]);
21124		}
21125		if (isset($tmptransfmrk)) {
21126			$this->transfmrk[$topage] = $tmptransfmrk;
21127		} elseif (isset($this->transfmrk[$topage])) {
21128			unset($this->transfmrk[$topage]);
21129		}
21130		if (isset($tmpannots)) {
21131			$this->PageAnnots[$topage] = $tmpannots;
21132		} elseif (isset($this->PageAnnots[$topage])) {
21133			unset($this->PageAnnots[$topage]);
21134		}
21135		// adjust outlines
21136		$tmpoutlines = $this->outlines;
21137		foreach ($tmpoutlines as $key => $outline) {
21138			if (!$outline['f']) {
21139				if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
21140					$this->outlines[$key]['p'] = ($outline['p'] + 1);
21141				} elseif ($outline['p'] == $frompage) {
21142					$this->outlines[$key]['p'] = $topage;
21143				}
21144			}
21145		}
21146		// adjust dests
21147		$tmpdests = $this->dests;
21148		foreach ($tmpdests as $key => $dest) {
21149			if (!$dest['f']) {
21150				if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
21151					$this->dests[$key]['p'] = ($dest['p'] + 1);
21152				} elseif ($dest['p'] == $frompage) {
21153					$this->dests[$key]['p'] = $topage;
21154				}
21155			}
21156		}
21157		// adjust links
21158		$tmplinks = $this->links;
21159		foreach ($tmplinks as $key => $link) {
21160			if (!$link['f']) {
21161				if (($link['p'] >= $topage) AND ($link['p'] < $frompage)) {
21162					$this->links[$key]['p'] = ($link['p'] + 1);
21163				} elseif ($link['p'] == $frompage) {
21164					$this->links[$key]['p'] = $topage;
21165				}
21166			}
21167		}
21168		// adjust javascript
21169		$jfrompage = $frompage;
21170		$jtopage = $topage;
21171		if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) {
21172			foreach($pamatch[0] as $pk => $pmatch) {
21173				$pagenum = intval($pamatch[3][$pk]) + 1;
21174				if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
21175					$newpage = ($pagenum + 1);
21176				} elseif ($pagenum == $jfrompage) {
21177					$newpage = $jtopage;
21178				} else {
21179					$newpage = $pagenum;
21180				}
21181				--$newpage;
21182				$newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21183				$this->javascript = str_replace($pmatch, $newjs, $this->javascript);
21184			}
21185			unset($pamatch);
21186		}
21187		// return to last page
21188		$this->lastPage(true);
21189		return true;
21190	}
21191
21192	/**
21193	 * Remove the specified page.
21194	 * @param int $page page to remove
21195	 * @return bool true in case of success, false in case of error.
21196	 * @public
21197	 * @since 4.6.004 (2009-04-23)
21198	 */
21199	public function deletePage($page) {
21200		if (($page < 1) OR ($page > $this->numpages)) {
21201			return false;
21202		}
21203		// delete current page
21204		unset($this->pages[$page]);
21205		unset($this->pagedim[$page]);
21206		unset($this->pagelen[$page]);
21207		unset($this->intmrk[$page]);
21208		unset($this->bordermrk[$page]);
21209		unset($this->cntmrk[$page]);
21210		foreach ($this->pageobjects[$page] as $oid) {
21211			if (isset($this->offsets[$oid])){
21212				unset($this->offsets[$oid]);
21213			}
21214		}
21215		unset($this->pageobjects[$page]);
21216		if (isset($this->footerpos[$page])) {
21217			unset($this->footerpos[$page]);
21218		}
21219		if (isset($this->footerlen[$page])) {
21220			unset($this->footerlen[$page]);
21221		}
21222		if (isset($this->transfmrk[$page])) {
21223			unset($this->transfmrk[$page]);
21224		}
21225		if (isset($this->PageAnnots[$page])) {
21226			unset($this->PageAnnots[$page]);
21227		}
21228		if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
21229			for ($i = $page; $i > 0; --$i) {
21230				if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) {
21231					--$this->pagegroups[$this->newpagegroup[$i]];
21232					break;
21233				}
21234			}
21235		}
21236		if (isset($this->pageopen[$page])) {
21237			unset($this->pageopen[$page]);
21238		}
21239		if ($page < $this->numpages) {
21240			// update remaining pages
21241			for ($i = $page; $i < $this->numpages; ++$i) {
21242				$j = $i + 1;
21243				// shift pages
21244				$this->setPageBuffer($i, $this->getPageBuffer($j));
21245				$this->pagedim[$i] = $this->pagedim[$j];
21246				$this->pagelen[$i] = $this->pagelen[$j];
21247				$this->intmrk[$i] = $this->intmrk[$j];
21248				$this->bordermrk[$i] = $this->bordermrk[$j];
21249				$this->cntmrk[$i] = $this->cntmrk[$j];
21250				$this->pageobjects[$i] = $this->pageobjects[$j];
21251				if (isset($this->footerpos[$j])) {
21252					$this->footerpos[$i] = $this->footerpos[$j];
21253				} elseif (isset($this->footerpos[$i])) {
21254					unset($this->footerpos[$i]);
21255				}
21256				if (isset($this->footerlen[$j])) {
21257					$this->footerlen[$i] = $this->footerlen[$j];
21258				} elseif (isset($this->footerlen[$i])) {
21259					unset($this->footerlen[$i]);
21260				}
21261				if (isset($this->transfmrk[$j])) {
21262					$this->transfmrk[$i] = $this->transfmrk[$j];
21263				} elseif (isset($this->transfmrk[$i])) {
21264					unset($this->transfmrk[$i]);
21265				}
21266				if (isset($this->PageAnnots[$j])) {
21267					$this->PageAnnots[$i] = $this->PageAnnots[$j];
21268				} elseif (isset($this->PageAnnots[$i])) {
21269					unset($this->PageAnnots[$i]);
21270				}
21271				if (isset($this->newpagegroup[$j])) {
21272					$this->newpagegroup[$i] = $this->newpagegroup[$j];
21273					unset($this->newpagegroup[$j]);
21274				}
21275				if ($this->currpagegroup == $j) {
21276					$this->currpagegroup = $i;
21277				}
21278				if (isset($this->pageopen[$j])) {
21279					$this->pageopen[$i] = $this->pageopen[$j];
21280				} elseif (isset($this->pageopen[$i])) {
21281					unset($this->pageopen[$i]);
21282				}
21283			}
21284			// remove last page
21285			unset($this->pages[$this->numpages]);
21286			unset($this->pagedim[$this->numpages]);
21287			unset($this->pagelen[$this->numpages]);
21288			unset($this->intmrk[$this->numpages]);
21289			unset($this->bordermrk[$this->numpages]);
21290			unset($this->cntmrk[$this->numpages]);
21291			foreach ($this->pageobjects[$this->numpages] as $oid) {
21292				if (isset($this->offsets[$oid])){
21293					unset($this->offsets[$oid]);
21294				}
21295			}
21296			unset($this->pageobjects[$this->numpages]);
21297			if (isset($this->footerpos[$this->numpages])) {
21298				unset($this->footerpos[$this->numpages]);
21299			}
21300			if (isset($this->footerlen[$this->numpages])) {
21301				unset($this->footerlen[$this->numpages]);
21302			}
21303			if (isset($this->transfmrk[$this->numpages])) {
21304				unset($this->transfmrk[$this->numpages]);
21305			}
21306			if (isset($this->PageAnnots[$this->numpages])) {
21307				unset($this->PageAnnots[$this->numpages]);
21308			}
21309			if (isset($this->newpagegroup[$this->numpages])) {
21310				unset($this->newpagegroup[$this->numpages]);
21311			}
21312			if ($this->currpagegroup == $this->numpages) {
21313				$this->currpagegroup = ($this->numpages - 1);
21314			}
21315			if (isset($this->pagegroups[$this->numpages])) {
21316				unset($this->pagegroups[$this->numpages]);
21317			}
21318			if (isset($this->pageopen[$this->numpages])) {
21319				unset($this->pageopen[$this->numpages]);
21320			}
21321		}
21322		--$this->numpages;
21323		$this->page = $this->numpages;
21324		// adjust outlines
21325		$tmpoutlines = $this->outlines;
21326		foreach ($tmpoutlines as $key => $outline) {
21327			if (!$outline['f']) {
21328				if ($outline['p'] > $page) {
21329					$this->outlines[$key]['p'] = $outline['p'] - 1;
21330				} elseif ($outline['p'] == $page) {
21331					unset($this->outlines[$key]);
21332				}
21333			}
21334		}
21335		// adjust dests
21336		$tmpdests = $this->dests;
21337		foreach ($tmpdests as $key => $dest) {
21338			if (!$dest['f']) {
21339				if ($dest['p'] > $page) {
21340					$this->dests[$key]['p'] = $dest['p'] - 1;
21341				} elseif ($dest['p'] == $page) {
21342					unset($this->dests[$key]);
21343				}
21344			}
21345		}
21346		// adjust links
21347		$tmplinks = $this->links;
21348		foreach ($tmplinks as $key => $link) {
21349			if (!$link['f']) {
21350				if ($link['p'] > $page) {
21351					$this->links[$key]['p'] = $link['p'] - 1;
21352				} elseif ($link['p'] == $page) {
21353					unset($this->links[$key]);
21354				}
21355			}
21356		}
21357		// adjust javascript
21358		$jpage = $page;
21359		if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) {
21360			foreach($pamatch[0] as $pk => $pmatch) {
21361				$pagenum = intval($pamatch[3][$pk]) + 1;
21362				if ($pagenum >= $jpage) {
21363					$newpage = ($pagenum - 1);
21364				} elseif ($pagenum == $jpage) {
21365					$newpage = 1;
21366				} else {
21367					$newpage = $pagenum;
21368				}
21369				--$newpage;
21370				$newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21371				$this->javascript = str_replace($pmatch, $newjs, $this->javascript);
21372			}
21373			unset($pamatch);
21374		}
21375		// return to last page
21376		if ($this->numpages > 0) {
21377			$this->lastPage(true);
21378		}
21379		return true;
21380	}
21381
21382	/**
21383	 * Clone the specified page to a new page.
21384	 * @param int $page number of page to copy (0 = current page)
21385	 * @return bool true in case of success, false in case of error.
21386	 * @public
21387	 * @since 4.9.015 (2010-04-20)
21388	 */
21389	public function copyPage($page=0) {
21390		if ($page == 0) {
21391			// default value
21392			$page = $this->page;
21393		}
21394		if (($page < 1) OR ($page > $this->numpages)) {
21395			return false;
21396		}
21397		// close the last page
21398		$this->endPage();
21399		// copy all page-related states
21400		++$this->numpages;
21401		$this->page = $this->numpages;
21402		$this->setPageBuffer($this->page, $this->getPageBuffer($page));
21403		$this->pagedim[$this->page] = $this->pagedim[$page];
21404		$this->pagelen[$this->page] = $this->pagelen[$page];
21405		$this->intmrk[$this->page] = $this->intmrk[$page];
21406		$this->bordermrk[$this->page] = $this->bordermrk[$page];
21407		$this->cntmrk[$this->page] = $this->cntmrk[$page];
21408		$this->pageobjects[$this->page] = $this->pageobjects[$page];
21409		$this->pageopen[$this->page] = false;
21410		if (isset($this->footerpos[$page])) {
21411			$this->footerpos[$this->page] = $this->footerpos[$page];
21412		}
21413		if (isset($this->footerlen[$page])) {
21414			$this->footerlen[$this->page] = $this->footerlen[$page];
21415		}
21416		if (isset($this->transfmrk[$page])) {
21417			$this->transfmrk[$this->page] = $this->transfmrk[$page];
21418		}
21419		if (isset($this->PageAnnots[$page])) {
21420			$this->PageAnnots[$this->page] = $this->PageAnnots[$page];
21421		}
21422		if (isset($this->newpagegroup[$page])) {
21423			// start a new group
21424			$this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1;
21425			$this->currpagegroup = $this->newpagegroup[$this->page];
21426			$this->pagegroups[$this->currpagegroup] = 1;
21427		} elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
21428			++$this->pagegroups[$this->currpagegroup];
21429		}
21430		// copy outlines
21431		$tmpoutlines = $this->outlines;
21432		foreach ($tmpoutlines as $key => $outline) {
21433			if ($outline['p'] == $page) {
21434				$this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'x' => $outline['x'], 'y' => $outline['y'], 'p' => $this->page, 'f' => $outline['f'], 's' => $outline['s'], 'c' => $outline['c']);
21435			}
21436		}
21437		// copy links
21438		$tmplinks = $this->links;
21439		foreach ($tmplinks as $key => $link) {
21440			if ($link['p'] == $page) {
21441				$this->links[] = array('p' => $this->page, 'y' => $link['y'], 'f' => $link['f']);
21442			}
21443		}
21444		// return to last page
21445		$this->lastPage(true);
21446		return true;
21447	}
21448
21449	/**
21450	 * Output a Table of Content Index (TOC).
21451	 * This method must be called after all Bookmarks were set.
21452	 * Before calling this method you have to open the page using the addTOCPage() method.
21453	 * After calling this method you have to call endTOCPage() to close the TOC page.
21454	 * You can override this method to achieve different styles.
21455	 * @param int|null $page page number where this TOC should be inserted (leave empty for current page).
21456	 * @param string $numbersfont set the font for page numbers (please use monospaced font for better alignment).
21457	 * @param string $filler string used to fill the space between text and page number.
21458	 * @param string $toc_name name to use for TOC bookmark.
21459	 * @param string $style Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
21460	 * @param array $color RGB color array for bookmark title (values from 0 to 255).
21461	 * @public
21462	 * @author Nicola Asuni
21463	 * @since 4.5.000 (2009-01-02)
21464	 * @see addTOCPage(), endTOCPage(), addHTMLTOC()
21465	 */
21466	public function addTOC($page=null, $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
21467		$fontsize = $this->FontSizePt;
21468		$fontfamily = $this->FontFamily;
21469		$fontstyle = $this->FontStyle;
21470		$w = $this->w - $this->lMargin - $this->rMargin;
21471		$spacer = $this->GetStringWidth(chr(32)) * 4;
21472		$lmargin = $this->lMargin;
21473		$rmargin = $this->rMargin;
21474		$x_start = $this->GetX();
21475		$page_first = $this->page;
21476		$current_page = $this->page;
21477		$page_fill_start = false;
21478		$page_fill_end = false;
21479		$current_column = $this->current_column;
21480		if (TCPDF_STATIC::empty_string($numbersfont)) {
21481			$numbersfont = $this->default_monospaced_font;
21482		}
21483		if (TCPDF_STATIC::empty_string($filler)) {
21484			$filler = ' ';
21485		}
21486		if (TCPDF_STATIC::empty_string($page)) {
21487			$gap = ' ';
21488		} else {
21489			$gap = '';
21490			if ($page < 1) {
21491				$page = 1;
21492			}
21493		}
21494		$this->setFont($numbersfont, $fontstyle, $fontsize);
21495		$numwidth = $this->GetStringWidth('00000');
21496		$maxpage = 0; //used for pages on attached documents
21497		foreach ($this->outlines as $key => $outline) {
21498			// check for extra pages (used for attachments)
21499			if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) {
21500				$outline['p'] += ($this->page - $page_first);
21501			}
21502			if ($this->rtl) {
21503				$aligntext = 'R';
21504				$alignnum = 'L';
21505			} else {
21506				$aligntext = 'L';
21507				$alignnum = 'R';
21508			}
21509			if ($outline['l'] == 0) {
21510				$this->setFont($fontfamily, $outline['s'].'B', $fontsize);
21511			} else {
21512				$this->setFont($fontfamily, $outline['s'], $fontsize - $outline['l']);
21513			}
21514			$this->setTextColorArray($outline['c']);
21515			// check for page break
21516			$this->checkPageBreak(2 * $this->getCellHeight($this->FontSize));
21517			// set margins and X position
21518			if (($this->page == $current_page) AND ($this->current_column == $current_column)) {
21519				$this->lMargin = $lmargin;
21520				$this->rMargin = $rmargin;
21521			} else {
21522				if ($this->current_column != $current_column) {
21523					if ($this->rtl) {
21524						$x_start = $this->w - $this->columns[$this->current_column]['x'];
21525					} else {
21526						$x_start = $this->columns[$this->current_column]['x'];
21527					}
21528				}
21529				$lmargin = $this->lMargin;
21530				$rmargin = $this->rMargin;
21531				$current_page = $this->page;
21532				$current_column = $this->current_column;
21533			}
21534			$this->setX($x_start);
21535			$indent = ($spacer * $outline['l']);
21536			if ($this->rtl) {
21537				$this->x -= $indent;
21538				$this->rMargin = $this->w - $this->x;
21539			} else {
21540				$this->x += $indent;
21541				$this->lMargin = $this->x;
21542			}
21543			$link = $this->AddLink();
21544			$this->setLink($link, $outline['y'], $outline['p']);
21545			// write the text
21546			if ($this->rtl) {
21547				$txt = ' '.$outline['t'];
21548			} else {
21549				$txt = $outline['t'].' ';
21550			}
21551			$this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
21552			if ($this->rtl) {
21553				$tw = $this->x - $this->lMargin;
21554			} else {
21555				$tw = $this->w - $this->rMargin - $this->x;
21556			}
21557			$this->setFont($numbersfont, $fontstyle, $fontsize);
21558			if (TCPDF_STATIC::empty_string($page)) {
21559				$pagenum = $outline['p'];
21560			} else {
21561				// placemark to be replaced with the correct number
21562				$pagenum = '{#'.($outline['p']).'}';
21563				if ($this->isUnicodeFont()) {
21564					$pagenum = '{'.$pagenum.'}';
21565				}
21566				$maxpage = max($maxpage, $outline['p']);
21567			}
21568			$fw = ($tw - $this->GetStringWidth($pagenum.$filler));
21569			$wfiller = $this->GetStringWidth($filler);
21570			if ($wfiller > 0) {
21571				$numfills = floor($fw / $wfiller);
21572			} else {
21573				$numfills = 0;
21574			}
21575			if ($numfills > 0) {
21576				$rowfill = str_repeat($filler, $numfills);
21577			} else {
21578				$rowfill = '';
21579			}
21580			if ($this->rtl) {
21581				$pagenum = $pagenum.$gap.$rowfill;
21582			} else {
21583				$pagenum = $rowfill.$gap.$pagenum;
21584			}
21585			// write the number
21586			$this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
21587		}
21588		$page_last = $this->getPage();
21589		$numpages = ($page_last - $page_first + 1);
21590		// account for booklet mode
21591		if ($this->booklet) {
21592			// check if a blank page is required before TOC
21593			$page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21594			$page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21595			if ($page_fill_start) {
21596				// add a page at the end (to be moved before TOC)
21597				$this->addPage();
21598				++$page_last;
21599				++$numpages;
21600			}
21601			if ($page_fill_end) {
21602				// add a page at the end
21603				$this->addPage();
21604				++$page_last;
21605				++$numpages;
21606			}
21607		}
21608		$maxpage = max($maxpage, $page_last);
21609		if (!TCPDF_STATIC::empty_string($page)) {
21610			for ($p = $page_first; $p <= $page_last; ++$p) {
21611				// get page data
21612				$temppage = $this->getPageBuffer($p);
21613				for ($n = 1; $n <= $maxpage; ++$n) {
21614					// update page numbers
21615					$a = '{#'.$n.'}';
21616					// get page number aliases
21617					$pnalias = $this->getInternalPageNumberAliases($a);
21618					// calculate replacement number
21619					if (($n >= $page) AND ($n <= $this->numpages)) {
21620						$np = $n + $numpages;
21621					} else {
21622						$np = $n;
21623					}
21624					$na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21625					$nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21626					// replace aliases with numbers
21627					foreach ($pnalias['u'] as $u) {
21628						$sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
21629						if ($this->rtl) {
21630							$nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21631						} else {
21632							$nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21633						}
21634						$temppage = str_replace($u, $nr, $temppage);
21635					}
21636					foreach ($pnalias['a'] as $a) {
21637						$sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
21638						if ($this->rtl) {
21639							$nr = $na.' '.$sfill;
21640						} else {
21641							$nr = $sfill.' '.$na;
21642						}
21643						$temppage = str_replace($a, $nr, $temppage);
21644					}
21645				}
21646				// save changes
21647				$this->setPageBuffer($p, $temppage);
21648			}
21649			// move pages
21650			$this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21651			if ($page_fill_start) {
21652				$this->movePage($page_last, $page_first);
21653			}
21654			for ($i = 0; $i < $numpages; ++$i) {
21655				$this->movePage($page_last, $page);
21656			}
21657		}
21658	}
21659
21660	/**
21661	 * Output a Table Of Content Index (TOC) using HTML templates.
21662	 * This method must be called after all Bookmarks were set.
21663	 * Before calling this method you have to open the page using the addTOCPage() method.
21664	 * After calling this method you have to call endTOCPage() to close the TOC page.
21665	 * @param int|null $page page number where this TOC should be inserted (leave empty for current page).
21666	 * @param string $toc_name name to use for TOC bookmark.
21667	 * @param array $templates array of html templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number.
21668	 * @param boolean $correct_align if true correct the number alignment (numbers must be in monospaced font like courier and right aligned on LTR, or left aligned on RTL)
21669	 * @param string $style Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
21670	 * @param array $color RGB color array for title (values from 0 to 255).
21671	 * @public
21672	 * @author Nicola Asuni
21673	 * @since 5.0.001 (2010-05-06)
21674	 * @see addTOCPage(), endTOCPage(), addTOC()
21675	 */
21676	public function addHTMLTOC($page=null, $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
21677		$filler = ' ';
21678		$prev_htmlLinkColorArray = $this->htmlLinkColorArray;
21679		$prev_htmlLinkFontStyle = $this->htmlLinkFontStyle;
21680		// set new style for link
21681		$this->htmlLinkColorArray = array();
21682		$this->htmlLinkFontStyle = '';
21683		$page_first = $this->getPage();
21684		$page_fill_start = false;
21685		$page_fill_end = false;
21686		// get the font type used for numbers in each template
21687		$current_font = $this->FontFamily;
21688		foreach ($templates as $level => $html) {
21689			$dom = $this->getHtmlDomArray($html);
21690			foreach ($dom as $key => $value) {
21691				if ($value['value'] == '#TOC_PAGE_NUMBER#') {
21692					$this->setFont($dom[($key - 1)]['fontname']);
21693					$templates['F'.$level] = $this->isUnicodeFont();
21694				}
21695			}
21696		}
21697		$this->setFont($current_font);
21698		$maxpage = 0; //used for pages on attached documents
21699		foreach ($this->outlines as $key => $outline) {
21700			// get HTML template
21701			$row = $templates[$outline['l']];
21702			if (TCPDF_STATIC::empty_string($page)) {
21703				$pagenum = $outline['p'];
21704			} else {
21705				// placemark to be replaced with the correct number
21706				$pagenum = '{#'.($outline['p']).'}';
21707				if (isset($templates['F'.$outline['l']]) && $templates['F'.$outline['l']]) {
21708					$pagenum = '{'.$pagenum.'}';
21709				}
21710				$maxpage = max($maxpage, $outline['p']);
21711			}
21712			// replace templates with current values
21713			$row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
21714			$row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
21715			// add link to page
21716			$row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
21717			// write bookmark entry
21718			$this->writeHTML($row, false, false, true, false, '');
21719		}
21720		// restore link styles
21721		$this->htmlLinkColorArray = $prev_htmlLinkColorArray;
21722		$this->htmlLinkFontStyle = $prev_htmlLinkFontStyle;
21723		// move TOC page and replace numbers
21724		$page_last = $this->getPage();
21725		$numpages = ($page_last - $page_first + 1);
21726		// account for booklet mode
21727		if ($this->booklet) {
21728			// check if a blank page is required before TOC
21729			$page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21730			$page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21731			if ($page_fill_start) {
21732				// add a page at the end (to be moved before TOC)
21733				$this->addPage();
21734				++$page_last;
21735				++$numpages;
21736			}
21737			if ($page_fill_end) {
21738				// add a page at the end
21739				$this->addPage();
21740				++$page_last;
21741				++$numpages;
21742			}
21743		}
21744		$maxpage = max($maxpage, $page_last);
21745		if (!TCPDF_STATIC::empty_string($page)) {
21746			for ($p = $page_first; $p <= $page_last; ++$p) {
21747				// get page data
21748				$temppage = $this->getPageBuffer($p);
21749				for ($n = 1; $n <= $maxpage; ++$n) {
21750					// update page numbers
21751					$a = '{#'.$n.'}';
21752					// get page number aliases
21753					$pnalias = $this->getInternalPageNumberAliases($a);
21754					// calculate replacement number
21755					if ($n >= $page) {
21756						$np = $n + $numpages;
21757					} else {
21758						$np = $n;
21759					}
21760					$na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21761					$nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21762					// replace aliases with numbers
21763					foreach ($pnalias['u'] as $u) {
21764						if ($correct_align) {
21765							$sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
21766							if ($this->rtl) {
21767								$nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21768							} else {
21769								$nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21770							}
21771						} else {
21772							$nr = $nu;
21773						}
21774						$temppage = str_replace($u, $nr, $temppage);
21775					}
21776					foreach ($pnalias['a'] as $a) {
21777						if ($correct_align) {
21778							$sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
21779							if ($this->rtl) {
21780								$nr = $na.' '.$sfill;
21781							} else {
21782								$nr = $sfill.' '.$na;
21783							}
21784						} else {
21785							$nr = $na;
21786						}
21787						$temppage = str_replace($a, $nr, $temppage);
21788					}
21789				}
21790				// save changes
21791				$this->setPageBuffer($p, $temppage);
21792			}
21793			// move pages
21794			$this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21795			if ($page_fill_start) {
21796				$this->movePage($page_last, $page_first);
21797			}
21798			for ($i = 0; $i < $numpages; ++$i) {
21799				$this->movePage($page_last, $page);
21800			}
21801		}
21802	}
21803
21804	/**
21805	 * Stores a copy of the current TCPDF object used for undo operation.
21806	 * @public
21807	 * @since 4.5.029 (2009-03-19)
21808	 */
21809	public function startTransaction() {
21810		if (isset($this->objcopy)) {
21811			// remove previous copy
21812			$this->commitTransaction();
21813		}
21814		// record current page number and Y position
21815		$this->start_transaction_page = $this->page;
21816		$this->start_transaction_y = $this->y;
21817		// clone current object
21818		$this->objcopy = TCPDF_STATIC::objclone($this);
21819	}
21820
21821	/**
21822	 * Delete the copy of the current TCPDF object used for undo operation.
21823	 * @public
21824	 * @since 4.5.029 (2009-03-19)
21825	 */
21826	public function commitTransaction() {
21827		if (isset($this->objcopy)) {
21828			$this->objcopy->_destroy(true, true);
21829			/* The unique file_id should not be used during cleanup again */
21830			$this->objcopy->file_id = NULL;
21831			unset($this->objcopy);
21832		}
21833	}
21834
21835	/**
21836	 * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction().
21837	 * @param boolean $self if true restores current class object to previous state without the need of reassignment via the returned value.
21838	 * @return TCPDF object.
21839	 * @public
21840	 * @since 4.5.029 (2009-03-19)
21841	 */
21842	public function rollbackTransaction($self=false) {
21843		if (isset($this->objcopy)) {
21844			$objcopy = $this->objcopy;
21845			$this->_destroy(true, true);
21846			if ($self) {
21847				$objvars = get_object_vars($objcopy);
21848				foreach ($objvars as $key => $value) {
21849					$this->$key = $value;
21850				}
21851				$objcopy->_destroy(true, true);
21852				/* The unique file_id should not be used during cleanup again */
21853				$objcopy->file_id = NULL;
21854				unset($objcopy);
21855				return $this;
21856			}
21857			/* The unique file_id should not be used during cleanup again */
21858			$this->file_id = NULL;
21859			return $objcopy;
21860		}
21861		return $this;
21862	}
21863
21864	// --- MULTI COLUMNS METHODS -----------------------
21865
21866	/**
21867	 * Set multiple columns of the same size
21868	 * @param int $numcols number of columns (set to zero to disable columns mode)
21869	 * @param int $width column width
21870	 * @param int|null $y column starting Y position (leave empty for current Y position)
21871	 * @public
21872	 * @since 4.9.001 (2010-03-28)
21873	 */
21874	public function setEqualColumns($numcols=0, $width=0, $y=null) {
21875		$this->columns = array();
21876		if ($numcols < 2) {
21877			$numcols = 0;
21878			$this->columns = array();
21879		} else {
21880			// maximum column width
21881			$maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
21882			if (($width == 0) OR ($width > $maxwidth)) {
21883				$width = $maxwidth;
21884			}
21885			if (TCPDF_STATIC::empty_string($y)) {
21886				$y = $this->y;
21887			}
21888			// space between columns
21889			$space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
21890			// fill the columns array (with, space, starting Y position)
21891			for ($i = 0; $i < $numcols; ++$i) {
21892				$this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
21893			}
21894		}
21895		$this->num_columns = $numcols;
21896		$this->current_column = 0;
21897		$this->column_start_page = $this->page;
21898		$this->selectColumn(0);
21899	}
21900
21901	/**
21902	 * Remove columns and reset page margins.
21903	 * @public
21904	 * @since 5.9.072 (2011-04-26)
21905	 */
21906	public function resetColumns() {
21907		$this->lMargin = $this->original_lMargin;
21908		$this->rMargin = $this->original_rMargin;
21909		$this->setEqualColumns();
21910	}
21911
21912	/**
21913	 * Set columns array.
21914	 * Each column is represented by an array of arrays with the following keys: (w = width, s = space between columns, y = column top position).
21915	 * @param array $columns
21916	 * @public
21917	 * @since 4.9.001 (2010-03-28)
21918	 */
21919	public function setColumnsArray($columns) {
21920		$this->columns = $columns;
21921		$this->num_columns = count($columns);
21922		$this->current_column = 0;
21923		$this->column_start_page = $this->page;
21924		$this->selectColumn(0);
21925	}
21926
21927	/**
21928	 * Set position at a given column
21929	 * @param int|null $col column number (from 0 to getNumberOfColumns()-1); empty string = current column.
21930	 * @public
21931	 * @since 4.9.001 (2010-03-28)
21932	 */
21933	public function selectColumn($col=null) {
21934		if (TCPDF_STATIC::empty_string($col)) {
21935			$col = $this->current_column;
21936		} elseif ($col >= $this->num_columns) {
21937			$col = 0;
21938		}
21939		$xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
21940		$enable_thead = false;
21941		if ($this->num_columns > 1) {
21942			if ($col != $this->current_column) {
21943				// move Y pointer at the top of the column
21944				if ($this->column_start_page == $this->page) {
21945					$this->y = $this->columns[$col]['y'];
21946				} else {
21947					$this->y = $this->tMargin;
21948				}
21949				// Avoid to write table headers more than once
21950				if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) {
21951					$enable_thead = true;
21952					$this->maxselcol['page'] = $this->page;
21953					$this->maxselcol['column'] = $col;
21954				}
21955			}
21956			$xshift = $this->colxshift;
21957			// set X position of the current column by case
21958			$listindent = ($this->listindentlevel * $this->listindent);
21959			// calculate column X position
21960			$colpos = 0;
21961			for ($i = 0; $i < $col; ++$i) {
21962				$colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']);
21963			}
21964			if ($this->rtl) {
21965				$x = $this->w - $this->original_rMargin - $colpos;
21966				$this->rMargin = ($this->w - $x + $listindent);
21967				$this->lMargin = ($x - $this->columns[$col]['w']);
21968				$this->x = $x - $listindent;
21969			} else {
21970				$x = $this->original_lMargin + $colpos;
21971				$this->lMargin = ($x + $listindent);
21972				$this->rMargin = ($this->w - $x - $this->columns[$col]['w']);
21973				$this->x = $x + $listindent;
21974			}
21975			$this->columns[$col]['x'] = $x;
21976		}
21977		$this->current_column = $col;
21978		// fix for HTML mode
21979		$this->newline = true;
21980		// print HTML table header (if any)
21981		if ((!TCPDF_STATIC::empty_string($this->thead)) AND (!$this->inthead)) {
21982			if ($enable_thead) {
21983				// print table header
21984				$this->writeHTML($this->thead, false, false, false, false, '');
21985				$this->y += $xshift['s']['V'];
21986				// store end of header position
21987				if (!isset($this->columns[$col]['th'])) {
21988					$this->columns[$col]['th'] = array();
21989				}
21990				$this->columns[$col]['th']['\''.$this->page.'\''] = $this->y;
21991				$this->lasth = 0;
21992			} elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) {
21993				$this->y = $this->columns[$col]['th']['\''.$this->page.'\''];
21994			}
21995		}
21996		// account for an html table cell over multiple columns
21997		if ($this->rtl) {
21998			$this->rMargin += $xshift['x'];
21999			$this->x -= ($xshift['x'] + $xshift['p']['R']);
22000		} else {
22001			$this->lMargin += $xshift['x'];
22002			$this->x += $xshift['x'] + $xshift['p']['L'];
22003		}
22004	}
22005
22006	/**
22007	 * Return the current column number
22008	 * @return int current column number
22009	 * @public
22010	 * @since 5.5.011 (2010-07-08)
22011	 */
22012	public function getColumn() {
22013		return $this->current_column;
22014	}
22015
22016	/**
22017	 * Return the current number of columns.
22018	 * @return int number of columns
22019	 * @public
22020	 * @since 5.8.018 (2010-08-25)
22021	 */
22022	public function getNumberOfColumns() {
22023		return $this->num_columns;
22024	}
22025
22026	/**
22027	 * Set Text rendering mode.
22028	 * @param int $stroke outline size in user units (0 = disable).
22029	 * @param boolean $fill if true fills the text (default).
22030	 * @param boolean $clip if true activate clipping mode
22031	 * @public
22032	 * @since 4.9.008 (2009-04-02)
22033	 */
22034	public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
22035		// Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
22036		// convert text rendering parameters
22037		if ($stroke < 0) {
22038			$stroke = 0;
22039		}
22040		if ($fill === true) {
22041			if ($stroke > 0) {
22042				if ($clip === true) {
22043					// Fill, then stroke text and add to path for clipping
22044					$textrendermode = 6;
22045				} else {
22046					// Fill, then stroke text
22047					$textrendermode = 2;
22048				}
22049				$textstrokewidth = $stroke;
22050			} else {
22051				if ($clip === true) {
22052					// Fill text and add to path for clipping
22053					$textrendermode = 4;
22054				} else {
22055					// Fill text
22056					$textrendermode = 0;
22057				}
22058			}
22059		} else {
22060			if ($stroke > 0) {
22061				if ($clip === true) {
22062					// Stroke text and add to path for clipping
22063					$textrendermode = 5;
22064				} else {
22065					// Stroke text
22066					$textrendermode = 1;
22067				}
22068				$textstrokewidth = $stroke;
22069			} else {
22070				if ($clip === true) {
22071					// Add text to path for clipping
22072					$textrendermode = 7;
22073				} else {
22074					// Neither fill nor stroke text (invisible)
22075					$textrendermode = 3;
22076				}
22077			}
22078		}
22079		$this->textrendermode = $textrendermode;
22080		$this->textstrokewidth = $stroke;
22081	}
22082
22083	/**
22084	 * Set parameters for drop shadow effect for text.
22085	 * @param array $params Array of parameters: enabled (boolean) set to true to enable shadow; depth_w (float) shadow width in user units; depth_h (float) shadow height in user units; color (array) shadow color or false to use the stroke color; opacity (float) Alpha value: real value from 0 (transparent) to 1 (opaque); blend_mode (string) blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity.
22086	 * @since 5.9.174 (2012-07-25)
22087	 * @public
22088	*/
22089	public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) {
22090		if (isset($params['enabled'])) {
22091			$this->txtshadow['enabled'] = $params['enabled']?true:false;
22092		} else {
22093			$this->txtshadow['enabled'] = false;
22094		}
22095		if (isset($params['depth_w'])) {
22096			$this->txtshadow['depth_w'] = floatval($params['depth_w']);
22097		} else {
22098			$this->txtshadow['depth_w'] = 0;
22099		}
22100		if (isset($params['depth_h'])) {
22101			$this->txtshadow['depth_h'] = floatval($params['depth_h']);
22102		} else {
22103			$this->txtshadow['depth_h'] = 0;
22104		}
22105		if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) {
22106			$this->txtshadow['color'] = $params['color'];
22107		} else {
22108			$this->txtshadow['color'] = $this->strokecolor;
22109		}
22110		if (isset($params['opacity'])) {
22111			$this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity'])));
22112		} else {
22113			$this->txtshadow['opacity'] = 1;
22114		}
22115		if (isset($params['blend_mode']) AND in_array($params['blend_mode'], array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
22116			$this->txtshadow['blend_mode'] = $params['blend_mode'];
22117		} else {
22118			$this->txtshadow['blend_mode'] = 'Normal';
22119		}
22120		if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) {
22121			$this->txtshadow['enabled'] = false;
22122		}
22123	}
22124
22125	/**
22126	 * Return the text shadow parameters array.
22127	 * @return array array of parameters.
22128	 * @since 5.9.174 (2012-07-25)
22129	 * @public
22130	 */
22131	public function getTextShadow() {
22132		return $this->txtshadow;
22133	}
22134
22135	/**
22136	 * Returns an array of chars containing soft hyphens.
22137	 * @param array $word array of chars
22138	 * @param array $patterns Array of hypenation patterns.
22139	 * @param array $dictionary Array of words to be returned without applying the hyphenation algorithm.
22140	 * @param int $leftmin Minimum number of character to leave on the left of the word without applying the hyphens.
22141	 * @param int $rightmin Minimum number of character to leave on the right of the word without applying the hyphens.
22142	 * @param int $charmin Minimum word length to apply the hyphenation algorithm.
22143	 * @param int $charmax Maximum length of broken piece of word.
22144	 * @return array text with soft hyphens
22145	 * @author Nicola Asuni
22146	 * @since 4.9.012 (2010-04-12)
22147	 * @protected
22148	 */
22149	protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22150		$hyphenword = array(); // hyphens positions
22151		$numchars = count($word);
22152		if ($numchars <= $charmin) {
22153			return $word;
22154		}
22155		$word_string = TCPDF_FONTS::UTF8ArrSubString($word, '', '', $this->isunicode);
22156		// some words will be returned as-is
22157		$pattern = '/^([a-zA-Z0-9_\.\-]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/';
22158		if (preg_match($pattern, $word_string) > 0) {
22159			// email
22160			return $word;
22161		}
22162		$pattern = '/(([a-zA-Z0-9\-]+\.)?)((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/';
22163		if (preg_match($pattern, $word_string) > 0) {
22164			// URL
22165			return $word;
22166		}
22167		if (isset($dictionary[$word_string])) {
22168			return TCPDF_FONTS::UTF8StringToArray($dictionary[$word_string], $this->isunicode, $this->CurrentFont);
22169		}
22170		// surround word with '_' characters
22171		$tmpword = array_merge(array(46), $word, array(46));
22172		$tmpnumchars = $numchars + 2;
22173		$maxpos = $tmpnumchars - 1;
22174		for ($pos = 0; $pos < $maxpos; ++$pos) {
22175			$imax = min(($tmpnumchars - $pos), $charmax);
22176			for ($i = 1; $i <= $imax; ++$i) {
22177				$subword = strtolower(TCPDF_FONTS::UTF8ArrSubString($tmpword, $pos, ($pos + $i), $this->isunicode));
22178				if (isset($patterns[$subword])) {
22179					$pattern = TCPDF_FONTS::UTF8StringToArray($patterns[$subword], $this->isunicode, $this->CurrentFont);
22180					$pattern_length = count($pattern);
22181					$digits = 1;
22182					for ($j = 0; $j < $pattern_length; ++$j) {
22183						// check if $pattern[$j] is a number = hyphenation level (only numbers from 1 to 5 are valid)
22184						if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
22185							if ($j == 0) {
22186								$zero = $pos - 1;
22187							} else {
22188								$zero = $pos + $j - $digits;
22189							}
22190							// get hyphenation level
22191							$level = ($pattern[$j] - 48);
22192							// if two levels from two different patterns match at the same point, the higher one is selected.
22193							if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] < $level)) {
22194								$hyphenword[$zero] = $level;
22195							}
22196							++$digits;
22197						}
22198					}
22199				}
22200			}
22201		}
22202		$inserted = 0;
22203		$maxpos = $numchars - $rightmin;
22204		for ($i = $leftmin; $i <= $maxpos; ++$i) {
22205			// only odd levels indicate allowed hyphenation points
22206			if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
22207				// 173 = soft hyphen character
22208				array_splice($word, $i + $inserted, 0, 173);
22209				++$inserted;
22210			}
22211		}
22212		return $word;
22213	}
22214
22215	/**
22216	 * Returns text with soft hyphens.
22217	 * @param string $text text to process
22218	 * @param mixed $patterns Array of hypenation patterns or a TEX file containing hypenation patterns. TEX patterns can be downloaded from http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/
22219	 * @param array $dictionary Array of words to be returned without applying the hyphenation algorithm.
22220	 * @param int $leftmin Minimum number of character to leave on the left of the word without applying the hyphens.
22221	 * @param int $rightmin Minimum number of character to leave on the right of the word without applying the hyphens.
22222	 * @param int $charmin Minimum word length to apply the hyphenation algorithm.
22223	 * @param int $charmax Maximum length of broken piece of word.
22224	 * @return string text with soft hyphens
22225	 * @author Nicola Asuni
22226	 * @since 4.9.012 (2010-04-12)
22227	 * @public
22228	 */
22229	public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22230		$text = $this->unhtmlentities($text);
22231		$word = array(); // last word
22232		$txtarr = array(); // text to be returned
22233		$intag = false; // true if we are inside an HTML tag
22234		$skip = false; // true to skip hyphenation
22235		if (!is_array($patterns)) {
22236			$patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patterns);
22237		}
22238		// get array of characters
22239		$unichars = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
22240		// for each char
22241		foreach ($unichars as $char) {
22242			if ((!$intag) AND (!$skip) AND TCPDF_FONT_DATA::$uni_type[$char] == 'L') {
22243				// letter character
22244				$word[] = $char;
22245			} else {
22246				// other type of character
22247				if (!TCPDF_STATIC::empty_string($word)) {
22248					// hypenate the word
22249					$txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22250					$word = array();
22251				}
22252				$txtarr[] = $char;
22253				if (chr($char) == '<') {
22254					// we are inside an HTML tag
22255					$intag = true;
22256				} elseif ($intag AND (chr($char) == '>')) {
22257					// end of HTML tag
22258					$intag = false;
22259					// check for style tag
22260					$expected = array(115, 116, 121, 108, 101); // = 'style'
22261					$current = array_slice($txtarr, -6, 5); // last 5 chars
22262					$compare = array_diff($expected, $current);
22263					if (empty($compare)) {
22264						// check if it is a closing tag
22265						$expected = array(47); // = '/'
22266						$current = array_slice($txtarr, -7, 1);
22267						$compare = array_diff($expected, $current);
22268						if (empty($compare)) {
22269							// closing style tag
22270							$skip = false;
22271						} else {
22272							// opening style tag
22273							$skip = true;
22274						}
22275					}
22276				}
22277			}
22278		}
22279		if (!TCPDF_STATIC::empty_string($word)) {
22280			// hypenate the word
22281			$txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22282		}
22283		// convert char array to string and return
22284		return TCPDF_FONTS::UTF8ArrSubString($txtarr, '', '', $this->isunicode);
22285	}
22286
22287	/**
22288	 * Enable/disable rasterization of vector images using ImageMagick library.
22289	 * @param boolean $mode if true enable rasterization, false otherwise.
22290	 * @public
22291	 * @since 5.0.000 (2010-04-27)
22292	 */
22293	public function setRasterizeVectorImages($mode) {
22294		$this->rasterize_vector_images = $mode;
22295	}
22296
22297	/**
22298	 * Enable or disable default option for font subsetting.
22299	 * @param boolean $enable if true enable font subsetting by default.
22300	 * @author Nicola Asuni
22301	 * @public
22302	 * @since 5.3.002 (2010-06-07)
22303	 */
22304	public function setFontSubsetting($enable=true) {
22305		if ($this->pdfa_mode) {
22306			$this->font_subsetting = false;
22307		} else {
22308			$this->font_subsetting = $enable ? true : false;
22309		}
22310	}
22311
22312	/**
22313	 * Return the default option for font subsetting.
22314	 * @return bool default font subsetting state.
22315	 * @author Nicola Asuni
22316	 * @public
22317	 * @since 5.3.002 (2010-06-07)
22318	 */
22319	public function getFontSubsetting() {
22320		return $this->font_subsetting;
22321	}
22322
22323	/**
22324	 * Left trim the input string
22325	 * @param string $str string to trim
22326	 * @param string $replace string that replace spaces.
22327	 * @return string left trimmed string
22328	 * @author Nicola Asuni
22329	 * @public
22330	 * @since 5.8.000 (2010-08-11)
22331	 */
22332	public function stringLeftTrim($str, $replace='') {
22333		return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str);
22334	}
22335
22336	/**
22337	 * Right trim the input string
22338	 * @param string $str string to trim
22339	 * @param string $replace string that replace spaces.
22340	 * @return string right trimmed string
22341	 * @author Nicola Asuni
22342	 * @public
22343	 * @since 5.8.000 (2010-08-11)
22344	 */
22345	public function stringRightTrim($str, $replace='') {
22346		return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str);
22347	}
22348
22349	/**
22350	 * Trim the input string
22351	 * @param string $str string to trim
22352	 * @param string $replace string that replace spaces.
22353	 * @return string trimmed string
22354	 * @author Nicola Asuni
22355	 * @public
22356	 * @since 5.8.000 (2010-08-11)
22357	 */
22358	public function stringTrim($str, $replace='') {
22359		$str = $this->stringLeftTrim($str, $replace);
22360		$str = $this->stringRightTrim($str, $replace);
22361		return $str;
22362	}
22363
22364	/**
22365	 * Return true if the current font is unicode type.
22366	 * @return bool true for unicode font, false otherwise.
22367	 * @author Nicola Asuni
22368	 * @public
22369	 * @since 5.8.002 (2010-08-14)
22370	 */
22371	public function isUnicodeFont() {
22372		return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0'));
22373	}
22374
22375	/**
22376	 * Return normalized font name
22377	 * @param string $fontfamily property string containing font family names
22378	 * @return string normalized font name
22379	 * @author Nicola Asuni
22380	 * @public
22381	 * @since 5.8.004 (2010-08-17)
22382	 */
22383	public function getFontFamilyName($fontfamily) {
22384		// remove spaces and symbols
22385		$fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily));
22386		// extract all font names
22387		$fontslist = preg_split('/[,]/', $fontfamily);
22388		// find first valid font name
22389		foreach ($fontslist as $font) {
22390			// replace font variations
22391			$font = preg_replace('/regular$/', '', $font);
22392			$font = preg_replace('/italic$/', 'I', $font);
22393			$font = preg_replace('/oblique$/', 'I', $font);
22394			$font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
22395			// replace common family names and core fonts
22396			$pattern = array();
22397			$replacement = array();
22398			$pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
22399			$replacement[] = 'times';
22400			$pattern[] = '/^sansserif/';
22401			$replacement[] = 'helvetica';
22402			$pattern[] = '/^monospace/';
22403			$replacement[] = 'courier';
22404			$font = preg_replace($pattern, $replacement, $font);
22405			if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) {
22406				return $font;
22407			}
22408		}
22409		// return current font as default
22410		return $this->CurrentFont['fontkey'];
22411	}
22412
22413	/**
22414	 * Start a new XObject Template.
22415	 * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images).
22416	 * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked.
22417	 * Note: X,Y coordinates will be reset to 0,0.
22418	 * @param int $w Template width in user units (empty string or zero = page width less margins).
22419	 * @param int $h Template height in user units (empty string or zero = page height less margins).
22420	 * @param mixed $group Set transparency group. Can be a boolean value or an array specifying optional parameters: 'CS' (solour space name), 'I' (boolean flag to indicate isolated group) and 'K' (boolean flag to indicate knockout group).
22421	 * @return string|false the XObject Template ID in case of success or false in case of error.
22422	 * @author Nicola Asuni
22423	 * @public
22424	 * @since 5.8.017 (2010-08-24)
22425	 * @see endTemplate(), printTemplate()
22426	 */
22427	public function startTemplate($w=0, $h=0, $group=false) {
22428		if ($this->inxobj) {
22429			// we are already inside an XObject template
22430			return false;
22431		}
22432		$this->inxobj = true;
22433		++$this->n;
22434		// XObject ID
22435		$this->xobjid = 'XT'.$this->n;
22436		// object ID
22437		$this->xobjects[$this->xobjid] = array('n' => $this->n);
22438		// store current graphic state
22439		$this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars();
22440		// initialize data
22441		$this->xobjects[$this->xobjid]['intmrk'] = 0;
22442		$this->xobjects[$this->xobjid]['transfmrk'] = array();
22443		$this->xobjects[$this->xobjid]['outdata'] = '';
22444		$this->xobjects[$this->xobjid]['xobjects'] = array();
22445		$this->xobjects[$this->xobjid]['images'] = array();
22446		$this->xobjects[$this->xobjid]['fonts'] = array();
22447		$this->xobjects[$this->xobjid]['annotations'] = array();
22448		$this->xobjects[$this->xobjid]['extgstates'] = array();
22449		$this->xobjects[$this->xobjid]['gradients'] = array();
22450		$this->xobjects[$this->xobjid]['spot_colors'] = array();
22451		// set new environment
22452		$this->num_columns = 1;
22453		$this->current_column = 0;
22454		$this->setAutoPageBreak(false);
22455		if (($w === '') OR ($w <= 0)) {
22456			$w = $this->w - $this->lMargin - $this->rMargin;
22457		}
22458		if (($h === '') OR ($h <= 0)) {
22459			$h = $this->h - $this->tMargin - $this->bMargin;
22460		}
22461		$this->xobjects[$this->xobjid]['x'] = 0;
22462		$this->xobjects[$this->xobjid]['y'] = 0;
22463		$this->xobjects[$this->xobjid]['w'] = $w;
22464		$this->xobjects[$this->xobjid]['h'] = $h;
22465		$this->w = $w;
22466		$this->h = $h;
22467		$this->wPt = $this->w * $this->k;
22468		$this->hPt = $this->h * $this->k;
22469		$this->fwPt = $this->wPt;
22470		$this->fhPt = $this->hPt;
22471		$this->x = 0;
22472		$this->y = 0;
22473		$this->lMargin = 0;
22474		$this->rMargin = 0;
22475		$this->tMargin = 0;
22476		$this->bMargin = 0;
22477		// set group mode
22478		$this->xobjects[$this->xobjid]['group'] = $group;
22479		return $this->xobjid;
22480	}
22481
22482	/**
22483	 * End the current XObject Template started with startTemplate() and restore the previous graphic state.
22484	 * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images).
22485	 * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked.
22486	 * @return string|false the XObject Template ID in case of success or false in case of error.
22487	 * @author Nicola Asuni
22488	 * @public
22489	 * @since 5.8.017 (2010-08-24)
22490	 * @see startTemplate(), printTemplate()
22491	 */
22492	public function endTemplate() {
22493		if (!$this->inxobj) {
22494			// we are not inside a template
22495			return false;
22496		}
22497		$this->inxobj = false;
22498		// restore previous graphic state
22499		$this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true);
22500		return $this->xobjid;
22501	}
22502
22503	/**
22504	 * Print an XObject Template.
22505	 * You can print an XObject Template inside the currently opened Template.
22506	 * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images).
22507	 * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked.
22508	 * @param string $id The ID of XObject Template to print.
22509	 * @param float|null $x X position in user units (empty string = current x position)
22510	 * @param float|null $y Y position in user units (empty string = current y position)
22511	 * @param float $w Width in user units (zero = remaining page width)
22512	 * @param float $h Height in user units (zero = remaining page height)
22513	 * @param string $align Indicates the alignment of the pointer next to template insertion relative to template height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
22514	 * @param string $palign Allows to center or align the template on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
22515	 * @param boolean $fitonpage If true the template is resized to not exceed page dimensions.
22516	 * @author Nicola Asuni
22517	 * @public
22518	 * @since 5.8.017 (2010-08-24)
22519	 * @see startTemplate(), endTemplate()
22520	 */
22521	public function printTemplate($id, $x=null, $y=null, $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
22522		if ($this->state != 2) {
22523			 return;
22524		}
22525		if (!isset($this->xobjects[$id])) {
22526			$this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
22527		}
22528		if ($this->inxobj) {
22529			if ($id == $this->xobjid) {
22530				// close current template
22531				$this->endTemplate();
22532			} else {
22533				// use the template as resource for the template currently opened
22534				$this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id];
22535			}
22536		}
22537		// set default values
22538		if (TCPDF_STATIC::empty_string($x)) {
22539			$x = $this->x;
22540		}
22541		if (TCPDF_STATIC::empty_string($y)) {
22542			$y = $this->y;
22543		}
22544		// check page for no-write regions and adapt page margins if necessary
22545		list($x, $y) = $this->checkPageRegions($h, $x, $y);
22546		$ow = $this->xobjects[$id]['w'];
22547		if ($ow <= 0) {
22548			$ow = 1;
22549		}
22550		$oh = $this->xobjects[$id]['h'];
22551		if ($oh <= 0) {
22552			$oh = 1;
22553		}
22554		// calculate template width and height on document
22555		if (($w <= 0) AND ($h <= 0)) {
22556			$w = $ow;
22557			$h = $oh;
22558		} elseif ($w <= 0) {
22559			$w = $h * $ow / $oh;
22560		} elseif ($h <= 0) {
22561			$h = $w * $oh / $ow;
22562		}
22563		// fit the template on available space
22564		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22565		// set page alignment
22566		$rb_y = $y + $h;
22567		// set alignment
22568		if ($this->rtl) {
22569			if ($palign == 'L') {
22570				$xt = $this->lMargin;
22571			} elseif ($palign == 'C') {
22572				$xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22573			} elseif ($palign == 'R') {
22574				$xt = $this->w - $this->rMargin - $w;
22575			} else {
22576				$xt = $x - $w;
22577			}
22578			$rb_x = $xt;
22579		} else {
22580			if ($palign == 'L') {
22581				$xt = $this->lMargin;
22582			} elseif ($palign == 'C') {
22583				$xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22584			} elseif ($palign == 'R') {
22585				$xt = $this->w - $this->rMargin - $w;
22586			} else {
22587				$xt = $x;
22588			}
22589			$rb_x = $xt + $w;
22590		}
22591		// print XObject Template + Transformation matrix
22592		$this->StartTransform();
22593		// translate and scale
22594		$sx = ($w / $ow);
22595		$sy = ($h / $oh);
22596		$tm = array();
22597		$tm[0] = $sx;
22598		$tm[1] = 0;
22599		$tm[2] = 0;
22600		$tm[3] = $sy;
22601		$tm[4] = $xt * $this->k;
22602		$tm[5] = ($this->h - $h - $y) * $this->k;
22603		$this->Transform($tm);
22604		// set object
22605		$this->_out('/'.$id.' Do');
22606		$this->StopTransform();
22607		// add annotations
22608		if (!empty($this->xobjects[$id]['annotations'])) {
22609			foreach ($this->xobjects[$id]['annotations'] as $annot) {
22610				// transform original coordinates
22611				$coordlt = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k)));
22612				$ax = ($coordlt[4] / $this->k);
22613				$ay = ($this->h - $h - ($coordlt[5] / $this->k));
22614				$coordrb = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k)));
22615				$aw = ($coordrb[4] / $this->k) - $ax;
22616				$ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay;
22617				$this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
22618			}
22619		}
22620		// set pointer to align the next text/objects
22621		switch($align) {
22622			case 'T': {
22623				$this->y = $y;
22624				$this->x = $rb_x;
22625				break;
22626			}
22627			case 'M': {
22628				$this->y = $y + round($h/2);
22629				$this->x = $rb_x;
22630				break;
22631			}
22632			case 'B': {
22633				$this->y = $rb_y;
22634				$this->x = $rb_x;
22635				break;
22636			}
22637			case 'N': {
22638				$this->setY($rb_y);
22639				break;
22640			}
22641			default:{
22642				break;
22643			}
22644		}
22645	}
22646
22647	/**
22648	 * Set the percentage of character stretching.
22649	 * @param int $perc percentage of stretching (100 = no stretching)
22650	 * @author Nicola Asuni
22651	 * @public
22652	 * @since 5.9.000 (2010-09-29)
22653	 */
22654	public function setFontStretching($perc=100) {
22655		$this->font_stretching = $perc;
22656	}
22657
22658	/**
22659	 * Get the percentage of character stretching.
22660	 * @return float stretching value
22661	 * @author Nicola Asuni
22662	 * @public
22663	 * @since 5.9.000 (2010-09-29)
22664	 */
22665	public function getFontStretching() {
22666		return $this->font_stretching;
22667	}
22668
22669	/**
22670	 * Set the amount to increase or decrease the space between characters in a text.
22671	 * @param float $spacing amount to increase or decrease the space between characters in a text (0 = default spacing)
22672	 * @author Nicola Asuni
22673	 * @public
22674	 * @since 5.9.000 (2010-09-29)
22675	 */
22676	public function setFontSpacing($spacing=0) {
22677		$this->font_spacing = $spacing;
22678	}
22679
22680	/**
22681	 * Get the amount to increase or decrease the space between characters in a text.
22682	 * @return int font spacing (tracking) value
22683	 * @author Nicola Asuni
22684	 * @public
22685	 * @since 5.9.000 (2010-09-29)
22686	 */
22687	public function getFontSpacing() {
22688		return $this->font_spacing;
22689	}
22690
22691	/**
22692	 * Return an array of no-write page regions
22693	 * @return array of no-write page regions
22694	 * @author Nicola Asuni
22695	 * @public
22696	 * @since 5.9.003 (2010-10-13)
22697	 * @see setPageRegions(), addPageRegion()
22698	 */
22699	public function getPageRegions() {
22700		return $this->page_regions;
22701	}
22702
22703	/**
22704	 * Set no-write regions on page.
22705	 * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code.
22706	 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22707	 * You can set multiple regions for the same page.
22708	 * @param array $regions array of no-write regions. For each region you can define an array as follow: ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right). Omit this parameter to remove all regions.
22709	 * @author Nicola Asuni
22710	 * @public
22711	 * @since 5.9.003 (2010-10-13)
22712	 * @see addPageRegion(), getPageRegions()
22713	 */
22714	public function setPageRegions($regions=array()) {
22715		// empty current regions array
22716		$this->page_regions = array();
22717		// add regions
22718		foreach ($regions as $data) {
22719			$this->addPageRegion($data);
22720		}
22721	}
22722
22723	/**
22724	 * Add a single no-write region on selected page.
22725	 * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code.
22726	 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22727	 * You can set multiple regions for the same page.
22728	 * @param array $region array of a single no-write region array: ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right).
22729	 * @author Nicola Asuni
22730	 * @public
22731	 * @since 5.9.003 (2010-10-13)
22732	 * @see setPageRegions(), getPageRegions()
22733	 */
22734	public function addPageRegion($region) {
22735		if (!isset($region['page']) OR empty($region['page'])) {
22736			$region['page'] = $this->page;
22737		}
22738		if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
22739			AND isset($region['yt'])  AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
22740			AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
22741			$this->page_regions[] = $region;
22742		}
22743	}
22744
22745	/**
22746	 * Remove a single no-write region.
22747	 * @param int $key region key
22748	 * @author Nicola Asuni
22749	 * @public
22750	 * @since 5.9.003 (2010-10-13)
22751	 * @see setPageRegions(), getPageRegions()
22752	 */
22753	public function removePageRegion($key) {
22754		if (isset($this->page_regions[$key])) {
22755			unset($this->page_regions[$key]);
22756		}
22757	}
22758
22759	/**
22760	 * Check page for no-write regions and adapt current coordinates and page margins if necessary.
22761	 * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code.
22762	 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22763	 * @param float $h height of the text/image/object to print in user units
22764	 * @param float $x current X coordinate in user units
22765	 * @param float $y current Y coordinate in user units
22766	 * @return float[] array($x, $y)
22767	 * @author Nicola Asuni
22768	 * @protected
22769	 * @since 5.9.003 (2010-10-13)
22770	 */
22771	protected function checkPageRegions($h, $x, $y) {
22772		// set default values
22773		if ($x === '') {
22774			$x = $this->x;
22775		}
22776		if ($y === '') {
22777			$y = $this->y;
22778		}
22779		if (!$this->check_page_regions OR empty($this->page_regions)) {
22780			// no page regions defined
22781			return array($x, $y);
22782		}
22783		if (empty($h)) {
22784			$h = $this->getCellHeight($this->FontSize);
22785		}
22786		// check for page break
22787		if ($this->checkPageBreak($h, $y)) {
22788			// the content will be printed on a new page
22789			$x = $this->x;
22790			$y = $this->y;
22791		}
22792		if ($this->num_columns > 1) {
22793			if ($this->rtl) {
22794				$this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22795			} else {
22796				$this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22797			}
22798		} else {
22799			if ($this->rtl) {
22800				$this->lMargin = max($this->clMargin, $this->original_lMargin);
22801			} else {
22802				$this->rMargin = max($this->crMargin, $this->original_rMargin);
22803			}
22804		}
22805		// adjust coordinates and page margins
22806		foreach ($this->page_regions as $regid => $regdata) {
22807			if ($regdata['page'] == $this->page) {
22808				// check region boundaries
22809				if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
22810					// Y is inside the region
22811					$minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
22812					$yt = max($y, $regdata['yt']);
22813					$yb = min(($yt + $h), $regdata['yb']);
22814					$xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt'];
22815					$xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt'];
22816					if ($regdata['side'] == 'L') { // left side
22817						$new_margin = max($xt, $xb);
22818						if ($this->lMargin < $new_margin) {
22819							if ($this->rtl) {
22820								// adjust left page margin
22821								$this->lMargin = max(0, $new_margin);
22822							}
22823							if ($x < $new_margin) {
22824								// adjust x position
22825								$x = $new_margin;
22826								if ($new_margin > ($this->w - $this->rMargin)) {
22827									// adjust y position
22828									$y = $regdata['yb'] - $h;
22829								}
22830							}
22831						}
22832					} elseif ($regdata['side'] == 'R') { // right side
22833						$new_margin = min($xt, $xb);
22834						if (($this->w - $this->rMargin) > $new_margin) {
22835							if (!$this->rtl) {
22836								// adjust right page margin
22837								$this->rMargin = max(0, ($this->w - $new_margin));
22838							}
22839							if ($x > $new_margin) {
22840								// adjust x position
22841								$x = $new_margin;
22842								if ($new_margin > $this->lMargin) {
22843									// adjust y position
22844									$y = $regdata['yb'] - $h;
22845								}
22846							}
22847						}
22848					}
22849				}
22850			}
22851		}
22852		return array($x, $y);
22853	}
22854
22855	// --- SVG METHODS ---------------------------------------------------------
22856
22857	/**
22858	 * Embedd a Scalable Vector Graphics (SVG) image.
22859	 * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
22860	 * @param string $file Name of the SVG file or a '@' character followed by the SVG data string.
22861	 * @param float|null $x Abscissa of the upper-left corner.
22862	 * @param float|null $y Ordinate of the upper-left corner.
22863	 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
22864	 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
22865	 * @param mixed $link URL or identifier returned by AddLink().
22866	 * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul> If the alignment is an empty string, then the pointer will be restored on the starting SVG position.
22867	 * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
22868	 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
22869	 * @param boolean $fitonpage if true the image is resized to not exceed page dimensions.
22870	 * @author Nicola Asuni
22871	 * @since 5.0.000 (2010-05-02)
22872	 * @public
22873	 */
22874	public function ImageSVG($file, $x=null, $y=null, $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
22875		if ($this->state != 2) {
22876			 return;
22877		}
22878		// reset SVG vars
22879		$this->svggradients = array();
22880		$this->svggradientid = 0;
22881		$this->svgdefsmode = false;
22882		$this->svgdefs = array();
22883		$this->svgclipmode = false;
22884		$this->svgclippaths = array();
22885		$this->svgcliptm = array();
22886		$this->svgclipid = 0;
22887		$this->svgtext = '';
22888		$this->svgtextmode = array();
22889		if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
22890			// convert SVG to raster image using GD or ImageMagick libraries
22891			return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22892		}
22893		if ($file[0] === '@') { // image from string
22894			$this->svgdir = '';
22895			$svgdata = substr($file, 1);
22896		} else { // SVG file
22897			$this->svgdir = dirname($file);
22898            $svgdata = $this->getCachedFileContents($file);
22899		}
22900		if ($svgdata === FALSE) {
22901			$this->Error('SVG file not found: '.$file);
22902		}
22903		if (TCPDF_STATIC::empty_string($x)) {
22904			$x = $this->x;
22905		}
22906		if (TCPDF_STATIC::empty_string($y)) {
22907			$y = $this->y;
22908		}
22909		// check page for no-write regions and adapt page margins if necessary
22910		list($x, $y) = $this->checkPageRegions($h, $x, $y);
22911		$k = $this->k;
22912		$ox = 0;
22913		$oy = 0;
22914		$ow = $w;
22915		$oh = $h;
22916		$aspect_ratio_align = 'xMidYMid';
22917		$aspect_ratio_ms = 'meet';
22918		$regs = array();
22919		// get original image width and height
22920		preg_match('/<svg([^\>]*)>/si', $svgdata, $regs);
22921		if (isset($regs[1]) AND !empty($regs[1])) {
22922			$tmp = array();
22923			if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22924				$ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22925			}
22926			$tmp = array();
22927			if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22928				$oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22929			}
22930			$tmp = array();
22931			if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22932				$ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22933			}
22934			$tmp = array();
22935			if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22936				$oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22937			}
22938			$tmp = array();
22939			$view_box = array();
22940			if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
22941				if (count($tmp) == 5) {
22942					array_shift($tmp);
22943					foreach ($tmp as $key => $val) {
22944						$view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
22945					}
22946					$ox = $view_box[0];
22947					$oy = $view_box[1];
22948				}
22949				// get aspect ratio
22950				$tmp = array();
22951				if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22952					$aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
22953					switch (count($aspect_ratio)) {
22954						case 3: {
22955							$aspect_ratio_align = $aspect_ratio[1];
22956							$aspect_ratio_ms = $aspect_ratio[2];
22957							break;
22958						}
22959						case 2: {
22960							$aspect_ratio_align = $aspect_ratio[0];
22961							$aspect_ratio_ms = $aspect_ratio[1];
22962							break;
22963						}
22964						case 1: {
22965							$aspect_ratio_align = $aspect_ratio[0];
22966							$aspect_ratio_ms = 'meet';
22967							break;
22968						}
22969					}
22970				}
22971			}
22972		}
22973		if ($ow <= 0) {
22974			$ow = 1;
22975		}
22976		if ($oh <= 0) {
22977			$oh = 1;
22978		}
22979		// calculate image width and height on document
22980		if (($w <= 0) AND ($h <= 0)) {
22981			// convert image size to document unit
22982			$w = $ow;
22983			$h = $oh;
22984		} elseif ($w <= 0) {
22985			$w = $h * $ow / $oh;
22986		} elseif ($h <= 0) {
22987			$h = $w * $oh / $ow;
22988		}
22989		// fit the image on available space
22990		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22991		if ($this->rasterize_vector_images) {
22992			// convert SVG to raster image using GD or ImageMagick libraries
22993			return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22994		}
22995		// set alignment
22996		$this->img_rb_y = $y + $h;
22997		// set alignment
22998		if ($this->rtl) {
22999			if ($palign == 'L') {
23000				$ximg = $this->lMargin;
23001			} elseif ($palign == 'C') {
23002				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
23003			} elseif ($palign == 'R') {
23004				$ximg = $this->w - $this->rMargin - $w;
23005			} else {
23006				$ximg = $x - $w;
23007			}
23008			$this->img_rb_x = $ximg;
23009		} else {
23010			if ($palign == 'L') {
23011				$ximg = $this->lMargin;
23012			} elseif ($palign == 'C') {
23013				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
23014			} elseif ($palign == 'R') {
23015				$ximg = $this->w - $this->rMargin - $w;
23016			} else {
23017				$ximg = $x;
23018			}
23019			$this->img_rb_x = $ximg + $w;
23020		}
23021		// store current graphic vars
23022		$gvars = $this->getGraphicVars();
23023		// store SVG position and scale factors
23024		$svgoffset_x = ($ximg - $ox) * $this->k;
23025		$svgoffset_y = -($y - $oy) * $this->k;
23026		if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
23027			$ow = $view_box[2];
23028			$oh = $view_box[3];
23029		} else {
23030			if ($ow <= 0) {
23031				$ow = $w;
23032			}
23033			if ($oh <= 0) {
23034				$oh = $h;
23035			}
23036		}
23037		$svgscale_x = $w / $ow;
23038		$svgscale_y = $h / $oh;
23039		// scaling and alignment
23040		if ($aspect_ratio_align != 'none') {
23041			// store current scaling values
23042			$svgscale_old_x = $svgscale_x;
23043			$svgscale_old_y = $svgscale_y;
23044			// force uniform scaling
23045			if ($aspect_ratio_ms == 'slice') {
23046				// the entire viewport is covered by the viewBox
23047				if ($svgscale_x > $svgscale_y) {
23048					$svgscale_y = $svgscale_x;
23049				} elseif ($svgscale_x < $svgscale_y) {
23050					$svgscale_x = $svgscale_y;
23051				}
23052			} else { // meet
23053				// the entire viewBox is visible within the viewport
23054				if ($svgscale_x < $svgscale_y) {
23055					$svgscale_y = $svgscale_x;
23056				} elseif ($svgscale_x > $svgscale_y) {
23057					$svgscale_x = $svgscale_y;
23058				}
23059			}
23060			// correct X alignment
23061			switch (substr($aspect_ratio_align, 1, 3)) {
23062				case 'Min': {
23063					// do nothing
23064					break;
23065				}
23066				case 'Max': {
23067					$svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
23068					break;
23069				}
23070				default:
23071				case 'Mid': {
23072					$svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
23073					break;
23074				}
23075			}
23076			// correct Y alignment
23077			switch (substr($aspect_ratio_align, 5)) {
23078				case 'Min': {
23079					// do nothing
23080					break;
23081				}
23082				case 'Max': {
23083					$svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
23084					break;
23085				}
23086				default:
23087				case 'Mid': {
23088					$svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
23089					break;
23090				}
23091			}
23092		}
23093		// store current page break mode
23094		$page_break_mode = $this->AutoPageBreak;
23095		$page_break_margin = $this->getBreakMargin();
23096		$cell_padding = $this->cell_padding;
23097		$this->setCellPadding(0);
23098		$this->setAutoPageBreak(false);
23099		// save the current graphic state
23100		$this->_out('q'.$this->epsmarker);
23101		// set initial clipping mask
23102		$this->Rect($ximg, $y, $w, $h, 'CNZ', array(), array());
23103		// scale and translate
23104		$e = $ox * $this->k * (1 - $svgscale_x);
23105		$f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
23106		$this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y)));
23107		// creates a new XML parser to be used by the other XML functions
23108		$parser = xml_parser_create('UTF-8');
23109		// the following function allows to use parser inside object
23110		xml_set_object($parser, $this);
23111		// disable case-folding for this XML parser
23112		xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
23113		// sets the element handler functions for the XML parser
23114		xml_set_element_handler($parser, 'startSVGElementHandler', 'endSVGElementHandler');
23115		// sets the character data handler function for the XML parser
23116		xml_set_character_data_handler($parser, 'segSVGContentHandler');
23117		// start parsing an XML document
23118		if (!xml_parse($parser, $svgdata)) {
23119			$error_message = sprintf('SVG Error: %s at line %d', xml_error_string(xml_get_error_code($parser)), xml_get_current_line_number($parser));
23120			$this->Error($error_message);
23121		}
23122		// free this XML parser
23123		xml_parser_free($parser);
23124
23125		// >= PHP 7.0.0 "explicitly unset the reference to parser to avoid memory leaks"
23126		unset($parser);
23127
23128		// restore previous graphic state
23129		$this->_out($this->epsmarker.'Q');
23130		// restore graphic vars
23131		$this->setGraphicVars($gvars);
23132		$this->lasth = $gvars['lasth'];
23133		if (!empty($border)) {
23134			$bx = $this->x;
23135			$by = $this->y;
23136			$this->x = $ximg;
23137			if ($this->rtl) {
23138				$this->x += $w;
23139			}
23140			$this->y = $y;
23141			$this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
23142			$this->x = $bx;
23143			$this->y = $by;
23144		}
23145		if ($link) {
23146			$this->Link($ximg, $y, $w, $h, $link, 0);
23147		}
23148		// set pointer to align the next text/objects
23149		switch($align) {
23150			case 'T':{
23151				$this->y = $y;
23152				$this->x = $this->img_rb_x;
23153				break;
23154			}
23155			case 'M':{
23156				$this->y = $y + round($h/2);
23157				$this->x = $this->img_rb_x;
23158				break;
23159			}
23160			case 'B':{
23161				$this->y = $this->img_rb_y;
23162				$this->x = $this->img_rb_x;
23163				break;
23164			}
23165			case 'N':{
23166				$this->setY($this->img_rb_y);
23167				break;
23168			}
23169			default:{
23170				// restore pointer to starting position
23171				$this->x = $gvars['x'];
23172				$this->y = $gvars['y'];
23173				$this->page = $gvars['page'];
23174				$this->current_column = $gvars['current_column'];
23175				$this->tMargin = $gvars['tMargin'];
23176				$this->bMargin = $gvars['bMargin'];
23177				$this->w = $gvars['w'];
23178				$this->h = $gvars['h'];
23179				$this->wPt = $gvars['wPt'];
23180				$this->hPt = $gvars['hPt'];
23181				$this->fwPt = $gvars['fwPt'];
23182				$this->fhPt = $gvars['fhPt'];
23183				break;
23184			}
23185		}
23186		$this->endlinex = $this->img_rb_x;
23187		// restore page break
23188		$this->setAutoPageBreak($page_break_mode, $page_break_margin);
23189		$this->cell_padding = $cell_padding;
23190	}
23191
23192	/**
23193	 * Convert SVG transformation matrix to PDF.
23194	 * @param array $tm original SVG transformation matrix
23195	 * @return array transformation matrix
23196	 * @protected
23197	 * @since 5.0.000 (2010-05-02)
23198	 */
23199	protected function convertSVGtMatrix($tm) {
23200		$a = $tm[0];
23201		$b = -$tm[1];
23202		$c = -$tm[2];
23203		$d = $tm[3];
23204		$e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
23205		$f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
23206		$x = 0;
23207		$y = $this->h * $this->k;
23208		$e = ($x * (1 - $a)) - ($y * $c) + $e;
23209		$f = ($y * (1 - $d)) - ($x * $b) + $f;
23210		return array($a, $b, $c, $d, $e, $f);
23211	}
23212
23213	/**
23214	 * Apply SVG graphic transformation matrix.
23215	 * @param array $tm original SVG transformation matrix
23216	 * @protected
23217	 * @since 5.0.000 (2010-05-02)
23218	 */
23219	protected function SVGTransform($tm) {
23220		$this->Transform($this->convertSVGtMatrix($tm));
23221	}
23222
23223	/**
23224	 * Apply the requested SVG styles (*** TO BE COMPLETED ***)
23225	 * @param array $svgstyle array of SVG styles to apply
23226	 * @param array $prevsvgstyle array of previous SVG style
23227	 * @param int $x X origin of the bounding box
23228	 * @param int $y Y origin of the bounding box
23229	 * @param int $w width of the bounding box
23230	 * @param int $h height of the bounding box
23231	 * @param string $clip_function clip function
23232	 * @param array $clip_params array of parameters for clipping function
23233	 * @return string style
23234	 * @author Nicola Asuni
23235	 * @since 5.0.000 (2010-05-02)
23236	 * @protected
23237	 */
23238	protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
23239		if ($this->state != 2) {
23240			 return;
23241		}
23242		$objstyle = '';
23243		$minlen = (0.01 / $this->k); // minimum acceptable length
23244		if (!isset($svgstyle['opacity'])) {
23245			return $objstyle;
23246		}
23247		// clip-path
23248		$regs = array();
23249		if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
23250			$clip_path = $this->svgclippaths[$regs[1]];
23251			foreach ($clip_path as $cp) {
23252				$this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
23253			}
23254		}
23255		// opacity
23256		if ($svgstyle['opacity'] != 1) {
23257			$this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false);
23258		}
23259		// color
23260		$fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors);
23261		$this->setFillColorArray($fill_color);
23262		// text color
23263		$text_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors);
23264		$this->setTextColorArray($text_color);
23265		// clip
23266		if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
23267			$top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
23268			$right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
23269			$bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
23270			$left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
23271			$cx = $x + $left;
23272			$cy = $y + $top;
23273			$cw = $w - $left - $right;
23274			$ch = $h - $top - $bottom;
23275			if ($svgstyle['clip-rule'] == 'evenodd') {
23276				$clip_rule = 'CNZ';
23277			} else {
23278				$clip_rule = 'CEO';
23279			}
23280			$this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
23281		}
23282		// fill
23283		$regs = array();
23284		if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
23285			// gradient
23286			$gradient = $this->svggradients[$regs[1]];
23287			if (isset($gradient['xref'])) {
23288				// reference to another gradient definition
23289				$newgradient = $this->svggradients[$gradient['xref']];
23290				$newgradient['coords'] = $gradient['coords'];
23291				$newgradient['mode'] = $gradient['mode'];
23292				$newgradient['type'] = $gradient['type'];
23293				$newgradient['gradientUnits'] = $gradient['gradientUnits'];
23294				if (isset($gradient['gradientTransform'])) {
23295					$newgradient['gradientTransform'] = $gradient['gradientTransform'];
23296				}
23297				$gradient = $newgradient;
23298			}
23299			//save current Graphic State
23300			$this->_outSaveGraphicsState();
23301			//set clipping area
23302			if (!empty($clip_function) AND method_exists($this, $clip_function)) {
23303				$bbox = call_user_func_array(array($this, $clip_function), $clip_params);
23304				if ((!isset($gradient['type']) OR ($gradient['type'] != 3)) AND is_array($bbox) AND (count($bbox) == 4)) {
23305					list($x, $y, $w, $h) = $bbox;
23306				}
23307			}
23308			if ($gradient['mode'] == 'measure') {
23309				if (!isset($gradient['coords'][4])) {
23310					$gradient['coords'][4] = 0.5;
23311				}
23312				if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
23313					$gtm = $gradient['gradientTransform'];
23314					// apply transformation matrix
23315					$xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
23316					$ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
23317					$xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
23318					$yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
23319					$r = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
23320					$gradient['coords'][0] = $xa;
23321					$gradient['coords'][1] = $ya;
23322					$gradient['coords'][2] = $xb;
23323					$gradient['coords'][3] = $yb;
23324					$gradient['coords'][4] = $r;
23325				}
23326				// convert SVG coordinates to user units
23327				$gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
23328				$gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
23329				$gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
23330				$gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
23331				$gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
23332				if ($w <= $minlen) {
23333					$w = $minlen;
23334				}
23335				if ($h <= $minlen) {
23336					$h = $minlen;
23337				}
23338				// shift units
23339				if ($gradient['gradientUnits'] == 'objectBoundingBox') {
23340					// convert to SVG coordinate system
23341					$gradient['coords'][0] += $x;
23342					$gradient['coords'][1] += $y;
23343					$gradient['coords'][2] += $x;
23344					$gradient['coords'][3] += $y;
23345				}
23346				// calculate percentages
23347				$gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w);
23348				$gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h);
23349				$gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w);
23350				$gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h);
23351				$gradient['coords'][4] /= $w;
23352			} elseif ($gradient['mode'] == 'percentage') {
23353				foreach($gradient['coords'] as $key => $val) {
23354					$gradient['coords'][$key] = (intval($val) / 100);
23355					if ($val < 0) {
23356						$gradient['coords'][$key] = 0;
23357					} elseif ($val > 1) {
23358						$gradient['coords'][$key] = 1;
23359					}
23360				}
23361			}
23362			if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
23363				// single color (no shading)
23364				$gradient['coords'][0] = 1;
23365				$gradient['coords'][1] = 0;
23366				$gradient['coords'][2] = 0.999;
23367				$gradient['coords'][3] = 0;
23368			}
23369			// swap Y coordinates
23370			$tmp = $gradient['coords'][1];
23371			$gradient['coords'][1] = $gradient['coords'][3];
23372			$gradient['coords'][3] = $tmp;
23373			// set transformation map for gradient
23374			$cy = ($this->h - $y);
23375			if ($gradient['type'] == 3) {
23376				// circular gradient
23377				$cy -= ($gradient['coords'][1] * ($w + $h));
23378				$h = $w = max($w, $h);
23379			} else {
23380				$cy -= $h;
23381			}
23382			$this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), ($cy * $this->k)));
23383			if (count($gradient['stops']) > 1) {
23384				$this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
23385			}
23386		} elseif ($svgstyle['fill'] != 'none') {
23387			$fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors);
23388			if ($svgstyle['fill-opacity'] != 1) {
23389				$this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false);
23390			}
23391			$this->setFillColorArray($fill_color);
23392			if ($svgstyle['fill-rule'] == 'evenodd') {
23393				$objstyle .= 'F*';
23394			} else {
23395				$objstyle .= 'F';
23396			}
23397		}
23398		// stroke
23399		if ($svgstyle['stroke'] != 'none') {
23400			if ($svgstyle['stroke-opacity'] != 1) {
23401				$this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false);
23402			} elseif (preg_match('/rgba\(\d+%?,\s*\d+%?,\s*\d+%?,\s*(\d+(?:\.\d+)?)\)/i', $svgstyle['stroke'], $rgba_matches)) {
23403				$this->setAlpha($rgba_matches[1], 'Normal', $this->alpha['ca'], false);
23404			}
23405			$stroke_style = array(
23406				'color' => TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors),
23407				'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
23408				'cap' => $svgstyle['stroke-linecap'],
23409				'join' => $svgstyle['stroke-linejoin']
23410				);
23411			if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
23412				$stroke_style['dash'] = $svgstyle['stroke-dasharray'];
23413			}
23414			$this->setLineStyle($stroke_style);
23415			$objstyle .= 'D';
23416		}
23417		// font
23418		$regs = array();
23419		if (!empty($svgstyle['font'])) {
23420			if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
23421				$font_family = $this->getFontFamilyName($regs[1]);
23422			} else {
23423				$font_family = $svgstyle['font-family'];
23424			}
23425			if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23426				$font_size = trim($regs[1]);
23427			} else {
23428				$font_size = $svgstyle['font-size'];
23429			}
23430			if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23431				$font_style = trim($regs[1]);
23432			} else {
23433				$font_style = $svgstyle['font-style'];
23434			}
23435			if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23436				$font_weight = trim($regs[1]);
23437			} else {
23438				$font_weight = $svgstyle['font-weight'];
23439			}
23440			if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23441				$font_stretch = trim($regs[1]);
23442			} else {
23443				$font_stretch = $svgstyle['font-stretch'];
23444			}
23445			if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23446				$font_spacing = trim($regs[1]);
23447			} else {
23448				$font_spacing = $svgstyle['letter-spacing'];
23449			}
23450		} else {
23451			$font_family = $this->getFontFamilyName($svgstyle['font-family']);
23452			$font_size = $svgstyle['font-size'];
23453			$font_style = $svgstyle['font-style'];
23454			$font_weight = $svgstyle['font-weight'];
23455			$font_stretch = $svgstyle['font-stretch'];
23456			$font_spacing = $svgstyle['letter-spacing'];
23457		}
23458		$font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit);
23459		$font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
23460		$font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
23461		switch ($font_style) {
23462			case 'italic': {
23463				$font_style = 'I';
23464				break;
23465			}
23466			case 'oblique': {
23467				$font_style = 'I';
23468				break;
23469			}
23470			default:
23471			case 'normal': {
23472				$font_style = '';
23473				break;
23474			}
23475		}
23476		switch ($font_weight) {
23477			case 'bold':
23478			case 'bolder': {
23479				$font_style .= 'B';
23480				break;
23481			}
23482			case 'normal': {
23483				if ((substr($font_family, -1) == 'I') AND (substr($font_family, -2, 1) == 'B')) {
23484					$font_family = substr($font_family, 0, -2).'I';
23485				} elseif (substr($font_family, -1) == 'B') {
23486					$font_family = substr($font_family, 0, -1);
23487				}
23488				break;
23489			}
23490		}
23491		switch ($svgstyle['text-decoration']) {
23492			case 'underline': {
23493				$font_style .= 'U';
23494				break;
23495			}
23496			case 'overline': {
23497				$font_style .= 'O';
23498				break;
23499			}
23500			case 'line-through': {
23501				$font_style .= 'D';
23502				break;
23503			}
23504			default:
23505			case 'none': {
23506				break;
23507			}
23508		}
23509		$this->setFont($font_family, $font_style, $font_size);
23510		$this->setFontStretching($font_stretch);
23511		$this->setFontSpacing($font_spacing);
23512		return $objstyle;
23513	}
23514
23515	/**
23516	 * Draws an SVG path
23517	 * @param string $d attribute d of the path SVG element
23518	 * @param string $style Style of rendering. Possible values are:
23519	 * <ul>
23520	 *	 <li>D or empty string: Draw (default).</li>
23521	 *	 <li>F: Fill.</li>
23522	 *	 <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
23523	 *	 <li>DF or FD: Draw and fill.</li>
23524	 *	 <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
23525	 *	 <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
23526	 *	 <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
23527	 * </ul>
23528	 * @return array of container box measures (x, y, w, h)
23529	 * @author Nicola Asuni
23530	 * @since 5.0.000 (2010-05-02)
23531	 * @protected
23532	 */
23533	protected function SVGPath($d, $style='') {
23534		if ($this->state != 2) {
23535			return;
23536		}
23537		// set fill/stroke style
23538		$op = TCPDF_STATIC::getPathPaintOperator($style, '');
23539		if (empty($op)) {
23540			return;
23541		}
23542		$paths = array();
23543		$d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
23544		$d = preg_replace('/(\.[0-9]+)(\.)/s', '\\1 \\2', $d);
23545		preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER);
23546		$x = 0;
23547		$y = 0;
23548		$x1 = 0;
23549		$y1 = 0;
23550		$x2 = 0;
23551		$y2 = 0;
23552		$xmin = 2147483647;
23553		$xmax = 0;
23554		$ymin = 2147483647;
23555		$ymax = 0;
23556		$xinitial = 0;
23557		$yinitial = 0;
23558		$relcoord = false;
23559		$minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
23560		$firstcmd = true; // used to print first point
23561		// draw curve pieces
23562		foreach ($paths as $key => $val) {
23563			// get curve type
23564			$cmd = trim($val[1]);
23565			if (strtolower($cmd) == $cmd) {
23566				// use relative coordinated instead of absolute
23567				$relcoord = true;
23568				$xoffset = $x;
23569				$yoffset = $y;
23570			} else {
23571				$relcoord = false;
23572				$xoffset = 0;
23573				$yoffset = 0;
23574			}
23575			$params = array();
23576			if (isset($val[2])) {
23577				// get curve parameters
23578				$rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
23579				$params = array();
23580				foreach ($rawparams as $ck => $cp) {
23581					$params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
23582					if (abs($params[$ck]) < $minlen) {
23583						// approximate little values to zero
23584						$params[$ck] = 0;
23585					}
23586				}
23587			}
23588			// store current origin point
23589			$x0 = $x;
23590			$y0 = $y;
23591			switch (strtoupper($cmd)) {
23592				case 'M': { // moveto
23593					foreach ($params as $ck => $cp) {
23594						if (($ck % 2) == 0) {
23595							$x = $cp + $xoffset;
23596						} else {
23597							$y = $cp + $yoffset;
23598							if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23599								if ($ck == 1) {
23600									$this->_outPoint($x, $y);
23601									$firstcmd = false;
23602									$xinitial = $x;
23603									$yinitial = $y;
23604								} else {
23605									$this->_outLine($x, $y);
23606								}
23607								$x0 = $x;
23608								$y0 = $y;
23609							}
23610							$xmin = min($xmin, $x);
23611							$ymin = min($ymin, $y);
23612							$xmax = max($xmax, $x);
23613							$ymax = max($ymax, $y);
23614							if ($relcoord) {
23615								$xoffset = $x;
23616								$yoffset = $y;
23617							}
23618						}
23619					}
23620					break;
23621				}
23622				case 'L': { // lineto
23623					foreach ($params as $ck => $cp) {
23624						if (($ck % 2) == 0) {
23625							$x = $cp + $xoffset;
23626						} else {
23627							$y = $cp + $yoffset;
23628							if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23629								$this->_outLine($x, $y);
23630								$x0 = $x;
23631								$y0 = $y;
23632							}
23633							$xmin = min($xmin, $x);
23634							$ymin = min($ymin, $y);
23635							$xmax = max($xmax, $x);
23636							$ymax = max($ymax, $y);
23637							if ($relcoord) {
23638								$xoffset = $x;
23639								$yoffset = $y;
23640							}
23641						}
23642					}
23643					break;
23644				}
23645				case 'H': { // horizontal lineto
23646					foreach ($params as $ck => $cp) {
23647						$x = $cp + $xoffset;
23648						if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23649							$this->_outLine($x, $y);
23650							$x0 = $x;
23651							$y0 = $y;
23652						}
23653						$xmin = min($xmin, $x);
23654						$xmax = max($xmax, $x);
23655						if ($relcoord) {
23656							$xoffset = $x;
23657						}
23658					}
23659					break;
23660				}
23661				case 'V': { // vertical lineto
23662					foreach ($params as $ck => $cp) {
23663						$y = $cp + $yoffset;
23664						if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23665							$this->_outLine($x, $y);
23666							$x0 = $x;
23667							$y0 = $y;
23668						}
23669						$ymin = min($ymin, $y);
23670						$ymax = max($ymax, $y);
23671						if ($relcoord) {
23672							$yoffset = $y;
23673						}
23674					}
23675					break;
23676				}
23677				case 'C': { // curveto
23678					foreach ($params as $ck => $cp) {
23679						$params[$ck] = $cp;
23680						if ((($ck + 1) % 6) == 0) {
23681							$x1 = $params[($ck - 5)] + $xoffset;
23682							$y1 = $params[($ck - 4)] + $yoffset;
23683							$x2 = $params[($ck - 3)] + $xoffset;
23684							$y2 = $params[($ck - 2)] + $yoffset;
23685							$x = $params[($ck - 1)] + $xoffset;
23686							$y = $params[($ck)] + $yoffset;
23687							$this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23688							$xmin = min($xmin, $x, $x1, $x2);
23689							$ymin = min($ymin, $y, $y1, $y2);
23690							$xmax = max($xmax, $x, $x1, $x2);
23691							$ymax = max($ymax, $y, $y1, $y2);
23692							if ($relcoord) {
23693								$xoffset = $x;
23694								$yoffset = $y;
23695							}
23696						}
23697					}
23698					break;
23699				}
23700				case 'S': { // shorthand/smooth curveto
23701					foreach ($params as $ck => $cp) {
23702						$params[$ck] = $cp;
23703						if ((($ck + 1) % 4) == 0) {
23704							if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
23705								$x1 = (2 * $x) - $x2;
23706								$y1 = (2 * $y) - $y2;
23707							} else {
23708								$x1 = $x;
23709								$y1 = $y;
23710							}
23711							$x2 = $params[($ck - 3)] + $xoffset;
23712							$y2 = $params[($ck - 2)] + $yoffset;
23713							$x = $params[($ck - 1)] + $xoffset;
23714							$y = $params[($ck)] + $yoffset;
23715							$this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23716							$xmin = min($xmin, $x, $x1, $x2);
23717							$ymin = min($ymin, $y, $y1, $y2);
23718							$xmax = max($xmax, $x, $x1, $x2);
23719							$ymax = max($ymax, $y, $y1, $y2);
23720							if ($relcoord) {
23721								$xoffset = $x;
23722								$yoffset = $y;
23723							}
23724						}
23725					}
23726					break;
23727				}
23728				case 'Q': { // quadratic Bezier curveto
23729					foreach ($params as $ck => $cp) {
23730						$params[$ck] = $cp;
23731						if ((($ck + 1) % 4) == 0) {
23732							// convert quadratic points to cubic points
23733							$x1 = $params[($ck - 3)] + $xoffset;
23734							$y1 = $params[($ck - 2)] + $yoffset;
23735							$xa = ($x + (2 * $x1)) / 3;
23736							$ya = ($y + (2 * $y1)) / 3;
23737							$x = $params[($ck - 1)] + $xoffset;
23738							$y = $params[($ck)] + $yoffset;
23739							$xb = ($x + (2 * $x1)) / 3;
23740							$yb = ($y + (2 * $y1)) / 3;
23741							$this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23742							$xmin = min($xmin, $x, $xa, $xb);
23743							$ymin = min($ymin, $y, $ya, $yb);
23744							$xmax = max($xmax, $x, $xa, $xb);
23745							$ymax = max($ymax, $y, $ya, $yb);
23746							if ($relcoord) {
23747								$xoffset = $x;
23748								$yoffset = $y;
23749							}
23750						}
23751					}
23752					break;
23753				}
23754				case 'T': { // shorthand/smooth quadratic Bezier curveto
23755					foreach ($params as $ck => $cp) {
23756						$params[$ck] = $cp;
23757						if (($ck % 2) != 0) {
23758							if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
23759								$x1 = (2 * $x) - $x1;
23760								$y1 = (2 * $y) - $y1;
23761							} else {
23762								$x1 = $x;
23763								$y1 = $y;
23764							}
23765							// convert quadratic points to cubic points
23766							$xa = ($x + (2 * $x1)) / 3;
23767							$ya = ($y + (2 * $y1)) / 3;
23768							$x = $params[($ck - 1)] + $xoffset;
23769							$y = $params[($ck)] + $yoffset;
23770							$xb = ($x + (2 * $x1)) / 3;
23771							$yb = ($y + (2 * $y1)) / 3;
23772							$this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23773							$xmin = min($xmin, $x, $xa, $xb);
23774							$ymin = min($ymin, $y, $ya, $yb);
23775							$xmax = max($xmax, $x, $xa, $xb);
23776							$ymax = max($ymax, $y, $ya, $yb);
23777							if ($relcoord) {
23778								$xoffset = $x;
23779								$yoffset = $y;
23780							}
23781						}
23782					}
23783					break;
23784				}
23785				case 'A': { // elliptical arc
23786					foreach ($params as $ck => $cp) {
23787						$params[$ck] = $cp;
23788						if ((($ck + 1) % 7) == 0) {
23789							$x0 = $x;
23790							$y0 = $y;
23791							$rx = max(abs($params[($ck - 6)]), .000000001);
23792							$ry = max(abs($params[($ck - 5)]), .000000001);
23793							$ang = -$rawparams[($ck - 4)];
23794							$angle = deg2rad($ang);
23795							$fa = $rawparams[($ck - 3)]; // large-arc-flag
23796							$fs = $rawparams[($ck - 2)]; // sweep-flag
23797							$x = $params[($ck - 1)] + $xoffset;
23798							$y = $params[$ck] + $yoffset;
23799							if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
23800								// endpoints are almost identical
23801								$xmin = min($xmin, $x);
23802								$ymin = min($ymin, $y);
23803								$xmax = max($xmax, $x);
23804								$ymax = max($ymax, $y);
23805							} else {
23806								$cos_ang = cos($angle);
23807								$sin_ang = sin($angle);
23808								$a = (($x0 - $x) / 2);
23809								$b = (($y0 - $y) / 2);
23810								$xa = ($a * $cos_ang) - ($b * $sin_ang);
23811								$ya = ($a * $sin_ang) + ($b * $cos_ang);
23812								$rx2 = $rx * $rx;
23813								$ry2 = $ry * $ry;
23814								$xa2 = $xa * $xa;
23815								$ya2 = $ya * $ya;
23816								$delta = ($xa2 / $rx2) + ($ya2 / $ry2);
23817								if ($delta > 1) {
23818									$rx *= sqrt($delta);
23819									$ry *= sqrt($delta);
23820									$rx2 = $rx * $rx;
23821									$ry2 = $ry * $ry;
23822								}
23823								$numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
23824								if ($numerator < 0) {
23825									$root = 0;
23826								} else {
23827									$root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
23828								}
23829								if ($fa == $fs){
23830									$root *= -1;
23831								}
23832								$cax = $root * (($rx * $ya) / $ry);
23833								$cay = -$root * (($ry * $xa) / $rx);
23834								// coordinates of ellipse center
23835								$cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
23836								$cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
23837								// get angles
23838								$angs = TCPDF_STATIC::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
23839								$dang = TCPDF_STATIC::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
23840								if (($fs == 0) AND ($dang > 0)) {
23841									$dang -= (2 * M_PI);
23842								} elseif (($fs == 1) AND ($dang < 0)) {
23843									$dang += (2 * M_PI);
23844								}
23845								$angf = $angs - $dang;
23846								if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
23847									// reverse angles
23848									$tmp = $angs;
23849									$angs = $angf;
23850									$angf = $tmp;
23851								}
23852								$angs = round(rad2deg($angs), 6);
23853								$angf = round(rad2deg($angf), 6);
23854								// covent angles to positive values
23855								if (($angs < 0) AND ($angf < 0)) {
23856									$angs += 360;
23857									$angf += 360;
23858								}
23859								$pie = false;
23860								if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
23861									$pie = true;
23862								}
23863								list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
23864								$xmin = min($xmin, $x, $axmin);
23865								$ymin = min($ymin, $y, $aymin);
23866								$xmax = max($xmax, $x, $axmax);
23867								$ymax = max($ymax, $y, $aymax);
23868							}
23869							if ($relcoord) {
23870								$xoffset = $x;
23871								$yoffset = $y;
23872							}
23873						}
23874					}
23875					break;
23876				}
23877				case 'Z': {
23878					$this->_out('h');
23879					$x = $x0 = $xinitial;
23880					$y = $y0 = $yinitial;
23881					break;
23882				}
23883			}
23884			$firstcmd = false;
23885		} // end foreach
23886		if (!empty($op)) {
23887			$this->_out($op);
23888		}
23889		return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
23890	}
23891
23892	/**
23893	 * Return the tag name without the namespace
23894	 * @param string $name Tag name
23895	 * @protected
23896	 */
23897	protected function removeTagNamespace($name) {
23898		if(strpos($name, ':') !== false) {
23899			$parts = explode(':', $name);
23900			return $parts[(sizeof($parts) - 1)];
23901		}
23902		return $name;
23903	}
23904
23905	/**
23906	 * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***)
23907	 * @param resource|string $parser The first parameter, parser, is a reference to the XML parser calling the handler.
23908	 * @param string $name The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters.
23909	 * @param array $attribs The third parameter, attribs, contains an associative array with the element's attributes (if any). The keys of this array are the attribute names, the values are the attribute values. Attribute names are case-folded on the same criteria as element names. Attribute values are not case-folded. The original order of the attributes can be retrieved by walking through attribs the normal way, using each(). The first key in the array was the first attribute, and so on.
23910	 * @param array $ctm tranformation matrix for clipping mode (starting transformation matrix).
23911	 * @author Nicola Asuni
23912	 * @since 5.0.000 (2010-05-02)
23913	 * @protected
23914	 */
23915	protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
23916		$name = $this->removeTagNamespace($name);
23917		// check if we are in clip mode
23918		if ($this->svgclipmode) {
23919			$this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]);
23920			return;
23921		}
23922		if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
23923			if (isset($attribs['id'])) {
23924				$attribs['child_elements'] = array();
23925				$this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23926				return;
23927			}
23928			if (end($this->svgdefs) !== FALSE) {
23929				$last_svgdefs_id = key($this->svgdefs);
23930				if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
23931					$attribs['id'] = 'DF_'.(count($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements']) + 1);
23932					$this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23933					return;
23934				}
23935			}
23936			return;
23937		}
23938		$clipping = false;
23939		if ($parser == 'clip-path') {
23940			// set clipping mode
23941			$clipping = true;
23942		}
23943		// get styling properties
23944		$prev_svgstyle = $this->svgstyles[max(0,(count($this->svgstyles) - 1))]; // previous style
23945		$svgstyle = $this->svgstyles[0]; // set default style
23946		if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) {
23947			// default fill attribute for clipping
23948			$attribs['fill'] = 'none';
23949		}
23950		if (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) {
23951			// fix style for regular expression
23952			$attribs['style'] = ';'.$attribs['style'];
23953		}
23954		foreach ($prev_svgstyle as $key => $val) {
23955			if (in_array($key, TCPDF_IMAGES::$svginheritprop)) {
23956				// inherit previous value
23957				$svgstyle[$key] = $val;
23958			}
23959			if (isset($attribs[$key]) AND !TCPDF_STATIC::empty_string($attribs[$key])) {
23960				// specific attribute settings
23961				if ($attribs[$key] == 'inherit') {
23962					$svgstyle[$key] = $val;
23963				} else {
23964					$svgstyle[$key] = $attribs[$key];
23965				}
23966			} elseif (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) {
23967				// CSS style syntax
23968				$attrval = array();
23969				if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
23970					if ($attrval[1] == 'inherit') {
23971						$svgstyle[$key] = $val;
23972					} else {
23973						$svgstyle[$key] = $attrval[1];
23974					}
23975				}
23976			}
23977		}
23978		// transformation matrix
23979		if (!empty($ctm)) {
23980			$tm = $ctm;
23981		} else {
23982			$tm = array(1,0,0,1,0,0);
23983		}
23984		if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
23985			$tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, TCPDF_STATIC::getSVGTransformMatrix($attribs['transform']));
23986		}
23987		$svgstyle['transfmatrix'] = $tm;
23988		$invisible = false;
23989		if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
23990			// the current graphics element is invisible (nothing is painted)
23991			$invisible = true;
23992		}
23993		// process tag
23994		switch($name) {
23995			case 'defs': {
23996				$this->svgdefsmode = true;
23997				break;
23998			}
23999			// clipPath
24000			case 'clipPath': {
24001				if ($invisible) {
24002					break;
24003				}
24004				$this->svgclipmode = true;
24005				if (!isset($attribs['id'])) {
24006					$attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1);
24007				}
24008				$this->svgclipid = $attribs['id'];
24009				$this->svgclippaths[$this->svgclipid] = array();
24010				$this->svgcliptm[$this->svgclipid] = $tm;
24011				break;
24012			}
24013			case 'svg': {
24014				// start of SVG object
24015				if(++$this->svg_tag_depth <= 1) {
24016					break;
24017				}
24018				// inner SVG
24019				array_push($this->svgstyles, $svgstyle);
24020				$this->StartTransform();
24021				$svgX = (isset($attribs['x'])?$attribs['x']:0);
24022				$svgY = (isset($attribs['y'])?$attribs['y']:0);
24023				$svgW = (isset($attribs['width'])?$attribs['width']:0);
24024				$svgH = (isset($attribs['height'])?$attribs['height']:0);
24025				// set x, y position using transform matrix
24026				$tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array( 1, 0, 0, 1, $svgX, $svgY));
24027				$this->SVGTransform($tm);
24028				// set clipping for width and height
24029				$x = 0;
24030				$y = 0;
24031				$w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):$this->w);
24032				$h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):$this->h);
24033				// draw clipping rect
24034				$this->Rect($x, $y, $w, $h, 'CNZ', array(), array());
24035				// parse viewbox, calculate extra transformation matrix
24036				if (isset($attribs['viewBox'])) {
24037					$tmp = array();
24038					preg_match_all("/[0-9]+/", $attribs['viewBox'], $tmp);
24039					$tmp = $tmp[0];
24040					if (sizeof($tmp) == 4) {
24041						$vx = $tmp[0];
24042						$vy = $tmp[1];
24043						$vw = $tmp[2];
24044						$vh = $tmp[3];
24045						// get aspect ratio
24046						$tmp = array();
24047						$aspectX = 'xMid';
24048						$aspectY = 'YMid';
24049						$fit = 'meet';
24050						if (isset($attribs['preserveAspectRatio'])) {
24051							if($attribs['preserveAspectRatio'] == 'none') {
24052								$fit = 'none';
24053							} else {
24054								preg_match_all('/[a-zA-Z]+/', $attribs['preserveAspectRatio'], $tmp);
24055								$tmp = $tmp[0];
24056								if ((sizeof($tmp) == 2) AND (strlen($tmp[0]) == 8) AND (in_array($tmp[1], array('meet', 'slice', 'none')))) {
24057									$aspectX = substr($tmp[0], 0, 4);
24058									$aspectY = substr($tmp[0], 4, 4);
24059									$fit = $tmp[1];
24060								}
24061							}
24062						}
24063						$wr = ($svgW / $vw);
24064						$hr = ($svgH / $vh);
24065						$ax = $ay = 0;
24066						if ((($fit == 'meet') AND ($hr < $wr)) OR (($fit == 'slice') AND ($hr > $wr))) {
24067							if ($aspectX == 'xMax') {
24068								$ax = (($vw * ($wr / $hr)) - $vw);
24069							}
24070							if ($aspectX == 'xMid') {
24071								$ax = ((($vw * ($wr / $hr)) - $vw) / 2);
24072							}
24073							$wr = $hr;
24074						} elseif ((($fit == 'meet') AND ($hr > $wr)) OR (($fit == 'slice') AND ($hr < $wr))) {
24075							if ($aspectY == 'YMax') {
24076								$ay = (($vh * ($hr / $wr)) - $vh);
24077							}
24078							if ($aspectY == 'YMid') {
24079								$ay = ((($vh * ($hr / $wr)) - $vh) / 2);
24080							}
24081							$hr = $wr;
24082						}
24083						$newtm = array($wr, 0, 0, $hr, (($wr * ($ax - $vx)) - $svgX), (($hr * ($ay - $vy)) - $svgY));
24084						$tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, $newtm);
24085						$this->SVGTransform($tm);
24086					}
24087				}
24088				$this->setSVGStyles($svgstyle, $prev_svgstyle);
24089				break;
24090			}
24091			case 'g': {
24092				// group together related graphics elements
24093				array_push($this->svgstyles, $svgstyle);
24094				$this->StartTransform();
24095				$x = (isset($attribs['x'])?$attribs['x']:0);
24096				$y = (isset($attribs['y'])?$attribs['y']:0);
24097				$w = 1;//(isset($attribs['width'])?$attribs['width']:1);
24098				$h = 1;//(isset($attribs['height'])?$attribs['height']:1);
24099				$tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
24100				$this->SVGTransform($tm);
24101				$this->setSVGStyles($svgstyle, $prev_svgstyle);
24102				break;
24103			}
24104			case 'linearGradient': {
24105				if ($this->pdfa_mode && $this->pdfa_version < 2) {
24106					break;
24107				}
24108				if (!isset($attribs['id'])) {
24109					$attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
24110				}
24111				$this->svggradientid = $attribs['id'];
24112				$this->svggradients[$this->svggradientid] = array();
24113				$this->svggradients[$this->svggradientid]['type'] = 2;
24114				$this->svggradients[$this->svggradientid]['stops'] = array();
24115				if (isset($attribs['gradientUnits'])) {
24116					$this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
24117				} else {
24118					$this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
24119				}
24120				//$attribs['spreadMethod']
24121				if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2'])))
24122					OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%'))
24123						OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%'))
24124						OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%'))
24125						OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) {
24126					$this->svggradients[$this->svggradientid]['mode'] = 'percentage';
24127				} else {
24128					$this->svggradients[$this->svggradientid]['mode'] = 'measure';
24129				}
24130				$x1 = (isset($attribs['x1'])?$attribs['x1']:'0');
24131				$y1 = (isset($attribs['y1'])?$attribs['y1']:'0');
24132				$x2 = (isset($attribs['x2'])?$attribs['x2']:'100');
24133				$y2 = (isset($attribs['y2'])?$attribs['y2']:'0');
24134				if (isset($attribs['gradientTransform'])) {
24135					$this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
24136				}
24137				$this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
24138				if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24139					// gradient is defined on another place
24140					$this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
24141				}
24142				break;
24143			}
24144			case 'radialGradient': {
24145				if ($this->pdfa_mode && $this->pdfa_version < 2) {
24146					break;
24147				}
24148				if (!isset($attribs['id'])) {
24149					$attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
24150				}
24151				$this->svggradientid = $attribs['id'];
24152				$this->svggradients[$this->svggradientid] = array();
24153				$this->svggradients[$this->svggradientid]['type'] = 3;
24154				$this->svggradients[$this->svggradientid]['stops'] = array();
24155				if (isset($attribs['gradientUnits'])) {
24156					$this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
24157				} else {
24158					$this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
24159				}
24160				//$attribs['spreadMethod']
24161				if (((!isset($attribs['cx'])) AND (!isset($attribs['cy'])))
24162					OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%'))
24163					OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')))) {
24164					$this->svggradients[$this->svggradientid]['mode'] = 'percentage';
24165				} elseif (isset($attribs['r']) AND is_numeric($attribs['r']) AND ($attribs['r']) <= 1) {
24166					$this->svggradients[$this->svggradientid]['mode'] = 'ratio';
24167				} else {
24168					$this->svggradients[$this->svggradientid]['mode'] = 'measure';
24169				}
24170				$cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5);
24171				$cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5);
24172				$fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx);
24173				$fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy);
24174				$r = (isset($attribs['r']) ? $attribs['r'] : 0.5);
24175				if (isset($attribs['gradientTransform'])) {
24176					$this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
24177				}
24178				$this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
24179				if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24180					// gradient is defined on another place
24181					$this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
24182				}
24183				break;
24184			}
24185			case 'stop': {
24186				// gradient stops
24187				if (substr($attribs['offset'], -1) == '%') {
24188					$offset = floatval(substr($attribs['offset'], 0, -1)) / 100;
24189				} else {
24190					$offset = floatval($attribs['offset']);
24191					if ($offset > 1) {
24192						$offset /= 100;
24193					}
24194				}
24195				$stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors):'black';
24196				$opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
24197				$this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
24198				break;
24199			}
24200			// paths
24201			case 'path': {
24202				if ($invisible) {
24203					break;
24204				}
24205				if (isset($attribs['d'])) {
24206					$d = trim($attribs['d']);
24207					if (!empty($d)) {
24208						$x = (isset($attribs['x'])?$attribs['x']:0);
24209						$y = (isset($attribs['y'])?$attribs['y']:0);
24210						$w = (isset($attribs['width'])?$attribs['width']:1);
24211						$h = (isset($attribs['height'])?$attribs['height']:1);
24212						$tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
24213						if ($clipping) {
24214							$this->SVGTransform($tm);
24215							$this->SVGPath($d, 'CNZ');
24216						} else {
24217							$this->StartTransform();
24218							$this->SVGTransform($tm);
24219							$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ'));
24220							if (!empty($obstyle)) {
24221								$this->SVGPath($d, $obstyle);
24222							}
24223							$this->StopTransform();
24224						}
24225					}
24226				}
24227				break;
24228			}
24229			// shapes
24230			case 'rect': {
24231				if ($invisible) {
24232					break;
24233				}
24234				$x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24235				$y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24236				$w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24237				$h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24238				$rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
24239				$ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
24240				if ($clipping) {
24241					$this->SVGTransform($tm);
24242					$this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
24243				} else {
24244					$this->StartTransform();
24245					$this->SVGTransform($tm);
24246					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
24247					if (!empty($obstyle)) {
24248						$this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
24249					}
24250					$this->StopTransform();
24251				}
24252				break;
24253			}
24254			case 'circle': {
24255				if ($invisible) {
24256					break;
24257				}
24258				$r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0);
24259				$cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
24260				$cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
24261				$x = ($cx - $r);
24262				$y = ($cy - $r);
24263				$w = (2 * $r);
24264				$h = $w;
24265				if ($clipping) {
24266					$this->SVGTransform($tm);
24267					$this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
24268				} else {
24269					$this->StartTransform();
24270					$this->SVGTransform($tm);
24271					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
24272					if (!empty($obstyle)) {
24273						$this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
24274					}
24275					$this->StopTransform();
24276				}
24277				break;
24278			}
24279			case 'ellipse': {
24280				if ($invisible) {
24281					break;
24282				}
24283				$rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0);
24284				$ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0);
24285				$cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
24286				$cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
24287				$x = ($cx - $rx);
24288				$y = ($cy - $ry);
24289				$w = (2 * $rx);
24290				$h = (2 * $ry);
24291				if ($clipping) {
24292					$this->SVGTransform($tm);
24293					$this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
24294				} else {
24295					$this->StartTransform();
24296					$this->SVGTransform($tm);
24297					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
24298					if (!empty($obstyle)) {
24299						$this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
24300					}
24301					$this->StopTransform();
24302				}
24303				break;
24304			}
24305			case 'line': {
24306				if ($invisible) {
24307					break;
24308				}
24309				$x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
24310				$y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
24311				$x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
24312				$y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
24313				$x = $x1;
24314				$y = $y1;
24315				$w = abs($x2 - $x1);
24316				$h = abs($y2 - $y1);
24317				if (!$clipping) {
24318					$this->StartTransform();
24319					$this->SVGTransform($tm);
24320					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
24321					$this->Line($x1, $y1, $x2, $y2);
24322					$this->StopTransform();
24323				}
24324				break;
24325			}
24326			case 'polyline':
24327			case 'polygon': {
24328				if ($invisible) {
24329					break;
24330				}
24331				$points = (isset($attribs['points'])?$attribs['points']:'0 0');
24332				$points = trim($points);
24333				// note that point may use a complex syntax not covered here
24334				$points = preg_split('/[\,\s]+/si', $points);
24335				if (count($points) < 4) {
24336					break;
24337				}
24338				$p = array();
24339				$xmin = 2147483647;
24340				$xmax = 0;
24341				$ymin = 2147483647;
24342				$ymax = 0;
24343				foreach ($points as $key => $val) {
24344					$p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
24345					if (($key % 2) == 0) {
24346						// X coordinate
24347						$xmin = min($xmin, $p[$key]);
24348						$xmax = max($xmax, $p[$key]);
24349					} else {
24350						// Y coordinate
24351						$ymin = min($ymin, $p[$key]);
24352						$ymax = max($ymax, $p[$key]);
24353					}
24354				}
24355				$x = $xmin;
24356				$y = $ymin;
24357				$w = ($xmax - $xmin);
24358				$h = ($ymax - $ymin);
24359				if ($name == 'polyline') {
24360					$this->StartTransform();
24361					$this->SVGTransform($tm);
24362					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
24363					if (!empty($obstyle)) {
24364						$this->PolyLine($p, $obstyle, array(), array());
24365					}
24366					$this->StopTransform();
24367				} else { // polygon
24368					if ($clipping) {
24369						$this->SVGTransform($tm);
24370						$this->Polygon($p, 'CNZ', array(), array(), true);
24371					} else {
24372						$this->StartTransform();
24373						$this->SVGTransform($tm);
24374						$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
24375						if (!empty($obstyle)) {
24376							$this->Polygon($p, $obstyle, array(), array(), true);
24377						}
24378						$this->StopTransform();
24379					}
24380				}
24381				break;
24382			}
24383			// image
24384			case 'image': {
24385				if ($invisible) {
24386					break;
24387				}
24388				if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
24389					break;
24390				}
24391				$x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24392				$y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24393				$w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24394				$h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24395				$img = $attribs['xlink:href'];
24396				if (!$clipping) {
24397					$this->StartTransform();
24398					$this->SVGTransform($tm);
24399					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
24400					if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
24401						// embedded image encoded as base64
24402						$img = '@'.base64_decode(substr($img, strlen($m[0])));
24403					} else {
24404						// fix image path
24405						if (!TCPDF_STATIC::empty_string($this->svgdir) AND (($img[0] == '.') OR (basename($img) == $img))) {
24406							// replace relative path with full server path
24407							$img = $this->svgdir.'/'.$img;
24408						}
24409						if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
24410							$findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
24411							if (($findroot === false) OR ($findroot > 1)) {
24412								if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
24413									$img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
24414								} else {
24415									$img = $_SERVER['DOCUMENT_ROOT'].$img;
24416								}
24417							}
24418						}
24419						$img = urldecode($img);
24420						$testscrtype = @parse_url($img);
24421						if (empty($testscrtype['query'])) {
24422							// convert URL to server path
24423							$img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
24424						} elseif (preg_match('|^https?://|', $img) !== 1) {
24425							// convert server path to URL
24426							$img = str_replace(K_PATH_MAIN, K_PATH_URL, $img);
24427						}
24428					}
24429					// get image type
24430					$imgtype = TCPDF_IMAGES::getImageFileType($img);
24431					if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
24432						$this->ImageEps($img, $x, $y, $w, $h);
24433					} elseif ($imgtype == 'svg') {
24434						// store SVG vars
24435						$svggradients = $this->svggradients;
24436						$svggradientid = $this->svggradientid;
24437						$svgdefsmode = $this->svgdefsmode;
24438						$svgdefs = $this->svgdefs;
24439						$svgclipmode = $this->svgclipmode;
24440						$svgclippaths = $this->svgclippaths;
24441						$svgcliptm = $this->svgcliptm;
24442						$svgclipid = $this->svgclipid;
24443						$svgtext = $this->svgtext;
24444						$svgtextmode = $this->svgtextmode;
24445						$this->ImageSVG($img, $x, $y, $w, $h);
24446						// restore SVG vars
24447						$this->svggradients = $svggradients;
24448						$this->svggradientid = $svggradientid;
24449						$this->svgdefsmode = $svgdefsmode;
24450						$this->svgdefs = $svgdefs;
24451						$this->svgclipmode = $svgclipmode;
24452						$this->svgclippaths = $svgclippaths;
24453						$this->svgcliptm = $svgcliptm;
24454						$this->svgclipid = $svgclipid;
24455						$this->svgtext = $svgtext;
24456						$this->svgtextmode = $svgtextmode;
24457					} else {
24458						$this->Image($img, $x, $y, $w, $h);
24459					}
24460					$this->StopTransform();
24461				}
24462				break;
24463			}
24464			// text
24465			case 'text':
24466			case 'tspan': {
24467				if (isset($this->svgtextmode['text-anchor']) AND !empty($this->svgtext)) {
24468					// @TODO: unsupported feature
24469				}
24470				// only basic support - advanced features must be implemented
24471				$this->svgtextmode['invisible'] = $invisible;
24472				if ($invisible) {
24473					break;
24474				}
24475				array_push($this->svgstyles, $svgstyle);
24476				if (isset($attribs['x'])) {
24477					$x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false);
24478				} elseif ($name == 'tspan') {
24479					$x = $this->x;
24480				} else {
24481					$x = 0;
24482				}
24483				if (isset($attribs['dx'])) {
24484					$x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false);
24485				}
24486				if (isset($attribs['y'])) {
24487					$y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false);
24488				} elseif ($name == 'tspan') {
24489					$y = $this->y;
24490				} else {
24491					$y = 0;
24492				}
24493				if (isset($attribs['dy'])) {
24494					$y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false);
24495				}
24496				$svgstyle['text-color'] = $svgstyle['fill'];
24497				$this->svgtext = '';
24498				if (isset($svgstyle['text-anchor'])) {
24499					$this->svgtextmode['text-anchor'] = $svgstyle['text-anchor'];
24500				} else {
24501					$this->svgtextmode['text-anchor'] = 'start';
24502				}
24503				if (isset($svgstyle['direction'])) {
24504					if ($svgstyle['direction'] == 'rtl') {
24505						$this->svgtextmode['rtl'] = true;
24506					} else {
24507						$this->svgtextmode['rtl'] = false;
24508					}
24509				} else {
24510					$this->svgtextmode['rtl'] = false;
24511				}
24512				if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
24513					$this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false);
24514				} else {
24515					$this->svgtextmode['stroke'] = false;
24516				}
24517				$this->StartTransform();
24518				$this->SVGTransform($tm);
24519				$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
24520				$this->x = $x;
24521				$this->y = $y;
24522				break;
24523			}
24524			// use
24525			case 'use': {
24526				if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24527					$svgdefid = substr($attribs['xlink:href'], 1);
24528					if (isset($this->svgdefs[$svgdefid])) {
24529						$use = $this->svgdefs[$svgdefid];
24530						if (isset($attribs['xlink:href'])) {
24531							unset($attribs['xlink:href']);
24532						}
24533						if (isset($attribs['id'])) {
24534							unset($attribs['id']);
24535						}
24536						if (isset($use['attribs']['x']) AND isset($attribs['x'])) {
24537							$attribs['x'] += $use['attribs']['x'];
24538						}
24539						if (isset($use['attribs']['y']) AND isset($attribs['y'])) {
24540							$attribs['y'] += $use['attribs']['y'];
24541						}
24542						if (empty($attribs['style'])) {
24543							$attribs['style'] = '';
24544						}
24545						if (!empty($use['attribs']['style'])) {
24546							// merge styles
24547							$attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']);
24548						}
24549						$attribs = array_merge($use['attribs'], $attribs);
24550						$this->startSVGElementHandler($parser, $use['name'], $attribs);
24551						return;
24552					}
24553				}
24554				break;
24555			}
24556			default: {
24557				break;
24558			}
24559		} // end of switch
24560		// process child elements
24561		if (!empty($attribs['child_elements'])) {
24562			$child_elements = $attribs['child_elements'];
24563			unset($attribs['child_elements']);
24564			foreach($child_elements as $child_element) {
24565				if (empty($child_element['attribs']['closing_tag'])) {
24566					$this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']);
24567				} else {
24568					if (isset($child_element['attribs']['content'])) {
24569						$this->svgtext = $child_element['attribs']['content'];
24570					}
24571					$this->endSVGElementHandler('child-tag', $child_element['name']);
24572				}
24573			}
24574		}
24575	}
24576
24577	/**
24578	 * Sets the closing SVG element handler function for the XML parser.
24579	 * @param resource|string $parser The first parameter, parser, is a reference to the XML parser calling the handler.
24580	 * @param string $name The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters.
24581	 * @author Nicola Asuni
24582	 * @since 5.0.000 (2010-05-02)
24583	 * @protected
24584	 */
24585	protected function endSVGElementHandler($parser, $name) {
24586		$name = $this->removeTagNamespace($name);
24587		if ($this->svgdefsmode AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {;
24588			if (end($this->svgdefs) !== FALSE) {
24589				$last_svgdefs_id = key($this->svgdefs);
24590				if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
24591					foreach($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) {
24592						if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) {
24593							$this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24594							return;
24595						}
24596					}
24597					if ($this->svgdefs[$last_svgdefs_id]['name'] == $name) {
24598						$this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24599						return;
24600					}
24601				}
24602			}
24603			return;
24604		}
24605		switch($name) {
24606			case 'defs': {
24607				$this->svgdefsmode = false;
24608				break;
24609			}
24610			// clipPath
24611			case 'clipPath': {
24612				$this->svgclipmode = false;
24613				break;
24614			}
24615			case 'svg': {
24616				if (--$this->svg_tag_depth <= 0) {
24617					break;
24618				}
24619			}
24620			case 'g': {
24621				// ungroup: remove last style from array
24622				array_pop($this->svgstyles);
24623				$this->StopTransform();
24624				break;
24625			}
24626			case 'text':
24627			case 'tspan': {
24628				if ($this->svgtextmode['invisible']) {
24629					// This implementation must be fixed to following the rule:
24630					// If the 'visibility' property is set to hidden on a 'tspan', 'tref' or 'altGlyph' element, then the text is invisible but still takes up space in text layout calculations.
24631					break;
24632				}
24633				// print text
24634				$text = $this->svgtext;
24635				//$text = $this->stringTrim($text);
24636				$textlen = $this->GetStringWidth($text);
24637				if ($this->svgtextmode['text-anchor'] != 'start') {
24638					// check if string is RTL text
24639					if ($this->svgtextmode['text-anchor'] == 'end') {
24640						if ($this->svgtextmode['rtl']) {
24641							$this->x += $textlen;
24642						} else {
24643							$this->x -= $textlen;
24644						}
24645					} elseif ($this->svgtextmode['text-anchor'] == 'middle') {
24646						if ($this->svgtextmode['rtl']) {
24647							$this->x += ($textlen / 2);
24648						} else {
24649							$this->x -= ($textlen / 2);
24650						}
24651					}
24652				}
24653				$textrendermode = $this->textrendermode;
24654				$textstrokewidth = $this->textstrokewidth;
24655				$this->setTextRenderingMode($this->svgtextmode['stroke'], true, false);
24656				if ($name == 'text') {
24657					// store current coordinates
24658					$tmpx = $this->x;
24659					$tmpy = $this->y;
24660				}
24661				// print the text
24662				$this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
24663				if ($name == 'text') {
24664					// restore coordinates
24665					$this->x = $tmpx;
24666					$this->y = $tmpy;
24667				}
24668				// restore previous rendering mode
24669				$this->textrendermode = $textrendermode;
24670				$this->textstrokewidth = $textstrokewidth;
24671				$this->svgtext = '';
24672				$this->StopTransform();
24673				if (!$this->svgdefsmode) {
24674					array_pop($this->svgstyles);
24675				}
24676				break;
24677			}
24678			default: {
24679				break;
24680			}
24681		}
24682	}
24683
24684	/**
24685	 * Sets the character data handler function for the XML parser.
24686	 * @param resource $parser The first parameter, parser, is a reference to the XML parser calling the handler.
24687	 * @param string $data The second parameter, data, contains the character data as a string.
24688	 * @author Nicola Asuni
24689	 * @since 5.0.000 (2010-05-02)
24690	 * @protected
24691	 */
24692	protected function segSVGContentHandler($parser, $data) {
24693		$this->svgtext .= $data;
24694	}
24695
24696	// --- END SVG METHODS -----------------------------------------------------
24697
24698    /**
24699     * Keeps files in memory, so it doesn't need to downloaded everytime in a loop
24700     * @param string $file
24701     * @return string
24702     */
24703    protected function getCachedFileContents($file)
24704    {
24705        if (!isset($this->fileContentCache[$file])) {
24706            $this->fileContentCache[$file] = TCPDF_STATIC::fileGetContents($file);
24707        }
24708        return $this->fileContentCache[$file];
24709    }
24710
24711    /**
24712     * Avoid multiple calls to an external server to see if a file exists
24713     * @param string $file
24714     * @return bool
24715     */
24716    protected function fileExists($file)
24717    {
24718        if (isset($this->fileContentCache[$file]) || false !== $this->getImageBuffer($file)) {
24719            return true;
24720        }
24721
24722        return TCPDF_STATIC::file_exists($file);
24723    }
24724
24725} // END OF TCPDF CLASS
24726
24727//============================================================+
24728// END OF FILE
24729//============================================================+
24730