1<?php
2//============================================================+
3// File name   : tcpdf.php
4// Version     : 6.0.091
5// Begin       : 2002-08-03
6// Last Update : 2014-08-13
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-2014 Nicola Asuni - Tecnick.com LTD
11//
12// This file is part of TCPDF software library.
13//
14// TCPDF is free software: you can ioredistribute 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 ImagMagick (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 Extention, 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 ImagMagick (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 Extention, 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.0.091
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.0.091
132 * @author Nicola Asuni - info@tecnick.com
133 */
134class TCPDF {
135
136	// Protected properties
137
138	/**
139	 * Current page number.
140	 * @protected
141	 */
142	protected $page;
143
144	/**
145	 * Current object number.
146	 * @protected
147	 */
148	protected $n;
149
150	/**
151	 * Array of object offsets.
152	 * @protected
153	 */
154	protected $offsets = array();
155
156	/**
157	 * Array of object IDs for each page.
158	 * @protected
159	 */
160	protected $pageobjects = array();
161
162	/**
163	 * Buffer holding in-memory PDF.
164	 * @protected
165	 */
166	protected $buffer;
167
168	/**
169	 * Array containing pages.
170	 * @protected
171	 */
172	protected $pages = array();
173
174	/**
175	 * Current document state.
176	 * @protected
177	 */
178	protected $state;
179
180	/**
181	 * Compression flag.
182	 * @protected
183	 */
184	protected $compress;
185
186	/**
187	 * Current page orientation (P = Portrait, L = Landscape).
188	 * @protected
189	 */
190	protected $CurOrientation;
191
192	/**
193	 * Page dimensions.
194	 * @protected
195	 */
196	protected $pagedim = array();
197
198	/**
199	 * Scale factor (number of points in user unit).
200	 * @protected
201	 */
202	protected $k;
203
204	/**
205	 * Width of page format in points.
206	 * @protected
207	 */
208	protected $fwPt;
209
210	/**
211	 * Height of page format in points.
212	 * @protected
213	 */
214	protected $fhPt;
215
216	/**
217	 * Current width of page in points.
218	 * @protected
219	 */
220	protected $wPt;
221
222	/**
223	 * Current height of page in points.
224	 * @protected
225	 */
226	protected $hPt;
227
228	/**
229	 * Current width of page in user unit.
230	 * @protected
231	 */
232	protected $w;
233
234	/**
235	 * Current height of page in user unit.
236	 * @protected
237	 */
238	protected $h;
239
240	/**
241	 * Left margin.
242	 * @protected
243	 */
244	protected $lMargin;
245
246	/**
247	 * Right margin.
248	 * @protected
249	 */
250	protected $rMargin;
251
252	/**
253	 * Cell left margin (used by regions).
254	 * @protected
255	 */
256	protected $clMargin;
257
258	/**
259	 * Cell right margin (used by regions).
260	 * @protected
261	 */
262	protected $crMargin;
263
264	/**
265	 * Top margin.
266	 * @protected
267	 */
268	protected $tMargin;
269
270	/**
271	 * Page break margin.
272	 * @protected
273	 */
274	protected $bMargin;
275
276	/**
277	 * Array of cell internal paddings ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
278	 * @since 5.9.000 (2010-10-03)
279	 * @protected
280	 */
281	protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
282
283	/**
284	 * Array of cell margins ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
285	 * @since 5.9.000 (2010-10-04)
286	 * @protected
287	 */
288	protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
289
290	/**
291	 * Current horizontal position in user unit for cell positioning.
292	 * @protected
293	 */
294	protected $x;
295
296	/**
297	 * Current vertical position in user unit for cell positioning.
298	 * @protected
299	 */
300	protected $y;
301
302	/**
303	 * Height of last cell printed.
304	 * @protected
305	 */
306	protected $lasth;
307
308	/**
309	 * Line width in user unit.
310	 * @protected
311	 */
312	protected $LineWidth;
313
314	/**
315	 * Array of standard font names.
316	 * @protected
317	 */
318	protected $CoreFonts;
319
320	/**
321	 * Array of used fonts.
322	 * @protected
323	 */
324	protected $fonts = array();
325
326	/**
327	 * Array of font files.
328	 * @protected
329	 */
330	protected $FontFiles = array();
331
332	/**
333	 * Array of encoding differences.
334	 * @protected
335	 */
336	protected $diffs = array();
337
338	/**
339	 * Array of used images.
340	 * @protected
341	 */
342	protected $images = array();
343
344	/**
345	 * Array of cached files.
346	 * @protected
347	 */
348	protected $cached_files = array();
349
350	/**
351	 * Array of Annotations in pages.
352	 * @protected
353	 */
354	protected $PageAnnots = array();
355
356	/**
357	 * Array of internal links.
358	 * @protected
359	 */
360	protected $links = array();
361
362	/**
363	 * Current font family.
364	 * @protected
365	 */
366	protected $FontFamily;
367
368	/**
369	 * Current font style.
370	 * @protected
371	 */
372	protected $FontStyle;
373
374	/**
375	 * Current font ascent (distance between font top and baseline).
376	 * @protected
377	 * @since 2.8.000 (2007-03-29)
378	 */
379	protected $FontAscent;
380
381	/**
382	 * Current font descent (distance between font bottom and baseline).
383	 * @protected
384	 * @since 2.8.000 (2007-03-29)
385	 */
386	protected $FontDescent;
387
388	/**
389	 * Underlining flag.
390	 * @protected
391	 */
392	protected $underline;
393
394	/**
395	 * Overlining flag.
396	 * @protected
397	 */
398	protected $overline;
399
400	/**
401	 * Current font info.
402	 * @protected
403	 */
404	protected $CurrentFont;
405
406	/**
407	 * Current font size in points.
408	 * @protected
409	 */
410	protected $FontSizePt;
411
412	/**
413	 * Current font size in user unit.
414	 * @protected
415	 */
416	protected $FontSize;
417
418	/**
419	 * Commands for drawing color.
420	 * @protected
421	 */
422	protected $DrawColor;
423
424	/**
425	 * Commands for filling color.
426	 * @protected
427	 */
428	protected $FillColor;
429
430	/**
431	 * Commands for text color.
432	 * @protected
433	 */
434	protected $TextColor;
435
436	/**
437	 * Indicates whether fill and text colors are different.
438	 * @protected
439	 */
440	protected $ColorFlag;
441
442	/**
443	 * Automatic page breaking.
444	 * @protected
445	 */
446	protected $AutoPageBreak;
447
448	/**
449	 * Threshold used to trigger page breaks.
450	 * @protected
451	 */
452	protected $PageBreakTrigger;
453
454	/**
455	 * Flag set when processing page header.
456	 * @protected
457	 */
458	protected $InHeader = false;
459
460	/**
461	 * Flag set when processing page footer.
462	 * @protected
463	 */
464	protected $InFooter = false;
465
466	/**
467	 * Zoom display mode.
468	 * @protected
469	 */
470	protected $ZoomMode;
471
472	/**
473	 * Layout display mode.
474	 * @protected
475	 */
476	protected $LayoutMode;
477
478	/**
479	 * If true set the document information dictionary in Unicode.
480	 * @protected
481	 */
482	protected $docinfounicode = true;
483
484	/**
485	 * Document title.
486	 * @protected
487	 */
488	protected $title = '';
489
490	/**
491	 * Document subject.
492	 * @protected
493	 */
494	protected $subject = '';
495
496	/**
497	 * Document author.
498	 * @protected
499	 */
500	protected $author = '';
501
502	/**
503	 * Document keywords.
504	 * @protected
505	 */
506	protected $keywords = '';
507
508	/**
509	 * Document creator.
510	 * @protected
511	 */
512	protected $creator = '';
513
514	/**
515	 * Starting page number.
516	 * @protected
517	 */
518	protected $starting_page_number = 1;
519
520	/**
521	 * The right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image.
522	 * @since 2002-07-31
523	 * @author Nicola Asuni
524	 * @protected
525	 */
526	protected $img_rb_x;
527
528	/**
529	 * The right-bottom corner Y coordinate of last inserted image.
530	 * @since 2002-07-31
531	 * @author Nicola Asuni
532	 * @protected
533	 */
534	protected $img_rb_y;
535
536	/**
537	 * Adjusting factor to convert pixels to user units.
538	 * @since 2004-06-14
539	 * @author Nicola Asuni
540	 * @protected
541	 */
542	protected $imgscale = 1;
543
544	/**
545	 * Boolean flag set to true when the input text is unicode (require unicode fonts).
546	 * @since 2005-01-02
547	 * @author Nicola Asuni
548	 * @protected
549	 */
550	protected $isunicode = false;
551
552	/**
553	 * PDF version.
554	 * @since 1.5.3
555	 * @protected
556	 */
557	protected $PDFVersion = '1.7';
558
559	/**
560	 * ID of the stored default header template (-1 = not set).
561	 * @protected
562	 */
563	protected $header_xobjid = false;
564
565	/**
566	 * If true reset the Header Xobject template at each page
567	 * @protected
568	 */
569	protected $header_xobj_autoreset = false;
570
571	/**
572	 * Minimum distance between header and top page margin.
573	 * @protected
574	 */
575	protected $header_margin;
576
577	/**
578	 * Minimum distance between footer and bottom page margin.
579	 * @protected
580	 */
581	protected $footer_margin;
582
583	/**
584	 * Original left margin value.
585	 * @protected
586	 * @since 1.53.0.TC013
587	 */
588	protected $original_lMargin;
589
590	/**
591	 * Original right margin value.
592	 * @protected
593	 * @since 1.53.0.TC013
594	 */
595	protected $original_rMargin;
596
597	/**
598	 * Default font used on page header.
599	 * @protected
600	 */
601	protected $header_font;
602
603	/**
604	 * Default font used on page footer.
605	 * @protected
606	 */
607	protected $footer_font;
608
609	/**
610	 * Language templates.
611	 * @protected
612	 */
613	protected $l;
614
615	/**
616	 * Barcode to print on page footer (only if set).
617	 * @protected
618	 */
619	protected $barcode = false;
620
621	/**
622	 * Boolean flag to print/hide page header.
623	 * @protected
624	 */
625	protected $print_header = true;
626
627	/**
628	 * Boolean flag to print/hide page footer.
629	 * @protected
630	 */
631	protected $print_footer = true;
632
633	/**
634	 * Header image logo.
635	 * @protected
636	 */
637	protected $header_logo = '';
638
639	/**
640	 * Width of header image logo in user units.
641	 * @protected
642	 */
643	protected $header_logo_width = 30;
644
645	/**
646	 * Title to be printed on default page header.
647	 * @protected
648	 */
649	protected $header_title = '';
650
651	/**
652	 * String to pring on page header after title.
653	 * @protected
654	 */
655	protected $header_string = '';
656
657	/**
658	 * Color for header text (RGB array).
659	 * @since 5.9.174 (2012-07-25)
660	 * @protected
661	 */
662	protected $header_text_color = array(0,0,0);
663
664	/**
665	 * Color for header line (RGB array).
666	 * @since 5.9.174 (2012-07-25)
667	 * @protected
668	 */
669	protected $header_line_color = array(0,0,0);
670
671	/**
672	 * Color for footer text (RGB array).
673	 * @since 5.9.174 (2012-07-25)
674	 * @protected
675	 */
676	protected $footer_text_color = array(0,0,0);
677
678	/**
679	 * Color for footer line (RGB array).
680	 * @since 5.9.174 (2012-07-25)
681	 * @protected
682	 */
683	protected $footer_line_color = array(0,0,0);
684
685	/**
686	 * Text shadow data array.
687	 * @since 5.9.174 (2012-07-25)
688	 * @protected
689	 */
690	protected $txtshadow = array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal');
691
692	/**
693	 * Default number of columns for html table.
694	 * @protected
695	 */
696	protected $default_table_columns = 4;
697
698	// variables for html parser
699
700	/**
701	 * HTML PARSER: array to store current link and rendering styles.
702	 * @protected
703	 */
704	protected $HREF = array();
705
706	/**
707	 * List of available fonts on filesystem.
708	 * @protected
709	 */
710	protected $fontlist = array();
711
712	/**
713	 * Current foreground color.
714	 * @protected
715	 */
716	protected $fgcolor;
717
718	/**
719	 * HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
720	 * @protected
721	 */
722	protected $listordered = array();
723
724	/**
725	 * HTML PARSER: array count list items on nested lists.
726	 * @protected
727	 */
728	protected $listcount = array();
729
730	/**
731	 * HTML PARSER: current list nesting level.
732	 * @protected
733	 */
734	protected $listnum = 0;
735
736	/**
737	 * HTML PARSER: indent amount for lists.
738	 * @protected
739	 */
740	protected $listindent = 0;
741
742	/**
743	 * HTML PARSER: current list indententation level.
744	 * @protected
745	 */
746	protected $listindentlevel = 0;
747
748	/**
749	 * Current background color.
750	 * @protected
751	 */
752	protected $bgcolor;
753
754	/**
755	 * Temporary font size in points.
756	 * @protected
757	 */
758	protected $tempfontsize = 10;
759
760	/**
761	 * Spacer string for LI tags.
762	 * @protected
763	 */
764	protected $lispacer = '';
765
766	/**
767	 * Default encoding.
768	 * @protected
769	 * @since 1.53.0.TC010
770	 */
771	protected $encoding = 'UTF-8';
772
773	/**
774	 * PHP internal encoding.
775	 * @protected
776	 * @since 1.53.0.TC016
777	 */
778	protected $internal_encoding;
779
780	/**
781	 * Boolean flag to indicate if the document language is Right-To-Left.
782	 * @protected
783	 * @since 2.0.000
784	 */
785	protected $rtl = false;
786
787	/**
788	 * Boolean flag used to force RTL or LTR string direction.
789	 * @protected
790	 * @since 2.0.000
791	 */
792	protected $tmprtl = false;
793
794	// --- Variables used for document encryption:
795
796	/**
797	 * IBoolean flag indicating whether document is protected.
798	 * @protected
799	 * @since 2.0.000 (2008-01-02)
800	 */
801	protected $encrypted;
802
803	/**
804	 * Array containing encryption settings.
805	 * @protected
806	 * @since 5.0.005 (2010-05-11)
807	 */
808	protected $encryptdata = array();
809
810	/**
811	 * Last RC4 key encrypted (cached for optimisation).
812	 * @protected
813	 * @since 2.0.000 (2008-01-02)
814	 */
815	protected $last_enc_key;
816
817	/**
818	 * Last RC4 computed key.
819	 * @protected
820	 * @since 2.0.000 (2008-01-02)
821	 */
822	protected $last_enc_key_c;
823
824	/**
825	 * File ID (used on document trailer).
826	 * @protected
827	 * @since 5.0.005 (2010-05-12)
828	 */
829	protected $file_id;
830
831	// --- bookmark ---
832
833	/**
834	 * Outlines for bookmark.
835	 * @protected
836	 * @since 2.1.002 (2008-02-12)
837	 */
838	protected $outlines = array();
839
840	/**
841	 * Outline root for bookmark.
842	 * @protected
843	 * @since 2.1.002 (2008-02-12)
844	 */
845	protected $OutlineRoot;
846
847	// --- javascript and form ---
848
849	/**
850	 * Javascript code.
851	 * @protected
852	 * @since 2.1.002 (2008-02-12)
853	 */
854	protected $javascript = '';
855
856	/**
857	 * Javascript counter.
858	 * @protected
859	 * @since 2.1.002 (2008-02-12)
860	 */
861	protected $n_js;
862
863	/**
864	 * line through state
865	 * @protected
866	 * @since 2.8.000 (2008-03-19)
867	 */
868	protected $linethrough;
869
870	/**
871	 * Array with additional document-wide usage rights for the document.
872	 * @protected
873	 * @since 5.8.014 (2010-08-23)
874	 */
875	protected $ur = array();
876
877	/**
878	 * DPI (Dot Per Inch) Document Resolution (do not change).
879	 * @protected
880	 * @since 3.0.000 (2008-03-27)
881	 */
882	protected $dpi = 72;
883
884	/**
885	 * Array of page numbers were a new page group was started (the page numbers are the keys of the array).
886	 * @protected
887	 * @since 3.0.000 (2008-03-27)
888	 */
889	protected $newpagegroup = array();
890
891	/**
892	 * Array that contains the number of pages in each page group.
893	 * @protected
894	 * @since 3.0.000 (2008-03-27)
895	 */
896	protected $pagegroups = array();
897
898	/**
899	 * Current page group number.
900	 * @protected
901	 * @since 3.0.000 (2008-03-27)
902	 */
903	protected $currpagegroup = 0;
904
905	/**
906	 * Array of transparency objects and parameters.
907	 * @protected
908	 * @since 3.0.000 (2008-03-27)
909	 */
910	protected $extgstates;
911
912	/**
913	 * Set the default JPEG compression quality (1-100).
914	 * @protected
915	 * @since 3.0.000 (2008-03-27)
916	 */
917	protected $jpeg_quality;
918
919	/**
920	 * Default cell height ratio.
921	 * @protected
922	 * @since 3.0.014 (2008-05-23)
923	 */
924	protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
925
926	/**
927	 * PDF viewer preferences.
928	 * @protected
929	 * @since 3.1.000 (2008-06-09)
930	 */
931	protected $viewer_preferences;
932
933	/**
934	 * A name object specifying how the document should be displayed when opened.
935	 * @protected
936	 * @since 3.1.000 (2008-06-09)
937	 */
938	protected $PageMode;
939
940	/**
941	 * Array for storing gradient information.
942	 * @protected
943	 * @since 3.1.000 (2008-06-09)
944	 */
945	protected $gradients = array();
946
947	/**
948	 * Array used to store positions inside the pages buffer (keys are the page numbers).
949	 * @protected
950	 * @since 3.2.000 (2008-06-26)
951	 */
952	protected $intmrk = array();
953
954	/**
955	 * Array used to store positions inside the pages buffer (keys are the page numbers).
956	 * @protected
957	 * @since 5.7.000 (2010-08-03)
958	 */
959	protected $bordermrk = array();
960
961	/**
962	 * Array used to store page positions to track empty pages (keys are the page numbers).
963	 * @protected
964	 * @since 5.8.007 (2010-08-18)
965	 */
966	protected $emptypagemrk = array();
967
968	/**
969	 * Array used to store content positions inside the pages buffer (keys are the page numbers).
970	 * @protected
971	 * @since 4.6.021 (2009-07-20)
972	 */
973	protected $cntmrk = array();
974
975	/**
976	 * Array used to store footer positions of each page.
977	 * @protected
978	 * @since 3.2.000 (2008-07-01)
979	 */
980	protected $footerpos = array();
981
982	/**
983	 * Array used to store footer length of each page.
984	 * @protected
985	 * @since 4.0.014 (2008-07-29)
986	 */
987	protected $footerlen = array();
988
989	/**
990	 * Boolean flag to indicate if a new line is created.
991	 * @protected
992	 * @since 3.2.000 (2008-07-01)
993	 */
994	protected $newline = true;
995
996	/**
997	 * End position of the latest inserted line.
998	 * @protected
999	 * @since 3.2.000 (2008-07-01)
1000	 */
1001	protected $endlinex = 0;
1002
1003	/**
1004	 * PDF string for width value of the last line.
1005	 * @protected
1006	 * @since 4.0.006 (2008-07-16)
1007	 */
1008	protected $linestyleWidth = '';
1009
1010	/**
1011	 * PDF string for CAP value of the last line.
1012	 * @protected
1013	 * @since 4.0.006 (2008-07-16)
1014	 */
1015	protected $linestyleCap = '0 J';
1016
1017	/**
1018	 * PDF string for join value of the last line.
1019	 * @protected
1020	 * @since 4.0.006 (2008-07-16)
1021	 */
1022	protected $linestyleJoin = '0 j';
1023
1024	/**
1025	 * PDF string for dash value of the last line.
1026	 * @protected
1027	 * @since 4.0.006 (2008-07-16)
1028	 */
1029	protected $linestyleDash = '[] 0 d';
1030
1031	/**
1032	 * Boolean flag to indicate if marked-content sequence is open.
1033	 * @protected
1034	 * @since 4.0.013 (2008-07-28)
1035	 */
1036	protected $openMarkedContent = false;
1037
1038	/**
1039	 * Count the latest inserted vertical spaces on HTML.
1040	 * @protected
1041	 * @since 4.0.021 (2008-08-24)
1042	 */
1043	protected $htmlvspace = 0;
1044
1045	/**
1046	 * Array of Spot colors.
1047	 * @protected
1048	 * @since 4.0.024 (2008-09-12)
1049	 */
1050	protected $spot_colors = array();
1051
1052	/**
1053	 * Symbol used for HTML unordered list items.
1054	 * @protected
1055	 * @since 4.0.028 (2008-09-26)
1056	 */
1057	protected $lisymbol = '';
1058
1059	/**
1060	 * String used to mark the beginning and end of EPS image blocks.
1061	 * @protected
1062	 * @since 4.1.000 (2008-10-18)
1063	 */
1064	protected $epsmarker = 'x#!#EPS#!#x';
1065
1066	/**
1067	 * Array of transformation matrix.
1068	 * @protected
1069	 * @since 4.2.000 (2008-10-29)
1070	 */
1071	protected $transfmatrix = array();
1072
1073	/**
1074	 * Current key for transformation matrix.
1075	 * @protected
1076	 * @since 4.8.005 (2009-09-17)
1077	 */
1078	protected $transfmatrix_key = 0;
1079
1080	/**
1081	 * Booklet mode for double-sided pages.
1082	 * @protected
1083	 * @since 4.2.000 (2008-10-29)
1084	 */
1085	protected $booklet = false;
1086
1087	/**
1088	 * Epsilon value used for float calculations.
1089	 * @protected
1090	 * @since 4.2.000 (2008-10-29)
1091	 */
1092	protected $feps = 0.005;
1093
1094	/**
1095	 * Array used for custom vertical spaces for HTML tags.
1096	 * @protected
1097	 * @since 4.2.001 (2008-10-30)
1098	 */
1099	protected $tagvspaces = array();
1100
1101	/**
1102	 * HTML PARSER: custom indent amount for lists. Negative value means disabled.
1103	 * @protected
1104	 * @since 4.2.007 (2008-11-12)
1105	 */
1106	protected $customlistindent = -1;
1107
1108	/**
1109	 * Boolean flag to indicate if the border of the cell sides that cross the page should be removed.
1110	 * @protected
1111	 * @since 4.2.010 (2008-11-14)
1112	 */
1113	protected $opencell = true;
1114
1115	/**
1116	 * Array of files to embedd.
1117	 * @protected
1118	 * @since 4.4.000 (2008-12-07)
1119	 */
1120	protected $embeddedfiles = array();
1121
1122	/**
1123	 * Boolean flag to indicate if we are inside a PRE tag.
1124	 * @protected
1125	 * @since 4.4.001 (2008-12-08)
1126	 */
1127	protected $premode = false;
1128
1129	/**
1130	 * Array used to store positions of graphics transformation blocks inside the page buffer.
1131	 * keys are the page numbers
1132	 * @protected
1133	 * @since 4.4.002 (2008-12-09)
1134	 */
1135	protected $transfmrk = array();
1136
1137	/**
1138	 * Default color for html links.
1139	 * @protected
1140	 * @since 4.4.003 (2008-12-09)
1141	 */
1142	protected $htmlLinkColorArray = array(0, 0, 255);
1143
1144	/**
1145	 * Default font style to add to html links.
1146	 * @protected
1147	 * @since 4.4.003 (2008-12-09)
1148	 */
1149	protected $htmlLinkFontStyle = 'U';
1150
1151	/**
1152	 * Counts the number of pages.
1153	 * @protected
1154	 * @since 4.5.000 (2008-12-31)
1155	 */
1156	protected $numpages = 0;
1157
1158	/**
1159	 * Array containing page lengths in bytes.
1160	 * @protected
1161	 * @since 4.5.000 (2008-12-31)
1162	 */
1163	protected $pagelen = array();
1164
1165	/**
1166	 * Counts the number of pages.
1167	 * @protected
1168	 * @since 4.5.000 (2008-12-31)
1169	 */
1170	protected $numimages = 0;
1171
1172	/**
1173	 * Store the image keys.
1174	 * @protected
1175	 * @since 4.5.000 (2008-12-31)
1176	 */
1177	protected $imagekeys = array();
1178
1179	/**
1180	 * Length of the buffer in bytes.
1181	 * @protected
1182	 * @since 4.5.000 (2008-12-31)
1183	 */
1184	protected $bufferlen = 0;
1185
1186	/**
1187	 * If true enables disk caching.
1188	 * @protected
1189	 * @since 4.5.000 (2008-12-31)
1190	 */
1191	protected $diskcache = false;
1192
1193	/**
1194	 * Counts the number of fonts.
1195	 * @protected
1196	 * @since 4.5.000 (2009-01-02)
1197	 */
1198	protected $numfonts = 0;
1199
1200	/**
1201	 * Store the font keys.
1202	 * @protected
1203	 * @since 4.5.000 (2009-01-02)
1204	 */
1205	protected $fontkeys = array();
1206
1207	/**
1208	 * Store the font object IDs.
1209	 * @protected
1210	 * @since 4.8.001 (2009-09-09)
1211	 */
1212	protected $font_obj_ids = array();
1213
1214	/**
1215	 * Store the fage status (true when opened, false when closed).
1216	 * @protected
1217	 * @since 4.5.000 (2009-01-02)
1218	 */
1219	protected $pageopen = array();
1220
1221	/**
1222	 * Default monospace font.
1223	 * @protected
1224	 * @since 4.5.025 (2009-03-10)
1225	 */
1226	protected $default_monospaced_font = 'courier';
1227
1228	/**
1229	 * Cloned copy of the current class object.
1230	 * @protected
1231	 * @since 4.5.029 (2009-03-19)
1232	 */
1233	protected $objcopy;
1234
1235	/**
1236	 * Array used to store the lengths of cache files.
1237	 * @protected
1238	 * @since 4.5.029 (2009-03-19)
1239	 */
1240	protected $cache_file_length = array();
1241
1242	/**
1243	 * Table header content to be repeated on each new page.
1244	 * @protected
1245	 * @since 4.5.030 (2009-03-20)
1246	 */
1247	protected $thead = '';
1248
1249	/**
1250	 * Margins used for table header.
1251	 * @protected
1252	 * @since 4.5.030 (2009-03-20)
1253	 */
1254	protected $theadMargins = array();
1255
1256	/**
1257	 * Boolean flag to enable document digital signature.
1258	 * @protected
1259	 * @since 4.6.005 (2009-04-24)
1260	 */
1261	protected $sign = false;
1262
1263	/**
1264	 * Digital signature data.
1265	 * @protected
1266	 * @since 4.6.005 (2009-04-24)
1267	 */
1268	protected $signature_data = array();
1269
1270	/**
1271	 * Digital signature max length.
1272	 * @protected
1273	 * @since 4.6.005 (2009-04-24)
1274	 */
1275	protected $signature_max_length = 11742;
1276
1277	/**
1278	 * Data for digital signature appearance.
1279	 * @protected
1280	 * @since 5.3.011 (2010-06-16)
1281	 */
1282	protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
1283
1284	/**
1285	 * Array of empty digital signature appearances.
1286	 * @protected
1287	 * @since 5.9.101 (2011-07-06)
1288	 */
1289	protected $empty_signature_appearance = array();
1290
1291	/**
1292	 * Boolean flag to enable document timestamping with TSA.
1293	 * @protected
1294	 * @since 6.0.085 (2014-06-19)
1295	 */
1296	protected $tsa_timestamp = false;
1297
1298	/**
1299	 * Timestamping data.
1300	 * @protected
1301	 * @since 6.0.085 (2014-06-19)
1302	 */
1303	protected $tsa_data = array();
1304
1305	/**
1306	 * Regular expression used to find blank characters (required for word-wrapping).
1307	 * @protected
1308	 * @since 4.6.006 (2009-04-28)
1309	 */
1310	protected $re_spaces = '/[^\S\xa0]/';
1311
1312	/**
1313	 * Array of $re_spaces parts.
1314	 * @protected
1315	 * @since 5.5.011 (2010-07-09)
1316	 */
1317	protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
1318
1319	/**
1320	 * Digital signature object ID.
1321	 * @protected
1322	 * @since 4.6.022 (2009-06-23)
1323	 */
1324	protected $sig_obj_id = 0;
1325
1326	/**
1327	 * ID of page objects.
1328	 * @protected
1329	 * @since 4.7.000 (2009-08-29)
1330	 */
1331	protected $page_obj_id = array();
1332
1333	/**
1334	 * List of form annotations IDs.
1335	 * @protected
1336	 * @since 4.8.000 (2009-09-07)
1337	 */
1338	protected $form_obj_id = array();
1339
1340	/**
1341	 * 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.
1342	 * @protected
1343	 * @since 4.8.000 (2009-09-07)
1344	 */
1345	protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1346
1347	/**
1348	 * Javascript objects array.
1349	 * @protected
1350	 * @since 4.8.000 (2009-09-07)
1351	 */
1352	protected $js_objects = array();
1353
1354	/**
1355	 * Current form action (used during XHTML rendering).
1356	 * @protected
1357	 * @since 4.8.000 (2009-09-07)
1358	 */
1359	protected $form_action = '';
1360
1361	/**
1362	 * Current form encryption type (used during XHTML rendering).
1363	 * @protected
1364	 * @since 4.8.000 (2009-09-07)
1365	 */
1366	protected $form_enctype = 'application/x-www-form-urlencoded';
1367
1368	/**
1369	 * Current method to submit forms.
1370	 * @protected
1371	 * @since 4.8.000 (2009-09-07)
1372	 */
1373	protected $form_mode = 'post';
1374
1375	/**
1376	 * List of fonts used on form fields (fontname => fontkey).
1377	 * @protected
1378	 * @since 4.8.001 (2009-09-09)
1379	 */
1380	protected $annotation_fonts = array();
1381
1382	/**
1383	 * List of radio buttons parent objects.
1384	 * @protected
1385	 * @since 4.8.001 (2009-09-09)
1386	 */
1387	protected $radiobutton_groups = array();
1388
1389	/**
1390	 * List of radio group objects IDs.
1391	 * @protected
1392	 * @since 4.8.001 (2009-09-09)
1393	 */
1394	protected $radio_groups = array();
1395
1396	/**
1397	 * Text indentation value (used for text-indent CSS attribute).
1398	 * @protected
1399	 * @since 4.8.006 (2009-09-23)
1400	 */
1401	protected $textindent = 0;
1402
1403	/**
1404	 * Store page number when startTransaction() is called.
1405	 * @protected
1406	 * @since 4.8.006 (2009-09-23)
1407	 */
1408	protected $start_transaction_page = 0;
1409
1410	/**
1411	 * Store Y position when startTransaction() is called.
1412	 * @protected
1413	 * @since 4.9.001 (2010-03-28)
1414	 */
1415	protected $start_transaction_y = 0;
1416
1417	/**
1418	 * True when we are printing the thead section on a new page.
1419	 * @protected
1420	 * @since 4.8.027 (2010-01-25)
1421	 */
1422	protected $inthead = false;
1423
1424	/**
1425	 * Array of column measures (width, space, starting Y position).
1426	 * @protected
1427	 * @since 4.9.001 (2010-03-28)
1428	 */
1429	protected $columns = array();
1430
1431	/**
1432	 * Number of colums.
1433	 * @protected
1434	 * @since 4.9.001 (2010-03-28)
1435	 */
1436	protected $num_columns = 1;
1437
1438	/**
1439	 * Current column number.
1440	 * @protected
1441	 * @since 4.9.001 (2010-03-28)
1442	 */
1443	protected $current_column = 0;
1444
1445	/**
1446	 * Starting page for columns.
1447	 * @protected
1448	 * @since 4.9.001 (2010-03-28)
1449	 */
1450	protected $column_start_page = 0;
1451
1452	/**
1453	 * Maximum page and column selected.
1454	 * @protected
1455	 * @since 5.8.000 (2010-08-11)
1456	 */
1457	protected $maxselcol = array('page' => 0, 'column' => 0);
1458
1459	/**
1460	 * Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding.
1461	 * @protected
1462	 * @since 5.8.000 (2010-08-11)
1463	 */
1464	protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
1465
1466	/**
1467	 * 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.
1468	 * @protected
1469	 * @since 4.9.008 (2010-04-03)
1470	 */
1471	protected $textrendermode = 0;
1472
1473	/**
1474	 * Text stroke width in doc units.
1475	 * @protected
1476	 * @since 4.9.008 (2010-04-03)
1477	 */
1478	protected $textstrokewidth = 0;
1479
1480	/**
1481	 * Current stroke color.
1482	 * @protected
1483	 * @since 4.9.008 (2010-04-03)
1484	 */
1485	protected $strokecolor;
1486
1487	/**
1488	 * Default unit of measure for document.
1489	 * @protected
1490	 * @since 5.0.000 (2010-04-22)
1491	 */
1492	protected $pdfunit = 'mm';
1493
1494	/**
1495	 * Boolean flag true when we are on TOC (Table Of Content) page.
1496	 * @protected
1497	 */
1498	protected $tocpage = false;
1499
1500	/**
1501	 * Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library.
1502	 * @protected
1503	 * @since 5.0.000 (2010-04-26)
1504	 */
1505	protected $rasterize_vector_images = false;
1506
1507	/**
1508	 * Boolean flag: if true enables font subsetting by default.
1509	 * @protected
1510	 * @since 5.3.002 (2010-06-07)
1511	 */
1512	protected $font_subsetting = true;
1513
1514	/**
1515	 * Array of default graphic settings.
1516	 * @protected
1517	 * @since 5.5.008 (2010-07-02)
1518	 */
1519	protected $default_graphic_vars = array();
1520
1521	/**
1522	 * Array of XObjects.
1523	 * @protected
1524	 * @since 5.8.014 (2010-08-23)
1525	 */
1526	protected $xobjects = array();
1527
1528	/**
1529	 * Boolean value true when we are inside an XObject.
1530	 * @protected
1531	 * @since 5.8.017 (2010-08-24)
1532	 */
1533	protected $inxobj = false;
1534
1535	/**
1536	 * Current XObject ID.
1537	 * @protected
1538	 * @since 5.8.017 (2010-08-24)
1539	 */
1540	protected $xobjid = '';
1541
1542	/**
1543	 * Percentage of character stretching.
1544	 * @protected
1545	 * @since 5.9.000 (2010-09-29)
1546	 */
1547	protected $font_stretching = 100;
1548
1549	/**
1550	 * Increases or decreases the space between characters in a text by the specified amount (tracking).
1551	 * @protected
1552	 * @since 5.9.000 (2010-09-29)
1553	 */
1554	protected $font_spacing = 0;
1555
1556	/**
1557	 * Array of no-write regions.
1558	 * ('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)
1559	 * @protected
1560	 * @since 5.9.003 (2010-10-14)
1561	 */
1562	protected $page_regions = array();
1563
1564	/**
1565	 * Boolean value true when page region check is active.
1566	 * @protected
1567	 */
1568	protected $check_page_regions = true;
1569
1570	/**
1571	 * Array of PDF layers data.
1572	 * @protected
1573	 * @since 5.9.102 (2011-07-13)
1574	 */
1575	protected $pdflayers = array();
1576
1577	/**
1578	 * A dictionary of names and corresponding destinations (Dests key on document Catalog).
1579	 * @protected
1580	 * @since 5.9.097 (2011-06-23)
1581	 */
1582	protected $dests = array();
1583
1584	/**
1585	 * Object ID for Named Destinations
1586	 * @protected
1587	 * @since 5.9.097 (2011-06-23)
1588	 */
1589	protected $n_dests;
1590
1591	/**
1592	 * Embedded Files Names
1593	 * @protected
1594	 * @since 5.9.204 (2013-01-23)
1595	 */
1596	protected $efnames = array();
1597
1598	/**
1599	 * Directory used for the last SVG image.
1600	 * @protected
1601	 * @since 5.0.000 (2010-05-05)
1602	 */
1603	protected $svgdir = '';
1604
1605	/**
1606	 *  Deafult unit of measure for SVG.
1607	 * @protected
1608	 * @since 5.0.000 (2010-05-02)
1609	 */
1610	protected $svgunit = 'px';
1611
1612	/**
1613	 * Array of SVG gradients.
1614	 * @protected
1615	 * @since 5.0.000 (2010-05-02)
1616	 */
1617	protected $svggradients = array();
1618
1619	/**
1620	 * ID of last SVG gradient.
1621	 * @protected
1622	 * @since 5.0.000 (2010-05-02)
1623	 */
1624	protected $svggradientid = 0;
1625
1626	/**
1627	 * Boolean value true when in SVG defs group.
1628	 * @protected
1629	 * @since 5.0.000 (2010-05-02)
1630	 */
1631	protected $svgdefsmode = false;
1632
1633	/**
1634	 * Array of SVG defs.
1635	 * @protected
1636	 * @since 5.0.000 (2010-05-02)
1637	 */
1638	protected $svgdefs = array();
1639
1640	/**
1641	 * Boolean value true when in SVG clipPath tag.
1642	 * @protected
1643	 * @since 5.0.000 (2010-04-26)
1644	 */
1645	protected $svgclipmode = false;
1646
1647	/**
1648	 * Array of SVG clipPath commands.
1649	 * @protected
1650	 * @since 5.0.000 (2010-05-02)
1651	 */
1652	protected $svgclippaths = array();
1653
1654	/**
1655	 * Array of SVG clipPath tranformation matrix.
1656	 * @protected
1657	 * @since 5.8.022 (2010-08-31)
1658	 */
1659	protected $svgcliptm = array();
1660
1661	/**
1662	 * ID of last SVG clipPath.
1663	 * @protected
1664	 * @since 5.0.000 (2010-05-02)
1665	 */
1666	protected $svgclipid = 0;
1667
1668	/**
1669	 * SVG text.
1670	 * @protected
1671	 * @since 5.0.000 (2010-05-02)
1672	 */
1673	protected $svgtext = '';
1674
1675	/**
1676	 * SVG text properties.
1677	 * @protected
1678	 * @since 5.8.013 (2010-08-23)
1679	 */
1680	protected $svgtextmode = array();
1681
1682	/**
1683	 * Array of SVG properties.
1684	 * @protected
1685	 * @since 5.0.000 (2010-05-02)
1686	 */
1687	protected $svgstyles = array(array(
1688		'alignment-baseline' => 'auto',
1689		'baseline-shift' => 'baseline',
1690		'clip' => 'auto',
1691		'clip-path' => 'none',
1692		'clip-rule' => 'nonzero',
1693		'color' => 'black',
1694		'color-interpolation' => 'sRGB',
1695		'color-interpolation-filters' => 'linearRGB',
1696		'color-profile' => 'auto',
1697		'color-rendering' => 'auto',
1698		'cursor' => 'auto',
1699		'direction' => 'ltr',
1700		'display' => 'inline',
1701		'dominant-baseline' => 'auto',
1702		'enable-background' => 'accumulate',
1703		'fill' => 'black',
1704		'fill-opacity' => 1,
1705		'fill-rule' => 'nonzero',
1706		'filter' => 'none',
1707		'flood-color' => 'black',
1708		'flood-opacity' => 1,
1709		'font' => '',
1710		'font-family' => 'helvetica',
1711		'font-size' => 'medium',
1712		'font-size-adjust' => 'none',
1713		'font-stretch' => 'normal',
1714		'font-style' => 'normal',
1715		'font-variant' => 'normal',
1716		'font-weight' => 'normal',
1717		'glyph-orientation-horizontal' => '0deg',
1718		'glyph-orientation-vertical' => 'auto',
1719		'image-rendering' => 'auto',
1720		'kerning' => 'auto',
1721		'letter-spacing' => 'normal',
1722		'lighting-color' => 'white',
1723		'marker' => '',
1724		'marker-end' => 'none',
1725		'marker-mid' => 'none',
1726		'marker-start' => 'none',
1727		'mask' => 'none',
1728		'opacity' => 1,
1729		'overflow' => 'auto',
1730		'pointer-events' => 'visiblePainted',
1731		'shape-rendering' => 'auto',
1732		'stop-color' => 'black',
1733		'stop-opacity' => 1,
1734		'stroke' => 'none',
1735		'stroke-dasharray' => 'none',
1736		'stroke-dashoffset' => 0,
1737		'stroke-linecap' => 'butt',
1738		'stroke-linejoin' => 'miter',
1739		'stroke-miterlimit' => 4,
1740		'stroke-opacity' => 1,
1741		'stroke-width' => 1,
1742		'text-anchor' => 'start',
1743		'text-decoration' => 'none',
1744		'text-rendering' => 'auto',
1745		'unicode-bidi' => 'normal',
1746		'visibility' => 'visible',
1747		'word-spacing' => 'normal',
1748		'writing-mode' => 'lr-tb',
1749		'text-color' => 'black',
1750		'transfmatrix' => array(1, 0, 0, 1, 0, 0)
1751		));
1752
1753	/**
1754	 * If true force sRGB color profile for all document.
1755	 * @protected
1756	 * @since 5.9.121 (2011-09-28)
1757	 */
1758	protected $force_srgb = false;
1759
1760	/**
1761	 * If true set the document to PDF/A mode.
1762	 * @protected
1763	 * @since 5.9.121 (2011-09-27)
1764	 */
1765	protected $pdfa_mode = false;
1766
1767	/**
1768	 * Document creation date-time
1769	 * @protected
1770	 * @since 5.9.152 (2012-03-22)
1771	 */
1772	protected $doc_creation_timestamp;
1773
1774	/**
1775	 * Document modification date-time
1776	 * @protected
1777	 * @since 5.9.152 (2012-03-22)
1778	 */
1779	protected $doc_modification_timestamp;
1780
1781	/**
1782	 * Custom XMP data.
1783	 * @protected
1784	 * @since 5.9.128 (2011-10-06)
1785	 */
1786	protected $custom_xmp = '';
1787
1788	/**
1789	 * Overprint mode array.
1790	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
1791	 * @protected
1792	 * @since 5.9.152 (2012-03-23)
1793	 */
1794	protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0);
1795
1796	/**
1797	 * Alpha mode array.
1798	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
1799	 * @protected
1800	 * @since 5.9.152 (2012-03-23)
1801	 */
1802	protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false);
1803
1804	/**
1805	 * Define the page boundaries boxes to be set on document.
1806	 * @protected
1807	 * @since 5.9.152 (2012-03-23)
1808	 */
1809	protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
1810
1811	/**
1812	 * If true print TCPDF meta link.
1813	 * @protected
1814	 * @since 5.9.152 (2012-03-23)
1815	 */
1816	protected $tcpdflink = true;
1817
1818	/**
1819	 * Cache array for computed GD gamma values.
1820	 * @protected
1821	 * @since 5.9.1632 (2012-06-05)
1822	 */
1823	protected $gdgammacache = array();
1824
1825	//------------------------------------------------------------
1826	// METHODS
1827	//------------------------------------------------------------
1828
1829	/**
1830	 * This is the class constructor.
1831	 * It allows to set up the page format, the orientation and the measure unit used in all the methods (except for the font sizes).
1832	 *
1833	 * IMPORTANT: Please note that this method sets the mb_internal_encoding to ASCII, so if you are using the mbstring module functions with TCPDF you need to correctly set/unset the mb_internal_encoding when needed.
1834	 *
1835	 * @param $orientation (string) 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>
1836	 * @param $unit (string) 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.
1837	 * @param $format (mixed) 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().
1838	 * @param $unicode (boolean) TRUE means that the input text is unicode (default = true)
1839	 * @param $encoding (string) Charset encoding (used only when converting back html entities); default is UTF-8.
1840	 * @param $diskcache (boolean) If TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower).
1841	 * @param $pdfa (boolean) If TRUE set the document to PDF/A mode.
1842	 * @public
1843	 * @see getPageSizeFromFormat(), setPageFormat()
1844	 */
1845	public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) {
1846		/* Set internal character encoding to ASCII */
1847		if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1848			$this->internal_encoding = mb_internal_encoding();
1849			mb_internal_encoding('ASCII');
1850		}
1851		$this->font_obj_ids = array();
1852		$this->page_obj_id = array();
1853		$this->form_obj_id = array();
1854		// set pdf/a mode
1855		$this->pdfa_mode = $pdfa;
1856		$this->force_srgb = false;
1857		// set disk caching
1858		$this->diskcache = $diskcache ? true : false;
1859		// set language direction
1860		$this->rtl = false;
1861		$this->tmprtl = false;
1862		// some checks
1863		$this->_dochecks();
1864		// initialization of properties
1865		$this->isunicode = $unicode;
1866		$this->page = 0;
1867		$this->transfmrk[0] = array();
1868		$this->pagedim = array();
1869		$this->n = 2;
1870		$this->buffer = '';
1871		$this->pages = array();
1872		$this->state = 0;
1873		$this->fonts = array();
1874		$this->FontFiles = array();
1875		$this->diffs = array();
1876		$this->images = array();
1877		$this->links = array();
1878		$this->gradients = array();
1879		$this->InFooter = false;
1880		$this->lasth = 0;
1881		$this->FontFamily = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
1882		$this->FontStyle = '';
1883		$this->FontSizePt = 12;
1884		$this->underline = false;
1885		$this->overline = false;
1886		$this->linethrough = false;
1887		$this->DrawColor = '0 G';
1888		$this->FillColor = '0 g';
1889		$this->TextColor = '0 g';
1890		$this->ColorFlag = false;
1891		$this->pdflayers = array();
1892		// encryption values
1893		$this->encrypted = false;
1894		$this->last_enc_key = '';
1895		// standard Unicode fonts
1896		$this->CoreFonts = array(
1897			'courier'=>'Courier',
1898			'courierB'=>'Courier-Bold',
1899			'courierI'=>'Courier-Oblique',
1900			'courierBI'=>'Courier-BoldOblique',
1901			'helvetica'=>'Helvetica',
1902			'helveticaB'=>'Helvetica-Bold',
1903			'helveticaI'=>'Helvetica-Oblique',
1904			'helveticaBI'=>'Helvetica-BoldOblique',
1905			'times'=>'Times-Roman',
1906			'timesB'=>'Times-Bold',
1907			'timesI'=>'Times-Italic',
1908			'timesBI'=>'Times-BoldItalic',
1909			'symbol'=>'Symbol',
1910			'zapfdingbats'=>'ZapfDingbats'
1911		);
1912		// set scale factor
1913		$this->setPageUnit($unit);
1914		// set page format and orientation
1915		$this->setPageFormat($format, $orientation);
1916		// page margins (1 cm)
1917		$margin = 28.35 / $this->k;
1918		$this->SetMargins($margin, $margin);
1919		$this->clMargin = $this->lMargin;
1920		$this->crMargin = $this->rMargin;
1921		// internal cell padding
1922		$cpadding = $margin / 10;
1923		$this->setCellPaddings($cpadding, 0, $cpadding, 0);
1924		// cell margins
1925		$this->setCellMargins(0, 0, 0, 0);
1926		// line width (0.2 mm)
1927		$this->LineWidth = 0.57 / $this->k;
1928		$this->linestyleWidth = sprintf('%F w', ($this->LineWidth * $this->k));
1929		$this->linestyleCap = '0 J';
1930		$this->linestyleJoin = '0 j';
1931		$this->linestyleDash = '[] 0 d';
1932		// automatic page break
1933		$this->SetAutoPageBreak(true, (2 * $margin));
1934		// full width display mode
1935		$this->SetDisplayMode('fullwidth');
1936		// compression
1937		$this->SetCompression();
1938		// set default PDF version number
1939		$this->setPDFVersion();
1940		$this->tcpdflink = true;
1941		$this->encoding = $encoding;
1942		$this->HREF = array();
1943		$this->getFontsList();
1944		$this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1945		$this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0);
1946		$this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1947		$this->extgstates = array();
1948		$this->setTextShadow();
1949		// signature
1950		$this->sign = false;
1951		$this->tsa_timestamp = false;
1952		$this->tsa_data = array();
1953		$this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0', 'name' => 'Signature');
1954		$this->empty_signature_appearance = array();
1955		// user's rights
1956		$this->ur['enabled'] = false;
1957		$this->ur['document'] = '/FullSave';
1958		$this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
1959		$this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1960		$this->ur['signature'] = '/Modify';
1961		$this->ur['ef'] = '/Create/Delete/Modify/Import';
1962		$this->ur['formex'] = '';
1963		// set default JPEG quality
1964		$this->jpeg_quality = 75;
1965		// initialize some settings
1966		TCPDF_FONTS::utf8Bidi(array(''), '', false, $this->isunicode, $this->CurrentFont);
1967		// set default font
1968		$this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
1969		$this->setHeaderFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt));
1970		$this->setFooterFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt));
1971		// check if PCRE Unicode support is enabled
1972		if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
1973			// PCRE unicode support is turned ON
1974			// \s     : any whitespace character
1975			// \p{Z}  : any separator
1976			// \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words.
1977			// \xa0   : Unicode Character 'NO-BREAK SPACE' (U+00A0)
1978			//$this->setSpacesRE('/(?!\xa0)[\s\p{Z}\p{Lo}]/u');
1979			$this->setSpacesRE('/(?!\xa0)[\s\p{Z}]/u');
1980		} else {
1981			// PCRE unicode support is turned OFF
1982			$this->setSpacesRE('/[^\S\xa0]/');
1983		}
1984		$this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1985		// set file ID for trailer
1986		$serformat = (is_array($format) ? serialize($format) : $format);
1987		$this->file_id = md5(TCPDF_STATIC::getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding));
1988		// set document creation and modification timestamp
1989		$this->doc_creation_timestamp = time();
1990		$this->doc_modification_timestamp = $this->doc_creation_timestamp;
1991		// get default graphic vars
1992		$this->default_graphic_vars = $this->getGraphicVars();
1993		$this->header_xobj_autoreset = false;
1994		$this->custom_xmp = '';
1995	}
1996
1997	/**
1998	 * Default destructor.
1999	 * @public
2000	 * @since 1.53.0.TC016
2001	 */
2002	public function __destruct() {
2003		// restore internal encoding
2004		if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
2005			mb_internal_encoding($this->internal_encoding);
2006		}
2007		// unset all class variables
2008		$this->_destroy(true);
2009	}
2010
2011	/**
2012	 * Set the units of measure for the document.
2013	 * @param $unit (string) 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.
2014	 * @public
2015	 * @since 3.0.015 (2008-06-06)
2016	 */
2017	public function setPageUnit($unit) {
2018		$unit = strtolower($unit);
2019		//Set scale factor
2020		switch ($unit) {
2021			// points
2022			case 'px':
2023			case 'pt': {
2024				$this->k = 1;
2025				break;
2026			}
2027			// millimeters
2028			case 'mm': {
2029				$this->k = $this->dpi / 25.4;
2030				break;
2031			}
2032			// centimeters
2033			case 'cm': {
2034				$this->k = $this->dpi / 2.54;
2035				break;
2036			}
2037			// inches
2038			case 'in': {
2039				$this->k = $this->dpi;
2040				break;
2041			}
2042			// unsupported unit
2043			default : {
2044				$this->Error('Incorrect unit: '.$unit);
2045				break;
2046			}
2047		}
2048		$this->pdfunit = $unit;
2049		if (isset($this->CurOrientation)) {
2050			$this->setPageOrientation($this->CurOrientation);
2051		}
2052	}
2053
2054	/**
2055	 * Change the format of the current page
2056	 * @param $format (mixed) 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>
2057	 * <li>['format'] = page format name (one of the above);</li>
2058	 * <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>
2059	 * <li>['PZ'] : The page's preferred zoom (magnification) factor.</li>
2060	 * <li>['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed:</li>
2061	 * <li>['MediaBox']['llx'] : lower-left x coordinate</li>
2062	 * <li>['MediaBox']['lly'] : lower-left y coordinate</li>
2063	 * <li>['MediaBox']['urx'] : upper-right x coordinate</li>
2064	 * <li>['MediaBox']['ury'] : upper-right y coordinate</li>
2065	 * <li>['CropBox'] : the visible region of default user space:</li>
2066	 * <li>['CropBox']['llx'] : lower-left x coordinate</li>
2067	 * <li>['CropBox']['lly'] : lower-left y coordinate</li>
2068	 * <li>['CropBox']['urx'] : upper-right x coordinate</li>
2069	 * <li>['CropBox']['ury'] : upper-right y coordinate</li>
2070	 * <li>['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment:</li>
2071	 * <li>['BleedBox']['llx'] : lower-left x coordinate</li>
2072	 * <li>['BleedBox']['lly'] : lower-left y coordinate</li>
2073	 * <li>['BleedBox']['urx'] : upper-right x coordinate</li>
2074	 * <li>['BleedBox']['ury'] : upper-right y coordinate</li>
2075	 * <li>['TrimBox'] : the intended dimensions of the finished page after trimming:</li>
2076	 * <li>['TrimBox']['llx'] : lower-left x coordinate</li>
2077	 * <li>['TrimBox']['lly'] : lower-left y coordinate</li>
2078	 * <li>['TrimBox']['urx'] : upper-right x coordinate</li>
2079	 * <li>['TrimBox']['ury'] : upper-right y coordinate</li>
2080	 * <li>['ArtBox'] : the extent of the page's meaningful content:</li>
2081	 * <li>['ArtBox']['llx'] : lower-left x coordinate</li>
2082	 * <li>['ArtBox']['lly'] : lower-left y coordinate</li>
2083	 * <li>['ArtBox']['urx'] : upper-right x coordinate</li>
2084	 * <li>['ArtBox']['ury'] : upper-right y coordinate</li>
2085	 * <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>
2086	 * <li>['BoxColorInfo'][BOXTYPE]['C'] : an array of three numbers in the range 0-255, representing the components in the DeviceRGB colour space.</li>
2087	 * <li>['BoxColorInfo'][BOXTYPE]['W'] : the guideline width in default user units</li>
2088	 * <li>['BoxColorInfo'][BOXTYPE]['S'] : the guideline style: S = Solid; D = Dashed</li>
2089	 * <li>['BoxColorInfo'][BOXTYPE]['D'] : dash array defining a pattern of dashes and gaps to be used in drawing dashed guidelines</li>
2090	 * <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>
2091	 * <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>
2092	 * <li>['trans']['S'] : transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li>
2093	 * <li>['trans']['D'] : The duration of the transition effect, in seconds.</li>
2094	 * <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>
2095	 * <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>
2096	 * <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>
2097	 * <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>
2098	 * <li>['trans']['B'] : (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li>
2099	 * </ul>
2100	 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul>
2101	 * <li>P or Portrait (default)</li>
2102	 * <li>L or Landscape</li>
2103	 * <li>'' (empty string) for automatic orientation</li>
2104	 * </ul>
2105	 * @protected
2106	 * @since 3.0.015 (2008-06-06)
2107	 * @see getPageSizeFromFormat()
2108	 */
2109	protected function setPageFormat($format, $orientation='P') {
2110		if (!empty($format) AND isset($this->pagedim[$this->page])) {
2111			// remove inherited values
2112			unset($this->pagedim[$this->page]);
2113		}
2114		if (is_string($format)) {
2115			// get page measures from format name
2116			$pf = TCPDF_STATIC::getPageSizeFromFormat($format);
2117			$this->fwPt = $pf[0];
2118			$this->fhPt = $pf[1];
2119		} else {
2120			// the boundaries of the physical medium on which the page shall be displayed or printed
2121			if (isset($format['MediaBox'])) {
2122				$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);
2123				$this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k);
2124				$this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k);
2125			} else {
2126				if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) {
2127					$pf = array(($format[0] * $this->k), ($format[1] * $this->k));
2128				} else {
2129					if (!isset($format['format'])) {
2130						// default value
2131						$format['format'] = 'A4';
2132					}
2133					$pf = TCPDF_STATIC::getPageSizeFromFormat($format['format']);
2134				}
2135				$this->fwPt = $pf[0];
2136				$this->fhPt = $pf[1];
2137				$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2138			}
2139			// the visible region of default user space
2140			if (isset($format['CropBox'])) {
2141				$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);
2142			}
2143			// the region to which the contents of the page shall be clipped when output in a production environment
2144			if (isset($format['BleedBox'])) {
2145				$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);
2146			}
2147			// the intended dimensions of the finished page after trimming
2148			if (isset($format['TrimBox'])) {
2149				$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);
2150			}
2151			// the page's meaningful content (including potential white space)
2152			if (isset($format['ArtBox'])) {
2153				$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);
2154			}
2155			// specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
2156			if (isset($format['BoxColorInfo'])) {
2157				$this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo'];
2158			}
2159			if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) {
2160				// The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2161				$this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']);
2162			}
2163			if (isset($format['PZ'])) {
2164				// The page's preferred zoom (magnification) factor
2165				$this->pagedim[$this->page]['PZ'] = floatval($format['PZ']);
2166			}
2167			if (isset($format['trans'])) {
2168				// The style and duration of the visual transition to use when moving from another page to the given page during a presentation
2169				if (isset($format['trans']['Dur'])) {
2170					// The page's display duration
2171					$this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']);
2172				}
2173				$stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
2174				if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
2175					// The transition style that shall be used when moving to this page from another during a presentation
2176					$this->pagedim[$this->page]['trans']['S'] = $format['trans']['S'];
2177					$valid_effect = array('Split', 'Blinds');
2178					$valid_vals = array('H', 'V');
2179					if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
2180						$this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm'];
2181					}
2182					$valid_effect = array('Split', 'Box', 'Fly');
2183					$valid_vals = array('I', 'O');
2184					if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
2185						$this->pagedim[$this->page]['trans']['M'] = $format['trans']['M'];
2186					}
2187					$valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
2188					if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
2189						if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
2190							OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
2191							OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
2192							$this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']);
2193						}
2194					}
2195					if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
2196						$this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']);
2197					}
2198					if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
2199						$this->pagedim[$this->page]['trans']['B'] = 'true';
2200					}
2201				} else {
2202					$this->pagedim[$this->page]['trans']['S'] = 'R';
2203				}
2204				if (isset($format['trans']['D'])) {
2205					// The duration of the transition effect, in seconds
2206					$this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']);
2207				} else {
2208					$this->pagedim[$this->page]['trans']['D'] = 1;
2209				}
2210			}
2211		}
2212		$this->setPageOrientation($orientation);
2213	}
2214
2215	/**
2216	 * Set page orientation.
2217	 * @param $orientation (string) 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>
2218	 * @param $autopagebreak (boolean) Boolean indicating if auto-page-break mode should be on or off.
2219	 * @param $bottommargin (float) bottom margin of the page.
2220	 * @public
2221	 * @since 3.0.015 (2008-06-06)
2222	 */
2223	public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
2224		if (!isset($this->pagedim[$this->page]['MediaBox'])) {
2225			// the boundaries of the physical medium on which the page shall be displayed or printed
2226			$this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2227		}
2228		if (!isset($this->pagedim[$this->page]['CropBox'])) {
2229			// the visible region of default user space
2230			$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);
2231		}
2232		if (!isset($this->pagedim[$this->page]['BleedBox'])) {
2233			// the region to which the contents of the page shall be clipped when output in a production environment
2234			$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);
2235		}
2236		if (!isset($this->pagedim[$this->page]['TrimBox'])) {
2237			// the intended dimensions of the finished page after trimming
2238			$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);
2239		}
2240		if (!isset($this->pagedim[$this->page]['ArtBox'])) {
2241			// the page's meaningful content (including potential white space)
2242			$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);
2243		}
2244		if (!isset($this->pagedim[$this->page]['Rotate'])) {
2245			// The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2246			$this->pagedim[$this->page]['Rotate'] = 0;
2247		}
2248		if (!isset($this->pagedim[$this->page]['PZ'])) {
2249			// The page's preferred zoom (magnification) factor
2250			$this->pagedim[$this->page]['PZ'] = 1;
2251		}
2252		if ($this->fwPt > $this->fhPt) {
2253			// landscape
2254			$default_orientation = 'L';
2255		} else {
2256			// portrait
2257			$default_orientation = 'P';
2258		}
2259		$valid_orientations = array('P', 'L');
2260		if (empty($orientation)) {
2261			$orientation = $default_orientation;
2262		} else {
2263			$orientation = strtoupper($orientation[0]);
2264		}
2265		if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
2266			$this->CurOrientation = $orientation;
2267			$this->wPt = $this->fhPt;
2268			$this->hPt = $this->fwPt;
2269		} else {
2270			$this->CurOrientation = $default_orientation;
2271			$this->wPt = $this->fwPt;
2272			$this->hPt = $this->fhPt;
2273		}
2274		if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
2275			// swap X and Y coordinates (change page orientation)
2276			$this->pagedim = TCPDF_STATIC::swapPageBoxCoordinates($this->page, $this->pagedim);
2277		}
2278		$this->w = ($this->wPt / $this->k);
2279		$this->h = ($this->hPt / $this->k);
2280		if (TCPDF_STATIC::empty_string($autopagebreak)) {
2281			if (isset($this->AutoPageBreak)) {
2282				$autopagebreak = $this->AutoPageBreak;
2283			} else {
2284				$autopagebreak = true;
2285			}
2286		}
2287		if (TCPDF_STATIC::empty_string($bottommargin)) {
2288			if (isset($this->bMargin)) {
2289				$bottommargin = $this->bMargin;
2290			} else {
2291				// default value = 2 cm
2292				$bottommargin = 2 * 28.35 / $this->k;
2293			}
2294		}
2295		$this->SetAutoPageBreak($autopagebreak, $bottommargin);
2296		// store page dimensions
2297		$this->pagedim[$this->page]['w'] = $this->wPt;
2298		$this->pagedim[$this->page]['h'] = $this->hPt;
2299		$this->pagedim[$this->page]['wk'] = $this->w;
2300		$this->pagedim[$this->page]['hk'] = $this->h;
2301		$this->pagedim[$this->page]['tm'] = $this->tMargin;
2302		$this->pagedim[$this->page]['bm'] = $bottommargin;
2303		$this->pagedim[$this->page]['lm'] = $this->lMargin;
2304		$this->pagedim[$this->page]['rm'] = $this->rMargin;
2305		$this->pagedim[$this->page]['pb'] = $autopagebreak;
2306		$this->pagedim[$this->page]['or'] = $this->CurOrientation;
2307		$this->pagedim[$this->page]['olm'] = $this->original_lMargin;
2308		$this->pagedim[$this->page]['orm'] = $this->original_rMargin;
2309	}
2310
2311	/**
2312	 * Set regular expression to detect withespaces or word separators.
2313	 * The pattern delimiter must be the forward-slash character "/".
2314	 * Some example patterns are:
2315	 * <pre>
2316	 * Non-Unicode or missing PCRE unicode support: "/[^\S\xa0]/"
2317	 * Unicode and PCRE unicode support: "/(?!\xa0)[\s\p{Z}]/u"
2318	 * Unicode and PCRE unicode support in Chinese mode: "/(?!\xa0)[\s\p{Z}\p{Lo}]/u"
2319	 * if PCRE unicode support is turned ON ("\P" is the negate class of "\p"):
2320	 *      \s     : any whitespace character
2321	 *      \p{Z}  : any separator
2322	 *      \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words.
2323	 *      \xa0   : Unicode Character 'NO-BREAK SPACE' (U+00A0)
2324	 * </pre>
2325	 * @param $re (string) regular expression (leave empty for default).
2326	 * @public
2327	 * @since 4.6.016 (2009-06-15)
2328	 */
2329	public function setSpacesRE($re='/[^\S\xa0]/') {
2330		$this->re_spaces = $re;
2331		$re_parts = explode('/', $re);
2332		// get pattern parts
2333		$this->re_space = array();
2334		if (isset($re_parts[1]) AND !empty($re_parts[1])) {
2335			$this->re_space['p'] = $re_parts[1];
2336		} else {
2337			$this->re_space['p'] = '[\s]';
2338		}
2339		// set pattern modifiers
2340		if (isset($re_parts[2]) AND !empty($re_parts[2])) {
2341			$this->re_space['m'] = $re_parts[2];
2342		} else {
2343			$this->re_space['m'] = '';
2344		}
2345	}
2346
2347	/**
2348	 * Enable or disable Right-To-Left language mode
2349	 * @param $enable (Boolean) if true enable Right-To-Left language mode.
2350	 * @param $resetx (Boolean) if true reset the X position on direction change.
2351	 * @public
2352	 * @since 2.0.000 (2008-01-03)
2353	 */
2354	public function setRTL($enable, $resetx=true) {
2355		$enable = $enable ? true : false;
2356		$resetx = ($resetx AND ($enable != $this->rtl));
2357		$this->rtl = $enable;
2358		$this->tmprtl = false;
2359		if ($resetx) {
2360			$this->Ln(0);
2361		}
2362	}
2363
2364	/**
2365	 * Return the RTL status
2366	 * @return boolean
2367	 * @public
2368	 * @since 4.0.012 (2008-07-24)
2369	 */
2370	public function getRTL() {
2371		return $this->rtl;
2372	}
2373
2374	/**
2375	 * Force temporary RTL language direction
2376	 * @param $mode (mixed) can be false, 'L' for LTR or 'R' for RTL
2377	 * @public
2378	 * @since 2.1.000 (2008-01-09)
2379	 */
2380	public function setTempRTL($mode) {
2381		$newmode = false;
2382		switch (strtoupper($mode)) {
2383			case 'LTR':
2384			case 'L': {
2385				if ($this->rtl) {
2386					$newmode = 'L';
2387				}
2388				break;
2389			}
2390			case 'RTL':
2391			case 'R': {
2392				if (!$this->rtl) {
2393					$newmode = 'R';
2394				}
2395				break;
2396			}
2397			case false:
2398			default: {
2399				$newmode = false;
2400				break;
2401			}
2402		}
2403		$this->tmprtl = $newmode;
2404	}
2405
2406	/**
2407	 * Return the current temporary RTL status
2408	 * @return boolean
2409	 * @public
2410	 * @since 4.8.014 (2009-11-04)
2411	 */
2412	public function isRTLTextDir() {
2413		return ($this->rtl OR ($this->tmprtl == 'R'));
2414	}
2415
2416	/**
2417	 * Set the last cell height.
2418	 * @param $h (float) cell height.
2419	 * @author Nicola Asuni
2420	 * @public
2421	 * @since 1.53.0.TC034
2422	 */
2423	public function setLastH($h) {
2424		$this->lasth = $h;
2425	}
2426
2427	/**
2428	 * Return the cell height
2429	 * @param $fontsize (int) Font size in internal units
2430	 * @param $padding (boolean) If true add cell padding
2431	 * @public
2432	 */
2433	public function getCellHeight($fontsize, $padding=TRUE) {
2434		$height = ($fontsize * $this->cell_height_ratio);
2435		if ($padding) {
2436			$height += ($this->cell_padding['T'] + $this->cell_padding['B']);
2437		}
2438		return round($height, 6);
2439	}
2440
2441	/**
2442	 * Reset the last cell height.
2443	 * @public
2444	 * @since 5.9.000 (2010-10-03)
2445	 */
2446	public function resetLastH() {
2447		$this->lasth = $this->getCellHeight($this->FontSize);
2448	}
2449
2450	/**
2451	 * Get the last cell height.
2452	 * @return last cell height
2453	 * @public
2454	 * @since 4.0.017 (2008-08-05)
2455	 */
2456	public function getLastH() {
2457		return $this->lasth;
2458	}
2459
2460	/**
2461	 * Set the adjusting factor to convert pixels to user units.
2462	 * @param $scale (float) adjusting factor to convert pixels to user units.
2463	 * @author Nicola Asuni
2464	 * @public
2465	 * @since 1.5.2
2466	 */
2467	public function setImageScale($scale) {
2468		$this->imgscale = $scale;
2469	}
2470
2471	/**
2472	 * Returns the adjusting factor to convert pixels to user units.
2473	 * @return float adjusting factor to convert pixels to user units.
2474	 * @author Nicola Asuni
2475	 * @public
2476	 * @since 1.5.2
2477	 */
2478	public function getImageScale() {
2479		return $this->imgscale;
2480	}
2481
2482	/**
2483	 * Returns an array of page dimensions:
2484	 * <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>
2485	 * @param $pagenum (int) page number (empty = current page)
2486	 * @return array of page dimensions.
2487	 * @author Nicola Asuni
2488	 * @public
2489	 * @since 4.5.027 (2009-03-16)
2490	 */
2491	public function getPageDimensions($pagenum='') {
2492		if (empty($pagenum)) {
2493			$pagenum = $this->page;
2494		}
2495		return $this->pagedim[$pagenum];
2496	}
2497
2498	/**
2499	 * Returns the page width in units.
2500	 * @param $pagenum (int) page number (empty = current page)
2501	 * @return int page width.
2502	 * @author Nicola Asuni
2503	 * @public
2504	 * @since 1.5.2
2505	 * @see getPageDimensions()
2506	 */
2507	public function getPageWidth($pagenum='') {
2508		if (empty($pagenum)) {
2509			return $this->w;
2510		}
2511		return $this->pagedim[$pagenum]['w'];
2512	}
2513
2514	/**
2515	 * Returns the page height in units.
2516	 * @param $pagenum (int) page number (empty = current page)
2517	 * @return int page height.
2518	 * @author Nicola Asuni
2519	 * @public
2520	 * @since 1.5.2
2521	 * @see getPageDimensions()
2522	 */
2523	public function getPageHeight($pagenum='') {
2524		if (empty($pagenum)) {
2525			return $this->h;
2526		}
2527		return $this->pagedim[$pagenum]['h'];
2528	}
2529
2530	/**
2531	 * Returns the page break margin.
2532	 * @param $pagenum (int) page number (empty = current page)
2533	 * @return int page break margin.
2534	 * @author Nicola Asuni
2535	 * @public
2536	 * @since 1.5.2
2537	 * @see getPageDimensions()
2538	 */
2539	public function getBreakMargin($pagenum='') {
2540		if (empty($pagenum)) {
2541			return $this->bMargin;
2542		}
2543		return $this->pagedim[$pagenum]['bm'];
2544	}
2545
2546	/**
2547	 * Returns the scale factor (number of points in user unit).
2548	 * @return int scale factor.
2549	 * @author Nicola Asuni
2550	 * @public
2551	 * @since 1.5.2
2552	 */
2553	public function getScaleFactor() {
2554		return $this->k;
2555	}
2556
2557	/**
2558	 * Defines the left, top and right margins.
2559	 * @param $left (float) Left margin.
2560	 * @param $top (float) Top margin.
2561	 * @param $right (float) Right margin. Default value is the left one.
2562	 * @param $keepmargins (boolean) if true overwrites the default page margins
2563	 * @public
2564	 * @since 1.0
2565	 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
2566	 */
2567	public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
2568		//Set left, top and right margins
2569		$this->lMargin = $left;
2570		$this->tMargin = $top;
2571		if ($right == -1) {
2572			$right = $left;
2573		}
2574		$this->rMargin = $right;
2575		if ($keepmargins) {
2576			// overwrite original values
2577			$this->original_lMargin = $this->lMargin;
2578			$this->original_rMargin = $this->rMargin;
2579		}
2580	}
2581
2582	/**
2583	 * 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.
2584	 * @param $margin (float) The margin.
2585	 * @public
2586	 * @since 1.4
2587	 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
2588	 */
2589	public function SetLeftMargin($margin) {
2590		//Set left margin
2591		$this->lMargin = $margin;
2592		if (($this->page > 0) AND ($this->x < $margin)) {
2593			$this->x = $margin;
2594		}
2595	}
2596
2597	/**
2598	 * Defines the top margin. The method can be called before creating the first page.
2599	 * @param $margin (float) The margin.
2600	 * @public
2601	 * @since 1.5
2602	 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
2603	 */
2604	public function SetTopMargin($margin) {
2605		//Set top margin
2606		$this->tMargin = $margin;
2607		if (($this->page > 0) AND ($this->y < $margin)) {
2608			$this->y = $margin;
2609		}
2610	}
2611
2612	/**
2613	 * Defines the right margin. The method can be called before creating the first page.
2614	 * @param $margin (float) The margin.
2615	 * @public
2616	 * @since 1.5
2617	 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
2618	 */
2619	public function SetRightMargin($margin) {
2620		$this->rMargin = $margin;
2621		if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
2622			$this->x = $this->w - $margin;
2623		}
2624	}
2625
2626	/**
2627	 * Set the same internal Cell padding for top, right, bottom, left-
2628	 * @param $pad (float) internal padding.
2629	 * @public
2630	 * @since 2.1.000 (2008-01-09)
2631	 * @see getCellPaddings(), setCellPaddings()
2632	 */
2633	public function SetCellPadding($pad) {
2634		if ($pad >= 0) {
2635			$this->cell_padding['L'] = $pad;
2636			$this->cell_padding['T'] = $pad;
2637			$this->cell_padding['R'] = $pad;
2638			$this->cell_padding['B'] = $pad;
2639		}
2640	}
2641
2642	/**
2643	 * Set the internal Cell paddings.
2644	 * @param $left (float) left padding
2645	 * @param $top (float) top padding
2646	 * @param $right (float) right padding
2647	 * @param $bottom (float) bottom padding
2648	 * @public
2649	 * @since 5.9.000 (2010-10-03)
2650	 * @see getCellPaddings(), SetCellPadding()
2651	 */
2652	public function setCellPaddings($left='', $top='', $right='', $bottom='') {
2653		if (($left !== '') AND ($left >= 0)) {
2654			$this->cell_padding['L'] = $left;
2655		}
2656		if (($top !== '') AND ($top >= 0)) {
2657			$this->cell_padding['T'] = $top;
2658		}
2659		if (($right !== '') AND ($right >= 0)) {
2660			$this->cell_padding['R'] = $right;
2661		}
2662		if (($bottom !== '') AND ($bottom >= 0)) {
2663			$this->cell_padding['B'] = $bottom;
2664		}
2665	}
2666
2667	/**
2668	 * Get the internal Cell padding array.
2669	 * @return array of padding values
2670	 * @public
2671	 * @since 5.9.000 (2010-10-03)
2672	 * @see setCellPaddings(), SetCellPadding()
2673	 */
2674	public function getCellPaddings() {
2675		return $this->cell_padding;
2676	}
2677
2678	/**
2679	 * Set the internal Cell margins.
2680	 * @param $left (float) left margin
2681	 * @param $top (float) top margin
2682	 * @param $right (float) right margin
2683	 * @param $bottom (float) bottom margin
2684	 * @public
2685	 * @since 5.9.000 (2010-10-03)
2686	 * @see getCellMargins()
2687	 */
2688	public function setCellMargins($left='', $top='', $right='', $bottom='') {
2689		if (($left !== '') AND ($left >= 0)) {
2690			$this->cell_margin['L'] = $left;
2691		}
2692		if (($top !== '') AND ($top >= 0)) {
2693			$this->cell_margin['T'] = $top;
2694		}
2695		if (($right !== '') AND ($right >= 0)) {
2696			$this->cell_margin['R'] = $right;
2697		}
2698		if (($bottom !== '') AND ($bottom >= 0)) {
2699			$this->cell_margin['B'] = $bottom;
2700		}
2701	}
2702
2703	/**
2704	 * Get the internal Cell margin array.
2705	 * @return array of margin values
2706	 * @public
2707	 * @since 5.9.000 (2010-10-03)
2708	 * @see setCellMargins()
2709	 */
2710	public function getCellMargins() {
2711		return $this->cell_margin;
2712	}
2713
2714	/**
2715	 * Adjust the internal Cell padding array to take account of the line width.
2716	 * @param $brd (mixed) 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)))
2717	 * @return array of adjustments
2718	 * @public
2719	 * @since 5.9.000 (2010-10-03)
2720	 */
2721	protected function adjustCellPadding($brd=0) {
2722		if (empty($brd)) {
2723			return;
2724		}
2725		if (is_string($brd)) {
2726			// convert string to array
2727			$slen = strlen($brd);
2728			$newbrd = array();
2729			for ($i = 0; $i < $slen; ++$i) {
2730				$newbrd[$brd[$i]] = true;
2731			}
2732			$brd = $newbrd;
2733		} elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) {
2734			$brd = array('LRTB' => true);
2735		}
2736		if (!is_array($brd)) {
2737			return;
2738		}
2739		// store current cell padding
2740		$cp = $this->cell_padding;
2741		// select border mode
2742		if (isset($brd['mode'])) {
2743			$mode = $brd['mode'];
2744			unset($brd['mode']);
2745		} else {
2746			$mode = 'normal';
2747		}
2748		// process borders
2749		foreach ($brd as $border => $style) {
2750			$line_width = $this->LineWidth;
2751			if (is_array($style) AND isset($style['width'])) {
2752				// get border width
2753				$line_width = $style['width'];
2754			}
2755			$adj = 0; // line width inside the cell
2756			switch ($mode) {
2757				case 'ext': {
2758					$adj = 0;
2759					break;
2760				}
2761				case 'int': {
2762					$adj = $line_width;
2763					break;
2764				}
2765				case 'normal':
2766				default: {
2767					$adj = ($line_width / 2);
2768					break;
2769				}
2770			}
2771			// correct internal cell padding if required to avoid overlap between text and lines
2772			if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) {
2773				$this->cell_padding['T'] = $adj;
2774			}
2775			if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) {
2776				$this->cell_padding['R'] = $adj;
2777			}
2778			if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) {
2779				$this->cell_padding['B'] = $adj;
2780			}
2781			if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) {
2782				$this->cell_padding['L'] = $adj;
2783			}
2784		}
2785		return array('T' => ($this->cell_padding['T'] - $cp['T']), 'R' => ($this->cell_padding['R'] - $cp['R']), 'B' => ($this->cell_padding['B'] - $cp['B']), 'L' => ($this->cell_padding['L'] - $cp['L']));
2786	}
2787
2788	/**
2789	 * 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.
2790	 * @param $auto (boolean) Boolean indicating if mode should be on or off.
2791	 * @param $margin (float) Distance from the bottom of the page.
2792	 * @public
2793	 * @since 1.0
2794	 * @see Cell(), MultiCell(), AcceptPageBreak()
2795	 */
2796	public function SetAutoPageBreak($auto, $margin=0) {
2797		$this->AutoPageBreak = $auto ? true : false;
2798		$this->bMargin = $margin;
2799		$this->PageBreakTrigger = $this->h - $margin;
2800	}
2801
2802	/**
2803	 * Return the auto-page-break mode (true or false).
2804	 * @return boolean auto-page-break mode
2805	 * @public
2806	 * @since 5.9.088
2807	 */
2808	public function getAutoPageBreak() {
2809		return $this->AutoPageBreak;
2810	}
2811
2812	/**
2813	 * Defines the way the document is to be displayed by the viewer.
2814	 * @param $zoom (mixed) 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>
2815	 * @param $layout (string) 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>
2816	 * @param $mode (string) 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>
2817	 * @public
2818	 * @since 1.2
2819	 */
2820	public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
2821		if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
2822			$this->ZoomMode = $zoom;
2823		} else {
2824			$this->Error('Incorrect zoom display mode: '.$zoom);
2825		}
2826		$this->LayoutMode = TCPDF_STATIC::getPageLayoutMode($layout);
2827		$this->PageMode = TCPDF_STATIC::getPageMode($mode);
2828	}
2829
2830	/**
2831	 * 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.
2832	 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
2833	 * @param $compress (boolean) Boolean indicating if compression must be enabled.
2834	 * @public
2835	 * @since 1.4
2836	 */
2837	public function SetCompression($compress=true) {
2838		if (function_exists('gzcompress')) {
2839			$this->compress = $compress ? true : false;
2840		} else {
2841			$this->compress = false;
2842		}
2843	}
2844
2845	/**
2846	 * Set flag to force sRGB_IEC61966-2.1 black scaled ICC color profile for the whole document.
2847	 * @param $mode (boolean) If true force sRGB output intent.
2848	 * @public
2849	 * @since 5.9.121 (2011-09-28)
2850	 */
2851	public function setSRGBmode($mode=false) {
2852		$this->force_srgb = $mode ? true : false;
2853	}
2854
2855	/**
2856	 * Turn on/off Unicode mode for document information dictionary (meta tags).
2857	 * This has effect only when unicode mode is set to false.
2858	 * @param $unicode (boolean) if true set the meta information in Unicode
2859	 * @since 5.9.027 (2010-12-01)
2860	 * @public
2861	 */
2862	public function SetDocInfoUnicode($unicode=true) {
2863		$this->docinfounicode = $unicode ? true : false;
2864	}
2865
2866	/**
2867	 * Defines the title of the document.
2868	 * @param $title (string) The title.
2869	 * @public
2870	 * @since 1.2
2871	 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
2872	 */
2873	public function SetTitle($title) {
2874		$this->title = $title;
2875	}
2876
2877	/**
2878	 * Defines the subject of the document.
2879	 * @param $subject (string) The subject.
2880	 * @public
2881	 * @since 1.2
2882	 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
2883	 */
2884	public function SetSubject($subject) {
2885		$this->subject = $subject;
2886	}
2887
2888	/**
2889	 * Defines the author of the document.
2890	 * @param $author (string) The name of the author.
2891	 * @public
2892	 * @since 1.2
2893	 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
2894	 */
2895	public function SetAuthor($author) {
2896		$this->author = $author;
2897	}
2898
2899	/**
2900	 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
2901	 * @param $keywords (string) The list of keywords.
2902	 * @public
2903	 * @since 1.2
2904	 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
2905	 */
2906	public function SetKeywords($keywords) {
2907		$this->keywords = $keywords;
2908	}
2909
2910	/**
2911	 * Defines the creator of the document. This is typically the name of the application that generates the PDF.
2912	 * @param $creator (string) The name of the creator.
2913	 * @public
2914	 * @since 1.2
2915	 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
2916	 */
2917	public function SetCreator($creator) {
2918		$this->creator = $creator;
2919	}
2920
2921	/**
2922	 * Throw an exception or print an error message and die if the K_TCPDF_PARSER_THROW_EXCEPTION_ERROR constant is set to true.
2923	 * @param $msg (string) The error message
2924	 * @public
2925	 * @since 1.0
2926	 */
2927	public function Error($msg) {
2928		// unset all class variables
2929		$this->_destroy(true);
2930		if (defined('K_TCPDF_THROW_EXCEPTION_ERROR') AND !K_TCPDF_THROW_EXCEPTION_ERROR) {
2931			die('<strong>TCPDF ERROR: </strong>'.$msg);
2932		} else {
2933			throw new Exception('TCPDF ERROR: '.$msg);
2934		}
2935	}
2936
2937	/**
2938	 * This method begins the generation of the PDF document.
2939	 * It is not necessary to call it explicitly because AddPage() does it automatically.
2940	 * Note: no page is created by this method
2941	 * @public
2942	 * @since 1.0
2943	 * @see AddPage(), Close()
2944	 */
2945	public function Open() {
2946		$this->state = 1;
2947	}
2948
2949	/**
2950	 * Terminates the PDF document.
2951	 * It is not necessary to call this method explicitly because Output() does it automatically.
2952	 * If the document contains no page, AddPage() is called to prevent from getting an invalid document.
2953	 * @public
2954	 * @since 1.0
2955	 * @see Open(), Output()
2956	 */
2957	public function Close() {
2958		if ($this->state == 3) {
2959			return;
2960		}
2961		if ($this->page == 0) {
2962			$this->AddPage();
2963		}
2964		$this->endLayer();
2965		if ($this->tcpdflink) {
2966			// save current graphic settings
2967			$gvars = $this->getGraphicVars();
2968			$this->setEqualColumns();
2969			$this->lastpage(true);
2970			$this->SetAutoPageBreak(false);
2971			$this->x = 0;
2972			$this->y = $this->h - (1 / $this->k);
2973			$this->lMargin = 0;
2974			$this->_outSaveGraphicsState();
2975			$font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
2976			$this->SetFont($font, '', 1);
2977			$this->setTextRenderingMode(0, false, false);
2978			$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";
2979			$lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
2980			$this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
2981			$this->_outRestoreGraphicsState();
2982			// restore graphic settings
2983			$this->setGraphicVars($gvars);
2984		}
2985		// close page
2986		$this->endPage();
2987		// close document
2988		$this->_enddoc();
2989		// unset all class variables (except critical ones)
2990		$this->_destroy(false);
2991	}
2992
2993	/**
2994	 * Move pointer at the specified document page and update page dimensions.
2995	 * @param $pnum (int) page number (1 ... numpages)
2996	 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
2997	 * @public
2998	 * @since 2.1.000 (2008-01-07)
2999	 * @see getPage(), lastpage(), getNumPages()
3000	 */
3001	public function setPage($pnum, $resetmargins=false) {
3002		if (($pnum == $this->page) AND ($this->state == 2)) {
3003			return;
3004		}
3005		if (($pnum > 0) AND ($pnum <= $this->numpages)) {
3006			$this->state = 2;
3007			// save current graphic settings
3008			//$gvars = $this->getGraphicVars();
3009			$oldpage = $this->page;
3010			$this->page = $pnum;
3011			$this->wPt = $this->pagedim[$this->page]['w'];
3012			$this->hPt = $this->pagedim[$this->page]['h'];
3013			$this->w = $this->pagedim[$this->page]['wk'];
3014			$this->h = $this->pagedim[$this->page]['hk'];
3015			$this->tMargin = $this->pagedim[$this->page]['tm'];
3016			$this->bMargin = $this->pagedim[$this->page]['bm'];
3017			$this->original_lMargin = $this->pagedim[$this->page]['olm'];
3018			$this->original_rMargin = $this->pagedim[$this->page]['orm'];
3019			$this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
3020			$this->CurOrientation = $this->pagedim[$this->page]['or'];
3021			$this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
3022			// restore graphic settings
3023			//$this->setGraphicVars($gvars);
3024			if ($resetmargins) {
3025				$this->lMargin = $this->pagedim[$this->page]['olm'];
3026				$this->rMargin = $this->pagedim[$this->page]['orm'];
3027				$this->SetY($this->tMargin);
3028			} else {
3029				// account for booklet mode
3030				if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3031					$deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
3032					$this->lMargin += $deltam;
3033					$this->rMargin -= $deltam;
3034				}
3035			}
3036		} else {
3037			$this->Error('Wrong page number on setPage() function: '.$pnum);
3038		}
3039	}
3040
3041	/**
3042	 * Reset pointer to the last document page.
3043	 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
3044	 * @public
3045	 * @since 2.0.000 (2008-01-04)
3046	 * @see setPage(), getPage(), getNumPages()
3047	 */
3048	public function lastPage($resetmargins=false) {
3049		$this->setPage($this->getNumPages(), $resetmargins);
3050	}
3051
3052	/**
3053	 * Get current document page number.
3054	 * @return int page number
3055	 * @public
3056	 * @since 2.1.000 (2008-01-07)
3057	 * @see setPage(), lastpage(), getNumPages()
3058	 */
3059	public function getPage() {
3060		return $this->page;
3061	}
3062
3063	/**
3064	 * Get the total number of insered pages.
3065	 * @return int number of pages
3066	 * @public
3067	 * @since 2.1.000 (2008-01-07)
3068	 * @see setPage(), getPage(), lastpage()
3069	 */
3070	public function getNumPages() {
3071		return $this->numpages;
3072	}
3073
3074	/**
3075	 * Adds a new TOC (Table Of Content) page to the document.
3076	 * @param $orientation (string) page orientation.
3077	 * @param $format (mixed) 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().
3078	 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
3079	 * @public
3080	 * @since 5.0.001 (2010-05-06)
3081	 * @see AddPage(), startPage(), endPage(), endTOCPage()
3082	 */
3083	public function addTOCPage($orientation='', $format='', $keepmargins=false) {
3084		$this->AddPage($orientation, $format, $keepmargins, true);
3085	}
3086
3087	/**
3088	 * Terminate the current TOC (Table Of Content) page
3089	 * @public
3090	 * @since 5.0.001 (2010-05-06)
3091	 * @see AddPage(), startPage(), endPage(), addTOCPage()
3092	 */
3093	public function endTOCPage() {
3094		$this->endPage(true);
3095	}
3096
3097	/**
3098	 * 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).
3099	 * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.
3100	 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3101	 * @param $format (mixed) 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().
3102	 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
3103	 * @param $tocpage (boolean) if true set the tocpage state to true (the added page will be used to display Table Of Content).
3104	 * @public
3105	 * @since 1.0
3106	 * @see startPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3107	 */
3108	public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
3109		if ($this->inxobj) {
3110			// we are inside an XObject template
3111			return;
3112		}
3113		if (!isset($this->original_lMargin) OR $keepmargins) {
3114			$this->original_lMargin = $this->lMargin;
3115		}
3116		if (!isset($this->original_rMargin) OR $keepmargins) {
3117			$this->original_rMargin = $this->rMargin;
3118		}
3119		// terminate previous page
3120		$this->endPage();
3121		// start new page
3122		$this->startPage($orientation, $format, $tocpage);
3123	}
3124
3125	/**
3126	 * Terminate the current page
3127	 * @param $tocpage (boolean) if true set the tocpage state to false (end the page used to display Table Of Content).
3128	 * @public
3129	 * @since 4.2.010 (2008-11-14)
3130	 * @see AddPage(), startPage(), addTOCPage(), endTOCPage()
3131	 */
3132	public function endPage($tocpage=false) {
3133		// check if page is already closed
3134		if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
3135			return;
3136		}
3137		// print page footer
3138		$this->setFooter();
3139		// close page
3140		$this->_endpage();
3141		// mark page as closed
3142		$this->pageopen[$this->page] = false;
3143		if ($tocpage) {
3144			$this->tocpage = false;
3145		}
3146	}
3147
3148	/**
3149	 * Starts a new page to the document. The page must be closed using the endPage() function.
3150	 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
3151	 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3152	 * @param $format (mixed) 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().
3153	 * @param $tocpage (boolean) if true the page is designated to contain the Table-Of-Content.
3154	 * @since 4.2.010 (2008-11-14)
3155	 * @see AddPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3156	 * @public
3157	 */
3158	public function startPage($orientation='', $format='', $tocpage=false) {
3159		if ($tocpage) {
3160			$this->tocpage = true;
3161		}
3162		// move page numbers of documents to be attached
3163		if ($this->tocpage) {
3164			// move reference to unexistent pages (used for page attachments)
3165			// adjust outlines
3166			$tmpoutlines = $this->outlines;
3167			foreach ($tmpoutlines as $key => $outline) {
3168				if (!$outline['f'] AND ($outline['p'] > $this->numpages)) {
3169					$this->outlines[$key]['p'] = ($outline['p'] + 1);
3170				}
3171			}
3172			// adjust dests
3173			$tmpdests = $this->dests;
3174			foreach ($tmpdests as $key => $dest) {
3175				if (!$dest['f'] AND ($dest['p'] > $this->numpages)) {
3176					$this->dests[$key]['p'] = ($dest['p'] + 1);
3177				}
3178			}
3179			// adjust links
3180			$tmplinks = $this->links;
3181			foreach ($tmplinks as $key => $link) {
3182				if (!$link['f'] AND ($link['p'] > $this->numpages)) {
3183					$this->links[$key]['p'] = ($link['p'] + 1);
3184				}
3185			}
3186		}
3187		if ($this->numpages > $this->page) {
3188			// this page has been already added
3189			$this->setPage($this->page + 1);
3190			$this->SetY($this->tMargin);
3191			return;
3192		}
3193		// start a new page
3194		if ($this->state == 0) {
3195			$this->Open();
3196		}
3197		++$this->numpages;
3198		$this->swapMargins($this->booklet);
3199		// save current graphic settings
3200		$gvars = $this->getGraphicVars();
3201		// start new page
3202		$this->_beginpage($orientation, $format);
3203		// mark page as open
3204		$this->pageopen[$this->page] = true;
3205		// restore graphic settings
3206		$this->setGraphicVars($gvars);
3207		// mark this point
3208		$this->setPageMark();
3209		// print page header
3210		$this->setHeader();
3211		// restore graphic settings
3212		$this->setGraphicVars($gvars);
3213		// mark this point
3214		$this->setPageMark();
3215		// print table header (if any)
3216		$this->setTableHeader();
3217		// set mark for empty page check
3218		$this->emptypagemrk[$this->page]= $this->pagelen[$this->page];
3219	}
3220
3221	/**
3222	 * Set start-writing mark on current page stream used to put borders and fills.
3223	 * Borders and fills are always created after content and inserted on the position marked by this method.
3224	 * This function must be called after calling Image() function for a background image.
3225	 * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
3226	 * @public
3227	 * @since 4.0.016 (2008-07-30)
3228	 */
3229	public function setPageMark() {
3230		$this->intmrk[$this->page] = $this->pagelen[$this->page];
3231		$this->bordermrk[$this->page] = $this->intmrk[$this->page];
3232		$this->setContentMark();
3233	}
3234
3235	/**
3236	 * Set start-writing mark on selected page.
3237	 * Borders and fills are always created after content and inserted on the position marked by this method.
3238	 * @param $page (int) page number (default is the current page)
3239	 * @protected
3240	 * @since 4.6.021 (2009-07-20)
3241	 */
3242	protected function setContentMark($page=0) {
3243		if ($page <= 0) {
3244			$page = $this->page;
3245		}
3246		if (isset($this->footerlen[$page])) {
3247			$this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
3248		} else {
3249			$this->cntmrk[$page] = $this->pagelen[$page];
3250		}
3251	}
3252
3253	/**
3254	 * Set header data.
3255	 * @param $ln (string) header image logo
3256	 * @param $lw (string) header image logo width in mm
3257	 * @param $ht (string) string to print as title on document header
3258	 * @param $hs (string) string to print on document header
3259	 * @param $tc (array) RGB array color for text.
3260	 * @param $lc (array) RGB array color for line.
3261	 * @public
3262	 */
3263	public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) {
3264		$this->header_logo = $ln;
3265		$this->header_logo_width = $lw;
3266		$this->header_title = $ht;
3267		$this->header_string = $hs;
3268		$this->header_text_color = $tc;
3269		$this->header_line_color = $lc;
3270	}
3271
3272	/**
3273	 * Set footer data.
3274	 * @param $tc (array) RGB array color for text.
3275	 * @param $lc (array) RGB array color for line.
3276	 * @public
3277	 */
3278	public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) {
3279		$this->footer_text_color = $tc;
3280		$this->footer_line_color = $lc;
3281	}
3282
3283	/**
3284	 * Returns header data:
3285	 * <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>
3286	 * @return array()
3287	 * @public
3288	 * @since 4.0.012 (2008-07-24)
3289	 */
3290	public function getHeaderData() {
3291		$ret = array();
3292		$ret['logo'] = $this->header_logo;
3293		$ret['logo_width'] = $this->header_logo_width;
3294		$ret['title'] = $this->header_title;
3295		$ret['string'] = $this->header_string;
3296		$ret['text_color'] = $this->header_text_color;
3297		$ret['line_color'] = $this->header_line_color;
3298		return $ret;
3299	}
3300
3301	/**
3302	 * Set header margin.
3303	 * (minimum distance between header and top page margin)
3304	 * @param $hm (int) distance in user units
3305	 * @public
3306	 */
3307	public function setHeaderMargin($hm=10) {
3308		$this->header_margin = $hm;
3309	}
3310
3311	/**
3312	 * Returns header margin in user units.
3313	 * @return float
3314	 * @since 4.0.012 (2008-07-24)
3315	 * @public
3316	 */
3317	public function getHeaderMargin() {
3318		return $this->header_margin;
3319	}
3320
3321	/**
3322	 * Set footer margin.
3323	 * (minimum distance between footer and bottom page margin)
3324	 * @param $fm (int) distance in user units
3325	 * @public
3326	 */
3327	public function setFooterMargin($fm=10) {
3328		$this->footer_margin = $fm;
3329	}
3330
3331	/**
3332	 * Returns footer margin in user units.
3333	 * @return float
3334	 * @since 4.0.012 (2008-07-24)
3335	 * @public
3336	 */
3337	public function getFooterMargin() {
3338		return $this->footer_margin;
3339	}
3340	/**
3341	 * Set a flag to print page header.
3342	 * @param $val (boolean) set to true to print the page header (default), false otherwise.
3343	 * @public
3344	 */
3345	public function setPrintHeader($val=true) {
3346		$this->print_header = $val ? true : false;
3347	}
3348
3349	/**
3350	 * Set a flag to print page footer.
3351	 * @param $val (boolean) set to true to print the page footer (default), false otherwise.
3352	 * @public
3353	 */
3354	public function setPrintFooter($val=true) {
3355		$this->print_footer = $val ? true : false;
3356	}
3357
3358	/**
3359	 * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
3360	 * @return float
3361	 * @public
3362	 */
3363	public function getImageRBX() {
3364		return $this->img_rb_x;
3365	}
3366
3367	/**
3368	 * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
3369	 * @return float
3370	 * @public
3371	 */
3372	public function getImageRBY() {
3373		return $this->img_rb_y;
3374	}
3375
3376	/**
3377	 * Reset the xobject template used by Header() method.
3378	 * @public
3379	 */
3380	public function resetHeaderTemplate() {
3381		$this->header_xobjid = false;
3382	}
3383
3384	/**
3385	 * Set a flag to automatically reset the xobject template used by Header() method at each page.
3386	 * @param $val (boolean) set to true to reset Header xobject template at each page, false otherwise.
3387	 * @public
3388	 */
3389	public function setHeaderTemplateAutoreset($val=true) {
3390		$this->header_xobj_autoreset = $val ? true : false;
3391	}
3392
3393	/**
3394	 * This method is used to render the page header.
3395	 * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3396	 * @public
3397	 */
3398	public function Header() {
3399		if ($this->header_xobjid === false) {
3400			// start a new XObject Template
3401			$this->header_xobjid = $this->startTemplate($this->w, $this->tMargin);
3402			$headerfont = $this->getHeaderFont();
3403			$headerdata = $this->getHeaderData();
3404			$this->y = $this->header_margin;
3405			if ($this->rtl) {
3406				$this->x = $this->w - $this->original_rMargin;
3407			} else {
3408				$this->x = $this->original_lMargin;
3409			}
3410			if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
3411				$imgtype = TCPDF_IMAGES::getImageFileType(K_PATH_IMAGES.$headerdata['logo']);
3412				if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
3413					$this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3414				} elseif ($imgtype == 'svg') {
3415					$this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3416				} else {
3417					$this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3418				}
3419				$imgy = $this->getImageRBY();
3420			} else {
3421				$imgy = $this->y;
3422			}
3423			$cell_height = $this->getCellHeight($headerfont[2] / $this->k);
3424			// set starting margin for text data cell
3425			if ($this->getRTL()) {
3426				$header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1);
3427			} else {
3428				$header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1);
3429			}
3430			$cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1);
3431			$this->SetTextColorArray($this->header_text_color);
3432			// header title
3433			$this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
3434			$this->SetX($header_x);
3435			$this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
3436			// header string
3437			$this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
3438			$this->SetX($header_x);
3439			$this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false);
3440			// print an ending header line
3441			$this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color']));
3442			$this->SetY((2.835 / $this->k) + max($imgy, $this->y));
3443			if ($this->rtl) {
3444				$this->SetX($this->original_rMargin);
3445			} else {
3446				$this->SetX($this->original_lMargin);
3447			}
3448			$this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C');
3449			$this->endTemplate();
3450		}
3451		// print header template
3452		$x = 0;
3453		$dx = 0;
3454		if (!$this->header_xobj_autoreset AND $this->booklet AND (($this->page % 2) == 0)) {
3455			// adjust margins for booklet mode
3456			$dx = ($this->original_lMargin - $this->original_rMargin);
3457		}
3458		if ($this->rtl) {
3459			$x = $this->w + $dx;
3460		} else {
3461			$x = 0 + $dx;
3462		}
3463		$this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false);
3464		if ($this->header_xobj_autoreset) {
3465			// reset header xobject template at each page
3466			$this->header_xobjid = false;
3467		}
3468	}
3469
3470	/**
3471	 * This method is used to render the page footer.
3472	 * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3473	 * @public
3474	 */
3475	public function Footer() {
3476		$cur_y = $this->y;
3477		$this->SetTextColorArray($this->footer_text_color);
3478		//set style for cell border
3479		$line_width = (0.85 / $this->k);
3480		$this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color));
3481		//print document barcode
3482		$barcode = $this->getBarcode();
3483		if (!empty($barcode)) {
3484			$this->Ln($line_width);
3485			$barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3);
3486			$style = array(
3487				'position' => $this->rtl?'R':'L',
3488				'align' => $this->rtl?'R':'L',
3489				'stretch' => false,
3490				'fitwidth' => true,
3491				'cellfitalign' => '',
3492				'border' => false,
3493				'padding' => 0,
3494				'fgcolor' => array(0,0,0),
3495				'bgcolor' => false,
3496				'text' => false
3497			);
3498			$this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, '');
3499		}
3500		$w_page = isset($this->l['w_page']) ? $this->l['w_page'].' ' : '';
3501		if (empty($this->pagegroups)) {
3502			$pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
3503		} else {
3504			$pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
3505		}
3506		$this->SetY($cur_y);
3507		//Print page number
3508		if ($this->getRTL()) {
3509			$this->SetX($this->original_rMargin);
3510			$this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
3511		} else {
3512			$this->SetX($this->original_lMargin);
3513			$this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R');
3514		}
3515	}
3516
3517	/**
3518	 * This method is used to render the page header.
3519	 * @protected
3520	 * @since 4.0.012 (2008-07-24)
3521	 */
3522	protected function setHeader() {
3523		if (!$this->print_header OR ($this->state != 2)) {
3524			return;
3525		}
3526		$this->InHeader = true;
3527		$this->setGraphicVars($this->default_graphic_vars);
3528		$temp_thead = $this->thead;
3529		$temp_theadMargins = $this->theadMargins;
3530		$lasth = $this->lasth;
3531		$newline = $this->newline;
3532		$this->_outSaveGraphicsState();
3533		$this->rMargin = $this->original_rMargin;
3534		$this->lMargin = $this->original_lMargin;
3535		$this->SetCellPadding(0);
3536		//set current position
3537		if ($this->rtl) {
3538			$this->SetXY($this->original_rMargin, $this->header_margin);
3539		} else {
3540			$this->SetXY($this->original_lMargin, $this->header_margin);
3541		}
3542		$this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
3543		$this->Header();
3544		//restore position
3545		if ($this->rtl) {
3546			$this->SetXY($this->original_rMargin, $this->tMargin);
3547		} else {
3548			$this->SetXY($this->original_lMargin, $this->tMargin);
3549		}
3550		$this->_outRestoreGraphicsState();
3551		$this->lasth = $lasth;
3552		$this->thead = $temp_thead;
3553		$this->theadMargins = $temp_theadMargins;
3554		$this->newline = $newline;
3555		$this->InHeader = false;
3556	}
3557
3558	/**
3559	 * This method is used to render the page footer.
3560	 * @protected
3561	 * @since 4.0.012 (2008-07-24)
3562	 */
3563	protected function setFooter() {
3564		if ($this->state != 2) {
3565			return;
3566		}
3567		$this->InFooter = true;
3568		// save current graphic settings
3569		$gvars = $this->getGraphicVars();
3570		// mark this point
3571		$this->footerpos[$this->page] = $this->pagelen[$this->page];
3572		$this->_out("\n");
3573		if ($this->print_footer) {
3574			$this->setGraphicVars($this->default_graphic_vars);
3575			$this->current_column = 0;
3576			$this->num_columns = 1;
3577			$temp_thead = $this->thead;
3578			$temp_theadMargins = $this->theadMargins;
3579			$lasth = $this->lasth;
3580			$this->_outSaveGraphicsState();
3581			$this->rMargin = $this->original_rMargin;
3582			$this->lMargin = $this->original_lMargin;
3583			$this->SetCellPadding(0);
3584			//set current position
3585			$footer_y = $this->h - $this->footer_margin;
3586			if ($this->rtl) {
3587				$this->SetXY($this->original_rMargin, $footer_y);
3588			} else {
3589				$this->SetXY($this->original_lMargin, $footer_y);
3590			}
3591			$this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
3592			$this->Footer();
3593			//restore position
3594			if ($this->rtl) {
3595				$this->SetXY($this->original_rMargin, $this->tMargin);
3596			} else {
3597				$this->SetXY($this->original_lMargin, $this->tMargin);
3598			}
3599			$this->_outRestoreGraphicsState();
3600			$this->lasth = $lasth;
3601			$this->thead = $temp_thead;
3602			$this->theadMargins = $temp_theadMargins;
3603		}
3604		// restore graphic settings
3605		$this->setGraphicVars($gvars);
3606		$this->current_column = $gvars['current_column'];
3607		$this->num_columns = $gvars['num_columns'];
3608		// calculate footer length
3609		$this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
3610		$this->InFooter = false;
3611	}
3612
3613	/**
3614	 * Check if we are on the page body (excluding page header and footer).
3615	 * @return true if we are not in page header nor in page footer, false otherwise.
3616	 * @protected
3617	 * @since 5.9.091 (2011-06-15)
3618	 */
3619	protected function inPageBody() {
3620		return (($this->InHeader === false) AND ($this->InFooter === false));
3621	}
3622
3623	/**
3624	 * This method is used to render the table header on new page (if any).
3625	 * @protected
3626	 * @since 4.5.030 (2009-03-25)
3627	 */
3628	protected function setTableHeader() {
3629		if ($this->num_columns > 1) {
3630			// multi column mode
3631			return;
3632		}
3633		if (isset($this->theadMargins['top'])) {
3634			// restore the original top-margin
3635			$this->tMargin = $this->theadMargins['top'];
3636			$this->pagedim[$this->page]['tm'] = $this->tMargin;
3637			$this->y = $this->tMargin;
3638		}
3639		if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
3640			// set margins
3641			$prev_lMargin = $this->lMargin;
3642			$prev_rMargin = $this->rMargin;
3643			$prev_cell_padding = $this->cell_padding;
3644			$this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']);
3645			$this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']);
3646			$this->cell_padding = $this->theadMargins['cell_padding'];
3647			if ($this->rtl) {
3648				$this->x = $this->w - $this->rMargin;
3649			} else {
3650				$this->x = $this->lMargin;
3651			}
3652			// account for special "cell" mode
3653			if ($this->theadMargins['cell']) {
3654				if ($this->rtl) {
3655					$this->x -= $this->cell_padding['R'];
3656				} else {
3657					$this->x += $this->cell_padding['L'];
3658				}
3659			}
3660			$gvars = $this->getGraphicVars();
3661			if (!empty($this->theadMargins['gvars'])) {
3662				// set the correct graphic style
3663				$this->setGraphicVars($this->theadMargins['gvars']);
3664				$this->rMargin = $gvars['rMargin'];
3665				$this->lMargin = $gvars['lMargin'];
3666			}
3667			// print table header
3668			$this->writeHTML($this->thead, false, false, false, false, '');
3669			$this->setGraphicVars($gvars);
3670			// set new top margin to skip the table headers
3671			if (!isset($this->theadMargins['top'])) {
3672				$this->theadMargins['top'] = $this->tMargin;
3673			}
3674			// store end of header position
3675			if (!isset($this->columns[0]['th'])) {
3676				$this->columns[0]['th'] = array();
3677			}
3678			$this->columns[0]['th']['\''.$this->page.'\''] = $this->y;
3679			$this->tMargin = $this->y;
3680			$this->pagedim[$this->page]['tm'] = $this->tMargin;
3681			$this->lasth = 0;
3682			$this->lMargin = $prev_lMargin;
3683			$this->rMargin = $prev_rMargin;
3684			$this->cell_padding = $prev_cell_padding;
3685		}
3686	}
3687
3688	/**
3689	 * Returns the current page number.
3690	 * @return int page number
3691	 * @public
3692	 * @since 1.0
3693	 * @see getAliasNbPages()
3694	 */
3695	public function PageNo() {
3696		return $this->page;
3697	}
3698
3699	/**
3700	 * Returns the array of spot colors.
3701	 * @return (array) Spot colors array.
3702	 * @public
3703	 * @since 6.0.038 (2013-09-30)
3704	 */
3705	public function getAllSpotColors() {
3706		return $this->spot_colors;
3707	}
3708
3709	/**
3710	 * Defines a new spot color.
3711	 * It can be expressed in RGB components or gray scale.
3712	 * The method can be called before the first page is created and the value is retained from page to page.
3713	 * @param $name (string) Full name of the spot color.
3714	 * @param $c (float) Cyan color for CMYK. Value between 0 and 100.
3715	 * @param $m (float) Magenta color for CMYK. Value between 0 and 100.
3716	 * @param $y (float) Yellow color for CMYK. Value between 0 and 100.
3717	 * @param $k (float) Key (Black) color for CMYK. Value between 0 and 100.
3718	 * @public
3719	 * @since 4.0.024 (2008-09-12)
3720	 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3721	 */
3722	public function AddSpotColor($name, $c, $m, $y, $k) {
3723		if (!isset($this->spot_colors[$name])) {
3724			$i = (1 + count($this->spot_colors));
3725			$this->spot_colors[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i);
3726		}
3727	}
3728
3729	/**
3730	 * Set the spot color for the specified type ('draw', 'fill', 'text').
3731	 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3732	 * @param $name (string) Name of the spot color.
3733	 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3734	 * @return (string) PDF color command.
3735	 * @public
3736	 * @since 5.9.125 (2011-10-03)
3737	 */
3738	public function setSpotColor($type, $name, $tint=100) {
3739		$spotcolor = TCPDF_COLORS::getSpotColor($name, $this->spot_colors);
3740		if ($spotcolor === false) {
3741			$this->Error('Undefined spot color: '.$name.', you must add it using the AddSpotColor() method.');
3742		}
3743		$tint = (max(0, min(100, $tint)) / 100);
3744		$pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']);
3745		switch ($type) {
3746			case 'draw': {
3747				$pdfcolor .= sprintf('CS %F SCN', $tint);
3748				$this->DrawColor = $pdfcolor;
3749				$this->strokecolor = $spotcolor;
3750				break;
3751			}
3752			case 'fill': {
3753				$pdfcolor .= sprintf('cs %F scn', $tint);
3754				$this->FillColor = $pdfcolor;
3755				$this->bgcolor = $spotcolor;
3756				break;
3757			}
3758			case 'text': {
3759				$pdfcolor .= sprintf('cs %F scn', $tint);
3760				$this->TextColor = $pdfcolor;
3761				$this->fgcolor = $spotcolor;
3762				break;
3763			}
3764		}
3765		$this->ColorFlag = ($this->FillColor != $this->TextColor);
3766		if ($this->state == 2) {
3767			$this->_out($pdfcolor);
3768		}
3769		if ($this->inxobj) {
3770			// we are inside an XObject template
3771			$this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name];
3772		}
3773		return $pdfcolor;
3774	}
3775
3776	/**
3777	 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
3778	 * @param $name (string) Name of the spot color.
3779	 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3780	 * @public
3781	 * @since 4.0.024 (2008-09-12)
3782	 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3783	 */
3784	public function SetDrawSpotColor($name, $tint=100) {
3785		$this->setSpotColor('draw', $name, $tint);
3786	}
3787
3788	/**
3789	 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
3790	 * @param $name (string) Name of the spot color.
3791	 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3792	 * @public
3793	 * @since 4.0.024 (2008-09-12)
3794	 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
3795	 */
3796	public function SetFillSpotColor($name, $tint=100) {
3797		$this->setSpotColor('fill', $name, $tint);
3798	}
3799
3800	/**
3801	 * Defines the spot color used for text.
3802	 * @param $name (string) Name of the spot color.
3803	 * @param $tint (int) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3804	 * @public
3805	 * @since 4.0.024 (2008-09-12)
3806	 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
3807	 */
3808	public function SetTextSpotColor($name, $tint=100) {
3809		$this->setSpotColor('text', $name, $tint);
3810	}
3811
3812	/**
3813	 * Set the color array for the specified type ('draw', 'fill', 'text').
3814	 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3815	 * The method can be called before the first page is created and the value is retained from page to page.
3816	 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3817	 * @param $color (array) Array of colors (1=gray, 3=RGB, 4=CMYK or 5=spotcolor=CMYK+name values).
3818	 * @param $ret (boolean) If true do not send the PDF command.
3819	 * @return (string) The PDF command or empty string.
3820	 * @public
3821	 * @since 3.1.000 (2008-06-11)
3822	 */
3823	public function setColorArray($type, $color, $ret=false) {
3824		if (is_array($color)) {
3825			$color = array_values($color);
3826			// component: grey, RGB red or CMYK cyan
3827			$c = isset($color[0]) ? $color[0] : -1;
3828			// component: RGB green or CMYK magenta
3829			$m = isset($color[1]) ? $color[1] : -1;
3830			// component: RGB blue or CMYK yellow
3831			$y = isset($color[2]) ? $color[2] : -1;
3832			// component: CMYK black
3833			$k = isset($color[3]) ? $color[3] : -1;
3834			// color name
3835			$name = isset($color[4]) ? $color[4] : '';
3836			if ($c >= 0) {
3837				return $this->setColor($type, $c, $m, $y, $k, $ret, $name);
3838			}
3839		}
3840		return '';
3841	}
3842
3843	/**
3844	 * Defines the color used for all drawing operations (lines, rectangles and cell borders).
3845	 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3846	 * The method can be called before the first page is created and the value is retained from page to page.
3847	 * @param $color (array) Array of colors (1, 3 or 4 values).
3848	 * @param $ret (boolean) If true do not send the PDF command.
3849	 * @return string the PDF command
3850	 * @public
3851	 * @since 3.1.000 (2008-06-11)
3852	 * @see SetDrawColor()
3853	 */
3854	public function SetDrawColorArray($color, $ret=false) {
3855		return $this->setColorArray('draw', $color, $ret);
3856	}
3857
3858	/**
3859	 * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
3860	 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3861	 * The method can be called before the first page is created and the value is retained from page to page.
3862	 * @param $color (array) Array of colors (1, 3 or 4 values).
3863	 * @param $ret (boolean) If true do not send the PDF command.
3864	 * @public
3865	 * @since 3.1.000 (2008-6-11)
3866	 * @see SetFillColor()
3867	 */
3868	public function SetFillColorArray($color, $ret=false) {
3869		return $this->setColorArray('fill', $color, $ret);
3870	}
3871
3872	/**
3873	 * Defines the color used for text. It can be expressed in RGB components or gray scale.
3874	 * The method can be called before the first page is created and the value is retained from page to page.
3875	 * @param $color (array) Array of colors (1, 3 or 4 values).
3876	 * @param $ret (boolean) If true do not send the PDF command.
3877	 * @public
3878	 * @since 3.1.000 (2008-6-11)
3879	 * @see SetFillColor()
3880	 */
3881	public function SetTextColorArray($color, $ret=false) {
3882		return $this->setColorArray('text', $color, $ret);
3883	}
3884
3885	/**
3886	 * Defines the color used by the specified type ('draw', 'fill', 'text').
3887	 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3888	 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3889	 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3890	 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3891	 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3892	 * @param $ret (boolean) If true do not send the command.
3893	 * @param $name (string) spot color name (if any)
3894	 * @return (string) The PDF command or empty string.
3895	 * @public
3896	 * @since 5.9.125 (2011-10-03)
3897	 */
3898	public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3899		// set default values
3900		if (!is_numeric($col1)) {
3901			$col1 = 0;
3902		}
3903		if (!is_numeric($col2)) {
3904			$col2 = -1;
3905		}
3906		if (!is_numeric($col3)) {
3907			$col3 = -1;
3908		}
3909		if (!is_numeric($col4)) {
3910			$col4 = -1;
3911		}
3912		// set color by case
3913		$suffix = '';
3914		if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
3915			// Grey scale
3916			$col1 = max(0, min(255, $col1));
3917			$intcolor = array('G' => $col1);
3918			$pdfcolor = sprintf('%F ', ($col1 / 255));
3919			$suffix = 'g';
3920		} elseif ($col4 == -1) {
3921			// RGB
3922			$col1 = max(0, min(255, $col1));
3923			$col2 = max(0, min(255, $col2));
3924			$col3 = max(0, min(255, $col3));
3925			$intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
3926			$pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255));
3927			$suffix = 'rg';
3928		} else {
3929			$col1 = max(0, min(100, $col1));
3930			$col2 = max(0, min(100, $col2));
3931			$col3 = max(0, min(100, $col3));
3932			$col4 = max(0, min(100, $col4));
3933			if (empty($name)) {
3934				// CMYK
3935				$intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
3936				$pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
3937				$suffix = 'k';
3938			} else {
3939				// SPOT COLOR
3940				$intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
3941				$this->AddSpotColor($name, $col1, $col2, $col3, $col4);
3942				$pdfcolor = $this->setSpotColor($type, $name, 100);
3943			}
3944		}
3945		switch ($type) {
3946			case 'draw': {
3947				$pdfcolor .= strtoupper($suffix);
3948				$this->DrawColor = $pdfcolor;
3949				$this->strokecolor = $intcolor;
3950				break;
3951			}
3952			case 'fill': {
3953				$pdfcolor .= $suffix;
3954				$this->FillColor = $pdfcolor;
3955				$this->bgcolor = $intcolor;
3956				break;
3957			}
3958			case 'text': {
3959				$pdfcolor .= $suffix;
3960				$this->TextColor = $pdfcolor;
3961				$this->fgcolor = $intcolor;
3962				break;
3963			}
3964		}
3965		$this->ColorFlag = ($this->FillColor != $this->TextColor);
3966		if (($type != 'text') AND ($this->state == 2)) {
3967			if (!$ret) {
3968				$this->_out($pdfcolor);
3969			}
3970			return $pdfcolor;
3971		}
3972		return '';
3973	}
3974
3975	/**
3976	 * 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.
3977	 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3978	 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3979	 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3980	 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3981	 * @param $ret (boolean) If true do not send the command.
3982	 * @param $name (string) spot color name (if any)
3983	 * @return string the PDF command
3984	 * @public
3985	 * @since 1.3
3986	 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
3987	 */
3988	public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3989		return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name);
3990	}
3991
3992	/**
3993	 * 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.
3994	 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3995	 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3996	 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3997	 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3998	 * @param $ret (boolean) If true do not send the command.
3999	 * @param $name (string) Spot color name (if any).
4000	 * @return (string) The PDF command.
4001	 * @public
4002	 * @since 1.3
4003	 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
4004	 */
4005	public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4006		return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name);
4007	}
4008
4009	/**
4010	 * 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.
4011	 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
4012	 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
4013	 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
4014	 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
4015	 * @param $ret (boolean) If true do not send the command.
4016	 * @param $name (string) Spot color name (if any).
4017	 * @return (string) Empty string.
4018	 * @public
4019	 * @since 1.3
4020	 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
4021	 */
4022	public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4023		return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name);
4024	}
4025
4026	/**
4027	 * Returns the length of a string in user unit. A font must be selected.<br>
4028	 * @param $s (string) The string whose length is to be computed
4029	 * @param $fontname (string) 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.
4030	 * @param $fontstyle (string) 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.
4031	 * @param $fontsize (float) Font size in points. The default value is the current size.
4032	 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
4033	 * @return mixed int total string length or array of characted widths
4034	 * @author Nicola Asuni
4035	 * @public
4036	 * @since 1.2
4037	 */
4038	public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4039		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);
4040	}
4041
4042	/**
4043	 * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br>
4044	 * @param $sa (string) The array of chars whose total length is to be computed
4045	 * @param $fontname (string) 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.
4046	 * @param $fontstyle (string) 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.
4047	 * @param $fontsize (float) Font size in points. The default value is the current size.
4048	 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
4049	 * @return mixed int total string length or array of characted widths
4050	 * @author Nicola Asuni
4051	 * @public
4052	 * @since 2.4.000 (2008-03-06)
4053	 */
4054	public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4055		// store current values
4056		if (!TCPDF_STATIC::empty_string($fontname)) {
4057			$prev_FontFamily = $this->FontFamily;
4058			$prev_FontStyle = $this->FontStyle;
4059			$prev_FontSizePt = $this->FontSizePt;
4060			$this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false);
4061		}
4062		// convert UTF-8 array to Latin1 if required
4063		if ($this->isunicode AND (!$this->isUnicodeFont())) {
4064			$sa = TCPDF_FONTS::UTF8ArrToLatin1Arr($sa);
4065		}
4066		$w = 0; // total width
4067		$wa = array(); // array of characters widths
4068		foreach ($sa as $ck => $char) {
4069			// character width
4070			$cw = $this->GetCharWidth($char, isset($sa[($ck + 1)]));
4071			$wa[] = $cw;
4072			$w += $cw;
4073		}
4074		// restore previous values
4075		if (!TCPDF_STATIC::empty_string($fontname)) {
4076			$this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
4077		}
4078		if ($getarray) {
4079			return $wa;
4080		}
4081		return $w;
4082	}
4083
4084	/**
4085	 * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking).
4086	 * @param $char (int) The char code whose length is to be returned
4087	 * @param $notlast (boolean) If false ignore the font-spacing.
4088	 * @return float char width
4089	 * @author Nicola Asuni
4090	 * @public
4091	 * @since 2.4.000 (2008-03-06)
4092	 */
4093	public function GetCharWidth($char, $notlast=true) {
4094		// get raw width
4095		$chw = $this->getRawCharWidth($char);
4096		if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) {
4097			// increase/decrease font spacing
4098			$chw += $this->font_spacing;
4099		}
4100		if ($this->font_stretching != 100) {
4101			// fixed stretching mode
4102			$chw *= ($this->font_stretching / 100);
4103		}
4104		return $chw;
4105	}
4106
4107	/**
4108	 * Returns the length of the char in user unit for the current font.
4109	 * @param $char (int) The char code whose length is to be returned
4110	 * @return float char width
4111	 * @author Nicola Asuni
4112	 * @public
4113	 * @since 5.9.000 (2010-09-28)
4114	 */
4115	public function getRawCharWidth($char) {
4116		if ($char == 173) {
4117			// SHY character will not be printed
4118			return (0);
4119		}
4120		if (isset($this->CurrentFont['cw'][$char])) {
4121			$w = $this->CurrentFont['cw'][$char];
4122		} elseif (isset($this->CurrentFont['dw'])) {
4123			// default width
4124			$w = $this->CurrentFont['dw'];
4125		} elseif (isset($this->CurrentFont['cw'][32])) {
4126			// default width
4127			$w = $this->CurrentFont['cw'][32];
4128		} else {
4129			$w = 600;
4130		}
4131		return $this->getAbsFontMeasure($w);
4132	}
4133
4134	/**
4135	 * Returns the numbero of characters in a string.
4136	 * @param $s (string) The input string.
4137	 * @return int number of characters
4138	 * @public
4139	 * @since 2.0.0001 (2008-01-07)
4140	 */
4141	public function GetNumChars($s) {
4142		if ($this->isUnicodeFont()) {
4143			return count(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont));
4144		}
4145		return strlen($s);
4146	}
4147
4148	/**
4149	 * Fill the list of available fonts ($this->fontlist).
4150	 * @protected
4151	 * @since 4.0.013 (2008-07-28)
4152	 */
4153	protected function getFontsList() {
4154		if (($fontsdir = opendir(TCPDF_FONTS::_getfontpath())) !== false) {
4155			while (($file = readdir($fontsdir)) !== false) {
4156				if (substr($file, -4) == '.php') {
4157					array_push($this->fontlist, strtolower(basename($file, '.php')));
4158				}
4159			}
4160			closedir($fontsdir);
4161		}
4162	}
4163
4164	/**
4165	 * Returns the unicode caracter specified by the value
4166	 * @param $c (int) UTF-8 value
4167	 * @return Returns the specified character.
4168	 * @since 2.3.000 (2008-03-05)
4169	 * @public
4170	 * @deprecated
4171	 */
4172	public function unichr($c) {
4173		return TCPDF_FONTS::unichr($c, $this->isunicode);
4174	}
4175
4176	/**
4177	 * Convert and add the selected TrueType or Type1 font to the fonts folder (that must be writeable).
4178	 * @param $fontfile (string) Font file (full path).
4179	 * @param $fonttype (string) Font type. Leave empty for autodetect mode. Valid values are: TrueTypeUnicode, TrueType, Type1, CID0JP = CID-0 Japanese, CID0KR = CID-0 Korean, CID0CS = CID-0 Chinese Simplified, CID0CT = CID-0 Chinese Traditional.
4180	 * @param $enc (string) Name of the encoding table to use. Leave empty for default mode. Omit this parameter for TrueType Unicode and symbolic fonts like Symbol or ZapfDingBats.
4181	 * @param $flags (int) Unsigned 32-bit integer containing flags specifying various characteristics of the font (PDF32000:2008 - 9.8.2 Font Descriptor Flags): +1 for fixed font; +4 for symbol or +32 for non-symbol; +64 for italic. Fixed and Italic mode are generally autodetected so you have to set it to 32 = non-symbolic font (default) or 4 = symbolic font.
4182	 * @param $outpath (string) Output path for generated font files (must be writeable by the web server). Leave empty for default font folder.
4183	 * @param $platid (int) Platform ID for CMAP table to extract (when building a Unicode font for Windows this value should be 3, for Macintosh should be 1).
4184	 * @param $encid (int) Encoding ID for CMAP table to extract (when building a Unicode font for Windows this value should be 1, for Macintosh should be 0). When Platform ID is 3, legal values for Encoding ID are: 0=Symbol, 1=Unicode, 2=ShiftJIS, 3=PRC, 4=Big5, 5=Wansung, 6=Johab, 7=Reserved, 8=Reserved, 9=Reserved, 10=UCS-4.
4185	 * @param $addcbbox (boolean) If true includes the character bounding box information on the php font file.
4186	 * @return (string) TCPDF font name.
4187	 * @author Nicola Asuni
4188	 * @since 5.9.123 (2010-09-30)
4189	 * @public
4190	 * @deprecated
4191	 */
4192	public function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false) {
4193		return TCPDF_FONTS::addTTFfont($fontfile, $fonttype, $enc, $flags, $outpath, $platid, $encid, $addcbbox);
4194	}
4195
4196	/**
4197	 * Imports a TrueType, Type1, core, or CID0 font and makes it available.
4198	 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
4199	 * 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.
4200	 * @param $family (string) Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
4201	 * @param $style (string) 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>
4202	 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4203	 * @return array containing the font data, or false in case of error.
4204	 * @param $subset (mixed) 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.
4205	 * @public
4206	 * @since 1.5
4207	 * @see SetFont(), setFontSubsetting()
4208	 */
4209	public function AddFont($family, $style='', $fontfile='', $subset='default') {
4210		if ($subset === 'default') {
4211			$subset = $this->font_subsetting;
4212		}
4213		if ($this->pdfa_mode) {
4214			$subset = false;
4215		}
4216		if (TCPDF_STATIC::empty_string($family)) {
4217			if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
4218				$family = $this->FontFamily;
4219			} else {
4220				$this->Error('Empty font family');
4221			}
4222		}
4223		// move embedded styles on $style
4224		if (substr($family, -1) == 'I') {
4225			$style .= 'I';
4226			$family = substr($family, 0, -1);
4227		}
4228		if (substr($family, -1) == 'B') {
4229			$style .= 'B';
4230			$family = substr($family, 0, -1);
4231		}
4232		// normalize family name
4233		$family = strtolower($family);
4234		if ((!$this->isunicode) AND ($family == 'arial')) {
4235			$family = 'helvetica';
4236		}
4237		if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
4238			$style = '';
4239		}
4240		if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) {
4241			// all fonts must be embedded
4242			$family = 'pdfa'.$family;
4243		}
4244		$tempstyle = strtoupper($style);
4245		$style = '';
4246		// underline
4247		if (strpos($tempstyle, 'U') !== false) {
4248			$this->underline = true;
4249		} else {
4250			$this->underline = false;
4251		}
4252		// line-through (deleted)
4253		if (strpos($tempstyle, 'D') !== false) {
4254			$this->linethrough = true;
4255		} else {
4256			$this->linethrough = false;
4257		}
4258		// overline
4259		if (strpos($tempstyle, 'O') !== false) {
4260			$this->overline = true;
4261		} else {
4262			$this->overline = false;
4263		}
4264		// bold
4265		if (strpos($tempstyle, 'B') !== false) {
4266			$style .= 'B';
4267		}
4268		// oblique
4269		if (strpos($tempstyle, 'I') !== false) {
4270			$style .= 'I';
4271		}
4272		$bistyle = $style;
4273		$fontkey = $family.$style;
4274		$font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : '');
4275		$fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
4276		// check if the font has been already added
4277		$fb = $this->getFontBuffer($fontkey);
4278		if ($fb !== false) {
4279			if ($this->inxobj) {
4280				// we are inside an XObject template
4281				$this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i'];
4282			}
4283			return $fontdata;
4284		}
4285		// get specified font directory (if any)
4286		$fontdir = false;
4287		if (!TCPDF_STATIC::empty_string($fontfile)) {
4288			$fontdir = dirname($fontfile);
4289			if (TCPDF_STATIC::empty_string($fontdir) OR ($fontdir == '.')) {
4290				$fontdir = '';
4291			} else {
4292				$fontdir .= '/';
4293			}
4294		}
4295		// true when the font style variation is missing
4296		$missing_style = false;
4297		// search and include font file
4298		if (TCPDF_STATIC::empty_string($fontfile) OR (!@file_exists($fontfile))) {
4299			// build a standard filenames for specified font
4300			$tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
4301			$fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);
4302			if (TCPDF_STATIC::empty_string($fontfile)) {
4303				$missing_style = true;
4304				// try to remove the style part
4305				$tmp_fontfile = str_replace(' ', '', $family).'.php';
4306				$fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);
4307			}
4308		}
4309		// include font file
4310		if (!TCPDF_STATIC::empty_string($fontfile) AND (@file_exists($fontfile))) {
4311			include($fontfile);
4312		} else {
4313			$this->Error('Could not include font definition file: '.$family.'');
4314		}
4315		// check font parameters
4316		if ((!isset($type)) OR (!isset($cw))) {
4317			$this->Error('The font definition file has a bad format: '.$fontfile.'');
4318		}
4319		// SET default parameters
4320		if (!isset($file) OR TCPDF_STATIC::empty_string($file)) {
4321			$file = '';
4322		}
4323		if (!isset($enc) OR TCPDF_STATIC::empty_string($enc)) {
4324			$enc = '';
4325		}
4326		if (!isset($cidinfo) OR TCPDF_STATIC::empty_string($cidinfo)) {
4327			$cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0);
4328			$cidinfo['uni2cid'] = array();
4329		}
4330		if (!isset($ctg) OR TCPDF_STATIC::empty_string($ctg)) {
4331			$ctg = '';
4332		}
4333		if (!isset($desc) OR TCPDF_STATIC::empty_string($desc)) {
4334			$desc = array();
4335		}
4336		if (!isset($up) OR TCPDF_STATIC::empty_string($up)) {
4337			$up = -100;
4338		}
4339		if (!isset($ut) OR TCPDF_STATIC::empty_string($ut)) {
4340			$ut = 50;
4341		}
4342		if (!isset($cw) OR TCPDF_STATIC::empty_string($cw)) {
4343			$cw = array();
4344		}
4345		if (!isset($dw) OR TCPDF_STATIC::empty_string($dw)) {
4346			// set default width
4347			if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
4348				$dw = $desc['MissingWidth'];
4349			} elseif (isset($cw[32])) {
4350				$dw = $cw[32];
4351			} else {
4352				$dw = 600;
4353			}
4354		}
4355		++$this->numfonts;
4356		if ($type == 'core') {
4357			$name = $this->CoreFonts[$fontkey];
4358			$subset = false;
4359		} elseif (($type == 'TrueType') OR ($type == 'Type1')) {
4360			$subset = false;
4361		} elseif ($type == 'TrueTypeUnicode') {
4362			$enc = 'Identity-H';
4363		} elseif ($type == 'cidfont0') {
4364			if ($this->pdfa_mode) {
4365				$this->Error('All fonts must be embedded in PDF/A mode!');
4366			}
4367		} else {
4368			$this->Error('Unknow font type: '.$type.'');
4369		}
4370		// set name if unset
4371		if (!isset($name) OR empty($name)) {
4372			$name = $fontkey;
4373		}
4374		// create artificial font style variations if missing (only works with non-embedded fonts)
4375		if (($type != 'core') AND $missing_style) {
4376			// style variations
4377			$styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
4378			$name .= $styles[$bistyle];
4379			// artificial bold
4380			if (strpos($bistyle, 'B') !== false) {
4381				if (isset($desc['StemV'])) {
4382					// from normal to bold
4383					$desc['StemV'] = round($desc['StemV'] * 1.75);
4384				} else {
4385					// bold
4386					$desc['StemV'] = 123;
4387				}
4388			}
4389			// artificial italic
4390			if (strpos($bistyle, 'I') !== false) {
4391				if (isset($desc['ItalicAngle'])) {
4392					$desc['ItalicAngle'] -= 11;
4393				} else {
4394					$desc['ItalicAngle'] = -11;
4395				}
4396				if (isset($desc['Flags'])) {
4397					$desc['Flags'] |= 64; //bit 7
4398				} else {
4399					$desc['Flags'] = 64;
4400				}
4401			}
4402		}
4403		// check if the array of characters bounding boxes is defined
4404		if (!isset($cbbox)) {
4405			$cbbox = array();
4406		}
4407		// initialize subsetchars
4408		$subsetchars = array_fill(0, 255, true);
4409		$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));
4410		if ($this->inxobj) {
4411			// we are inside an XObject template
4412			$this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts;
4413		}
4414		if (isset($diff) AND (!empty($diff))) {
4415			//Search existing encodings
4416			$d = 0;
4417			$nb = count($this->diffs);
4418			for ($i=1; $i <= $nb; ++$i) {
4419				if ($this->diffs[$i] == $diff) {
4420					$d = $i;
4421					break;
4422				}
4423			}
4424			if ($d == 0) {
4425				$d = $nb + 1;
4426				$this->diffs[$d] = $diff;
4427			}
4428			$this->setFontSubBuffer($fontkey, 'diff', $d);
4429		}
4430		if (!TCPDF_STATIC::empty_string($file)) {
4431			if (!isset($this->FontFiles[$file])) {
4432				if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
4433					$this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4434				} elseif ($type != 'core') {
4435					$this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4436				}
4437			} else {
4438				// update fontkeys that are sharing this font file
4439				$this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset);
4440				if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) {
4441					$this->FontFiles[$file]['fontkeys'][] = $fontkey;
4442				}
4443			}
4444		}
4445		return $fontdata;
4446	}
4447
4448	/**
4449	 * Sets the font used to print character strings.
4450	 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
4451	 * The method can be called before the first page is created and the font is retained from page to page.
4452	 * If you just wish to change the current font size, it is simpler to call SetFontSize().
4453	 * 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 />
4454	 * @param $family (string) 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.
4455	 * @param $style (string) 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.
4456	 * @param $size (float) 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
4457	 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4458	 * @param $subset (mixed) 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.
4459	 * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
4460	 * @author Nicola Asuni
4461	 * @public
4462	 * @since 1.0
4463	 * @see AddFont(), SetFontSize()
4464	 */
4465	public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) {
4466		//Select a font; size given in points
4467		if ($size === null) {
4468			$size = $this->FontSizePt;
4469		}
4470		if ($size < 0) {
4471			$size = 0;
4472		}
4473		// try to add font (if not already added)
4474		$fontdata = $this->AddFont($family, $style, $fontfile, $subset);
4475		$this->FontFamily = $fontdata['family'];
4476		$this->FontStyle = $fontdata['style'];
4477		if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
4478			// save subset chars of the previous font
4479			$this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
4480		}
4481		$this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
4482		$this->SetFontSize($size, $out);
4483	}
4484
4485	/**
4486	 * Defines the size of the current font.
4487	 * @param $size (float) The font size in points.
4488	 * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
4489	 * @public
4490	 * @since 1.0
4491	 * @see SetFont()
4492	 */
4493	public function SetFontSize($size, $out=true) {
4494		// font size in points
4495		$this->FontSizePt = $size;
4496		// font size in user units
4497		$this->FontSize = $size / $this->k;
4498		// calculate some font metrics
4499		if (isset($this->CurrentFont['desc']['FontBBox'])) {
4500			$bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4501			$font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
4502		} else {
4503			$font_height = $size * 1.219;
4504		}
4505		if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
4506			$font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000);
4507		}
4508		if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) {
4509			$font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000);
4510		}
4511		if (!isset($font_ascent) AND !isset($font_descent)) {
4512			// core font
4513			$font_ascent = 0.76 * $font_height;
4514			$font_descent = $font_height - $font_ascent;
4515		} elseif (!isset($font_descent)) {
4516			$font_descent = $font_height - $font_ascent;
4517		} elseif (!isset($font_ascent)) {
4518			$font_ascent = $font_height - $font_descent;
4519		}
4520		$this->FontAscent = ($font_ascent / $this->k);
4521		$this->FontDescent = ($font_descent / $this->k);
4522		if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i'])) AND ($this->state == 2)) {
4523			$this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4524		}
4525	}
4526
4527	/**
4528	 * Returns the bounding box of the current font in user units.
4529	 * @return array
4530	 * @public
4531	 * @since 5.9.152 (2012-03-23)
4532	 */
4533	public function getFontBBox() {
4534		$fbbox = array();
4535		if (isset($this->CurrentFont['desc']['FontBBox'])) {
4536			$tmpbbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4537			$fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox);
4538		} else {
4539			// Find max width
4540			if (isset($this->CurrentFont['desc']['MaxWidth'])) {
4541				$maxw = $this->getAbsFontMeasure(intval($this->CurrentFont['desc']['MaxWidth']));
4542			} else {
4543				$maxw = 0;
4544				if (isset($this->CurrentFont['desc']['MissingWidth'])) {
4545					$maxw = max($maxw, $this->CurrentFont['desc']['MissingWidth']);
4546				}
4547				if (isset($this->CurrentFont['desc']['AvgWidth'])) {
4548					$maxw = max($maxw, $this->CurrentFont['desc']['AvgWidth']);
4549				}
4550				if (isset($this->CurrentFont['dw'])) {
4551					$maxw = max($maxw, $this->CurrentFont['dw']);
4552				}
4553				foreach ($this->CurrentFont['cw'] as $char => $w) {
4554					$maxw = max($maxw, $w);
4555				}
4556				if ($maxw == 0) {
4557					$maxw = 600;
4558				}
4559				$maxw = $this->getAbsFontMeasure($maxw);
4560			}
4561			$fbbox = array(0, (0 - $this->FontDescent), $maxw, $this->FontAscent);
4562		}
4563		return $fbbox;
4564	}
4565
4566	/**
4567	 * Convert a relative font measure into absolute value.
4568	 * @param $s (int) Font measure.
4569	 * @return float Absolute measure.
4570	 * @since 5.9.186 (2012-09-13)
4571	 */
4572	public function getAbsFontMeasure($s) {
4573		return ($s * $this->FontSize / 1000);
4574	}
4575
4576	/**
4577	 * Returns the glyph bounding box of the specified character in the current font in user units.
4578	 * @param $char (int) Input character code.
4579	 * @return mixed array(xMin, yMin, xMax, yMax) or FALSE if not defined.
4580	 * @since 5.9.186 (2012-09-13)
4581	 */
4582	public function getCharBBox($char) {
4583		if (isset($this->CurrentFont['cbbox'][$char])) {
4584			return array_map(array($this,'getAbsFontMeasure'), $this->CurrentFont['cbbox'][intval($char)]);
4585		}
4586		return false;
4587	}
4588
4589	/**
4590	 * Return the font descent value
4591	 * @param $font (string) font name
4592	 * @param $style (string) font style
4593	 * @param $size (float) The size (in points)
4594	 * @return int font descent
4595	 * @public
4596	 * @author Nicola Asuni
4597	 * @since 4.9.003 (2010-03-30)
4598	 */
4599	public function getFontDescent($font, $style='', $size=0) {
4600		$fontdata = $this->AddFont($font, $style);
4601		$fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4602		if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
4603			$descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
4604		} else {
4605			$descent = (1.219 * 0.24 * $size);
4606		}
4607		return ($descent / $this->k);
4608	}
4609
4610	/**
4611	 * Return the font ascent value.
4612	 * @param $font (string) font name
4613	 * @param $style (string) font style
4614	 * @param $size (float) The size (in points)
4615	 * @return int font ascent
4616	 * @public
4617	 * @author Nicola Asuni
4618	 * @since 4.9.003 (2010-03-30)
4619	 */
4620	public function getFontAscent($font, $style='', $size=0) {
4621		$fontdata = $this->AddFont($font, $style);
4622		$fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4623		if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
4624			$ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
4625		} else {
4626			$ascent = 1.219 * 0.76 * $size;
4627		}
4628		return ($ascent / $this->k);
4629	}
4630
4631	/**
4632	 * Return true in the character is present in the specified font.
4633	 * @param $char (mixed) Character to check (integer value or string)
4634	 * @param $font (string) Font name (family name).
4635	 * @param $style (string) Font style.
4636	 * @return (boolean) true if the char is defined, false otherwise.
4637	 * @public
4638	 * @since 5.9.153 (2012-03-28)
4639	 */
4640	public function isCharDefined($char, $font='', $style='') {
4641		if (is_string($char)) {
4642			// get character code
4643			$char = TCPDF_FONTS::UTF8StringToArray($char, $this->isunicode, $this->CurrentFont);
4644			$char = $char[0];
4645		}
4646		if (TCPDF_STATIC::empty_string($font)) {
4647			if (TCPDF_STATIC::empty_string($style)) {
4648				return (isset($this->CurrentFont['cw'][intval($char)]));
4649			}
4650			$font = $this->FontFamily;
4651		}
4652		$fontdata = $this->AddFont($font, $style);
4653		$fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4654		return (isset($fontinfo['cw'][intval($char)]));
4655	}
4656
4657	/**
4658	 * Replace missing font characters on selected font with specified substitutions.
4659	 * @param $text (string) Text to process.
4660	 * @param $font (string) Font name (family name).
4661	 * @param $style (string) Font style.
4662	 * @param $subs (array) 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.
4663	 * @return (string) Processed text.
4664	 * @public
4665	 * @since 5.9.153 (2012-03-28)
4666	 */
4667	public function replaceMissingChars($text, $font='', $style='', $subs=array()) {
4668		if (empty($subs)) {
4669			return $text;
4670		}
4671		if (TCPDF_STATIC::empty_string($font)) {
4672			$font = $this->FontFamily;
4673		}
4674		$fontdata = $this->AddFont($font, $style);
4675		$fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4676		$uniarr = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
4677		foreach ($uniarr as $k => $chr) {
4678			if (!isset($fontinfo['cw'][$chr])) {
4679				// this character is missing on the selected font
4680				if (isset($subs[$chr])) {
4681					// we have available substitutions
4682					if (is_array($subs[$chr])) {
4683						foreach($subs[$chr] as $s) {
4684							if (isset($fontinfo['cw'][$s])) {
4685								$uniarr[$k] = $s;
4686								break;
4687							}
4688						}
4689					} elseif (isset($fontinfo['cw'][$subs[$chr]])) {
4690						$uniarr[$k] = $subs[$chr];
4691					}
4692				}
4693			}
4694		}
4695		return TCPDF_FONTS::UniArrSubString(TCPDF_FONTS::UTF8ArrayToUniArray($uniarr, $this->isunicode));
4696	}
4697
4698	/**
4699	 * Defines the default monospaced font.
4700	 * @param $font (string) Font name.
4701	 * @public
4702	 * @since 4.5.025
4703	 */
4704	public function SetDefaultMonospacedFont($font) {
4705		$this->default_monospaced_font = $font;
4706	}
4707
4708	/**
4709	 * 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 />
4710	 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
4711	 * @public
4712	 * @since 1.5
4713	 * @see Cell(), Write(), Image(), Link(), SetLink()
4714	 */
4715	public function AddLink() {
4716		// create a new internal link
4717		$n = count($this->links) + 1;
4718		$this->links[$n] = array('p' => 0, 'y' => 0, 'f' => false);
4719		return $n;
4720	}
4721
4722	/**
4723	 * Defines the page and position a link points to.
4724	 * @param $link (int) The link identifier returned by AddLink()
4725	 * @param $y (float) Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
4726	 * @param $page (int) 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.
4727	 * @public
4728	 * @since 1.5
4729	 * @see AddLink()
4730	 */
4731	public function SetLink($link, $y=0, $page=-1) {
4732		$fixed = false;
4733		if (!empty($page) AND ($page[0] == '*')) {
4734			$page = intval(substr($page, 1));
4735			// this page number will not be changed when moving/add/deleting pages
4736			$fixed = true;
4737		}
4738		if ($page < 0) {
4739			$page = $this->page;
4740		}
4741		if ($y == -1) {
4742			$y = $this->y;
4743		}
4744		$this->links[$link] = array('p' => $page, 'y' => $y, 'f' => $fixed);
4745	}
4746
4747	/**
4748	 * Puts a link on a rectangular area of the page.
4749	 * 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.
4750	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
4751	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
4752	 * @param $w (float) Width of the rectangle
4753	 * @param $h (float) Height of the rectangle
4754	 * @param $link (mixed) URL or identifier returned by AddLink()
4755	 * @param $spaces (int) number of spaces on the text to link
4756	 * @public
4757	 * @since 1.5
4758	 * @see AddLink(), Annotation(), Cell(), Write(), Image()
4759	 */
4760	public function Link($x, $y, $w, $h, $link, $spaces=0) {
4761		$this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
4762	}
4763
4764	/**
4765	 * Puts a markup annotation on a rectangular area of the page.
4766	 * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!
4767	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
4768	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
4769	 * @param $w (float) Width of the rectangle
4770	 * @param $h (float) Height of the rectangle
4771	 * @param $text (string) annotation text or alternate content
4772	 * @param $opt (array) array of options (see section 8.4 of PDF reference 1.7).
4773	 * @param $spaces (int) number of spaces on the text to link
4774	 * @public
4775	 * @since 4.0.018 (2008-08-06)
4776	 */
4777	public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
4778		if ($this->inxobj) {
4779			// store parameters for later use on template
4780			$this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
4781			return;
4782		}
4783		if ($x === '') {
4784			$x = $this->x;
4785		}
4786		if ($y === '') {
4787			$y = $this->y;
4788		}
4789		// check page for no-write regions and adapt page margins if necessary
4790		list($x, $y) = $this->checkPageRegions($h, $x, $y);
4791		// recalculate coordinates to account for graphic transformations
4792		if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) {
4793			for ($i=$this->transfmatrix_key; $i > 0; --$i) {
4794				$maxid = count($this->transfmatrix[$i]) - 1;
4795				for ($j=$maxid; $j >= 0; --$j) {
4796					$ctm = $this->transfmatrix[$i][$j];
4797					if (isset($ctm['a'])) {
4798						$x = $x * $this->k;
4799						$y = ($this->h - $y) * $this->k;
4800						$w = $w * $this->k;
4801						$h = $h * $this->k;
4802						// top left
4803						$xt = $x;
4804						$yt = $y;
4805						$x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4806						$y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4807						// top right
4808						$xt = $x + $w;
4809						$yt = $y;
4810						$x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4811						$y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4812						// bottom left
4813						$xt = $x;
4814						$yt = $y - $h;
4815						$x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4816						$y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4817						// bottom right
4818						$xt = $x + $w;
4819						$yt = $y - $h;
4820						$x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4821						$y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4822						// new coordinates (rectangle area)
4823						$x = min($x1, $x2, $x3, $x4);
4824						$y = max($y1, $y2, $y3, $y4);
4825						$w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
4826						$h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
4827						$x = $x / $this->k;
4828						$y = $this->h - ($y / $this->k);
4829					}
4830				}
4831			}
4832		}
4833		if ($this->page <= 0) {
4834			$page = 1;
4835		} else {
4836			$page = $this->page;
4837		}
4838		if (!isset($this->PageAnnots[$page])) {
4839			$this->PageAnnots[$page] = array();
4840		}
4841		$this->PageAnnots[$page][] = array('n' => ++$this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
4842		if (!$this->pdfa_mode) {
4843			if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!TCPDF_STATIC::empty_string($opt['FS']))
4844				AND (@file_exists($opt['FS']) OR TCPDF_STATIC::isValidURL($opt['FS']))
4845				AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
4846				$this->embeddedfiles[basename($opt['FS'])] = array('f' => ++$this->n, 'n' => ++$this->n, 'file' => $opt['FS']);
4847			}
4848		}
4849		// Add widgets annotation's icons
4850		if (isset($opt['mk']['i']) AND @file_exists($opt['mk']['i'])) {
4851			$this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
4852		}
4853		if (isset($opt['mk']['ri']) AND @file_exists($opt['mk']['ri'])) {
4854			$this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4855		}
4856		if (isset($opt['mk']['ix']) AND @file_exists($opt['mk']['ix'])) {
4857			$this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4858		}
4859	}
4860
4861	/**
4862	 * Embedd the attached files.
4863	 * @since 4.4.000 (2008-12-07)
4864	 * @protected
4865	 * @see Annotation()
4866	 */
4867	protected function _putEmbeddedFiles() {
4868		if ($this->pdfa_mode) {
4869			// embedded files are not allowed in PDF/A mode
4870			return;
4871		}
4872		reset($this->embeddedfiles);
4873		foreach ($this->embeddedfiles as $filename => $filedata) {
4874			$data = TCPDF_STATIC::fileGetContents($filedata['file']);
4875			if ($data !== FALSE) {
4876				$rawsize = strlen($data);
4877				if ($rawsize > 0) {
4878					// update name tree
4879					$this->efnames[$filename] = $filedata['f'].' 0 R';
4880					// embedded file specification object
4881					$out = $this->_getobj($filedata['f'])."\n";
4882					$out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']).' /EF <</F '.$filedata['n'].' 0 R>> >>';
4883					$out .= "\n".'endobj';
4884					$this->_out($out);
4885					// embedded file object
4886					$filter = '';
4887					if ($this->compress) {
4888						$data = gzcompress($data);
4889						$filter = ' /Filter /FlateDecode';
4890					}
4891					$stream = $this->_getrawstream($data, $filedata['n']);
4892					$out = $this->_getobj($filedata['n'])."\n";
4893					$out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>';
4894					$out .= ' stream'."\n".$stream."\n".'endstream';
4895					$out .= "\n".'endobj';
4896					$this->_out($out);
4897				}
4898			}
4899		}
4900	}
4901
4902	/**
4903	 * Prints a text cell at the specified position.
4904	 * This method allows to place a string precisely on the page.
4905	 * @param $x (float) Abscissa of the cell origin
4906	 * @param $y (float) Ordinate of the cell origin
4907	 * @param $txt (string) String to print
4908	 * @param $fstroke (int) outline size in user units (false = disable)
4909	 * @param $fclip (boolean) if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
4910	 * @param $ffill (boolean) if true fills the text
4911	 * @param $border (mixed) 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)))
4912	 * @param $ln (int) 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.
4913	 * @param $align (string) 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>
4914	 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
4915	 * @param $link (mixed) URL or identifier returned by AddLink().
4916	 * @param $stretch (int) 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.
4917	 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
4918	 * @param $calign (string) 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>
4919	 * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
4920	 * @param $rtloff (boolean) if true uses the page top-left corner as origin of axis for $x and $y initial position.
4921	 * @public
4922	 * @since 1.0
4923	 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()
4924	 */
4925	public function Text($x, $y, $txt, $fstroke=false, $fclip=false, $ffill=true, $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M', $rtloff=false) {
4926		$textrendermode = $this->textrendermode;
4927		$textstrokewidth = $this->textstrokewidth;
4928		$this->setTextRenderingMode($fstroke, $ffill, $fclip);
4929		$this->SetXY($x, $y, $rtloff);
4930		$this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
4931		// restore previous rendering mode
4932		$this->textrendermode = $textrendermode;
4933		$this->textstrokewidth = $textstrokewidth;
4934	}
4935
4936	/**
4937	 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
4938	 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
4939	 * This method is called automatically and should not be called directly by the application.
4940	 * @return boolean
4941	 * @public
4942	 * @since 1.4
4943	 * @see SetAutoPageBreak()
4944	 */
4945	public function AcceptPageBreak() {
4946		if ($this->num_columns > 1) {
4947			// multi column mode
4948			if ($this->current_column < ($this->num_columns - 1)) {
4949				// go to next column
4950				$this->selectColumn($this->current_column + 1);
4951			} elseif ($this->AutoPageBreak) {
4952				// add a new page
4953				$this->AddPage();
4954				// set first column
4955				$this->selectColumn(0);
4956			}
4957			// avoid page breaking from checkPageBreak()
4958			return false;
4959		}
4960		return $this->AutoPageBreak;
4961	}
4962
4963	/**
4964	 * Add page if needed.
4965	 * @param $h (float) Cell height. Default value: 0.
4966	 * @param $y (mixed) starting y position, leave empty for current position.
4967	 * @param $addpage (boolean) if true add a page, otherwise only return the true/false state
4968	 * @return boolean true in case of page break, false otherwise.
4969	 * @since 3.2.000 (2008-07-01)
4970	 * @protected
4971	 */
4972	protected function checkPageBreak($h=0, $y='', $addpage=true) {
4973		if (TCPDF_STATIC::empty_string($y)) {
4974			$y = $this->y;
4975		}
4976		$current_page = $this->page;
4977		if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) {
4978			if ($addpage) {
4979				//Automatic page break
4980				$x = $this->x;
4981				$this->AddPage($this->CurOrientation);
4982				$this->y = $this->tMargin;
4983				$oldpage = $this->page - 1;
4984				if ($this->rtl) {
4985					if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
4986						$this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
4987					} else {
4988						$this->x = $x;
4989					}
4990				} else {
4991					if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
4992						$this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
4993					} else {
4994						$this->x = $x;
4995					}
4996				}
4997			}
4998			return true;
4999		}
5000		if ($current_page != $this->page) {
5001			// account for columns mode
5002			return true;
5003		}
5004		return false;
5005	}
5006
5007	/**
5008	 * 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 />
5009	 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
5010	 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
5011	 * @param $h (float) Cell height. Default value: 0.
5012	 * @param $txt (string) String to print. Default value: empty string.
5013	 * @param $border (mixed) 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)))
5014	 * @param $ln (int) 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.
5015	 * @param $align (string) 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>
5016	 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5017	 * @param $link (mixed) URL or identifier returned by AddLink().
5018	 * @param $stretch (int) 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.
5019	 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5020	 * @param $calign (string) 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>
5021	 * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
5022	 * @public
5023	 * @since 1.0
5024	 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
5025	 */
5026	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') {
5027		$prev_cell_margin = $this->cell_margin;
5028		$prev_cell_padding = $this->cell_padding;
5029		$this->adjustCellPadding($border);
5030		if (!$ignore_min_height) {
5031			$min_cell_height = $this->getCellHeight($this->FontSize);
5032			if ($h < $min_cell_height) {
5033				$h = $min_cell_height;
5034			}
5035		}
5036		$this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']);
5037		// apply text shadow if enabled
5038		if ($this->txtshadow['enabled']) {
5039			// save data
5040			$x = $this->x;
5041			$y = $this->y;
5042			$bc = $this->bgcolor;
5043			$fc = $this->fgcolor;
5044			$sc = $this->strokecolor;
5045			$alpha = $this->alpha;
5046			// print shadow
5047			$this->x += $this->txtshadow['depth_w'];
5048			$this->y += $this->txtshadow['depth_h'];
5049			$this->SetFillColorArray($this->txtshadow['color']);
5050			$this->SetTextColorArray($this->txtshadow['color']);
5051			$this->SetDrawColorArray($this->txtshadow['color']);
5052			if ($this->txtshadow['opacity'] != $alpha['CA']) {
5053				$this->setAlpha($this->txtshadow['opacity'], $this->txtshadow['blend_mode']);
5054			}
5055			if ($this->state == 2) {
5056				$this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5057			}
5058			//restore data
5059			$this->x = $x;
5060			$this->y = $y;
5061			$this->SetFillColorArray($bc);
5062			$this->SetTextColorArray($fc);
5063			$this->SetDrawColorArray($sc);
5064			if ($this->txtshadow['opacity'] != $alpha['CA']) {
5065				$this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']);
5066			}
5067		}
5068		if ($this->state == 2) {
5069			$this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5070		}
5071		$this->cell_padding = $prev_cell_padding;
5072		$this->cell_margin = $prev_cell_margin;
5073	}
5074
5075	/**
5076	 * 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 />
5077	 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
5078	 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
5079	 * @param $h (float) Cell height. Default value: 0.
5080	 * @param $txt (string) String to print. Default value: empty string.
5081	 * @param $border (mixed) 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)))
5082	 * @param $ln (int) 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.
5083	 * @param $align (string) 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>
5084	 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5085	 * @param $link (mixed) URL or identifier returned by AddLink().
5086	 * @param $stretch (int) 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.
5087	 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5088	 * @param $calign (string) 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>
5089	 * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>M : middle</li><li>B : bottom</li></ul>
5090	 * @return string containing cell code
5091	 * @protected
5092	 * @since 1.0
5093	 * @see Cell()
5094	 */
5095	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') {
5096		// replace 'NO-BREAK SPACE' (U+00A0) character with a simple space
5097		$txt = str_replace(TCPDF_FONTS::unichr(160, $this->isunicode), ' ', $txt);
5098		$prev_cell_margin = $this->cell_margin;
5099		$prev_cell_padding = $this->cell_padding;
5100		$txt = TCPDF_STATIC::removeSHY($txt, $this->isunicode);
5101		$rs = ''; //string to be returned
5102		$this->adjustCellPadding($border);
5103		if (!$ignore_min_height) {
5104			$min_cell_height = $this->getCellHeight($this->FontSize);
5105			if ($h < $min_cell_height) {
5106				$h = $min_cell_height;
5107			}
5108		}
5109		$k = $this->k;
5110		// check page for no-write regions and adapt page margins if necessary
5111		list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
5112		if ($this->rtl) {
5113			$x = $this->x - $this->cell_margin['R'];
5114		} else {
5115			$x = $this->x + $this->cell_margin['L'];
5116		}
5117		$y = $this->y + $this->cell_margin['T'];
5118		$prev_font_stretching = $this->font_stretching;
5119		$prev_font_spacing = $this->font_spacing;
5120		// cell vertical alignment
5121		switch ($calign) {
5122			case 'A': {
5123				// font top
5124				switch ($valign) {
5125					case 'T': {
5126						// top
5127						$y -= $this->cell_padding['T'];
5128						break;
5129					}
5130					case 'B': {
5131						// bottom
5132						$y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent);
5133						break;
5134					}
5135					default:
5136					case 'C':
5137					case 'M': {
5138						// center
5139						$y -= (($h - $this->FontAscent - $this->FontDescent) / 2);
5140						break;
5141					}
5142				}
5143				break;
5144			}
5145			case 'L': {
5146				// font baseline
5147				switch ($valign) {
5148					case 'T': {
5149						// top
5150						$y -= ($this->cell_padding['T'] + $this->FontAscent);
5151						break;
5152					}
5153					case 'B': {
5154						// bottom
5155						$y -= ($h - $this->cell_padding['B'] - $this->FontDescent);
5156						break;
5157					}
5158					default:
5159					case 'C':
5160					case 'M': {
5161						// center
5162						$y -= (($h + $this->FontAscent - $this->FontDescent) / 2);
5163						break;
5164					}
5165				}
5166				break;
5167			}
5168			case 'D': {
5169				// font bottom
5170				switch ($valign) {
5171					case 'T': {
5172						// top
5173						$y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent);
5174						break;
5175					}
5176					case 'B': {
5177						// bottom
5178						$y -= ($h - $this->cell_padding['B']);
5179						break;
5180					}
5181					default:
5182					case 'C':
5183					case 'M': {
5184						// center
5185						$y -= (($h + $this->FontAscent + $this->FontDescent) / 2);
5186						break;
5187					}
5188				}
5189				break;
5190			}
5191			case 'B': {
5192				// cell bottom
5193				$y -= $h;
5194				break;
5195			}
5196			case 'C':
5197			case 'M': {
5198				// cell center
5199				$y -= ($h / 2);
5200				break;
5201			}
5202			default:
5203			case 'T': {
5204				// cell top
5205				break;
5206			}
5207		}
5208		// text vertical alignment
5209		switch ($valign) {
5210			case 'T': {
5211				// top
5212				$yt = $y + $this->cell_padding['T'];
5213				break;
5214			}
5215			case 'B': {
5216				// bottom
5217				$yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent;
5218				break;
5219			}
5220			default:
5221			case 'C':
5222			case 'M': {
5223				// center
5224				$yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2);
5225				break;
5226			}
5227		}
5228		$basefonty = $yt + $this->FontAscent;
5229		if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5230			if ($this->rtl) {
5231				$w = $x - $this->lMargin;
5232			} else {
5233				$w = $this->w - $this->rMargin - $x;
5234			}
5235		}
5236		$s = '';
5237		// fill and borders
5238		if (is_string($border) AND (strlen($border) == 4)) {
5239			// full border
5240			$border = 1;
5241		}
5242		if ($fill OR ($border == 1)) {
5243			if ($fill) {
5244				$op = ($border == 1) ? 'B' : 'f';
5245			} else {
5246				$op = 'S';
5247			}
5248			if ($this->rtl) {
5249				$xk = (($x - $w) * $k);
5250			} else {
5251				$xk = ($x * $k);
5252			}
5253			$s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op);
5254		}
5255		// draw borders
5256		$s .= $this->getCellBorder($x, $y, $w, $h, $border);
5257		if ($txt != '') {
5258			$txt2 = $txt;
5259			if ($this->isunicode) {
5260				if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5261					$txt2 = TCPDF_FONTS::UTF8ToLatin1($txt2, $this->isunicode, $this->CurrentFont);
5262				} else {
5263					$unicode = TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont); // array of UTF-8 unicode values
5264					$unicode = TCPDF_FONTS::utf8Bidi($unicode, '', $this->tmprtl, $this->isunicode, $this->CurrentFont);
5265					// replace thai chars (if any)
5266					if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) {
5267						// number of chars
5268						$numchars = count($unicode);
5269						// po pla, for far, for fan
5270						$longtail = array(0x0e1b, 0x0e1d, 0x0e1f);
5271						// do chada, to patak
5272						$lowtail = array(0x0e0e, 0x0e0f);
5273						// mai hun arkad, sara i, sara ii, sara ue, sara uee
5274						$upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37);
5275						// mai ek, mai tho, mai tri, mai chattawa, karan
5276						$tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c);
5277						// sara u, sara uu, pinthu
5278						$lowvowel = array(0x0e38, 0x0e39, 0x0e3a);
5279						$output = array();
5280						for ($i = 0; $i < $numchars; $i++) {
5281							if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) {
5282								$ch0 = $unicode[$i];
5283								$ch1 = ($i > 0) ? $unicode[($i - 1)] : 0;
5284								$ch2 = ($i > 1) ? $unicode[($i - 2)] : 0;
5285								$chn = ($i < ($numchars - 1)) ? $unicode[($i + 1)] : 0;
5286								if (in_array($ch0, $tonemark)) {
5287									if ($chn == 0x0e33) {
5288										// sara um
5289										if (in_array($ch1, $longtail)) {
5290											// tonemark at upper left
5291											$output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5292										} else {
5293											// tonemark at upper right (normal position)
5294											$output[] = $ch0;
5295										}
5296									} elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) {
5297										// tonemark at lower left
5298										$output[] = $this->replaceChar($ch0, (0xf705 + $ch0 - 0x0e48));
5299									} elseif (in_array($ch1, $upvowel)) {
5300										if (in_array($ch2, $longtail)) {
5301											// tonemark at upper left
5302											$output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5303										} else {
5304											// tonemark at upper right (normal position)
5305											$output[] = $ch0;
5306										}
5307									} else {
5308										// tonemark at lower right
5309										$output[] = $this->replaceChar($ch0, (0xf70a + $ch0 - 0x0e48));
5310									}
5311								} elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) {
5312									// add lower left nikhahit and sara aa
5313									if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) {
5314										$output[] = 0xf711;
5315										$this->CurrentFont['subsetchars'][0xf711] = true;
5316										$output[] = 0x0e32;
5317										$this->CurrentFont['subsetchars'][0x0e32] = true;
5318									} else {
5319										$output[] = $ch0;
5320									}
5321								} elseif (in_array($ch1, $longtail)) {
5322									if ($ch0 == 0x0e31) {
5323										// lower left mai hun arkad
5324										$output[] = $this->replaceChar($ch0, 0xf710);
5325									} elseif (in_array($ch0, $upvowel)) {
5326										// lower left
5327										$output[] = $this->replaceChar($ch0, (0xf701 + $ch0 - 0x0e34));
5328									} elseif ($ch0 == 0x0e47) {
5329										// lower left mai tai koo
5330										$output[] = $this->replaceChar($ch0, 0xf712);
5331									} else {
5332										// normal character
5333										$output[] = $ch0;
5334									}
5335								} elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) {
5336									// lower vowel
5337									$output[] = $this->replaceChar($ch0, (0xf718 + $ch0 - 0x0e38));
5338								} elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) {
5339									// yo ying without lower part
5340									$output[] = $this->replaceChar($ch0, 0xf70f);
5341								} elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) {
5342									// tho santan without lower part
5343									$output[] = $this->replaceChar($ch0, 0xf700);
5344								} else {
5345									$output[] = $ch0;
5346								}
5347							} else {
5348								// non-thai character
5349								$output[] = $unicode[$i];
5350							}
5351						}
5352						$unicode = $output;
5353						// update font subsetchars
5354						$this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
5355					} // end of K_THAI_TOPCHARS
5356					$txt2 = TCPDF_FONTS::arrUTF8ToUTF16BE($unicode, false);
5357				}
5358			}
5359			$txt2 = TCPDF_STATIC::_escape($txt2);
5360			// get current text width (considering general font stretching and spacing)
5361			$txwidth = $this->GetStringWidth($txt);
5362			$width = $txwidth;
5363			// check for stretch mode
5364			if ($stretch > 0) {
5365				// calculate ratio between cell width and text width
5366				if ($width <= 0) {
5367					$ratio = 1;
5368				} else {
5369					$ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width);
5370				}
5371				// check if stretching is required
5372				if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) {
5373					// the text will be stretched to fit cell width
5374					if ($stretch > 2) {
5375						// set new character spacing
5376						$this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100));
5377					} else {
5378						// set new horizontal stretching
5379						$this->font_stretching *= $ratio;
5380					}
5381					// recalculate text width (the text fills the entire cell)
5382					$width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5383					// reset alignment
5384					$align = '';
5385				}
5386			}
5387			if ($this->font_stretching != 100) {
5388				// apply font stretching
5389				$rs .= sprintf('BT %F Tz ET ', $this->font_stretching);
5390			}
5391			if ($this->font_spacing != 0) {
5392				// increase/decrease font spacing
5393				$rs .= sprintf('BT %F Tc ET ', ($this->font_spacing * $this->k));
5394			}
5395			if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5396				$s .= 'q '.$this->TextColor.' ';
5397			}
5398			// rendering mode
5399			$s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, ($this->textstrokewidth * $this->k));
5400			// count number of spaces
5401			$ns = substr_count($txt, chr(32));
5402			// Justification
5403			$spacewidth = 0;
5404			if (($align == 'J') AND ($ns > 0)) {
5405				if ($this->isUnicodeFont()) {
5406					// get string width without spaces
5407					$width = $this->GetStringWidth(str_replace(' ', '', $txt));
5408					// calculate average space width
5409					$spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / ($this->FontSize?$this->FontSize:1);
5410					if ($this->font_stretching != 100) {
5411						// word spacing is affected by stretching
5412						$spacewidth /= ($this->font_stretching / 100);
5413					}
5414					// set word position to be used with TJ operator
5415					$txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2);
5416					$unicode_justification = true;
5417				} else {
5418					// get string width
5419					$width = $txwidth;
5420					// new space width
5421					$spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k;
5422					if ($this->font_stretching != 100) {
5423						// word spacing (Tw) is affected by stretching
5424						$spacewidth /= ($this->font_stretching / 100);
5425					}
5426					// set word spacing
5427					$rs .= sprintf('BT %F Tw ET ', $spacewidth);
5428				}
5429				$width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5430			}
5431			// replace carriage return characters
5432			$txt2 = str_replace("\r", ' ', $txt2);
5433			switch ($align) {
5434				case 'C': {
5435					$dx = ($w - $width) / 2;
5436					break;
5437				}
5438				case 'R': {
5439					if ($this->rtl) {
5440						$dx = $this->cell_padding['R'];
5441					} else {
5442						$dx = $w - $width - $this->cell_padding['R'];
5443					}
5444					break;
5445				}
5446				case 'L': {
5447					if ($this->rtl) {
5448						$dx = $w - $width - $this->cell_padding['L'];
5449					} else {
5450						$dx = $this->cell_padding['L'];
5451					}
5452					break;
5453				}
5454				case 'J':
5455				default: {
5456					if ($this->rtl) {
5457						$dx = $this->cell_padding['R'];
5458					} else {
5459						$dx = $this->cell_padding['L'];
5460					}
5461					break;
5462				}
5463			}
5464			if ($this->rtl) {
5465				$xdx = $x - $dx - $width;
5466			} else {
5467				$xdx = $x + $dx;
5468			}
5469			$xdk = $xdx * $k;
5470			// print text
5471			$s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
5472			if (isset($uniblock)) {
5473				// print overlapping characters as separate string
5474				$xshift = 0; // horizontal shift
5475				$ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k);
5476				$spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1));
5477				foreach ($uniblock as $uk => $uniarr) {
5478					if (($uk % 2) == 0) {
5479						// x space to skip
5480						if ($spacewidth != 0) {
5481							// justification shift
5482							$xshift += (count(array_keys($uniarr, 32)) * $spw);
5483						}
5484						$xshift += $this->GetArrStringWidth($uniarr); // + shift justification
5485					} else {
5486						// character to print
5487						$topchr = TCPDF_FONTS::arrUTF8ToUTF16BE($uniarr, false);
5488						$topchr = TCPDF_STATIC::_escape($topchr);
5489						$s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr);
5490					}
5491				}
5492			}
5493			if ($this->underline) {
5494				$s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
5495			}
5496			if ($this->linethrough) {
5497				$s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
5498			}
5499			if ($this->overline) {
5500				$s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
5501			}
5502			if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5503				$s .= ' Q';
5504			}
5505			if ($link) {
5506				$this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns);
5507			}
5508		}
5509		// output cell
5510		if ($s) {
5511			// output cell
5512			$rs .= $s;
5513			if ($this->font_spacing != 0) {
5514				// reset font spacing mode
5515				$rs .= ' BT 0 Tc ET';
5516			}
5517			if ($this->font_stretching != 100) {
5518				// reset font stretching mode
5519				$rs .= ' BT 100 Tz ET';
5520			}
5521		}
5522		// reset word spacing
5523		if (!$this->isUnicodeFont() AND ($align == 'J')) {
5524			$rs .= ' BT 0 Tw ET';
5525		}
5526		// reset stretching and spacing
5527		$this->font_stretching = $prev_font_stretching;
5528		$this->font_spacing = $prev_font_spacing;
5529		$this->lasth = $h;
5530		if ($ln > 0) {
5531			//Go to the beginning of the next line
5532			$this->y = $y + $h + $this->cell_margin['B'];
5533			if ($ln == 1) {
5534				if ($this->rtl) {
5535					$this->x = $this->w - $this->rMargin;
5536				} else {
5537					$this->x = $this->lMargin;
5538				}
5539			}
5540		} else {
5541			// go left or right by case
5542			if ($this->rtl) {
5543				$this->x = $x - $w - $this->cell_margin['L'];
5544			} else {
5545				$this->x = $x + $w + $this->cell_margin['R'];
5546			}
5547		}
5548		$gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
5549		$rs = $gstyles.$rs;
5550		$this->cell_padding = $prev_cell_padding;
5551		$this->cell_margin = $prev_cell_margin;
5552		return $rs;
5553	}
5554
5555	/**
5556	 * Replace a char if is defined on the current font.
5557	 * @param $oldchar (int) Integer code (unicode) of the character to replace.
5558	 * @param $newchar (int) Integer code (unicode) of the new character.
5559	 * @return int the replaced char or the old char in case the new char i not defined
5560	 * @protected
5561	 * @since 5.9.167 (2012-06-22)
5562	 */
5563	protected function replaceChar($oldchar, $newchar) {
5564		if ($this->isCharDefined($newchar)) {
5565			// add the new char on the subset list
5566			$this->CurrentFont['subsetchars'][$newchar] = true;
5567			// return the new character
5568			return $newchar;
5569		}
5570		// return the old char
5571		return $oldchar;
5572	}
5573
5574	/**
5575	 * Returns the code to draw the cell border
5576	 * @param $x (float) X coordinate.
5577	 * @param $y (float) Y coordinate.
5578	 * @param $w (float) Cell width.
5579	 * @param $h (float) Cell height.
5580	 * @param $brd (mixed) 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)))
5581	 * @return string containing cell border code
5582	 * @protected
5583	 * @see SetLineStyle()
5584	 * @since 5.7.000 (2010-08-02)
5585	 */
5586	protected function getCellBorder($x, $y, $w, $h, $brd) {
5587		$s = ''; // string to be returned
5588		if (empty($brd)) {
5589			return $s;
5590		}
5591		if ($brd == 1) {
5592			$brd = array('LRTB' => true);
5593		}
5594		// calculate coordinates for border
5595		$k = $this->k;
5596		if ($this->rtl) {
5597			$xeL = ($x - $w) * $k;
5598			$xeR = $x * $k;
5599		} else {
5600			$xeL = $x * $k;
5601			$xeR = ($x + $w) * $k;
5602		}
5603		$yeL = (($this->h - ($y + $h)) * $k);
5604		$yeT = (($this->h - $y) * $k);
5605		$xeT = $xeL;
5606		$xeB = $xeR;
5607		$yeR = $yeT;
5608		$yeB = $yeL;
5609		if (is_string($brd)) {
5610			// convert string to array
5611			$slen = strlen($brd);
5612			$newbrd = array();
5613			for ($i = 0; $i < $slen; ++$i) {
5614				$newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
5615			}
5616			$brd = $newbrd;
5617		}
5618		if (isset($brd['mode'])) {
5619			$mode = $brd['mode'];
5620			unset($brd['mode']);
5621		} else {
5622			$mode = 'normal';
5623		}
5624		foreach ($brd as $border => $style) {
5625			if (is_array($style) AND !empty($style)) {
5626				// apply border style
5627				$prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' ';
5628				$s .= $this->SetLineStyle($style, true)."\n";
5629			}
5630			switch ($mode) {
5631				case 'ext': {
5632					$off = (($this->LineWidth / 2) * $k);
5633					$xL = $xeL - $off;
5634					$xR = $xeR + $off;
5635					$yT = $yeT + $off;
5636					$yL = $yeL - $off;
5637					$xT = $xL;
5638					$xB = $xR;
5639					$yR = $yT;
5640					$yB = $yL;
5641					$w += $this->LineWidth;
5642					$h += $this->LineWidth;
5643					break;
5644				}
5645				case 'int': {
5646					$off = ($this->LineWidth / 2) * $k;
5647					$xL = $xeL + $off;
5648					$xR = $xeR - $off;
5649					$yT = $yeT - $off;
5650					$yL = $yeL + $off;
5651					$xT = $xL;
5652					$xB = $xR;
5653					$yR = $yT;
5654					$yB = $yL;
5655					$w -= $this->LineWidth;
5656					$h -= $this->LineWidth;
5657					break;
5658				}
5659				case 'normal':
5660				default: {
5661					$xL = $xeL;
5662					$xT = $xeT;
5663					$xB = $xeB;
5664					$xR = $xeR;
5665					$yL = $yeL;
5666					$yT = $yeT;
5667					$yB = $yeB;
5668					$yR = $yeR;
5669					break;
5670				}
5671			}
5672			// draw borders by case
5673			if (strlen($border) == 4) {
5674				$s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k));
5675			} elseif (strlen($border) == 3) {
5676				if (strpos($border,'B') === false) { // LTR
5677					$s .= sprintf('%F %F m ', $xL, $yL);
5678					$s .= sprintf('%F %F l ', $xT, $yT);
5679					$s .= sprintf('%F %F l ', $xR, $yR);
5680					$s .= sprintf('%F %F l ', $xB, $yB);
5681					$s .= 'S ';
5682				} elseif (strpos($border,'L') === false) { // TRB
5683					$s .= sprintf('%F %F m ', $xT, $yT);
5684					$s .= sprintf('%F %F l ', $xR, $yR);
5685					$s .= sprintf('%F %F l ', $xB, $yB);
5686					$s .= sprintf('%F %F l ', $xL, $yL);
5687					$s .= 'S ';
5688				} elseif (strpos($border,'T') === false) { // RBL
5689					$s .= sprintf('%F %F m ', $xR, $yR);
5690					$s .= sprintf('%F %F l ', $xB, $yB);
5691					$s .= sprintf('%F %F l ', $xL, $yL);
5692					$s .= sprintf('%F %F l ', $xT, $yT);
5693					$s .= 'S ';
5694				} elseif (strpos($border,'R') === false) { // BLT
5695					$s .= sprintf('%F %F m ', $xB, $yB);
5696					$s .= sprintf('%F %F l ', $xL, $yL);
5697					$s .= sprintf('%F %F l ', $xT, $yT);
5698					$s .= sprintf('%F %F l ', $xR, $yR);
5699					$s .= 'S ';
5700				}
5701			} elseif (strlen($border) == 2) {
5702				if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
5703					$s .= sprintf('%F %F m ', $xL, $yL);
5704					$s .= sprintf('%F %F l ', $xT, $yT);
5705					$s .= sprintf('%F %F l ', $xR, $yR);
5706					$s .= 'S ';
5707				} elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
5708					$s .= sprintf('%F %F m ', $xT, $yT);
5709					$s .= sprintf('%F %F l ', $xR, $yR);
5710					$s .= sprintf('%F %F l ', $xB, $yB);
5711					$s .= 'S ';
5712				} elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
5713					$s .= sprintf('%F %F m ', $xR, $yR);
5714					$s .= sprintf('%F %F l ', $xB, $yB);
5715					$s .= sprintf('%F %F l ', $xL, $yL);
5716					$s .= 'S ';
5717				} elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
5718					$s .= sprintf('%F %F m ', $xB, $yB);
5719					$s .= sprintf('%F %F l ', $xL, $yL);
5720					$s .= sprintf('%F %F l ', $xT, $yT);
5721					$s .= 'S ';
5722				} elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
5723					$s .= sprintf('%F %F m ', $xL, $yL);
5724					$s .= sprintf('%F %F l ', $xT, $yT);
5725					$s .= 'S ';
5726					$s .= sprintf('%F %F m ', $xR, $yR);
5727					$s .= sprintf('%F %F l ', $xB, $yB);
5728					$s .= 'S ';
5729				} elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
5730					$s .= sprintf('%F %F m ', $xT, $yT);
5731					$s .= sprintf('%F %F l ', $xR, $yR);
5732					$s .= 'S ';
5733					$s .= sprintf('%F %F m ', $xB, $yB);
5734					$s .= sprintf('%F %F l ', $xL, $yL);
5735					$s .= 'S ';
5736				}
5737			} else { // strlen($border) == 1
5738				if (strpos($border,'L') !== false) { // L
5739					$s .= sprintf('%F %F m ', $xL, $yL);
5740					$s .= sprintf('%F %F l ', $xT, $yT);
5741					$s .= 'S ';
5742				} elseif (strpos($border,'T') !== false) { // T
5743					$s .= sprintf('%F %F m ', $xT, $yT);
5744					$s .= sprintf('%F %F l ', $xR, $yR);
5745					$s .= 'S ';
5746				} elseif (strpos($border,'R') !== false) { // R
5747					$s .= sprintf('%F %F m ', $xR, $yR);
5748					$s .= sprintf('%F %F l ', $xB, $yB);
5749					$s .= 'S ';
5750				} elseif (strpos($border,'B') !== false) { // B
5751					$s .= sprintf('%F %F m ', $xB, $yB);
5752					$s .= sprintf('%F %F l ', $xL, $yL);
5753					$s .= 'S ';
5754				}
5755			}
5756			if (is_array($style) AND !empty($style)) {
5757				// reset border style to previous value
5758				$s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n";
5759			}
5760		}
5761		return $s;
5762	}
5763
5764	/**
5765	 * This method allows printing text with line breaks.
5766	 * 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 />
5767	 * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
5768	 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
5769	 * @param $h (float) Cell minimum height. The cell extends automatically if needed.
5770	 * @param $txt (string) String to print
5771	 * @param $border (mixed) 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)))
5772	 * @param $align (string) 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>
5773	 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5774	 * @param $ln (int) 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>
5775	 * @param $x (float) x position in user units
5776	 * @param $y (float) y position in user units
5777	 * @param $reseth (boolean) if true reset the last cell height (default true).
5778	 * @param $stretch (int) 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.
5779	 * @param $ishtml (boolean) 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.
5780	 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
5781	 * @param $maxh (float) 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.
5782	 * @param $valign (string) 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.
5783	 * @param $fitcell (boolean) 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 wqual to $h.
5784	 * @return int Return the number of cells or 1 for html mode.
5785	 * @public
5786	 * @since 1.3
5787	 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
5788	 */
5789	public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $valign='T', $fitcell=false) {
5790		$prev_cell_margin = $this->cell_margin;
5791		$prev_cell_padding = $this->cell_padding;
5792		// adjust internal padding
5793		$this->adjustCellPadding($border);
5794		$mc_padding = $this->cell_padding;
5795		$mc_margin = $this->cell_margin;
5796		$this->cell_padding['T'] = 0;
5797		$this->cell_padding['B'] = 0;
5798		$this->setCellMargins(0, 0, 0, 0);
5799		if (TCPDF_STATIC::empty_string($this->lasth) OR $reseth) {
5800			// reset row height
5801			$this->resetLastH();
5802		}
5803		if (!TCPDF_STATIC::empty_string($y)) {
5804			$this->SetY($y);
5805		} else {
5806			$y = $this->GetY();
5807		}
5808		$resth = 0;
5809		if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) {
5810			// spit cell in more pages/columns
5811			$newh = ($this->PageBreakTrigger - $y);
5812			$resth = ($h - $newh); // cell to be printed on the next page/column
5813			$h = $newh;
5814		}
5815		// get current page number
5816		$startpage = $this->page;
5817		// get current column
5818		$startcolumn = $this->current_column;
5819		if (!TCPDF_STATIC::empty_string($x)) {
5820			$this->SetX($x);
5821		} else {
5822			$x = $this->GetX();
5823		}
5824		// check page for no-write regions and adapt page margins if necessary
5825		list($x, $y) = $this->checkPageRegions(0, $x, $y);
5826		// apply margins
5827		$oy = $y + $mc_margin['T'];
5828		if ($this->rtl) {
5829			$ox = ($this->w - $x - $mc_margin['R']);
5830		} else {
5831			$ox = ($x + $mc_margin['L']);
5832		}
5833		$this->x = $ox;
5834		$this->y = $oy;
5835		// set width
5836		if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5837			if ($this->rtl) {
5838				$w = ($this->x - $this->lMargin - $mc_margin['L']);
5839			} else {
5840				$w = ($this->w - $this->x - $this->rMargin - $mc_margin['R']);
5841			}
5842		}
5843		// store original margin values
5844		$lMargin = $this->lMargin;
5845		$rMargin = $this->rMargin;
5846		if ($this->rtl) {
5847			$this->rMargin = ($this->w - $this->x);
5848			$this->lMargin = ($this->x - $w);
5849		} else {
5850			$this->lMargin = ($this->x);
5851			$this->rMargin = ($this->w - $this->x - $w);
5852		}
5853		$this->clMargin = $this->lMargin;
5854		$this->crMargin = $this->rMargin;
5855		if ($autopadding) {
5856			// add top padding
5857			$this->y += $mc_padding['T'];
5858		}
5859		if ($ishtml) { // ******* Write HTML text
5860			$this->writeHTML($txt, true, false, $reseth, true, $align);
5861			$nl = 1;
5862		} else { // ******* Write simple text
5863			$prev_FontSizePt = $this->FontSizePt;
5864			if ($fitcell) {
5865				// ajust height values
5866				$tobottom = ($this->h - $this->y - $this->bMargin - $this->cell_padding['T'] - $this->cell_padding['B']);
5867				$h = $maxh = max(min($h, $tobottom), min($maxh, $tobottom));
5868			}
5869			// vertical alignment
5870			if ($maxh > 0) {
5871				// get text height
5872				$text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5873				if ($fitcell AND ($text_height > $maxh) AND ($this->FontSizePt > 1)) {
5874					// try to reduce font size to fit text on cell (use a quick search algorithm)
5875					$fmin = 1;
5876					$fmax = $this->FontSizePt;
5877					$diff_epsilon = (1 / $this->k); // one point (min resolution)
5878					$maxit = (2 * min(100, max(10, intval($fmax)))); // max number of iterations
5879					while ($maxit >= 0) {
5880						$fmid = (($fmax + $fmin) / 2);
5881						$this->SetFontSize($fmid, false);
5882						$this->resetLastH();
5883						$text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5884						$diff = ($maxh - $text_height);
5885						if ($diff >= 0) {
5886							if ($diff <= $diff_epsilon) {
5887								break;
5888							}
5889							$fmin = $fmid;
5890						} else {
5891							$fmax = $fmid;
5892						}
5893						--$maxit;
5894					}
5895					if ($maxit < 0) {
5896						// premature exit, we get the minimum font value to fit the cell
5897						$this->SetFontSize($fmin);
5898						$this->resetLastH();
5899						$text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5900					} else {
5901						$this->SetFontSize($fmid);
5902						$this->resetLastH();
5903					}
5904				}
5905				if ($text_height < $maxh) {
5906					if ($valign == 'M') {
5907						// text vertically centered
5908						$this->y += (($maxh - $text_height) / 2);
5909					} elseif ($valign == 'B') {
5910						// text vertically aligned on bottom
5911						$this->y += ($maxh - $text_height);
5912					}
5913				}
5914			}
5915			$nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
5916			if ($fitcell) {
5917				// restore font size
5918				$this->SetFontSize($prev_FontSizePt);
5919			}
5920		}
5921		if ($autopadding) {
5922			// add bottom padding
5923			$this->y += $mc_padding['B'];
5924		}
5925		// Get end-of-text Y position
5926		$currentY = $this->y;
5927		// get latest page number
5928		$endpage = $this->page;
5929		if ($resth > 0) {
5930			$skip = ($endpage - $startpage);
5931			$tmpresth = $resth;
5932			while ($tmpresth > 0) {
5933				if ($skip <= 0) {
5934					// add a page (or trig AcceptPageBreak() for multicolumn mode)
5935					$this->checkPageBreak($this->PageBreakTrigger + 1);
5936				}
5937				if ($this->num_columns > 1) {
5938					$tmpresth -= ($this->h - $this->y - $this->bMargin);
5939				} else {
5940					$tmpresth -= ($this->h - $this->tMargin - $this->bMargin);
5941				}
5942				--$skip;
5943			}
5944			$currentY = $this->y;
5945			$endpage = $this->page;
5946		}
5947		// get latest column
5948		$endcolumn = $this->current_column;
5949		if ($this->num_columns == 0) {
5950			$this->num_columns = 1;
5951		}
5952		// disable page regions check
5953		$check_page_regions = $this->check_page_regions;
5954		$this->check_page_regions = false;
5955		// get border modes
5956		$border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
5957		$border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
5958		$border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
5959		// design borders around HTML cells.
5960		for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
5961			$ccode = '';
5962			$this->setPage($page);
5963			if ($this->num_columns < 2) {
5964				// single-column mode
5965				$this->SetX($x);
5966				$this->y = $this->tMargin;
5967			}
5968			// account for margin changes
5969			if ($page > $startpage) {
5970				if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
5971					$this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
5972				} elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
5973					$this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
5974				}
5975			}
5976			if ($startpage == $endpage) {
5977				// single page
5978				for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
5979					$this->selectColumn($column);
5980					if ($this->rtl) {
5981						$this->x -= $mc_margin['R'];
5982					} else {
5983						$this->x += $mc_margin['L'];
5984					}
5985					if ($startcolumn == $endcolumn) { // single column
5986						$cborder = $border;
5987						$h = max($h, ($currentY - $oy));
5988						$this->y = $oy;
5989					} elseif ($column == $startcolumn) { // first column
5990						$cborder = $border_start;
5991						$this->y = $oy;
5992						$h = $this->h - $this->y - $this->bMargin;
5993					} elseif ($column == $endcolumn) { // end column
5994						$cborder = $border_end;
5995						$h = $currentY - $this->y;
5996						if ($resth > $h) {
5997							$h = $resth;
5998						}
5999					} else { // middle column
6000						$cborder = $border_middle;
6001						$h = $this->h - $this->y - $this->bMargin;
6002						$resth -= $h;
6003					}
6004					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6005				} // end for each column
6006			} elseif ($page == $startpage) { // first page
6007				for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
6008					$this->selectColumn($column);
6009					if ($this->rtl) {
6010						$this->x -= $mc_margin['R'];
6011					} else {
6012						$this->x += $mc_margin['L'];
6013					}
6014					if ($column == $startcolumn) { // first column
6015						$cborder = $border_start;
6016						$this->y = $oy;
6017						$h = $this->h - $this->y - $this->bMargin;
6018					} else { // middle column
6019						$cborder = $border_middle;
6020						$h = $this->h - $this->y - $this->bMargin;
6021						$resth -= $h;
6022					}
6023					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6024				} // end for each column
6025			} elseif ($page == $endpage) { // last page
6026				for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
6027					$this->selectColumn($column);
6028					if ($this->rtl) {
6029						$this->x -= $mc_margin['R'];
6030					} else {
6031						$this->x += $mc_margin['L'];
6032					}
6033					if ($column == $endcolumn) {
6034						// end column
6035						$cborder = $border_end;
6036						$h = $currentY - $this->y;
6037						if ($resth > $h) {
6038							$h = $resth;
6039						}
6040					} else {
6041						// middle column
6042						$cborder = $border_middle;
6043						$h = $this->h - $this->y - $this->bMargin;
6044						$resth -= $h;
6045					}
6046					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6047				} // end for each column
6048			} else { // middle page
6049				for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
6050					$this->selectColumn($column);
6051					if ($this->rtl) {
6052						$this->x -= $mc_margin['R'];
6053					} else {
6054						$this->x += $mc_margin['L'];
6055					}
6056					$cborder = $border_middle;
6057					$h = $this->h - $this->y - $this->bMargin;
6058					$resth -= $h;
6059					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6060				} // end for each column
6061			}
6062			if ($cborder OR $fill) {
6063				$offsetlen = strlen($ccode);
6064				// draw border and fill
6065				if ($this->inxobj) {
6066					// we are inside an XObject template
6067					if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
6068						$pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
6069						$pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
6070						$this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
6071					} else {
6072						$pagemark = $this->xobjects[$this->xobjid]['intmrk'];
6073						$this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
6074					}
6075					$pagebuff = $this->xobjects[$this->xobjid]['outdata'];
6076					$pstart = substr($pagebuff, 0, $pagemark);
6077					$pend = substr($pagebuff, $pagemark);
6078					$this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
6079				} else {
6080					if (end($this->transfmrk[$this->page]) !== false) {
6081						$pagemarkkey = key($this->transfmrk[$this->page]);
6082						$pagemark = $this->transfmrk[$this->page][$pagemarkkey];
6083						$this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
6084					} elseif ($this->InFooter) {
6085						$pagemark = $this->footerpos[$this->page];
6086						$this->footerpos[$this->page] += $offsetlen;
6087					} else {
6088						$pagemark = $this->intmrk[$this->page];
6089						$this->intmrk[$this->page] += $offsetlen;
6090					}
6091					$pagebuff = $this->getPageBuffer($this->page);
6092					$pstart = substr($pagebuff, 0, $pagemark);
6093					$pend = substr($pagebuff, $pagemark);
6094					$this->setPageBuffer($this->page, $pstart.$ccode.$pend);
6095				}
6096			}
6097		} // end for each page
6098		// restore page regions check
6099		$this->check_page_regions = $check_page_regions;
6100		// Get end-of-cell Y position
6101		$currentY = $this->GetY();
6102		// restore previous values
6103		if ($this->num_columns > 1) {
6104			$this->selectColumn();
6105		} else {
6106			// restore original margins
6107			$this->lMargin = $lMargin;
6108			$this->rMargin = $rMargin;
6109			if ($this->page > $startpage) {
6110				// check for margin variations between pages (i.e. booklet mode)
6111				$dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']);
6112				$dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']);
6113				if (($dl != 0) OR ($dr != 0)) {
6114					$this->lMargin += $dl;
6115					$this->rMargin += $dr;
6116				}
6117			}
6118		}
6119		if ($ln > 0) {
6120			//Go to the beginning of the next line
6121			$this->SetY($currentY + $mc_margin['B']);
6122			if ($ln == 2) {
6123				$this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6124			}
6125		} else {
6126			// go left or right by case
6127			$this->setPage($startpage);
6128			$this->y = $y;
6129			$this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6130		}
6131		$this->setContentMark();
6132		$this->cell_padding = $prev_cell_padding;
6133		$this->cell_margin = $prev_cell_margin;
6134		$this->clMargin = $this->lMargin;
6135		$this->crMargin = $this->rMargin;
6136		return $nl;
6137	}
6138
6139	/**
6140	 * This method return the estimated number of lines for print a simple text string using Multicell() method.
6141	 * @param $txt (string) String for calculating his height
6142	 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
6143	 * @param $reseth (boolean) if true reset the last cell height (default false).
6144	 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
6145	 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
6146	 * @param $border (mixed) 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)))
6147	 * @return float Return the minimal height needed for multicell method for printing the $txt param.
6148	 * @author Alexander Escalona Fern\E1ndez, Nicola Asuni
6149	 * @public
6150	 * @since 4.5.011
6151	 */
6152	public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6153		if ($txt === NULL) {
6154			return 0;
6155		}
6156		if ($txt === '') {
6157			// empty string
6158			return 1;
6159		}
6160		// adjust internal padding
6161		$prev_cell_padding = $this->cell_padding;
6162		$prev_lasth = $this->lasth;
6163		if (is_array($cellpadding)) {
6164			$this->cell_padding = $cellpadding;
6165		}
6166		$this->adjustCellPadding($border);
6167		if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
6168			if ($this->rtl) {
6169				$w = $this->x - $this->lMargin;
6170			} else {
6171				$w = $this->w - $this->rMargin - $this->x;
6172			}
6173		}
6174		$wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6175		if ($reseth) {
6176			// reset row height
6177			$this->resetLastH();
6178		}
6179		$lines = 1;
6180		$sum = 0;
6181		$chars = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont), $txt, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6182		$charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
6183		$length = count($chars);
6184		$lastSeparator = -1;
6185		for ($i = 0; $i < $length; ++$i) {
6186			$c = $chars[$i];
6187			$charWidth = $charsWidth[$i];
6188			if (($c != 160)
6189					AND (($c == 173)
6190						OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6191						OR (($c == 45)
6192							AND ($i > 0) AND ($i < ($length - 1))
6193							AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i - 1)], $this->isunicode))
6194							AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6195						)
6196					)
6197				) {
6198				$lastSeparator = $i;
6199			}
6200			if ((($sum + $charWidth) > $wmax) OR ($c == 10)) {
6201				++$lines;
6202				if ($c == 10) {
6203					$lastSeparator = -1;
6204					$sum = 0;
6205				} elseif ($lastSeparator != -1) {
6206					$i = $lastSeparator;
6207					$lastSeparator = -1;
6208					$sum = 0;
6209				} else {
6210					$sum = $charWidth;
6211				}
6212			} else {
6213				$sum += $charWidth;
6214			}
6215		}
6216		if ($chars[($length - 1)] == 10) {
6217			--$lines;
6218		}
6219		$this->cell_padding = $prev_cell_padding;
6220		$this->lasth = $prev_lasth;
6221		return $lines;
6222	}
6223
6224	/**
6225	 * This method return the estimated height needed for printing a simple text string using the Multicell() method.
6226	 * Generally, if you want to know the exact height for a block of content you can use the following alternative technique:
6227	 * @pre
6228	 *  // store current object
6229	 *  $pdf->startTransaction();
6230	 *  // store starting values
6231	 *  $start_y = $pdf->GetY();
6232	 *  $start_page = $pdf->getPage();
6233	 *  // call your printing functions with your parameters
6234	 *  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6235	 *  $pdf->MultiCell($w=0, $h=0, $txt, $border=1, $align='L', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0);
6236	 *  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6237	 *  // get the new Y
6238	 *  $end_y = $pdf->GetY();
6239	 *  $end_page = $pdf->getPage();
6240	 *  // calculate height
6241	 *  $height = 0;
6242	 *  if ($end_page == $start_page) {
6243	 *  	$height = $end_y - $start_y;
6244	 *  } else {
6245	 *  	for ($page=$start_page; $page <= $end_page; ++$page) {
6246	 *  		$this->setPage($page);
6247	 *  		if ($page == $start_page) {
6248	 *  			// first page
6249	 *  			$height = $this->h - $start_y - $this->bMargin;
6250	 *  		} elseif ($page == $end_page) {
6251	 *  			// last page
6252	 *  			$height = $end_y - $this->tMargin;
6253	 *  		} else {
6254	 *  			$height = $this->h - $this->tMargin - $this->bMargin;
6255	 *  		}
6256	 *  	}
6257	 *  }
6258	 *  // restore previous object
6259	 *  $pdf = $pdf->rollbackTransaction();
6260	 *
6261	 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
6262	 * @param $txt (string) String for calculating his height
6263	 * @param $reseth (boolean) if true reset the last cell height (default false).
6264	 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
6265	 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
6266	 * @param $border (mixed) 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)))
6267	 * @return float Return the minimal height needed for multicell method for printing the $txt param.
6268	 * @author Nicola Asuni, Alexander Escalona Fern\E1ndez
6269	 * @public
6270	 */
6271	public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6272		// adjust internal padding
6273		$prev_cell_padding = $this->cell_padding;
6274		$prev_lasth = $this->lasth;
6275		if (is_array($cellpadding)) {
6276			$this->cell_padding = $cellpadding;
6277		}
6278		$this->adjustCellPadding($border);
6279		$lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
6280		$height = $this->getCellHeight(($lines * $this->FontSize), $autopadding);
6281		$this->cell_padding = $prev_cell_padding;
6282		$this->lasth = $prev_lasth;
6283		return $height;
6284	}
6285
6286	/**
6287	 * This method prints text from the current position.<br />
6288	 * @param $h (float) Line height
6289	 * @param $txt (string) String to print
6290	 * @param $link (mixed) URL or identifier returned by AddLink()
6291	 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
6292	 * @param $align (string) 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>
6293	 * @param $ln (boolean) if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
6294	 * @param $stretch (int) 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.
6295	 * @param $firstline (boolean) if true prints only the first line and return the remaining string.
6296	 * @param $firstblock (boolean) if true the string is the starting of a line.
6297	 * @param $maxh (float) maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature.
6298	 * @param $wadj (float) first line width will be reduced by this amount (used in HTML mode).
6299	 * @param $margin (array) margin array of the parent container
6300	 * @return mixed Return the number of cells or the remaining string if $firstline = true.
6301	 * @public
6302	 * @since 1.5
6303	 */
6304	public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
6305		// check page for no-write regions and adapt page margins if necessary
6306		list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
6307		if (strlen($txt) == 0) {
6308			// fix empty text
6309			$txt = ' ';
6310		}
6311		if ($margin === '') {
6312			// set default margins
6313			$margin = $this->cell_margin;
6314		}
6315		// remove carriage returns
6316		$s = str_replace("\r", '', $txt);
6317		// check if string contains arabic text
6318		if (preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_ARABIC, $s)) {
6319			$arabic = true;
6320		} else {
6321			$arabic = false;
6322		}
6323		// check if string contains RTL text
6324		if ($arabic OR ($this->tmprtl == 'R') OR preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $s)) {
6325			$rtlmode = true;
6326		} else {
6327			$rtlmode = false;
6328		}
6329		// get a char width
6330		$chrwidth = $this->GetCharWidth(46); // dot character
6331		// get array of unicode values
6332		$chars = TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont);
6333		// calculate maximum width for a single character on string
6334		$chrw = $this->GetArrStringWidth($chars, '', '', 0, true);
6335		array_walk($chrw, array($this, 'getRawCharWidth'));
6336		$maxchwidth = max($chrw);
6337		// get array of chars
6338		$uchars = TCPDF_FONTS::UTF8ArrayToUniArray($chars, $this->isunicode);
6339		// get the number of characters
6340		$nb = count($chars);
6341		// replacement for SHY character (minus symbol)
6342		$shy_replacement = 45;
6343		$shy_replacement_char = TCPDF_FONTS::unichr($shy_replacement, $this->isunicode);
6344		// widht for SHY replacement
6345		$shy_replacement_width = $this->GetCharWidth($shy_replacement);
6346		// max Y
6347		$maxy = $this->y + $maxh - $h - $this->cell_padding['T'] - $this->cell_padding['B'];
6348		// page width
6349		$pw = $w = $this->w - $this->lMargin - $this->rMargin;
6350		// calculate remaining line width ($w)
6351		if ($this->rtl) {
6352			$w = $this->x - $this->lMargin;
6353		} else {
6354			$w = $this->w - $this->rMargin - $this->x;
6355		}
6356		// max column width
6357		$wmax = ($w - $wadj);
6358		if (!$firstline) {
6359			$wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']);
6360		}
6361		if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) {
6362			// the maximum width character do not fit on column
6363			return '';
6364		}
6365		// minimum row height
6366		$row_height = max($h, $this->getCellHeight($this->FontSize));
6367		$start_page = $this->page;
6368		$i = 0; // character position
6369		$j = 0; // current starting position
6370		$sep = -1; // position of the last blank space
6371		$prevsep = $sep; // previous separator
6372		$shy = false; // true if the last blank is a soft hypen (SHY)
6373		$prevshy = $shy; // previous shy mode
6374		$l = 0; // current string length
6375		$nl = 0; //number of lines
6376		$linebreak = false;
6377		$pc = 0; // previous character
6378		// for each character
6379		while ($i < $nb) {
6380			if (($maxh > 0) AND ($this->y >= $maxy) ) {
6381				break;
6382			}
6383			//Get the current character
6384			$c = $chars[$i];
6385			if ($c == 10) { // 10 = "\n" = new line
6386				//Explicit line break
6387				if ($align == 'J') {
6388					if ($this->rtl) {
6389						$talign = 'R';
6390					} else {
6391						$talign = 'L';
6392					}
6393				} else {
6394					$talign = $align;
6395				}
6396				$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6397				if ($firstline) {
6398					$startx = $this->x;
6399					$tmparr = array_slice($chars, $j, ($i - $j));
6400					if ($rtlmode) {
6401						$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6402					}
6403					$linew = $this->GetArrStringWidth($tmparr);
6404					unset($tmparr);
6405					if ($this->rtl) {
6406						$this->endlinex = $startx - $linew;
6407					} else {
6408						$this->endlinex = $startx + $linew;
6409					}
6410					$w = $linew;
6411					$tmpcellpadding = $this->cell_padding;
6412					if ($maxh == 0) {
6413						$this->SetCellPadding(0);
6414					}
6415				}
6416				if ($firstblock AND $this->isRTLTextDir()) {
6417					$tmpstr = $this->stringRightTrim($tmpstr);
6418				}
6419				// Skip newlines at the begining of a page or column
6420				if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) {
6421					$this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
6422				}
6423				unset($tmpstr);
6424				if ($firstline) {
6425					$this->cell_padding = $tmpcellpadding;
6426					return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6427				}
6428				++$nl;
6429				$j = $i + 1;
6430				$l = 0;
6431				$sep = -1;
6432				$prevsep = $sep;
6433				$shy = false;
6434				// account for margin changes
6435				if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6436					$this->AcceptPageBreak();
6437					if ($this->rtl) {
6438						$this->x -= $margin['R'];
6439					} else {
6440						$this->x += $margin['L'];
6441					}
6442					$this->lMargin += $margin['L'];
6443					$this->rMargin += $margin['R'];
6444				}
6445				$w = $this->getRemainingWidth();
6446				$wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']);
6447			} else {
6448				// 160 is the non-breaking space.
6449				// 173 is SHY (Soft Hypen).
6450				// \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
6451				// \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
6452				// \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
6453				if (($c != 160)
6454					AND (($c == 173)
6455						OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6456						OR (($c == 45)
6457							AND ($i < ($nb - 1))
6458							AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($pc, $this->isunicode))
6459							AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6460						)
6461					)
6462				) {
6463					// update last blank space position
6464					$prevsep = $sep;
6465					$sep = $i;
6466					// check if is a SHY
6467					if (($c == 173) OR ($c == 45)) {
6468						$prevshy = $shy;
6469						$shy = true;
6470						if ($pc == 45) {
6471							$tmp_shy_replacement_width = 0;
6472							$tmp_shy_replacement_char = '';
6473						} else {
6474							$tmp_shy_replacement_width = $shy_replacement_width;
6475							$tmp_shy_replacement_char = $shy_replacement_char;
6476						}
6477					} else {
6478						$shy = false;
6479					}
6480				}
6481				// update string length
6482				if ($this->isUnicodeFont() AND ($arabic)) {
6483					// with bidirectional algorithm some chars may be changed affecting the line length
6484					// *** very slow ***
6485					$l = $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl, $this->isunicode, $this->CurrentFont));
6486				} else {
6487					$l += $this->GetCharWidth($c);
6488				}
6489				if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) >= $wmax))) {
6490					if (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) {
6491						$sep = $prevsep;
6492						$shy = $prevshy;
6493					}
6494					// we have reached the end of column
6495					if ($sep == -1) {
6496						// check if the line was already started
6497						if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth)))
6498							OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) {
6499							// print a void cell and go to next line
6500							$this->Cell($w, $h, '', 0, 1);
6501							$linebreak = true;
6502							if ($firstline) {
6503								return (TCPDF_FONTS::UniArrSubString($uchars, $j));
6504							}
6505						} else {
6506							// truncate the word because do not fit on column
6507							$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6508							if ($firstline) {
6509								$startx = $this->x;
6510								$tmparr = array_slice($chars, $j, ($i - $j));
6511								if ($rtlmode) {
6512									$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6513								}
6514								$linew = $this->GetArrStringWidth($tmparr);
6515								unset($tmparr);
6516								if ($this->rtl) {
6517									$this->endlinex = $startx - $linew;
6518								} else {
6519									$this->endlinex = $startx + $linew;
6520								}
6521								$w = $linew;
6522								$tmpcellpadding = $this->cell_padding;
6523								if ($maxh == 0) {
6524									$this->SetCellPadding(0);
6525								}
6526							}
6527							if ($firstblock AND $this->isRTLTextDir()) {
6528								$tmpstr = $this->stringRightTrim($tmpstr);
6529							}
6530							$this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6531							unset($tmpstr);
6532							if ($firstline) {
6533								$this->cell_padding = $tmpcellpadding;
6534								return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6535							}
6536							$j = $i;
6537							--$i;
6538						}
6539					} else {
6540						// word wrapping
6541						if ($this->rtl AND (!$firstblock) AND ($sep < $i)) {
6542							$endspace = 1;
6543						} else {
6544							$endspace = 0;
6545						}
6546						// check the length of the next string
6547						$strrest = TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace));
6548						$nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $this->stringTrim($strrest));
6549						if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
6550							// truncate the word because do not fit on a full page width
6551							$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6552							if ($firstline) {
6553								$startx = $this->x;
6554								$tmparr = array_slice($chars, $j, ($i - $j));
6555								if ($rtlmode) {
6556									$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6557								}
6558								$linew = $this->GetArrStringWidth($tmparr);
6559								unset($tmparr);
6560								if ($this->rtl) {
6561									$this->endlinex = ($startx - $linew);
6562								} else {
6563									$this->endlinex = ($startx + $linew);
6564								}
6565								$w = $linew;
6566								$tmpcellpadding = $this->cell_padding;
6567								if ($maxh == 0) {
6568									$this->SetCellPadding(0);
6569								}
6570							}
6571							if ($firstblock AND $this->isRTLTextDir()) {
6572								$tmpstr = $this->stringRightTrim($tmpstr);
6573							}
6574							$this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6575							unset($tmpstr);
6576							if ($firstline) {
6577								$this->cell_padding = $tmpcellpadding;
6578								return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6579							}
6580							$j = $i;
6581							--$i;
6582						} else {
6583							// word wrapping
6584							if ($shy) {
6585								// add hypen (minus symbol) at the end of the line
6586								$shy_width = $tmp_shy_replacement_width;
6587								if ($this->rtl) {
6588									$shy_char_left = $tmp_shy_replacement_char;
6589									$shy_char_right = '';
6590								} else {
6591									$shy_char_left = '';
6592									$shy_char_right = $tmp_shy_replacement_char;
6593								}
6594							} else {
6595								$shy_width = 0;
6596								$shy_char_left = '';
6597								$shy_char_right = '';
6598							}
6599							$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, ($sep + $endspace));
6600							if ($firstline) {
6601								$startx = $this->x;
6602								$tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
6603								if ($rtlmode) {
6604									$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6605								}
6606								$linew = $this->GetArrStringWidth($tmparr);
6607								unset($tmparr);
6608								if ($this->rtl) {
6609									$this->endlinex = $startx - $linew - $shy_width;
6610								} else {
6611									$this->endlinex = $startx + $linew + $shy_width;
6612								}
6613								$w = $linew;
6614								$tmpcellpadding = $this->cell_padding;
6615								if ($maxh == 0) {
6616									$this->SetCellPadding(0);
6617								}
6618							}
6619							// print the line
6620							if ($firstblock AND $this->isRTLTextDir()) {
6621								$tmpstr = $this->stringRightTrim($tmpstr);
6622							}
6623							$this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
6624							unset($tmpstr);
6625							if ($firstline) {
6626								if ($chars[$sep] == 45) {
6627									$endspace += 1;
6628								}
6629								// return the remaining text
6630								$this->cell_padding = $tmpcellpadding;
6631								return (TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace)));
6632							}
6633							$i = $sep;
6634							$sep = -1;
6635							$shy = false;
6636							$j = ($i + 1);
6637						}
6638					}
6639					// account for margin changes
6640					if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6641						$this->AcceptPageBreak();
6642						if ($this->rtl) {
6643							$this->x -= $margin['R'];
6644						} else {
6645							$this->x += $margin['L'];
6646						}
6647						$this->lMargin += $margin['L'];
6648						$this->rMargin += $margin['R'];
6649					}
6650					$w = $this->getRemainingWidth();
6651					$wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6652					if ($linebreak) {
6653						$linebreak = false;
6654					} else {
6655						++$nl;
6656						$l = 0;
6657					}
6658				}
6659			}
6660			// save last character
6661			$pc = $c;
6662			++$i;
6663		} // end while i < nb
6664		// print last substring (if any)
6665		if ($l > 0) {
6666			switch ($align) {
6667				case 'J':
6668				case 'C': {
6669					$w = $w;
6670					break;
6671				}
6672				case 'L': {
6673					if ($this->rtl) {
6674						$w = $w;
6675					} else {
6676						$w = $l;
6677					}
6678					break;
6679				}
6680				case 'R': {
6681					if ($this->rtl) {
6682						$w = $l;
6683					} else {
6684						$w = $w;
6685					}
6686					break;
6687				}
6688				default: {
6689					$w = $l;
6690					break;
6691				}
6692			}
6693			$tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $nb);
6694			if ($firstline) {
6695				$startx = $this->x;
6696				$tmparr = array_slice($chars, $j, ($nb - $j));
6697				if ($rtlmode) {
6698					$tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6699				}
6700				$linew = $this->GetArrStringWidth($tmparr);
6701				unset($tmparr);
6702				if ($this->rtl) {
6703					$this->endlinex = $startx - $linew;
6704				} else {
6705					$this->endlinex = $startx + $linew;
6706				}
6707				$w = $linew;
6708				$tmpcellpadding = $this->cell_padding;
6709				if ($maxh == 0) {
6710					$this->SetCellPadding(0);
6711				}
6712			}
6713			if ($firstblock AND $this->isRTLTextDir()) {
6714				$tmpstr = $this->stringRightTrim($tmpstr);
6715			}
6716			$this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
6717			unset($tmpstr);
6718			if ($firstline) {
6719				$this->cell_padding = $tmpcellpadding;
6720				return (TCPDF_FONTS::UniArrSubString($uchars, $nb));
6721			}
6722			++$nl;
6723		}
6724		if ($firstline) {
6725			return '';
6726		}
6727		return $nl;
6728	}
6729
6730	/**
6731	 * Returns the remaining width between the current position and margins.
6732	 * @return int Return the remaining width
6733	 * @protected
6734	 */
6735	protected function getRemainingWidth() {
6736		list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y);
6737		if ($this->rtl) {
6738			return ($this->x - $this->lMargin);
6739		} else {
6740			return ($this->w - $this->rMargin - $this->x);
6741		}
6742	}
6743
6744	/**
6745	 * Set the block dimensions accounting for page breaks and page/column fitting
6746	 * @param $w (float) width
6747	 * @param $h (float) height
6748	 * @param $x (float) X coordinate
6749	 * @param $y (float) Y coodiante
6750	 * @param $fitonpage (boolean) if true the block is resized to not exceed page dimensions.
6751	 * @return array($w, $h, $x, $y)
6752	 * @protected
6753	 * @since 5.5.009 (2010-07-05)
6754	 */
6755	protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
6756		if ($w <= 0) {
6757			// set maximum width
6758			$w = ($this->w - $this->lMargin - $this->rMargin);
6759			if ($w <= 0) {
6760				$w = 1;
6761			}
6762		}
6763		if ($h <= 0) {
6764			// set maximum height
6765			$h = ($this->PageBreakTrigger - $this->tMargin);
6766			if ($h <= 0) {
6767				$h = 1;
6768			}
6769		}
6770		// resize the block to be vertically contained on a single page or single column
6771		if ($fitonpage OR $this->AutoPageBreak) {
6772			$ratio_wh = ($w / $h);
6773			if ($h > ($this->PageBreakTrigger - $this->tMargin)) {
6774				$h = $this->PageBreakTrigger - $this->tMargin;
6775				$w = ($h * $ratio_wh);
6776			}
6777			// resize the block to be horizontally contained on a single page or single column
6778			if ($fitonpage) {
6779				$maxw = ($this->w - $this->lMargin - $this->rMargin);
6780				if ($w > $maxw) {
6781					$w = $maxw;
6782					$h = ($w / $ratio_wh);
6783				}
6784			}
6785		}
6786		// Check whether we need a new page or new column first as this does not fit
6787		$prev_x = $this->x;
6788		$prev_y = $this->y;
6789		if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) {
6790			$y = $this->y;
6791			if ($this->rtl) {
6792				$x += ($prev_x - $this->x);
6793			} else {
6794				$x += ($this->x - $prev_x);
6795			}
6796			$this->newline = true;
6797		}
6798		// resize the block to be contained on the remaining available page or column space
6799		if ($fitonpage) {
6800			$ratio_wh = ($w / $h);
6801			if (($y + $h) > $this->PageBreakTrigger) {
6802				$h = $this->PageBreakTrigger - $y;
6803				$w = ($h * $ratio_wh);
6804			}
6805			if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
6806				$w = $this->w - $this->rMargin - $x;
6807				$h = ($w / $ratio_wh);
6808			} elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
6809				$w = $x - $this->lMargin;
6810				$h = ($w / $ratio_wh);
6811			}
6812		}
6813		return array($w, $h, $x, $y);
6814	}
6815
6816	/**
6817	 * Puts an image in the page.
6818	 * The upper-left corner must be given.
6819	 * The dimensions can be specified in different ways:<ul>
6820	 * <li>explicit width and height (expressed in user unit)</li>
6821	 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
6822	 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
6823	 * 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;
6824	 * The format can be specified explicitly or inferred from the file extension.<br />
6825	 * It is possible to put a link on the image.<br />
6826	 * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
6827	 * @param $file (string) 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').
6828	 * @param $x (float) Abscissa of the upper-left corner (LTR) or upper-right corner (RTL).
6829	 * @param $y (float) Ordinate of the upper-left corner (LTR) or upper-right corner (RTL).
6830	 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
6831	 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
6832	 * @param $type (string) 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.
6833	 * @param $link (mixed) URL or identifier returned by AddLink().
6834	 * @param $align (string) 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>
6835	 * @param $resize (mixed) 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).
6836	 * @param $dpi (int) dot-per-inch resolution used on resize
6837	 * @param $palign (string) 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>
6838	 * @param $ismask (boolean) true if this image is a mask, false otherwise
6839	 * @param $imgmask (mixed) image object returned by this function or false
6840	 * @param $border (mixed) 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)))
6841	 * @param $fitbox (mixed) 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).
6842	 * @param $hidden (boolean) If true do not display the image.
6843	 * @param $fitonpage (boolean) If true the image is resized to not exceed page dimensions.
6844	 * @param $alt (boolean) If true the image will be added as alternative and not directly printed (the ID of the image will be returned).
6845	 * @param $altimgs (array) 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.
6846	 * @return image information
6847	 * @public
6848	 * @since 1.1
6849	 */
6850	public function Image($file, $x='', $y='', $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()) {
6851		if ($this->state != 2) {
6852			return;
6853		}
6854		if ($x === '') {
6855			$x = $this->x;
6856		}
6857		if ($y === '') {
6858			$y = $this->y;
6859		}
6860		// check page for no-write regions and adapt page margins if necessary
6861		list($x, $y) = $this->checkPageRegions($h, $x, $y);
6862		$exurl = ''; // external streams
6863		$imsize = FALSE;
6864		// check if we are passing an image as file or string
6865		if ($file[0] === '@') {
6866			// image from string
6867			$imgdata = substr($file, 1);
6868		} else { // image file
6869			if ($file[0] === '*') {
6870				// image as external stream
6871				$file = substr($file, 1);
6872				$exurl = $file;
6873			}
6874			// check if is a local file
6875			if (!@file_exists($file)) {
6876				// try to encode spaces on filename
6877				$tfile = str_replace(' ', '%20', $file);
6878				if (@file_exists($tfile)) {
6879					$file = $tfile;
6880				}
6881			}
6882			if (($imsize = @getimagesize($file)) === FALSE) {
6883				if (in_array($file, $this->imagekeys)) {
6884					// get existing image data
6885					$info = $this->getImageBuffer($file);
6886					$imsize = array($info['w'], $info['h']);
6887				} elseif (strpos($file, '__tcpdf_img') === FALSE) {
6888					$imgdata = TCPDF_STATIC::fileGetContents($file);
6889				}
6890			}
6891		}
6892		if (!empty($imgdata)) {
6893			// copy image to cache
6894			$original_file = $file;
6895			$file = TCPDF_STATIC::getObjFilename('img');
6896			$fp = fopen($file, 'w');
6897			if (!$fp) {
6898				$this->Error('Unable to write file: '.$file);
6899			}
6900			fwrite($fp, $imgdata);
6901			fclose($fp);
6902			unset($imgdata);
6903			$imsize = @getimagesize($file);
6904			if ($imsize === FALSE) {
6905				unlink($file);
6906				$file = $original_file;
6907			} else {
6908				$this->cached_files[] = $file;
6909			}
6910		}
6911		if ($imsize === FALSE) {
6912			if (($w > 0) AND ($h > 0)) {
6913				// get measures from specified data
6914				$pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6915				$ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6916				$imsize = array($pw, $ph);
6917			} else {
6918				$this->Error('[Image] Unable to get the size of the image: '.$file);
6919			}
6920		}
6921		// file hash
6922		$filehash = md5($this->file_id.$file);
6923		// get original image width and height in pixels
6924		list($pixw, $pixh) = $imsize;
6925		// calculate image width and height on document
6926		if (($w <= 0) AND ($h <= 0)) {
6927			// convert image size to document unit
6928			$w = $this->pixelsToUnits($pixw);
6929			$h = $this->pixelsToUnits($pixh);
6930		} elseif ($w <= 0) {
6931			$w = $h * $pixw / $pixh;
6932		} elseif ($h <= 0) {
6933			$h = $w * $pixh / $pixw;
6934		} elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
6935			if (strlen($fitbox) !== 2) {
6936				// set default alignment
6937				$fitbox = '--';
6938			}
6939			// scale image dimensions proportionally to fit within the ($w, $h) box
6940			if ((($w * $pixh) / ($h * $pixw)) < 1) {
6941				// store current height
6942				$oldh = $h;
6943				// calculate new height
6944				$h = $w * $pixh / $pixw;
6945				// height difference
6946				$hdiff = ($oldh - $h);
6947				// vertical alignment
6948				switch (strtoupper($fitbox[1])) {
6949					case 'T': {
6950						break;
6951					}
6952					case 'M': {
6953						$y += ($hdiff / 2);
6954						break;
6955					}
6956					case 'B': {
6957						$y += $hdiff;
6958						break;
6959					}
6960				}
6961			} else {
6962				// store current width
6963				$oldw = $w;
6964				// calculate new width
6965				$w = $h * $pixw / $pixh;
6966				// width difference
6967				$wdiff = ($oldw - $w);
6968				// horizontal alignment
6969				switch (strtoupper($fitbox[0])) {
6970					case 'L': {
6971						if ($this->rtl) {
6972							$x -= $wdiff;
6973						}
6974						break;
6975					}
6976					case 'C': {
6977						if ($this->rtl) {
6978							$x -= ($wdiff / 2);
6979						} else {
6980							$x += ($wdiff / 2);
6981						}
6982						break;
6983					}
6984					case 'R': {
6985						if (!$this->rtl) {
6986							$x += $wdiff;
6987						}
6988						break;
6989					}
6990				}
6991			}
6992		}
6993		// fit the image on available space
6994		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
6995		// calculate new minimum dimensions in pixels
6996		$neww = round($w * $this->k * $dpi / $this->dpi);
6997		$newh = round($h * $this->k * $dpi / $this->dpi);
6998		// check if resize is necessary (resize is used only to reduce the image)
6999		$newsize = ($neww * $newh);
7000		$pixsize = ($pixw * $pixh);
7001		if (intval($resize) == 2) {
7002			$resize = true;
7003		} elseif ($newsize >= $pixsize) {
7004			$resize = false;
7005		}
7006		// check if image has been already added on document
7007		$newimage = true;
7008		if (in_array($file, $this->imagekeys)) {
7009			$newimage = false;
7010			// get existing image data
7011			$info = $this->getImageBuffer($file);
7012			if (strpos($file, '__tcpdf_imgmask_') === FALSE) {
7013				// check if the newer image is larger
7014				$oldsize = ($info['w'] * $info['h']);
7015				if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7016					$newimage = true;
7017				}
7018			}
7019		} elseif (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_imgmask_') === FALSE)) {
7020			// create temp image file (without alpha channel)
7021			$tempfile_plain = K_PATH_CACHE.'__tcpdf_imgmask_plain_'.$filehash;
7022			// create temp alpha file
7023			$tempfile_alpha = K_PATH_CACHE.'__tcpdf_imgmask_alpha_'.$filehash;
7024			// check for cached images
7025			if (in_array($tempfile_plain, $this->imagekeys)) {
7026				// get existing image data
7027				$info = $this->getImageBuffer($tempfile_plain);
7028				// check if the newer image is larger
7029				$oldsize = ($info['w'] * $info['h']);
7030				if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7031					$newimage = true;
7032				} else {
7033					$newimage = false;
7034					// embed mask image
7035					$imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7036					// embed image, masked with previously embedded mask
7037					return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7038				}
7039			}
7040		}
7041		if ($newimage) {
7042			//First use of image, get info
7043			$type = strtolower($type);
7044			if ($type == '') {
7045				$type = TCPDF_IMAGES::getImageFileType($file, $imsize);
7046			} elseif ($type == 'jpg') {
7047				$type = 'jpeg';
7048			}
7049			$mqr = TCPDF_STATIC::get_mqr();
7050			TCPDF_STATIC::set_mqr(false);
7051			// Specific image handlers (defined on TCPDF_IMAGES CLASS)
7052			$mtd = '_parse'.$type;
7053			// GD image handler function
7054			$gdfunction = 'imagecreatefrom'.$type;
7055			$info = false;
7056			if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
7057				// TCPDF image functions
7058				$info = TCPDF_IMAGES::$mtd($file);
7059				if (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_imgmask_') === FALSE)
7060					AND (($info === 'pngalpha') OR (isset($info['trns']) AND !empty($info['trns'])))) {
7061					return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
7062				}
7063			}
7064			if (($info === false) AND function_exists($gdfunction)) {
7065				try {
7066					// GD library
7067					$img = $gdfunction($file);
7068					if ($img !== false) {
7069						if ($resize) {
7070							$imgr = imagecreatetruecolor($neww, $newh);
7071							if (($type == 'gif') OR ($type == 'png')) {
7072								$imgr = TCPDF_IMAGES::setGDImageTransparency($imgr, $img);
7073							}
7074							imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
7075							$img = $imgr;
7076						}
7077						if (($type == 'gif') OR ($type == 'png')) {
7078							$info = TCPDF_IMAGES::_toPNG($img);
7079						} else {
7080							$info = TCPDF_IMAGES::_toJPEG($img, $this->jpeg_quality);
7081						}
7082					}
7083				} catch(Exception $e) {
7084					$info = false;
7085				}
7086			}
7087			if (($info === false) AND extension_loaded('imagick')) {
7088				try {
7089					// ImageMagick library
7090					$img = new Imagick();
7091					if ($type == 'svg') {
7092						if ($file[0] === '@') {
7093							// image from string
7094							$svgimg = substr($file, 1);
7095						} else {
7096							// get SVG file content
7097							$svgimg = TCPDF_STATIC::fileGetContents($file);
7098						}
7099						if ($svgimg !== FALSE) {
7100							// get width and height
7101							$regs = array();
7102							if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) {
7103								$svgtag = $regs[1];
7104								$tmp = array();
7105								if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7106									$ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7107									$owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit;
7108									$svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
7109								} else {
7110									$ow = $w;
7111								}
7112								$tmp = array();
7113								if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7114									$oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7115									$ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit;
7116									$svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
7117								} else {
7118									$oh = $h;
7119								}
7120								$tmp = array();
7121								if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
7122									$vbw = ($ow * $this->imgscale * $this->k);
7123									$vbh = ($oh * $this->imgscale * $this->k);
7124									$vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh);
7125									$svgtag = $vbox.$svgtag;
7126								}
7127								$svgimg = preg_replace('/<svg([^\>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
7128							}
7129							$img->readImageBlob($svgimg);
7130						}
7131					} else {
7132						$img->readImage($file);
7133					}
7134					if ($resize) {
7135						$img->resizeImage($neww, $newh, 10, 1, false);
7136					}
7137					$img->setCompressionQuality($this->jpeg_quality);
7138					$img->setImageFormat('jpeg');
7139					$tempname = TCPDF_STATIC::getObjFilename('img');
7140					$img->writeImage($tempname);
7141					$info = TCPDF_IMAGES::_parsejpeg($tempname);
7142					unlink($tempname);
7143					$img->destroy();
7144				} catch(Exception $e) {
7145					$info = false;
7146				}
7147			}
7148			if ($info === false) {
7149				// unable to process image
7150				return;
7151			}
7152			TCPDF_STATIC::set_mqr($mqr);
7153			if ($ismask) {
7154				// force grayscale
7155				$info['cs'] = 'DeviceGray';
7156			}
7157			if ($imgmask !== false) {
7158				$info['masked'] = $imgmask;
7159			}
7160			if (!empty($exurl)) {
7161				$info['exurl'] = $exurl;
7162			}
7163			// array of alternative images
7164			$info['altimgs'] = $altimgs;
7165			// add image to document
7166			$info['i'] = $this->setImageBuffer($file, $info);
7167		}
7168		// set alignment
7169		$this->img_rb_y = $y + $h;
7170		// set alignment
7171		if ($this->rtl) {
7172			if ($palign == 'L') {
7173				$ximg = $this->lMargin;
7174			} elseif ($palign == 'C') {
7175				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7176			} elseif ($palign == 'R') {
7177				$ximg = $this->w - $this->rMargin - $w;
7178			} else {
7179				$ximg = $x - $w;
7180			}
7181			$this->img_rb_x = $ximg;
7182		} else {
7183			if ($palign == 'L') {
7184				$ximg = $this->lMargin;
7185			} elseif ($palign == 'C') {
7186				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7187			} elseif ($palign == 'R') {
7188				$ximg = $this->w - $this->rMargin - $w;
7189			} else {
7190				$ximg = $x;
7191			}
7192			$this->img_rb_x = $ximg + $w;
7193		}
7194		if ($ismask OR $hidden) {
7195			// image is not displayed
7196			return $info['i'];
7197		}
7198		$xkimg = $ximg * $this->k;
7199		if (!$alt) {
7200			// only non-alternative immages will be set
7201			$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']));
7202		}
7203		if (!empty($border)) {
7204			$bx = $this->x;
7205			$by = $this->y;
7206			$this->x = $ximg;
7207			if ($this->rtl) {
7208				$this->x += $w;
7209			}
7210			$this->y = $y;
7211			$this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
7212			$this->x = $bx;
7213			$this->y = $by;
7214		}
7215		if ($link) {
7216			$this->Link($ximg, $y, $w, $h, $link, 0);
7217		}
7218		// set pointer to align the next text/objects
7219		switch($align) {
7220			case 'T': {
7221				$this->y = $y;
7222				$this->x = $this->img_rb_x;
7223				break;
7224			}
7225			case 'M': {
7226				$this->y = $y + round($h/2);
7227				$this->x = $this->img_rb_x;
7228				break;
7229			}
7230			case 'B': {
7231				$this->y = $this->img_rb_y;
7232				$this->x = $this->img_rb_x;
7233				break;
7234			}
7235			case 'N': {
7236				$this->SetY($this->img_rb_y);
7237				break;
7238			}
7239			default:{
7240				break;
7241			}
7242		}
7243		$this->endlinex = $this->img_rb_x;
7244		if ($this->inxobj) {
7245			// we are inside an XObject template
7246			$this->xobjects[$this->xobjid]['images'][] = $info['i'];
7247		}
7248		return $info['i'];
7249	}
7250
7251	/**
7252	 * Extract info from a PNG image with alpha channel using the Imagick or GD library.
7253	 * @param $file (string) Name of the file containing the image.
7254	 * @param $x (float) Abscissa of the upper-left corner.
7255	 * @param $y (float) Ordinate of the upper-left corner.
7256	 * @param $wpx (float) Original width of the image in pixels.
7257	 * @param $hpx (float) original height of the image in pixels.
7258	 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
7259	 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
7260	 * @param $type (string) 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.
7261	 * @param $link (mixed) URL or identifier returned by AddLink().
7262	 * @param $align (string) 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>
7263	 * @param $resize (boolean) If true resize (reduce) the image to fit $w and $h (requires GD library).
7264	 * @param $dpi (int) dot-per-inch resolution used on resize
7265	 * @param $palign (string) 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>
7266	 * @param $filehash (string) File hash used to build unique file names.
7267	 * @author Nicola Asuni
7268	 * @protected
7269	 * @since 4.3.007 (2008-12-04)
7270	 * @see Image()
7271	 */
7272	protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
7273		// create temp images
7274		if (empty($filehash)) {
7275			$filehash = md5($this->file_id.$file);
7276		}
7277		// create temp image file (without alpha channel)
7278		$tempfile_plain = K_PATH_CACHE.'__tcpdf_imgmask_plain_'.$filehash;
7279		// create temp alpha file
7280		$tempfile_alpha = K_PATH_CACHE.'__tcpdf_imgmask_alpha_'.$filehash;
7281		$parsed = false;
7282		$parse_error = '';
7283		// ImageMagick extension
7284		if (($parsed === false) AND extension_loaded('imagick')) {
7285			try {
7286				// ImageMagick library
7287				$img = new Imagick();
7288				$img->readImage($file);
7289				// clone image object
7290				$imga = TCPDF_STATIC::objclone($img);
7291				// extract alpha channel
7292				if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) {
7293					$img->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT);
7294				} else {
7295					$img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
7296					$img->negateImage(true);
7297				}
7298				$img->setImageFormat('png');
7299				$img->writeImage($tempfile_alpha);
7300				// remove alpha channel
7301				if (method_exists($imga, 'setImageMatte')) {
7302					$imga->setImageMatte(false);
7303				} else {
7304					$imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
7305				}
7306				$imga->setImageFormat('png');
7307				$imga->writeImage($tempfile_plain);
7308				$parsed = true;
7309			} catch (Exception $e) {
7310				// Imagemagick fails, try with GD
7311				$parse_error = 'Imagick library error: '.$e->getMessage();
7312			}
7313		}
7314		// GD extension
7315		if (($parsed === false) AND function_exists('imagecreatefrompng')) {
7316			try {
7317				// generate images
7318				$img = imagecreatefrompng($file);
7319				$imgalpha = imagecreate($wpx, $hpx);
7320				// generate gray scale palette (0 -> 255)
7321				for ($c = 0; $c < 256; ++$c) {
7322					ImageColorAllocate($imgalpha, $c, $c, $c);
7323				}
7324				// extract alpha channel
7325				for ($xpx = 0; $xpx < $wpx; ++$xpx) {
7326					for ($ypx = 0; $ypx < $hpx; ++$ypx) {
7327						$color = imagecolorat($img, $xpx, $ypx);
7328						// get and correct gamma color
7329						$alpha = $this->getGDgamma($img, $color);
7330						imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
7331					}
7332				}
7333				imagepng($imgalpha, $tempfile_alpha);
7334				imagedestroy($imgalpha);
7335				// extract image without alpha channel
7336				$imgplain = imagecreatetruecolor($wpx, $hpx);
7337				imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
7338				imagepng($imgplain, $tempfile_plain);
7339				imagedestroy($imgplain);
7340				$parsed = true;
7341			} catch (Exception $e) {
7342				// GD fails
7343				$parse_error = 'GD library error: '.$e->getMessage();
7344			}
7345		}
7346		if ($parsed === false) {
7347			if (empty($parse_error)) {
7348				$this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
7349			} else {
7350				$this->Error($parse_error);
7351			}
7352		}
7353		// embed mask image
7354		$imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7355		// embed image, masked with previously embedded mask
7356		$this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7357		// remove temp files
7358		unlink($tempfile_alpha);
7359		unlink($tempfile_plain);
7360	}
7361
7362	/**
7363	 * Get the GD-corrected PNG gamma value from alpha color
7364	 * @param $img (int) GD image Resource ID.
7365	 * @param $c (int) alpha color
7366	 * @protected
7367	 * @since 4.3.007 (2008-12-04)
7368	 */
7369	protected function getGDgamma($img, $c) {
7370		if (!isset($this->gdgammacache['#'.$c])) {
7371			$colors = imagecolorsforindex($img, $c);
7372			// GD alpha is only 7 bit (0 -> 127)
7373			$this->gdgammacache['#'.$c] = (((127 - $colors['alpha']) / 127) * 255);
7374			// correct gamma
7375			$this->gdgammacache['#'.$c] = (pow(($this->gdgammacache['#'.$c] / 255), 2.2) * 255);
7376			// store the latest values on cache to improve performances
7377			if (count($this->gdgammacache) > 8) {
7378				// remove one element from the cache array
7379				array_shift($this->gdgammacache);
7380			}
7381		}
7382		return $this->gdgammacache['#'.$c];
7383	}
7384
7385	/**
7386	 * Performs a line break.
7387	 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
7388	 * @param $h (float) The height of the break. By default, the value equals the height of the last printed cell.
7389	 * @param $cell (boolean) if true add the current left (or right o for RTL) padding to the X coordinate
7390	 * @public
7391	 * @since 1.0
7392	 * @see Cell()
7393	 */
7394	public function Ln($h='', $cell=false) {
7395		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'])) {
7396			// revove vertical space from the top of the column
7397			return;
7398		}
7399		if ($cell) {
7400			if ($this->rtl) {
7401				$cellpadding = $this->cell_padding['R'];
7402			} else {
7403				$cellpadding = $this->cell_padding['L'];
7404			}
7405		} else {
7406			$cellpadding = 0;
7407		}
7408		if ($this->rtl) {
7409			$this->x = $this->w - $this->rMargin - $cellpadding;
7410		} else {
7411			$this->x = $this->lMargin + $cellpadding;
7412		}
7413		if (is_string($h)) {
7414			$this->y += $this->lasth;
7415		} else {
7416			$this->y += $h;
7417		}
7418		$this->newline = true;
7419	}
7420
7421	/**
7422	 * Returns the relative X value of current position.
7423	 * The value is relative to the left border for LTR languages and to the right border for RTL languages.
7424	 * @return float
7425	 * @public
7426	 * @since 1.2
7427	 * @see SetX(), GetY(), SetY()
7428	 */
7429	public function GetX() {
7430		//Get x position
7431		if ($this->rtl) {
7432			return ($this->w - $this->x);
7433		} else {
7434			return $this->x;
7435		}
7436	}
7437
7438	/**
7439	 * Returns the absolute X value of current position.
7440	 * @return float
7441	 * @public
7442	 * @since 1.2
7443	 * @see SetX(), GetY(), SetY()
7444	 */
7445	public function GetAbsX() {
7446		return $this->x;
7447	}
7448
7449	/**
7450	 * Returns the ordinate of the current position.
7451	 * @return float
7452	 * @public
7453	 * @since 1.0
7454	 * @see SetY(), GetX(), SetX()
7455	 */
7456	public function GetY() {
7457		return $this->y;
7458	}
7459
7460	/**
7461	 * Defines the abscissa of the current position.
7462	 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
7463	 * @param $x (float) The value of the abscissa in user units.
7464	 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7465	 * @public
7466	 * @since 1.2
7467	 * @see GetX(), GetY(), SetY(), SetXY()
7468	 */
7469	public function SetX($x, $rtloff=false) {
7470		$x = floatval($x);
7471		if (!$rtloff AND $this->rtl) {
7472			if ($x >= 0) {
7473				$this->x = $this->w - $x;
7474			} else {
7475				$this->x = abs($x);
7476			}
7477		} else {
7478			if ($x >= 0) {
7479				$this->x = $x;
7480			} else {
7481				$this->x = $this->w + $x;
7482			}
7483		}
7484		if ($this->x < 0) {
7485			$this->x = 0;
7486		}
7487		if ($this->x > $this->w) {
7488			$this->x = $this->w;
7489		}
7490	}
7491
7492	/**
7493	 * Moves the current abscissa back to the left margin and sets the ordinate.
7494	 * If the passed value is negative, it is relative to the bottom of the page.
7495	 * @param $y (float) The value of the ordinate in user units.
7496	 * @param $resetx (bool) if true (default) reset the X position.
7497	 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7498	 * @public
7499	 * @since 1.0
7500	 * @see GetX(), GetY(), SetY(), SetXY()
7501	 */
7502	public function SetY($y, $resetx=true, $rtloff=false) {
7503		$y = floatval($y);
7504		if ($resetx) {
7505			//reset x
7506			if (!$rtloff AND $this->rtl) {
7507				$this->x = $this->w - $this->rMargin;
7508			} else {
7509				$this->x = $this->lMargin;
7510			}
7511		}
7512		if ($y >= 0) {
7513			$this->y = $y;
7514		} else {
7515			$this->y = $this->h + $y;
7516		}
7517		if ($this->y < 0) {
7518			$this->y = 0;
7519		}
7520		if ($this->y > $this->h) {
7521			$this->y = $this->h;
7522		}
7523	}
7524
7525	/**
7526	 * Defines the abscissa and ordinate of the current position.
7527	 * If the passed values are negative, they are relative respectively to the right and bottom of the page.
7528	 * @param $x (float) The value of the abscissa.
7529	 * @param $y (float) The value of the ordinate.
7530	 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7531	 * @public
7532	 * @since 1.2
7533	 * @see SetX(), SetY()
7534	 */
7535	public function SetXY($x, $y, $rtloff=false) {
7536		$this->SetY($y, false, $rtloff);
7537		$this->SetX($x, $rtloff);
7538	}
7539
7540	/**
7541	 * Set the absolute X coordinate of the current pointer.
7542	 * @param $x (float) The value of the abscissa in user units.
7543	 * @public
7544	 * @since 5.9.186 (2012-09-13)
7545	 * @see setAbsX(), setAbsY(), SetAbsXY()
7546	 */
7547	public function SetAbsX($x) {
7548		$this->x = floatval($x);
7549	}
7550
7551	/**
7552	 * Set the absolute Y coordinate of the current pointer.
7553	 * @param $y (float) (float) The value of the ordinate in user units.
7554	 * @public
7555	 * @since 5.9.186 (2012-09-13)
7556	 * @see setAbsX(), setAbsY(), SetAbsXY()
7557	 */
7558	public function SetAbsY($y) {
7559		$this->y = floatval($y);
7560	}
7561
7562	/**
7563	 * Set the absolute X and Y coordinates of the current pointer.
7564	 * @param $x (float) The value of the abscissa in user units.
7565	 * @param $y (float) (float) The value of the ordinate in user units.
7566	 * @public
7567	 * @since 5.9.186 (2012-09-13)
7568	 * @see setAbsX(), setAbsY(), SetAbsXY()
7569	 */
7570	public function SetAbsXY($x, $y) {
7571		$this->SetAbsX($x);
7572		$this->SetAbsY($y);
7573	}
7574
7575	/**
7576	 * Send the document to a given destination: string, local file or browser.
7577	 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
7578	 * The method first calls Close() if necessary to terminate the document.
7579	 * @param $name (string) The name of the file when saved. Note that special characters are removed and blanks characters are replaced with the underscore character.
7580	 * @param $dest (string) 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>
7581	 * @public
7582	 * @since 1.0
7583	 * @see Close()
7584	 */
7585	public function Output($name='doc.pdf', $dest='I') {
7586		//Output PDF to some destination
7587		//Finish document if necessary
7588		if ($this->state < 3) {
7589			$this->Close();
7590		}
7591		//Normalize parameters
7592		if (is_bool($dest)) {
7593			$dest = $dest ? 'D' : 'F';
7594		}
7595		$dest = strtoupper($dest);
7596		if ($dest[0] != 'F') {
7597			$name = preg_replace('/[\s]+/', '_', $name);
7598			$name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
7599		}
7600		if ($this->sign) {
7601			// *** apply digital signature to the document ***
7602			// get the document content
7603			$pdfdoc = $this->getBuffer();
7604			// remove last newline
7605			$pdfdoc = substr($pdfdoc, 0, -1);
7606			// Remove the original buffer
7607			if (isset($this->diskcache) AND $this->diskcache) {
7608				// remove buffer file from cache
7609				unlink($this->buffer);
7610			}
7611			unset($this->buffer);
7612			// remove filler space
7613			$byterange_string_len = strlen(TCPDF_STATIC::$byterange_string);
7614			// define the ByteRange
7615			$byte_range = array();
7616			$byte_range[0] = 0;
7617			$byte_range[1] = strpos($pdfdoc, TCPDF_STATIC::$byterange_string) + $byterange_string_len + 10;
7618			$byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
7619			$byte_range[3] = strlen($pdfdoc) - $byte_range[2];
7620			$pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
7621			// replace the ByteRange
7622			$byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
7623			$byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
7624			$pdfdoc = str_replace(TCPDF_STATIC::$byterange_string, $byterange, $pdfdoc);
7625			// write the document to a temporary folder
7626			$tempdoc = TCPDF_STATIC::getObjFilename('doc');
7627			$f = fopen($tempdoc, 'wb');
7628			if (!$f) {
7629				$this->Error('Unable to create temporary file: '.$tempdoc);
7630			}
7631			$pdfdoc_length = strlen($pdfdoc);
7632			fwrite($f, $pdfdoc, $pdfdoc_length);
7633			fclose($f);
7634			// get digital signature via openssl library
7635			$tempsign = TCPDF_STATIC::getObjFilename('sig');
7636			if (empty($this->signature_data['extracerts'])) {
7637				openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
7638			} else {
7639				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']);
7640			}
7641			unlink($tempdoc);
7642			// read signature
7643			$signature = file_get_contents($tempsign);
7644			unlink($tempsign);
7645			// extract signature
7646			$signature = substr($signature, $pdfdoc_length);
7647			$signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
7648			$tmparr = explode("\n\n", $signature);
7649			$signature = $tmparr[1];
7650			unset($tmparr);
7651			// decode signature
7652			$signature = base64_decode(trim($signature));
7653			// add TSA timestamp to signature
7654			$signature = $this->applyTSA($signature);
7655			// convert signature to hex
7656			$signature = current(unpack('H*', $signature));
7657			$signature = str_pad($signature, $this->signature_max_length, '0');
7658			// disable disk caching
7659			$this->diskcache = false;
7660			// Add signature to the document
7661			$this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
7662			$this->bufferlen = strlen($this->buffer);
7663		}
7664		switch($dest) {
7665			case 'I': {
7666				// Send PDF to the standard output
7667				if (ob_get_contents()) {
7668					$this->Error('Some data has already been output, can\'t send PDF file');
7669				}
7670				if (php_sapi_name() != 'cli') {
7671					// send output to a browser
7672					header('Content-Type: application/pdf');
7673					if (headers_sent()) {
7674						$this->Error('Some data has already been output to browser, can\'t send PDF file');
7675					}
7676					header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7677					//header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7678					header('Pragma: public');
7679					header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7680					header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7681					header('Content-Disposition: inline; filename="'.basename($name).'"');
7682					TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7683				} else {
7684					echo $this->getBuffer();
7685				}
7686				break;
7687			}
7688			case 'D': {
7689				// download PDF as file
7690				if (ob_get_contents()) {
7691					$this->Error('Some data has already been output, can\'t send PDF file');
7692				}
7693				header('Content-Description: File Transfer');
7694				if (headers_sent()) {
7695					$this->Error('Some data has already been output to browser, can\'t send PDF file');
7696				}
7697				header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7698				//header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7699				header('Pragma: public');
7700				header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7701				header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7702				// force download dialog
7703				if (strpos(php_sapi_name(), 'cgi') === false) {
7704					header('Content-Type: application/force-download');
7705					header('Content-Type: application/octet-stream', false);
7706					header('Content-Type: application/download', false);
7707					header('Content-Type: application/pdf', false);
7708				} else {
7709					header('Content-Type: application/pdf');
7710				}
7711				// use the Content-Disposition header to supply a recommended filename
7712				header('Content-Disposition: attachment; filename="'.basename($name).'"');
7713				header('Content-Transfer-Encoding: binary');
7714				TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7715				break;
7716			}
7717			case 'F':
7718			case 'FI':
7719			case 'FD': {
7720				// save PDF to a local file
7721				if ($this->diskcache) {
7722					copy($this->buffer, $name);
7723				} else {
7724					$f = fopen($name, 'wb');
7725					if (!$f) {
7726						$this->Error('Unable to create output file: '.$name);
7727					}
7728					fwrite($f, $this->getBuffer(), $this->bufferlen);
7729					fclose($f);
7730				}
7731				if ($dest == 'FI') {
7732					// send headers to browser
7733					header('Content-Type: application/pdf');
7734					header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7735					//header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7736					header('Pragma: public');
7737					header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7738					header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7739					header('Content-Disposition: inline; filename="'.basename($name).'"');
7740					TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7741				} elseif ($dest == 'FD') {
7742					// send headers to browser
7743					if (ob_get_contents()) {
7744						$this->Error('Some data has already been output, can\'t send PDF file');
7745					}
7746					header('Content-Description: File Transfer');
7747					if (headers_sent()) {
7748						$this->Error('Some data has already been output to browser, can\'t send PDF file');
7749					}
7750					header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7751					header('Pragma: public');
7752					header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7753					header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7754					// force download dialog
7755					if (strpos(php_sapi_name(), 'cgi') === false) {
7756						header('Content-Type: application/force-download');
7757						header('Content-Type: application/octet-stream', false);
7758						header('Content-Type: application/download', false);
7759						header('Content-Type: application/pdf', false);
7760					} else {
7761						header('Content-Type: application/pdf');
7762					}
7763					// use the Content-Disposition header to supply a recommended filename
7764					header('Content-Disposition: attachment; filename="'.basename($name).'"');
7765					header('Content-Transfer-Encoding: binary');
7766					TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7767				}
7768				break;
7769			}
7770			case 'E': {
7771				// return PDF as base64 mime multi-part email attachment (RFC 2045)
7772				$retval = 'Content-Type: application/pdf;'."\r\n";
7773				$retval .= ' name="'.$name.'"'."\r\n";
7774				$retval .= 'Content-Transfer-Encoding: base64'."\r\n";
7775				$retval .= 'Content-Disposition: attachment;'."\r\n";
7776				$retval .= ' filename="'.$name.'"'."\r\n\r\n";
7777				$retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
7778				return $retval;
7779			}
7780			case 'S': {
7781				// returns PDF as a string
7782				return $this->getBuffer();
7783			}
7784			default: {
7785				$this->Error('Incorrect output destination: '.$dest);
7786			}
7787		}
7788		return '';
7789	}
7790
7791	/**
7792	 * Unset all class variables except the following critical variables.
7793	 * @param $destroyall (boolean) if true destroys all class variables, otherwise preserves critical variables.
7794	 * @param $preserve_objcopy (boolean) if true preserves the objcopy variable
7795	 * @public
7796	 * @since 4.5.016 (2009-02-24)
7797	 */
7798	public function _destroy($destroyall=false, $preserve_objcopy=false) {
7799		if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!TCPDF_STATIC::empty_string($this->buffer))) {
7800			// remove buffer file from cache
7801			unlink($this->buffer);
7802		}
7803		if ($destroyall AND !empty($this->cached_files)) {
7804			// remove cached files
7805			foreach ($this->cached_files as $cachefile) {
7806				if (is_file($cachefile)) {
7807					unlink($cachefile);
7808				}
7809			}
7810			unset($this->cached_files);
7811		}
7812		$preserve = array(
7813			'internal_encoding',
7814			'state',
7815			'bufferlen',
7816			'buffer',
7817			'diskcache',
7818			'cached_files',
7819			'sign',
7820			'signature_data',
7821			'signature_max_length',
7822			'byterange_string',
7823			'tsa_timestamp',
7824			'tsa_data'
7825		);
7826		foreach (array_keys(get_object_vars($this)) as $val) {
7827			if ($destroyall OR !in_array($val, $preserve)) {
7828				if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) {
7829					unset($this->$val);
7830				}
7831			}
7832		}
7833	}
7834
7835	/**
7836	 * Check for locale-related bug
7837	 * @protected
7838	 */
7839	protected function _dochecks() {
7840		//Check for locale-related bug
7841		if (1.1 == 1) {
7842			$this->Error('Don\'t alter the locale before including class file');
7843		}
7844		//Check for decimal separator
7845		if (sprintf('%.1F', 1.0) != '1.0') {
7846			setlocale(LC_NUMERIC, 'C');
7847		}
7848	}
7849
7850	/**
7851	 * Return an array containing variations for the basic page number alias.
7852	 * @param $a (string) Base alias.
7853	 * @return array of page number aliases
7854	 * @protected
7855	 */
7856	protected function getInternalPageNumberAliases($a= '') {
7857		$alias = array();
7858		// build array of Unicode + ASCII variants (the order is important)
7859		$alias = array('u' => array(), 'a' => array());
7860		$u = '{'.$a.'}';
7861		$alias['u'][] = TCPDF_STATIC::_escape($u);
7862		if ($this->isunicode) {
7863			$alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($u, $this->isunicode, $this->CurrentFont));
7864			$alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($u, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7865			$alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($a, $this->isunicode, $this->CurrentFont));
7866			$alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($a, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7867		}
7868		$alias['a'][] = TCPDF_STATIC::_escape($a);
7869		return $alias;
7870	}
7871
7872	/**
7873	 * Return an array containing all internal page aliases.
7874	 * @return array of page number aliases
7875	 * @protected
7876	 */
7877	protected function getAllInternalPageNumberAliases() {
7878		$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);
7879		$pnalias = array();
7880		foreach($basic_alias as $k => $a) {
7881			$pnalias[$k] = $this->getInternalPageNumberAliases($a);
7882		}
7883		return $pnalias;
7884	}
7885
7886	/**
7887	 * Replace right shift page number aliases with spaces to correct right alignment.
7888	 * This works perfectly only when using monospaced fonts.
7889	 * @param $page (string) Page content.
7890	 * @param $aliases (array) Array of page aliases.
7891	 * @param $diff (int) initial difference to add.
7892	 * @return replaced page content.
7893	 * @protected
7894	 */
7895	protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
7896		foreach ($aliases as $type => $alias) {
7897			foreach ($alias as $a) {
7898				// find position of compensation factor
7899				$startnum = (strpos($a, ':') + 1);
7900				$a = substr($a, 0, $startnum);
7901				if (($pos = strpos($page, $a)) !== false) {
7902					// end of alias
7903					$endnum = strpos($page, '}', $pos);
7904					// string to be replaced
7905					$aa = substr($page, $pos, ($endnum - $pos + 1));
7906					// get compensation factor
7907					$ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum));
7908					$ratio = preg_replace('/[^0-9\.]/', '', $ratio);
7909					$ratio = floatval($ratio);
7910					if ($type == 'u') {
7911						$chrdiff = floor(($diff + 12) * $ratio);
7912						$shift = str_repeat(' ', $chrdiff);
7913						$shift = TCPDF_FONTS::UTF8ToUTF16BE($shift, false, $this->isunicode, $this->CurrentFont);
7914					} else {
7915						$chrdiff = floor(($diff + 11) * $ratio);
7916						$shift = str_repeat(' ', $chrdiff);
7917					}
7918					$page = str_replace($aa, $shift, $page);
7919				}
7920			}
7921		}
7922		return $page;
7923	}
7924
7925	/**
7926	 * Set page boxes to be included on page descriptions.
7927	 * @param $boxes (array) Array of page boxes to set on document: ('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox').
7928	 * @protected
7929	 */
7930	protected function setPageBoxTypes($boxes) {
7931		$this->page_boxes = array();
7932		foreach ($boxes as $box) {
7933			if (in_array($box, TCPDF_STATIC::$pageboxes)) {
7934				$this->page_boxes[] = $box;
7935			}
7936		}
7937	}
7938
7939	/**
7940	 * Output pages (and replace page number aliases).
7941	 * @protected
7942	 */
7943	protected function _putpages() {
7944		$filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
7945		// get internal aliases for page numbers
7946		$pnalias = $this->getAllInternalPageNumberAliases();
7947		$num_pages = $this->numpages;
7948		$ptpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $num_pages - 1));
7949		$ptpu = TCPDF_FONTS::UTF8ToUTF16BE($ptpa, false, $this->isunicode, $this->CurrentFont);
7950		$ptp_num_chars = $this->GetNumChars($ptpa);
7951		$pagegroupnum = 0;
7952		$groupnum = 0;
7953		$ptgu = 1;
7954		$ptga = 1;
7955		$ptg_num_chars = 1;
7956		for ($n = 1; $n <= $num_pages; ++$n) {
7957			// get current page
7958			$temppage = $this->getPageBuffer($n);
7959			$pagelen = strlen($temppage);
7960			// set replacements for total pages number
7961			$pnpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $n - 1));
7962			$pnpu = TCPDF_FONTS::UTF8ToUTF16BE($pnpa, false, $this->isunicode, $this->CurrentFont);
7963			$pnp_num_chars = $this->GetNumChars($pnpa);
7964			$pdiff = 0; // difference used for right shift alignment of page numbers
7965			$gdiff = 0; // difference used for right shift alignment of page group numbers
7966			if (!empty($this->pagegroups)) {
7967				if (isset($this->newpagegroup[$n])) {
7968					$pagegroupnum = 0;
7969					++$groupnum;
7970					$ptga = TCPDF_STATIC::formatPageNumber($this->pagegroups[$groupnum]);
7971					$ptgu = TCPDF_FONTS::UTF8ToUTF16BE($ptga, false, $this->isunicode, $this->CurrentFont);
7972					$ptg_num_chars = $this->GetNumChars($ptga);
7973				}
7974				++$pagegroupnum;
7975				$pnga = TCPDF_STATIC::formatPageNumber($pagegroupnum);
7976				$pngu = TCPDF_FONTS::UTF8ToUTF16BE($pnga, false, $this->isunicode, $this->CurrentFont);
7977				$png_num_chars = $this->GetNumChars($pnga);
7978				// replace page numbers
7979				$replace = array();
7980				$replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
7981				$replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
7982				$replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
7983				$replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
7984				list($temppage, $gdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $gdiff);
7985			}
7986			// replace page numbers
7987			$replace = array();
7988			$replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
7989			$replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
7990			$replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
7991			$replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
7992			list($temppage, $pdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $pdiff);
7993			// replace right shift alias
7994			$temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
7995			// replace EPS marker
7996			$temppage = str_replace($this->epsmarker, '', $temppage);
7997			//Page
7998			$this->page_obj_id[$n] = $this->_newobj();
7999			$out = '<<';
8000			$out .= ' /Type /Page';
8001			$out .= ' /Parent 1 0 R';
8002			if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
8003				$out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp);
8004			}
8005			$out .= ' /Resources 2 0 R';
8006			foreach ($this->page_boxes as $box) {
8007				$out .= ' /'.$box;
8008				$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']);
8009			}
8010			if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
8011				$out .= ' /BoxColorInfo <<';
8012				foreach ($this->page_boxes as $box) {
8013					if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
8014						$out .= ' /'.$box.' <<';
8015						if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
8016							$color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
8017							$out .= ' /C [';
8018							$out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
8019							$out .= ' ]';
8020						}
8021						if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
8022							$out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
8023						}
8024						if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
8025							$out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
8026						}
8027						if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
8028							$dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
8029							$out .= ' /D [';
8030							foreach ($dashes as $dash) {
8031								$out .= sprintf(' %F', ($dash * $this->k));
8032							}
8033							$out .= ' ]';
8034						}
8035						$out .= ' >>';
8036					}
8037				}
8038				$out .= ' >>';
8039			}
8040			$out .= ' /Contents '.($this->n + 1).' 0 R';
8041			$out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
8042			if (!$this->pdfa_mode) {
8043				$out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
8044			}
8045			if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
8046				// page transitions
8047				if (isset($this->pagedim[$n]['trans']['Dur'])) {
8048					$out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
8049				}
8050				$out .= ' /Trans <<';
8051				$out .= ' /Type /Trans';
8052				if (isset($this->pagedim[$n]['trans']['S'])) {
8053					$out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
8054				}
8055				if (isset($this->pagedim[$n]['trans']['D'])) {
8056					$out .= ' /D '.$this->pagedim[$n]['trans']['D'];
8057				}
8058				if (isset($this->pagedim[$n]['trans']['Dm'])) {
8059					$out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
8060				}
8061				if (isset($this->pagedim[$n]['trans']['M'])) {
8062					$out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
8063				}
8064				if (isset($this->pagedim[$n]['trans']['Di'])) {
8065					$out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
8066				}
8067				if (isset($this->pagedim[$n]['trans']['SS'])) {
8068					$out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
8069				}
8070				if (isset($this->pagedim[$n]['trans']['B'])) {
8071					$out .= ' /B '.$this->pagedim[$n]['trans']['B'];
8072				}
8073				$out .= ' >>';
8074			}
8075			$out .= $this->_getannotsrefs($n);
8076			$out .= ' /PZ '.$this->pagedim[$n]['PZ'];
8077			$out .= ' >>';
8078			$out .= "\n".'endobj';
8079			$this->_out($out);
8080			//Page content
8081			$p = ($this->compress) ? gzcompress($temppage) : $temppage;
8082			$this->_newobj();
8083			$p = $this->_getrawstream($p);
8084			$this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
8085			if ($this->diskcache) {
8086				// remove temporary files
8087				unlink($this->pages[$n]);
8088			}
8089		}
8090		//Pages root
8091		$out = $this->_getobj(1)."\n";
8092		$out .= '<< /Type /Pages /Kids [';
8093		foreach($this->page_obj_id as $page_obj) {
8094			$out .= ' '.$page_obj.' 0 R';
8095		}
8096		$out .= ' ] /Count '.$num_pages.' >>';
8097		$out .= "\n".'endobj';
8098		$this->_out($out);
8099	}
8100
8101	/**
8102	 * Output references to page annotations
8103	 * @param $n (int) page number
8104	 * @protected
8105	 * @author Nicola Asuni
8106	 * @since 4.7.000 (2008-08-29)
8107	 * @deprecated
8108	 */
8109	protected function _putannotsrefs($n) {
8110		$this->_out($this->_getannotsrefs($n));
8111	}
8112
8113	/**
8114	 * Get references to page annotations.
8115	 * @param $n (int) page number
8116	 * @return string
8117	 * @protected
8118	 * @author Nicola Asuni
8119	 * @since 5.0.010 (2010-05-17)
8120	 */
8121	protected function _getannotsrefs($n) {
8122		if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
8123			return '';
8124		}
8125		$out = ' /Annots [';
8126		if (isset($this->PageAnnots[$n])) {
8127			foreach ($this->PageAnnots[$n] as $key => $val) {
8128				if (!in_array($val['n'], $this->radio_groups)) {
8129					$out .= ' '.$val['n'].' 0 R';
8130				}
8131			}
8132			// add radiobutton groups
8133			if (isset($this->radiobutton_groups[$n])) {
8134				foreach ($this->radiobutton_groups[$n] as $key => $data) {
8135					if (isset($data['n'])) {
8136						$out .= ' '.$data['n'].' 0 R';
8137					}
8138				}
8139			}
8140		}
8141		if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
8142			// set reference for signature object
8143			$out .= ' '.$this->sig_obj_id.' 0 R';
8144		}
8145		if (!empty($this->empty_signature_appearance)) {
8146			foreach ($this->empty_signature_appearance as $esa) {
8147				if ($esa['page'] == $n) {
8148					// set reference for empty signature objects
8149					$out .= ' '.$esa['objid'].' 0 R';
8150				}
8151			}
8152		}
8153		$out .= ' ]';
8154		return $out;
8155	}
8156
8157	/**
8158	 * Output annotations objects for all pages.
8159	 * !!! THIS METHOD IS NOT YET COMPLETED !!!
8160	 * See section 12.5 of PDF 32000_2008 reference.
8161	 * @protected
8162	 * @author Nicola Asuni
8163	 * @since 4.0.018 (2008-08-06)
8164	 */
8165	protected function _putannotsobjs() {
8166		// reset object counter
8167		for ($n=1; $n <= $this->numpages; ++$n) {
8168			if (isset($this->PageAnnots[$n])) {
8169				// set page annotations
8170				foreach ($this->PageAnnots[$n] as $key => $pl) {
8171					$annot_obj_id = $this->PageAnnots[$n][$key]['n'];
8172					// create annotation object for grouping radiobuttons
8173					if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
8174						$radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n'];
8175						$annots = '<<';
8176						$annots .= ' /Type /Annot';
8177						$annots .= ' /Subtype /Widget';
8178						$annots .= ' /Rect [0 0 0 0]';
8179						if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) {
8180							// read only
8181							$annots .= ' /F 68';
8182							$annots .= ' /Ff 49153';
8183						} else {
8184							$annots .= ' /F 4'; // default print for PDF/A
8185							$annots .= ' /Ff 49152';
8186						}
8187						$annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
8188						if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8189							$annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id);
8190						}
8191						$annots .= ' /FT /Btn';
8192						$annots .= ' /Kids [';
8193						$defval = '';
8194						foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) {
8195							if (isset($data['kid'])) {
8196								$annots .= ' '.$data['kid'].' 0 R';
8197								if ($data['def'] !== 'Off') {
8198									$defval = $data['def'];
8199								}
8200							}
8201						}
8202						$annots .= ' ]';
8203						if (!empty($defval)) {
8204							$annots .= ' /V /'.$defval;
8205						}
8206						$annots .= ' >>';
8207						$this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
8208						$this->form_obj_id[] = $radio_button_obj_id;
8209						// store object id to be used on Parent entry of Kids
8210						$this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id;
8211					}
8212					$formfield = false;
8213					$pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
8214					$a = $pl['x'] * $this->k;
8215					$b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k);
8216					$c = $pl['w'] * $this->k;
8217					$d = $pl['h'] * $this->k;
8218					$rect = sprintf('%F %F %F %F', $a, $b, $a+$c, $b+$d);
8219					// create new annotation object
8220					$annots = '<</Type /Annot';
8221					$annots .= ' /Subtype /'.$pl['opt']['subtype'];
8222					$annots .= ' /Rect ['.$rect.']';
8223					$ft = array('Btn', 'Tx', 'Ch', 'Sig');
8224					if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
8225						$annots .= ' /FT /'.$pl['opt']['ft'];
8226						$formfield = true;
8227					}
8228					$annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
8229					$annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
8230					$annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
8231					$annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp);
8232					if (isset($pl['opt']['f'])) {
8233						$fval = 0;
8234						if (is_array($pl['opt']['f'])) {
8235							foreach ($pl['opt']['f'] as $f) {
8236								switch (strtolower($f)) {
8237									case 'invisible': {
8238										$fval += 1 << 0;
8239										break;
8240									}
8241									case 'hidden': {
8242										$fval += 1 << 1;
8243										break;
8244									}
8245									case 'print': {
8246										$fval += 1 << 2;
8247										break;
8248									}
8249									case 'nozoom': {
8250										$fval += 1 << 3;
8251										break;
8252									}
8253									case 'norotate': {
8254										$fval += 1 << 4;
8255										break;
8256									}
8257									case 'noview': {
8258										$fval += 1 << 5;
8259										break;
8260									}
8261									case 'readonly': {
8262										$fval += 1 << 6;
8263										break;
8264									}
8265									case 'locked': {
8266										$fval += 1 << 8;
8267										break;
8268									}
8269									case 'togglenoview': {
8270										$fval += 1 << 9;
8271										break;
8272									}
8273									case 'lockedcontents': {
8274										$fval += 1 << 10;
8275										break;
8276									}
8277									default: {
8278										break;
8279									}
8280								}
8281							}
8282						} else {
8283							$fval = intval($pl['opt']['f']);
8284						}
8285					} else {
8286						$fval = 4;
8287					}
8288					if ($this->pdfa_mode) {
8289						// force print flag for PDF/A mode
8290						$fval |= 4;
8291					}
8292					$annots .= ' /F '.intval($fval);
8293					if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
8294						$annots .= ' /AS /'.$pl['opt']['as'];
8295					}
8296					if (isset($pl['opt']['ap'])) {
8297						// appearance stream
8298						$annots .= ' /AP <<';
8299						if (is_array($pl['opt']['ap'])) {
8300							foreach ($pl['opt']['ap'] as $apmode => $apdef) {
8301								// $apmode can be: n = normal; r = rollover; d = down;
8302								$annots .= ' /'.strtoupper($apmode);
8303								if (is_array($apdef)) {
8304									$annots .= ' <<';
8305									foreach ($apdef as $apstate => $stream) {
8306										// reference to XObject that define the appearance for this mode-state
8307										$apsobjid = $this->_putAPXObject($c, $d, $stream);
8308										$annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
8309									}
8310									$annots .= ' >>';
8311								} else {
8312									// reference to XObject that define the appearance for this mode
8313									$apsobjid = $this->_putAPXObject($c, $d, $apdef);
8314									$annots .= ' '.$apsobjid.' 0 R';
8315								}
8316							}
8317						} else {
8318							$annots .= $pl['opt']['ap'];
8319						}
8320						$annots .= ' >>';
8321					}
8322					if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
8323						$annots .= ' /BS <<';
8324						$annots .= ' /Type /Border';
8325						if (isset($pl['opt']['bs']['w'])) {
8326							$annots .= ' /W '.intval($pl['opt']['bs']['w']);
8327						}
8328						$bstyles = array('S', 'D', 'B', 'I', 'U');
8329						if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
8330							$annots .= ' /S /'.$pl['opt']['bs']['s'];
8331						}
8332						if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
8333							$annots .= ' /D [';
8334							foreach ($pl['opt']['bs']['d'] as $cord) {
8335								$annots .= ' '.intval($cord);
8336							}
8337							$annots .= ']';
8338						}
8339						$annots .= ' >>';
8340					} else {
8341						$annots .= ' /Border [';
8342						if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
8343							$annots .= intval($pl['opt']['border'][0]).' ';
8344							$annots .= intval($pl['opt']['border'][1]).' ';
8345							$annots .= intval($pl['opt']['border'][2]);
8346							if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
8347								$annots .= ' [';
8348								foreach ($pl['opt']['border'][3] as $dash) {
8349									$annots .= intval($dash).' ';
8350								}
8351								$annots .= ']';
8352							}
8353						} else {
8354							$annots .= '0 0 0';
8355						}
8356						$annots .= ']';
8357					}
8358					if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
8359						$annots .= ' /BE <<';
8360						$bstyles = array('S', 'C');
8361						if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) {
8362							$annots .= ' /S /'.$pl['opt']['bs']['s'];
8363						} else {
8364							$annots .= ' /S /S';
8365						}
8366						if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
8367							$annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']);
8368						}
8369						$annots .= '>>';
8370					}
8371					if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
8372						$annots .= ' /C '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['c']);
8373					}
8374					//$annots .= ' /StructParent ';
8375					//$annots .= ' /OC ';
8376					$markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
8377					if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
8378						// this is a markup type
8379						if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8380							$annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
8381						}
8382						//$annots .= ' /Popup ';
8383						if (isset($pl['opt']['ca'])) {
8384							$annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca']));
8385						}
8386						if (isset($pl['opt']['rc'])) {
8387							$annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8388						}
8389						$annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp);
8390						//$annots .= ' /IRT ';
8391						if (isset($pl['opt']['subj'])) {
8392							$annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
8393						}
8394						//$annots .= ' /RT ';
8395						//$annots .= ' /IT ';
8396						//$annots .= ' /ExData ';
8397					}
8398					$lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
8399					// Annotation types
8400					switch (strtolower($pl['opt']['subtype'])) {
8401						case 'text': {
8402							if (isset($pl['opt']['open'])) {
8403								$annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
8404							}
8405							$iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
8406							if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8407								$annots .= ' /Name /'.$pl['opt']['name'];
8408							} else {
8409								$annots .= ' /Name /Note';
8410							}
8411							$statemodels = array('Marked', 'Review');
8412							if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
8413								$annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8414							} else {
8415								$pl['opt']['statemodel'] = 'Marked';
8416								$annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8417							}
8418							if ($pl['opt']['statemodel'] == 'Marked') {
8419								$states = array('Accepted', 'Unmarked');
8420							} else {
8421								$states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
8422							}
8423							if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
8424								$annots .= ' /State /'.$pl['opt']['state'];
8425							} else {
8426								if ($pl['opt']['statemodel'] == 'Marked') {
8427									$annots .= ' /State /Unmarked';
8428								} else {
8429									$annots .= ' /State /None';
8430								}
8431							}
8432							break;
8433						}
8434						case 'link': {
8435							if (is_string($pl['txt'])) {
8436								if ($pl['txt'][0] == '#') {
8437									// internal destination
8438									$annots .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($pl['txt'], 1));
8439								} elseif ($pl['txt'][0] == '%') {
8440									// embedded PDF file
8441									$filename = basename(substr($pl['txt'], 1));
8442									$annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
8443								} elseif ($pl['txt'][0] == '*') {
8444									// embedded generic file
8445									$filename = basename(substr($pl['txt'], 1));
8446									$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});';
8447									$annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>';
8448								} else {
8449									$parsedUrl = parse_url($pl['txt']);
8450									if (empty($parsedUrl['scheme']) AND (strtolower(substr($parsedUrl['path'], -4)) == '.pdf')) {
8451										// relative link to a PDF file
8452										$dest = '[0 /Fit]'; // default page 0
8453										if (!empty($parsedUrl['fragment'])) {
8454											// check for named destination
8455											$tmp = explode('=', $parsedUrl['fragment']);
8456											$dest = '('.((count($tmp) == 2) ? $tmp[1] : $tmp[0]).')';
8457										}
8458										$annots .= ' /A <</S /GoToR /D '.$dest.' /F '.$this->_datastring($this->unhtmlentities($parsedUrl['path']), $annot_obj_id).' /NewWindow true>>';
8459									} else {
8460										// external URI link
8461										$annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
8462									}
8463								}
8464							} elseif (isset($this->links[$pl['txt']])) {
8465								// internal link ID
8466								$l = $this->links[$pl['txt']];
8467								if (isset($this->page_obj_id[($l['p'])])) {
8468									$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)));
8469								}
8470							}
8471							$hmodes = array('N', 'I', 'O', 'P');
8472							if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
8473								$annots .= ' /H /'.$pl['opt']['h'];
8474							} else {
8475								$annots .= ' /H /I';
8476							}
8477							//$annots .= ' /PA ';
8478							//$annots .= ' /Quadpoints ';
8479							break;
8480						}
8481						case 'freetext': {
8482							if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8483								$annots .= ' /DA ('.$pl['opt']['da'].')';
8484							}
8485							if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8486								$annots .= ' /Q '.intval($pl['opt']['q']);
8487							}
8488							if (isset($pl['opt']['rc'])) {
8489								$annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8490							}
8491							if (isset($pl['opt']['ds'])) {
8492								$annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
8493							}
8494							if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
8495								$annots .= ' /CL [';
8496								foreach ($pl['opt']['cl'] as $cl) {
8497									$annots .= sprintf('%F ', $cl * $this->k);
8498								}
8499								$annots .= ']';
8500							}
8501							$tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
8502							if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
8503								$annots .= ' /IT /'.$pl['opt']['it'];
8504							}
8505							if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
8506								$l = $pl['opt']['rd'][0] * $this->k;
8507								$r = $pl['opt']['rd'][1] * $this->k;
8508								$t = $pl['opt']['rd'][2] * $this->k;
8509								$b = $pl['opt']['rd'][3] * $this->k;
8510								$annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']';
8511							}
8512							if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
8513								$annots .= ' /LE /'.$pl['opt']['le'];
8514							}
8515							break;
8516						}
8517						case 'line': {
8518							break;
8519						}
8520						case 'square': {
8521							break;
8522						}
8523						case 'circle': {
8524							break;
8525						}
8526						case 'polygon': {
8527							break;
8528						}
8529						case 'polyline': {
8530							break;
8531						}
8532						case 'highlight': {
8533							break;
8534						}
8535						case 'underline': {
8536							break;
8537						}
8538						case 'squiggly': {
8539							break;
8540						}
8541						case 'strikeout': {
8542							break;
8543						}
8544						case 'stamp': {
8545							break;
8546						}
8547						case 'caret': {
8548							break;
8549						}
8550						case 'ink': {
8551							break;
8552						}
8553						case 'popup': {
8554							break;
8555						}
8556						case 'fileattachment': {
8557							if ($this->pdfa_mode) {
8558								// embedded files are not allowed in PDF/A mode
8559								break;
8560							}
8561							if (!isset($pl['opt']['fs'])) {
8562								break;
8563							}
8564							$filename = basename($pl['opt']['fs']);
8565							if (isset($this->embeddedfiles[$filename]['f'])) {
8566								$annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R';
8567								$iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
8568								if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8569									$annots .= ' /Name /'.$pl['opt']['name'];
8570								} else {
8571									$annots .= ' /Name /PushPin';
8572								}
8573								// index (zero-based) of the annotation in the Annots array of this page
8574								$this->embeddedfiles[$filename]['a'] = $key;
8575							}
8576							break;
8577						}
8578						case 'sound': {
8579							if (!isset($pl['opt']['fs'])) {
8580								break;
8581							}
8582							$filename = basename($pl['opt']['fs']);
8583							if (isset($this->embeddedfiles[$filename]['f'])) {
8584								// ... TO BE COMPLETED ...
8585								// /R /C /B /E /CO /CP
8586								$annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R';
8587								$iconsapp = array('Speaker', 'Mic');
8588								if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8589									$annots .= ' /Name /'.$pl['opt']['name'];
8590								} else {
8591									$annots .= ' /Name /Speaker';
8592								}
8593							}
8594							break;
8595						}
8596						case 'movie': {
8597							break;
8598						}
8599						case 'widget': {
8600							$hmode = array('N', 'I', 'O', 'P', 'T');
8601							if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
8602								$annots .= ' /H /'.$pl['opt']['h'];
8603							}
8604							if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
8605								$annots .= ' /MK <<';
8606								if (isset($pl['opt']['mk']['r'])) {
8607									$annots .= ' /R '.$pl['opt']['mk']['r'];
8608								}
8609								if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
8610									$annots .= ' /BC '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bc']);
8611								}
8612								if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
8613									$annots .= ' /BG '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bg']);
8614								}
8615								if (isset($pl['opt']['mk']['ca'])) {
8616									$annots .= ' /CA '.$pl['opt']['mk']['ca'];
8617								}
8618								if (isset($pl['opt']['mk']['rc'])) {
8619									$annots .= ' /RC '.$pl['opt']['mk']['rc'];
8620								}
8621								if (isset($pl['opt']['mk']['ac'])) {
8622									$annots .= ' /AC '.$pl['opt']['mk']['ac'];
8623								}
8624								if (isset($pl['opt']['mk']['i'])) {
8625									$info = $this->getImageBuffer($pl['opt']['mk']['i']);
8626									if ($info !== false) {
8627										$annots .= ' /I '.$info['n'].' 0 R';
8628									}
8629								}
8630								if (isset($pl['opt']['mk']['ri'])) {
8631									$info = $this->getImageBuffer($pl['opt']['mk']['ri']);
8632									if ($info !== false) {
8633										$annots .= ' /RI '.$info['n'].' 0 R';
8634									}
8635								}
8636								if (isset($pl['opt']['mk']['ix'])) {
8637									$info = $this->getImageBuffer($pl['opt']['mk']['ix']);
8638									if ($info !== false) {
8639										$annots .= ' /IX '.$info['n'].' 0 R';
8640									}
8641								}
8642								if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
8643									$annots .= ' /IF <<';
8644									$if_sw = array('A', 'B', 'S', 'N');
8645									if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
8646										$annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
8647									}
8648									$if_s = array('A', 'P');
8649									if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
8650										$annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
8651									}
8652									if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
8653										$annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
8654									}
8655									if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
8656										$annots .= ' /FB true';
8657									}
8658									$annots .= '>>';
8659								}
8660								if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
8661									$annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
8662								}
8663								$annots .= '>>';
8664							} // end MK
8665							// --- Entries for field dictionaries ---
8666							if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
8667								// set parent
8668								$annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
8669							}
8670							if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8671								$annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
8672							}
8673							if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8674								$annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
8675							}
8676							if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
8677								$annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
8678							}
8679							if (isset($pl['opt']['ff'])) {
8680								if (is_array($pl['opt']['ff'])) {
8681									// array of bit settings
8682									$flag = 0;
8683									foreach($pl['opt']['ff'] as $val) {
8684										$flag += 1 << ($val - 1);
8685									}
8686								} else {
8687									$flag = intval($pl['opt']['ff']);
8688								}
8689								$annots .= ' /Ff '.$flag;
8690							}
8691							if (isset($pl['opt']['maxlen'])) {
8692								$annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
8693							}
8694							if (isset($pl['opt']['v'])) {
8695								$annots .= ' /V';
8696								if (is_array($pl['opt']['v'])) {
8697									foreach ($pl['opt']['v'] AS $optval) {
8698										if (is_float($optval)) {
8699											$optval = sprintf('%F', $optval);
8700										}
8701										$annots .= ' '.$optval;
8702									}
8703								} else {
8704									$annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
8705								}
8706							}
8707							if (isset($pl['opt']['dv'])) {
8708								$annots .= ' /DV';
8709								if (is_array($pl['opt']['dv'])) {
8710									foreach ($pl['opt']['dv'] AS $optval) {
8711										if (is_float($optval)) {
8712											$optval = sprintf('%F', $optval);
8713										}
8714										$annots .= ' '.$optval;
8715									}
8716								} else {
8717									$annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
8718								}
8719							}
8720							if (isset($pl['opt']['rv'])) {
8721								$annots .= ' /RV';
8722								if (is_array($pl['opt']['rv'])) {
8723									foreach ($pl['opt']['rv'] AS $optval) {
8724										if (is_float($optval)) {
8725											$optval = sprintf('%F', $optval);
8726										}
8727										$annots .= ' '.$optval;
8728									}
8729								} else {
8730									$annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
8731								}
8732							}
8733							if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
8734								$annots .= ' /A << '.$pl['opt']['a'].' >>';
8735							}
8736							if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
8737								$annots .= ' /AA << '.$pl['opt']['aa'].' >>';
8738							}
8739							if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8740								$annots .= ' /DA ('.$pl['opt']['da'].')';
8741							}
8742							if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8743								$annots .= ' /Q '.intval($pl['opt']['q']);
8744							}
8745							if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
8746								$annots .= ' /Opt [';
8747								foreach($pl['opt']['opt'] AS $copt) {
8748									if (is_array($copt)) {
8749										$annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
8750									} else {
8751										$annots .= ' '.$this->_textstring($copt, $annot_obj_id);
8752									}
8753								}
8754								$annots .= ']';
8755							}
8756							if (isset($pl['opt']['ti'])) {
8757								$annots .= ' /TI '.intval($pl['opt']['ti']);
8758							}
8759							if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
8760								$annots .= ' /I [';
8761								foreach($pl['opt']['i'] AS $copt) {
8762									$annots .= intval($copt).' ';
8763								}
8764								$annots .= ']';
8765							}
8766							break;
8767						}
8768						case 'screen': {
8769							break;
8770						}
8771						case 'printermark': {
8772							break;
8773						}
8774						case 'trapnet': {
8775							break;
8776						}
8777						case 'watermark': {
8778							break;
8779						}
8780						case '3d': {
8781							break;
8782						}
8783						default: {
8784							break;
8785						}
8786					}
8787					$annots .= '>>';
8788					// create new annotation object
8789					$this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
8790					if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
8791						// store reference of form object
8792						$this->form_obj_id[] = $annot_obj_id;
8793					}
8794				}
8795			}
8796		} // end for each page
8797	}
8798
8799	/**
8800	 * Put appearance streams XObject used to define annotation's appearance states.
8801	 * @param $w (int) annotation width
8802	 * @param $h (int) annotation height
8803	 * @param $stream (string) appearance stream
8804	 * @return int object ID
8805	 * @protected
8806	 * @since 4.8.001 (2009-09-09)
8807	 */
8808	protected function _putAPXObject($w=0, $h=0, $stream='') {
8809		$stream = trim($stream);
8810		$out = $this->_getobj()."\n";
8811		$this->xobjects['AX'.$this->n] = array('n' => $this->n);
8812		$out .= '<<';
8813		$out .= ' /Type /XObject';
8814		$out .= ' /Subtype /Form';
8815		$out .= ' /FormType 1';
8816		if ($this->compress) {
8817			$stream = gzcompress($stream);
8818			$out .= ' /Filter /FlateDecode';
8819		}
8820		$rect = sprintf('%F %F', $w, $h);
8821		$out .= ' /BBox [0 0 '.$rect.']';
8822		$out .= ' /Matrix [1 0 0 1 0 0]';
8823		$out .= ' /Resources 2 0 R';
8824		$stream = $this->_getrawstream($stream);
8825		$out .= ' /Length '.strlen($stream);
8826		$out .= ' >>';
8827		$out .= ' stream'."\n".$stream."\n".'endstream';
8828		$out .= "\n".'endobj';
8829		$this->_out($out);
8830		return $this->n;
8831	}
8832
8833	/**
8834	 * Output fonts.
8835	 * @author Nicola Asuni
8836	 * @protected
8837	 */
8838	protected function _putfonts() {
8839		$nf = $this->n;
8840		foreach ($this->diffs as $diff) {
8841			//Encodings
8842			$this->_newobj();
8843			$this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
8844		}
8845		$mqr = TCPDF_STATIC::get_mqr();
8846		TCPDF_STATIC::set_mqr(false);
8847		foreach ($this->FontFiles as $file => $info) {
8848			// search and get font file to embedd
8849			$fontfile = TCPDF_FONTS::getFontFullPath($file, $info['fontdir']);
8850			if (!TCPDF_STATIC::empty_string($fontfile)) {
8851				$font = file_get_contents($fontfile);
8852				$compressed = (substr($file, -2) == '.z');
8853				if ((!$compressed) AND (isset($info['length2']))) {
8854					$header = (ord($font[0]) == 128);
8855					if ($header) {
8856						// strip first binary header
8857						$font = substr($font, 6);
8858					}
8859					if ($header AND (ord($font[$info['length1']]) == 128)) {
8860						// strip second binary header
8861						$font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
8862					}
8863				} elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
8864					if ($compressed) {
8865						// uncompress font
8866						$font = gzuncompress($font);
8867					}
8868					// merge subset characters
8869					$subsetchars = array(); // used chars
8870					foreach ($info['fontkeys'] as $fontkey) {
8871						$fontinfo = $this->getFontBuffer($fontkey);
8872						$subsetchars += $fontinfo['subsetchars'];
8873					}
8874					// rebuild a font subset
8875					$font = TCPDF_FONTS::_getTrueTypeFontSubset($font, $subsetchars);
8876					// calculate new font length
8877					$info['length1'] = strlen($font);
8878					if ($compressed) {
8879						// recompress font
8880						$font = gzcompress($font);
8881					}
8882				}
8883				$this->_newobj();
8884				$this->FontFiles[$file]['n'] = $this->n;
8885				$stream = $this->_getrawstream($font);
8886				$out = '<< /Length '.strlen($stream);
8887				if ($compressed) {
8888					$out .= ' /Filter /FlateDecode';
8889				}
8890				$out .= ' /Length1 '.$info['length1'];
8891				if (isset($info['length2'])) {
8892					$out .= ' /Length2 '.$info['length2'].' /Length3 0';
8893				}
8894				$out .= ' >>';
8895				$out .= ' stream'."\n".$stream."\n".'endstream';
8896				$out .= "\n".'endobj';
8897				$this->_out($out);
8898			}
8899		}
8900		TCPDF_STATIC::set_mqr($mqr);
8901		foreach ($this->fontkeys as $k) {
8902			//Font objects
8903			$font = $this->getFontBuffer($k);
8904			$type = $font['type'];
8905			$name = $font['name'];
8906			if ($type == 'core') {
8907				// standard core font
8908				$out = $this->_getobj($this->font_obj_ids[$k])."\n";
8909				$out .= '<</Type /Font';
8910				$out .= ' /Subtype /Type1';
8911				$out .= ' /BaseFont /'.$name;
8912				$out .= ' /Name /F'.$font['i'];
8913				if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
8914					$out .= ' /Encoding /WinAnsiEncoding';
8915				}
8916				if ($k == 'helvetica') {
8917					// add default font for annotations
8918					$this->annotation_fonts[$k] = $font['i'];
8919				}
8920				$out .= ' >>';
8921				$out .= "\n".'endobj';
8922				$this->_out($out);
8923			} elseif (($type == 'Type1') OR ($type == 'TrueType')) {
8924				// additional Type1 or TrueType font
8925				$out = $this->_getobj($this->font_obj_ids[$k])."\n";
8926				$out .= '<</Type /Font';
8927				$out .= ' /Subtype /'.$type;
8928				$out .= ' /BaseFont /'.$name;
8929				$out .= ' /Name /F'.$font['i'];
8930				$out .= ' /FirstChar 32 /LastChar 255';
8931				$out .= ' /Widths '.($this->n + 1).' 0 R';
8932				$out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
8933				if ($font['enc']) {
8934					if (isset($font['diff'])) {
8935						$out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
8936					} else {
8937						$out .= ' /Encoding /WinAnsiEncoding';
8938					}
8939				}
8940				$out .= ' >>';
8941				$out .= "\n".'endobj';
8942				$this->_out($out);
8943				// Widths
8944				$this->_newobj();
8945				$s = '[';
8946				for ($i = 32; $i < 256; ++$i) {
8947					if (isset($font['cw'][$i])) {
8948						$s .= $font['cw'][$i].' ';
8949					} else {
8950						$s .= $font['dw'].' ';
8951					}
8952				}
8953				$s .= ']';
8954				$s .= "\n".'endobj';
8955				$this->_out($s);
8956				//Descriptor
8957				$this->_newobj();
8958				$s = '<</Type /FontDescriptor /FontName /'.$name;
8959				foreach ($font['desc'] as $fdk => $fdv) {
8960					if (is_float($fdv)) {
8961						$fdv = sprintf('%F', $fdv);
8962					}
8963					$s .= ' /'.$fdk.' '.$fdv.'';
8964				}
8965				if (!TCPDF_STATIC::empty_string($font['file'])) {
8966					$s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
8967				}
8968				$s .= '>>';
8969				$s .= "\n".'endobj';
8970				$this->_out($s);
8971			} else {
8972				// additional types
8973				$mtd = '_put'.strtolower($type);
8974				if (!method_exists($this, $mtd)) {
8975					$this->Error('Unsupported font type: '.$type);
8976				}
8977				$this->$mtd($font);
8978			}
8979		}
8980	}
8981
8982	/**
8983	 * Adds unicode fonts.<br>
8984	 * Based on PDF Reference 1.3 (section 5)
8985	 * @param $font (array) font data
8986	 * @protected
8987	 * @author Nicola Asuni
8988	 * @since 1.52.0.TC005 (2005-01-05)
8989	 */
8990	protected function _puttruetypeunicode($font) {
8991		$fontname = '';
8992		if ($font['subset']) {
8993			// change name for font subsetting
8994			$subtag = sprintf('%06u', $font['i']);
8995			$subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
8996			$fontname .= $subtag.'+';
8997		}
8998		$fontname .= $font['name'];
8999		// Type0 Font
9000		// A composite font composed of other fonts, organized hierarchically
9001		$out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
9002		$out .= '<< /Type /Font';
9003		$out .= ' /Subtype /Type0';
9004		$out .= ' /BaseFont /'.$fontname;
9005		$out .= ' /Name /F'.$font['i'];
9006		$out .= ' /Encoding /'.$font['enc'];
9007		$out .= ' /ToUnicode '.($this->n + 1).' 0 R';
9008		$out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
9009		$out .= ' >>';
9010		$out .= "\n".'endobj';
9011		$this->_out($out);
9012		// ToUnicode map for Identity-H
9013		$stream = TCPDF_FONT_DATA::$uni_identity_h;
9014		// ToUnicode Object
9015		$this->_newobj();
9016		$stream = ($this->compress) ? gzcompress($stream) : $stream;
9017		$filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9018		$stream = $this->_getrawstream($stream);
9019		$this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
9020		// CIDFontType2
9021		// A CIDFont whose glyph descriptions are based on TrueType font technology
9022		$oid = $this->_newobj();
9023		$out = '<< /Type /Font';
9024		$out .= ' /Subtype /CIDFontType2';
9025		$out .= ' /BaseFont /'.$fontname;
9026		// A dictionary containing entries that define the character collection of the CIDFont.
9027		$cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9028		$cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9029		$cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9030		$out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
9031		$out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
9032		$out .= ' /DW '.$font['dw']; // default width
9033		$out .= "\n".TCPDF_FONTS::_putfontwidths($font, 0);
9034		if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
9035			$out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
9036		}
9037		$out .= ' >>';
9038		$out .= "\n".'endobj';
9039		$this->_out($out);
9040		// Font descriptor
9041		// A font descriptor describing the CIDFont default metrics other than its glyph widths
9042		$this->_newobj();
9043		$out = '<< /Type /FontDescriptor';
9044		$out .= ' /FontName /'.$fontname;
9045		foreach ($font['desc'] as $key => $value) {
9046			if (is_float($value)) {
9047				$value = sprintf('%F', $value);
9048			}
9049			$out .= ' /'.$key.' '.$value;
9050		}
9051		$fontdir = false;
9052		if (!TCPDF_STATIC::empty_string($font['file'])) {
9053			// A stream containing a TrueType font
9054			$out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
9055			$fontdir = $this->FontFiles[$font['file']]['fontdir'];
9056		}
9057		$out .= ' >>';
9058		$out .= "\n".'endobj';
9059		$this->_out($out);
9060		if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
9061			$this->_newobj();
9062			// Embed CIDToGIDMap
9063			// A specification of the mapping from CIDs to glyph indices
9064			// search and get CTG font file to embedd
9065			$ctgfile = strtolower($font['ctg']);
9066			// search and get ctg font file to embedd
9067			$fontfile = TCPDF_FONTS::getFontFullPath($ctgfile, $fontdir);
9068			if (TCPDF_STATIC::empty_string($fontfile)) {
9069				$this->Error('Font file not found: '.$ctgfile);
9070			}
9071			$stream = $this->_getrawstream(file_get_contents($fontfile));
9072			$out = '<< /Length '.strlen($stream).'';
9073			if (substr($fontfile, -2) == '.z') { // check file extension
9074				// Decompresses data encoded using the public-domain
9075				// zlib/deflate compression method, reproducing the
9076				// original text or binary data
9077				$out .= ' /Filter /FlateDecode';
9078			}
9079			$out .= ' >>';
9080			$out .= ' stream'."\n".$stream."\n".'endstream';
9081			$out .= "\n".'endobj';
9082			$this->_out($out);
9083		}
9084	}
9085
9086	/**
9087	 * Output CID-0 fonts.
9088	 * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format
9089	 * @param $font (array) font data
9090	 * @protected
9091	 * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
9092	 * @since 3.2.000 (2008-06-23)
9093	 */
9094	protected function _putcidfont0($font) {
9095		$cidoffset = 0;
9096		if (!isset($font['cw'][1])) {
9097			$cidoffset = 31;
9098		}
9099		if (isset($font['cidinfo']['uni2cid'])) {
9100			// convert unicode to cid.
9101			$uni2cid = $font['cidinfo']['uni2cid'];
9102			$cw = array();
9103			foreach ($font['cw'] as $uni => $width) {
9104				if (isset($uni2cid[$uni])) {
9105					$cw[($uni2cid[$uni] + $cidoffset)] = $width;
9106				} elseif ($uni < 256) {
9107					$cw[$uni] = $width;
9108				} // else unknown character
9109			}
9110			$font = array_merge($font, array('cw' => $cw));
9111		}
9112		$name = $font['name'];
9113		$enc = $font['enc'];
9114		if ($enc) {
9115			$longname = $name.'-'.$enc;
9116		} else {
9117			$longname = $name;
9118		}
9119		$out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
9120		$out .= '<</Type /Font';
9121		$out .= ' /Subtype /Type0';
9122		$out .= ' /BaseFont /'.$longname;
9123		$out .= ' /Name /F'.$font['i'];
9124		if ($enc) {
9125			$out .= ' /Encoding /'.$enc;
9126		}
9127		$out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
9128		$out .= ' >>';
9129		$out .= "\n".'endobj';
9130		$this->_out($out);
9131		$oid = $this->_newobj();
9132		$out = '<</Type /Font';
9133		$out .= ' /Subtype /CIDFontType0';
9134		$out .= ' /BaseFont /'.$name;
9135		$cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9136		$cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9137		$cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9138		$out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
9139		$out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
9140		$out .= ' /DW '.$font['dw'];
9141		$out .= "\n".TCPDF_FONTS::_putfontwidths($font, $cidoffset);
9142		$out .= ' >>';
9143		$out .= "\n".'endobj';
9144		$this->_out($out);
9145		$this->_newobj();
9146		$s = '<</Type /FontDescriptor /FontName /'.$name;
9147		foreach ($font['desc'] as $k => $v) {
9148			if ($k != 'Style') {
9149				if (is_float($v)) {
9150					$v = sprintf('%F', $v);
9151				}
9152				$s .= ' /'.$k.' '.$v.'';
9153			}
9154		}
9155		$s .= '>>';
9156		$s .= "\n".'endobj';
9157		$this->_out($s);
9158	}
9159
9160	/**
9161	 * Output images.
9162	 * @protected
9163	 */
9164	protected function _putimages() {
9165		$filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9166		foreach ($this->imagekeys as $file) {
9167			$info = $this->getImageBuffer($file);
9168			// set object for alternate images array
9169			if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
9170				$altoid = $this->_newobj();
9171				$out = '[';
9172				foreach ($info['altimgs'] as $altimage) {
9173					if (isset($this->xobjects['I'.$altimage[0]]['n'])) {
9174						$out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R';
9175						$out .= ' /DefaultForPrinting';
9176						if ($altimage[1] === true) {
9177							$out .= ' true';
9178						} else {
9179							$out .= ' false';
9180						}
9181						$out .= ' >>';
9182					}
9183				}
9184				$out .= ' ]';
9185				$out .= "\n".'endobj';
9186				$this->_out($out);
9187			}
9188			// set image object
9189			$oid = $this->_newobj();
9190			$this->xobjects['I'.$info['i']] = array('n' => $oid);
9191			$this->setImageSubBuffer($file, 'n', $this->n);
9192			$out = '<</Type /XObject';
9193			$out .= ' /Subtype /Image';
9194			$out .= ' /Width '.$info['w'];
9195			$out .= ' /Height '.$info['h'];
9196			if (array_key_exists('masked', $info)) {
9197				$out .= ' /SMask '.($this->n - 1).' 0 R';
9198			}
9199			// set color space
9200			$icc = false;
9201			if (isset($info['icc']) AND ($info['icc'] !== false)) {
9202				// ICC Colour Space
9203				$icc = true;
9204				$out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]';
9205			} elseif ($info['cs'] == 'Indexed') {
9206				// Indexed Colour Space
9207				$out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
9208			} else {
9209				// Device Colour Space
9210				$out .= ' /ColorSpace /'.$info['cs'];
9211			}
9212			if ($info['cs'] == 'DeviceCMYK') {
9213				$out .= ' /Decode [1 0 1 0 1 0 1 0]';
9214			}
9215			$out .= ' /BitsPerComponent '.$info['bpc'];
9216			if (isset($altoid) AND ($altoid > 0)) {
9217				// reference to alternate images dictionary
9218				$out .= ' /Alternates '.$altoid.' 0 R';
9219			}
9220			if (isset($info['exurl']) AND !empty($info['exurl'])) {
9221				// external stream
9222				$out .= ' /Length 0';
9223				$out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
9224				if (isset($info['f'])) {
9225					$out .= ' /FFilter /'.$info['f'];
9226				}
9227				$out .= ' >>';
9228				$out .= ' stream'."\n".'endstream';
9229			} else {
9230				if (isset($info['f'])) {
9231					$out .= ' /Filter /'.$info['f'];
9232				}
9233				if (isset($info['parms'])) {
9234					$out .= ' '.$info['parms'];
9235				}
9236				if (isset($info['trns']) AND is_array($info['trns'])) {
9237					$trns = '';
9238					$count_info = count($info['trns']);
9239					if ($info['cs'] == 'Indexed') {
9240						$maxval =(pow(2, $info['bpc']) - 1);
9241						for ($i = 0; $i < $count_info; ++$i) {
9242							if (($info['trns'][$i] != 0) AND ($info['trns'][$i] != $maxval)) {
9243								// this is not a binary type mask @TODO: create a SMask
9244								$trns = '';
9245								break;
9246							} elseif (empty($trns) AND ($info['trns'][$i] == 0)) {
9247								// store the first fully transparent value
9248								$trns .= $i.' '.$i.' ';
9249							}
9250						}
9251					} else {
9252						// grayscale or RGB
9253						for ($i = 0; $i < $count_info; ++$i) {
9254							if ($info['trns'][$i] == 0) {
9255								$trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
9256							}
9257						}
9258					}
9259					// Colour Key Masking
9260					if (!empty($trns)) {
9261						$out .= ' /Mask ['.$trns.']';
9262					}
9263				}
9264				$stream = $this->_getrawstream($info['data']);
9265				$out .= ' /Length '.strlen($stream).' >>';
9266				$out .= ' stream'."\n".$stream."\n".'endstream';
9267			}
9268			$out .= "\n".'endobj';
9269			$this->_out($out);
9270			if ($icc) {
9271				// ICC colour profile
9272				$this->_newobj();
9273				$icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
9274				$icc = $this->_getrawstream($icc);
9275				$this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9276			} elseif ($info['cs'] == 'Indexed') {
9277				// colour palette
9278				$this->_newobj();
9279				$pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
9280				$pal = $this->_getrawstream($pal);
9281				$this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
9282			}
9283		}
9284	}
9285
9286	/**
9287	 * Output Form XObjects Templates.
9288	 * @author Nicola Asuni
9289	 * @since 5.8.017 (2010-08-24)
9290	 * @protected
9291	 * @see startTemplate(), endTemplate(), printTemplate()
9292	 */
9293	protected function _putxobjects() {
9294		foreach ($this->xobjects as $key => $data) {
9295			if (isset($data['outdata'])) {
9296				$stream = str_replace($this->epsmarker, '', trim($data['outdata']));
9297				$out = $this->_getobj($data['n'])."\n";
9298				$out .= '<<';
9299				$out .= ' /Type /XObject';
9300				$out .= ' /Subtype /Form';
9301				$out .= ' /FormType 1';
9302				if ($this->compress) {
9303					$stream = gzcompress($stream);
9304					$out .= ' /Filter /FlateDecode';
9305				}
9306				$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));
9307				$out .= ' /Matrix [1 0 0 1 0 0]';
9308				$out .= ' /Resources <<';
9309				$out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9310				if (!$this->pdfa_mode) {
9311					// transparency
9312					if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
9313						$out .= ' /ExtGState <<';
9314						foreach ($data['extgstates'] as $k => $extgstate) {
9315							if (isset($this->extgstates[$k]['name'])) {
9316								$out .= ' /'.$this->extgstates[$k]['name'];
9317							} else {
9318								$out .= ' /GS'.$k;
9319							}
9320							$out .= ' '.$this->extgstates[$k]['n'].' 0 R';
9321						}
9322						$out .= ' >>';
9323					}
9324					if (isset($data['gradients']) AND !empty($data['gradients'])) {
9325						$gp = '';
9326						$gs = '';
9327						foreach ($data['gradients'] as $id => $grad) {
9328							// gradient patterns
9329							$gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R';
9330							// gradient shadings
9331							$gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R';
9332						}
9333						$out .= ' /Pattern <<'.$gp.' >>';
9334						$out .= ' /Shading <<'.$gs.' >>';
9335					}
9336				}
9337				// spot colors
9338				if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
9339					$out .= ' /ColorSpace <<';
9340					foreach ($data['spot_colors'] as $name => $color) {
9341						$out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R';
9342					}
9343					$out .= ' >>';
9344				}
9345				// fonts
9346				if (!empty($data['fonts'])) {
9347					$out .= ' /Font <<';
9348					foreach ($data['fonts'] as $fontkey => $fontid) {
9349						$out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9350					}
9351					$out .= ' >>';
9352				}
9353				// images or nested xobjects
9354				if (!empty($data['images']) OR !empty($data['xobjects'])) {
9355					$out .= ' /XObject <<';
9356					foreach ($data['images'] as $imgid) {
9357						$out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
9358					}
9359					foreach ($data['xobjects'] as $sub_id => $sub_objid) {
9360						$out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
9361					}
9362					$out .= ' >>';
9363				}
9364				$out .= ' >>'; //end resources
9365				if (isset($data['group']) AND ($data['group'] !== false)) {
9366					// set transparency group
9367					$out .= ' /Group << /Type /Group /S /Transparency';
9368					if (is_array($data['group'])) {
9369						if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
9370							$out .= ' /CS /'.$data['group']['CS'];
9371						}
9372						if (isset($data['group']['I'])) {
9373							$out .= ' /I /'.($data['group']['I']===true?'true':'false');
9374						}
9375						if (isset($data['group']['K'])) {
9376							$out .= ' /K /'.($data['group']['K']===true?'true':'false');
9377						}
9378					}
9379					$out .= ' >>';
9380				}
9381				$stream = $this->_getrawstream($stream, $data['n']);
9382				$out .= ' /Length '.strlen($stream);
9383				$out .= ' >>';
9384				$out .= ' stream'."\n".$stream."\n".'endstream';
9385				$out .= "\n".'endobj';
9386				$this->_out($out);
9387			}
9388		}
9389	}
9390
9391	/**
9392	 * Output Spot Colors Resources.
9393	 * @protected
9394	 * @since 4.0.024 (2008-09-12)
9395	 */
9396	protected function _putspotcolors() {
9397		foreach ($this->spot_colors as $name => $color) {
9398			$this->_newobj();
9399			$this->spot_colors[$name]['n'] = $this->n;
9400			$out = '[/Separation /'.str_replace(' ', '#20', $name);
9401			$out .= ' /DeviceCMYK <<';
9402			$out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
9403			$out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
9404			$out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
9405			$out .= "\n".'endobj';
9406			$this->_out($out);
9407		}
9408	}
9409
9410	/**
9411	 * Return XObjects Dictionary.
9412	 * @return string XObjects dictionary
9413	 * @protected
9414	 * @since 5.8.014 (2010-08-23)
9415	 */
9416	protected function _getxobjectdict() {
9417		$out = '';
9418		foreach ($this->xobjects as $id => $objid) {
9419			$out .= ' /'.$id.' '.$objid['n'].' 0 R';
9420		}
9421		return $out;
9422	}
9423
9424	/**
9425	 * Output Resources Dictionary.
9426	 * @protected
9427	 */
9428	protected function _putresourcedict() {
9429		$out = $this->_getobj(2)."\n";
9430		$out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9431		$out .= ' /Font <<';
9432		foreach ($this->fontkeys as $fontkey) {
9433			$font = $this->getFontBuffer($fontkey);
9434			$out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
9435		}
9436		$out .= ' >>';
9437		$out .= ' /XObject <<';
9438		$out .= $this->_getxobjectdict();
9439		$out .= ' >>';
9440		// layers
9441		if (!empty($this->pdflayers)) {
9442			$out .= ' /Properties <<';
9443			foreach ($this->pdflayers as $layer) {
9444				$out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
9445			}
9446			$out .= ' >>';
9447		}
9448		if (!$this->pdfa_mode) {
9449			// transparency
9450			if (isset($this->extgstates) AND !empty($this->extgstates)) {
9451				$out .= ' /ExtGState <<';
9452				foreach ($this->extgstates as $k => $extgstate) {
9453					if (isset($extgstate['name'])) {
9454						$out .= ' /'.$extgstate['name'];
9455					} else {
9456						$out .= ' /GS'.$k;
9457					}
9458					$out .= ' '.$extgstate['n'].' 0 R';
9459				}
9460				$out .= ' >>';
9461			}
9462			if (isset($this->gradients) AND !empty($this->gradients)) {
9463				$gp = '';
9464				$gs = '';
9465				foreach ($this->gradients as $id => $grad) {
9466					// gradient patterns
9467					$gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
9468					// gradient shadings
9469					$gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
9470				}
9471				$out .= ' /Pattern <<'.$gp.' >>';
9472				$out .= ' /Shading <<'.$gs.' >>';
9473			}
9474		}
9475		// spot colors
9476		if (isset($this->spot_colors) AND !empty($this->spot_colors)) {
9477			$out .= ' /ColorSpace <<';
9478			foreach ($this->spot_colors as $color) {
9479				$out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
9480			}
9481			$out .= ' >>';
9482		}
9483		$out .= ' >>';
9484		$out .= "\n".'endobj';
9485		$this->_out($out);
9486	}
9487
9488	/**
9489	 * Output Resources.
9490	 * @protected
9491	 */
9492	protected function _putresources() {
9493		$this->_putextgstates();
9494		$this->_putocg();
9495		$this->_putfonts();
9496		$this->_putimages();
9497		$this->_putspotcolors();
9498		$this->_putshaders();
9499		$this->_putxobjects();
9500		$this->_putresourcedict();
9501		$this->_putdests();
9502		$this->_putEmbeddedFiles();
9503		$this->_putannotsobjs();
9504		$this->_putjavascript();
9505		$this->_putbookmarks();
9506		$this->_putencryption();
9507	}
9508
9509	/**
9510	 * Adds some Metadata information (Document Information Dictionary)
9511	 * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference)
9512	 * @return int object id
9513	 * @protected
9514	 */
9515	protected function _putinfo() {
9516		$oid = $this->_newobj();
9517		$out = '<<';
9518		// store current isunicode value
9519		$prev_isunicode = $this->isunicode;
9520		if ($this->docinfounicode) {
9521			$this->isunicode = true;
9522		}
9523		if (!TCPDF_STATIC::empty_string($this->title)) {
9524			// The document's title.
9525			$out .= ' /Title '.$this->_textstring($this->title, $oid);
9526		}
9527		if (!TCPDF_STATIC::empty_string($this->author)) {
9528			// The name of the person who created the document.
9529			$out .= ' /Author '.$this->_textstring($this->author, $oid);
9530		}
9531		if (!TCPDF_STATIC::empty_string($this->subject)) {
9532			// The subject of the document.
9533			$out .= ' /Subject '.$this->_textstring($this->subject, $oid);
9534		}
9535		if (!TCPDF_STATIC::empty_string($this->keywords)) {
9536			// Keywords associated with the document.
9537			$out .= ' /Keywords '.$this->_textstring($this->keywords, $oid);
9538		}
9539		if (!TCPDF_STATIC::empty_string($this->creator)) {
9540			// 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.
9541			$out .= ' /Creator '.$this->_textstring($this->creator, $oid);
9542		}
9543		// restore previous isunicode value
9544		$this->isunicode = $prev_isunicode;
9545		// default producer
9546		$out .= ' /Producer '.$this->_textstring(TCPDF_STATIC::getTCPDFProducer(), $oid);
9547		// The date and time the document was created, in human-readable form
9548		$out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp);
9549		// The date and time the document was most recently modified, in human-readable form
9550		$out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp);
9551		// A name object indicating whether the document has been modified to include trapping information
9552		$out .= ' /Trapped /False';
9553		$out .= ' >>';
9554		$out .= "\n".'endobj';
9555		$this->_out($out);
9556		return $oid;
9557	}
9558
9559	/**
9560	 * Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag.
9561	 * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method!
9562	 * @param $xmp (string) Custom XMP data.
9563	 * @since 5.9.128 (2011-10-06)
9564	 * @public
9565	 */
9566	public function setExtraXMP($xmp) {
9567		$this->custom_xmp = $xmp;
9568	}
9569
9570	/**
9571	 * Put XMP data object and return ID.
9572	 * @return (int) The object ID.
9573	 * @since 5.9.121 (2011-09-28)
9574	 * @protected
9575	 */
9576	protected function _putXMP() {
9577		$oid = $this->_newobj();
9578		// store current isunicode value
9579		$prev_isunicode = $this->isunicode;
9580		$this->isunicode = true;
9581		$prev_encrypted = $this->encrypted;
9582		$this->encrypted = false;
9583		// set XMP data
9584		$xmp = '<?xpacket begin="'.TCPDF_FONTS::unichr(0xfeff, $this->isunicode).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
9585		$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";
9586		$xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
9587		$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
9588		$xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
9589		$xmp .= "\t\t\t".'<dc:title>'."\n";
9590		$xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9591		$xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->title).'</rdf:li>'."\n";
9592		$xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9593		$xmp .= "\t\t\t".'</dc:title>'."\n";
9594		$xmp .= "\t\t\t".'<dc:creator>'."\n";
9595		$xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
9596		$xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->author).'</rdf:li>'."\n";
9597		$xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
9598		$xmp .= "\t\t\t".'</dc:creator>'."\n";
9599		$xmp .= "\t\t\t".'<dc:description>'."\n";
9600		$xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9601		$xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->subject).'</rdf:li>'."\n";
9602		$xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9603		$xmp .= "\t\t\t".'</dc:description>'."\n";
9604		$xmp .= "\t\t\t".'<dc:subject>'."\n";
9605		$xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9606		$xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->keywords).'</rdf:li>'."\n";
9607		$xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9608		$xmp .= "\t\t\t".'</dc:subject>'."\n";
9609		$xmp .= "\t\t".'</rdf:Description>'."\n";
9610		// convert doc creation date format
9611		$dcdate = TCPDF_STATIC::getFormattedDate($this->doc_creation_timestamp);
9612		$doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2);
9613		$doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2);
9614		$doccreationdate .= '+'.substr($dcdate, 15, 2).':'.substr($dcdate, 18, 2);
9615		$doccreationdate = TCPDF_STATIC::_escapeXML($doccreationdate);
9616		// convert doc modification date format
9617		$dmdate = TCPDF_STATIC::getFormattedDate($this->doc_modification_timestamp);
9618		$docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2);
9619		$docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2);
9620		$docmoddate .= '+'.substr($dmdate, 15, 2).':'.substr($dmdate, 18, 2);
9621		$docmoddate = TCPDF_STATIC::_escapeXML($docmoddate);
9622		$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
9623		$xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n";
9624		$xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n";
9625		$xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n";
9626		$xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n";
9627		$xmp .= "\t\t".'</rdf:Description>'."\n";
9628		$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
9629		$xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC::_escapeXML($this->keywords).'</pdf:Keywords>'."\n";
9630		$xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC::_escapeXML(TCPDF_STATIC::getTCPDFProducer()).'</pdf:Producer>'."\n";
9631		$xmp .= "\t\t".'</rdf:Description>'."\n";
9632		$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
9633		$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);
9634		$xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
9635		$xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
9636		$xmp .= "\t\t".'</rdf:Description>'."\n";
9637		if ($this->pdfa_mode) {
9638			$xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
9639			$xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n";
9640			$xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
9641			$xmp .= "\t\t".'</rdf:Description>'."\n";
9642		}
9643		// XMP extension schemas
9644		$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";
9645		$xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
9646		$xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9647		$xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9648		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
9649		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
9650		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
9651		$xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9652		$xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9653		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
9654		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
9655		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
9656		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9657		$xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9658		$xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9659		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9660		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
9661		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9662		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9663		$xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9664		$xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9665		$xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9666		$xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9667		$xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9668		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
9669		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
9670		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
9671		$xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9672		$xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9673		$xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9674		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9675		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
9676		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
9677		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
9678		$xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9679		$xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9680		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9681		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
9682		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
9683		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9684		$xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9685		$xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9686		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9687		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
9688		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
9689		$xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9690		$xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9691		$xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9692		$xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9693		$xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9694		$xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9695		$xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
9696		$xmp .= "\t\t".'</rdf:Description>'."\n";
9697		$xmp .= "\t".'</rdf:RDF>'."\n";
9698		$xmp .= $this->custom_xmp;
9699		$xmp .= '</x:xmpmeta>'."\n";
9700		$xmp .= '<?xpacket end="w"?>';
9701		$out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
9702		// restore previous isunicode value
9703		$this->isunicode = $prev_isunicode;
9704		$this->encrypted = $prev_encrypted;
9705		$this->_out($out);
9706		return $oid;
9707	}
9708
9709	/**
9710	 * Output Catalog.
9711	 * @return int object id
9712	 * @protected
9713	 */
9714	protected function _putcatalog() {
9715		// put XMP
9716		$xmpobj = $this->_putXMP();
9717		// if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile
9718		if ($this->pdfa_mode OR $this->force_srgb) {
9719			$iccobj = $this->_newobj();
9720			$icc = file_get_contents(dirname(__FILE__).'/include/sRGB.icc');
9721			$filter = '';
9722			if ($this->compress) {
9723				$filter = ' /Filter /FlateDecode';
9724				$icc = gzcompress($icc);
9725			}
9726			$icc = $this->_getrawstream($icc);
9727			$this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9728		}
9729		// start catalog
9730		$oid = $this->_newobj();
9731		$out = '<< /Type /Catalog';
9732		$out .= ' /Version /'.$this->PDFVersion;
9733		//$out .= ' /Extensions <<>>';
9734		$out .= ' /Pages 1 0 R';
9735		//$out .= ' /PageLabels ' //...;
9736		$out .= ' /Names <<';
9737		if ((!$this->pdfa_mode) AND !empty($this->n_js)) {
9738			$out .= ' /JavaScript '.$this->n_js;
9739		}
9740		if (!empty($this->efnames)) {
9741			$out .= ' /EmbeddedFiles <</Names [';
9742			foreach ($this->efnames AS $fn => $fref) {
9743				$out .= ' '.$this->_datastring($fn).' '.$fref;
9744			}
9745			$out .= ' ]>>';
9746		}
9747		$out .= ' >>';
9748		if (!empty($this->dests)) {
9749			$out .= ' /Dests '.($this->n_dests).' 0 R';
9750		}
9751		$out .= $this->_putviewerpreferences();
9752		if (isset($this->LayoutMode) AND (!TCPDF_STATIC::empty_string($this->LayoutMode))) {
9753			$out .= ' /PageLayout /'.$this->LayoutMode;
9754		}
9755		if (isset($this->PageMode) AND (!TCPDF_STATIC::empty_string($this->PageMode))) {
9756			$out .= ' /PageMode /'.$this->PageMode;
9757		}
9758		if (count($this->outlines) > 0) {
9759			$out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
9760			$out .= ' /PageMode /UseOutlines';
9761		}
9762		//$out .= ' /Threads []';
9763		if ($this->ZoomMode == 'fullpage') {
9764			$out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
9765		} elseif ($this->ZoomMode == 'fullwidth') {
9766			$out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
9767		} elseif ($this->ZoomMode == 'real') {
9768			$out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
9769		} elseif (!is_string($this->ZoomMode)) {
9770			$out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100));
9771		}
9772		//$out .= ' /AA <<>>';
9773		//$out .= ' /URI <<>>';
9774		$out .= ' /Metadata '.$xmpobj.' 0 R';
9775		//$out .= ' /StructTreeRoot <<>>';
9776		//$out .= ' /MarkInfo <<>>';
9777		if (isset($this->l['a_meta_language'])) {
9778			$out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
9779		}
9780		//$out .= ' /SpiderInfo <<>>';
9781		// set OutputIntent to sRGB IEC61966-2.1 if required
9782		if ($this->pdfa_mode OR $this->force_srgb) {
9783			$out .= ' /OutputIntents [<<';
9784			$out .= ' /Type /OutputIntent';
9785			$out .= ' /S /GTS_PDFA1';
9786			$out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9787			$out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9788			$out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
9789			$out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9790			$out .= ' /DestOutputProfile '.$iccobj.' 0 R';
9791			$out .= ' >>]';
9792		}
9793		//$out .= ' /PieceInfo <<>>';
9794		if (!empty($this->pdflayers)) {
9795			$lyrobjs = '';
9796			$lyrobjs_off = '';
9797			$lyrobjs_lock = '';
9798			foreach ($this->pdflayers as $layer) {
9799				$layer_obj_ref = ' '.$layer['objid'].' 0 R';
9800				$lyrobjs .= $layer_obj_ref;
9801				if ($layer['view'] === false) {
9802					$lyrobjs_off .= $layer_obj_ref;
9803				}
9804				if ($layer['lock']) {
9805					$lyrobjs_lock .= $layer_obj_ref;
9806				}
9807			}
9808			$out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
9809			$out .= ' /D <<';
9810			$out .= ' /Name '.$this->_textstring('Layers', $oid);
9811			$out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
9812			$out .= ' /BaseState /ON';
9813			$out .= ' /OFF ['.$lyrobjs_off.']';
9814			$out .= ' /Locked ['.$lyrobjs_lock.']';
9815			$out .= ' /Intent /View';
9816			$out .= ' /AS [';
9817			$out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
9818			$out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
9819			$out .= ' ]';
9820			$out .= ' /Order ['.$lyrobjs.']';
9821			$out .= ' /ListMode /AllPages';
9822			//$out .= ' /RBGroups ['..']';
9823			//$out .= ' /Locked ['..']';
9824			$out .= ' >>';
9825			$out .= ' >>';
9826		}
9827		// AcroForm
9828		if (!empty($this->form_obj_id)
9829			OR ($this->sign AND isset($this->signature_data['cert_type']))
9830			OR !empty($this->empty_signature_appearance)) {
9831			$out .= ' /AcroForm <<';
9832			$objrefs = '';
9833			if ($this->sign AND isset($this->signature_data['cert_type'])) {
9834				// set reference for signature object
9835				$objrefs .= $this->sig_obj_id.' 0 R';
9836			}
9837			if (!empty($this->empty_signature_appearance)) {
9838				foreach ($this->empty_signature_appearance as $esa) {
9839					// set reference for empty signature objects
9840					$objrefs .= ' '.$esa['objid'].' 0 R';
9841				}
9842			}
9843			if (!empty($this->form_obj_id)) {
9844				foreach($this->form_obj_id as $objid) {
9845					$objrefs .= ' '.$objid.' 0 R';
9846				}
9847			}
9848			$out .= ' /Fields ['.$objrefs.']';
9849			// It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
9850			if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
9851				$out .= ' /NeedAppearances false';
9852			}
9853			if ($this->sign AND isset($this->signature_data['cert_type'])) {
9854				if ($this->signature_data['cert_type'] > 0) {
9855					$out .= ' /SigFlags 3';
9856				} else {
9857					$out .= ' /SigFlags 1';
9858				}
9859			}
9860			//$out .= ' /CO ';
9861			if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
9862				$out .= ' /DR <<';
9863				$out .= ' /Font <<';
9864				foreach ($this->annotation_fonts as $fontkey => $fontid) {
9865					$out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9866				}
9867				$out .= ' >> >>';
9868			}
9869			$font = $this->getFontBuffer('helvetica');
9870			$out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
9871			$out .= ' /Q '.(($this->rtl)?'2':'0');
9872			//$out .= ' /XFA ';
9873			$out .= ' >>';
9874			// signatures
9875			if ($this->sign AND isset($this->signature_data['cert_type'])
9876				AND (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A'))) {
9877				if ($this->signature_data['cert_type'] > 0) {
9878					$out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
9879				} else {
9880					$out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
9881				}
9882			}
9883		}
9884		//$out .= ' /Legal <<>>';
9885		//$out .= ' /Requirements []';
9886		//$out .= ' /Collection <<>>';
9887		//$out .= ' /NeedsRendering true';
9888		$out .= ' >>';
9889		$out .= "\n".'endobj';
9890		$this->_out($out);
9891		return $oid;
9892	}
9893
9894	/**
9895	 * Output viewer preferences.
9896	 * @return string for viewer preferences
9897	 * @author Nicola asuni
9898	 * @since 3.1.000 (2008-06-09)
9899	 * @protected
9900	 */
9901	protected function _putviewerpreferences() {
9902		$vp = $this->viewer_preferences;
9903		$out = ' /ViewerPreferences <<';
9904		if ($this->rtl) {
9905			$out .= ' /Direction /R2L';
9906		} else {
9907			$out .= ' /Direction /L2R';
9908		}
9909		if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) {
9910			$out .= ' /HideToolbar true';
9911		}
9912		if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) {
9913			$out .= ' /HideMenubar true';
9914		}
9915		if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) {
9916			$out .= ' /HideWindowUI true';
9917		}
9918		if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) {
9919			$out .= ' /FitWindow true';
9920		}
9921		if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) {
9922			$out .= ' /CenterWindow true';
9923		}
9924		if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) {
9925			$out .= ' /DisplayDocTitle true';
9926		}
9927		if (isset($vp['NonFullScreenPageMode'])) {
9928			$out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode'];
9929		}
9930		if (isset($vp['ViewArea'])) {
9931			$out .= ' /ViewArea /'.$vp['ViewArea'];
9932		}
9933		if (isset($vp['ViewClip'])) {
9934			$out .= ' /ViewClip /'.$vp['ViewClip'];
9935		}
9936		if (isset($vp['PrintArea'])) {
9937			$out .= ' /PrintArea /'.$vp['PrintArea'];
9938		}
9939		if (isset($vp['PrintClip'])) {
9940			$out .= ' /PrintClip /'.$vp['PrintClip'];
9941		}
9942		if (isset($vp['PrintScaling'])) {
9943			$out .= ' /PrintScaling /'.$vp['PrintScaling'];
9944		}
9945		if (isset($vp['Duplex']) AND (!TCPDF_STATIC::empty_string($vp['Duplex']))) {
9946			$out .= ' /Duplex /'.$vp['Duplex'];
9947		}
9948		if (isset($vp['PickTrayByPDFSize'])) {
9949			if ($vp['PickTrayByPDFSize']) {
9950				$out .= ' /PickTrayByPDFSize true';
9951			} else {
9952				$out .= ' /PickTrayByPDFSize false';
9953			}
9954		}
9955		if (isset($vp['PrintPageRange'])) {
9956			$PrintPageRangeNum = '';
9957			foreach ($vp['PrintPageRange'] as $k => $v) {
9958				$PrintPageRangeNum .= ' '.($v - 1).'';
9959			}
9960			$out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
9961		}
9962		if (isset($vp['NumCopies'])) {
9963			$out .= ' /NumCopies '.intval($vp['NumCopies']);
9964		}
9965		$out .= ' >>';
9966		return $out;
9967	}
9968
9969	/**
9970	 * Output PDF File Header (7.5.2).
9971	 * @protected
9972	 */
9973	protected function _putheader() {
9974		$this->_out('%PDF-'.$this->PDFVersion);
9975		$this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
9976	}
9977
9978	/**
9979	 * Output end of document (EOF).
9980	 * @protected
9981	 */
9982	protected function _enddoc() {
9983		if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
9984			// save subset chars of the previous font
9985			$this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
9986		}
9987		$this->state = 1;
9988		$this->_putheader();
9989		$this->_putpages();
9990		$this->_putresources();
9991		// empty signature fields
9992		if (!empty($this->empty_signature_appearance)) {
9993			foreach ($this->empty_signature_appearance as $key => $esa) {
9994				// widget annotation for empty signature
9995				$out = $this->_getobj($esa['objid'])."\n";
9996				$out .= '<< /Type /Annot';
9997				$out .= ' /Subtype /Widget';
9998				$out .= ' /Rect ['.$esa['rect'].']';
9999				$out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page
10000				$out .= ' /F 4';
10001				$out .= ' /FT /Sig';
10002				$signame = $esa['name'].sprintf(' [%03d]', ($key + 1));
10003				$out .= ' /T '.$this->_textstring($signame, $esa['objid']);
10004				$out .= ' /Ff 0';
10005				$out .= ' >>';
10006				$out .= "\n".'endobj';
10007				$this->_out($out);
10008			}
10009		}
10010		// Signature
10011		if ($this->sign AND isset($this->signature_data['cert_type'])) {
10012			// widget annotation for signature
10013			$out = $this->_getobj($this->sig_obj_id)."\n";
10014			$out .= '<< /Type /Annot';
10015			$out .= ' /Subtype /Widget';
10016			$out .= ' /Rect ['.$this->signature_appearance['rect'].']';
10017			$out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
10018			$out .= ' /F 4';
10019			$out .= ' /FT /Sig';
10020			$out .= ' /T '.$this->_textstring($this->signature_appearance['name'], $this->sig_obj_id);
10021			$out .= ' /Ff 0';
10022			$out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
10023			$out .= ' >>';
10024			$out .= "\n".'endobj';
10025			$this->_out($out);
10026			// signature
10027			$this->_putsignature();
10028		}
10029		// Info
10030		$objid_info = $this->_putinfo();
10031		// Catalog
10032		$objid_catalog = $this->_putcatalog();
10033		// Cross-ref
10034		$o = $this->bufferlen;
10035		// XREF section
10036		$this->_out('xref');
10037		$this->_out('0 '.($this->n + 1));
10038		$this->_out('0000000000 65535 f ');
10039		$freegen = ($this->n + 2);
10040		for ($i=1; $i <= $this->n; ++$i) {
10041			if (!isset($this->offsets[$i]) AND ($i > 1)) {
10042				$this->_out(sprintf('0000000000 %05d f ', $freegen));
10043				++$freegen;
10044			} else {
10045				$this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
10046			}
10047		}
10048		// TRAILER
10049		$out = 'trailer'."\n";
10050		$out .= '<<';
10051		$out .= ' /Size '.($this->n + 1);
10052		$out .= ' /Root '.$objid_catalog.' 0 R';
10053		$out .= ' /Info '.$objid_info.' 0 R';
10054		if ($this->encrypted) {
10055			$out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
10056		}
10057		$out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
10058		$out .= ' >>';
10059		$this->_out($out);
10060		$this->_out('startxref');
10061		$this->_out($o);
10062		$this->_out('%%EOF');
10063		$this->state = 3; // end-of-doc
10064		if ($this->diskcache) {
10065			// remove temporary files used for images
10066			foreach ($this->imagekeys as $key) {
10067				// remove temporary files
10068				unlink($this->images[$key]);
10069			}
10070			foreach ($this->fontkeys as $key) {
10071				// remove temporary files
10072				unlink($this->fonts[$key]);
10073			}
10074		}
10075	}
10076
10077	/**
10078	 * Initialize a new page.
10079	 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
10080	 * @param $format (mixed) 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().
10081	 * @protected
10082	 * @see getPageSizeFromFormat(), setPageFormat()
10083	 */
10084	protected function _beginpage($orientation='', $format='') {
10085		++$this->page;
10086		$this->pageobjects[$this->page] = array();
10087		$this->setPageBuffer($this->page, '');
10088		// initialize array for graphics tranformation positions inside a page buffer
10089		$this->transfmrk[$this->page] = array();
10090		$this->state = 2;
10091		if (TCPDF_STATIC::empty_string($orientation)) {
10092			if (isset($this->CurOrientation)) {
10093				$orientation = $this->CurOrientation;
10094			} elseif ($this->fwPt > $this->fhPt) {
10095				// landscape
10096				$orientation = 'L';
10097			} else {
10098				// portrait
10099				$orientation = 'P';
10100			}
10101		}
10102		if (TCPDF_STATIC::empty_string($format)) {
10103			$this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
10104			$this->setPageOrientation($orientation);
10105		} else {
10106			$this->setPageFormat($format, $orientation);
10107		}
10108		if ($this->rtl) {
10109			$this->x = $this->w - $this->rMargin;
10110		} else {
10111			$this->x = $this->lMargin;
10112		}
10113		$this->y = $this->tMargin;
10114		if (isset($this->newpagegroup[$this->page])) {
10115			// start a new group
10116			$this->currpagegroup = $this->newpagegroup[$this->page];
10117			$this->pagegroups[$this->currpagegroup] = 1;
10118		} elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
10119			++$this->pagegroups[$this->currpagegroup];
10120		}
10121	}
10122
10123	/**
10124	 * Mark end of page.
10125	 * @protected
10126	 */
10127	protected function _endpage() {
10128		$this->setVisibility('all');
10129		$this->state = 1;
10130	}
10131
10132	/**
10133	 * Begin a new object and return the object number.
10134	 * @return int object number
10135	 * @protected
10136	 */
10137	protected function _newobj() {
10138		$this->_out($this->_getobj());
10139		return $this->n;
10140	}
10141
10142	/**
10143	 * Return the starting object string for the selected object ID.
10144	 * @param $objid (int) Object ID (leave empty to get a new ID).
10145	 * @return string the starting object string
10146	 * @protected
10147	 * @since 5.8.009 (2010-08-20)
10148	 */
10149	protected function _getobj($objid='') {
10150		if ($objid === '') {
10151			++$this->n;
10152			$objid = $this->n;
10153		}
10154		$this->offsets[$objid] = $this->bufferlen;
10155		$this->pageobjects[$this->page][] = $objid;
10156		return $objid.' 0 obj';
10157	}
10158
10159	/**
10160	 * Underline text.
10161	 * @param $x (int) X coordinate
10162	 * @param $y (int) Y coordinate
10163	 * @param $txt (string) text to underline
10164	 * @protected
10165	 */
10166	protected function _dounderline($x, $y, $txt) {
10167		$w = $this->GetStringWidth($txt);
10168		return $this->_dounderlinew($x, $y, $w);
10169	}
10170
10171	/**
10172	 * Underline for rectangular text area.
10173	 * @param $x (int) X coordinate
10174	 * @param $y (int) Y coordinate
10175	 * @param $w (int) width to underline
10176	 * @protected
10177	 * @since 4.8.008 (2009-09-29)
10178	 */
10179	protected function _dounderlinew($x, $y, $w) {
10180		$linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10181		return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
10182	}
10183
10184	/**
10185	 * Line through text.
10186	 * @param $x (int) X coordinate
10187	 * @param $y (int) Y coordinate
10188	 * @param $txt (string) text to linethrough
10189	 * @protected
10190	 */
10191	protected function _dolinethrough($x, $y, $txt) {
10192		$w = $this->GetStringWidth($txt);
10193		return $this->_dolinethroughw($x, $y, $w);
10194	}
10195
10196	/**
10197	 * Line through for rectangular text area.
10198	 * @param $x (int) X coordinate
10199	 * @param $y (int) Y coordinate
10200	 * @param $w (int) line length (width)
10201	 * @protected
10202	 * @since 4.9.008 (2009-09-29)
10203	 */
10204	protected function _dolinethroughw($x, $y, $w) {
10205		$linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10206		return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew);
10207	}
10208
10209	/**
10210	 * Overline text.
10211	 * @param $x (int) X coordinate
10212	 * @param $y (int) Y coordinate
10213	 * @param $txt (string) text to overline
10214	 * @protected
10215	 * @since 4.9.015 (2010-04-19)
10216	 */
10217	protected function _dooverline($x, $y, $txt) {
10218		$w = $this->GetStringWidth($txt);
10219		return $this->_dooverlinew($x, $y, $w);
10220	}
10221
10222	/**
10223	 * Overline for rectangular text area.
10224	 * @param $x (int) X coordinate
10225	 * @param $y (int) Y coordinate
10226	 * @param $w (int) width to overline
10227	 * @protected
10228	 * @since 4.9.015 (2010-04-19)
10229	 */
10230	protected function _dooverlinew($x, $y, $w) {
10231		$linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10232		return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
10233
10234	}
10235
10236	/**
10237	 * Format a data string for meta information
10238	 * @param $s (string) data string to escape.
10239	 * @param $n (int) object ID
10240	 * @return string escaped string.
10241	 * @protected
10242	 */
10243	protected function _datastring($s, $n=0) {
10244		if ($n == 0) {
10245			$n = $this->n;
10246		}
10247		$s = $this->_encrypt_data($n, $s);
10248		return '('. TCPDF_STATIC::_escape($s).')';
10249	}
10250
10251	/**
10252	 * Set the document creation timestamp
10253	 * @param $time (mixed) Document creation timestamp in seconds or date-time string.
10254	 * @public
10255	 * @since 5.9.152 (2012-03-23)
10256	 */
10257	public function setDocCreationTimestamp($time) {
10258		if (is_string($time)) {
10259			$time = TCPDF_STATIC::getTimestamp($time);
10260		}
10261		$this->doc_creation_timestamp = intval($time);
10262	}
10263
10264	/**
10265	 * Set the document modification timestamp
10266	 * @param $time (mixed) Document modification timestamp in seconds or date-time string.
10267	 * @public
10268	 * @since 5.9.152 (2012-03-23)
10269	 */
10270	public function setDocModificationTimestamp($time) {
10271		if (is_string($time)) {
10272			$time = TCPDF_STATIC::getTimestamp($time);
10273		}
10274		$this->doc_modification_timestamp = intval($time);
10275	}
10276
10277	/**
10278	 * Returns document creation timestamp in seconds.
10279	 * @return (int) Creation timestamp in seconds.
10280	 * @public
10281	 * @since 5.9.152 (2012-03-23)
10282	 */
10283	public function getDocCreationTimestamp() {
10284		return $this->doc_creation_timestamp;
10285	}
10286
10287	/**
10288	 * Returns document modification timestamp in seconds.
10289	 * @return (int) Modfication timestamp in seconds.
10290	 * @public
10291	 * @since 5.9.152 (2012-03-23)
10292	 */
10293	public function getDocModificationTimestamp() {
10294		return $this->doc_modification_timestamp;
10295	}
10296
10297	/**
10298	 * Returns a formatted date for meta information
10299	 * @param $n (int) Object ID.
10300	 * @param $timestamp (int) Timestamp to convert.
10301	 * @return string escaped date string.
10302	 * @protected
10303	 * @since 4.6.028 (2009-08-25)
10304	 */
10305	protected function _datestring($n=0, $timestamp=0) {
10306		if ((empty($timestamp)) OR ($timestamp < 0)) {
10307			$timestamp = $this->doc_creation_timestamp;
10308		}
10309		return $this->_datastring('D:'.TCPDF_STATIC::getFormattedDate($timestamp), $n);
10310	}
10311
10312	/**
10313	 * Format a text string for meta information
10314	 * @param $s (string) string to escape.
10315	 * @param $n (int) object ID
10316	 * @return string escaped string.
10317	 * @protected
10318	 */
10319	protected function _textstring($s, $n=0) {
10320		if ($this->isunicode) {
10321			//Convert string to UTF-16BE
10322			$s = TCPDF_FONTS::UTF8ToUTF16BE($s, true, $this->isunicode, $this->CurrentFont);
10323		}
10324		return $this->_datastring($s, $n);
10325	}
10326
10327	/**
10328	 * THIS METHOD IS DEPRECATED
10329	 * Format a text string
10330	 * @param $s (string) string to escape.
10331	 * @return string escaped string.
10332	 * @protected
10333	 * @deprecated
10334	 */
10335	protected function _escapetext($s) {
10336		if ($this->isunicode) {
10337			if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
10338				$s = TCPDF_FONTS::UTF8ToLatin1($s, $this->isunicode, $this->CurrentFont);
10339			} else {
10340				//Convert string to UTF-16BE and reverse RTL language
10341				$s = TCPDF_FONTS::utf8StrRev($s, false, $this->tmprtl, $this->isunicode, $this->CurrentFont);
10342			}
10343		}
10344		return TCPDF_STATIC::_escape($s);
10345	}
10346
10347	/**
10348	 * get raw output stream.
10349	 * @param $s (string) string to output.
10350	 * @param $n (int) object reference for encryption mode
10351	 * @protected
10352	 * @author Nicola Asuni
10353	 * @since 5.5.000 (2010-06-22)
10354	 */
10355	protected function _getrawstream($s, $n=0) {
10356		if ($n <= 0) {
10357			// default to current object
10358			$n = $this->n;
10359		}
10360		return $this->_encrypt_data($n, $s);
10361	}
10362
10363	/**
10364	 * Format output stream (DEPRECATED).
10365	 * @param $s (string) string to output.
10366	 * @param $n (int) object reference for encryption mode
10367	 * @protected
10368	 * @deprecated
10369	 */
10370	protected function _getstream($s, $n=0) {
10371		return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream';
10372	}
10373
10374	/**
10375	 * Output a stream (DEPRECATED).
10376	 * @param $s (string) string to output.
10377	 * @param $n (int) object reference for encryption mode
10378	 * @protected
10379	 * @deprecated
10380	 */
10381	protected function _putstream($s, $n=0) {
10382		$this->_out($this->_getstream($s, $n));
10383	}
10384
10385	/**
10386	 * Output a string to the document.
10387	 * @param $s (string) string to output.
10388	 * @protected
10389	 */
10390	protected function _out($s) {
10391		if ($this->state == 2) {
10392			if ($this->inxobj) {
10393				// we are inside an XObject template
10394				$this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
10395			} elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
10396				// puts data before page footer
10397				$pagebuff = $this->getPageBuffer($this->page);
10398				$page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
10399				$footer = substr($pagebuff, -$this->footerlen[$this->page]);
10400				$this->setPageBuffer($this->page, $page.$s."\n".$footer);
10401				// update footer position
10402				$this->footerpos[$this->page] += strlen($s."\n");
10403			} else {
10404				// set page data
10405				$this->setPageBuffer($this->page, $s."\n", true);
10406			}
10407		} elseif ($this->state > 0) {
10408			// set general data
10409			$this->setBuffer($s."\n");
10410		}
10411	}
10412
10413	/**
10414	 * Set header font.
10415	 * @param $font (array) Array describing the basic font parameters: (family, style, size).
10416	 * @public
10417	 * @since 1.1
10418	 */
10419	public function setHeaderFont($font) {
10420		$this->header_font = $font;
10421	}
10422
10423	/**
10424	 * Get header font.
10425	 * @return array() Array describing the basic font parameters: (family, style, size).
10426	 * @public
10427	 * @since 4.0.012 (2008-07-24)
10428	 */
10429	public function getHeaderFont() {
10430		return $this->header_font;
10431	}
10432
10433	/**
10434	 * Set footer font.
10435	 * @param $font (array) Array describing the basic font parameters: (family, style, size).
10436	 * @public
10437	 * @since 1.1
10438	 */
10439	public function setFooterFont($font) {
10440		$this->footer_font = $font;
10441	}
10442
10443	/**
10444	 * Get Footer font.
10445	 * @return array() Array describing the basic font parameters: (family, style, size).
10446	 * @public
10447	 * @since 4.0.012 (2008-07-24)
10448	 */
10449	public function getFooterFont() {
10450		return $this->footer_font;
10451	}
10452
10453	/**
10454	 * Set language array.
10455	 * @param $language (array)
10456	 * @public
10457	 * @since 1.1
10458	 */
10459	public function setLanguageArray($language) {
10460		$this->l = $language;
10461		if (isset($this->l['a_meta_dir'])) {
10462			$this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
10463		} else {
10464			$this->rtl = false;
10465		}
10466	}
10467
10468	/**
10469	 * Returns the PDF data.
10470	 * @public
10471	 */
10472	public function getPDFData() {
10473		if ($this->state < 3) {
10474			$this->Close();
10475		}
10476		return $this->buffer;
10477	}
10478
10479	/**
10480	 * Output anchor link.
10481	 * @param $url (string) 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;)
10482	 * @param $name (string) link name
10483	 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
10484	 * @param $firstline (boolean) if true prints only the first line and return the remaining string.
10485	 * @param $color (array) array of RGB text color
10486	 * @param $style (string) font style (U, D, B, I)
10487	 * @param $firstblock (boolean) if true the string is the starting of a line.
10488	 * @return the number of cells used or the remaining text if $firstline = true;
10489	 * @public
10490	 */
10491	public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
10492		if (isset($url[1]) AND ($url[0] == '#')) {
10493			// convert url to internal link
10494			$lnkdata = explode(',', $url);
10495			if (isset($lnkdata[0]) ) {
10496				$page = substr($lnkdata[0], 1);
10497				if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
10498					$lnky = floatval($lnkdata[1]);
10499				} else {
10500					$lnky = 0;
10501				}
10502				$url = $this->AddLink();
10503				$this->SetLink($url, $lnky, $page);
10504			}
10505		}
10506		// store current settings
10507		$prevcolor = $this->fgcolor;
10508		$prevstyle = $this->FontStyle;
10509		if (empty($color)) {
10510			$this->SetTextColorArray($this->htmlLinkColorArray);
10511		} else {
10512			$this->SetTextColorArray($color);
10513		}
10514		if ($style == -1) {
10515			$this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
10516		} else {
10517			$this->SetFont('', $this->FontStyle.$style);
10518		}
10519		$ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
10520		// restore settings
10521		$this->SetFont('', $prevstyle);
10522		$this->SetTextColorArray($prevcolor);
10523		return $ret;
10524	}
10525
10526	/**
10527	 * Converts pixels to User's Units.
10528	 * @param $px (int) pixels
10529	 * @return float value in user's unit
10530	 * @public
10531	 * @see setImageScale(), getImageScale()
10532	 */
10533	public function pixelsToUnits($px) {
10534		return ($px / ($this->imgscale * $this->k));
10535	}
10536
10537	/**
10538	 * Reverse function for htmlentities.
10539	 * Convert entities in UTF-8.
10540	 * @param $text_to_convert (string) Text to convert.
10541	 * @return string converted text string
10542	 * @public
10543	 */
10544	public function unhtmlentities($text_to_convert) {
10545		return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
10546	}
10547
10548	// ENCRYPTION METHODS ----------------------------------
10549
10550	/**
10551	 * Compute encryption key depending on object number where the encrypted data is stored.
10552	 * This is used for all strings and streams without crypt filter specifier.
10553	 * @param $n (int) object number
10554	 * @return int object key
10555	 * @protected
10556	 * @author Nicola Asuni
10557	 * @since 2.0.000 (2008-01-02)
10558	 */
10559	protected function _objectkey($n) {
10560		$objkey = $this->encryptdata['key'].pack('VXxx', $n);
10561		if ($this->encryptdata['mode'] == 2) { // AES-128
10562			// AES padding
10563			$objkey .= "\x73\x41\x6C\x54"; // sAlT
10564		}
10565		$objkey = substr(TCPDF_STATIC::_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
10566		$objkey = substr($objkey, 0, 16);
10567		return $objkey;
10568	}
10569
10570	/**
10571	 * Encrypt the input string.
10572	 * @param $n (int) object number
10573	 * @param $s (string) data string to encrypt
10574	 * @return encrypted string
10575	 * @protected
10576	 * @author Nicola Asuni
10577	 * @since 5.0.005 (2010-05-11)
10578	 */
10579	protected function _encrypt_data($n, $s) {
10580		if (!$this->encrypted) {
10581			return $s;
10582		}
10583		switch ($this->encryptdata['mode']) {
10584			case 0:   // RC4-40
10585			case 1: { // RC4-128
10586				$s = TCPDF_STATIC::_RC4($this->_objectkey($n), $s, $this->last_enc_key, $this->last_enc_key_c);
10587				break;
10588			}
10589			case 2: { // AES-128
10590				$s = TCPDF_STATIC::_AES($this->_objectkey($n), $s);
10591				break;
10592			}
10593			case 3: { // AES-256
10594				$s = TCPDF_STATIC::_AES($this->encryptdata['key'], $s);
10595				break;
10596			}
10597		}
10598		return $s;
10599	}
10600
10601	/**
10602	 * Put encryption on PDF document.
10603	 * @protected
10604	 * @author Nicola Asuni
10605	 * @since 2.0.000 (2008-01-02)
10606	 */
10607	protected function _putencryption() {
10608		if (!$this->encrypted) {
10609			return;
10610		}
10611		$this->encryptdata['objid'] = $this->_newobj();
10612		$out = '<<';
10613		if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
10614			$this->encryptdata['Filter'] = 'Standard';
10615		}
10616		$out .= ' /Filter /'.$this->encryptdata['Filter'];
10617		if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
10618			$out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
10619		}
10620		if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
10621			$this->encryptdata['V'] = 1;
10622		}
10623		// V is a code specifying the algorithm to be used in encrypting and decrypting the document
10624		$out .= ' /V '.$this->encryptdata['V'];
10625		if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
10626			// The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
10627			$out .= ' /Length '.$this->encryptdata['Length'];
10628		} else {
10629			$out .= ' /Length 40';
10630		}
10631		if ($this->encryptdata['V'] >= 4) {
10632			if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
10633				$this->encryptdata['StmF'] = 'Identity';
10634			}
10635			if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
10636				// The name of the crypt filter that shall be used when decrypting all strings in the document.
10637				$this->encryptdata['StrF'] = 'Identity';
10638			}
10639			// A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
10640			if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
10641				$out .= ' /CF <<';
10642				$out .= ' /'.$this->encryptdata['StmF'].' <<';
10643				$out .= ' /Type /CryptFilter';
10644				if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
10645					// The method used
10646					$out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
10647					if ($this->encryptdata['pubkey']) {
10648						$out .= ' /Recipients [';
10649						foreach ($this->encryptdata['Recipients'] as $rec) {
10650							$out .= ' <'.$rec.'>';
10651						}
10652						$out .= ' ]';
10653						if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
10654							$out .= ' /EncryptMetadata false';
10655						} else {
10656							$out .= ' /EncryptMetadata true';
10657						}
10658					}
10659				} else {
10660					$out .= ' /CFM /None';
10661				}
10662				if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
10663					// The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
10664					$out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
10665				} else {
10666					$out .= ' /AuthEvent /DocOpen';
10667				}
10668				if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
10669					// The bit length of the encryption key.
10670					$out .= ' /Length '.$this->encryptdata['CF']['Length'];
10671				}
10672				$out .= ' >> >>';
10673			}
10674			// The name of the crypt filter that shall be used by default when decrypting streams.
10675			$out .= ' /StmF /'.$this->encryptdata['StmF'];
10676			// The name of the crypt filter that shall be used when decrypting all strings in the document.
10677			$out .= ' /StrF /'.$this->encryptdata['StrF'];
10678			if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
10679				// The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
10680				$out .= ' /EFF /'.$this->encryptdata[''];
10681			}
10682		}
10683		// Additional encryption dictionary entries for the standard security handler
10684		if ($this->encryptdata['pubkey']) {
10685			if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
10686				$out .= ' /Recipients [';
10687				foreach ($this->encryptdata['Recipients'] as $rec) {
10688					$out .= ' <'.$rec.'>';
10689				}
10690				$out .= ' ]';
10691			}
10692		} else {
10693			$out .= ' /R';
10694			if ($this->encryptdata['V'] == 5) { // AES-256
10695				$out .= ' 5';
10696				$out .= ' /OE ('.TCPDF_STATIC::_escape($this->encryptdata['OE']).')';
10697				$out .= ' /UE ('.TCPDF_STATIC::_escape($this->encryptdata['UE']).')';
10698				$out .= ' /Perms ('.TCPDF_STATIC::_escape($this->encryptdata['perms']).')';
10699			} elseif ($this->encryptdata['V'] == 4) { // AES-128
10700				$out .= ' 4';
10701			} elseif ($this->encryptdata['V'] < 2) { // RC-40
10702				$out .= ' 2';
10703			} else { // RC-128
10704				$out .= ' 3';
10705			}
10706			$out .= ' /O ('.TCPDF_STATIC::_escape($this->encryptdata['O']).')';
10707			$out .= ' /U ('.TCPDF_STATIC::_escape($this->encryptdata['U']).')';
10708			$out .= ' /P '.$this->encryptdata['P'];
10709			if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
10710				$out .= ' /EncryptMetadata false';
10711			} else {
10712				$out .= ' /EncryptMetadata true';
10713			}
10714		}
10715		$out .= ' >>';
10716		$out .= "\n".'endobj';
10717		$this->_out($out);
10718	}
10719
10720	/**
10721	 * Compute U value (used for encryption)
10722	 * @return string U value
10723	 * @protected
10724	 * @since 2.0.000 (2008-01-02)
10725	 * @author Nicola Asuni
10726	 */
10727	protected function _Uvalue() {
10728		if ($this->encryptdata['mode'] == 0) { // RC4-40
10729			return TCPDF_STATIC::_RC4($this->encryptdata['key'], TCPDF_STATIC::$enc_padding, $this->last_enc_key, $this->last_enc_key_c);
10730		} elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
10731			$tmp = TCPDF_STATIC::_md5_16(TCPDF_STATIC::$enc_padding.$this->encryptdata['fileid']);
10732			$enc = TCPDF_STATIC::_RC4($this->encryptdata['key'], $tmp, $this->last_enc_key, $this->last_enc_key_c);
10733			$len = strlen($tmp);
10734			for ($i = 1; $i <= 19; ++$i) {
10735				$ek = '';
10736				for ($j = 0; $j < $len; ++$j) {
10737					$ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i);
10738				}
10739				$enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10740			}
10741			$enc .= str_repeat("\x00", 16);
10742			return substr($enc, 0, 32);
10743		} elseif ($this->encryptdata['mode'] == 3) { // AES-256
10744			$seed = TCPDF_STATIC::_md5_16(TCPDF_STATIC::getRandomSeed());
10745			// User Validation Salt
10746			$this->encryptdata['UVS'] = substr($seed, 0, 8);
10747			// User Key Salt
10748			$this->encryptdata['UKS'] = substr($seed, 8, 16);
10749			return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
10750		}
10751	}
10752
10753	/**
10754	 * Compute UE value (used for encryption)
10755	 * @return string UE value
10756	 * @protected
10757	 * @since 5.9.006 (2010-10-19)
10758	 * @author Nicola Asuni
10759	 */
10760	protected function _UEvalue() {
10761		$hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
10762		$iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
10763		return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
10764	}
10765
10766	/**
10767	 * Compute O value (used for encryption)
10768	 * @return string O value
10769	 * @protected
10770	 * @since 2.0.000 (2008-01-02)
10771	 * @author Nicola Asuni
10772	 */
10773	protected function _Ovalue() {
10774		if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
10775			$tmp = TCPDF_STATIC::_md5_16($this->encryptdata['owner_password']);
10776			if ($this->encryptdata['mode'] > 0) {
10777				for ($i = 0; $i < 50; ++$i) {
10778					$tmp = TCPDF_STATIC::_md5_16($tmp);
10779				}
10780			}
10781			$owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
10782			$enc = TCPDF_STATIC::_RC4($owner_key, $this->encryptdata['user_password'], $this->last_enc_key, $this->last_enc_key_c);
10783			if ($this->encryptdata['mode'] > 0) {
10784				$len = strlen($owner_key);
10785				for ($i = 1; $i <= 19; ++$i) {
10786					$ek = '';
10787					for ($j = 0; $j < $len; ++$j) {
10788						$ek .= chr(ord($owner_key[$j]) ^ $i);
10789					}
10790					$enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10791				}
10792			}
10793			return $enc;
10794		} elseif ($this->encryptdata['mode'] == 3) { // AES-256
10795			$seed = TCPDF_STATIC::_md5_16(TCPDF_STATIC::getRandomSeed());
10796			// Owner Validation Salt
10797			$this->encryptdata['OVS'] = substr($seed, 0, 8);
10798			// Owner Key Salt
10799			$this->encryptdata['OKS'] = substr($seed, 8, 16);
10800			return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
10801		}
10802	}
10803
10804	/**
10805	 * Compute OE value (used for encryption)
10806	 * @return string OE value
10807	 * @protected
10808	 * @since 5.9.006 (2010-10-19)
10809	 * @author Nicola Asuni
10810	 */
10811	protected function _OEvalue() {
10812		$hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
10813		$iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
10814		return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
10815	}
10816
10817	/**
10818	 * Convert password for AES-256 encryption mode
10819	 * @param $password (string) password
10820	 * @return string password
10821	 * @protected
10822	 * @since 5.9.006 (2010-10-19)
10823	 * @author Nicola Asuni
10824	 */
10825	protected function _fixAES256Password($password) {
10826		$psw = ''; // password to be returned
10827		$psw_array = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($password, $this->isunicode, $this->CurrentFont), $password, $this->rtl, $this->isunicode, $this->CurrentFont);
10828		foreach ($psw_array as $c) {
10829			$psw .= TCPDF_FONTS::unichr($c, $this->isunicode);
10830		}
10831		return substr($psw, 0, 127);
10832	}
10833
10834	/**
10835	 * Compute encryption key
10836	 * @protected
10837	 * @since 2.0.000 (2008-01-02)
10838	 * @author Nicola Asuni
10839	 */
10840	protected function _generateencryptionkey() {
10841		$keybytelen = ($this->encryptdata['Length'] / 8);
10842		if (!$this->encryptdata['pubkey']) { // standard mode
10843			if ($this->encryptdata['mode'] == 3) { // AES-256
10844				// generate 256 bit random key
10845				$this->encryptdata['key'] = substr(hash('sha256', TCPDF_STATIC::getRandomSeed(), true), 0, $keybytelen);
10846				// truncate passwords
10847				$this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
10848				$this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
10849				// Compute U value
10850				$this->encryptdata['U'] = $this->_Uvalue();
10851				// Compute UE value
10852				$this->encryptdata['UE'] = $this->_UEvalue();
10853				// Compute O value
10854				$this->encryptdata['O'] = $this->_Ovalue();
10855				// Compute OE value
10856				$this->encryptdata['OE'] = $this->_OEvalue();
10857				// Compute P value
10858				$this->encryptdata['P'] = $this->encryptdata['protection'];
10859				// Computing the encryption dictionary's Perms (permissions) value
10860				$perms = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
10861				$perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
10862				if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
10863					$perms .= 'F';
10864				} else {
10865					$perms .= 'T';
10866				}
10867				$perms .= 'adb'; // bytes 9-11
10868				$perms .= 'nick'; // bytes 12-15
10869				$iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB));
10870				$this->encryptdata['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->encryptdata['key'], $perms, MCRYPT_MODE_ECB, $iv);
10871			} else { // RC4-40, RC4-128, AES-128
10872				// Pad passwords
10873				$this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10874				$this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10875				// Compute O value
10876				$this->encryptdata['O'] = $this->_Ovalue();
10877				// get default permissions (reverse byte order)
10878				$permissions = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']);
10879				// Compute encryption key
10880				$tmp = TCPDF_STATIC::_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
10881				if ($this->encryptdata['mode'] > 0) {
10882					for ($i = 0; $i < 50; ++$i) {
10883						$tmp = TCPDF_STATIC::_md5_16(substr($tmp, 0, $keybytelen));
10884					}
10885				}
10886				$this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
10887				// Compute U value
10888				$this->encryptdata['U'] = $this->_Uvalue();
10889				// Compute P value
10890				$this->encryptdata['P'] = $this->encryptdata['protection'];
10891			}
10892		} else { // Public-Key mode
10893			// random 20-byte seed
10894			$seed = sha1(TCPDF_STATIC::getRandomSeed(), true);
10895			$recipient_bytes = '';
10896			foreach ($this->encryptdata['pubkeys'] as $pubkey) {
10897				// for each public certificate
10898				if (isset($pubkey['p'])) {
10899					$pkprotection = TCPDF_STATIC::getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
10900				} else {
10901					$pkprotection = $this->encryptdata['protection'];
10902				}
10903				// get default permissions (reverse byte order)
10904				$pkpermissions = TCPDF_STATIC::getEncPermissionsString($pkprotection);
10905				// envelope data
10906				$envelope = $seed.$pkpermissions;
10907				// write the envelope data to a temporary file
10908				$tempkeyfile = TCPDF_STATIC::getObjFilename('key');
10909				$f = fopen($tempkeyfile, 'wb');
10910				if (!$f) {
10911					$this->Error('Unable to create temporary key file: '.$tempkeyfile);
10912				}
10913				$envelope_length = strlen($envelope);
10914				fwrite($f, $envelope, $envelope_length);
10915				fclose($f);
10916				$tempencfile = TCPDF_STATIC::getObjFilename('enc');
10917				if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) {
10918					$this->Error('Unable to encrypt the file: '.$tempkeyfile);
10919				}
10920				unlink($tempkeyfile);
10921				// read encryption signature
10922				$signature = file_get_contents($tempencfile, false, null, $envelope_length);
10923				unlink($tempencfile);
10924				// extract signature
10925				$signature = substr($signature, strpos($signature, 'Content-Disposition'));
10926				$tmparr = explode("\n\n", $signature);
10927				$signature = trim($tmparr[1]);
10928				unset($tmparr);
10929				// decode signature
10930				$signature = base64_decode($signature);
10931				// convert signature to hex
10932				$hexsignature = current(unpack('H*', $signature));
10933				// store signature on recipients array
10934				$this->encryptdata['Recipients'][] = $hexsignature;
10935				// The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
10936				$recipient_bytes .= $signature;
10937			}
10938			// calculate encryption key
10939			if ($this->encryptdata['mode'] == 3) { // AES-256
10940				$this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
10941			} else { // RC4-40, RC4-128, AES-128
10942				$this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
10943			}
10944		}
10945	}
10946
10947	/**
10948	 * Set document protection
10949	 * Remark: the protection against modification is for people who have the full Acrobat product.
10950	 * 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.
10951	 * 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.
10952	 * @param $permissions (Array) 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>
10953	 * @param $user_pass (String) user password. Empty by default.
10954	 * @param $owner_pass (String) owner password. If not specified, a random value is used.
10955	 * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
10956	 * @param $pubkeys (String) 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')))
10957	 * @public
10958	 * @since 2.0.000 (2008-01-02)
10959	 * @author Nicola Asuni
10960	 */
10961	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) {
10962		if ($this->pdfa_mode) {
10963			// encryption is not allowed in PDF/A mode
10964			return;
10965		}
10966		$this->encryptdata['protection'] = TCPDF_STATIC::getUserPermissionCode($permissions, $mode);
10967		if (($pubkeys !== null) AND (is_array($pubkeys))) {
10968			// public-key mode
10969			$this->encryptdata['pubkeys'] = $pubkeys;
10970			if ($mode == 0) {
10971				// public-Key Security requires at least 128 bit
10972				$mode = 1;
10973			}
10974			if (!function_exists('openssl_pkcs7_encrypt')) {
10975				$this->Error('Public-Key Security requires openssl library.');
10976			}
10977			// Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
10978			$this->encryptdata['pubkey'] = true;
10979			$this->encryptdata['Filter'] = 'Adobe.PubSec';
10980			$this->encryptdata['StmF'] = 'DefaultCryptFilter';
10981			$this->encryptdata['StrF'] = 'DefaultCryptFilter';
10982		} else {
10983			// standard mode (password mode)
10984			$this->encryptdata['pubkey'] = false;
10985			$this->encryptdata['Filter'] = 'Standard';
10986			$this->encryptdata['StmF'] = 'StdCF';
10987			$this->encryptdata['StrF'] = 'StdCF';
10988		}
10989		if ($mode > 1) { // AES
10990			if (!extension_loaded('mcrypt')) {
10991				$this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).');
10992			}
10993			if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
10994				$this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
10995			}
10996			if (($mode == 3) AND !function_exists('hash')) {
10997				// the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
10998				$this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
10999			}
11000		}
11001		if ($owner_pass === null) {
11002			$owner_pass = md5(TCPDF_STATIC::getRandomSeed());
11003		}
11004		$this->encryptdata['user_password'] = $user_pass;
11005		$this->encryptdata['owner_password'] = $owner_pass;
11006		$this->encryptdata['mode'] = $mode;
11007		switch ($mode) {
11008			case 0: { // RC4 40 bit
11009				$this->encryptdata['V'] = 1;
11010				$this->encryptdata['Length'] = 40;
11011				$this->encryptdata['CF']['CFM'] = 'V2';
11012				break;
11013			}
11014			case 1: { // RC4 128 bit
11015				$this->encryptdata['V'] = 2;
11016				$this->encryptdata['Length'] = 128;
11017				$this->encryptdata['CF']['CFM'] = 'V2';
11018				if ($this->encryptdata['pubkey']) {
11019					$this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
11020					$this->encryptdata['Recipients'] = array();
11021				}
11022				break;
11023			}
11024			case 2: { // AES 128 bit
11025				$this->encryptdata['V'] = 4;
11026				$this->encryptdata['Length'] = 128;
11027				$this->encryptdata['CF']['CFM'] = 'AESV2';
11028				$this->encryptdata['CF']['Length'] = 128;
11029				if ($this->encryptdata['pubkey']) {
11030					$this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
11031					$this->encryptdata['Recipients'] = array();
11032				}
11033				break;
11034			}
11035			case 3: { // AES 256 bit
11036				$this->encryptdata['V'] = 5;
11037				$this->encryptdata['Length'] = 256;
11038				$this->encryptdata['CF']['CFM'] = 'AESV3';
11039				$this->encryptdata['CF']['Length'] = 256;
11040				if ($this->encryptdata['pubkey']) {
11041					$this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
11042					$this->encryptdata['Recipients'] = array();
11043				}
11044				break;
11045			}
11046		}
11047		$this->encrypted = true;
11048		$this->encryptdata['fileid'] = TCPDF_STATIC::convertHexStringToString($this->file_id);
11049		$this->_generateencryptionkey();
11050	}
11051
11052	// END OF ENCRYPTION FUNCTIONS -------------------------
11053
11054	// START TRANSFORMATIONS SECTION -----------------------
11055
11056	/**
11057	 * Starts a 2D tranformation saving current graphic state.
11058	 * This function must be called before scaling, mirroring, translation, rotation and skewing.
11059	 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
11060	 * @public
11061	 * @since 2.1.000 (2008-01-07)
11062	 * @see StartTransform(), StopTransform()
11063	 */
11064	public function StartTransform() {
11065		if ($this->state != 2) {
11066			return;
11067		}
11068		$this->_outSaveGraphicsState();
11069		if ($this->inxobj) {
11070			// we are inside an XObject template
11071			$this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
11072		} else {
11073			$this->transfmrk[$this->page][] = $this->pagelen[$this->page];
11074		}
11075		++$this->transfmatrix_key;
11076		$this->transfmatrix[$this->transfmatrix_key] = array();
11077	}
11078
11079	/**
11080	 * Stops a 2D tranformation restoring previous graphic state.
11081	 * This function must be called after scaling, mirroring, translation, rotation and skewing.
11082	 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
11083	 * @public
11084	 * @since 2.1.000 (2008-01-07)
11085	 * @see StartTransform(), StopTransform()
11086	 */
11087	public function StopTransform() {
11088		if ($this->state != 2) {
11089			return;
11090		}
11091		$this->_outRestoreGraphicsState();
11092		if (isset($this->transfmatrix[$this->transfmatrix_key])) {
11093			array_pop($this->transfmatrix[$this->transfmatrix_key]);
11094			--$this->transfmatrix_key;
11095		}
11096		if ($this->inxobj) {
11097			// we are inside an XObject template
11098			array_pop($this->xobjects[$this->xobjid]['transfmrk']);
11099		} else {
11100			array_pop($this->transfmrk[$this->page]);
11101		}
11102	}
11103	/**
11104	 * Horizontal Scaling.
11105	 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
11106	 * @param $x (int) abscissa of the scaling center. Default is current x position
11107	 * @param $y (int) ordinate of the scaling center. Default is current y position
11108	 * @public
11109	 * @since 2.1.000 (2008-01-07)
11110	 * @see StartTransform(), StopTransform()
11111	 */
11112	public function ScaleX($s_x, $x='', $y='') {
11113		$this->Scale($s_x, 100, $x, $y);
11114	}
11115
11116	/**
11117	 * Vertical Scaling.
11118	 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
11119	 * @param $x (int) abscissa of the scaling center. Default is current x position
11120	 * @param $y (int) ordinate of the scaling center. Default is current y position
11121	 * @public
11122	 * @since 2.1.000 (2008-01-07)
11123	 * @see StartTransform(), StopTransform()
11124	 */
11125	public function ScaleY($s_y, $x='', $y='') {
11126		$this->Scale(100, $s_y, $x, $y);
11127	}
11128
11129	/**
11130	 * Vertical and horizontal proportional Scaling.
11131	 * @param $s (float) scaling factor for width and height as percent. 0 is not allowed.
11132	 * @param $x (int) abscissa of the scaling center. Default is current x position
11133	 * @param $y (int) ordinate of the scaling center. Default is current y position
11134	 * @public
11135	 * @since 2.1.000 (2008-01-07)
11136	 * @see StartTransform(), StopTransform()
11137	 */
11138	public function ScaleXY($s, $x='', $y='') {
11139		$this->Scale($s, $s, $x, $y);
11140	}
11141
11142	/**
11143	 * Vertical and horizontal non-proportional Scaling.
11144	 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
11145	 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
11146	 * @param $x (int) abscissa of the scaling center. Default is current x position
11147	 * @param $y (int) ordinate of the scaling center. Default is current y position
11148	 * @public
11149	 * @since 2.1.000 (2008-01-07)
11150	 * @see StartTransform(), StopTransform()
11151	 */
11152	public function Scale($s_x, $s_y, $x='', $y='') {
11153		if ($x === '') {
11154			$x = $this->x;
11155		}
11156		if ($y === '') {
11157			$y = $this->y;
11158		}
11159		if (($s_x == 0) OR ($s_y == 0)) {
11160			$this->Error('Please do not use values equal to zero for scaling');
11161		}
11162		$y = ($this->h - $y) * $this->k;
11163		$x *= $this->k;
11164		//calculate elements of transformation matrix
11165		$s_x /= 100;
11166		$s_y /= 100;
11167		$tm = array();
11168		$tm[0] = $s_x;
11169		$tm[1] = 0;
11170		$tm[2] = 0;
11171		$tm[3] = $s_y;
11172		$tm[4] = $x * (1 - $s_x);
11173		$tm[5] = $y * (1 - $s_y);
11174		//scale the coordinate system
11175		$this->Transform($tm);
11176	}
11177
11178	/**
11179	 * Horizontal Mirroring.
11180	 * @param $x (int) abscissa of the point. Default is current x position
11181	 * @public
11182	 * @since 2.1.000 (2008-01-07)
11183	 * @see StartTransform(), StopTransform()
11184	 */
11185	public function MirrorH($x='') {
11186		$this->Scale(-100, 100, $x);
11187	}
11188
11189	/**
11190	 * Verical Mirroring.
11191	 * @param $y (int) ordinate of the point. Default is current y position
11192	 * @public
11193	 * @since 2.1.000 (2008-01-07)
11194	 * @see StartTransform(), StopTransform()
11195	 */
11196	public function MirrorV($y='') {
11197		$this->Scale(100, -100, '', $y);
11198	}
11199
11200	/**
11201	 * Point reflection mirroring.
11202	 * @param $x (int) abscissa of the point. Default is current x position
11203	 * @param $y (int) ordinate of the point. Default is current y position
11204	 * @public
11205	 * @since 2.1.000 (2008-01-07)
11206	 * @see StartTransform(), StopTransform()
11207	 */
11208	public function MirrorP($x='',$y='') {
11209		$this->Scale(-100, -100, $x, $y);
11210	}
11211
11212	/**
11213	 * Reflection against a straight line through point (x, y) with the gradient angle (angle).
11214	 * @param $angle (float) gradient angle of the straight line. Default is 0 (horizontal line).
11215	 * @param $x (int) abscissa of the point. Default is current x position
11216	 * @param $y (int) ordinate of the point. Default is current y position
11217	 * @public
11218	 * @since 2.1.000 (2008-01-07)
11219	 * @see StartTransform(), StopTransform()
11220	 */
11221	public function MirrorL($angle=0, $x='',$y='') {
11222		$this->Scale(-100, 100, $x, $y);
11223		$this->Rotate(-2*($angle-90), $x, $y);
11224	}
11225
11226	/**
11227	 * Translate graphic object horizontally.
11228	 * @param $t_x (int) movement to the right (or left for RTL)
11229	 * @public
11230	 * @since 2.1.000 (2008-01-07)
11231	 * @see StartTransform(), StopTransform()
11232	 */
11233	public function TranslateX($t_x) {
11234		$this->Translate($t_x, 0);
11235	}
11236
11237	/**
11238	 * Translate graphic object vertically.
11239	 * @param $t_y (int) movement to the bottom
11240	 * @public
11241	 * @since 2.1.000 (2008-01-07)
11242	 * @see StartTransform(), StopTransform()
11243	 */
11244	public function TranslateY($t_y) {
11245		$this->Translate(0, $t_y);
11246	}
11247
11248	/**
11249	 * Translate graphic object horizontally and vertically.
11250	 * @param $t_x (int) movement to the right
11251	 * @param $t_y (int) movement to the bottom
11252	 * @public
11253	 * @since 2.1.000 (2008-01-07)
11254	 * @see StartTransform(), StopTransform()
11255	 */
11256	public function Translate($t_x, $t_y) {
11257		//calculate elements of transformation matrix
11258		$tm = array();
11259		$tm[0] = 1;
11260		$tm[1] = 0;
11261		$tm[2] = 0;
11262		$tm[3] = 1;
11263		$tm[4] = $t_x * $this->k;
11264		$tm[5] = -$t_y * $this->k;
11265		//translate the coordinate system
11266		$this->Transform($tm);
11267	}
11268
11269	/**
11270	 * Rotate object.
11271	 * @param $angle (float) angle in degrees for counter-clockwise rotation
11272	 * @param $x (int) abscissa of the rotation center. Default is current x position
11273	 * @param $y (int) ordinate of the rotation center. Default is current y position
11274	 * @public
11275	 * @since 2.1.000 (2008-01-07)
11276	 * @see StartTransform(), StopTransform()
11277	 */
11278	public function Rotate($angle, $x='', $y='') {
11279		if ($x === '') {
11280			$x = $this->x;
11281		}
11282		if ($y === '') {
11283			$y = $this->y;
11284		}
11285		$y = ($this->h - $y) * $this->k;
11286		$x *= $this->k;
11287		//calculate elements of transformation matrix
11288		$tm = array();
11289		$tm[0] = cos(deg2rad($angle));
11290		$tm[1] = sin(deg2rad($angle));
11291		$tm[2] = -$tm[1];
11292		$tm[3] = $tm[0];
11293		$tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
11294		$tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
11295		//rotate the coordinate system around ($x,$y)
11296		$this->Transform($tm);
11297	}
11298
11299	/**
11300	 * Skew horizontally.
11301	 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
11302	 * @param $x (int) abscissa of the skewing center. default is current x position
11303	 * @param $y (int) ordinate of the skewing center. default is current y position
11304	 * @public
11305	 * @since 2.1.000 (2008-01-07)
11306	 * @see StartTransform(), StopTransform()
11307	 */
11308	public function SkewX($angle_x, $x='', $y='') {
11309		$this->Skew($angle_x, 0, $x, $y);
11310	}
11311
11312	/**
11313	 * Skew vertically.
11314	 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
11315	 * @param $x (int) abscissa of the skewing center. default is current x position
11316	 * @param $y (int) ordinate of the skewing center. default is current y position
11317	 * @public
11318	 * @since 2.1.000 (2008-01-07)
11319	 * @see StartTransform(), StopTransform()
11320	 */
11321	public function SkewY($angle_y, $x='', $y='') {
11322		$this->Skew(0, $angle_y, $x, $y);
11323	}
11324
11325	/**
11326	 * Skew.
11327	 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
11328	 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
11329	 * @param $x (int) abscissa of the skewing center. default is current x position
11330	 * @param $y (int) ordinate of the skewing center. default is current y position
11331	 * @public
11332	 * @since 2.1.000 (2008-01-07)
11333	 * @see StartTransform(), StopTransform()
11334	 */
11335	public function Skew($angle_x, $angle_y, $x='', $y='') {
11336		if ($x === '') {
11337			$x = $this->x;
11338		}
11339		if ($y === '') {
11340			$y = $this->y;
11341		}
11342		if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
11343			$this->Error('Please use values between -90 and +90 degrees for Skewing.');
11344		}
11345		$x *= $this->k;
11346		$y = ($this->h - $y) * $this->k;
11347		//calculate elements of transformation matrix
11348		$tm = array();
11349		$tm[0] = 1;
11350		$tm[1] = tan(deg2rad($angle_y));
11351		$tm[2] = tan(deg2rad($angle_x));
11352		$tm[3] = 1;
11353		$tm[4] = -$tm[2] * $y;
11354		$tm[5] = -$tm[1] * $x;
11355		//skew the coordinate system
11356		$this->Transform($tm);
11357	}
11358
11359	/**
11360	 * Apply graphic transformations.
11361	 * @param $tm (array) transformation matrix
11362	 * @protected
11363	 * @since 2.1.000 (2008-01-07)
11364	 * @see StartTransform(), StopTransform()
11365	 */
11366	protected function Transform($tm) {
11367		if ($this->state != 2) {
11368			return;
11369		}
11370		$this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
11371		// add tranformation matrix
11372		$this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
11373		// update transformation mark
11374		if ($this->inxobj) {
11375			// we are inside an XObject template
11376			if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
11377				$key = key($this->xobjects[$this->xobjid]['transfmrk']);
11378				$this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
11379			}
11380		} elseif (end($this->transfmrk[$this->page]) !== false) {
11381			$key = key($this->transfmrk[$this->page]);
11382			$this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
11383		}
11384	}
11385
11386	// END TRANSFORMATIONS SECTION -------------------------
11387
11388	// START GRAPHIC FUNCTIONS SECTION ---------------------
11389	// The following section is based on the code provided by David Hernandez Sanz
11390
11391	/**
11392	 * 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.
11393	 * @param $width (float) The width.
11394	 * @public
11395	 * @since 1.0
11396	 * @see Line(), Rect(), Cell(), MultiCell()
11397	 */
11398	public function SetLineWidth($width) {
11399		//Set line width
11400		$this->LineWidth = $width;
11401		$this->linestyleWidth = sprintf('%F w', ($width * $this->k));
11402		if ($this->state == 2) {
11403			$this->_out($this->linestyleWidth);
11404		}
11405	}
11406
11407	/**
11408	 * Returns the current the line width.
11409	 * @return int Line width
11410	 * @public
11411	 * @since 2.1.000 (2008-01-07)
11412	 * @see Line(), SetLineWidth()
11413	 */
11414	public function GetLineWidth() {
11415		return $this->LineWidth;
11416	}
11417
11418	/**
11419	 * Set line style.
11420	 * @param $style (array) Line style. Array with keys among the following:
11421	 * <ul>
11422	 *	 <li>width (float): Width of the line in user units.</li>
11423	 *	 <li>cap (string): Type of cap to put on the line. Possible values are:
11424	 * butt, round, square. The difference between "square" and "butt" is that
11425	 * "square" projects a flat end past the end of the line.</li>
11426	 *	 <li>join (string): Type of join. Possible values are: miter, round,
11427	 * bevel.</li>
11428	 *	 <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
11429	 * series of length values, which are the lengths of the on and off dashes.
11430	 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
11431	 * 1 off, 2 on, 1 off, ...</li>
11432	 *	 <li>phase (integer): Modifier on the dash pattern which is used to shift
11433	 * the point at which the pattern starts.</li>
11434	 *	 <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>
11435	 * </ul>
11436	 * @param $ret (boolean) if true do not send the command.
11437	 * @return string the PDF command
11438	 * @public
11439	 * @since 2.1.000 (2008-01-08)
11440	 */
11441	public function SetLineStyle($style, $ret=false) {
11442		$s = ''; // string to be returned
11443		if (!is_array($style)) {
11444			return;
11445		}
11446		if (isset($style['width'])) {
11447			$this->LineWidth = $style['width'];
11448			$this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k));
11449			$s .= $this->linestyleWidth.' ';
11450		}
11451		if (isset($style['cap'])) {
11452			$ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
11453			if (isset($ca[$style['cap']])) {
11454				$this->linestyleCap = $ca[$style['cap']].' J';
11455				$s .= $this->linestyleCap.' ';
11456			}
11457		}
11458		if (isset($style['join'])) {
11459			$ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
11460			if (isset($ja[$style['join']])) {
11461				$this->linestyleJoin = $ja[$style['join']].' j';
11462				$s .= $this->linestyleJoin.' ';
11463			}
11464		}
11465		if (isset($style['dash'])) {
11466			$dash_string = '';
11467			if ($style['dash']) {
11468				if (preg_match('/^.+,/', $style['dash']) > 0) {
11469					$tab = explode(',', $style['dash']);
11470				} else {
11471					$tab = array($style['dash']);
11472				}
11473				$dash_string = '';
11474				foreach ($tab as $i => $v) {
11475					if ($i) {
11476						$dash_string .= ' ';
11477					}
11478					$dash_string .= sprintf('%F', $v);
11479				}
11480			}
11481			if (!isset($style['phase']) OR !$style['dash']) {
11482				$style['phase'] = 0;
11483			}
11484			$this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']);
11485			$s .= $this->linestyleDash.' ';
11486		}
11487		if (isset($style['color'])) {
11488			$s .= $this->SetDrawColorArray($style['color'], true).' ';
11489		}
11490		if (!$ret AND ($this->state == 2)) {
11491			$this->_out($s);
11492		}
11493		return $s;
11494	}
11495
11496	/**
11497	 * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment.
11498	 * @param $x (float) Abscissa of point.
11499	 * @param $y (float) Ordinate of point.
11500	 * @protected
11501	 * @since 2.1.000 (2008-01-08)
11502	 */
11503	protected function _outPoint($x, $y) {
11504		if ($this->state == 2) {
11505			$this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k)));
11506		}
11507	}
11508
11509	/**
11510	 * Append a straight line segment from the current point to the point (x, y).
11511	 * The new current point shall be (x, y).
11512	 * @param $x (float) Abscissa of end point.
11513	 * @param $y (float) Ordinate of end point.
11514	 * @protected
11515	 * @since 2.1.000 (2008-01-08)
11516	 */
11517	protected function _outLine($x, $y) {
11518		if ($this->state == 2) {
11519			$this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k)));
11520		}
11521	}
11522
11523	/**
11524	 * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space.
11525	 * @param $x (float) Abscissa of upper-left corner.
11526	 * @param $y (float) Ordinate of upper-left corner.
11527	 * @param $w (float) Width.
11528	 * @param $h (float) Height.
11529	 * @param $op (string) options
11530	 * @protected
11531	 * @since 2.1.000 (2008-01-08)
11532	 */
11533	protected function _outRect($x, $y, $w, $h, $op) {
11534		if ($this->state == 2) {
11535			$this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k), (($this->h - $y) * $this->k), ($w * $this->k), (-$h * $this->k), $op));
11536		}
11537	}
11538
11539	/**
11540	 * 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.
11541	 * The new current point shall be (x3, y3).
11542	 * @param $x1 (float) Abscissa of control point 1.
11543	 * @param $y1 (float) Ordinate of control point 1.
11544	 * @param $x2 (float) Abscissa of control point 2.
11545	 * @param $y2 (float) Ordinate of control point 2.
11546	 * @param $x3 (float) Abscissa of end point.
11547	 * @param $y3 (float) Ordinate of end point.
11548	 * @protected
11549	 * @since 2.1.000 (2008-01-08)
11550	 */
11551	protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
11552		if ($this->state == 2) {
11553			$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)));
11554		}
11555	}
11556
11557	/**
11558	 * 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.
11559	 * The new current point shall be (x3, y3).
11560	 * @param $x2 (float) Abscissa of control point 2.
11561	 * @param $y2 (float) Ordinate of control point 2.
11562	 * @param $x3 (float) Abscissa of end point.
11563	 * @param $y3 (float) Ordinate of end point.
11564	 * @protected
11565	 * @since 4.9.019 (2010-04-26)
11566	 */
11567	protected function _outCurveV($x2, $y2, $x3, $y3) {
11568		if ($this->state == 2) {
11569			$this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11570		}
11571	}
11572
11573	/**
11574	 * 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.
11575	 * The new current point shall be (x3, y3).
11576	 * @param $x1 (float) Abscissa of control point 1.
11577	 * @param $y1 (float) Ordinate of control point 1.
11578	 * @param $x3 (float) Abscissa of end point.
11579	 * @param $y3 (float) Ordinate of end point.
11580	 * @protected
11581	 * @since 2.1.000 (2008-01-08)
11582	 */
11583	protected function _outCurveY($x1, $y1, $x3, $y3) {
11584		if ($this->state == 2) {
11585			$this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11586		}
11587	}
11588
11589	/**
11590	 * Draws a line between two points.
11591	 * @param $x1 (float) Abscissa of first point.
11592	 * @param $y1 (float) Ordinate of first point.
11593	 * @param $x2 (float) Abscissa of second point.
11594	 * @param $y2 (float) Ordinate of second point.
11595	 * @param $style (array) Line style. Array like for SetLineStyle(). Default value: default line style (empty array).
11596	 * @public
11597	 * @since 1.0
11598	 * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
11599	 */
11600	public function Line($x1, $y1, $x2, $y2, $style=array()) {
11601		if ($this->state != 2) {
11602			return;
11603		}
11604		if (is_array($style)) {
11605			$this->SetLineStyle($style);
11606		}
11607		$this->_outPoint($x1, $y1);
11608		$this->_outLine($x2, $y2);
11609		$this->_out('S');
11610	}
11611
11612	/**
11613	 * Draws a rectangle.
11614	 * @param $x (float) Abscissa of upper-left corner.
11615	 * @param $y (float) Ordinate of upper-left corner.
11616	 * @param $w (float) Width.
11617	 * @param $h (float) Height.
11618	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11619	 * @param $border_style (array) Border style of rectangle. Array with keys among the following:
11620	 * <ul>
11621	 *	 <li>all: Line style of all borders. Array like for SetLineStyle().</li>
11622	 *	 <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for SetLineStyle().</li>
11623	 * </ul>
11624	 * If a key is not present or is null, the correspondent border is not drawn. Default value: default line style (empty array).
11625	 * @param $fill_color (array) 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).
11626	 * @public
11627	 * @since 1.0
11628	 * @see SetLineStyle()
11629	 */
11630	public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
11631		if ($this->state != 2) {
11632			return;
11633		}
11634		if (empty($style)) {
11635			$style = 'S';
11636		}
11637		if (!(strpos($style, 'F') === false) AND !empty($fill_color)) {
11638			// set background color
11639			$this->SetFillColorArray($fill_color);
11640		}
11641		if (!empty($border_style)) {
11642			if (isset($border_style['all']) AND !empty($border_style['all'])) {
11643				//set global style for border
11644				$this->SetLineStyle($border_style['all']);
11645				$border_style = array();
11646			} else {
11647				// remove stroke operator from style
11648				$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*' );
11649				if (isset($opnostroke[$style])) {
11650					$style = $opnostroke[$style];
11651				}
11652			}
11653		}
11654		if (!empty($style)) {
11655			$op = TCPDF_STATIC::getPathPaintOperator($style);
11656			$this->_outRect($x, $y, $w, $h, $op);
11657		}
11658		if (!empty($border_style)) {
11659			$border_style2 = array();
11660			foreach ($border_style as $line => $value) {
11661				$length = strlen($line);
11662				for ($i = 0; $i < $length; ++$i) {
11663					$border_style2[$line[$i]] = $value;
11664				}
11665			}
11666			$border_style = $border_style2;
11667			if (isset($border_style['L']) AND $border_style['L']) {
11668				$this->Line($x, $y, $x, $y + $h, $border_style['L']);
11669			}
11670			if (isset($border_style['T']) AND $border_style['T']) {
11671				$this->Line($x, $y, $x + $w, $y, $border_style['T']);
11672			}
11673			if (isset($border_style['R']) AND $border_style['R']) {
11674				$this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
11675			}
11676			if (isset($border_style['B']) AND $border_style['B']) {
11677				$this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
11678			}
11679		}
11680	}
11681
11682	/**
11683	 * Draws a Bezier curve.
11684	 * The Bezier curve is a tangent to the line between the control points at
11685	 * either end of the curve.
11686	 * @param $x0 (float) Abscissa of start point.
11687	 * @param $y0 (float) Ordinate of start point.
11688	 * @param $x1 (float) Abscissa of control point 1.
11689	 * @param $y1 (float) Ordinate of control point 1.
11690	 * @param $x2 (float) Abscissa of control point 2.
11691	 * @param $y2 (float) Ordinate of control point 2.
11692	 * @param $x3 (float) Abscissa of end point.
11693	 * @param $y3 (float) Ordinate of end point.
11694	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11695	 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
11696	 * @param $fill_color (array) 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).
11697	 * @public
11698	 * @see SetLineStyle()
11699	 * @since 2.1.000 (2008-01-08)
11700	 */
11701	public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
11702		if ($this->state != 2) {
11703			return;
11704		}
11705		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11706			$this->SetFillColorArray($fill_color);
11707		}
11708		$op = TCPDF_STATIC::getPathPaintOperator($style);
11709		if ($line_style) {
11710			$this->SetLineStyle($line_style);
11711		}
11712		$this->_outPoint($x0, $y0);
11713		$this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11714		$this->_out($op);
11715	}
11716
11717	/**
11718	 * Draws a poly-Bezier curve.
11719	 * Each Bezier curve segment is a tangent to the line between the control points at
11720	 * either end of the curve.
11721	 * @param $x0 (float) Abscissa of start point.
11722	 * @param $y0 (float) Ordinate of start point.
11723	 * @param $segments (float) An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
11724	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11725	 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
11726	 * @param $fill_color (array) 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).
11727	 * @public
11728	 * @see SetLineStyle()
11729	 * @since 3.0008 (2008-05-12)
11730	 */
11731	public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
11732		if ($this->state != 2) {
11733			return;
11734		}
11735		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11736			$this->SetFillColorArray($fill_color);
11737		}
11738		$op = TCPDF_STATIC::getPathPaintOperator($style);
11739		if ($op == 'f') {
11740			$line_style = array();
11741		}
11742		if ($line_style) {
11743			$this->SetLineStyle($line_style);
11744		}
11745		$this->_outPoint($x0, $y0);
11746		foreach ($segments as $segment) {
11747			list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
11748			$this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11749		}
11750		$this->_out($op);
11751	}
11752
11753	/**
11754	 * Draws an ellipse.
11755	 * An ellipse is formed from n Bezier curves.
11756	 * @param $x0 (float) Abscissa of center point.
11757	 * @param $y0 (float) Ordinate of center point.
11758	 * @param $rx (float) Horizontal radius.
11759	 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
11760	 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
11761	 * @param $astart: (float) Angle start of draw line. Default value: 0.
11762	 * @param $afinish: (float) Angle finish of draw line. Default value: 360.
11763	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11764	 * @param $line_style (array) Line style of ellipse. Array like for SetLineStyle(). Default value: default line style (empty array).
11765	 * @param $fill_color (array) 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).
11766	 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
11767	 * @author Nicola Asuni
11768	 * @public
11769	 * @since 2.1.000 (2008-01-08)
11770	 */
11771	public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11772		if ($this->state != 2) {
11773			return;
11774		}
11775		if (TCPDF_STATIC::empty_string($ry) OR ($ry == 0)) {
11776			$ry = $rx;
11777		}
11778		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11779			$this->SetFillColorArray($fill_color);
11780		}
11781		$op = TCPDF_STATIC::getPathPaintOperator($style);
11782		if ($op == 'f') {
11783			$line_style = array();
11784		}
11785		if ($line_style) {
11786			$this->SetLineStyle($line_style);
11787		}
11788		$this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
11789		$this->_out($op);
11790	}
11791
11792	/**
11793	 * Append an elliptical arc to the current path.
11794	 * An ellipse is formed from n Bezier curves.
11795	 * @param $xc (float) Abscissa of center point.
11796	 * @param $yc (float) Ordinate of center point.
11797	 * @param $rx (float) Horizontal radius.
11798	 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
11799	 * @param $xang: (float) Angle between the X-axis and the major axis of the ellipse. Default value: 0.
11800	 * @param $angs: (float) Angle start of draw line. Default value: 0.
11801	 * @param $angf: (float) Angle finish of draw line. Default value: 360.
11802	 * @param $pie (boolean) if true do not mark the border point (used to draw pie sectors).
11803	 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
11804	 * @param $startpoint (boolean) if true output a starting point.
11805	 * @param $ccw (boolean) if true draws in counter-clockwise.
11806	 * @param $svg (boolean) if true the angles are in svg mode (already calculated).
11807	 * @return array bounding box coordinates (x min, y min, x max, y max)
11808	 * @author Nicola Asuni
11809	 * @protected
11810	 * @since 4.9.019 (2010-04-26)
11811	 */
11812	protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
11813		if (($rx <= 0) OR ($ry < 0)) {
11814			return;
11815		}
11816		$k = $this->k;
11817		if ($nc < 2) {
11818			$nc = 2;
11819		}
11820		$xmin = 2147483647;
11821		$ymin = 2147483647;
11822		$xmax = 0;
11823		$ymax = 0;
11824		if ($pie) {
11825			// center of the arc
11826			$this->_outPoint($xc, $yc);
11827		}
11828		$xang = deg2rad((float) $xang);
11829		$angs = deg2rad((float) $angs);
11830		$angf = deg2rad((float) $angf);
11831		if ($svg) {
11832			$as = $angs;
11833			$af = $angf;
11834		} else {
11835			$as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
11836			$af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
11837		}
11838		if ($as < 0) {
11839			$as += (2 * M_PI);
11840		}
11841		if ($af < 0) {
11842			$af += (2 * M_PI);
11843		}
11844		if ($ccw AND ($as > $af)) {
11845			// reverse rotation
11846			$as -= (2 * M_PI);
11847		} elseif (!$ccw AND ($as < $af)) {
11848			// reverse rotation
11849			$af -= (2 * M_PI);
11850		}
11851		$total_angle = ($af - $as);
11852		if ($nc < 2) {
11853			$nc = 2;
11854		}
11855		// total arcs to draw
11856		$nc *= (2 * abs($total_angle) / M_PI);
11857		$nc = round($nc) + 1;
11858		// angle of each arc
11859		$arcang = ($total_angle / $nc);
11860		// center point in PDF coordinates
11861		$x0 = $xc;
11862		$y0 = ($this->h - $yc);
11863		// starting angle
11864		$ang = $as;
11865		$alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
11866		$cos_xang = cos($xang);
11867		$sin_xang = sin($xang);
11868		$cos_ang = cos($ang);
11869		$sin_ang = sin($ang);
11870		// first arc point
11871		$px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11872		$py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11873		// first Bezier control point
11874		$qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11875		$qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11876		if ($pie) {
11877			// line from center to arc starting point
11878			$this->_outLine($px1, $this->h - $py1);
11879		} elseif ($startpoint) {
11880			// arc starting point
11881			$this->_outPoint($px1, $this->h - $py1);
11882		}
11883		// draw arcs
11884		for ($i = 1; $i <= $nc; ++$i) {
11885			// starting angle
11886			$ang = $as + ($i * $arcang);
11887			if ($i == $nc) {
11888				$ang = $af;
11889			}
11890			$cos_ang = cos($ang);
11891			$sin_ang = sin($ang);
11892			// second arc point
11893			$px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11894			$py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11895			// second Bezier control point
11896			$qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11897			$qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11898			// draw arc
11899			$cx1 = ($px1 + $qx1);
11900			$cy1 = ($this->h - ($py1 + $qy1));
11901			$cx2 = ($px2 - $qx2);
11902			$cy2 = ($this->h - ($py2 - $qy2));
11903			$cx3 = $px2;
11904			$cy3 = ($this->h - $py2);
11905			$this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
11906			// get bounding box coordinates
11907			$xmin = min($xmin, $cx1, $cx2, $cx3);
11908			$ymin = min($ymin, $cy1, $cy2, $cy3);
11909			$xmax = max($xmax, $cx1, $cx2, $cx3);
11910			$ymax = max($ymax, $cy1, $cy2, $cy3);
11911			// move to next point
11912			$px1 = $px2;
11913			$py1 = $py2;
11914			$qx1 = $qx2;
11915			$qy1 = $qy2;
11916		}
11917		if ($pie) {
11918			$this->_outLine($xc, $yc);
11919			// get bounding box coordinates
11920			$xmin = min($xmin, $xc);
11921			$ymin = min($ymin, $yc);
11922			$xmax = max($xmax, $xc);
11923			$ymax = max($ymax, $yc);
11924		}
11925		return array($xmin, $ymin, $xmax, $ymax);
11926	}
11927
11928	/**
11929	 * Draws a circle.
11930	 * A circle is formed from n Bezier curves.
11931	 * @param $x0 (float) Abscissa of center point.
11932	 * @param $y0 (float) Ordinate of center point.
11933	 * @param $r (float) Radius.
11934	 * @param $angstr: (float) Angle start of draw line. Default value: 0.
11935	 * @param $angend: (float) Angle finish of draw line. Default value: 360.
11936	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11937	 * @param $line_style (array) Line style of circle. Array like for SetLineStyle(). Default value: default line style (empty array).
11938	 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
11939	 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of circle.
11940	 * @public
11941	 * @since 2.1.000 (2008-01-08)
11942	 */
11943	public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11944		$this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
11945	}
11946
11947	/**
11948	 * Draws a polygonal line
11949	 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
11950	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11951	 * @param $line_style (array) Line style of polygon. Array with keys among the following:
11952	 * <ul>
11953	 *	 <li>all: Line style of all lines. Array like for SetLineStyle().</li>
11954	 *	 <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
11955	 * </ul>
11956	 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
11957	 * @param $fill_color (array) 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).
11958	 * @since 4.8.003 (2009-09-15)
11959	 * @public
11960	 */
11961	public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
11962		$this->Polygon($p, $style, $line_style, $fill_color, false);
11963	}
11964
11965	/**
11966	 * Draws a polygon.
11967	 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
11968	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11969	 * @param $line_style (array) Line style of polygon. Array with keys among the following:
11970	 * <ul>
11971	 *	 <li>all: Line style of all lines. Array like for SetLineStyle().</li>
11972	 *	 <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
11973	 * </ul>
11974	 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
11975	 * @param $fill_color (array) 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).
11976	 * @param $closed (boolean) if true the polygon is closes, otherwise will remain open
11977	 * @public
11978	 * @since 2.1.000 (2008-01-08)
11979	 */
11980	public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
11981		if ($this->state != 2) {
11982			return;
11983		}
11984		$nc = count($p); // number of coordinates
11985		$np = $nc / 2; // number of points
11986		if ($closed) {
11987			// close polygon by adding the first 2 points at the end (one line)
11988			for ($i = 0; $i < 4; ++$i) {
11989				$p[$nc + $i] = $p[$i];
11990			}
11991			// copy style for the last added line
11992			if (isset($line_style[0])) {
11993				$line_style[$np] = $line_style[0];
11994			}
11995			$nc += 4;
11996		}
11997		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11998			$this->SetFillColorArray($fill_color);
11999		}
12000		$op = TCPDF_STATIC::getPathPaintOperator($style);
12001		if ($op == 'f') {
12002			$line_style = array();
12003		}
12004		$draw = true;
12005		if ($line_style) {
12006			if (isset($line_style['all'])) {
12007				$this->SetLineStyle($line_style['all']);
12008			} else {
12009				$draw = false;
12010				if ($op == 'B') {
12011					// draw fill
12012					$op = 'f';
12013					$this->_outPoint($p[0], $p[1]);
12014					for ($i = 2; $i < $nc; $i = $i + 2) {
12015						$this->_outLine($p[$i], $p[$i + 1]);
12016					}
12017					$this->_out($op);
12018				}
12019				// draw outline
12020				$this->_outPoint($p[0], $p[1]);
12021				for ($i = 2; $i < $nc; $i = $i + 2) {
12022					$line_num = ($i / 2) - 1;
12023					if (isset($line_style[$line_num])) {
12024						if ($line_style[$line_num] != 0) {
12025							if (is_array($line_style[$line_num])) {
12026								$this->_out('S');
12027								$this->SetLineStyle($line_style[$line_num]);
12028								$this->_outPoint($p[$i - 2], $p[$i - 1]);
12029								$this->_outLine($p[$i], $p[$i + 1]);
12030								$this->_out('S');
12031								$this->_outPoint($p[$i], $p[$i + 1]);
12032							} else {
12033								$this->_outLine($p[$i], $p[$i + 1]);
12034							}
12035						}
12036					} else {
12037						$this->_outLine($p[$i], $p[$i + 1]);
12038					}
12039				}
12040				$this->_out($op);
12041			}
12042		}
12043		if ($draw) {
12044			$this->_outPoint($p[0], $p[1]);
12045			for ($i = 2; $i < $nc; $i = $i + 2) {
12046				$this->_outLine($p[$i], $p[$i + 1]);
12047			}
12048			$this->_out($op);
12049		}
12050	}
12051
12052	/**
12053	 * Draws a regular polygon.
12054	 * @param $x0 (float) Abscissa of center point.
12055	 * @param $y0 (float) Ordinate of center point.
12056	 * @param $r: (float) Radius of inscribed circle.
12057	 * @param $ns (integer) Number of sides.
12058	 * @param $angle (float) Angle oriented (anti-clockwise). Default value: 0.
12059	 * @param $draw_circle (boolean) Draw inscribed circle or not. Default value: false.
12060	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12061	 * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
12062	 * <ul>
12063	 *	 <li>all: Line style of all sides. Array like for SetLineStyle().</li>
12064	 *	 <li>0 to ($ns - 1): Line style of each side. Array like for SetLineStyle().</li>
12065	 * </ul>
12066	 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
12067	 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
12068	 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
12069	 * <ul>
12070	 *	 <li>D or empty string: Draw (default).</li>
12071	 *	 <li>F: Fill.</li>
12072	 *	 <li>DF or FD: Draw and fill.</li>
12073	 *	 <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
12074	 *	 <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
12075	 * </ul>
12076	 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
12077	 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
12078	 * @public
12079	 * @since 2.1.000 (2008-01-08)
12080	 */
12081	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()) {
12082		if (3 > $ns) {
12083			$ns = 3;
12084		}
12085		if ($draw_circle) {
12086			$this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12087		}
12088		$p = array();
12089		for ($i = 0; $i < $ns; ++$i) {
12090			$a = $angle + ($i * 360 / $ns);
12091			$a_rad = deg2rad((float) $a);
12092			$p[] = $x0 + ($r * sin($a_rad));
12093			$p[] = $y0 + ($r * cos($a_rad));
12094		}
12095		$this->Polygon($p, $style, $line_style, $fill_color);
12096	}
12097
12098	/**
12099	 * Draws a star polygon
12100	 * @param $x0 (float) Abscissa of center point.
12101	 * @param $y0 (float) Ordinate of center point.
12102	 * @param $r (float) Radius of inscribed circle.
12103	 * @param $nv (integer) Number of vertices.
12104	 * @param $ng (integer) Number of gap (if ($ng % $nv = 1) then is a regular polygon).
12105	 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
12106	 * @param $draw_circle: (boolean) Draw inscribed circle or not. Default value is false.
12107	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12108	 * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
12109	 * <ul>
12110	 *	 <li>all: Line style of all sides. Array like for
12111	 * SetLineStyle().</li>
12112	 *	 <li>0 to (n - 1): Line style of each side. Array like for SetLineStyle().</li>
12113	 * </ul>
12114	 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
12115	 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
12116	 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
12117	 * <ul>
12118	 *	 <li>D or empty string: Draw (default).</li>
12119	 *	 <li>F: Fill.</li>
12120	 *	 <li>DF or FD: Draw and fill.</li>
12121	 *	 <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
12122	 *	 <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
12123	 * </ul>
12124	 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
12125	 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
12126	 * @public
12127	 * @since 2.1.000 (2008-01-08)
12128	 */
12129	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()) {
12130		if ($nv < 2) {
12131			$nv = 2;
12132		}
12133		if ($draw_circle) {
12134			$this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12135		}
12136		$p2 = array();
12137		$visited = array();
12138		for ($i = 0; $i < $nv; ++$i) {
12139			$a = $angle + ($i * 360 / $nv);
12140			$a_rad = deg2rad((float) $a);
12141			$p2[] = $x0 + ($r * sin($a_rad));
12142			$p2[] = $y0 + ($r * cos($a_rad));
12143			$visited[] = false;
12144		}
12145		$p = array();
12146		$i = 0;
12147		do {
12148			$p[] = $p2[$i * 2];
12149			$p[] = $p2[($i * 2) + 1];
12150			$visited[$i] = true;
12151			$i += $ng;
12152			$i %= $nv;
12153		} while (!$visited[$i]);
12154		$this->Polygon($p, $style, $line_style, $fill_color);
12155	}
12156
12157	/**
12158	 * Draws a rounded rectangle.
12159	 * @param $x (float) Abscissa of upper-left corner.
12160	 * @param $y (float) Ordinate of upper-left corner.
12161	 * @param $w (float) Width.
12162	 * @param $h (float) Height.
12163	 * @param $r (float) the radius of the circle used to round off the corners of the rectangle.
12164	 * @param $round_corner (string) 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").
12165	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12166	 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12167	 * @param $fill_color (array) 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).
12168	 * @public
12169	 * @since 2.1.000 (2008-01-08)
12170	 */
12171	public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12172		$this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
12173	}
12174
12175	/**
12176	 * Draws a rounded rectangle.
12177	 * @param $x (float) Abscissa of upper-left corner.
12178	 * @param $y (float) Ordinate of upper-left corner.
12179	 * @param $w (float) Width.
12180	 * @param $h (float) Height.
12181	 * @param $rx (float) the x-axis radius of the ellipse used to round off the corners of the rectangle.
12182	 * @param $ry (float) the y-axis radius of the ellipse used to round off the corners of the rectangle.
12183	 * @param $round_corner (string) 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").
12184	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12185	 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12186	 * @param $fill_color (array) 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).
12187	 * @public
12188	 * @since 4.9.019 (2010-04-22)
12189	 */
12190	public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12191		if ($this->state != 2) {
12192			return;
12193		}
12194		if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
12195			// Not rounded
12196			$this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
12197			return;
12198		}
12199		// Rounded
12200		if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12201			$this->SetFillColorArray($fill_color);
12202		}
12203		$op = TCPDF_STATIC::getPathPaintOperator($style);
12204		if ($op == 'f') {
12205			$border_style = array();
12206		}
12207		if ($border_style) {
12208			$this->SetLineStyle($border_style);
12209		}
12210		$MyArc = 4 / 3 * (sqrt(2) - 1);
12211		$this->_outPoint($x + $rx, $y);
12212		$xc = $x + $w - $rx;
12213		$yc = $y + $ry;
12214		$this->_outLine($xc, $y);
12215		if ($round_corner[0]) {
12216			$this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
12217		} else {
12218			$this->_outLine($x + $w, $y);
12219		}
12220		$xc = $x + $w - $rx;
12221		$yc = $y + $h - $ry;
12222		$this->_outLine($x + $w, $yc);
12223		if ($round_corner[1]) {
12224			$this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
12225		} else {
12226			$this->_outLine($x + $w, $y + $h);
12227		}
12228		$xc = $x + $rx;
12229		$yc = $y + $h - $ry;
12230		$this->_outLine($xc, $y + $h);
12231		if ($round_corner[2]) {
12232			$this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
12233		} else {
12234			$this->_outLine($x, $y + $h);
12235		}
12236		$xc = $x + $rx;
12237		$yc = $y + $ry;
12238		$this->_outLine($x, $yc);
12239		if ($round_corner[3]) {
12240			$this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
12241		} else {
12242			$this->_outLine($x, $y);
12243			$this->_outLine($x + $rx, $y);
12244		}
12245		$this->_out($op);
12246	}
12247
12248	/**
12249	 * Draws a grahic arrow.
12250	 * @param $x0 (float) Abscissa of first point.
12251	 * @param $y0 (float) Ordinate of first point.
12252	 * @param $x1 (float) Abscissa of second point.
12253	 * @param $y1 (float) Ordinate of second point.
12254	 * @param $head_style (int) (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead)
12255	 * @param $arm_size (float) length of arrowhead arms
12256	 * @param $arm_angle (int) angle between an arm and the shaft
12257	 * @author Piotr Galecki, Nicola Asuni, Andy Meier
12258	 * @since 4.6.018 (2009-07-10)
12259	 */
12260	public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
12261		// getting arrow direction angle
12262		// 0 deg angle is when both arms go along X axis. angle grows clockwise.
12263		$dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
12264		if ($dir_angle < 0) {
12265			$dir_angle += (2 * M_PI);
12266		}
12267		$arm_angle = deg2rad($arm_angle);
12268		$sx1 = $x1;
12269		$sy1 = $y1;
12270		if ($head_style > 0) {
12271			// calculate the stopping point for the arrow shaft
12272			$sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
12273			$sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
12274		}
12275		// main arrow line / shaft
12276		$this->Line($x0, $y0, $sx1, $sy1);
12277		// left arrowhead arm tip
12278		$x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
12279		$y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
12280		// right arrowhead arm tip
12281		$x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
12282		$y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
12283		$mode = 'D';
12284		$style = array();
12285		switch ($head_style) {
12286			case 0: {
12287				// draw only arrowhead arms
12288				$mode = 'D';
12289				$style = array(1, 1, 0);
12290				break;
12291			}
12292			case 1: {
12293				// draw closed arrowhead, but no fill
12294				$mode = 'D';
12295				break;
12296			}
12297			case 2: {
12298				// closed and filled arrowhead
12299				$mode = 'DF';
12300				break;
12301			}
12302			case 3: {
12303				// filled arrowhead
12304				$mode = 'F';
12305				break;
12306			}
12307		}
12308		$this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
12309	}
12310
12311	// END GRAPHIC FUNCTIONS SECTION -----------------------
12312
12313	/**
12314	 * Add a Named Destination.
12315	 * NOTE: destination names are unique, so only last entry will be saved.
12316	 * @param $name (string) Destination name.
12317	 * @param $y (float) Y position in user units of the destiantion on the selected page (default = -1 = current position; 0 = page start;).
12318	 * @param $page (int|string) 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.
12319	 * @param $x (float) X position in user units of the destiantion on the selected page (default = -1 = current position;).
12320	 * @return (string) Stripped named destination identifier or false in case of error.
12321	 * @public
12322	 * @author Christian Deligant, Nicola Asuni
12323	 * @since 5.9.097 (2011-06-23)
12324	 */
12325	public function setDestination($name, $y=-1, $page='', $x=-1) {
12326		// remove unsupported characters
12327		$name = TCPDF_STATIC::encodeNameObject($name);
12328		if (TCPDF_STATIC::empty_string($name)) {
12329			return false;
12330		}
12331		if ($y == -1) {
12332			$y = $this->GetY();
12333		} elseif ($y < 0) {
12334			$y = 0;
12335		} elseif ($y > $this->h) {
12336			$y = $this->h;
12337		}
12338		if ($x == -1) {
12339			$x = $this->GetX();
12340		} elseif ($x < 0) {
12341			$x = 0;
12342		} elseif ($x > $this->w) {
12343			$x = $this->w;
12344		}
12345		$fixed = false;
12346		if (!empty($page) AND ($page[0] == '*')) {
12347			$page = intval(substr($page, 1));
12348			// this page number will not be changed when moving/add/deleting pages
12349			$fixed = true;
12350		}
12351		if (empty($page)) {
12352			$page = $this->PageNo();
12353			if (empty($page)) {
12354				return;
12355			}
12356		}
12357		$this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed);
12358		return $name;
12359	}
12360
12361	/**
12362	 * Return the Named Destination array.
12363	 * @return (array) Named Destination array.
12364	 * @public
12365	 * @author Nicola Asuni
12366	 * @since 5.9.097 (2011-06-23)
12367	 */
12368	public function getDestination() {
12369		return $this->dests;
12370	}
12371
12372	/**
12373	 * Insert Named Destinations.
12374	 * @protected
12375	 * @author Johannes G\FCntert, Nicola Asuni
12376	 * @since 5.9.098 (2011-06-23)
12377	 */
12378	protected function _putdests() {
12379		if (empty($this->dests)) {
12380			return;
12381		}
12382		$this->n_dests = $this->_newobj();
12383		$out = ' <<';
12384		foreach($this->dests as $name => $o) {
12385			$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)));
12386		}
12387		$out .= ' >>';
12388		$out .= "\n".'endobj';
12389		$this->_out($out);
12390	}
12391
12392	/**
12393	 * Adds a bookmark - alias for Bookmark().
12394	 * @param $txt (string) Bookmark description.
12395	 * @param $level (int) Bookmark level (minimum value is 0).
12396	 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
12397	 * @param $page (int|string) 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.
12398	 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic.
12399	 * @param $color (array) RGB color array (values from 0 to 255).
12400	 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;).
12401	 * @param $link (mixed) URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name).
12402	 * @public
12403	 */
12404	public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12405		$this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link);
12406	}
12407
12408	/**
12409	 * Adds a bookmark.
12410	 * @param $txt (string) Bookmark description.
12411	 * @param $level (int) Bookmark level (minimum value is 0).
12412	 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
12413	 * @param $page (int|string) 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.
12414	 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic.
12415	 * @param $color (array) RGB color array (values from 0 to 255).
12416	 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;).
12417	 * @param $link (mixed) URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name).
12418	 * @public
12419	 * @since 2.1.002 (2008-02-12)
12420	 */
12421	public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12422		if ($level < 0) {
12423			$level = 0;
12424		}
12425		if (isset($this->outlines[0])) {
12426			$lastoutline = end($this->outlines);
12427			$maxlevel = $lastoutline['l'] + 1;
12428		} else {
12429			$maxlevel = 0;
12430		}
12431		if ($level > $maxlevel) {
12432			$level = $maxlevel;
12433		}
12434		if ($y == -1) {
12435			$y = $this->GetY();
12436		} elseif ($y < 0) {
12437			$y = 0;
12438		} elseif ($y > $this->h) {
12439			$y = $this->h;
12440		}
12441		if ($x == -1) {
12442			$x = $this->GetX();
12443		} elseif ($x < 0) {
12444			$x = 0;
12445		} elseif ($x > $this->w) {
12446			$x = $this->w;
12447		}
12448		$fixed = false;
12449		if (!empty($page) AND ($page[0] == '*')) {
12450			$page = intval(substr($page, 1));
12451			// this page number will not be changed when moving/add/deleting pages
12452			$fixed = true;
12453		}
12454		if (empty($page)) {
12455			$page = $this->PageNo();
12456			if (empty($page)) {
12457				return;
12458			}
12459		}
12460		$this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed, 's' => strtoupper($style), 'c' => $color, 'u' => $link);
12461	}
12462
12463	/**
12464	 * Sort bookmarks for page and key.
12465	 * @protected
12466	 * @since 5.9.119 (2011-09-19)
12467	 */
12468	protected function sortBookmarks() {
12469		// get sorting columns
12470		$outline_p = array();
12471		$outline_y = array();
12472		foreach ($this->outlines as $key => $row) {
12473			$outline_p[$key] = $row['p'];
12474			$outline_k[$key] = $key;
12475		}
12476		// sort outlines by page and original position
12477		array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
12478	}
12479
12480	/**
12481	 * Create a bookmark PDF string.
12482	 * @protected
12483	 * @author Olivier Plathey, Nicola Asuni
12484	 * @since 2.1.002 (2008-02-12)
12485	 */
12486	protected function _putbookmarks() {
12487		$nb = count($this->outlines);
12488		if ($nb == 0) {
12489			return;
12490		}
12491		// sort bookmarks
12492		$this->sortBookmarks();
12493		$lru = array();
12494		$level = 0;
12495		foreach ($this->outlines as $i => $o) {
12496			if ($o['l'] > 0) {
12497				$parent = $lru[($o['l'] - 1)];
12498				//Set parent and last pointers
12499				$this->outlines[$i]['parent'] = $parent;
12500				$this->outlines[$parent]['last'] = $i;
12501				if ($o['l'] > $level) {
12502					//Level increasing: set first pointer
12503					$this->outlines[$parent]['first'] = $i;
12504				}
12505			} else {
12506				$this->outlines[$i]['parent'] = $nb;
12507			}
12508			if (($o['l'] <= $level) AND ($i > 0)) {
12509				//Set prev and next pointers
12510				$prev = $lru[$o['l']];
12511				$this->outlines[$prev]['next'] = $i;
12512				$this->outlines[$i]['prev'] = $prev;
12513			}
12514			$lru[$o['l']] = $i;
12515			$level = $o['l'];
12516		}
12517		//Outline items
12518		$n = $this->n + 1;
12519		$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';
12520		foreach ($this->outlines as $i => $o) {
12521			$oid = $this->_newobj();
12522			// covert HTML title to string
12523			$title = preg_replace($nltags, "\n", $o['t']);
12524			$title = preg_replace("/[\r]+/si", '', $title);
12525			$title = preg_replace("/[\n]+/si", "\n", $title);
12526			$title = strip_tags($title);
12527			$title = $this->stringTrim($title);
12528			$out = '<</Title '.$this->_textstring($title, $oid);
12529			$out .= ' /Parent '.($n + $o['parent']).' 0 R';
12530			if (isset($o['prev'])) {
12531				$out .= ' /Prev '.($n + $o['prev']).' 0 R';
12532			}
12533			if (isset($o['next'])) {
12534				$out .= ' /Next '.($n + $o['next']).' 0 R';
12535			}
12536			if (isset($o['first'])) {
12537				$out .= ' /First '.($n + $o['first']).' 0 R';
12538			}
12539			if (isset($o['last'])) {
12540				$out .= ' /Last '.($n + $o['last']).' 0 R';
12541			}
12542			if (isset($o['u']) AND !empty($o['u'])) {
12543				// link
12544				if (is_string($o['u'])) {
12545					if ($o['u'][0] == '#') {
12546						// internal destination
12547						$out .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($o['u'], 1));
12548					} elseif ($o['u'][0] == '%') {
12549						// embedded PDF file
12550						$filename = basename(substr($o['u'], 1));
12551						$out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
12552					} elseif ($o['u'][0] == '*') {
12553						// embedded generic file
12554						$filename = basename(substr($o['u'], 1));
12555						$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});';
12556						$out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>';
12557					} else {
12558						// external URI link
12559						$out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>';
12560					}
12561				} elseif (isset($this->links[$o['u']])) {
12562					// internal link ID
12563					$l = $this->links[$o['u']];
12564					if (isset($this->page_obj_id[($l['p'])])) {
12565						$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)));
12566					}
12567				}
12568			} elseif (isset($this->page_obj_id[($o['p'])])) {
12569				// link to a page
12570				$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)));
12571			}
12572			// set font style
12573			$style = 0;
12574			if (!empty($o['s'])) {
12575				// bold
12576				if (strpos($o['s'], 'B') !== false) {
12577					$style |= 2;
12578				}
12579				// oblique
12580				if (strpos($o['s'], 'I') !== false) {
12581					$style |= 1;
12582				}
12583			}
12584			$out .= sprintf(' /F %d', $style);
12585			// set bookmark color
12586			if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
12587				$color = array_values($o['c']);
12588				$out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
12589			} else {
12590				// black
12591				$out .= ' /C [0.0 0.0 0.0]';
12592			}
12593			$out .= ' /Count 0'; // normally closed item
12594			$out .= ' >>';
12595			$out .= "\n".'endobj';
12596			$this->_out($out);
12597		}
12598		//Outline root
12599		$this->OutlineRoot = $this->_newobj();
12600		$this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj');
12601	}
12602
12603	// --- JAVASCRIPT ------------------------------------------------------
12604
12605	/**
12606	 * Adds a javascript
12607	 * @param $script (string) Javascript code
12608	 * @public
12609	 * @author Johannes G\FCntert, Nicola Asuni
12610	 * @since 2.1.002 (2008-02-12)
12611	 */
12612	public function IncludeJS($script) {
12613		$this->javascript .= $script;
12614	}
12615
12616	/**
12617	 * Adds a javascript object and return object ID
12618	 * @param $script (string) Javascript code
12619	 * @param $onload (boolean) if true executes this object when opening the document
12620	 * @return int internal object ID
12621	 * @public
12622	 * @author Nicola Asuni
12623	 * @since 4.8.000 (2009-09-07)
12624	 */
12625	public function addJavascriptObject($script, $onload=false) {
12626		if ($this->pdfa_mode) {
12627			// javascript is not allowed in PDF/A mode
12628			return false;
12629		}
12630		++$this->n;
12631		$this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload);
12632		return $this->n;
12633	}
12634
12635	/**
12636	 * Create a javascript PDF string.
12637	 * @protected
12638	 * @author Johannes G\FCntert, Nicola Asuni
12639	 * @since 2.1.002 (2008-02-12)
12640	 */
12641	protected function _putjavascript() {
12642		if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) {
12643			return;
12644		}
12645		if (strpos($this->javascript, 'this.addField') > 0) {
12646			if (!$this->ur['enabled']) {
12647				//$this->setUserRights();
12648			}
12649			// the following two lines are used to avoid form fields duplication after saving
12650			// The addField method only works when releasing user rights (UR3)
12651			$jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
12652			$jsb = "getField('tcpdfdocsaved').value='saved';";
12653			$this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
12654		}
12655		// name tree for javascript
12656		$this->n_js = '<< /Names [';
12657		if (!empty($this->javascript)) {
12658			$this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
12659		}
12660		if (!empty($this->js_objects)) {
12661			foreach ($this->js_objects as $key => $val) {
12662				if ($val['onload']) {
12663					$this->n_js .= ' (JS'.$key.') '.$key.' 0 R';
12664				}
12665			}
12666		}
12667		$this->n_js .= ' ] >>';
12668		// default Javascript object
12669		if (!empty($this->javascript)) {
12670			$obj_id = $this->_newobj();
12671			$out = '<< /S /JavaScript';
12672			$out .= ' /JS '.$this->_textstring($this->javascript, $obj_id);
12673			$out .= ' >>';
12674			$out .= "\n".'endobj';
12675			$this->_out($out);
12676		}
12677		// additional Javascript objects
12678		if (!empty($this->js_objects)) {
12679			foreach ($this->js_objects as $key => $val) {
12680				$out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
12681				$this->_out($out);
12682			}
12683		}
12684	}
12685
12686	/**
12687	 * Adds a javascript form field.
12688	 * @param $type (string) field type
12689	 * @param $name (string) field name
12690	 * @param $x (int) horizontal position
12691	 * @param $y (int) vertical position
12692	 * @param $w (int) width
12693	 * @param $h (int) height
12694	 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12695	 * @protected
12696	 * @author Denis Van Nuffelen, Nicola Asuni
12697	 * @since 2.1.002 (2008-02-12)
12698	 */
12699	protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
12700		if ($this->rtl) {
12701			$x = $x - $w;
12702		}
12703		// the followind avoid fields duplication after saving the document
12704		$this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {";
12705		$k = $this->k;
12706		$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";
12707		$this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
12708		while (list($key, $val) = each($prop)) {
12709			if (strcmp(substr($key, -5), 'Color') == 0) {
12710				$val = TCPDF_COLORS::_JScolor($val);
12711			} else {
12712				$val = "'".$val."'";
12713			}
12714			$this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
12715		}
12716		if ($this->rtl) {
12717			$this->x -= $w;
12718		} else {
12719			$this->x += $w;
12720		}
12721		$this->javascript .= '}';
12722	}
12723
12724	// --- FORM FIELDS -----------------------------------------------------
12725
12726
12727
12728	/**
12729	 * Set default properties for form fields.
12730	 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12731	 * @public
12732	 * @author Nicola Asuni
12733	 * @since 4.8.000 (2009-09-06)
12734	 */
12735	public function setFormDefaultProp($prop=array()) {
12736		$this->default_form_prop = $prop;
12737	}
12738
12739	/**
12740	 * Return the default properties for form fields.
12741	 * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12742	 * @public
12743	 * @author Nicola Asuni
12744	 * @since 4.8.000 (2009-09-06)
12745	 */
12746	public function getFormDefaultProp() {
12747		return $this->default_form_prop;
12748	}
12749
12750	/**
12751	 * Creates a text field
12752	 * @param $name (string) field name
12753	 * @param $w (float) Width of the rectangle
12754	 * @param $h (float) Height of the rectangle
12755	 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12756	 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
12757	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12758	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12759	 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12760	 * @public
12761	 * @author Nicola Asuni
12762	 * @since 4.8.000 (2009-09-07)
12763	 */
12764	public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12765		if ($x === '') {
12766			$x = $this->x;
12767		}
12768		if ($y === '') {
12769			$y = $this->y;
12770		}
12771		// check page for no-write regions and adapt page margins if necessary
12772		list($x, $y) = $this->checkPageRegions($h, $x, $y);
12773		if ($js) {
12774			$this->_addfield('text', $name, $x, $y, $w, $h, $prop);
12775			return;
12776		}
12777		// get default style
12778		$prop = array_merge($this->getFormDefaultProp(), $prop);
12779		// get annotation data
12780		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12781		// set default appearance stream
12782		$this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12783		$fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12784		$popt['da'] = $fontstyle;
12785		// build appearance stream
12786		$popt['ap'] = array();
12787		$popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12788		$text = '';
12789		if (isset($prop['value']) AND !empty($prop['value'])) {
12790			$text = $prop['value'];
12791		} elseif (isset($opt['v']) AND !empty($opt['v'])) {
12792			$text = $opt['v'];
12793		}
12794		$tmpid = $this->startTemplate($w, $h, false);
12795		$align = '';
12796		if (isset($popt['q'])) {
12797			switch ($popt['q']) {
12798				case 0: {
12799					$align = 'L';
12800					break;
12801				}
12802				case 1: {
12803					$align = 'C';
12804					break;
12805				}
12806				case 2: {
12807					$align = 'R';
12808					break;
12809				}
12810				default: {
12811					$align = '';
12812					break;
12813				}
12814			}
12815		}
12816		$this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12817		$this->endTemplate();
12818		--$this->n;
12819		$popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12820		unset($this->xobjects[$tmpid]);
12821		$popt['ap']['n'] .= 'Q EMC';
12822		// merge options
12823		$opt = array_merge($popt, $opt);
12824		// remove some conflicting options
12825		unset($opt['bs']);
12826		// set remaining annotation data
12827		$opt['Subtype'] = 'Widget';
12828		$opt['ft'] = 'Tx';
12829		$opt['t'] = $name;
12830		// Additional annotation's parameters (check _putannotsobj() method):
12831		//$opt['f']
12832		//$opt['as']
12833		//$opt['bs']
12834		//$opt['be']
12835		//$opt['c']
12836		//$opt['border']
12837		//$opt['h']
12838		//$opt['mk'];
12839		//$opt['mk']['r']
12840		//$opt['mk']['bc'];
12841		//$opt['mk']['bg'];
12842		unset($opt['mk']['ca']);
12843		unset($opt['mk']['rc']);
12844		unset($opt['mk']['ac']);
12845		unset($opt['mk']['i']);
12846		unset($opt['mk']['ri']);
12847		unset($opt['mk']['ix']);
12848		unset($opt['mk']['if']);
12849		//$opt['mk']['if']['sw'];
12850		//$opt['mk']['if']['s'];
12851		//$opt['mk']['if']['a'];
12852		//$opt['mk']['if']['fb'];
12853		unset($opt['mk']['tp']);
12854		//$opt['tu']
12855		//$opt['tm']
12856		//$opt['ff']
12857		//$opt['v']
12858		//$opt['dv']
12859		//$opt['a']
12860		//$opt['aa']
12861		//$opt['q']
12862		$this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12863		if ($this->rtl) {
12864			$this->x -= $w;
12865		} else {
12866			$this->x += $w;
12867		}
12868	}
12869
12870	/**
12871	 * Creates a RadioButton field.
12872	 * @param $name (string) Field name.
12873	 * @param $w (int) Width of the radio button.
12874	 * @param $prop (array) Javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12875	 * @param $opt (array) Annotation parameters. Possible values are described on official PDF32000_2008 reference.
12876	 * @param $onvalue (string) Value to be returned if selected.
12877	 * @param $checked (boolean) Define the initial state.
12878	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12879	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12880	 * @param $js (boolean) If true put the field using JavaScript (requires Acrobat Writer to be rendered).
12881	 * @public
12882	 * @author Nicola Asuni
12883	 * @since 4.8.000 (2009-09-07)
12884	 */
12885	public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
12886		if ($x === '') {
12887			$x = $this->x;
12888		}
12889		if ($y === '') {
12890			$y = $this->y;
12891		}
12892		// check page for no-write regions and adapt page margins if necessary
12893		list($x, $y) = $this->checkPageRegions($w, $x, $y);
12894		if ($js) {
12895			$this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
12896			return;
12897		}
12898		if (TCPDF_STATIC::empty_string($onvalue)) {
12899			$onvalue = 'On';
12900		}
12901		if ($checked) {
12902			$defval = $onvalue;
12903		} else {
12904			$defval = 'Off';
12905		}
12906		// set font
12907		$font = 'zapfdingbats';
12908		if ($this->pdfa_mode) {
12909			// all fonts must be embedded
12910			$font = 'pdfa'.$font;
12911		}
12912		$this->AddFont($font);
12913		$tmpfont = $this->getFontBuffer($font);
12914		// set data for parent group
12915		if (!isset($this->radiobutton_groups[$this->page])) {
12916			$this->radiobutton_groups[$this->page] = array();
12917		}
12918		if (!isset($this->radiobutton_groups[$this->page][$name])) {
12919			$this->radiobutton_groups[$this->page][$name] = array();
12920			++$this->n;
12921			$this->radiobutton_groups[$this->page][$name]['n'] = $this->n;
12922			$this->radio_groups[] = $this->n;
12923		}
12924		$kid = ($this->n + 1);
12925		// save object ID to be added on Kids entry on parent object
12926		$this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval);
12927		// get default style
12928		$prop = array_merge($this->getFormDefaultProp(), $prop);
12929		$prop['NoToggleToOff'] = 'true';
12930		$prop['Radio'] = 'true';
12931		$prop['borderStyle'] = 'inset';
12932		// get annotation data
12933		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12934		// set additional default options
12935		$this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
12936		$fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
12937		$popt['da'] = $fontstyle;
12938		// build appearance stream
12939		$popt['ap'] = array();
12940		$popt['ap']['n'] = array();
12941		$fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k);
12942		$fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
12943		$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);
12944		$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);
12945		if (!isset($popt['mk'])) {
12946			$popt['mk'] = array();
12947		}
12948		$popt['mk']['ca'] = '(l)';
12949		// merge options
12950		$opt = array_merge($popt, $opt);
12951		// set remaining annotation data
12952		$opt['Subtype'] = 'Widget';
12953		$opt['ft'] = 'Btn';
12954		if ($checked) {
12955			$opt['v'] = array('/'.$onvalue);
12956			$opt['as'] = $onvalue;
12957		} else {
12958			$opt['as'] = 'Off';
12959		}
12960		// store readonly flag
12961		if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) {
12962			$this->radiobutton_groups[$this->page][$name]['#readonly#'] = false;
12963		}
12964		$this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64);
12965		$this->Annotation($x, $y, $w, $w, $name, $opt, 0);
12966		if ($this->rtl) {
12967			$this->x -= $w;
12968		} else {
12969			$this->x += $w;
12970		}
12971	}
12972
12973	/**
12974	 * Creates a List-box field
12975	 * @param $name (string) field name
12976	 * @param $w (int) width
12977	 * @param $h (int) height
12978	 * @param $values (array) array containing the list of values.
12979	 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12980	 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
12981	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12982	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12983	 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12984	 * @public
12985	 * @author Nicola Asuni
12986	 * @since 4.8.000 (2009-09-07)
12987	 */
12988	public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12989		if ($x === '') {
12990			$x = $this->x;
12991		}
12992		if ($y === '') {
12993			$y = $this->y;
12994		}
12995		// check page for no-write regions and adapt page margins if necessary
12996		list($x, $y) = $this->checkPageRegions($h, $x, $y);
12997		if ($js) {
12998			$this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
12999			$s = '';
13000			foreach ($values as $value) {
13001				if (is_array($value)) {
13002					$s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
13003				} else {
13004					$s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
13005				}
13006			}
13007			$this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
13008			return;
13009		}
13010		// get default style
13011		$prop = array_merge($this->getFormDefaultProp(), $prop);
13012		// get annotation data
13013		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13014		// set additional default values
13015		$this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13016		$fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13017		$popt['da'] = $fontstyle;
13018		// build appearance stream
13019		$popt['ap'] = array();
13020		$popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13021		$text = '';
13022		foreach($values as $item) {
13023			if (is_array($item)) {
13024				$text .= $item[1]."\n";
13025			} else {
13026				$text .= $item."\n";
13027			}
13028		}
13029		$tmpid = $this->startTemplate($w, $h, false);
13030		$this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13031		$this->endTemplate();
13032		--$this->n;
13033		$popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13034		unset($this->xobjects[$tmpid]);
13035		$popt['ap']['n'] .= 'Q EMC';
13036		// merge options
13037		$opt = array_merge($popt, $opt);
13038		// set remaining annotation data
13039		$opt['Subtype'] = 'Widget';
13040		$opt['ft'] = 'Ch';
13041		$opt['t'] = $name;
13042		$opt['opt'] = $values;
13043		unset($opt['mk']['ca']);
13044		unset($opt['mk']['rc']);
13045		unset($opt['mk']['ac']);
13046		unset($opt['mk']['i']);
13047		unset($opt['mk']['ri']);
13048		unset($opt['mk']['ix']);
13049		unset($opt['mk']['if']);
13050		unset($opt['mk']['tp']);
13051		$this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13052		if ($this->rtl) {
13053			$this->x -= $w;
13054		} else {
13055			$this->x += $w;
13056		}
13057	}
13058
13059	/**
13060	 * Creates a Combo-box field
13061	 * @param $name (string) field name
13062	 * @param $w (int) width
13063	 * @param $h (int) height
13064	 * @param $values (array) array containing the list of values.
13065	 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13066	 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13067	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
13068	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
13069	 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13070	 * @public
13071	 * @author Nicola Asuni
13072	 * @since 4.8.000 (2009-09-07)
13073	 */
13074	public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13075		if ($x === '') {
13076			$x = $this->x;
13077		}
13078		if ($y === '') {
13079			$y = $this->y;
13080		}
13081		// check page for no-write regions and adapt page margins if necessary
13082		list($x, $y) = $this->checkPageRegions($h, $x, $y);
13083		if ($js) {
13084			$this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
13085			$s = '';
13086			foreach ($values as $value) {
13087				if (is_array($value)) {
13088					$s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
13089				} else {
13090					$s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
13091				}
13092			}
13093			$this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
13094			return;
13095		}
13096		// get default style
13097		$prop = array_merge($this->getFormDefaultProp(), $prop);
13098		$prop['Combo'] = true;
13099		// get annotation data
13100		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13101		// set additional default options
13102		$this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13103		$fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13104		$popt['da'] = $fontstyle;
13105		// build appearance stream
13106		$popt['ap'] = array();
13107		$popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13108		$text = '';
13109		foreach($values as $item) {
13110			if (is_array($item)) {
13111				$text .= $item[1]."\n";
13112			} else {
13113				$text .= $item."\n";
13114			}
13115		}
13116		$tmpid = $this->startTemplate($w, $h, false);
13117		$this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13118		$this->endTemplate();
13119		--$this->n;
13120		$popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13121		unset($this->xobjects[$tmpid]);
13122		$popt['ap']['n'] .= 'Q EMC';
13123		// merge options
13124		$opt = array_merge($popt, $opt);
13125		// set remaining annotation data
13126		$opt['Subtype'] = 'Widget';
13127		$opt['ft'] = 'Ch';
13128		$opt['t'] = $name;
13129		$opt['opt'] = $values;
13130		unset($opt['mk']['ca']);
13131		unset($opt['mk']['rc']);
13132		unset($opt['mk']['ac']);
13133		unset($opt['mk']['i']);
13134		unset($opt['mk']['ri']);
13135		unset($opt['mk']['ix']);
13136		unset($opt['mk']['if']);
13137		unset($opt['mk']['tp']);
13138		$this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13139		if ($this->rtl) {
13140			$this->x -= $w;
13141		} else {
13142			$this->x += $w;
13143		}
13144	}
13145
13146	/**
13147	 * Creates a CheckBox field
13148	 * @param $name (string) field name
13149	 * @param $w (int) width
13150	 * @param $checked (boolean) define the initial state.
13151	 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13152	 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13153	 * @param $onvalue (string) value to be returned if selected.
13154	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
13155	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
13156	 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13157	 * @public
13158	 * @author Nicola Asuni
13159	 * @since 4.8.000 (2009-09-07)
13160	 */
13161	public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
13162		if ($x === '') {
13163			$x = $this->x;
13164		}
13165		if ($y === '') {
13166			$y = $this->y;
13167		}
13168		// check page for no-write regions and adapt page margins if necessary
13169		list($x, $y) = $this->checkPageRegions($w, $x, $y);
13170		if ($js) {
13171			$this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
13172			return;
13173		}
13174		if (!isset($prop['value'])) {
13175			$prop['value'] = array('Yes');
13176		}
13177		// get default style
13178		$prop = array_merge($this->getFormDefaultProp(), $prop);
13179		$prop['borderStyle'] = 'inset';
13180		// get annotation data
13181		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13182		// set additional default options
13183		$font = 'zapfdingbats';
13184		if ($this->pdfa_mode) {
13185			// all fonts must be embedded
13186			$font = 'pdfa'.$font;
13187		}
13188		$this->AddFont($font);
13189		$tmpfont = $this->getFontBuffer($font);
13190		$this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
13191		$fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
13192		$popt['da'] = $fontstyle;
13193		// build appearance stream
13194		$popt['ap'] = array();
13195		$popt['ap']['n'] = array();
13196		$fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k);
13197		$fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
13198		$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);
13199		$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);
13200		// merge options
13201		$opt = array_merge($popt, $opt);
13202		// set remaining annotation data
13203		$opt['Subtype'] = 'Widget';
13204		$opt['ft'] = 'Btn';
13205		$opt['t'] = $name;
13206		if (TCPDF_STATIC::empty_string($onvalue)) {
13207			$onvalue = 'Yes';
13208		}
13209		$opt['opt'] = array($onvalue);
13210		if ($checked) {
13211			$opt['v'] = array('/Yes');
13212			$opt['as'] = 'Yes';
13213		} else {
13214			$opt['v'] = array('/Off');
13215			$opt['as'] = 'Off';
13216		}
13217		$this->Annotation($x, $y, $w, $w, $name, $opt, 0);
13218		if ($this->rtl) {
13219			$this->x -= $w;
13220		} else {
13221			$this->x += $w;
13222		}
13223	}
13224
13225	/**
13226	 * Creates a button field
13227	 * @param $name (string) field name
13228	 * @param $w (int) width
13229	 * @param $h (int) height
13230	 * @param $caption (string) caption.
13231	 * @param $action (mixed) 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.
13232	 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13233	 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13234	 * @param $x (float) Abscissa of the upper-left corner of the rectangle
13235	 * @param $y (float) Ordinate of the upper-left corner of the rectangle
13236	 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13237	 * @public
13238	 * @author Nicola Asuni
13239	 * @since 4.8.000 (2009-09-07)
13240	 */
13241	public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13242		if ($x === '') {
13243			$x = $this->x;
13244		}
13245		if ($y === '') {
13246			$y = $this->y;
13247		}
13248		// check page for no-write regions and adapt page margins if necessary
13249		list($x, $y) = $this->checkPageRegions($h, $x, $y);
13250		if ($js) {
13251			$this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
13252			$this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
13253			$this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
13254			$this->javascript .= 'f'.$name.".highlight='push';\n";
13255			$this->javascript .= 'f'.$name.".print=false;\n";
13256			return;
13257		}
13258		// get default style
13259		$prop = array_merge($this->getFormDefaultProp(), $prop);
13260		$prop['Pushbutton'] = 'true';
13261		$prop['highlight'] = 'push';
13262		$prop['display'] = 'display.noPrint';
13263		// get annotation data
13264		$popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13265		$this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13266		$fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13267		$popt['da'] = $fontstyle;
13268		// build appearance stream
13269		$popt['ap'] = array();
13270		$popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13271		$tmpid = $this->startTemplate($w, $h, false);
13272		$bw = (2 / $this->k); // border width
13273		$border = array(
13274			'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13275			'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
13276			'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13277			'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
13278		$this->SetFillColor(204);
13279		$this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
13280		$this->endTemplate();
13281		--$this->n;
13282		$popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13283		unset($this->xobjects[$tmpid]);
13284		$popt['ap']['n'] .= 'Q EMC';
13285		// set additional default options
13286		if (!isset($popt['mk'])) {
13287			$popt['mk'] = array();
13288		}
13289		$ann_obj_id = ($this->n + 1);
13290		if (!empty($action) AND !is_array($action)) {
13291			$ann_obj_id = ($this->n + 2);
13292		}
13293		$popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
13294		$popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
13295		$popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
13296		// merge options
13297		$opt = array_merge($popt, $opt);
13298		// set remaining annotation data
13299		$opt['Subtype'] = 'Widget';
13300		$opt['ft'] = 'Btn';
13301		$opt['t'] = $caption;
13302		$opt['v'] = $name;
13303		if (!empty($action)) {
13304			if (is_array($action)) {
13305				// form action options as on section 12.7.5 of PDF32000_2008.
13306				$opt['aa'] = '/D <<';
13307				$bmode = array('SubmitForm', 'ResetForm', 'ImportData');
13308				foreach ($action AS $key => $val) {
13309					if (($key == 'S') AND in_array($val, $bmode)) {
13310						$opt['aa'] .= ' /S /'.$val;
13311					} elseif (($key == 'F') AND (!empty($val))) {
13312						$opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
13313					} elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
13314						$opt['aa'] .= ' /Fields [';
13315						foreach ($val AS $field) {
13316							$opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
13317						}
13318						$opt['aa'] .= ']';
13319					} elseif (($key == 'Flags')) {
13320						$ff = 0;
13321						if (is_array($val)) {
13322							foreach ($val AS $flag) {
13323								switch ($flag) {
13324									case 'Include/Exclude': {
13325										$ff += 1 << 0;
13326										break;
13327									}
13328									case 'IncludeNoValueFields': {
13329										$ff += 1 << 1;
13330										break;
13331									}
13332									case 'ExportFormat': {
13333										$ff += 1 << 2;
13334										break;
13335									}
13336									case 'GetMethod': {
13337										$ff += 1 << 3;
13338										break;
13339									}
13340									case 'SubmitCoordinates': {
13341										$ff += 1 << 4;
13342										break;
13343									}
13344									case 'XFDF': {
13345										$ff += 1 << 5;
13346										break;
13347									}
13348									case 'IncludeAppendSaves': {
13349										$ff += 1 << 6;
13350										break;
13351									}
13352									case 'IncludeAnnotations': {
13353										$ff += 1 << 7;
13354										break;
13355									}
13356									case 'SubmitPDF': {
13357										$ff += 1 << 8;
13358										break;
13359									}
13360									case 'CanonicalFormat': {
13361										$ff += 1 << 9;
13362										break;
13363									}
13364									case 'ExclNonUserAnnots': {
13365										$ff += 1 << 10;
13366										break;
13367									}
13368									case 'ExclFKey': {
13369										$ff += 1 << 11;
13370										break;
13371									}
13372									case 'EmbedForm': {
13373										$ff += 1 << 13;
13374										break;
13375									}
13376								}
13377							}
13378						} else {
13379							$ff = intval($val);
13380						}
13381						$opt['aa'] .= ' /Flags '.$ff;
13382					}
13383				}
13384				$opt['aa'] .= ' >>';
13385			} else {
13386				// Javascript action or raw action command
13387				$js_obj_id = $this->addJavascriptObject($action);
13388				$opt['aa'] = '/D '.$js_obj_id.' 0 R';
13389			}
13390		}
13391		$this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13392		if ($this->rtl) {
13393			$this->x -= $w;
13394		} else {
13395			$this->x += $w;
13396		}
13397	}
13398
13399	// --- END FORMS FIELDS ------------------------------------------------
13400
13401	/**
13402	 * Add certification signature (DocMDP or UR3)
13403	 * You can set only one signature type
13404	 * @protected
13405	 * @author Nicola Asuni
13406	 * @since 4.6.008 (2009-05-07)
13407	 */
13408	protected function _putsignature() {
13409		if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
13410			return;
13411		}
13412		$sigobjid = ($this->sig_obj_id + 1);
13413		$out = $this->_getobj($sigobjid)."\n";
13414		$out .= '<< /Type /Sig';
13415		$out .= ' /Filter /Adobe.PPKLite';
13416		$out .= ' /SubFilter /adbe.pkcs7.detached';
13417		$out .= ' '.TCPDF_STATIC::$byterange_string;
13418		$out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>';
13419		if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
13420			$out .= ' /Reference ['; // array of signature reference dictionaries
13421			$out .= ' << /Type /SigRef';
13422			if ($this->signature_data['cert_type'] > 0) {
13423				$out .= ' /TransformMethod /DocMDP';
13424				$out .= ' /TransformParams <<';
13425				$out .= ' /Type /TransformParams';
13426				$out .= ' /P '.$this->signature_data['cert_type'];
13427				$out .= ' /V /1.2';
13428			} else {
13429				$out .= ' /TransformMethod /UR3';
13430				$out .= ' /TransformParams <<';
13431				$out .= ' /Type /TransformParams';
13432				$out .= ' /V /2.2';
13433				if (!TCPDF_STATIC::empty_string($this->ur['document'])) {
13434					$out .= ' /Document['.$this->ur['document'].']';
13435				}
13436				if (!TCPDF_STATIC::empty_string($this->ur['form'])) {
13437					$out .= ' /Form['.$this->ur['form'].']';
13438				}
13439				if (!TCPDF_STATIC::empty_string($this->ur['signature'])) {
13440					$out .= ' /Signature['.$this->ur['signature'].']';
13441				}
13442				if (!TCPDF_STATIC::empty_string($this->ur['annots'])) {
13443					$out .= ' /Annots['.$this->ur['annots'].']';
13444				}
13445				if (!TCPDF_STATIC::empty_string($this->ur['ef'])) {
13446					$out .= ' /EF['.$this->ur['ef'].']';
13447				}
13448				if (!TCPDF_STATIC::empty_string($this->ur['formex'])) {
13449					$out .= ' /FormEX['.$this->ur['formex'].']';
13450				}
13451			}
13452			$out .= ' >>'; // close TransformParams
13453			// optional digest data (values must be calculated and replaced later)
13454			//$out .= ' /Data ********** 0 R';
13455			//$out .= ' /DigestMethod/MD5';
13456			//$out .= ' /DigestLocation[********** 34]';
13457			//$out .= ' /DigestValue<********************************>';
13458			$out .= ' >>';
13459			$out .= ' ]'; // end of reference
13460		}
13461		if (isset($this->signature_data['info']['Name']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Name'])) {
13462			$out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid);
13463		}
13464		if (isset($this->signature_data['info']['Location']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Location'])) {
13465			$out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid);
13466		}
13467		if (isset($this->signature_data['info']['Reason']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Reason'])) {
13468			$out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid);
13469		}
13470		if (isset($this->signature_data['info']['ContactInfo']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['ContactInfo'])) {
13471			$out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid);
13472		}
13473		$out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp);
13474		$out .= ' >>';
13475		$out .= "\n".'endobj';
13476		$this->_out($out);
13477	}
13478
13479	/**
13480	 * Set User's Rights for PDF Reader
13481	 * WARNING: This is experimental and currently do not work.
13482	 * Check the PDF Reference 8.7.1 Transform Methods,
13483	 * Table 8.105 Entries in the UR transform parameters dictionary
13484	 * @param $enable (boolean) if true enable user's rights on PDF reader
13485	 * @param $document (string) 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.
13486	 * @param $annots (string) 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.
13487	 * @param $form (string) Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
13488	 * @param $signature (string) 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.
13489	 * @param $ef (string) 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
13490	 Names specifying additional embedded-files-related usage rights for the document.
13491	 * @param $formex (string) 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.
13492	 * @public
13493	 * @author Nicola Asuni
13494	 * @since 2.9.000 (2008-03-26)
13495	 */
13496	public function setUserRights(
13497			$enable=true,
13498			$document='/FullSave',
13499			$annots='/Create/Delete/Modify/Copy/Import/Export',
13500			$form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
13501			$signature='/Modify',
13502			$ef='/Create/Delete/Modify/Import',
13503			$formex='') {
13504		$this->ur['enabled'] = $enable;
13505		$this->ur['document'] = $document;
13506		$this->ur['annots'] = $annots;
13507		$this->ur['form'] = $form;
13508		$this->ur['signature'] = $signature;
13509		$this->ur['ef'] = $ef;
13510		$this->ur['formex'] = $formex;
13511		if (!$this->sign) {
13512			$this->setSignature('', '', '', '', 0, array());
13513		}
13514	}
13515
13516	/**
13517	 * Enable document signature (requires the OpenSSL Library).
13518	 * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
13519	 * To create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13520	 * To export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13521	 * To convert pfx certificate to pem: openssl pkcs12 -in tcpdf.pfx -out tcpdf.crt -nodes
13522	 * @param $signing_cert (mixed) signing certificate (string or filename prefixed with 'file://')
13523	 * @param $private_key (mixed) private key (string or filename prefixed with 'file://')
13524	 * @param $private_key_password (string) password
13525	 * @param $extracerts (string) 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.
13526	 * @param $cert_type (int) 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.
13527	 * @param $info (array) array of option information: Name, Location, Reason, ContactInfo.
13528	 * @param $approval (string) Enable approval signature eg. for PDF incremental update
13529	 * @public
13530	 * @author Nicola Asuni
13531	 * @since 4.6.005 (2009-04-24)
13532	 */
13533	public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='') {
13534		// to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13535		// to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13536		// to convert pfx certificate to pem: openssl
13537		//     OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
13538		$this->sign = true;
13539		++$this->n;
13540		$this->sig_obj_id = $this->n; // signature widget
13541		++$this->n; // signature object ($this->sig_obj_id + 1)
13542		$this->signature_data = array();
13543		if (strlen($signing_cert) == 0) {
13544			$this->Error('Please provide a certificate file and password!');
13545		}
13546		if (strlen($private_key) == 0) {
13547			$private_key = $signing_cert;
13548		}
13549		$this->signature_data['signcert'] = $signing_cert;
13550		$this->signature_data['privkey'] = $private_key;
13551		$this->signature_data['password'] = $private_key_password;
13552		$this->signature_data['extracerts'] = $extracerts;
13553		$this->signature_data['cert_type'] = $cert_type;
13554		$this->signature_data['info'] = $info;
13555		$this->signature_data['approval'] = $approval;
13556	}
13557
13558	/**
13559	 * Set the digital signature appearance (a cliccable rectangle area to get signature properties)
13560	 * @param $x (float) Abscissa of the upper-left corner.
13561	 * @param $y (float) Ordinate of the upper-left corner.
13562	 * @param $w (float) Width of the signature area.
13563	 * @param $h (float) Height of the signature area.
13564	 * @param $page (int) option page number (if < 0 the current page is used).
13565	 * @param $name (string) Name of the signature.
13566	 * @public
13567	 * @author Nicola Asuni
13568	 * @since 5.3.011 (2010-06-17)
13569	 */
13570	public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13571		$this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13572	}
13573
13574	/**
13575	 * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties)
13576	 * @param $x (float) Abscissa of the upper-left corner.
13577	 * @param $y (float) Ordinate of the upper-left corner.
13578	 * @param $w (float) Width of the signature area.
13579	 * @param $h (float) Height of the signature area.
13580	 * @param $page (int) option page number (if < 0 the current page is used).
13581	 * @param $name (string) Name of the signature.
13582	 * @public
13583	 * @author Nicola Asuni
13584	 * @since 5.9.101 (2011-07-06)
13585	 */
13586	public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13587		++$this->n;
13588		$this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13589	}
13590
13591	/**
13592	 * Get the array that defines the signature appearance (page and rectangle coordinates).
13593	 * @param $x (float) Abscissa of the upper-left corner.
13594	 * @param $y (float) Ordinate of the upper-left corner.
13595	 * @param $w (float) Width of the signature area.
13596	 * @param $h (float) Height of the signature area.
13597	 * @param $page (int) option page number (if < 0 the current page is used).
13598	 * @param $name (string) Name of the signature.
13599	 * @return (array) Array defining page and rectangle coordinates of signature appearance.
13600	 * @protected
13601	 * @author Nicola Asuni
13602	 * @since 5.9.101 (2011-07-06)
13603	 */
13604	protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13605		$sigapp = array();
13606		if (($page < 1) OR ($page > $this->numpages)) {
13607			$sigapp['page'] = $this->page;
13608		} else {
13609			$sigapp['page'] = intval($page);
13610		}
13611		if (empty($name)) {
13612			$sigapp['name'] = 'Signature';
13613		} else {
13614			$sigapp['name'] = $name;
13615		}
13616		$a = $x * $this->k;
13617		$b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k);
13618		$c = $w * $this->k;
13619		$d = $h * $this->k;
13620		$sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d));
13621		return $sigapp;
13622	}
13623
13624	/**
13625	 * Enable document timestamping (requires the OpenSSL Library).
13626	 * The trusted timestamping improve document security that means that no one should be able to change the document once it has been recorded.
13627	 * Use with digital signature only!
13628	 * @param $tsa_host (string) Time Stamping Authority (TSA) server (prefixed with 'https://')
13629	 * @param $tsa_username (string) Specifies the username for TSA authorization (optional) OR specifies the TSA authorization PEM file (see: example_66.php, optional)
13630	 * @param $tsa_password (string) Specifies the password for TSA authorization (optional)
13631	 * @param $tsa_cert (string) Specifies the location of TSA certificate for authorization (optional for cURL)
13632	 * @public
13633	 * @author Richard Stockinger
13634	 * @since 6.0.090 (2014-06-16)
13635	 */
13636	public function setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='') {
13637		$this->tsa_data = array();
13638		if (!function_exists('curl_init')) {
13639			$this->Error('Please enable cURL PHP extension!');
13640		}
13641		if (strlen($tsa_host) == 0) {
13642			$this->Error('Please specify the host of Time Stamping Authority (TSA)!');
13643		}
13644		$this->tsa_data['tsa_host'] = $tsa_host;
13645		if (is_file($tsa_username)) {
13646			$this->tsa_data['tsa_auth'] = $tsa_username;
13647		} else {
13648			$this->tsa_data['tsa_username'] = $tsa_username;
13649		}
13650		$this->tsa_data['tsa_password'] = $tsa_password;
13651		$this->tsa_data['tsa_cert'] = $tsa_cert;
13652		$this->tsa_timestamp = true;
13653	}
13654
13655	/**
13656	 * NOT YET IMPLEMENTED
13657	 * Request TSA for a timestamp
13658	 * @param $signature (string) Digital signature as binary string
13659	 * @return (string) Timestamped digital signature
13660	 * @protected
13661	 * @author Richard Stockinger
13662	 * @since 6.0.090 (2014-06-16)
13663	 */
13664	protected function applyTSA($signature) {
13665		if (!$this->tsa_timestamp) {
13666			return $signature;
13667		}
13668		//@TODO: implement this feature
13669		return $signature;
13670	}
13671
13672	/**
13673	 * Create a new page group.
13674	 * NOTE: call this function before calling AddPage()
13675	 * @param $page (int) starting group page (leave empty for next page).
13676	 * @public
13677	 * @since 3.0.000 (2008-03-27)
13678	 */
13679	public function startPageGroup($page='') {
13680		if (empty($page)) {
13681			$page = $this->page + 1;
13682		}
13683		$this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1;
13684	}
13685
13686	/**
13687	 * Set the starting page number.
13688	 * @param $num (int) Starting page number.
13689	 * @since 5.9.093 (2011-06-16)
13690	 * @public
13691	 */
13692	public function setStartingPageNumber($num=1) {
13693		$this->starting_page_number = max(0, intval($num));
13694	}
13695
13696	/**
13697	 * Returns the string alias used right align page numbers.
13698	 * If the current font is unicode type, the returned string wil contain an additional open curly brace.
13699	 * @return string
13700	 * @since 5.9.099 (2011-06-27)
13701	 * @public
13702	 */
13703	public function getAliasRightShift() {
13704		// calculate aproximatively the ratio between widths of aliases and replacements.
13705		$ref = '{'.TCPDF_STATIC::$alias_right_shift.'}{'.TCPDF_STATIC::$alias_tot_pages.'}{'.TCPDF_STATIC::$alias_num_page.'}';
13706		$rep = str_repeat(' ', $this->GetNumChars($ref));
13707		$wrep = $this->GetStringWidth($rep);
13708		if ($wrep > 0) {
13709			$wdiff = max(1, ($this->GetStringWidth($ref) / $wrep));
13710		} else {
13711			$wdiff = 1;
13712		}
13713		$sdiff = sprintf('%F', $wdiff);
13714		$alias = TCPDF_STATIC::$alias_right_shift.$sdiff.'}';
13715		if ($this->isUnicodeFont()) {
13716			$alias = '{'.$alias;
13717		}
13718		return $alias;
13719	}
13720
13721	/**
13722	 * Returns the string alias used for the total number of pages.
13723	 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13724	 * This alias will be replaced by the total number of pages in the document.
13725	 * @return string
13726	 * @since 4.0.018 (2008-08-08)
13727	 * @public
13728	 */
13729	public function getAliasNbPages() {
13730		if ($this->isUnicodeFont()) {
13731			return '{'.TCPDF_STATIC::$alias_tot_pages.'}';
13732		}
13733		return TCPDF_STATIC::$alias_tot_pages;
13734	}
13735
13736	/**
13737	 * Returns the string alias used for the page number.
13738	 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13739	 * This alias will be replaced by the page number.
13740	 * @return string
13741	 * @since 4.5.000 (2009-01-02)
13742	 * @public
13743	 */
13744	public function getAliasNumPage() {
13745		if ($this->isUnicodeFont()) {
13746			return '{'.TCPDF_STATIC::$alias_num_page.'}';
13747		}
13748		return TCPDF_STATIC::$alias_num_page;
13749	}
13750
13751	/**
13752	 * Return the alias for the total number of pages in the current page group.
13753	 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13754	 * This alias will be replaced by the total number of pages in this group.
13755	 * @return alias of the current page group
13756	 * @public
13757	 * @since 3.0.000 (2008-03-27)
13758	 */
13759	public function getPageGroupAlias() {
13760		if ($this->isUnicodeFont()) {
13761			return '{'.TCPDF_STATIC::$alias_group_tot_pages.'}';
13762		}
13763		return TCPDF_STATIC::$alias_group_tot_pages;
13764	}
13765
13766	/**
13767	 * Return the alias for the page number on the current page group.
13768	 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13769	 * This alias will be replaced by the page number (relative to the belonging group).
13770	 * @return alias of the current page group
13771	 * @public
13772	 * @since 4.5.000 (2009-01-02)
13773	 */
13774	public function getPageNumGroupAlias() {
13775		if ($this->isUnicodeFont()) {
13776			return '{'.TCPDF_STATIC::$alias_group_num_page.'}';
13777		}
13778		return TCPDF_STATIC::$alias_group_num_page;
13779	}
13780
13781	/**
13782	 * Return the current page in the group.
13783	 * @return current page in the group
13784	 * @public
13785	 * @since 3.0.000 (2008-03-27)
13786	 */
13787	public function getGroupPageNo() {
13788		return $this->pagegroups[$this->currpagegroup];
13789	}
13790
13791	/**
13792	 * Returns the current group page number formatted as a string.
13793	 * @public
13794	 * @since 4.3.003 (2008-11-18)
13795	 * @see PaneNo(), formatPageNumber()
13796	 */
13797	public function getGroupPageNoFormatted() {
13798		return TCPDF_STATIC::formatPageNumber($this->getGroupPageNo());
13799	}
13800
13801	/**
13802	 * Returns the current page number formatted as a string.
13803	 * @public
13804	 * @since 4.2.005 (2008-11-06)
13805	 * @see PaneNo(), formatPageNumber()
13806	 */
13807	public function PageNoFormatted() {
13808		return TCPDF_STATIC::formatPageNumber($this->PageNo());
13809	}
13810
13811	/**
13812	 * Put pdf layers.
13813	 * @protected
13814	 * @since 3.0.000 (2008-03-27)
13815	 */
13816	protected function _putocg() {
13817		if (empty($this->pdflayers)) {
13818			return;
13819		}
13820		foreach ($this->pdflayers as $key => $layer) {
13821			 $this->pdflayers[$key]['objid'] = $this->_newobj();
13822			 $out = '<< /Type /OCG';
13823			 $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']);
13824			 $out .= ' /Usage <<';
13825			 if (isset($layer['print']) AND ($layer['print'] !== NULL)) {
13826				$out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>';
13827			 }
13828			 $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>';
13829			 $out .= ' >> >>';
13830			 $out .= "\n".'endobj';
13831			 $this->_out($out);
13832		}
13833	}
13834
13835	/**
13836	 * Start a new pdf layer.
13837	 * @param $name (string) Layer name (only a-z letters and numbers). Leave empty for automatic name.
13838	 * @param $print (boolean|null) Set to TRUE to print this layer, FALSE to not print and NULL to not set this option
13839	 * @param $view (boolean) Set to true to view this layer.
13840	 * @param $lock (boolean) If true lock the layer
13841	 * @public
13842	 * @since 5.9.102 (2011-07-13)
13843	 */
13844	public function startLayer($name='', $print=true, $view=true, $lock=true) {
13845		if ($this->state != 2) {
13846			return;
13847		}
13848		$layer = sprintf('LYR%03d', (count($this->pdflayers) + 1));
13849		if (empty($name)) {
13850			$name = $layer;
13851		} else {
13852			$name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
13853		}
13854		$this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view, 'lock' => $lock);
13855		$this->openMarkedContent = true;
13856		$this->_out('/OC /'.$layer.' BDC');
13857	}
13858
13859	/**
13860	 * End the current PDF layer.
13861	 * @public
13862	 * @since 5.9.102 (2011-07-13)
13863	 */
13864	public function endLayer() {
13865		if ($this->state != 2) {
13866			return;
13867		}
13868		if ($this->openMarkedContent) {
13869			// close existing open marked-content layer
13870			$this->_out('EMC');
13871			$this->openMarkedContent = false;
13872		}
13873	}
13874
13875	/**
13876	 * Set the visibility of the successive elements.
13877	 * This can be useful, for instance, to put a background
13878	 * image or color that will show on screen but won't print.
13879	 * @param $v (string) visibility mode. Legal values are: all, print, screen or view.
13880	 * @public
13881	 * @since 3.0.000 (2008-03-27)
13882	 */
13883	public function setVisibility($v) {
13884		if ($this->state != 2) {
13885			return;
13886		}
13887		$this->endLayer();
13888		switch($v) {
13889			case 'print': {
13890				$this->startLayer('Print', true, false);
13891				break;
13892			}
13893			case 'view':
13894			case 'screen': {
13895				$this->startLayer('View', false, true);
13896				break;
13897			}
13898			case 'all': {
13899				$this->_out('');
13900				break;
13901			}
13902			default: {
13903				$this->Error('Incorrect visibility: '.$v);
13904				break;
13905			}
13906		}
13907	}
13908
13909	/**
13910	 * Add transparency parameters to the current extgstate
13911	 * @param $parms (array) parameters
13912	 * @return the number of extgstates
13913	 * @protected
13914	 * @since 3.0.000 (2008-03-27)
13915	 */
13916	protected function addExtGState($parms) {
13917		if ($this->pdfa_mode) {
13918			// transparencies are not allowed in PDF/A mode
13919			return;
13920		}
13921		// check if this ExtGState already exist
13922		foreach ($this->extgstates as $i => $ext) {
13923			if ($ext['parms'] == $parms) {
13924				if ($this->inxobj) {
13925					// we are inside an XObject template
13926					$this->xobjects[$this->xobjid]['extgstates'][$i] = $ext;
13927				}
13928				// return reference to existing ExtGState
13929				return $i;
13930			}
13931		}
13932		$n = (count($this->extgstates) + 1);
13933		$this->extgstates[$n] = array('parms' => $parms);
13934		if ($this->inxobj) {
13935			// we are inside an XObject template
13936			$this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n];
13937		}
13938		return $n;
13939	}
13940
13941	/**
13942	 * Add an extgstate
13943	 * @param $gs (array) extgstate
13944	 * @protected
13945	 * @since 3.0.000 (2008-03-27)
13946	 */
13947	protected function setExtGState($gs) {
13948		if ($this->pdfa_mode OR ($this->state != 2)) {
13949			// transparency is not allowed in PDF/A mode
13950			return;
13951		}
13952		$this->_out(sprintf('/GS%d gs', $gs));
13953	}
13954
13955	/**
13956	 * Put extgstates for object transparency
13957	 * @protected
13958	 * @since 3.0.000 (2008-03-27)
13959	 */
13960	protected function _putextgstates() {
13961		foreach ($this->extgstates as $i => $ext) {
13962			$this->extgstates[$i]['n'] = $this->_newobj();
13963			$out = '<< /Type /ExtGState';
13964			foreach ($ext['parms'] as $k => $v) {
13965				if (is_float($v)) {
13966					$v = sprintf('%F', $v);
13967				} elseif ($v === true) {
13968					$v = 'true';
13969				} elseif ($v === false) {
13970					$v = 'false';
13971				}
13972				$out .= ' /'.$k.' '.$v;
13973			}
13974			$out .= ' >>';
13975			$out .= "\n".'endobj';
13976			$this->_out($out);
13977		}
13978	}
13979
13980	/**
13981	 * Set overprint mode for stroking (OP) and non-stroking (op) painting operations.
13982	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
13983	 * @param $stroking (boolean) If true apply overprint for stroking operations.
13984	 * @param $nonstroking (boolean) If true apply overprint for painting operations other than stroking.
13985	 * @param $mode (integer) 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).
13986	 * @public
13987	 * @since 5.9.152 (2012-03-23)
13988	 */
13989	public function setOverprint($stroking=true, $nonstroking='', $mode=0) {
13990		if ($this->state != 2) {
13991			return;
13992		}
13993		$stroking = $stroking ? true : false;
13994		if (TCPDF_STATIC::empty_string($nonstroking)) {
13995			// default value if not set
13996			$nonstroking = $stroking;
13997		} else {
13998			$nonstroking = $nonstroking ? true : false;
13999		}
14000		if (($mode != 0) AND ($mode != 1)) {
14001			$mode = 0;
14002		}
14003		$this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode);
14004		$gs = $this->addExtGState($this->overprint);
14005		$this->setExtGState($gs);
14006	}
14007
14008	/**
14009	 * Get the overprint mode array (OP, op, OPM).
14010	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
14011	 * @return array.
14012	 * @public
14013	 * @since 5.9.152 (2012-03-23)
14014	 */
14015	public function getOverprint() {
14016		return $this->overprint;
14017	}
14018
14019	/**
14020	 * Set alpha for stroking (CA) and non-stroking (ca) operations.
14021	 * @param $stroking (float) Alpha value for stroking operations: real value from 0 (transparent) to 1 (opaque).
14022	 * @param $bm (string) blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity
14023	 * @param $nonstroking (float) Alpha value for non-stroking operations: real value from 0 (transparent) to 1 (opaque).
14024	 * @param $ais (boolean)
14025	 * @public
14026	 * @since 3.0.000 (2008-03-27)
14027	 */
14028	public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) {
14029		if ($this->pdfa_mode) {
14030			// transparency is not allowed in PDF/A mode
14031			return;
14032		}
14033		$stroking = floatval($stroking);
14034		if (TCPDF_STATIC::empty_string($nonstroking)) {
14035			// default value if not set
14036			$nonstroking = $stroking;
14037		} else {
14038			$nonstroking = floatval($nonstroking);
14039		}
14040		if ($bm[0] == '/') {
14041			// remove trailing slash
14042			$bm = substr($bm, 1);
14043		}
14044		if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
14045			$bm = 'Normal';
14046		}
14047		$ais = $ais ? true : false;
14048		$this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais);
14049		$gs = $this->addExtGState($this->alpha);
14050		$this->setExtGState($gs);
14051	}
14052
14053	/**
14054	 * Get the alpha mode array (CA, ca, BM, AIS).
14055	 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
14056	 * @return array.
14057	 * @public
14058	 * @since 5.9.152 (2012-03-23)
14059	 */
14060	public function getAlpha() {
14061		return $this->alpha;
14062	}
14063
14064	/**
14065	 * Set the default JPEG compression quality (1-100)
14066	 * @param $quality (int) JPEG quality, integer between 1 and 100
14067	 * @public
14068	 * @since 3.0.000 (2008-03-27)
14069	 */
14070	public function setJPEGQuality($quality) {
14071		if (($quality < 1) OR ($quality > 100)) {
14072			$quality = 75;
14073		}
14074		$this->jpeg_quality = intval($quality);
14075	}
14076
14077	/**
14078	 * Set the default number of columns in a row for HTML tables.
14079	 * @param $cols (int) number of columns
14080	 * @public
14081	 * @since 3.0.014 (2008-06-04)
14082	 */
14083	public function setDefaultTableColumns($cols=4) {
14084		$this->default_table_columns = intval($cols);
14085	}
14086
14087	/**
14088	 * Set the height of the cell (line height) respect the font height.
14089	 * @param $h (int) cell proportion respect font height (typical value = 1.25).
14090	 * @public
14091	 * @since 3.0.014 (2008-06-04)
14092	 */
14093	public function setCellHeightRatio($h) {
14094		$this->cell_height_ratio = $h;
14095	}
14096
14097	/**
14098	 * return the height of cell repect font height.
14099	 * @public
14100	 * @since 4.0.012 (2008-07-24)
14101	 */
14102	public function getCellHeightRatio() {
14103		return $this->cell_height_ratio;
14104	}
14105
14106	/**
14107	 * Set the PDF version (check PDF reference for valid values).
14108	 * @param $version (string) PDF document version.
14109	 * @public
14110	 * @since 3.1.000 (2008-06-09)
14111	 */
14112	public function setPDFVersion($version='1.7') {
14113		if ($this->pdfa_mode) {
14114			// PDF/A mode
14115			$this->PDFVersion = '1.4';
14116		} else {
14117			$this->PDFVersion = $version;
14118		}
14119	}
14120
14121	/**
14122	 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
14123	 * (see Section 8.1 of PDF reference, "Viewer Preferences").
14124	 * <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>
14125	 * @param $preferences (array) array of options.
14126	 * @author Nicola Asuni
14127	 * @public
14128	 * @since 3.1.000 (2008-06-09)
14129	 */
14130	public function setViewerPreferences($preferences) {
14131		$this->viewer_preferences = $preferences;
14132	}
14133
14134	/**
14135	 * Paints color transition registration bars
14136	 * @param $x (float) abscissa of the top left corner of the rectangle.
14137	 * @param $y (float) ordinate of the top left corner of the rectangle.
14138	 * @param $w (float) width of the rectangle.
14139	 * @param $h (float) height of the rectangle.
14140	 * @param $transition (boolean) if true prints tcolor transitions to white.
14141	 * @param $vertical (boolean) if true prints bar vertically.
14142	 * @param $colors (string) 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.
14143	 * @author Nicola Asuni
14144	 * @since 4.9.000 (2010-03-26)
14145	 * @public
14146	 */
14147	public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
14148		if (strpos($colors, 'ALLSPOT') !== false) {
14149			// expand spot colors
14150			$spot_colors = '';
14151			foreach ($this->spot_colors as $spot_color_name => $v) {
14152				$spot_colors .= ','.$spot_color_name;
14153			}
14154			if (!empty($spot_colors)) {
14155				$spot_colors = substr($spot_colors, 1);
14156				$colors = str_replace('ALLSPOT', $spot_colors, $colors);
14157			} else {
14158				$colors = str_replace('ALLSPOT', 'NONE', $colors);
14159			}
14160		}
14161		$bars = explode(',', $colors);
14162		$numbars = count($bars); // number of bars to print
14163		if ($numbars <= 0) {
14164			return;
14165		}
14166		// set bar measures
14167		if ($vertical) {
14168			$coords = array(0, 0, 0, 1);
14169			$wb = $w / $numbars; // bar width
14170			$hb = $h; // bar height
14171			$xd = $wb; // delta x
14172			$yd = 0; // delta y
14173		} else {
14174			$coords = array(1, 0, 0, 0);
14175			$wb = $w; // bar width
14176			$hb = $h / $numbars; // bar height
14177			$xd = 0; // delta x
14178			$yd = $hb; // delta y
14179		}
14180		$xb = $x;
14181		$yb = $y;
14182		foreach ($bars as $col) {
14183			switch ($col) {
14184				// set transition colors
14185				case 'A': { // BLACK (GRAYSCALE)
14186					$col_a = array(255);
14187					$col_b = array(0);
14188					break;
14189				}
14190				case 'W': { // WHITE (GRAYSCALE)
14191					$col_a = array(0);
14192					$col_b = array(255);
14193					break;
14194				}
14195				case 'R': { // RED (RGB)
14196					$col_a = array(255,255,255);
14197					$col_b = array(255,0,0);
14198					break;
14199				}
14200				case 'G': { // GREEN (RGB)
14201					$col_a = array(255,255,255);
14202					$col_b = array(0,255,0);
14203					break;
14204				}
14205				case 'B': { // BLUE (RGB)
14206					$col_a = array(255,255,255);
14207					$col_b = array(0,0,255);
14208					break;
14209				}
14210				case 'C': { // CYAN (CMYK)
14211					$col_a = array(0,0,0,0);
14212					$col_b = array(100,0,0,0);
14213					break;
14214				}
14215				case 'M': { // MAGENTA (CMYK)
14216					$col_a = array(0,0,0,0);
14217					$col_b = array(0,100,0,0);
14218					break;
14219				}
14220				case 'Y': { // YELLOW (CMYK)
14221					$col_a = array(0,0,0,0);
14222					$col_b = array(0,0,100,0);
14223					break;
14224				}
14225				case 'K': { // KEY - BLACK (CMYK)
14226					$col_a = array(0,0,0,0);
14227					$col_b = array(0,0,0,100);
14228					break;
14229				}
14230				case 'RGB': { // BLACK REGISTRATION (RGB)
14231					$col_a = array(255,255,255);
14232					$col_b = array(0,0,0);
14233					break;
14234				}
14235				case 'CMYK': { // BLACK REGISTRATION (CMYK)
14236					$col_a = array(0,0,0,0);
14237					$col_b = array(100,100,100,100);
14238					break;
14239				}
14240				case 'ALL': { // SPOT COLOR REGISTRATION
14241					$col_a = array(0,0,0,0,'None');
14242					$col_b = array(100,100,100,100,'All');
14243					break;
14244				}
14245				case 'NONE': { // SKIP THIS COLOR
14246					$col_a = array(0,0,0,0,'None');
14247					$col_b = array(0,0,0,0,'None');
14248					break;
14249				}
14250				default: { // SPECIFIC SPOT COLOR NAME
14251					$col_a = array(0,0,0,0,'None');
14252					$col_b = TCPDF_COLORS::getSpotColor($col, $this->spot_colors);
14253					if ($col_b === false) {
14254						// in case of error defaults to the registration color
14255						$col_b = array(100,100,100,100,'All');
14256					}
14257					break;
14258				}
14259			}
14260			if ($col != 'NONE') {
14261				if ($transition) {
14262					// color gradient
14263					$this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
14264				} else {
14265					$this->SetFillColorArray($col_b);
14266					// colored rectangle
14267					$this->Rect($xb, $yb, $wb, $hb, 'F', array());
14268				}
14269				$xb += $xd;
14270				$yb += $yd;
14271			}
14272		}
14273	}
14274
14275	/**
14276	 * Paints crop marks.
14277	 * @param $x (float) abscissa of the crop mark center.
14278	 * @param $y (float) ordinate of the crop mark center.
14279	 * @param $w (float) width of the crop mark.
14280	 * @param $h (float) height of the crop mark.
14281	 * @param $type (string) 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.
14282	 * @param $color (array) crop mark color (default spot registration color).
14283	 * @author Nicola Asuni
14284	 * @since 4.9.000 (2010-03-26)
14285	 * @public
14286	 */
14287	public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(100,100,100,100,'All')) {
14288		$this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
14289		$type = strtoupper($type);
14290		$type = preg_replace('/[^A-Z\-\,]*/', '', $type);
14291		// split type in single components
14292		$type = str_replace('-', ',', $type);
14293		$type = str_replace('TL', 'T,L', $type);
14294		$type = str_replace('TR', 'T,R', $type);
14295		$type = str_replace('BL', 'F,L', $type);
14296		$type = str_replace('BR', 'F,R', $type);
14297		$type = str_replace('A', 'T,L', $type);
14298		$type = str_replace('B', 'T,R', $type);
14299		$type = str_replace('T,RO', 'BO', $type);
14300		$type = str_replace('C', 'F,L', $type);
14301		$type = str_replace('D', 'F,R', $type);
14302		$crops = explode(',', strtoupper($type));
14303		// remove duplicates
14304		$crops = array_unique($crops);
14305		$dw = ($w / 4); // horizontal space to leave before the intersection point
14306		$dh = ($h / 4); // vertical space to leave before the intersection point
14307		foreach ($crops as $crop) {
14308			switch ($crop) {
14309				case 'T':
14310				case 'TOP': {
14311					$x1 = $x;
14312					$y1 = ($y - $h);
14313					$x2 = $x;
14314					$y2 = ($y - $dh);
14315					break;
14316				}
14317				case 'F':
14318				case 'BOTTOM': {
14319					$x1 = $x;
14320					$y1 = ($y + $dh);
14321					$x2 = $x;
14322					$y2 = ($y + $h);
14323					break;
14324				}
14325				case 'L':
14326				case 'LEFT': {
14327					$x1 = ($x - $w);
14328					$y1 = $y;
14329					$x2 = ($x - $dw);
14330					$y2 = $y;
14331					break;
14332				}
14333				case 'R':
14334				case 'RIGHT': {
14335					$x1 = ($x + $dw);
14336					$y1 = $y;
14337					$x2 = ($x + $w);
14338					$y2 = $y;
14339					break;
14340				}
14341			}
14342			$this->Line($x1, $y1, $x2, $y2);
14343		}
14344	}
14345
14346	/**
14347	 * Paints a registration mark
14348	 * @param $x (float) abscissa of the registration mark center.
14349	 * @param $y (float) ordinate of the registration mark center.
14350	 * @param $r (float) radius of the crop mark.
14351	 * @param $double (boolean) if true print two concentric crop marks.
14352	 * @param $cola (array) crop mark color (default spot registration color 'All').
14353	 * @param $colb (array) second crop mark color (default spot registration color 'None').
14354	 * @author Nicola Asuni
14355	 * @since 4.9.000 (2010-03-26)
14356	 * @public
14357	 */
14358	public function registrationMark($x, $y, $r, $double=false, $cola=array(100,100,100,100,'All'), $colb=array(0,0,0,0,'None')) {
14359		$line_style = array('width' => max((0.5 / $this->k),($r / 30)), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
14360		$this->SetFillColorArray($cola);
14361		$this->PieSector($x, $y, $r, 90, 180, 'F');
14362		$this->PieSector($x, $y, $r, 270, 360, 'F');
14363		$this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14364		if ($double) {
14365			$ri = $r * 0.5;
14366			$this->SetFillColorArray($colb);
14367			$this->PieSector($x, $y, $ri, 90, 180, 'F');
14368			$this->PieSector($x, $y, $ri, 270, 360, 'F');
14369			$this->SetFillColorArray($cola);
14370			$this->PieSector($x, $y, $ri, 0, 90, 'F');
14371			$this->PieSector($x, $y, $ri, 180, 270, 'F');
14372			$this->Circle($x, $y, $ri, 0, 360, 'C', $line_style, array(), 8);
14373		}
14374	}
14375
14376	/**
14377	 * Paints a CMYK registration mark
14378	 * @param $x (float) abscissa of the registration mark center.
14379	 * @param $y (float) ordinate of the registration mark center.
14380	 * @param $r (float) radius of the crop mark.
14381	 * @author Nicola Asuni
14382	 * @since 6.0.038 (2013-09-30)
14383	 * @public
14384	 */
14385	public function registrationMarkCMYK($x, $y, $r) {
14386		// line width
14387		$lw = max((0.5 / $this->k),($r / 8));
14388		// internal radius
14389		$ri = ($r * 0.6);
14390		// external radius
14391		$re = ($r * 1.3);
14392		// Cyan
14393		$this->SetFillColorArray(array(100,0,0,0));
14394		$this->PieSector($x, $y, $ri, 270, 360, 'F');
14395		// Magenta
14396		$this->SetFillColorArray(array(0,100,0,0));
14397		$this->PieSector($x, $y, $ri, 0, 90, 'F');
14398		// Yellow
14399		$this->SetFillColorArray(array(0,0,100,0));
14400		$this->PieSector($x, $y, $ri, 90, 180, 'F');
14401		// Key - black
14402		$this->SetFillColorArray(array(0,0,0,100));
14403		$this->PieSector($x, $y, $ri, 180, 270, 'F');
14404		// registration color
14405		$line_style = array('width' => $lw, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(100,100,100,100,'All'));
14406		$this->SetFillColorArray(array(100,100,100,100,'All'));
14407		// external circle
14408		$this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14409		// cross lines
14410		$this->Line($x, ($y - $re), $x, ($y - $ri));
14411		$this->Line($x, ($y + $ri), $x, ($y + $re));
14412		$this->Line(($x - $re), $y, ($x - $ri), $y);
14413		$this->Line(($x + $ri), $y, ($x + $re), $y);
14414	}
14415
14416	/**
14417	 * Paints a linear colour gradient.
14418	 * @param $x (float) abscissa of the top left corner of the rectangle.
14419	 * @param $y (float) ordinate of the top left corner of the rectangle.
14420	 * @param $w (float) width of the rectangle.
14421	 * @param $h (float) height of the rectangle.
14422	 * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
14423	 * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
14424	 * @param $coords (array) 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).
14425	 * @author Andreas W\FCrmser, Nicola Asuni
14426	 * @since 3.1.000 (2008-06-09)
14427	 * @public
14428	 */
14429	public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
14430		$this->Clip($x, $y, $w, $h);
14431		$this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14432	}
14433
14434	/**
14435	 * Paints a radial colour gradient.
14436	 * @param $x (float) abscissa of the top left corner of the rectangle.
14437	 * @param $y (float) ordinate of the top left corner of the rectangle.
14438	 * @param $w (float) width of the rectangle.
14439	 * @param $h (float) height of the rectangle.
14440	 * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
14441	 * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
14442	 * @param $coords (array) 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.
14443	 * @author Andreas W\FCrmser, Nicola Asuni
14444	 * @since 3.1.000 (2008-06-09)
14445	 * @public
14446	 */
14447	public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
14448		$this->Clip($x, $y, $w, $h);
14449		$this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14450	}
14451
14452	/**
14453	 * Paints a coons patch mesh.
14454	 * @param $x (float) abscissa of the top left corner of the rectangle.
14455	 * @param $y (float) ordinate of the top left corner of the rectangle.
14456	 * @param $w (float) width of the rectangle.
14457	 * @param $h (float) height of the rectangle.
14458	 * @param $col1 (array) first color (lower left corner) (RGB components).
14459	 * @param $col2 (array) second color (lower right corner) (RGB components).
14460	 * @param $col3 (array) third color (upper right corner) (RGB components).
14461	 * @param $col4 (array) fourth color (upper left corner) (RGB components).
14462	 * @param $coords (array) <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>
14463	 * @param $coords_min (array) minimum value used by the coordinates. If a coordinate's value is smaller than this it will be cut to coords_min. default: 0
14464	 * @param $coords_max (array) maximum value used by the coordinates. If a coordinate's value is greater than this it will be cut to coords_max. default: 1
14465	 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
14466	 * @author Andreas W\FCrmser, Nicola Asuni
14467	 * @since 3.1.000 (2008-06-09)
14468	 * @public
14469	 */
14470	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) {
14471		if ($this->pdfa_mode OR ($this->state != 2)) {
14472			return;
14473		}
14474		$this->Clip($x, $y, $w, $h);
14475		$n = count($this->gradients) + 1;
14476		$this->gradients[$n] = array();
14477		$this->gradients[$n]['type'] = 6; //coons patch mesh
14478		$this->gradients[$n]['coords'] = array();
14479		$this->gradients[$n]['antialias'] = $antialias;
14480		$this->gradients[$n]['colors'] = array();
14481		$this->gradients[$n]['transparency'] = false;
14482		//check the coords array if it is the simple array or the multi patch array
14483		if (!isset($coords[0]['f'])) {
14484			//simple array -> convert to multi patch array
14485			if (!isset($col1[1])) {
14486				$col1[1] = $col1[2] = $col1[0];
14487			}
14488			if (!isset($col2[1])) {
14489				$col2[1] = $col2[2] = $col2[0];
14490			}
14491			if (!isset($col3[1])) {
14492				$col3[1] = $col3[2] = $col3[0];
14493			}
14494			if (!isset($col4[1])) {
14495				$col4[1] = $col4[2] = $col4[0];
14496			}
14497			$patch_array[0]['f'] = 0;
14498			$patch_array[0]['points'] = $coords;
14499			$patch_array[0]['colors'][0]['r'] = $col1[0];
14500			$patch_array[0]['colors'][0]['g'] = $col1[1];
14501			$patch_array[0]['colors'][0]['b'] = $col1[2];
14502			$patch_array[0]['colors'][1]['r'] = $col2[0];
14503			$patch_array[0]['colors'][1]['g'] = $col2[1];
14504			$patch_array[0]['colors'][1]['b'] = $col2[2];
14505			$patch_array[0]['colors'][2]['r'] = $col3[0];
14506			$patch_array[0]['colors'][2]['g'] = $col3[1];
14507			$patch_array[0]['colors'][2]['b'] = $col3[2];
14508			$patch_array[0]['colors'][3]['r'] = $col4[0];
14509			$patch_array[0]['colors'][3]['g'] = $col4[1];
14510			$patch_array[0]['colors'][3]['b'] = $col4[2];
14511		} else {
14512			//multi patch array
14513			$patch_array = $coords;
14514		}
14515		$bpcd = 65535; //16 bits per coordinate
14516		//build the data stream
14517		$this->gradients[$n]['stream'] = '';
14518		$count_patch = count($patch_array);
14519		for ($i=0; $i < $count_patch; ++$i) {
14520			$this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
14521			$count_points = count($patch_array[$i]['points']);
14522			for ($j=0; $j < $count_points; ++$j) {
14523				//each point as 16 bit
14524				$patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
14525				if ($patch_array[$i]['points'][$j] < 0) {
14526					$patch_array[$i]['points'][$j] = 0;
14527				}
14528				if ($patch_array[$i]['points'][$j] > $bpcd) {
14529					$patch_array[$i]['points'][$j] = $bpcd;
14530				}
14531				$this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
14532				$this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
14533			}
14534			$count_cols = count($patch_array[$i]['colors']);
14535			for ($j=0; $j < $count_cols; ++$j) {
14536				//each color component as 8 bit
14537				$this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
14538				$this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
14539				$this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
14540			}
14541		}
14542		//paint the gradient
14543		$this->_out('/Sh'.$n.' sh');
14544		//restore previous Graphic State
14545		$this->_outRestoreGraphicsState();
14546		if ($this->inxobj) {
14547			// we are inside an XObject template
14548			$this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14549		}
14550	}
14551
14552	/**
14553	 * Set a rectangular clipping area.
14554	 * @param $x (float) abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
14555	 * @param $y (float) ordinate of the top left corner of the rectangle.
14556	 * @param $w (float) width of the rectangle.
14557	 * @param $h (float) height of the rectangle.
14558	 * @author Andreas W\FCrmser, Nicola Asuni
14559	 * @since 3.1.000 (2008-06-09)
14560	 * @protected
14561	 */
14562	protected function Clip($x, $y, $w, $h) {
14563		if ($this->state != 2) {
14564			 return;
14565		}
14566		if ($this->rtl) {
14567			$x = $this->w - $x - $w;
14568		}
14569		//save current Graphic State
14570		$s = 'q';
14571		//set clipping area
14572		$s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
14573		//set up transformation matrix for gradient
14574		$s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
14575		$this->_out($s);
14576	}
14577
14578	/**
14579	 * Output gradient.
14580	 * @param $type (int) 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)
14581	 * @param $coords (array) array of coordinates.
14582	 * @param $stops (array) 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).
14583	 * @param $background (array) An array of colour components appropriate to the colour space, specifying a single background colour value.
14584	 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
14585	 * @author Nicola Asuni
14586	 * @since 3.1.000 (2008-06-09)
14587	 * @public
14588	 */
14589	public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
14590		if ($this->pdfa_mode OR ($this->state != 2)) {
14591			return;
14592		}
14593		$n = count($this->gradients) + 1;
14594		$this->gradients[$n] = array();
14595		$this->gradients[$n]['type'] = $type;
14596		$this->gradients[$n]['coords'] = $coords;
14597		$this->gradients[$n]['antialias'] = $antialias;
14598		$this->gradients[$n]['colors'] = array();
14599		$this->gradients[$n]['transparency'] = false;
14600		// color space
14601		$numcolspace = count($stops[0]['color']);
14602		$bcolor = array_values($background);
14603		switch($numcolspace) {
14604			case 5:   // SPOT
14605			case 4: { // CMYK
14606				$this->gradients[$n]['colspace'] = 'DeviceCMYK';
14607				if (!empty($background)) {
14608					$this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
14609				}
14610				break;
14611			}
14612			case 3: { // RGB
14613				$this->gradients[$n]['colspace'] = 'DeviceRGB';
14614				if (!empty($background)) {
14615					$this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
14616				}
14617				break;
14618			}
14619			case 1: { // GRAY SCALE
14620				$this->gradients[$n]['colspace'] = 'DeviceGray';
14621				if (!empty($background)) {
14622					$this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255);
14623				}
14624				break;
14625			}
14626		}
14627		$num_stops = count($stops);
14628		$last_stop_id = $num_stops - 1;
14629		foreach ($stops as $key => $stop) {
14630			$this->gradients[$n]['colors'][$key] = array();
14631			// offset represents a location along the gradient vector
14632			if (isset($stop['offset'])) {
14633				$this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
14634			} else {
14635				if ($key == 0) {
14636					$this->gradients[$n]['colors'][$key]['offset'] = 0;
14637				} elseif ($key == $last_stop_id) {
14638					$this->gradients[$n]['colors'][$key]['offset'] = 1;
14639				} else {
14640					$offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
14641					$this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
14642				}
14643			}
14644			if (isset($stop['opacity'])) {
14645				$this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
14646				if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) {
14647					$this->gradients[$n]['transparency'] = true;
14648				}
14649			} else {
14650				$this->gradients[$n]['colors'][$key]['opacity'] = 1;
14651			}
14652			// exponent for the exponential interpolation function
14653			if (isset($stop['exponent'])) {
14654				$this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
14655			} else {
14656				$this->gradients[$n]['colors'][$key]['exponent'] = 1;
14657			}
14658			// set colors
14659			$color = array_values($stop['color']);
14660			switch($numcolspace) {
14661				case 5:   // SPOT
14662				case 4: { // CMYK
14663					$this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
14664					break;
14665				}
14666				case 3: { // RGB
14667					$this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255);
14668					break;
14669				}
14670				case 1: { // GRAY SCALE
14671					$this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255);
14672					break;
14673				}
14674			}
14675		}
14676		if ($this->gradients[$n]['transparency']) {
14677			// paint luminosity gradient
14678			$this->_out('/TGS'.$n.' gs');
14679		}
14680		//paint the gradient
14681		$this->_out('/Sh'.$n.' sh');
14682		//restore previous Graphic State
14683		$this->_outRestoreGraphicsState();
14684		if ($this->inxobj) {
14685			// we are inside an XObject template
14686			$this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14687		}
14688	}
14689
14690	/**
14691	 * Output gradient shaders.
14692	 * @author Nicola Asuni
14693	 * @since 3.1.000 (2008-06-09)
14694	 * @protected
14695	 */
14696	function _putshaders() {
14697		if ($this->pdfa_mode) {
14698			return;
14699		}
14700		$idt = count($this->gradients); //index for transparency gradients
14701		foreach ($this->gradients as $id => $grad) {
14702			if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
14703				$fc = $this->_newobj();
14704				$out = '<<';
14705				$out .= ' /FunctionType 3';
14706				$out .= ' /Domain [0 1]';
14707				$functions = '';
14708				$bounds = '';
14709				$encode = '';
14710				$i = 1;
14711				$num_cols = count($grad['colors']);
14712				$lastcols = $num_cols - 1;
14713				for ($i = 1; $i < $num_cols; ++$i) {
14714					$functions .= ($fc + $i).' 0 R ';
14715					if ($i < $lastcols) {
14716						$bounds .= sprintf('%F ', $grad['colors'][$i]['offset']);
14717					}
14718					$encode .= '0 1 ';
14719				}
14720				$out .= ' /Functions ['.trim($functions).']';
14721				$out .= ' /Bounds ['.trim($bounds).']';
14722				$out .= ' /Encode ['.trim($encode).']';
14723				$out .= ' >>';
14724				$out .= "\n".'endobj';
14725				$this->_out($out);
14726				for ($i = 1; $i < $num_cols; ++$i) {
14727					$this->_newobj();
14728					$out = '<<';
14729					$out .= ' /FunctionType 2';
14730					$out .= ' /Domain [0 1]';
14731					$out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
14732					$out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
14733					$out .= ' /N '.$grad['colors'][$i]['exponent'];
14734					$out .= ' >>';
14735					$out .= "\n".'endobj';
14736					$this->_out($out);
14737				}
14738				// set transparency fuctions
14739				if ($grad['transparency']) {
14740					$ft = $this->_newobj();
14741					$out = '<<';
14742					$out .= ' /FunctionType 3';
14743					$out .= ' /Domain [0 1]';
14744					$functions = '';
14745					$i = 1;
14746					$num_cols = count($grad['colors']);
14747					for ($i = 1; $i < $num_cols; ++$i) {
14748						$functions .= ($ft + $i).' 0 R ';
14749					}
14750					$out .= ' /Functions ['.trim($functions).']';
14751					$out .= ' /Bounds ['.trim($bounds).']';
14752					$out .= ' /Encode ['.trim($encode).']';
14753					$out .= ' >>';
14754					$out .= "\n".'endobj';
14755					$this->_out($out);
14756					for ($i = 1; $i < $num_cols; ++$i) {
14757						$this->_newobj();
14758						$out = '<<';
14759						$out .= ' /FunctionType 2';
14760						$out .= ' /Domain [0 1]';
14761						$out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
14762						$out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
14763						$out .= ' /N '.$grad['colors'][$i]['exponent'];
14764						$out .= ' >>';
14765						$out .= "\n".'endobj';
14766						$this->_out($out);
14767					}
14768				}
14769			}
14770			// set shading object
14771			$this->_newobj();
14772			$out = '<< /ShadingType '.$grad['type'];
14773			if (isset($grad['colspace'])) {
14774				$out .= ' /ColorSpace /'.$grad['colspace'];
14775			} else {
14776				$out .= ' /ColorSpace /DeviceRGB';
14777			}
14778			if (isset($grad['background']) AND !empty($grad['background'])) {
14779				$out .= ' /Background ['.$grad['background'].']';
14780			}
14781			if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
14782				$out .= ' /AntiAlias true';
14783			}
14784			if ($grad['type'] == 2) {
14785				$out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
14786				$out .= ' /Domain [0 1]';
14787				$out .= ' /Function '.$fc.' 0 R';
14788				$out .= ' /Extend [true true]';
14789				$out .= ' >>';
14790			} elseif ($grad['type'] == 3) {
14791				//x0, y0, r0, x1, y1, r1
14792				//at this this time radius of inner circle is 0
14793				$out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
14794				$out .= ' /Domain [0 1]';
14795				$out .= ' /Function '.$fc.' 0 R';
14796				$out .= ' /Extend [true true]';
14797				$out .= ' >>';
14798			} elseif ($grad['type'] == 6) {
14799				$out .= ' /BitsPerCoordinate 16';
14800				$out .= ' /BitsPerComponent 8';
14801				$out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
14802				$out .= ' /BitsPerFlag 8';
14803				$stream = $this->_getrawstream($grad['stream']);
14804				$out .= ' /Length '.strlen($stream);
14805				$out .= ' >>';
14806				$out .= ' stream'."\n".$stream."\n".'endstream';
14807			}
14808			$out .= "\n".'endobj';
14809			$this->_out($out);
14810			if ($grad['transparency']) {
14811				$shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
14812				$shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
14813			}
14814			$this->gradients[$id]['id'] = $this->n;
14815			// set pattern object
14816			$this->_newobj();
14817			$out = '<< /Type /Pattern /PatternType 2';
14818			$out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
14819			$out .= ' >>';
14820			$out .= "\n".'endobj';
14821			$this->_out($out);
14822			$this->gradients[$id]['pattern'] = $this->n;
14823			// set shading and pattern for transparency mask
14824			if ($grad['transparency']) {
14825				// luminosity pattern
14826				$idgs = $id + $idt;
14827				$this->_newobj();
14828				$this->_out($shading_transparency);
14829				$this->gradients[$idgs]['id'] = $this->n;
14830				$this->_newobj();
14831				$out = '<< /Type /Pattern /PatternType 2';
14832				$out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
14833				$out .= ' >>';
14834				$out .= "\n".'endobj';
14835				$this->_out($out);
14836				$this->gradients[$idgs]['pattern'] = $this->n;
14837				// luminosity XObject
14838				$oid = $this->_newobj();
14839				$this->xobjects['LX'.$oid] = array('n' => $oid);
14840				$filter = '';
14841				$stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
14842				if ($this->compress) {
14843					$filter = ' /Filter /FlateDecode';
14844					$stream = gzcompress($stream);
14845				}
14846				$stream = $this->_getrawstream($stream);
14847				$out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
14848				$out .= ' /Length '.strlen($stream);
14849				$rect = sprintf('%F %F', $this->wPt, $this->hPt);
14850				$out .= ' /BBox [0 0 '.$rect.']';
14851				$out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
14852				$out .= ' /Resources <<';
14853				$out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
14854				$out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
14855				$out .= ' >>';
14856				$out .= ' >> ';
14857				$out .= ' stream'."\n".$stream."\n".'endstream';
14858				$out .= "\n".'endobj';
14859				$this->_out($out);
14860				// SMask
14861				$this->_newobj();
14862				$out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj';
14863				$this->_out($out);
14864				// ExtGState
14865				$this->_newobj();
14866				$out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj';
14867				$this->_out($out);
14868				$this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
14869			}
14870		}
14871	}
14872
14873	/**
14874	 * Draw the sector of a circle.
14875	 * It can be used for instance to render pie charts.
14876	 * @param $xc (float) abscissa of the center.
14877	 * @param $yc (float) ordinate of the center.
14878	 * @param $r (float) radius.
14879	 * @param $a (float) start angle (in degrees).
14880	 * @param $b (float) end angle (in degrees).
14881	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
14882	 * @param $cw: (float) indicates whether to go clockwise (default: true).
14883	 * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). Default: 90.
14884	 * @author Maxime Delorme, Nicola Asuni
14885	 * @since 3.1.000 (2008-06-09)
14886	 * @public
14887	 */
14888	public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
14889		$this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
14890	}
14891
14892	/**
14893	 * Draw the sector of an ellipse.
14894	 * It can be used for instance to render pie charts.
14895	 * @param $xc (float) abscissa of the center.
14896	 * @param $yc (float) ordinate of the center.
14897	 * @param $rx (float) the x-axis radius.
14898	 * @param $ry (float) the y-axis radius.
14899	 * @param $a (float) start angle (in degrees).
14900	 * @param $b (float) end angle (in degrees).
14901	 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
14902	 * @param $cw: (float) indicates whether to go clockwise.
14903	 * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock).
14904	 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of arc.
14905	 * @author Maxime Delorme, Nicola Asuni
14906	 * @since 3.1.000 (2008-06-09)
14907	 * @public
14908	 */
14909	public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
14910		if ($this->state != 2) {
14911			 return;
14912		}
14913		if ($this->rtl) {
14914			$xc = ($this->w - $xc);
14915		}
14916		$op = TCPDF_STATIC::getPathPaintOperator($style);
14917		if ($op == 'f') {
14918			$line_style = array();
14919		}
14920		if ($cw) {
14921			$d = $b;
14922			$b = (360 - $a + $o);
14923			$a = (360 - $d + $o);
14924		} else {
14925			$b += $o;
14926			$a += $o;
14927		}
14928		$this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
14929		$this->_out($op);
14930	}
14931
14932	/**
14933	 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
14934	 * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
14935	 * Only vector drawing is supported, not text or bitmap.
14936	 * 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).
14937	 * @param $file (string) Name of the file containing the image or a '@' character followed by the EPS/AI data string.
14938	 * @param $x (float) Abscissa of the upper-left corner.
14939	 * @param $y (float) Ordinate of the upper-left corner.
14940	 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
14941	 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
14942	 * @param $link (mixed) URL or identifier returned by AddLink().
14943	 * @param $useBoundingBox (boolean) specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
14944	 * @param $align (string) 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>
14945	 * @param $palign (string) 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>
14946	 * @param $border (mixed) 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)))
14947	 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
14948	 * @param $fixoutvals (boolean) if true remove values outside the bounding box.
14949	 * @author Valentin Schmidt, Nicola Asuni
14950	 * @since 3.1.000 (2008-06-09)
14951	 * @public
14952	 */
14953	public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
14954		if ($this->state != 2) {
14955			 return;
14956		}
14957		if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
14958			// convert EPS to raster image using GD or ImageMagick libraries
14959			return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14960		}
14961		if ($x === '') {
14962			$x = $this->x;
14963		}
14964		if ($y === '') {
14965			$y = $this->y;
14966		}
14967		// check page for no-write regions and adapt page margins if necessary
14968		list($x, $y) = $this->checkPageRegions($h, $x, $y);
14969		$k = $this->k;
14970		if ($file[0] === '@') { // image from string
14971			$data = substr($file, 1);
14972		} else { // EPS/AI file
14973			$data = TCPDF_STATIC::fileGetContents($file);
14974		}
14975		if ($data === FALSE) {
14976			$this->Error('EPS file not found: '.$file);
14977		}
14978		$regs = array();
14979		// EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
14980		preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
14981		if (count($regs) > 1) {
14982			$version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
14983			if (strpos($version_str, 'Adobe Illustrator') !== false) {
14984				$versexp = explode(' ', $version_str);
14985				$version = (float)array_pop($versexp);
14986				if ($version >= 9) {
14987					$this->Error('This version of Adobe Illustrator file is not supported: '.$file);
14988				}
14989			}
14990		}
14991		// strip binary bytes in front of PS-header
14992		$start = strpos($data, '%!PS-Adobe');
14993		if ($start > 0) {
14994			$data = substr($data, $start);
14995		}
14996		// find BoundingBox params
14997		preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
14998		if (count($regs) > 1) {
14999			list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
15000		} else {
15001			$this->Error('No BoundingBox found in EPS/AI file: '.$file);
15002		}
15003		$start = strpos($data, '%%EndSetup');
15004		if ($start === false) {
15005			$start = strpos($data, '%%EndProlog');
15006		}
15007		if ($start === false) {
15008			$start = strpos($data, '%%BoundingBox');
15009		}
15010		$data = substr($data, $start);
15011		$end = strpos($data, '%%PageTrailer');
15012		if ($end===false) {
15013			$end = strpos($data, 'showpage');
15014		}
15015		if ($end) {
15016			$data = substr($data, 0, $end);
15017		}
15018		// calculate image width and height on document
15019		if (($w <= 0) AND ($h <= 0)) {
15020			$w = ($x2 - $x1) / $k;
15021			$h = ($y2 - $y1) / $k;
15022		} elseif ($w <= 0) {
15023			$w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
15024		} elseif ($h <= 0) {
15025			$h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
15026		}
15027		// fit the image on available space
15028		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
15029		if ($this->rasterize_vector_images) {
15030			// convert EPS to raster image using GD or ImageMagick libraries
15031			return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
15032		}
15033		// set scaling factors
15034		$scale_x = $w / (($x2 - $x1) / $k);
15035		$scale_y = $h / (($y2 - $y1) / $k);
15036		// set alignment
15037		$this->img_rb_y = $y + $h;
15038		// set alignment
15039		if ($this->rtl) {
15040			if ($palign == 'L') {
15041				$ximg = $this->lMargin;
15042			} elseif ($palign == 'C') {
15043				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15044			} elseif ($palign == 'R') {
15045				$ximg = $this->w - $this->rMargin - $w;
15046			} else {
15047				$ximg = $x - $w;
15048			}
15049			$this->img_rb_x = $ximg;
15050		} else {
15051			if ($palign == 'L') {
15052				$ximg = $this->lMargin;
15053			} elseif ($palign == 'C') {
15054				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15055			} elseif ($palign == 'R') {
15056				$ximg = $this->w - $this->rMargin - $w;
15057			} else {
15058				$ximg = $x;
15059			}
15060			$this->img_rb_x = $ximg + $w;
15061		}
15062		if ($useBoundingBox) {
15063			$dx = $ximg * $k - $x1;
15064			$dy = $y * $k - $y1;
15065		} else {
15066			$dx = $ximg * $k;
15067			$dy = $y * $k;
15068		}
15069		// save the current graphic state
15070		$this->_out('q'.$this->epsmarker);
15071		// translate
15072		$this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
15073		// scale
15074		if (isset($scale_x)) {
15075			$this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
15076		}
15077		// handle pc/unix/mac line endings
15078		$lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY);
15079		$u=0;
15080		$cnt = count($lines);
15081		for ($i=0; $i < $cnt; ++$i) {
15082			$line = $lines[$i];
15083			if (($line == '') OR ($line[0] == '%')) {
15084				continue;
15085			}
15086			$len = strlen($line);
15087			// check for spot color names
15088			$color_name = '';
15089			if (strcasecmp('x', substr(trim($line), -1)) == 0) {
15090				if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) {
15091					// extract spot color name
15092					$color_name = $matches[0];
15093					// remove color name from string
15094					$line = str_replace(' '.$color_name, '', $line);
15095					// remove pharentesis from color name
15096					$color_name = substr($color_name, 1, -1);
15097				}
15098			}
15099			$chunks = explode(' ', $line);
15100			$cmd = trim(array_pop($chunks));
15101			// RGB
15102			if (($cmd == 'Xa') OR ($cmd == 'XA')) {
15103				$b = array_pop($chunks);
15104				$g = array_pop($chunks);
15105				$r = array_pop($chunks);
15106				$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!
15107				continue;
15108			}
15109			$skip = false;
15110			if ($fixoutvals) {
15111				// check for values outside the bounding box
15112				switch ($cmd) {
15113					case 'm':
15114					case 'l':
15115					case 'L': {
15116						// skip values outside bounding box
15117						foreach ($chunks as $key => $val) {
15118							if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) {
15119								$skip = true;
15120							} elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) {
15121								$skip = true;
15122							}
15123						}
15124					}
15125				}
15126			}
15127			switch ($cmd) {
15128				case 'm':
15129				case 'l':
15130				case 'v':
15131				case 'y':
15132				case 'c':
15133				case 'k':
15134				case 'K':
15135				case 'g':
15136				case 'G':
15137				case 's':
15138				case 'S':
15139				case 'J':
15140				case 'j':
15141				case 'w':
15142				case 'M':
15143				case 'd':
15144				case 'n': {
15145					if ($skip) {
15146						break;
15147					}
15148					$this->_out($line);
15149					break;
15150				}
15151				case 'x': {// custom fill color
15152					if (empty($color_name)) {
15153						// CMYK color
15154						list($col_c, $col_m, $col_y, $col_k) = $chunks;
15155						$this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
15156					} else {
15157						// Spot Color (CMYK + tint)
15158						list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15159						$this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15160						$color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15161						$this->_out($color_cmd);
15162					}
15163					break;
15164				}
15165				case 'X': { // custom stroke color
15166					if (empty($color_name)) {
15167						// CMYK color
15168						list($col_c, $col_m, $col_y, $col_k) = $chunks;
15169						$this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
15170					} else {
15171						// Spot Color (CMYK + tint)
15172						list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15173						$this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15174						$color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15175						$this->_out($color_cmd);
15176					}
15177					break;
15178				}
15179				case 'Y':
15180				case 'N':
15181				case 'V':
15182				case 'L':
15183				case 'C': {
15184					if ($skip) {
15185						break;
15186					}
15187					$line[($len - 1)] = strtolower($cmd);
15188					$this->_out($line);
15189					break;
15190				}
15191				case 'b':
15192				case 'B': {
15193					$this->_out($cmd . '*');
15194					break;
15195				}
15196				case 'f':
15197				case 'F': {
15198					if ($u > 0) {
15199						$isU = false;
15200						$max = min(($i + 5), $cnt);
15201						for ($j = ($i + 1); $j < $max; ++$j) {
15202							$isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
15203						}
15204						if ($isU) {
15205							$this->_out('f*');
15206						}
15207					} else {
15208						$this->_out('f*');
15209					}
15210					break;
15211				}
15212				case '*u': {
15213					++$u;
15214					break;
15215				}
15216				case '*U': {
15217					--$u;
15218					break;
15219				}
15220			}
15221		}
15222		// restore previous graphic state
15223		$this->_out($this->epsmarker.'Q');
15224		if (!empty($border)) {
15225			$bx = $this->x;
15226			$by = $this->y;
15227			$this->x = $ximg;
15228			if ($this->rtl) {
15229				$this->x += $w;
15230			}
15231			$this->y = $y;
15232			$this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
15233			$this->x = $bx;
15234			$this->y = $by;
15235		}
15236		if ($link) {
15237			$this->Link($ximg, $y, $w, $h, $link, 0);
15238		}
15239		// set pointer to align the next text/objects
15240		switch($align) {
15241			case 'T':{
15242				$this->y = $y;
15243				$this->x = $this->img_rb_x;
15244				break;
15245			}
15246			case 'M':{
15247				$this->y = $y + round($h/2);
15248				$this->x = $this->img_rb_x;
15249				break;
15250			}
15251			case 'B':{
15252				$this->y = $this->img_rb_y;
15253				$this->x = $this->img_rb_x;
15254				break;
15255			}
15256			case 'N':{
15257				$this->SetY($this->img_rb_y);
15258				break;
15259			}
15260			default:{
15261				break;
15262			}
15263		}
15264		$this->endlinex = $this->img_rb_x;
15265	}
15266
15267	/**
15268	 * Set document barcode.
15269	 * @param $bc (string) barcode
15270	 * @public
15271	 */
15272	public function setBarcode($bc='') {
15273		$this->barcode = $bc;
15274	}
15275
15276	/**
15277	 * Get current barcode.
15278	 * @return string
15279	 * @public
15280	 * @since 4.0.012 (2008-07-24)
15281	 */
15282	public function getBarcode() {
15283		return $this->barcode;
15284	}
15285
15286	/**
15287	 * Print a Linear Barcode.
15288	 * @param $code (string) code to print
15289	 * @param $type (string) type of barcode (see tcpdf_barcodes_1d.php for supported formats).
15290	 * @param $x (int) x position in user units (empty string = current x position)
15291	 * @param $y (int) y position in user units (empty string = current y position)
15292	 * @param $w (int) width in user units (empty string = remaining page width)
15293	 * @param $h (int) height in user units (empty string = remaining page height)
15294	 * @param $xres (float) width of the smallest bar in user units (empty string = default value = 0.4mm)
15295	 * @param $style (array) array of options:<ul>
15296	 * <li>boolean $style['border'] if true prints a border</li>
15297	 * <li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li>
15298	 * <li>int $style['hpadding'] horizontal padding in user units (set to 'auto' for automatic padding)</li>
15299	 * <li>int $style['vpadding'] vertical padding in user units (set to 'auto' for automatic padding)</li>
15300	 * <li>array $style['fgcolor'] color array for bars and text</li>
15301	 * <li>mixed $style['bgcolor'] color array for background (set to false for transparent)</li>
15302	 * <li>boolean $style['text'] if true prints text below the barcode</li>
15303	 * <li>string $style['label'] override default label</li>
15304	 * <li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li>
15305	 * <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>
15306	 * <li>string $style['position'] horizontal position of the containing barcode cell on the page: L = left margin; C = center; R = right margin.</li>
15307	 * <li>string $style['align'] horizontal position of the barcode on the containing rectangle: L = left; C = center; R = right.</li>
15308	 * <li>string $style['stretch'] if true stretch the barcode to best fit the available width, otherwise uses $xres resolution for a single bar.</li>
15309	 * <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>
15310	 * <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>
15311	 * @param $align (string) 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>
15312	 * @author Nicola Asuni
15313	 * @since 3.1.000 (2008-06-09)
15314	 * @public
15315	 */
15316	public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
15317		if (TCPDF_STATIC::empty_string(trim($code))) {
15318			return;
15319		}
15320		require_once(dirname(__FILE__).'/tcpdf_barcodes_1d.php');
15321		// save current graphic settings
15322		$gvars = $this->getGraphicVars();
15323		// create new barcode object
15324		$barcodeobj = new TCPDFBarcode($code, $type);
15325		$arrcode = $barcodeobj->getBarcodeArray();
15326		if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] <= 0)) {
15327			$this->Error('Error in 1D barcode string');
15328		}
15329		if ($arrcode['maxh'] <= 0) {
15330			$arrcode['maxh'] = 1;
15331		}
15332		// set default values
15333		if (!isset($style['position'])) {
15334			$style['position'] = '';
15335		} elseif ($style['position'] == 'S') {
15336			// keep this for backward compatibility
15337			$style['position'] = '';
15338			$style['stretch'] = true;
15339		}
15340		if (!isset($style['fitwidth'])) {
15341			if (!isset($style['stretch'])) {
15342				$style['fitwidth'] = true;
15343			} else {
15344				$style['fitwidth'] = false;
15345			}
15346		}
15347		if ($style['fitwidth']) {
15348			// disable stretch
15349			$style['stretch'] = false;
15350		}
15351		if (!isset($style['stretch'])) {
15352			if (($w === '') OR ($w <= 0)) {
15353				$style['stretch'] = false;
15354			} else {
15355				$style['stretch'] = true;
15356			}
15357		}
15358		if (!isset($style['fgcolor'])) {
15359			$style['fgcolor'] = array(0,0,0); // default black
15360		}
15361		if (!isset($style['bgcolor'])) {
15362			$style['bgcolor'] = false; // default transparent
15363		}
15364		if (!isset($style['border'])) {
15365			$style['border'] = false;
15366		}
15367		$fontsize = 0;
15368		if (!isset($style['text'])) {
15369			$style['text'] = false;
15370		}
15371		if ($style['text'] AND isset($style['font'])) {
15372			if (isset($style['fontsize'])) {
15373				$fontsize = $style['fontsize'];
15374			}
15375			$this->SetFont($style['font'], '', $fontsize);
15376		}
15377		if (!isset($style['stretchtext'])) {
15378			$style['stretchtext'] = 4;
15379		}
15380		if ($x === '') {
15381			$x = $this->x;
15382		}
15383		if ($y === '') {
15384			$y = $this->y;
15385		}
15386		// check page for no-write regions and adapt page margins if necessary
15387		list($x, $y) = $this->checkPageRegions($h, $x, $y);
15388		if (($w === '') OR ($w <= 0)) {
15389			if ($this->rtl) {
15390				$w = $x - $this->lMargin;
15391			} else {
15392				$w = $this->w - $this->rMargin - $x;
15393			}
15394		}
15395		// padding
15396		if (!isset($style['padding'])) {
15397			$padding = 0;
15398		} elseif ($style['padding'] === 'auto') {
15399			$padding = 10 * ($w / ($arrcode['maxw'] + 20));
15400		} else {
15401			$padding = floatval($style['padding']);
15402		}
15403		// horizontal padding
15404		if (!isset($style['hpadding'])) {
15405			$hpadding = $padding;
15406		} elseif ($style['hpadding'] === 'auto') {
15407			$hpadding = 10 * ($w / ($arrcode['maxw'] + 20));
15408		} else {
15409			$hpadding = floatval($style['hpadding']);
15410		}
15411		// vertical padding
15412		if (!isset($style['vpadding'])) {
15413			$vpadding = $padding;
15414		} elseif ($style['vpadding'] === 'auto') {
15415			$vpadding = ($hpadding / 2);
15416		} else {
15417			$vpadding = floatval($style['vpadding']);
15418		}
15419		// calculate xres (single bar width)
15420		$max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
15421		if ($style['stretch']) {
15422			$xres = $max_xres;
15423		} else {
15424			if (TCPDF_STATIC::empty_string($xres)) {
15425				$xres = (0.141 * $this->k); // default bar width = 0.4 mm
15426			}
15427			if ($xres > $max_xres) {
15428				// correct xres to fit on $w
15429				$xres = $max_xres;
15430			}
15431			if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
15432				OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
15433				$hpadding = 10 * $xres;
15434				if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
15435					$vpadding = ($hpadding / 2);
15436				}
15437			}
15438		}
15439		if ($style['fitwidth']) {
15440			$wold = $w;
15441			$w = (($arrcode['maxw'] * $xres) + (2 * $hpadding));
15442			if (isset($style['cellfitalign'])) {
15443				switch ($style['cellfitalign']) {
15444					case 'L': {
15445						if ($this->rtl) {
15446							$x -= ($wold - $w);
15447						}
15448						break;
15449					}
15450					case 'R': {
15451						if (!$this->rtl) {
15452							$x += ($wold - $w);
15453						}
15454						break;
15455					}
15456					case 'C': {
15457						if ($this->rtl) {
15458							$x -= (($wold - $w) / 2);
15459						} else {
15460							$x += (($wold - $w) / 2);
15461						}
15462						break;
15463					}
15464					default : {
15465						break;
15466					}
15467				}
15468			}
15469		}
15470		$text_height = $this->getCellHeight($fontsize / $this->k);
15471		// height
15472		if (($h === '') OR ($h <= 0)) {
15473			// set default height
15474			$h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height;
15475		}
15476		$barh = $h - $text_height - (2 * $vpadding);
15477		if ($barh <=0) {
15478			// try to reduce font or padding to fit barcode on available height
15479			if ($text_height > $h) {
15480				$fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio));
15481				$text_height = $this->getCellHeight($fontsize / $this->k);
15482				$this->SetFont($style['font'], '', $fontsize);
15483			}
15484			if ($vpadding > 0) {
15485				$vpadding = (($h - $text_height) / 4);
15486			}
15487			$barh = $h - $text_height - (2 * $vpadding);
15488		}
15489		// fit the barcode on available space
15490		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15491		// set alignment
15492		$this->img_rb_y = $y + $h;
15493		// set alignment
15494		if ($this->rtl) {
15495			if ($style['position'] == 'L') {
15496				$xpos = $this->lMargin;
15497			} elseif ($style['position'] == 'C') {
15498				$xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15499			} elseif ($style['position'] == 'R') {
15500				$xpos = $this->w - $this->rMargin - $w;
15501			} else {
15502				$xpos = $x - $w;
15503			}
15504			$this->img_rb_x = $xpos;
15505		} else {
15506			if ($style['position'] == 'L') {
15507				$xpos = $this->lMargin;
15508			} elseif ($style['position'] == 'C') {
15509				$xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15510			} elseif ($style['position'] == 'R') {
15511				$xpos = $this->w - $this->rMargin - $w;
15512			} else {
15513				$xpos = $x;
15514			}
15515			$this->img_rb_x = $xpos + $w;
15516		}
15517		$xpos_rect = $xpos;
15518		if (!isset($style['align'])) {
15519			$style['align'] = 'C';
15520		}
15521		switch ($style['align']) {
15522			case 'L': {
15523				$xpos = $xpos_rect + $hpadding;
15524				break;
15525			}
15526			case 'R': {
15527				$xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding;
15528				break;
15529			}
15530			case 'C':
15531			default : {
15532				$xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2);
15533				break;
15534			}
15535		}
15536		$xpos_text = $xpos;
15537		// barcode is always printed in LTR direction
15538		$tempRTL = $this->rtl;
15539		$this->rtl = false;
15540		// print background color
15541		if ($style['bgcolor']) {
15542			$this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15543		} elseif ($style['border']) {
15544			$this->Rect($xpos_rect, $y, $w, $h, 'D');
15545		}
15546		// set foreground color
15547		$this->SetDrawColorArray($style['fgcolor']);
15548		$this->SetTextColorArray($style['fgcolor']);
15549		// print bars
15550		foreach ($arrcode['bcode'] as $k => $v) {
15551			$bw = ($v['w'] * $xres);
15552			if ($v['t']) {
15553				// draw a vertical bar
15554				$ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']);
15555				$this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
15556			}
15557			$xpos += $bw;
15558		}
15559		// print text
15560		if ($style['text']) {
15561			if (isset($style['label']) AND !TCPDF_STATIC::empty_string($style['label'])) {
15562				$label = $style['label'];
15563			} else {
15564				$label = $code;
15565			}
15566			$txtwidth = ($arrcode['maxw'] * $xres);
15567			if ($this->GetStringWidth($label) > $txtwidth) {
15568				$style['stretchtext'] = 2;
15569			}
15570			// print text
15571			$this->x = $xpos_text;
15572			$this->y = $y + $vpadding + $barh;
15573			$cellpadding = $this->cell_padding;
15574			$this->SetCellPadding(0);
15575			$this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
15576			$this->cell_padding = $cellpadding;
15577		}
15578		// restore original direction
15579		$this->rtl = $tempRTL;
15580		// restore previous settings
15581		$this->setGraphicVars($gvars);
15582		// set pointer to align the next text/objects
15583		switch($align) {
15584			case 'T':{
15585				$this->y = $y;
15586				$this->x = $this->img_rb_x;
15587				break;
15588			}
15589			case 'M':{
15590				$this->y = $y + round($h / 2);
15591				$this->x = $this->img_rb_x;
15592				break;
15593			}
15594			case 'B':{
15595				$this->y = $this->img_rb_y;
15596				$this->x = $this->img_rb_x;
15597				break;
15598			}
15599			case 'N':{
15600				$this->SetY($this->img_rb_y);
15601				break;
15602			}
15603			default:{
15604				break;
15605			}
15606		}
15607		$this->endlinex = $this->img_rb_x;
15608	}
15609
15610	/**
15611	 * Print 2D Barcode.
15612	 * @param $code (string) code to print
15613	 * @param $type (string) type of barcode (see tcpdf_barcodes_2d.php for supported formats).
15614	 * @param $x (int) x position in user units
15615	 * @param $y (int) y position in user units
15616	 * @param $w (int) width in user units
15617	 * @param $h (int) height in user units
15618	 * @param $style (array) array of options:<ul>
15619	 * <li>boolean $style['border'] if true prints a border around the barcode</li>
15620	 * <li>int $style['padding'] padding to leave around the barcode in barcode units (set to 'auto' for automatic padding)</li>
15621	 * <li>int $style['hpadding'] horizontal padding in barcode units (set to 'auto' for automatic padding)</li>
15622	 * <li>int $style['vpadding'] vertical padding in barcode units (set to 'auto' for automatic padding)</li>
15623	 * <li>int $style['module_width'] width of a single module in points</li>
15624	 * <li>int $style['module_height'] height of a single module in points</li>
15625	 * <li>array $style['fgcolor'] color array for bars and text</li>
15626	 * <li>mixed $style['bgcolor'] color array for background or false for transparent</li>
15627	 * <li>string $style['position'] barcode position on the page: L = left margin; C = center; R = right margin; S = stretch</li><li>$style['module_width'] width of a single module in points</li>
15628	 * <li>$style['module_height'] height of a single module in points</li></ul>
15629	 * @param $align (string) 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 $distort (boolean) 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='', $y='', $w='', $h='', $style='', $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 (($arrcode === false) OR 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 ($x === '') {
15687			$x = $this->x;
15688		}
15689		if ($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 dimesions
15724		if ($w > $maxw) {
15725			$w = $maxw;
15726		}
15727		if ($h > $maxh) {
15728			$h = $maxh;
15729		}
15730		// set dimensions
15731		if ((($w === '') OR ($w <= 0)) AND (($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 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 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 $html (string) htmlcode to fix
15942	 * @param $default_css (string) CSS commands to add
15943	 * @param $tagvs (array) parameters for setHtmlVSpace method
15944	 * @param $tidy_options (array) 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='', $tidy_options='') {
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 $width (string) 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 $style (string) 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 $cssborder (string) 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 $csspadding (string) padding properties
16064	 * @param $width (float) 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 $cssmargin (string) margin properties
16118	 * @param $width (float) 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 $cssbspace (string) border-spacing CSS properties
16172	 * @param $width (float) 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 $spacing (string) letter-spacing value
16206	 * @param $parent (float) 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 $stretch (string) stretch mode
16237	 * @param $parent (float) 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 $val (string) String containing font size value and unit.
16308	 * @param $refsize (float) Reference font size in points.
16309	 * @param $parent_size (float) Parent font size in points.
16310	 * @param $defaultunit (string) 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 $html (string) 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>/isU', $html, $matches) > 0) {
16374			if (isset($matches[1][0])) {
16375				$css = array_merge($css, unserialize($this->unhtmlentities($matches[1][0])));
16376			}
16377			$html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
16378		}
16379		// extract external CSS files
16380		$matches = array();
16381		if (preg_match_all('/<link([^\>]*)>/isU', $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 = TCPDF_STATIC::fileGetContents(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>/isU', $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(serialize($css)).'</cssarray>';
16418		// remove head and style blocks
16419		$html = preg_replace('/<head([^\>]*)>(.*?)<\/head>/siU', '', $html);
16420		$html = preg_replace('/<style([^\>]*)>([^\<]*)<\/style>/isU', '', $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'] .= $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 (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
16615						if (TCPDF_STATIC::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
16616							$dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
16617						}
16618						for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
16619							$dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
16620						}
16621						if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
16622							$dom[($dom[$key]['parent'])]['attribute'] = array();
16623						}
16624						// header elements must be always contained in a single page
16625						$dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
16626					}
16627					if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC::empty_string($dom[($dom[$key]['parent'])]['thead']))) {
16628						// remove the nobr attributes from the table header
16629						$dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
16630						$dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
16631					}
16632				} else {
16633					// *** opening or self-closing html tag
16634					$dom[$key]['opening'] = true;
16635					$dom[$key]['parent'] = end($level);
16636					if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
16637						// self-closing tag
16638						$dom[$key]['self'] = true;
16639					} else {
16640						// opening tag
16641						array_push($level, $key);
16642						$dom[$key]['self'] = false;
16643					}
16644					// copy some values from parent
16645					$parentkey = 0;
16646					if ($key > 0) {
16647						$parentkey = $dom[$key]['parent'];
16648						$dom[$key]['hide'] = $dom[$parentkey]['hide'];
16649						$dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
16650						$dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
16651						$dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
16652						$dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
16653						$dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
16654						$dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
16655						$dom[$key]['fill'] = $dom[$parentkey]['fill'];
16656						$dom[$key]['clip'] = $dom[$parentkey]['clip'];
16657						$dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16658						$dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
16659						$dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
16660						$dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
16661						$dom[$key]['align'] = $dom[$parentkey]['align'];
16662						$dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16663						$dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16664						$dom[$key]['text-transform'] = $dom[$parentkey]['text-transform'];
16665						$dom[$key]['border'] = array();
16666						$dom[$key]['dir'] = $dom[$parentkey]['dir'];
16667					}
16668					// get attributes
16669					preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER);
16670					$dom[$key]['attribute'] = array(); // reset attribute array
16671					while (list($id, $name) = each($attr_array[1])) {
16672						$dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
16673					}
16674					if (!empty($css)) {
16675						// merge CSS style to current style
16676						list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC::getCSSdataArray($dom, $key, $css);
16677						$dom[$key]['attribute']['style'] = TCPDF_STATIC::getTagStyleFromCSSarray($dom[$key]['cssdata']);
16678					}
16679					// split style attributes
16680					if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
16681						// get style attributes
16682						preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
16683						$dom[$key]['style'] = array(); // reset style attribute array
16684						while (list($id, $name) = each($style_array[1])) {
16685							// in case of duplicate attribute the last replace the previous
16686							$dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
16687						}
16688						// --- get some style attributes ---
16689						// text direction
16690						if (isset($dom[$key]['style']['direction'])) {
16691							$dom[$key]['dir'] = $dom[$key]['style']['direction'];
16692						}
16693						// display
16694						if (isset($dom[$key]['style']['display'])) {
16695							$dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
16696						}
16697						// font family
16698						if (isset($dom[$key]['style']['font-family'])) {
16699							$dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
16700						}
16701						// list-style-type
16702						if (isset($dom[$key]['style']['list-style-type'])) {
16703							$dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
16704							if ($dom[$key]['listtype'] == 'inherit') {
16705								$dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16706							}
16707						}
16708						// text-indent
16709						if (isset($dom[$key]['style']['text-indent'])) {
16710							$dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
16711							if ($dom[$key]['text-indent'] == 'inherit') {
16712								$dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16713							}
16714						}
16715						// text-transform
16716						if (isset($dom[$key]['style']['text-transform'])) {
16717							$dom[$key]['text-transform'] = $dom[$key]['style']['text-transform'];
16718						}
16719						// font size
16720						if (isset($dom[$key]['style']['font-size'])) {
16721							$fsize = trim($dom[$key]['style']['font-size']);
16722							$dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt');
16723						}
16724						// font-stretch
16725						if (isset($dom[$key]['style']['font-stretch'])) {
16726							$dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
16727						}
16728						// letter-spacing
16729						if (isset($dom[$key]['style']['letter-spacing'])) {
16730							$dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
16731						}
16732						// line-height (internally is the cell height ratio)
16733						if (isset($dom[$key]['style']['line-height'])) {
16734							$lineheight = trim($dom[$key]['style']['line-height']);
16735							switch ($lineheight) {
16736								// A normal line height. This is default
16737								case 'normal': {
16738									$dom[$key]['line-height'] = $dom[0]['line-height'];
16739									break;
16740								}
16741								case 'inherit': {
16742									$dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16743								}
16744								default: {
16745									if (is_numeric($lineheight)) {
16746										// convert to percentage of font height
16747										$lineheight = ($lineheight * 100).'%';
16748									}
16749									$dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
16750									if (substr($lineheight, -1) !== '%') {
16751										if ($dom[$key]['fontsize'] <= 0) {
16752											$dom[$key]['line-height'] = 1;
16753										} else {
16754											$dom[$key]['line-height'] = (($dom[$key]['line-height'] - $this->cell_padding['T'] - $this->cell_padding['B']) / $dom[$key]['fontsize']);
16755										}
16756									}
16757								}
16758							}
16759						}
16760						// font style
16761						if (isset($dom[$key]['style']['font-weight'])) {
16762							if (strtolower($dom[$key]['style']['font-weight'][0]) == 'n') {
16763								if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
16764									$dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
16765								}
16766							} elseif (strtolower($dom[$key]['style']['font-weight'][0]) == 'b') {
16767								$dom[$key]['fontstyle'] .= 'B';
16768							}
16769						}
16770						if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style'][0]) == 'i')) {
16771							$dom[$key]['fontstyle'] .= 'I';
16772						}
16773						// font color
16774						if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['color']))) {
16775							$dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors);
16776						} elseif ($dom[$key]['value'] == 'a') {
16777							$dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16778						}
16779						// background color
16780						if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['background-color']))) {
16781							$dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors);
16782						}
16783						// text-decoration
16784						if (isset($dom[$key]['style']['text-decoration'])) {
16785							$decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
16786							foreach ($decors as $dec) {
16787								$dec = trim($dec);
16788								if (!TCPDF_STATIC::empty_string($dec)) {
16789									if ($dec[0] == 'u') {
16790										// underline
16791										$dom[$key]['fontstyle'] .= 'U';
16792									} elseif ($dec[0] == 'l') {
16793										// line-through
16794										$dom[$key]['fontstyle'] .= 'D';
16795									} elseif ($dec[0] == 'o') {
16796										// overline
16797										$dom[$key]['fontstyle'] .= 'O';
16798									}
16799								}
16800							}
16801						} elseif ($dom[$key]['value'] == 'a') {
16802							$dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16803						}
16804						// check for width attribute
16805						if (isset($dom[$key]['style']['width'])) {
16806							$dom[$key]['width'] = $dom[$key]['style']['width'];
16807						}
16808						// check for height attribute
16809						if (isset($dom[$key]['style']['height'])) {
16810							$dom[$key]['height'] = $dom[$key]['style']['height'];
16811						}
16812						// check for text alignment
16813						if (isset($dom[$key]['style']['text-align'])) {
16814							$dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align'][0]);
16815						}
16816						// check for CSS border properties
16817						if (isset($dom[$key]['style']['border'])) {
16818							$borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
16819							if (!empty($borderstyle)) {
16820								$dom[$key]['border']['LTRB'] = $borderstyle;
16821							}
16822						}
16823						if (isset($dom[$key]['style']['border-color'])) {
16824							$brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
16825							if (isset($brd_colors[3])) {
16826								$dom[$key]['border']['L']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[3], $this->spot_colors);
16827							}
16828							if (isset($brd_colors[1])) {
16829								$dom[$key]['border']['R']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[1], $this->spot_colors);
16830							}
16831							if (isset($brd_colors[0])) {
16832								$dom[$key]['border']['T']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[0], $this->spot_colors);
16833							}
16834							if (isset($brd_colors[2])) {
16835								$dom[$key]['border']['B']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[2], $this->spot_colors);
16836							}
16837						}
16838						if (isset($dom[$key]['style']['border-width'])) {
16839							$brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
16840							if (isset($brd_widths[3])) {
16841								$dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
16842							}
16843							if (isset($brd_widths[1])) {
16844								$dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
16845							}
16846							if (isset($brd_widths[0])) {
16847								$dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
16848							}
16849							if (isset($brd_widths[2])) {
16850								$dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
16851							}
16852						}
16853						if (isset($dom[$key]['style']['border-style'])) {
16854							$brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
16855							if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) {
16856								$dom[$key]['border']['L']['cap'] = 'square';
16857								$dom[$key]['border']['L']['join'] = 'miter';
16858								$dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
16859								if ($dom[$key]['border']['L']['dash'] < 0) {
16860									$dom[$key]['border']['L'] = array();
16861								}
16862							}
16863							if (isset($brd_styles[1])) {
16864								$dom[$key]['border']['R']['cap'] = 'square';
16865								$dom[$key]['border']['R']['join'] = 'miter';
16866								$dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
16867								if ($dom[$key]['border']['R']['dash'] < 0) {
16868									$dom[$key]['border']['R'] = array();
16869								}
16870							}
16871							if (isset($brd_styles[0])) {
16872								$dom[$key]['border']['T']['cap'] = 'square';
16873								$dom[$key]['border']['T']['join'] = 'miter';
16874								$dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
16875								if ($dom[$key]['border']['T']['dash'] < 0) {
16876									$dom[$key]['border']['T'] = array();
16877								}
16878							}
16879							if (isset($brd_styles[2])) {
16880								$dom[$key]['border']['B']['cap'] = 'square';
16881								$dom[$key]['border']['B']['join'] = 'miter';
16882								$dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
16883								if ($dom[$key]['border']['B']['dash'] < 0) {
16884									$dom[$key]['border']['B'] = array();
16885								}
16886							}
16887						}
16888						$cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
16889						foreach ($cellside as $bsk => $bsv) {
16890							if (isset($dom[$key]['style']['border-'.$bsv])) {
16891								$borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
16892								if (!empty($borderstyle)) {
16893									$dom[$key]['border'][$bsk] = $borderstyle;
16894								}
16895							}
16896							if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
16897								$dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors);
16898							}
16899							if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
16900								$dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
16901							}
16902							if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
16903								$dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
16904								if ($dom[$key]['border'][$bsk]['dash'] < 0) {
16905									$dom[$key]['border'][$bsk] = array();
16906								}
16907							}
16908						}
16909						// check for CSS padding properties
16910						if (isset($dom[$key]['style']['padding'])) {
16911							$dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
16912						} else {
16913							$dom[$key]['padding'] = $this->cell_padding;
16914						}
16915						foreach ($cellside as $psk => $psv) {
16916							if (isset($dom[$key]['style']['padding-'.$psv])) {
16917								$dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
16918							}
16919						}
16920						// check for CSS margin properties
16921						if (isset($dom[$key]['style']['margin'])) {
16922							$dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
16923						} else {
16924							$dom[$key]['margin'] = $this->cell_margin;
16925						}
16926						foreach ($cellside as $psk => $psv) {
16927							if (isset($dom[$key]['style']['margin-'.$psv])) {
16928								$dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
16929							}
16930						}
16931						// check for CSS border-spacing properties
16932						if (isset($dom[$key]['style']['border-spacing'])) {
16933							$dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
16934						}
16935						// page-break-inside
16936						if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
16937							$dom[$key]['attribute']['nobr'] = 'true';
16938						}
16939						// page-break-before
16940						if (isset($dom[$key]['style']['page-break-before'])) {
16941							if ($dom[$key]['style']['page-break-before'] == 'always') {
16942								$dom[$key]['attribute']['pagebreak'] = 'true';
16943							} elseif ($dom[$key]['style']['page-break-before'] == 'left') {
16944								$dom[$key]['attribute']['pagebreak'] = 'left';
16945							} elseif ($dom[$key]['style']['page-break-before'] == 'right') {
16946								$dom[$key]['attribute']['pagebreak'] = 'right';
16947							}
16948						}
16949						// page-break-after
16950						if (isset($dom[$key]['style']['page-break-after'])) {
16951							if ($dom[$key]['style']['page-break-after'] == 'always') {
16952								$dom[$key]['attribute']['pagebreakafter'] = 'true';
16953							} elseif ($dom[$key]['style']['page-break-after'] == 'left') {
16954								$dom[$key]['attribute']['pagebreakafter'] = 'left';
16955							} elseif ($dom[$key]['style']['page-break-after'] == 'right') {
16956								$dom[$key]['attribute']['pagebreakafter'] = 'right';
16957							}
16958						}
16959					}
16960					if (isset($dom[$key]['attribute']['display'])) {
16961						$dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
16962					}
16963					if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
16964						$borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
16965						if (!empty($borderstyle)) {
16966							$dom[$key]['border']['LTRB'] = $borderstyle;
16967						}
16968					}
16969					// check for font tag
16970					if ($dom[$key]['value'] == 'font') {
16971						// font family
16972						if (isset($dom[$key]['attribute']['face'])) {
16973							$dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
16974						}
16975						// font size
16976						if (isset($dom[$key]['attribute']['size'])) {
16977							if ($key > 0) {
16978								if ($dom[$key]['attribute']['size'][0] == '+') {
16979									$dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
16980								} elseif ($dom[$key]['attribute']['size'][0] == '-') {
16981									$dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
16982								} else {
16983									$dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16984								}
16985							} else {
16986								$dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16987							}
16988						}
16989					}
16990					// force natural alignment for lists
16991					if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
16992						AND (!isset($dom[$key]['align']) OR TCPDF_STATIC::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
16993						if ($this->rtl) {
16994							$dom[$key]['align'] = 'R';
16995						} else {
16996							$dom[$key]['align'] = 'L';
16997						}
16998					}
16999					if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
17000						if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
17001							$dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
17002						}
17003					}
17004					if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
17005						$dom[$key]['fontstyle'] .= 'B';
17006					}
17007					if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
17008						$dom[$key]['fontstyle'] .= 'I';
17009					}
17010					if ($dom[$key]['value'] == 'u') {
17011						$dom[$key]['fontstyle'] .= 'U';
17012					}
17013					if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
17014						$dom[$key]['fontstyle'] .= 'D';
17015					}
17016					if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
17017						$dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
17018					}
17019					if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
17020						$dom[$key]['fontname'] = $this->default_monospaced_font;
17021					}
17022					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)) {
17023						// headings h1, h2, h3, h4, h5, h6
17024						if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
17025							$headsize = (4 - intval($dom[$key]['value']{1})) * 2;
17026							$dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
17027						}
17028						if (!isset($dom[$key]['style']['font-weight'])) {
17029							$dom[$key]['fontstyle'] .= 'B';
17030						}
17031					}
17032					if (($dom[$key]['value'] == 'table')) {
17033						$dom[$key]['rows'] = 0; // number of rows
17034						$dom[$key]['trids'] = array(); // IDs of TR elements
17035						$dom[$key]['thead'] = ''; // table header rows
17036					}
17037					if (($dom[$key]['value'] == 'tr')) {
17038						$dom[$key]['cols'] = 0;
17039						if ($thead) {
17040							$dom[$key]['thead'] = true;
17041							// rows on thead block are printed as a separate table
17042						} else {
17043							$dom[$key]['thead'] = false;
17044							// store the number of rows on table element
17045							++$dom[($dom[$key]['parent'])]['rows'];
17046							// store the TR elements IDs on table element
17047							array_push($dom[($dom[$key]['parent'])]['trids'], $key);
17048						}
17049					}
17050					if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
17051						if (isset($dom[$key]['attribute']['colspan'])) {
17052							$colspan = intval($dom[$key]['attribute']['colspan']);
17053						} else {
17054							$colspan = 1;
17055						}
17056						$dom[$key]['attribute']['colspan'] = $colspan;
17057						$dom[($dom[$key]['parent'])]['cols'] += $colspan;
17058					}
17059					// text direction
17060					if (isset($dom[$key]['attribute']['dir'])) {
17061						$dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
17062					}
17063					// set foreground color attribute
17064					if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['color']))) {
17065						$dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors);
17066					} elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
17067						$dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
17068					}
17069					// set background color attribute
17070					if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['bgcolor']))) {
17071						$dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors);
17072					}
17073					// set stroke color attribute
17074					if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['strokecolor']))) {
17075						$dom[$key]['strokecolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors);
17076					}
17077					// check for width attribute
17078					if (isset($dom[$key]['attribute']['width'])) {
17079						$dom[$key]['width'] = $dom[$key]['attribute']['width'];
17080					}
17081					// check for height attribute
17082					if (isset($dom[$key]['attribute']['height'])) {
17083						$dom[$key]['height'] = $dom[$key]['attribute']['height'];
17084					}
17085					// check for text alignment
17086					if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
17087						$dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align'][0]);
17088					}
17089					// check for text rendering mode (the following attributes do not exist in HTML)
17090					if (isset($dom[$key]['attribute']['stroke'])) {
17091						// font stroke width
17092						$dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
17093					}
17094					if (isset($dom[$key]['attribute']['fill'])) {
17095						// font fill
17096						if ($dom[$key]['attribute']['fill'] == 'true') {
17097							$dom[$key]['fill'] = true;
17098						} else {
17099							$dom[$key]['fill'] = false;
17100						}
17101					}
17102					if (isset($dom[$key]['attribute']['clip'])) {
17103						// clipping mode
17104						if ($dom[$key]['attribute']['clip'] == 'true') {
17105							$dom[$key]['clip'] = true;
17106						} else {
17107							$dom[$key]['clip'] = false;
17108						}
17109					}
17110				} // end opening tag
17111			} else {
17112				// text
17113				$dom[$key]['tag'] = false;
17114				$dom[$key]['block'] = false;
17115				$dom[$key]['parent'] = end($level);
17116				$dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
17117				if (!empty($dom[$dom[$key]['parent']]['text-transform'])) {
17118					// text-transform for unicode requires mb_convert_case (Multibyte String Functions)
17119					if (function_exists('mb_convert_case')) {
17120						$ttm = array('capitalize' => MB_CASE_TITLE, 'uppercase' => MB_CASE_UPPER, 'lowercase' => MB_CASE_LOWER);
17121						if (isset($ttm[$dom[$dom[$key]['parent']]['text-transform']])) {
17122							$element = mb_convert_case($element, $ttm[$dom[$dom[$key]['parent']]['text-transform']], $this->encoding);
17123						}
17124					} elseif (!$this->isunicode) {
17125						switch ($dom[$dom[$key]['parent']]['text-transform']) {
17126							case 'capitalize': {
17127								$element = ucwords(strtolower($element));
17128								break;
17129							}
17130							case 'uppercase': {
17131								$element = strtoupper($element);
17132								break;
17133							}
17134							case 'lowercase': {
17135								$element = strtolower($element);
17136								break;
17137							}
17138						}
17139					}
17140				}
17141				$dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
17142			}
17143			++$elkey;
17144			++$key;
17145		}
17146		return $dom;
17147	}
17148
17149	/**
17150	 * Returns the string used to find spaces
17151	 * @return string
17152	 * @protected
17153	 * @author Nicola Asuni
17154	 * @since 4.8.024 (2010-01-15)
17155	 */
17156	protected function getSpaceString() {
17157		$spacestr = chr(32);
17158		if ($this->isUnicodeFont()) {
17159			$spacestr = chr(0).chr(32);
17160		}
17161		return $spacestr;
17162	}
17163
17164	/**
17165	 * Serialize an array of parameters to be used with TCPDF tag in HTML code.
17166	 * @param $pararray (array) parameters array
17167	 * @return sting containing serialized data
17168	 * @since 4.9.006 (2010-04-02)
17169	 * @public
17170	 * @deprecated
17171	 */
17172	public function serializeTCPDFtagParameters($pararray) {
17173		return TCPDF_STATIC::serializeTCPDFtagParameters($pararray);
17174	}
17175
17176	/**
17177	 * Prints a cell (rectangular area) with optional borders, background color and html text string.
17178	 * 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 />
17179	 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
17180	 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
17181	 * 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
17182	 * NOTE: all the HTML attributes must be enclosed in double-quote.
17183	 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
17184	 * @param $h (float) Cell minimum height. The cell extends automatically if needed.
17185	 * @param $x (float) upper-left corner X coordinate
17186	 * @param $y (float) upper-left corner Y coordinate
17187	 * @param $html (string) html text to print. Default value: empty string.
17188	 * @param $border (mixed) 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)))
17189	 * @param $ln (int) 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>
17190Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
17191	 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
17192	 * @param $reseth (boolean) if true reset the last cell height (default true).
17193	 * @param $align (string) 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>
17194	 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
17195	 * @see Multicell(), writeHTML()
17196	 * @public
17197	 */
17198	public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
17199		return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
17200	}
17201
17202	/**
17203	 * Allows to preserve some HTML formatting (limited support).<br />
17204	 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
17205	 * 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
17206	 * NOTE: all the HTML attributes must be enclosed in double-quote.
17207	 * @param $html (string) text to display
17208	 * @param $ln (boolean) if true add a new line after text (default = true)
17209	 * @param $fill (boolean) Indicates if the background must be painted (true) or transparent (false).
17210	 * @param $reseth (boolean) if true reset the last cell height (default false).
17211	 * @param $cell (boolean) if true add the current left (or right for RTL) padding to each Write (default false).
17212	 * @param $align (string) 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>
17213	 * @public
17214	 */
17215	public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
17216		$gvars = $this->getGraphicVars();
17217		// store current values
17218		$prev_cell_margin = $this->cell_margin;
17219		$prev_cell_padding = $this->cell_padding;
17220		$prevPage = $this->page;
17221		$prevlMargin = $this->lMargin;
17222		$prevrMargin = $this->rMargin;
17223		$curfontname = $this->FontFamily;
17224		$curfontstyle = $this->FontStyle;
17225		$curfontsize = $this->FontSizePt;
17226		$curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
17227		$curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
17228		$curfontstretcing = $this->font_stretching;
17229		$curfonttracking = $this->font_spacing;
17230		$this->newline = true;
17231		$newline = true;
17232		$startlinepage = $this->page;
17233		$minstartliney = $this->y;
17234		$maxbottomliney = 0;
17235		$startlinex = $this->x;
17236		$startliney = $this->y;
17237		$yshift = 0;
17238		$loop = 0;
17239		$curpos = 0;
17240		$this_method_vars = array();
17241		$undo = false;
17242		$fontaligned = false;
17243		$reverse_dir = false; // true when the text direction is reversed
17244		$this->premode = false;
17245		if ($this->inxobj) {
17246			// we are inside an XObject template
17247			$pask = count($this->xobjects[$this->xobjid]['annotations']);
17248		} elseif (isset($this->PageAnnots[$this->page])) {
17249			$pask = count($this->PageAnnots[$this->page]);
17250		} else {
17251			$pask = 0;
17252		}
17253		if ($this->inxobj) {
17254			// we are inside an XObject template
17255			$startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17256		} elseif (!$this->InFooter) {
17257			if (isset($this->footerlen[$this->page])) {
17258				$this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17259			} else {
17260				$this->footerpos[$this->page] = $this->pagelen[$this->page];
17261			}
17262			$startlinepos = $this->footerpos[$this->page];
17263		} else {
17264			// we are inside the footer
17265			$startlinepos = $this->pagelen[$this->page];
17266		}
17267		$lalign = $align;
17268		$plalign = $align;
17269		if ($this->rtl) {
17270			$w = $this->x - $this->lMargin;
17271		} else {
17272			$w = $this->w - $this->rMargin - $this->x;
17273		}
17274		$w -= ($this->cell_padding['L'] + $this->cell_padding['R']);
17275		if ($cell) {
17276			if ($this->rtl) {
17277				$this->x -= $this->cell_padding['R'];
17278				$this->lMargin += $this->cell_padding['R'];
17279			} else {
17280				$this->x += $this->cell_padding['L'];
17281				$this->rMargin += $this->cell_padding['L'];
17282			}
17283		}
17284		if ($this->customlistindent >= 0) {
17285			$this->listindent = $this->customlistindent;
17286		} else {
17287			$this->listindent = $this->GetStringWidth('000000');
17288		}
17289		$this->listindentlevel = 0;
17290		// save previous states
17291		$prev_cell_height_ratio = $this->cell_height_ratio;
17292		$prev_listnum = $this->listnum;
17293		$prev_listordered = $this->listordered;
17294		$prev_listcount = $this->listcount;
17295		$prev_lispacer = $this->lispacer;
17296		$this->listnum = 0;
17297		$this->listordered = array();
17298		$this->listcount = array();
17299		$this->lispacer = '';
17300		if ((TCPDF_STATIC::empty_string($this->lasth)) OR ($reseth)) {
17301			// reset row height
17302			$this->resetLastH();
17303		}
17304		$dom = $this->getHtmlDomArray($html);
17305		$maxel = count($dom);
17306		$key = 0;
17307		while ($key < $maxel) {
17308			if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND $dom[$key]['hide']) {
17309				// store the node key
17310				$hidden_node_key = $key;
17311				if ($dom[$key]['self']) {
17312					// skip just this self-closing tag
17313					++$key;
17314				} else {
17315					// skip this and all children tags
17316					while (($key < $maxel) AND (!$dom[$key]['tag'] OR $dom[$key]['opening'] OR ($dom[$key]['parent'] != $hidden_node_key))) {
17317						// skip hidden objects
17318						++$key;
17319					}
17320					++$key;
17321				}
17322			}
17323			if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
17324				// check for pagebreak
17325				if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
17326					// add a page (or trig AcceptPageBreak() for multicolumn mode)
17327					$this->checkPageBreak($this->PageBreakTrigger + 1);
17328					$this->htmlvspace = ($this->PageBreakTrigger + 1);
17329				}
17330				if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
17331					OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
17332					// add a page (or trig AcceptPageBreak() for multicolumn mode)
17333					$this->checkPageBreak($this->PageBreakTrigger + 1);
17334					$this->htmlvspace = ($this->PageBreakTrigger + 1);
17335				}
17336			}
17337			if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
17338				if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
17339					$dom[$key]['attribute']['nobr'] = false;
17340				} else {
17341					// store current object
17342					$this->startTransaction();
17343					// save this method vars
17344					$this_method_vars['html'] = $html;
17345					$this_method_vars['ln'] = $ln;
17346					$this_method_vars['fill'] = $fill;
17347					$this_method_vars['reseth'] = $reseth;
17348					$this_method_vars['cell'] = $cell;
17349					$this_method_vars['align'] = $align;
17350					$this_method_vars['gvars'] = $gvars;
17351					$this_method_vars['prevPage'] = $prevPage;
17352					$this_method_vars['prev_cell_margin'] = $prev_cell_margin;
17353					$this_method_vars['prev_cell_padding'] = $prev_cell_padding;
17354					$this_method_vars['prevlMargin'] = $prevlMargin;
17355					$this_method_vars['prevrMargin'] = $prevrMargin;
17356					$this_method_vars['curfontname'] = $curfontname;
17357					$this_method_vars['curfontstyle'] = $curfontstyle;
17358					$this_method_vars['curfontsize'] = $curfontsize;
17359					$this_method_vars['curfontascent'] = $curfontascent;
17360					$this_method_vars['curfontdescent'] = $curfontdescent;
17361					$this_method_vars['curfontstretcing'] = $curfontstretcing;
17362					$this_method_vars['curfonttracking'] = $curfonttracking;
17363					$this_method_vars['minstartliney'] = $minstartliney;
17364					$this_method_vars['maxbottomliney'] = $maxbottomliney;
17365					$this_method_vars['yshift'] = $yshift;
17366					$this_method_vars['startlinepage'] = $startlinepage;
17367					$this_method_vars['startlinepos'] = $startlinepos;
17368					$this_method_vars['startlinex'] = $startlinex;
17369					$this_method_vars['startliney'] = $startliney;
17370					$this_method_vars['newline'] = $newline;
17371					$this_method_vars['loop'] = $loop;
17372					$this_method_vars['curpos'] = $curpos;
17373					$this_method_vars['pask'] = $pask;
17374					$this_method_vars['lalign'] = $lalign;
17375					$this_method_vars['plalign'] = $plalign;
17376					$this_method_vars['w'] = $w;
17377					$this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
17378					$this_method_vars['prev_listnum'] = $prev_listnum;
17379					$this_method_vars['prev_listordered'] = $prev_listordered;
17380					$this_method_vars['prev_listcount'] = $prev_listcount;
17381					$this_method_vars['prev_lispacer'] = $prev_lispacer;
17382					$this_method_vars['fontaligned'] = $fontaligned;
17383					$this_method_vars['key'] = $key;
17384					$this_method_vars['dom'] = $dom;
17385				}
17386			}
17387			// print THEAD block
17388			if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
17389				if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC::empty_string($dom[$dom[$key]['parent']]['thead'])) {
17390					$this->inthead = true;
17391					// print table header (thead)
17392					$this->writeHTML($this->thead, false, false, false, false, '');
17393					// check if we are on a new page or on a new column
17394					if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
17395						// we are on a new page or on a new column and the total object height is less than the available vertical space.
17396						// restore previous object
17397						$this->rollbackTransaction(true);
17398						// restore previous values
17399						foreach ($this_method_vars as $vkey => $vval) {
17400							$$vkey = $vval;
17401						}
17402						// disable table header
17403						$tmp_thead = $this->thead;
17404						$this->thead = '';
17405						// add a page (or trig AcceptPageBreak() for multicolumn mode)
17406						$pre_y = $this->y;
17407						if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
17408							// fix for multicolumn mode
17409							$startliney = $this->y;
17410						}
17411						$this->start_transaction_page = $this->page;
17412						$this->start_transaction_y = $this->y;
17413						// restore table header
17414						$this->thead = $tmp_thead;
17415						// fix table border properties
17416						if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
17417							$tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
17418						} elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
17419							$tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
17420						} else {
17421							$tmp_cellspacing = 0;
17422						}
17423						$dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
17424						$dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
17425						$dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
17426						$xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']);
17427						$dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
17428						$dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
17429						// print table header (thead)
17430						$this->writeHTML($this->thead, false, false, false, false, '');
17431					}
17432				}
17433				// move $key index forward to skip THEAD block
17434				while ( ($key < $maxel) AND (!(
17435					($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
17436					OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
17437					++$key;
17438				}
17439			}
17440			if ($dom[$key]['tag'] OR ($key == 0)) {
17441				if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
17442					$dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
17443				}
17444				// vertically align image in line
17445				if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
17446					// get image height
17447					$imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], ($dom[$key]['fontsize'] / $this->k), 'px');
17448					$autolinebreak = false;
17449					if (!empty($dom[$key]['width'])) {
17450						$imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], ($dom[$key]['fontsize'] / $this->k), 'px', false);
17451						if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R']))
17452							AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L'])))
17453							OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) {
17454							// add automatic line break
17455							$autolinebreak = true;
17456							$this->Ln('', $cell);
17457							if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
17458								// go back to evaluate this line break
17459								--$key;
17460							}
17461						}
17462					}
17463					if (!$autolinebreak) {
17464						if ($this->inPageBody()) {
17465							$pre_y = $this->y;
17466							// check for page break
17467							if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
17468								// fix for multicolumn mode
17469								$startliney = $this->y;
17470							}
17471						}
17472						if ($this->page > $startlinepage) {
17473							// fix line splitted over two pages
17474							if (isset($this->footerlen[$startlinepage])) {
17475								$curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17476							}
17477							// line to be moved one page forward
17478							$pagebuff = $this->getPageBuffer($startlinepage);
17479							$linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17480							$tstart = substr($pagebuff, 0, $startlinepos);
17481							$tend = substr($this->getPageBuffer($startlinepage), $curpos);
17482							// remove line from previous page
17483							$this->setPageBuffer($startlinepage, $tstart.''.$tend);
17484							$pagebuff = $this->getPageBuffer($this->page);
17485							$tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17486							$tend = substr($pagebuff, $this->cntmrk[$this->page]);
17487							// add line start to current page
17488							$yshift = ($minstartliney - $this->y);
17489							if ($fontaligned) {
17490								$yshift += ($curfontsize / $this->k);
17491							}
17492							$try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17493							$this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17494							// shift the annotations and links
17495							if (isset($this->PageAnnots[$this->page])) {
17496								$next_pask = count($this->PageAnnots[$this->page]);
17497							} else {
17498								$next_pask = 0;
17499							}
17500							if (isset($this->PageAnnots[$startlinepage])) {
17501								foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17502									if ($pak >= $pask) {
17503										$this->PageAnnots[$this->page][] = $pac;
17504										unset($this->PageAnnots[$startlinepage][$pak]);
17505										$npak = count($this->PageAnnots[$this->page]) - 1;
17506										$this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17507									}
17508								}
17509							}
17510							$pask = $next_pask;
17511							$startlinepos = $this->cntmrk[$this->page];
17512							$startlinepage = $this->page;
17513							$startliney = $this->y;
17514							$this->newline = false;
17515						}
17516						$this->y += ($this->getCellHeight($curfontsize / $this->k) - ($curfontdescent * $this->cell_height_ratio) - $imgh);
17517						$minstartliney = min($this->y, $minstartliney);
17518						$maxbottomliney = ($startliney + $this->getCellHeight($curfontsize / $this->k));
17519					}
17520				} elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
17521					// account for different font size
17522					$pfontname = $curfontname;
17523					$pfontstyle = $curfontstyle;
17524					$pfontsize = $curfontsize;
17525					$fontname = (isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname);
17526					$fontstyle = (isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle);
17527					$fontsize = (isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize);
17528					$fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
17529					$fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
17530					if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
17531						OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17532						OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
17533						if (($key < ($maxel - 1)) AND (
17534								($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
17535								OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17536								OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize) AND ($fontsize >= 0) AND ($curfontsize >= 0) AND ($fontsize != $curfontsize))
17537							)) {
17538							if ($this->page > $startlinepage) {
17539								// fix lines splitted over two pages
17540								if (isset($this->footerlen[$startlinepage])) {
17541									$curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17542								}
17543								// line to be moved one page forward
17544								$pagebuff = $this->getPageBuffer($startlinepage);
17545								$linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17546								$tstart = substr($pagebuff, 0, $startlinepos);
17547								$tend = substr($this->getPageBuffer($startlinepage), $curpos);
17548								// remove line start from previous page
17549								$this->setPageBuffer($startlinepage, $tstart.''.$tend);
17550								$pagebuff = $this->getPageBuffer($this->page);
17551								$tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17552								$tend = substr($pagebuff, $this->cntmrk[$this->page]);
17553								// add line start to current page
17554								$yshift = ($minstartliney - $this->y);
17555								$try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17556								$this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17557								// shift the annotations and links
17558								if (isset($this->PageAnnots[$this->page])) {
17559									$next_pask = count($this->PageAnnots[$this->page]);
17560								} else {
17561									$next_pask = 0;
17562								}
17563								if (isset($this->PageAnnots[$startlinepage])) {
17564									foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17565										if ($pak >= $pask) {
17566											$this->PageAnnots[$this->page][] = $pac;
17567											unset($this->PageAnnots[$startlinepage][$pak]);
17568											$npak = count($this->PageAnnots[$this->page]) - 1;
17569											$this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17570										}
17571									}
17572								}
17573								$pask = $next_pask;
17574								$startlinepos = $this->cntmrk[$this->page];
17575								$startlinepage = $this->page;
17576								$startliney = $this->y;
17577							}
17578							if (!isset($dom[$key]['line-height'])) {
17579								$dom[$key]['line-height'] = $this->cell_height_ratio;
17580							}
17581							if (!$dom[$key]['block']) {
17582								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']))) {
17583									$this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
17584								}
17585								if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
17586									$current_line_align_data = array($key, $minstartliney, $maxbottomliney);
17587									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)))) {
17588										$minstartliney = min($this->y, $line_align_data[1]);
17589										$maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $line_align_data[2]);
17590									} else {
17591										$minstartliney = min($this->y, $minstartliney);
17592										$maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $maxbottomliney);
17593									}
17594									$line_align_data = $current_line_align_data;
17595								}
17596							}
17597							$this->cell_height_ratio = $dom[$key]['line-height'];
17598							$fontaligned = true;
17599						}
17600						$this->SetFont($fontname, $fontstyle, $fontsize);
17601						// reset row height
17602						$this->resetLastH();
17603						$curfontname = $fontname;
17604						$curfontstyle = $fontstyle;
17605						$curfontsize = $fontsize;
17606						$curfontascent = $fontascent;
17607						$curfontdescent = $fontdescent;
17608					}
17609				}
17610				// set text rendering mode
17611				$textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
17612				$textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
17613				$textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
17614				$this->setTextRenderingMode($textstroke, $textfill, $textclip);
17615				if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
17616					$this->setFontStretching($dom[$key]['font-stretch']);
17617				}
17618				if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
17619					$this->setFontSpacing($dom[$key]['letter-spacing']);
17620				}
17621				if (($plalign == 'J') AND $dom[$key]['block']) {
17622					$plalign = '';
17623				}
17624				// get current position on page buffer
17625				$curpos = $this->pagelen[$startlinepage];
17626				if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
17627					$this->SetFillColorArray($dom[$key]['bgcolor']);
17628					$wfill = true;
17629				} else {
17630					$wfill = $fill | false;
17631				}
17632				if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
17633					$this->SetTextColorArray($dom[$key]['fgcolor']);
17634				}
17635				if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
17636					$this->SetDrawColorArray($dom[$key]['strokecolor']);
17637				}
17638				if (isset($dom[$key]['align'])) {
17639					$lalign = $dom[$key]['align'];
17640				}
17641				if (TCPDF_STATIC::empty_string($lalign)) {
17642					$lalign = $align;
17643				}
17644			}
17645			// align lines
17646			if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
17647				$newline = true;
17648				$fontaligned = false;
17649				// we are at the beginning of a new line
17650				if (isset($startlinex)) {
17651					$yshift = ($minstartliney - $startliney);
17652					if (($yshift > 0) OR ($this->page > $startlinepage)) {
17653						$yshift = 0;
17654					}
17655					$t_x = 0;
17656					// the last line must be shifted to be aligned as requested
17657					$linew = abs($this->endlinex - $startlinex);
17658					if ($this->inxobj) {
17659						// we are inside an XObject template
17660						$pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
17661						if (isset($opentagpos)) {
17662							$midpos = $opentagpos;
17663						} else {
17664							$midpos = 0;
17665						}
17666						if ($midpos > 0) {
17667							$pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
17668							$pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
17669						} else {
17670							$pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
17671							$pend = '';
17672						}
17673					} else {
17674						$pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
17675						if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17676							$this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17677							$midpos = min($opentagpos, $this->footerpos[$startlinepage]);
17678						} elseif (isset($opentagpos)) {
17679							$midpos = $opentagpos;
17680						} elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17681							$this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17682							$midpos = $this->footerpos[$startlinepage];
17683						} else {
17684							$midpos = 0;
17685						}
17686						if ($midpos > 0) {
17687							$pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
17688							$pend = substr($this->getPageBuffer($startlinepage), $midpos);
17689						} else {
17690							$pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
17691							$pend = '';
17692						}
17693					}
17694					if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
17695						// calculate shifting amount
17696						$tw = $w;
17697						if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
17698							$tw += $this->cell_padding['R'];
17699						}
17700						if ($this->lMargin != $prevlMargin) {
17701							$tw += ($prevlMargin - $this->lMargin);
17702						}
17703						if ($this->rMargin != $prevrMargin) {
17704							$tw += ($prevrMargin - $this->rMargin);
17705						}
17706						$one_space_width = $this->GetStringWidth(chr(32));
17707						$no = 0; // number of spaces on a line contained on a single block
17708						if ($this->isRTLTextDir()) { // RTL
17709							// remove left space if exist
17710							$pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
17711							if ($pos1 > 0) {
17712								$pos1 = intval($pos1);
17713								if ($this->isUnicodeFont()) {
17714									$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
17715									$spacelen = 2;
17716								} else {
17717									$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
17718									$spacelen = 1;
17719								}
17720								if ($pos1 == $pos2) {
17721									$pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
17722									if (substr($pmid, $pos1, 4) == '[()]') {
17723										$linew -= $one_space_width;
17724									} elseif ($pos1 == strpos($pmid, '[(')) {
17725										$no = 1;
17726									}
17727								}
17728							}
17729						} else { // LTR
17730							// remove right space if exist
17731							$pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
17732							if ($pos1 > 0) {
17733								$pos1 = intval($pos1);
17734								if ($this->isUnicodeFont()) {
17735									$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
17736									$spacelen = 2;
17737								} else {
17738									$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
17739									$spacelen = 1;
17740								}
17741								if ($pos1 == $pos2) {
17742									$pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
17743									$linew -= $one_space_width;
17744								}
17745							}
17746						}
17747						$mdiff = ($tw - $linew);
17748						if ($plalign == 'C') {
17749							if ($this->rtl) {
17750								$t_x = -($mdiff / 2);
17751							} else {
17752								$t_x = ($mdiff / 2);
17753							}
17754						} elseif ($plalign == 'R') {
17755							// right alignment on LTR document
17756							$t_x = $mdiff;
17757						} elseif ($plalign == 'L') {
17758							// left alignment on RTL document
17759							$t_x = -$mdiff;
17760						} elseif (($plalign == 'J') AND ($plalign == $lalign)) {
17761							// Justification
17762							if ($this->isRTLTextDir()) {
17763								// align text on the left
17764								$t_x = -$mdiff;
17765							}
17766							$ns = 0; // number of spaces
17767							$pmidtemp = $pmid;
17768							// escape special characters
17769							$pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
17770							$pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
17771							// search spaces
17772							if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
17773								$spacestr = $this->getSpaceString();
17774								$maxkk = count($lnstring[1]) - 1;
17775								for ($kk=0; $kk <= $maxkk; ++$kk) {
17776									// restore special characters
17777									$lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
17778									$lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
17779									// store number of spaces on the strings
17780									$lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
17781									// count total spaces on line
17782									$ns += $lnstring[2][$kk];
17783									$lnstring[3][$kk] = $ns;
17784								}
17785								if ($ns == 0) {
17786									$ns = 1;
17787								}
17788								// calculate additional space to add to each existing space
17789								$spacewidth = ($mdiff / ($ns - $no)) * $this->k;
17790								if ($this->FontSize <= 0) {
17791									$this->FontSize = 1;
17792								}
17793								$spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize;
17794								if ($this->font_spacing != 0) {
17795									// fixed spacing mode
17796									$osw = -1000 * $this->font_spacing / $this->FontSize;
17797									$spacewidthu += $osw;
17798								}
17799								$nsmax = $ns;
17800								$ns = 0;
17801								reset($lnstring);
17802								$offset = 0;
17803								$strcount = 0;
17804								$prev_epsposbeg = 0;
17805								$textpos = 0;
17806								if ($this->isRTLTextDir()) {
17807									$textpos = $this->wPt;
17808								}
17809								while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
17810									// check if we are inside a string section '[( ... )]'
17811									$stroffset = strpos($pmid, '[(', $offset);
17812									if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
17813										// set offset to the end of string section
17814										$offset = strpos($pmid, ')]', $stroffset);
17815										while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
17816											$offset = strpos($pmid, ')]', ($offset + 1));
17817										}
17818										if ($offset === false) {
17819											$this->Error('HTML Justification: malformed PDF code.');
17820										}
17821										continue;
17822									}
17823									if ($this->isRTLTextDir()) {
17824										$spacew = ($spacewidth * ($nsmax - $ns));
17825									} else {
17826										$spacew = ($spacewidth * $ns);
17827									}
17828									$offset = $strpiece[2][1] + strlen($strpiece[2][0]);
17829									$epsposend = strpos($pmid, $this->epsmarker.'Q', $offset);
17830									if ($epsposend !== null) {
17831										$epsposend += strlen($this->epsmarker.'Q');
17832										$epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
17833										if ($epsposbeg === null) {
17834											$epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
17835											$prev_epsposbeg = $epsposbeg;
17836										}
17837										if (($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) {
17838											// shift EPS images
17839											$trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
17840											$pmid_b = substr($pmid, 0, $epsposbeg);
17841											$pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
17842											$pmid_e = substr($pmid, $epsposend);
17843											$pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
17844											$offset = $epsposend;
17845											continue;
17846										}
17847									}
17848									$currentxpos = 0;
17849									// shift blocks of code
17850									switch ($strpiece[2][0]) {
17851										case 'Td':
17852										case 'cm':
17853										case 'm':
17854										case 'l': {
17855											// get current X position
17856											preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
17857											if (!isset($xmatches[1])) {
17858												break;
17859											}
17860											$currentxpos = $xmatches[1];
17861											$textpos = $currentxpos;
17862											if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
17863												$ns = $lnstring[3][$strcount];
17864												if ($this->isRTLTextDir()) {
17865													$spacew = ($spacewidth * ($nsmax - $ns));
17866												}
17867												++$strcount;
17868											}
17869											// justify block
17870											if (preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $pmatch) == 1) {
17871												$newpmid = sprintf('%F',(floatval($pmatch[1]) + $spacew)).' '.$pmatch[2].' x*#!#*x'.$pmatch[3].$pmatch[4];
17872												$pmid = str_replace($pmatch[0], $newpmid, $pmid);
17873												unset($pmatch, $newpmid);
17874											}
17875											break;
17876										}
17877										case 're': {
17878											// justify block
17879											if (!TCPDF_STATIC::empty_string($this->lispacer)) {
17880												$this->lispacer = '';
17881												continue;
17882											}
17883											preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
17884											if (!isset($xmatches[1])) {
17885												break;
17886											}
17887											$currentxpos = $xmatches[1];
17888											$x_diff = 0;
17889											$w_diff = 0;
17890											if ($this->isRTLTextDir()) { // RTL
17891												if ($currentxpos < $textpos) {
17892													$x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
17893													$w_diff = ($spacewidth * $lnstring[2][$strcount]);
17894												} else {
17895													if ($strcount > 0) {
17896														$x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
17897														$w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17898													}
17899												}
17900											} else { // LTR
17901												if ($currentxpos > $textpos) {
17902													if ($strcount > 0) {
17903														$x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
17904													}
17905													$w_diff = ($spacewidth * $lnstring[2][$strcount]);
17906												} else {
17907													if ($strcount > 1) {
17908														$x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
17909													}
17910													if ($strcount > 0) {
17911														$w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17912													}
17913												}
17914											}
17915											if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $pmatch) == 1) {
17916												$newx = sprintf('%F',(floatval($pmatch[1]) + $x_diff));
17917												$neww = sprintf('%F',(floatval($pmatch[3]) + $w_diff));
17918												$newpmid = $newx.' '.$pmatch[2].' '.$neww.' '.$pmatch[4].' x*#!#*x'.$pmatch[5].$pmatch[6];
17919												$pmid = str_replace($pmatch[0], $newpmid, $pmid);
17920												unset($pmatch, $newpmid, $newx, $neww);
17921											}
17922											break;
17923										}
17924										case 'c': {
17925											// get current X position
17926											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);
17927											if (!isset($xmatches[1])) {
17928												break;
17929											}
17930											$currentxpos = $xmatches[1];
17931											// justify block
17932											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) {
17933												$newx1 = sprintf('%F',(floatval($pmatch[1]) + $spacew));
17934												$newx2 = sprintf('%F',(floatval($pmatch[3]) + $spacew));
17935												$newx3 = sprintf('%F',(floatval($pmatch[5]) + $spacew));
17936												$newpmid = $newx1.' '.$pmatch[2].' '.$newx2.' '.$pmatch[4].' '.$newx3.' '.$pmatch[6].' x*#!#*x'.$pmatch[7].$pmatch[8];
17937												$pmid = str_replace($pmatch[0], $newpmid, $pmid);
17938												unset($pmatch, $newpmid, $newx1, $newx2, $newx3);
17939											}
17940											break;
17941										}
17942									}
17943									// shift the annotations and links
17944									$cxpos = ($currentxpos / $this->k);
17945									$lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps);
17946									if ($this->inxobj) {
17947										// we are inside an XObject template
17948										foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17949											if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17950												if ($cxpos > $lmpos) {
17951													$this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k);
17952													$this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17953												} else {
17954													$this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17955												}
17956												break;
17957											}
17958										}
17959									} elseif (isset($this->PageAnnots[$this->page])) {
17960										foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17961											if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17962												if ($cxpos > $lmpos) {
17963													$this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
17964													$this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17965												} else {
17966													$this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17967												}
17968												break;
17969											}
17970										}
17971									}
17972								} // end of while
17973								// remove markers
17974								$pmid = str_replace('x*#!#*x', '', $pmid);
17975								if ($this->isUnicodeFont()) {
17976									// multibyte characters
17977									$spacew = $spacewidthu;
17978									if ($this->font_stretching != 100) {
17979										// word spacing is affected by stretching
17980										$spacew /= ($this->font_stretching / 100);
17981									}
17982									// escape special characters
17983									$pos = 0;
17984									$pmid = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmid);
17985									$pmid = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmid);
17986									if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmid, $pamatch) > 0) {
17987										foreach($pamatch[0] as $pk => $pmatch) {
17988											$replace = $pamatch[1][$pk];
17989											$replace = str_replace('#!#OP#!#', '(', $replace);
17990											$replace = str_replace('#!#CP#!#', ')', $replace);
17991											$newpmid = '[('.str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacew).' (', $replace).')]';
17992											$pos = strpos($pmid, $pmatch, $pos);
17993											if ($pos !== FALSE) {
17994												$pmid = substr_replace($pmid, $newpmid, $pos, strlen($pmatch));
17995											}
17996											++$pos;
17997										}
17998										unset($pamatch);
17999									}
18000									if ($this->inxobj) {
18001										// we are inside an XObject template
18002										$this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend;
18003									} else {
18004										$this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
18005									}
18006									$endlinepos = strlen($pstart."\n".$pmid."\n");
18007								} else {
18008									// non-unicode (single-byte characters)
18009									if ($this->font_stretching != 100) {
18010										// word spacing (Tw) is affected by stretching
18011										$spacewidth /= ($this->font_stretching / 100);
18012									}
18013									$rs = sprintf('%F Tw', $spacewidth);
18014									$pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
18015									if ($this->inxobj) {
18016										// we are inside an XObject template
18017										$this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
18018									} else {
18019										$this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
18020									}
18021									$endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
18022								}
18023							}
18024						} // end of J
18025					} // end if $startlinex
18026					if (($t_x != 0) OR ($yshift < 0)) {
18027						// shift the line
18028						$trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
18029						$pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18030						$endlinepos = strlen($pstart);
18031						if ($this->inxobj) {
18032							// we are inside an XObject template
18033							$this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
18034							foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
18035								if ($pak >= $pask) {
18036									$this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
18037									$this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
18038								}
18039							}
18040						} else {
18041							$this->setPageBuffer($startlinepage, $pstart.$pend);
18042							// shift the annotations and links
18043							if (isset($this->PageAnnots[$this->page])) {
18044								foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
18045									if ($pak >= $pask) {
18046										$this->PageAnnots[$this->page][$pak]['x'] += $t_x;
18047										$this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
18048									}
18049								}
18050							}
18051						}
18052						$this->y -= $yshift;
18053					}
18054				}
18055				$pbrk = $this->checkPageBreak($this->lasth);
18056				$this->newline = false;
18057				$startlinex = $this->x;
18058				$startliney = $this->y;
18059				if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
18060					$startliney -= ((0.3 * $this->FontSizePt) / $this->k);
18061				} elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
18062					$startliney -= (($this->FontSizePt / 0.7) / $this->k);
18063				} else {
18064					$minstartliney = $startliney;
18065					$maxbottomliney = ($this->y + $this->getCellHeight($fontsize / $this->k));
18066				}
18067				$startlinepage = $this->page;
18068				if (isset($endlinepos) AND (!$pbrk)) {
18069					$startlinepos = $endlinepos;
18070				} else {
18071					if ($this->inxobj) {
18072						// we are inside an XObject template
18073						$startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
18074					} elseif (!$this->InFooter) {
18075						if (isset($this->footerlen[$this->page])) {
18076							$this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
18077						} else {
18078							$this->footerpos[$this->page] = $this->pagelen[$this->page];
18079						}
18080						$startlinepos = $this->footerpos[$this->page];
18081					} else {
18082						$startlinepos = $this->pagelen[$this->page];
18083					}
18084				}
18085				unset($endlinepos);
18086				$plalign = $lalign;
18087				if (isset($this->PageAnnots[$this->page])) {
18088					$pask = count($this->PageAnnots[$this->page]);
18089				} else {
18090					$pask = 0;
18091				}
18092				if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
18093					AND (isset($this->emptypagemrk[$this->page]))
18094					AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) {
18095					$this->SetFont($fontname, $fontstyle, $fontsize);
18096					if ($wfill) {
18097						$this->SetFillColorArray($this->bgcolor);
18098					}
18099				}
18100			} // end newline
18101			if (isset($opentagpos)) {
18102				unset($opentagpos);
18103			}
18104			if ($dom[$key]['tag']) {
18105				if ($dom[$key]['opening']) {
18106					// get text indentation (if any)
18107					if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
18108						$this->textindent = $dom[$key]['text-indent'];
18109						$this->newline = true;
18110					}
18111					// table
18112					if (($dom[$key]['value'] == 'table') AND isset($dom[$key]['cols']) AND ($dom[$key]['cols'] > 0)) {
18113						// available page width
18114						if ($this->rtl) {
18115							$wtmp = $this->x - $this->lMargin;
18116						} else {
18117							$wtmp = $this->w - $this->rMargin - $this->x;
18118						}
18119						// get cell spacing
18120						if (isset($dom[$key]['attribute']['cellspacing'])) {
18121							$clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
18122							$cellspacing = array('H' => $clsp, 'V' => $clsp);
18123						} elseif (isset($dom[$key]['border-spacing'])) {
18124							$cellspacing = $dom[$key]['border-spacing'];
18125						} else {
18126							$cellspacing = array('H' => 0, 'V' => 0);
18127						}
18128						// table width
18129						if (isset($dom[$key]['width'])) {
18130							$table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
18131						} else {
18132							$table_width = $wtmp;
18133						}
18134						$table_width -= (2 * $cellspacing['H']);
18135						if (!$this->inthead) {
18136							$this->y += $cellspacing['V'];
18137						}
18138						if ($this->rtl) {
18139							$cellspacingx = -$cellspacing['H'];
18140						} else {
18141							$cellspacingx = $cellspacing['H'];
18142						}
18143						// total table width without cellspaces
18144						$table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
18145						// minimum column width
18146						$table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
18147						// array of custom column widths
18148						$table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
18149					}
18150					// table row
18151					if ($dom[$key]['value'] == 'tr') {
18152						// reset column counter
18153						$colid = 0;
18154					}
18155					// table cell
18156					if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
18157						$trid = $dom[$key]['parent'];
18158						$table_el = $dom[$trid]['parent'];
18159						if (!isset($dom[$table_el]['cols'])) {
18160							$dom[$table_el]['cols'] = $dom[$trid]['cols'];
18161						}
18162						// store border info
18163						$tdborder = 0;
18164						if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
18165							$tdborder = $dom[$key]['border'];
18166						}
18167						$colspan = intval($dom[$key]['attribute']['colspan']);
18168						if ($colspan <= 0) {
18169							$colspan = 1;
18170						}
18171						$old_cell_padding = $this->cell_padding;
18172						if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
18173							$crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
18174							$current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
18175						} elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
18176							$current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
18177						} else {
18178							$current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
18179						}
18180						$this->cell_padding = $current_cell_padding;
18181						if (isset($dom[$key]['height'])) {
18182							// minimum cell height
18183							$cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
18184						} else {
18185							$cellh = 0;
18186						}
18187						if (isset($dom[$key]['content'])) {
18188							$cell_content = stripslashes($dom[$key]['content']);
18189						} else {
18190							$cell_content = '&nbsp;';
18191						}
18192						$tagtype = $dom[$key]['value'];
18193						$parentid = $key;
18194						while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
18195							// move $key index forward
18196							++$key;
18197						}
18198						if (!isset($dom[$trid]['startpage'])) {
18199							$dom[$trid]['startpage'] = $this->page;
18200						} else {
18201							$this->setPage($dom[$trid]['startpage']);
18202						}
18203						if (!isset($dom[$trid]['startcolumn'])) {
18204							$dom[$trid]['startcolumn'] = $this->current_column;
18205						} elseif ($this->current_column != $dom[$trid]['startcolumn']) {
18206							$tmpx = $this->x;
18207							$this->selectColumn($dom[$trid]['startcolumn']);
18208							$this->x = $tmpx;
18209						}
18210						if (!isset($dom[$trid]['starty'])) {
18211							$dom[$trid]['starty'] = $this->y;
18212						} else {
18213							$this->y = $dom[$trid]['starty'];
18214						}
18215						if (!isset($dom[$trid]['startx'])) {
18216							$dom[$trid]['startx'] = $this->x;
18217							$this->x += $cellspacingx;
18218						} else {
18219							$this->x += ($cellspacingx / 2);
18220						}
18221						if (isset($dom[$parentid]['attribute']['rowspan'])) {
18222							$rowspan = intval($dom[$parentid]['attribute']['rowspan']);
18223						} else {
18224							$rowspan = 1;
18225						}
18226						// skip row-spanned cells started on the previous rows
18227						if (isset($dom[$table_el]['rowspans'])) {
18228							$rsk = 0;
18229							$rskmax = count($dom[$table_el]['rowspans']);
18230							while ($rsk < $rskmax) {
18231								$trwsp = $dom[$table_el]['rowspans'][$rsk];
18232								$rsstartx = $trwsp['startx'];
18233								$rsendx = $trwsp['endx'];
18234								// account for margin changes
18235								if ($trwsp['startpage'] < $this->page) {
18236									if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
18237										$dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
18238										$rsstartx -= $dl;
18239										$rsendx -= $dl;
18240									} elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
18241										$dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
18242										$rsstartx += $dl;
18243										$rsendx += $dl;
18244									}
18245								}
18246								if (($trwsp['rowspan'] > 0)
18247									AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps))
18248									AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps))
18249									AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) {
18250									// set the starting X position of the current cell
18251									$this->x = $rsendx + $cellspacingx;
18252									// increment column indicator
18253									$colid += $trwsp['colspan'];
18254									if (($trwsp['rowspan'] == 1)
18255										AND (isset($dom[$trid]['endy']))
18256										AND (isset($dom[$trid]['endpage']))
18257										AND (isset($dom[$trid]['endcolumn']))
18258										AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
18259										AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18260										// set ending Y position for row
18261										$dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18262										$dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
18263									}
18264									$rsk = 0;
18265								} else {
18266									++$rsk;
18267								}
18268							}
18269						}
18270						if (isset($dom[$parentid]['width'])) {
18271							// user specified width
18272							$cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
18273							$tmpcw = ($cellw / $colspan);
18274							for ($i = 0; $i < $colspan; ++$i) {
18275								$table_colwidths[($colid + $i)] = $tmpcw;
18276							}
18277						} else {
18278							// inherit column width
18279							$cellw = 0;
18280							for ($i = 0; $i < $colspan; ++$i) {
18281								$cellw += $table_colwidths[($colid + $i)];
18282							}
18283						}
18284						$cellw += (($colspan - 1) * $cellspacing['H']);
18285						// increment column indicator
18286						$colid += $colspan;
18287						// add rowspan information to table element
18288						if ($rowspan > 1) {
18289							$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));
18290						}
18291						$cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
18292						if ($rowspan > 1) {
18293							$dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
18294						}
18295						// push background colors
18296						if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
18297							$dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
18298						}
18299						// store border info
18300						if (isset($tdborder) AND !empty($tdborder)) {
18301							$dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
18302						}
18303						$prevLastH = $this->lasth;
18304						// store some info for multicolumn mode
18305						if ($this->rtl) {
18306							$this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
18307						} else {
18308							$this->colxshift['x'] = $this->x - $this->lMargin;
18309						}
18310						$this->colxshift['s'] = $cellspacing;
18311						$this->colxshift['p'] = $current_cell_padding;
18312						// ****** write the cell content ******
18313						$this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
18314						// restore some values
18315						$this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
18316						$this->lasth = $prevLastH;
18317						$this->cell_padding = $old_cell_padding;
18318						$dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
18319						// update the end of row position
18320						if ($rowspan <= 1) {
18321							if (isset($dom[$trid]['endy'])) {
18322								if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) {
18323									$dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
18324								} elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) {
18325									$dom[$trid]['endy'] = $this->y;
18326								}
18327							} else {
18328								$dom[$trid]['endy'] = $this->y;
18329							}
18330							if (isset($dom[$trid]['endpage'])) {
18331								$dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
18332							} else {
18333								$dom[$trid]['endpage'] = $this->page;
18334							}
18335							if (isset($dom[$trid]['endcolumn'])) {
18336								$dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
18337							} else {
18338								$dom[$trid]['endcolumn'] = $this->current_column;
18339							}
18340						} else {
18341							// account for row-spanned cells
18342							$dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
18343							$dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
18344							$dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
18345							$dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column;
18346						}
18347						if (isset($dom[$table_el]['rowspans'])) {
18348							// update endy and endpage on rowspanned cells
18349							foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
18350								if ($trwsp['rowspan'] > 0) {
18351									if (isset($dom[$trid]['endpage'])) {
18352										if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18353											$dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18354										} elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
18355											$dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
18356											$dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
18357											$dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
18358										} else {
18359											$dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
18360										}
18361									}
18362								}
18363							}
18364						}
18365						$this->x += ($cellspacingx / 2);
18366					} else {
18367						// opening tag (or self-closing tag)
18368						if (!isset($opentagpos)) {
18369							if ($this->inxobj) {
18370								// we are inside an XObject template
18371								$opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
18372							} elseif (!$this->InFooter) {
18373								if (isset($this->footerlen[$this->page])) {
18374									$this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
18375								} else {
18376									$this->footerpos[$this->page] = $this->pagelen[$this->page];
18377								}
18378								$opentagpos = $this->footerpos[$this->page];
18379							}
18380						}
18381						$dom = $this->openHTMLTagHandler($dom, $key, $cell);
18382					}
18383				} else { // closing tag
18384					$prev_numpages = $this->numpages;
18385					$old_bordermrk = $this->bordermrk[$this->page];
18386					$dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
18387					if ($this->bordermrk[$this->page] > $old_bordermrk) {
18388						$startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk);
18389					}
18390					if ($prev_numpages > $this->numpages) {
18391						$startlinepage = $this->page;
18392					}
18393				}
18394			} elseif (strlen($dom[$key]['value']) > 0) {
18395				// print list-item
18396				if (!TCPDF_STATIC::empty_string($this->lispacer) AND ($this->lispacer != '^')) {
18397					$this->SetFont($pfontname, $pfontstyle, $pfontsize);
18398					$this->resetLastH();
18399					$minstartliney = $this->y;
18400					$maxbottomliney = ($startliney + $this->getCellHeight($this->FontSize));
18401					if (is_numeric($pfontsize) AND ($pfontsize > 0)) {
18402						$this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
18403					}
18404					$this->SetFont($curfontname, $curfontstyle, $curfontsize);
18405					$this->resetLastH();
18406					if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
18407						$pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
18408						$pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
18409						$this->y += ($this->getCellHeight(($pfontsize - $curfontsize) / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
18410						$minstartliney = min($this->y, $minstartliney);
18411						$maxbottomliney = max(($this->y + $this->getCellHeight($pfontsize / $this->k)), $maxbottomliney);
18412					}
18413				}
18414				// text
18415				$this->htmlvspace = 0;
18416				if ((!$this->premode) AND $this->isRTLTextDir()) {
18417					// reverse spaces order
18418					$lsp = ''; // left spaces
18419					$rsp = ''; // right spaces
18420					if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18421						$lsp = $matches[1];
18422					}
18423					if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18424						$rsp = $matches[1];
18425					}
18426					$dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
18427				}
18428				if ($newline) {
18429					if (!$this->premode) {
18430						$prelen = strlen($dom[$key]['value']);
18431						if ($this->isRTLTextDir()) {
18432							// right trim except non-breaking space
18433							$dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
18434						} else {
18435							// left trim except non-breaking space
18436							$dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
18437						}
18438						$postlen = strlen($dom[$key]['value']);
18439						if (($postlen == 0) AND ($prelen > 0)) {
18440							$dom[$key]['trimmed_space'] = true;
18441						}
18442					}
18443					$newline = false;
18444					$firstblock = true;
18445				} else {
18446					$firstblock = false;
18447					// replace empty multiple spaces string with a single space
18448					$dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']);
18449				}
18450				$strrest = '';
18451				if ($this->rtl) {
18452					$this->x -= $this->textindent;
18453				} else {
18454					$this->x += $this->textindent;
18455				}
18456				if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
18457					$strlinelen = $this->GetStringWidth($dom[$key]['value']);
18458					if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
18459						// HTML <a> Link
18460						$hrefcolor = '';
18461						if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
18462							$hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
18463						}
18464						$hrefstyle = -1;
18465						if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
18466							$hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
18467						}
18468						$strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
18469					} else {
18470						$wadj = 0; // space to leave for block continuity
18471						if ($this->rtl) {
18472							$cwa = ($this->x - $this->lMargin);
18473						} else {
18474							$cwa = ($this->w - $this->rMargin - $this->x);
18475						}
18476						if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) {
18477							// check the next text blocks for continuity
18478							$nkey = ($key + 1);
18479							$write_block = true;
18480							$same_textdir = true;
18481							$tmp_fontname = $this->FontFamily;
18482							$tmp_fontstyle = $this->FontStyle;
18483							$tmp_fontsize = $this->FontSizePt;
18484							while ($write_block AND isset($dom[$nkey])) {
18485								if ($dom[$nkey]['tag']) {
18486									if ($dom[$nkey]['block']) {
18487										// end of block
18488										$write_block = false;
18489									}
18490									$tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
18491									$tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
18492									$tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
18493									$same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
18494								} else {
18495									$nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'+/', $this->re_space['m'], $dom[$nkey]['value']);
18496									if (isset($nextstr[0]) AND $same_textdir) {
18497										$wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
18498										if (isset($nextstr[1])) {
18499											$write_block = false;
18500										}
18501									}
18502								}
18503								++$nkey;
18504							}
18505						}
18506						if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) {
18507							$wadj = 0;
18508							$nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $dom[$key]['value']);
18509							$numblks = count($nextstr);
18510							if ($numblks > 1) {
18511								// try to split on blank spaces
18512								$wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)]));
18513							} else {
18514								// set the entire block on new line
18515								$wadj = $this->GetStringWidth($nextstr[0]);
18516							}
18517						}
18518						// check for reversed text direction
18519						if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) {
18520							// LTR text on RTL direction or RTL text on LTR direction
18521							$reverse_dir = true;
18522							$this->rtl = !$this->rtl;
18523							$revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems
18524							if ($this->rtl) {
18525								$this->x += $revshift;
18526							} else {
18527								$this->x -= $revshift;
18528							}
18529							$xws = $this->x;
18530						}
18531						// ****** write only until the end of the line and get the rest ******
18532						$strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
18533						// restore default direction
18534						if ($reverse_dir AND ($wadj == 0)) {
18535							$this->x = $xws;
18536							$this->rtl = !$this->rtl;
18537							$reverse_dir = false;
18538						}
18539					}
18540				}
18541				$this->textindent = 0;
18542				if (strlen($strrest) > 0) {
18543					// store the remaining string on the previous $key position
18544					$this->newline = true;
18545					if ($strrest == $dom[$key]['value']) {
18546						// used to avoid infinite loop
18547						++$loop;
18548					} else {
18549						$loop = 0;
18550					}
18551					$dom[$key]['value'] = $strrest;
18552					if ($cell) {
18553						if ($this->rtl) {
18554							$this->x -= $this->cell_padding['R'];
18555						} else {
18556							$this->x += $this->cell_padding['L'];
18557						}
18558					}
18559					if ($loop < 3) {
18560						--$key;
18561					}
18562				} else {
18563					$loop = 0;
18564					// add the positive font spacing of the last character (if any)
18565					 if ($this->font_spacing > 0) {
18566					 	if ($this->rtl) {
18567							$this->x -= $this->font_spacing;
18568						} else {
18569							$this->x += $this->font_spacing;
18570						}
18571					}
18572				}
18573			}
18574			++$key;
18575			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')) {
18576				// check if we are on a new page or on a new column
18577				if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) {
18578					// we are on a new page or on a new column and the total object height is less than the available vertical space.
18579					// restore previous object
18580					$this->rollbackTransaction(true);
18581					// restore previous values
18582					foreach ($this_method_vars as $vkey => $vval) {
18583						$$vkey = $vval;
18584					}
18585					if (!empty($dom[$key]['thead'])) {
18586						$this->inthead = true;
18587					}
18588					// add a page (or trig AcceptPageBreak() for multicolumn mode)
18589					$pre_y = $this->y;
18590					if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
18591						$startliney = $this->y;
18592					}
18593					$undo = true; // avoid infinite loop
18594				} else {
18595					$undo = false;
18596				}
18597			}
18598		} // end for each $key
18599		// align the last line
18600		if (isset($startlinex)) {
18601			$yshift = ($minstartliney - $startliney);
18602			if (($yshift > 0) OR ($this->page > $startlinepage)) {
18603				$yshift = 0;
18604			}
18605			$t_x = 0;
18606			// the last line must be shifted to be aligned as requested
18607			$linew = abs($this->endlinex - $startlinex);
18608			if ($this->inxobj) {
18609				// we are inside an XObject template
18610				$pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
18611				if (isset($opentagpos)) {
18612					$midpos = $opentagpos;
18613				} else {
18614					$midpos = 0;
18615				}
18616				if ($midpos > 0) {
18617					$pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
18618					$pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
18619				} else {
18620					$pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
18621					$pend = '';
18622				}
18623			} else {
18624				$pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
18625				if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18626					$this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18627					$midpos = min($opentagpos, $this->footerpos[$startlinepage]);
18628				} elseif (isset($opentagpos)) {
18629					$midpos = $opentagpos;
18630				} elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18631					$this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18632					$midpos = $this->footerpos[$startlinepage];
18633				} else {
18634					$midpos = 0;
18635				}
18636				if ($midpos > 0) {
18637					$pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
18638					$pend = substr($this->getPageBuffer($startlinepage), $midpos);
18639				} else {
18640					$pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
18641					$pend = '';
18642				}
18643			}
18644			if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
18645				// calculate shifting amount
18646				$tw = $w;
18647				if ($this->lMargin != $prevlMargin) {
18648					$tw += ($prevlMargin - $this->lMargin);
18649				}
18650				if ($this->rMargin != $prevrMargin) {
18651					$tw += ($prevrMargin - $this->rMargin);
18652				}
18653				$one_space_width = $this->GetStringWidth(chr(32));
18654				$no = 0; // number of spaces on a line contained on a single block
18655				if ($this->isRTLTextDir()) { // RTL
18656					// remove left space if exist
18657					$pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
18658					if ($pos1 > 0) {
18659						$pos1 = intval($pos1);
18660						if ($this->isUnicodeFont()) {
18661							$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
18662							$spacelen = 2;
18663						} else {
18664							$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
18665							$spacelen = 1;
18666						}
18667						if ($pos1 == $pos2) {
18668							$pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
18669							if (substr($pmid, $pos1, 4) == '[()]') {
18670								$linew -= $one_space_width;
18671							} elseif ($pos1 == strpos($pmid, '[(')) {
18672								$no = 1;
18673							}
18674						}
18675					}
18676				} else { // LTR
18677					// remove right space if exist
18678					$pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
18679					if ($pos1 > 0) {
18680						$pos1 = intval($pos1);
18681						if ($this->isUnicodeFont()) {
18682							$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
18683							$spacelen = 2;
18684						} else {
18685							$pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
18686							$spacelen = 1;
18687						}
18688						if ($pos1 == $pos2) {
18689							$pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
18690							$linew -= $one_space_width;
18691						}
18692					}
18693				}
18694				$mdiff = ($tw - $linew);
18695				if ($plalign == 'C') {
18696					if ($this->rtl) {
18697						$t_x = -($mdiff / 2);
18698					} else {
18699						$t_x = ($mdiff / 2);
18700					}
18701				} elseif ($plalign == 'R') {
18702					// right alignment on LTR document
18703					$t_x = $mdiff;
18704				} elseif ($plalign == 'L') {
18705					// left alignment on RTL document
18706					$t_x = -$mdiff;
18707				}
18708			} // end if startlinex
18709			if (($t_x != 0) OR ($yshift < 0)) {
18710				// shift the line
18711				$trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
18712				$pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18713				$endlinepos = strlen($pstart);
18714				if ($this->inxobj) {
18715					// we are inside an XObject template
18716					$this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
18717					foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
18718						if ($pak >= $pask) {
18719							$this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
18720							$this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
18721						}
18722					}
18723				} else {
18724					$this->setPageBuffer($startlinepage, $pstart.$pend);
18725					// shift the annotations and links
18726					if (isset($this->PageAnnots[$this->page])) {
18727						foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
18728							if ($pak >= $pask) {
18729								$this->PageAnnots[$this->page][$pak]['x'] += $t_x;
18730								$this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
18731							}
18732						}
18733					}
18734				}
18735				$this->y -= $yshift;
18736				$yshift = 0;
18737			}
18738		}
18739		// restore previous values
18740		$this->setGraphicVars($gvars);
18741		if ($this->num_columns > 1) {
18742			$this->selectColumn();
18743		} elseif ($this->page > $prevPage) {
18744			$this->lMargin = $this->pagedim[$this->page]['olm'];
18745			$this->rMargin = $this->pagedim[$this->page]['orm'];
18746		}
18747		// restore previous list state
18748		$this->cell_height_ratio = $prev_cell_height_ratio;
18749		$this->listnum = $prev_listnum;
18750		$this->listordered = $prev_listordered;
18751		$this->listcount = $prev_listcount;
18752		$this->lispacer = $prev_lispacer;
18753		if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
18754			$this->Ln($this->lasth);
18755			if ($this->y < $maxbottomliney) {
18756				$this->y = $maxbottomliney;
18757			}
18758		}
18759		unset($dom);
18760	}
18761
18762	/**
18763	 * Process opening tags.
18764	 * @param $dom (array) html dom array
18765	 * @param $key (int) current element id
18766	 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
18767	 * @return $dom array
18768	 * @protected
18769	 */
18770	protected function openHTMLTagHandler($dom, $key, $cell) {
18771		$tag = $dom[$key];
18772		$parent = $dom[($dom[$key]['parent'])];
18773		$firsttag = ($key == 1);
18774		// check for text direction attribute
18775		if (isset($tag['dir'])) {
18776			$this->setTempRTL($tag['dir']);
18777		} else {
18778			$this->tmprtl = false;
18779		}
18780		if ($tag['block']) {
18781			$hbz = 0; // distance from y to line bottom
18782			$hb = 0; // vertical space between block tags
18783			// calculate vertical space for block tags
18784			if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
18785				$cur_h = $this->tagvspaces[$tag['value']][0]['h'];
18786			} elseif (isset($tag['fontsize'])) {
18787				$cur_h = $this->getCellHeight($tag['fontsize'] / $this->k);
18788			} else {
18789				$cur_h = $this->getCellHeight($this->FontSize);
18790			}
18791			if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
18792				$on = $this->tagvspaces[$tag['value']][0]['n'];
18793			} elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18794				$on = 0.6;
18795			} else {
18796				$on = 1;
18797			}
18798			if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br', 'hr')))) {
18799				$hb = 0;
18800			} else {
18801				$hb = ($on * $cur_h);
18802			}
18803			if (($this->htmlvspace <= 0) AND ($on > 0)) {
18804				if (isset($parent['fontsize'])) {
18805					$hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
18806				} else {
18807					$hbz = $this->getCellHeight($this->FontSize);
18808				}
18809			}
18810			if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) {
18811				// fix vertical space after table
18812				$hbz = 0;
18813			}
18814			// closing vertical space
18815			$hbc = 0;
18816			if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
18817				$pre_h = $this->tagvspaces[$tag['value']][1]['h'];
18818			} elseif (isset($parent['fontsize'])) {
18819				$pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
18820			} else {
18821				$pre_h = $this->getCellHeight($this->FontSize);
18822			}
18823			if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
18824				$cn = $this->tagvspaces[$tag['value']][1]['n'];
18825			} elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18826				$cn = 0.6;
18827			} else {
18828				$cn = 1;
18829			}
18830			if (isset($this->tagvspaces[$tag['value']][1])) {
18831				$hbc = ($cn * $pre_h);
18832			}
18833		}
18834		// Opening tag
18835		switch($tag['value']) {
18836			case 'table': {
18837				$cp = 0;
18838				$cs = 0;
18839				$dom[$key]['rowspans'] = array();
18840				if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
18841					$this->htmlvspace = 0;
18842					// set table header
18843					if (!TCPDF_STATIC::empty_string($dom[$key]['thead'])) {
18844						// set table header
18845						$this->thead = $dom[$key]['thead'];
18846						if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
18847							$this->theadMargins = array();
18848							$this->theadMargins['cell_padding'] = $this->cell_padding;
18849							$this->theadMargins['lmargin'] = $this->lMargin;
18850							$this->theadMargins['rmargin'] = $this->rMargin;
18851							$this->theadMargins['page'] = $this->page;
18852							$this->theadMargins['cell'] = $cell;
18853							$this->theadMargins['gvars'] = $this->getGraphicVars();
18854						}
18855					}
18856				}
18857				// store current margins and page
18858				$dom[$key]['old_cell_padding'] = $this->cell_padding;
18859				if (isset($tag['attribute']['cellpadding'])) {
18860					$pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
18861					$this->SetCellPadding($pad);
18862				} elseif (isset($tag['padding'])) {
18863					$this->cell_padding = $tag['padding'];
18864				}
18865				if (isset($tag['attribute']['cellspacing'])) {
18866					$cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
18867				} elseif (isset($tag['border-spacing'])) {
18868					$cs = $tag['border-spacing']['V'];
18869				}
18870				$prev_y = $this->y;
18871				if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) {
18872					$this->inthead = true;
18873					// add a page (or trig AcceptPageBreak() for multicolumn mode)
18874					$this->checkPageBreak($this->PageBreakTrigger + 1);
18875				}
18876				break;
18877			}
18878			case 'tr': {
18879				// array of columns positions
18880				$dom[$key]['cellpos'] = array();
18881				break;
18882			}
18883			case 'hr': {
18884				if ((isset($tag['height'])) AND ($tag['height'] != '')) {
18885					$hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
18886				} else {
18887					$hrHeight = $this->GetLineWidth();
18888				}
18889				$this->addHTMLVertSpace($hbz, max($hb, ($hrHeight / 2)), $cell, $firsttag);
18890				$x = $this->GetX();
18891				$y = $this->GetY();
18892				$wtmp = $this->w - $this->lMargin - $this->rMargin;
18893				if ($cell) {
18894					$wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']);
18895				}
18896				if ((isset($tag['width'])) AND ($tag['width'] != '')) {
18897					$hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
18898				} else {
18899					$hrWidth = $wtmp;
18900				}
18901				$prevlinewidth = $this->GetLineWidth();
18902				$this->SetLineWidth($hrHeight);
18903				$this->Line($x, $y, $x + $hrWidth, $y);
18904				$this->SetLineWidth($prevlinewidth);
18905				$this->addHTMLVertSpace(max($hbc, ($hrHeight / 2)), 0, $cell, !isset($dom[($key + 1)]));
18906				break;
18907			}
18908			case 'a': {
18909				if (array_key_exists('href', $tag['attribute'])) {
18910					$this->HREF['url'] = $tag['attribute']['href'];
18911				}
18912				break;
18913			}
18914			case 'img': {
18915				if (!empty($tag['attribute']['src'])) {
18916					if ($tag['attribute']['src'][0] === '@') {
18917						// data stream
18918						$tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1));
18919						$type = '';
18920					} else {
18921						// get image type
18922						$type = TCPDF_IMAGES::getImageFileType($tag['attribute']['src']);
18923					}
18924					if (!isset($tag['width'])) {
18925						$tag['width'] = 0;
18926					}
18927					if (!isset($tag['height'])) {
18928						$tag['height'] = 0;
18929					}
18930					//if (!isset($tag['attribute']['align'])) {
18931						// the only alignment supported is "bottom"
18932						// further development is required for other modes.
18933						$tag['attribute']['align'] = 'bottom';
18934					//}
18935					switch($tag['attribute']['align']) {
18936						case 'top': {
18937							$align = 'T';
18938							break;
18939						}
18940						case 'middle': {
18941							$align = 'M';
18942							break;
18943						}
18944						case 'bottom': {
18945							$align = 'B';
18946							break;
18947						}
18948						default: {
18949							$align = 'B';
18950							break;
18951						}
18952					}
18953					$prevy = $this->y;
18954					$xpos = $this->x;
18955					$imglink = '';
18956					if (isset($this->HREF['url']) AND !TCPDF_STATIC::empty_string($this->HREF['url'])) {
18957						$imglink = $this->HREF['url'];
18958						if ($imglink[0] == '#') {
18959							// convert url to internal link
18960							$lnkdata = explode(',', $imglink);
18961							if (isset($lnkdata[0])) {
18962								$page = intval(substr($lnkdata[0], 1));
18963								if (empty($page) OR ($page <= 0)) {
18964									$page = $this->page;
18965								}
18966								if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
18967									$lnky = floatval($lnkdata[1]);
18968								} else {
18969									$lnky = 0;
18970								}
18971								$imglink = $this->AddLink();
18972								$this->SetLink($imglink, $lnky, $page);
18973							}
18974						}
18975					}
18976					$border = 0;
18977					if (isset($tag['border']) AND !empty($tag['border'])) {
18978						// currently only support 1 (frame) or a combination of 'LTRB'
18979						$border = $tag['border'];
18980					}
18981					$iw = '';
18982					if (isset($tag['width'])) {
18983						$iw = $this->getHTMLUnitToUnits($tag['width'], ($tag['fontsize'] / $this->k), 'px', false);
18984					}
18985					$ih = '';
18986					if (isset($tag['height'])) {
18987						$ih = $this->getHTMLUnitToUnits($tag['height'], ($tag['fontsize'] / $this->k), 'px', false);
18988					}
18989					if (($type == 'eps') OR ($type == 'ai')) {
18990						$this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
18991					} elseif ($type == 'svg') {
18992						$this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
18993					} else {
18994						$this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
18995					}
18996					switch($align) {
18997						case 'T': {
18998							$this->y = $prevy;
18999							break;
19000						}
19001						case 'M': {
19002							$this->y = (($this->img_rb_y + $prevy - ($this->getCellHeight($tag['fontsize'] / $this->k))) / 2);
19003							break;
19004						}
19005						case 'B': {
19006							$this->y = $this->img_rb_y - ($this->getCellHeight($tag['fontsize'] / $this->k) - ($this->getFontDescent($tag['fontname'], $tag['fontstyle'], $tag['fontsize']) * $this->cell_height_ratio));
19007							break;
19008						}
19009					}
19010				}
19011				break;
19012			}
19013			case 'dl': {
19014				++$this->listnum;
19015				if ($this->listnum == 1) {
19016					$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19017				} else {
19018					$this->addHTMLVertSpace(0, 0, $cell, $firsttag);
19019				}
19020				break;
19021			}
19022			case 'dt': {
19023				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19024				break;
19025			}
19026			case 'dd': {
19027				if ($this->rtl) {
19028					$this->rMargin += $this->listindent;
19029				} else {
19030					$this->lMargin += $this->listindent;
19031				}
19032				++$this->listindentlevel;
19033				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19034				break;
19035			}
19036			case 'ul':
19037			case 'ol': {
19038				++$this->listnum;
19039				if ($tag['value'] == 'ol') {
19040					$this->listordered[$this->listnum] = true;
19041				} else {
19042					$this->listordered[$this->listnum] = false;
19043				}
19044				if (isset($tag['attribute']['start'])) {
19045					$this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
19046				} else {
19047					$this->listcount[$this->listnum] = 0;
19048				}
19049				if ($this->rtl) {
19050					$this->rMargin += $this->listindent;
19051					$this->x -= $this->listindent;
19052				} else {
19053					$this->lMargin += $this->listindent;
19054					$this->x += $this->listindent;
19055				}
19056				++$this->listindentlevel;
19057				if ($this->listnum == 1) {
19058					if ($key > 1) {
19059						$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19060					}
19061				} else {
19062					$this->addHTMLVertSpace(0, 0, $cell, $firsttag);
19063				}
19064				break;
19065			}
19066			case 'li': {
19067				if ($key > 2) {
19068					$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19069				}
19070				if ($this->listordered[$this->listnum]) {
19071					// ordered item
19072					if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
19073						$this->lispacer = $parent['attribute']['type'];
19074					} elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
19075						$this->lispacer = $parent['listtype'];
19076					} elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
19077						$this->lispacer = $this->lisymbol;
19078					} else {
19079						$this->lispacer = '#';
19080					}
19081					++$this->listcount[$this->listnum];
19082					if (isset($tag['attribute']['value'])) {
19083						$this->listcount[$this->listnum] = intval($tag['attribute']['value']);
19084					}
19085				} else {
19086					// unordered item
19087					if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
19088						$this->lispacer = $parent['attribute']['type'];
19089					} elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
19090						$this->lispacer = $parent['listtype'];
19091					} elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
19092						$this->lispacer = $this->lisymbol;
19093					} else {
19094						$this->lispacer = '!';
19095					}
19096				}
19097				break;
19098			}
19099			case 'blockquote': {
19100				if ($this->rtl) {
19101					$this->rMargin += $this->listindent;
19102				} else {
19103					$this->lMargin += $this->listindent;
19104				}
19105				++$this->listindentlevel;
19106				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19107				break;
19108			}
19109			case 'br': {
19110				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19111				break;
19112			}
19113			case 'div': {
19114				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19115				break;
19116			}
19117			case 'p': {
19118				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19119				break;
19120			}
19121			case 'pre': {
19122				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19123				$this->premode = true;
19124				break;
19125			}
19126			case 'sup': {
19127				$this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
19128				break;
19129			}
19130			case 'sub': {
19131				$this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
19132				break;
19133			}
19134			case 'h1':
19135			case 'h2':
19136			case 'h3':
19137			case 'h4':
19138			case 'h5':
19139			case 'h6': {
19140				$this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19141				break;
19142			}
19143			// Form fields (since 4.8.000 - 2009-09-07)
19144			case 'form': {
19145				if (isset($tag['attribute']['action'])) {
19146					$this->form_action = $tag['attribute']['action'];
19147				} else {
19148					$this->Error('Please explicitly set action attribute path!');
19149				}
19150				if (isset($tag['attribute']['enctype'])) {
19151					$this->form_enctype = $tag['attribute']['enctype'];
19152				} else {
19153					$this->form_enctype = 'application/x-www-form-urlencoded';
19154				}
19155				if (isset($tag['attribute']['method'])) {
19156					$this->form_mode = $tag['attribute']['method'];
19157				} else {
19158					$this->form_mode = 'post';
19159				}
19160				break;
19161			}
19162			case 'input': {
19163				if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19164					$name = $tag['attribute']['name'];
19165				} else {
19166					break;
19167				}
19168				$prop = array();
19169				$opt = array();
19170				if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19171					$prop['readonly'] = true;
19172				}
19173				if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19174					$value = $tag['attribute']['value'];
19175				}
19176				if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC::empty_string($tag['attribute']['maxlength'])) {
19177					$opt['maxlen'] = intval($tag['attribute']['maxlength']);
19178				}
19179				$h = $this->getCellHeight($this->FontSize);
19180				if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19181					$w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
19182				} else {
19183					$w = $h;
19184				}
19185				if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
19186					$checked = true;
19187				} else {
19188					$checked = false;
19189				}
19190				if (isset($tag['align'])) {
19191					switch ($tag['align']) {
19192						case 'C': {
19193							$opt['q'] = 1;
19194							break;
19195						}
19196						case 'R': {
19197							$opt['q'] = 2;
19198							break;
19199						}
19200						case 'L':
19201						default: {
19202							break;
19203						}
19204					}
19205				}
19206				switch ($tag['attribute']['type']) {
19207					case 'text': {
19208						if (isset($value)) {
19209							$opt['v'] = $value;
19210						}
19211						$this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19212						break;
19213					}
19214					case 'password': {
19215						if (isset($value)) {
19216							$opt['v'] = $value;
19217						}
19218						$prop['password'] = 'true';
19219						$this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19220						break;
19221					}
19222					case 'checkbox': {
19223						if (!isset($value)) {
19224							break;
19225						}
19226						$this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
19227						break;
19228					}
19229					case 'radio': {
19230						if (!isset($value)) {
19231							break;
19232						}
19233						$this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
19234						break;
19235					}
19236					case 'submit': {
19237						if (!isset($value)) {
19238							$value = 'submit';
19239						}
19240						$w = $this->GetStringWidth($value) * 1.5;
19241						$h *= 1.6;
19242						$prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19243						$action = array();
19244						$action['S'] = 'SubmitForm';
19245						$action['F'] = $this->form_action;
19246						if ($this->form_enctype != 'FDF') {
19247							$action['Flags'] = array('ExportFormat');
19248						}
19249						if ($this->form_mode == 'get') {
19250							$action['Flags'] = array('GetMethod');
19251						}
19252						$this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
19253						break;
19254					}
19255					case 'reset': {
19256						if (!isset($value)) {
19257							$value = 'reset';
19258						}
19259						$w = $this->GetStringWidth($value) * 1.5;
19260						$h *= 1.6;
19261						$prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19262						$this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
19263						break;
19264					}
19265					case 'file': {
19266						$prop['fileSelect'] = 'true';
19267						$this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19268						if (!isset($value)) {
19269							$value = '*';
19270						}
19271						$w = $this->GetStringWidth($value) * 2;
19272						$h *= 1.2;
19273						$prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19274						$jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
19275						$this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19276						break;
19277					}
19278					case 'hidden': {
19279						if (isset($value)) {
19280							$opt['v'] = $value;
19281						}
19282						$opt['f'] = array('invisible', 'hidden');
19283						$this->TextField($name, 0, 0, $prop, $opt, '', '', false);
19284						break;
19285					}
19286					case 'image': {
19287						// THIS TYPE MUST BE FIXED
19288						if (isset($tag['attribute']['src']) AND !TCPDF_STATIC::empty_string($tag['attribute']['src'])) {
19289							$img = $tag['attribute']['src'];
19290						} else {
19291							break;
19292						}
19293						$value = 'img';
19294						//$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
19295						if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19296							$jsaction = $tag['attribute']['onclick'];
19297						} else {
19298							$jsaction = '';
19299						}
19300						$this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19301						break;
19302					}
19303					case 'button': {
19304						if (!isset($value)) {
19305							$value = ' ';
19306						}
19307						$w = $this->GetStringWidth($value) * 1.5;
19308						$h *= 1.6;
19309						$prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19310						if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19311							$jsaction = $tag['attribute']['onclick'];
19312						} else {
19313							$jsaction = '';
19314						}
19315						$this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19316						break;
19317					}
19318				}
19319				break;
19320			}
19321			case 'textarea': {
19322				$prop = array();
19323				$opt = array();
19324				if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19325					$prop['readonly'] = true;
19326				}
19327				if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19328					$name = $tag['attribute']['name'];
19329				} else {
19330					break;
19331				}
19332				if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19333					$opt['v'] = $tag['attribute']['value'];
19334				}
19335				if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC::empty_string($tag['attribute']['cols'])) {
19336					$w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
19337				} else {
19338					$w = 40;
19339				}
19340				if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC::empty_string($tag['attribute']['rows'])) {
19341					$h = intval($tag['attribute']['rows']) * $this->getCellHeight($this->FontSize);
19342				} else {
19343					$h = 10;
19344				}
19345				$prop['multiline'] = 'true';
19346				$this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19347				break;
19348			}
19349			case 'select': {
19350				$h = $this->getCellHeight($this->FontSize);
19351				if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19352					$h *= ($tag['attribute']['size'] + 1);
19353				}
19354				$prop = array();
19355				$opt = array();
19356				if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19357					$name = $tag['attribute']['name'];
19358				} else {
19359					break;
19360				}
19361				$w = 0;
19362				if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC::empty_string($tag['attribute']['opt'])) {
19363					$options = explode('#!NwL!#', $tag['attribute']['opt']);
19364					$values = array();
19365					foreach ($options as $val) {
19366						if (strpos($val, '#!TaB!#') !== false) {
19367							$opts = explode('#!TaB!#', $val);
19368							$values[] = $opts;
19369							$w = max($w, $this->GetStringWidth($opts[1]));
19370						} else {
19371							$values[] = $val;
19372							$w = max($w, $this->GetStringWidth($val));
19373						}
19374					}
19375				} else {
19376					break;
19377				}
19378				$w *= 2;
19379				if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
19380					$prop['multipleSelection'] = 'true';
19381					$this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19382				} else {
19383					$this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19384				}
19385				break;
19386			}
19387			case 'tcpdf': {
19388				if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
19389					// Special tag used to call TCPDF methods
19390					if (isset($tag['attribute']['method'])) {
19391						$tcpdf_method = $tag['attribute']['method'];
19392						if (method_exists($this, $tcpdf_method)) {
19393							if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
19394								$params = unserialize(urldecode($tag['attribute']['params']));
19395								call_user_func_array(array($this, $tcpdf_method), $params);
19396							} else {
19397								$this->$tcpdf_method();
19398							}
19399							$this->newline = true;
19400						}
19401					}
19402				}
19403				break;
19404			}
19405			default: {
19406				break;
19407			}
19408		}
19409		// define tags that support borders and background colors
19410		$bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
19411		if (in_array($tag['value'], $bordertags)) {
19412			// set border
19413			$dom[$key]['borderposition'] = $this->getBorderStartPosition();
19414		}
19415		if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
19416			$pba = $dom[$key]['attribute']['pagebreakafter'];
19417			// check for pagebreak
19418			if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19419				// add a page (or trig AcceptPageBreak() for multicolumn mode)
19420				$this->checkPageBreak($this->PageBreakTrigger + 1);
19421			}
19422			if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19423				OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19424				// add a page (or trig AcceptPageBreak() for multicolumn mode)
19425				$this->checkPageBreak($this->PageBreakTrigger + 1);
19426			}
19427		}
19428		return $dom;
19429	}
19430
19431	/**
19432	 * Process closing tags.
19433	 * @param $dom (array) html dom array
19434	 * @param $key (int) current element id
19435	 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
19436	 * @param $maxbottomliney (int) maximum y value of current line
19437	 * @return $dom array
19438	 * @protected
19439	 */
19440	protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
19441		$tag = $dom[$key];
19442		$parent = $dom[($dom[$key]['parent'])];
19443		$lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
19444		$in_table_head = false;
19445		// maximum x position (used to draw borders)
19446		if ($this->rtl) {
19447			$xmax = $this->w;
19448		} else {
19449			$xmax = 0;
19450		}
19451		if ($tag['block']) {
19452			$hbz = 0; // distance from y to line bottom
19453			$hb = 0; // vertical space between block tags
19454			// calculate vertical space for block tags
19455			if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
19456				$pre_h = $this->tagvspaces[$tag['value']][1]['h'];
19457			} elseif (isset($parent['fontsize'])) {
19458				$pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
19459			} else {
19460				$pre_h = $this->getCellHeight($this->FontSize);
19461			}
19462			if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
19463				$cn = $this->tagvspaces[$tag['value']][1]['n'];
19464			} elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
19465				$cn = 0.6;
19466			} else {
19467				$cn = 1;
19468			}
19469			if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) {
19470				$hb = 0;
19471			} else {
19472				$hb = ($cn * $pre_h);
19473			}
19474			if ($maxbottomliney > $this->PageBreakTrigger) {
19475				$hbz = $this->getCellHeight($this->FontSize);
19476			} elseif ($this->y < $maxbottomliney) {
19477				$hbz = ($maxbottomliney - $this->y);
19478			}
19479		}
19480		// Closing tag
19481		switch($tag['value']) {
19482			case 'tr': {
19483				$table_el = $dom[($dom[$key]['parent'])]['parent'];
19484				if (!isset($parent['endy'])) {
19485					$dom[($dom[$key]['parent'])]['endy'] = $this->y;
19486					$parent['endy'] = $this->y;
19487				}
19488				if (!isset($parent['endpage'])) {
19489					$dom[($dom[$key]['parent'])]['endpage'] = $this->page;
19490					$parent['endpage'] = $this->page;
19491				}
19492				if (!isset($parent['endcolumn'])) {
19493					$dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column;
19494					$parent['endcolumn'] = $this->current_column;
19495				}
19496				// update row-spanned cells
19497				if (isset($dom[$table_el]['rowspans'])) {
19498					foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19499						$dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
19500						if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19501							if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
19502								$dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
19503							} elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
19504								$dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19505								$dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19506								$dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19507							}
19508						}
19509					}
19510					// report new endy and endpage to the rowspanned cells
19511					foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19512						if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19513							$dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
19514							$dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19515							$dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
19516							$dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19517							$dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
19518							$dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19519						}
19520					}
19521					// update remaining rowspanned cells
19522					foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19523						if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19524							$dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
19525							$dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
19526							$dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
19527						}
19528					}
19529				}
19530				$prev_page = $this->page;
19531				$this->setPage($dom[($dom[$key]['parent'])]['endpage']);
19532				if ($this->num_columns > 1) {
19533					if ((($this->current_column == 0) AND ($dom[($dom[$key]['parent'])]['endcolumn'] == ($this->num_columns - 1)))
19534						OR (($this->current_column == $dom[($dom[$key]['parent'])]['endcolumn']) AND ($prev_page < $this->page))) {
19535						// page jump
19536						$this->selectColumn(0);
19537						$dom[($dom[$key]['parent'])]['endcolumn'] = 0;
19538						$dom[($dom[$key]['parent'])]['endy'] = $this->y;
19539					} else {
19540						$this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
19541						$this->y = $dom[($dom[$key]['parent'])]['endy'];
19542					}
19543				} else {
19544					$this->y = $dom[($dom[$key]['parent'])]['endy'];
19545				}
19546				if (isset($dom[$table_el]['attribute']['cellspacing'])) {
19547					$this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
19548				} elseif (isset($dom[$table_el]['border-spacing'])) {
19549					$this->y += $dom[$table_el]['border-spacing']['V'];
19550				}
19551				$this->Ln(0, $cell);
19552				if ($this->current_column == $parent['startcolumn']) {
19553					$this->x = $parent['startx'];
19554				}
19555				// account for booklet mode
19556				if ($this->page > $parent['startpage']) {
19557					if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
19558						$this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
19559					} elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
19560						$this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
19561					}
19562				}
19563				break;
19564			}
19565			case 'tablehead':
19566				// closing tag used for the thead part
19567				$in_table_head = true;
19568				$this->inthead = false;
19569			case 'table': {
19570				$table_el = $parent;
19571				// set default border
19572				if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
19573					// set default border
19574					$border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
19575				} else {
19576					$border = 0;
19577				}
19578				$default_border = $border;
19579				// fix bottom line alignment of last line before page break
19580				foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
19581					// update row-spanned cells
19582					if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19583						foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19584							if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) {
19585								$dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
19586							}
19587							if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) {
19588								$dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
19589							}
19590						}
19591					}
19592					if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
19593						$pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
19594						$dom[$prevtrkey]['endy'] = $pgendy;
19595						// update row-spanned cells
19596						if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19597							foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19598								if (($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
19599									$dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
19600									$dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
19601								}
19602							}
19603						}
19604					}
19605					$prevtrkey = $trkey;
19606					$table_el = $dom[($dom[$key]['parent'])];
19607				}
19608				// for each row
19609				if (count($table_el['trids']) > 0) {
19610					unset($xmax);
19611				}
19612				foreach ($table_el['trids'] as $j => $trkey) {
19613					$parent = $dom[$trkey];
19614					if (!isset($xmax)) {
19615						$xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
19616					}
19617					// for each cell on the row
19618					foreach ($parent['cellpos'] as $k => $cellpos) {
19619						if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
19620							$cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
19621							$cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
19622							$endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
19623							$startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
19624							$endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
19625							$startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
19626							$endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
19627						} else {
19628							$endy = $parent['endy'];
19629							$startpage = $parent['startpage'];
19630							$endpage = $parent['endpage'];
19631							$startcolumn = $parent['startcolumn'];
19632							$endcolumn = $parent['endcolumn'];
19633						}
19634						if ($this->num_columns == 0) {
19635							$this->num_columns = 1;
19636						}
19637						if (isset($cellpos['border'])) {
19638							$border = $cellpos['border'];
19639						}
19640						if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
19641							$this->SetFillColorArray($cellpos['bgcolor']);
19642							$fill = true;
19643						} else {
19644							$fill = false;
19645						}
19646						$x = $cellpos['startx'];
19647						$y = $parent['starty'];
19648						$starty = $y;
19649						$w = abs($cellpos['endx'] - $cellpos['startx']);
19650						// get border modes
19651						$border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
19652						$border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
19653						$border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19654						// design borders around HTML cells.
19655						for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
19656							$ccode = '';
19657							$this->setPage($page);
19658							if ($this->num_columns < 2) {
19659								// single-column mode
19660								$this->x = $x;
19661								$this->y = $this->tMargin;
19662							}
19663							// account for margin changes
19664							if ($page > $startpage) {
19665								if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
19666									$this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
19667								} elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
19668									$this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
19669								}
19670							}
19671							if ($startpage == $endpage) { // single page
19672								$deltacol = 0;
19673								$deltath = 0;
19674								for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
19675									$this->selectColumn($column);
19676									if ($startcolumn == $endcolumn) { // single column
19677										$cborder = $border;
19678										$h = $endy - $parent['starty'];
19679										$this->y = $y;
19680										$this->x = $x;
19681									} elseif ($column == $startcolumn) { // first column
19682										$cborder = $border_start;
19683										$this->y = $starty;
19684										$this->x = $x;
19685										$h = $this->h - $this->y - $this->bMargin;
19686										if ($this->rtl) {
19687											$deltacol = $this->x + $this->rMargin - $this->w;
19688										} else {
19689											$deltacol = $this->x - $this->lMargin;
19690										}
19691									} elseif ($column == $endcolumn) { // end column
19692										$cborder = $border_end;
19693										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19694											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19695										}
19696										$this->x += $deltacol;
19697										$h = $endy - $this->y;
19698									} else { // middle column
19699										$cborder = $border_middle;
19700										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19701											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19702										}
19703										$this->x += $deltacol;
19704										$h = $this->h - $this->y - $this->bMargin;
19705									}
19706									$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19707								} // end for each column
19708							} elseif ($page == $startpage) { // first page
19709								$deltacol = 0;
19710								$deltath = 0;
19711								for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
19712									$this->selectColumn($column);
19713									if ($column == $startcolumn) { // first column
19714										$cborder = $border_start;
19715										$this->y = $starty;
19716										$this->x = $x;
19717										$h = $this->h - $this->y - $this->bMargin;
19718										if ($this->rtl) {
19719											$deltacol = $this->x + $this->rMargin - $this->w;
19720										} else {
19721											$deltacol = $this->x - $this->lMargin;
19722										}
19723									} else { // middle column
19724										$cborder = $border_middle;
19725										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19726											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19727										}
19728										$this->x += $deltacol;
19729										$h = $this->h - $this->y - $this->bMargin;
19730									}
19731									$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19732								} // end for each column
19733							} elseif ($page == $endpage) { // last page
19734								$deltacol = 0;
19735								$deltath = 0;
19736								for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
19737									$this->selectColumn($column);
19738									if ($column == $endcolumn) { // end column
19739										$cborder = $border_end;
19740										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19741											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19742										}
19743										$this->x += $deltacol;
19744										$h = $endy - $this->y;
19745									} else { // middle column
19746										$cborder = $border_middle;
19747										if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19748											$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19749										}
19750										$this->x += $deltacol;
19751										$h = $this->h - $this->y - $this->bMargin;
19752									}
19753									$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19754								} // end for each column
19755							} else { // middle page
19756								$deltacol = 0;
19757								$deltath = 0;
19758								for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
19759									$this->selectColumn($column);
19760									$cborder = $border_middle;
19761									if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19762										$this->y = $this->columns[$column]['th']['\''.$page.'\''];
19763									}
19764									$this->x += $deltacol;
19765									$h = $this->h - $this->y - $this->bMargin;
19766									$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19767								} // end for each column
19768							}
19769							if ($cborder OR $fill) {
19770								$offsetlen = strlen($ccode);
19771								// draw border and fill
19772								if ($this->inxobj) {
19773									// we are inside an XObject template
19774									if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
19775										$pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
19776										$pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
19777										$this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
19778									} else {
19779										$pagemark = $this->xobjects[$this->xobjid]['intmrk'];
19780										$this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
19781									}
19782									$pagebuff = $this->xobjects[$this->xobjid]['outdata'];
19783									$pstart = substr($pagebuff, 0, $pagemark);
19784									$pend = substr($pagebuff, $pagemark);
19785									$this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
19786								} else {
19787									// draw border and fill
19788									if (end($this->transfmrk[$this->page]) !== false) {
19789										$pagemarkkey = key($this->transfmrk[$this->page]);
19790										$pagemark = $this->transfmrk[$this->page][$pagemarkkey];
19791									} elseif ($this->InFooter) {
19792										$pagemark = $this->footerpos[$this->page];
19793									} else {
19794										$pagemark = $this->intmrk[$this->page];
19795									}
19796									$pagebuff = $this->getPageBuffer($this->page);
19797									$pstart = substr($pagebuff, 0, $pagemark);
19798									$pend = substr($pagebuff, $pagemark);
19799									$this->setPageBuffer($this->page, $pstart.$ccode.$pend);
19800								}
19801							}
19802						} // end for each page
19803						// restore default border
19804						$border = $default_border;
19805					} // end for each cell on the row
19806					if (isset($table_el['attribute']['cellspacing'])) {
19807						$this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
19808					} elseif (isset($table_el['border-spacing'])) {
19809						$this->y += $table_el['border-spacing']['V'];
19810					}
19811					$this->Ln(0, $cell);
19812					$this->x = $parent['startx'];
19813					if ($endpage > $startpage) {
19814						if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
19815							$this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
19816						} elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
19817							$this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
19818						}
19819					}
19820				}
19821				if (!$in_table_head) { // we are not inside a thead section
19822					$this->cell_padding = $table_el['old_cell_padding'];
19823					// reset row height
19824					$this->resetLastH();
19825					if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) {
19826						$plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]);
19827						if (($plendiff > 0) AND ($plendiff < 60)) {
19828							$pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff);
19829							if (substr($pagediff, 0, 5) == 'BT /F') {
19830								// the difference is only a font setting
19831								$plendiff = 0;
19832							}
19833						}
19834						if ($plendiff == 0) {
19835							// remove last blank page
19836							$this->deletePage($this->numpages);
19837						}
19838					}
19839					if (isset($this->theadMargins['top'])) {
19840						// restore top margin
19841						$this->tMargin = $this->theadMargins['top'];
19842					}
19843					if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
19844						// reset main table header
19845						$this->thead = '';
19846						$this->theadMargins = array();
19847						$this->pagedim[$this->page]['tm'] = $this->tMargin;
19848					}
19849				}
19850				$parent = $table_el;
19851				break;
19852			}
19853			case 'a': {
19854				$this->HREF = '';
19855				break;
19856			}
19857			case 'sup': {
19858				$this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
19859				break;
19860			}
19861			case 'sub': {
19862				$this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k));
19863				break;
19864			}
19865			case 'div': {
19866				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19867				break;
19868			}
19869			case 'blockquote': {
19870				if ($this->rtl) {
19871					$this->rMargin -= $this->listindent;
19872				} else {
19873					$this->lMargin -= $this->listindent;
19874				}
19875				--$this->listindentlevel;
19876				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19877				break;
19878			}
19879			case 'p': {
19880				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19881				break;
19882			}
19883			case 'pre': {
19884				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19885				$this->premode = false;
19886				break;
19887			}
19888			case 'dl': {
19889				--$this->listnum;
19890				if ($this->listnum <= 0) {
19891					$this->listnum = 0;
19892					$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19893				} else {
19894					$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19895				}
19896				$this->resetLastH();
19897				break;
19898			}
19899			case 'dt': {
19900				$this->lispacer = '';
19901				$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19902				break;
19903			}
19904			case 'dd': {
19905				$this->lispacer = '';
19906				if ($this->rtl) {
19907					$this->rMargin -= $this->listindent;
19908				} else {
19909					$this->lMargin -= $this->listindent;
19910				}
19911				--$this->listindentlevel;
19912				$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19913				break;
19914			}
19915			case 'ul':
19916			case 'ol': {
19917				--$this->listnum;
19918				$this->lispacer = '';
19919				if ($this->rtl) {
19920					$this->rMargin -= $this->listindent;
19921				} else {
19922					$this->lMargin -= $this->listindent;
19923				}
19924				--$this->listindentlevel;
19925				if ($this->listnum <= 0) {
19926					$this->listnum = 0;
19927					$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19928				} else {
19929					$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19930				}
19931				$this->resetLastH();
19932				break;
19933			}
19934			case 'li': {
19935				$this->lispacer = '';
19936				$this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19937				break;
19938			}
19939			case 'h1':
19940			case 'h2':
19941			case 'h3':
19942			case 'h4':
19943			case 'h5':
19944			case 'h6': {
19945				$this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19946				break;
19947			}
19948			// Form fields (since 4.8.000 - 2009-09-07)
19949			case 'form': {
19950				$this->form_action = '';
19951				$this->form_enctype = 'application/x-www-form-urlencoded';
19952				break;
19953			}
19954			default : {
19955				break;
19956			}
19957		}
19958		// draw border and background (if any)
19959		$this->drawHTMLTagBorder($parent, $xmax);
19960		if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
19961			$pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
19962			// check for pagebreak
19963			if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19964				// add a page (or trig AcceptPageBreak() for multicolumn mode)
19965				$this->checkPageBreak($this->PageBreakTrigger + 1);
19966			}
19967			if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19968				OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19969				// add a page (or trig AcceptPageBreak() for multicolumn mode)
19970				$this->checkPageBreak($this->PageBreakTrigger + 1);
19971			}
19972		}
19973		$this->tmprtl = false;
19974		return $dom;
19975	}
19976
19977	/**
19978	 * Add vertical spaces if needed.
19979	 * @param $hbz (string) Distance between current y and line bottom.
19980	 * @param $hb (string) The height of the break.
19981	 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
19982	 * @param $firsttag (boolean) set to true when the tag is the first.
19983	 * @param $lasttag (boolean) set to true when the tag is the last.
19984	 * @protected
19985	 */
19986	protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
19987		if ($firsttag) {
19988			$this->Ln(0, $cell);
19989			$this->htmlvspace = 0;
19990			return;
19991		}
19992		if ($lasttag) {
19993			$this->Ln($hbz, $cell);
19994			$this->htmlvspace = 0;
19995			return;
19996		}
19997		if ($hb < $this->htmlvspace) {
19998			$hd = 0;
19999		} else {
20000			$hd = $hb - $this->htmlvspace;
20001			$this->htmlvspace = $hb;
20002		}
20003		$this->Ln(($hbz + $hd), $cell);
20004	}
20005
20006	/**
20007	 * Return the starting coordinates to draw an html border
20008	 * @return array containing top-left border coordinates
20009	 * @protected
20010	 * @since 5.7.000 (2010-08-03)
20011	 */
20012	protected function getBorderStartPosition() {
20013		if ($this->rtl) {
20014			$xmax = $this->lMargin;
20015		} else {
20016			$xmax = $this->w - $this->rMargin;
20017		}
20018		return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax);
20019	}
20020
20021	/**
20022	 * Draw an HTML block border and fill
20023	 * @param $tag (array) array of tag properties.
20024	 * @param $xmax (int) end X coordinate for border.
20025	 * @protected
20026	 * @since 5.7.000 (2010-08-03)
20027	 */
20028	protected function drawHTMLTagBorder($tag, $xmax) {
20029		if (!isset($tag['borderposition'])) {
20030			// nothing to draw
20031			return;
20032		}
20033		$prev_x = $this->x;
20034		$prev_y = $this->y;
20035		$prev_lasth = $this->lasth;
20036		$border = 0;
20037		$fill = false;
20038		$this->lasth = 0;
20039		if (isset($tag['border']) AND !empty($tag['border'])) {
20040			// get border style
20041			$border = $tag['border'];
20042			if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
20043				// border for table header
20044				$border = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
20045			}
20046		}
20047		if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
20048			// get background color
20049			$old_bgcolor = $this->bgcolor;
20050			$this->SetFillColorArray($tag['bgcolor']);
20051			$fill = true;
20052		}
20053		if (!$border AND !$fill) {
20054			// nothing to draw
20055			return;
20056		}
20057		if (isset($tag['attribute']['cellspacing'])) {
20058			$clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
20059			$cellspacing = array('H' => $clsp, 'V' => $clsp);
20060		} elseif (isset($tag['border-spacing'])) {
20061			$cellspacing = $tag['border-spacing'];
20062		} else {
20063			$cellspacing = array('H' => 0, 'V' => 0);
20064		}
20065		if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
20066			// draw the border externally respect the sqare edge.
20067			$border['mode'] = 'ext';
20068		}
20069		if ($this->rtl) {
20070			if ($xmax >= $tag['borderposition']['x']) {
20071				$xmax = $tag['borderposition']['xmax'];
20072			}
20073			$w = ($tag['borderposition']['x'] - $xmax);
20074		} else {
20075			if ($xmax <= $tag['borderposition']['x']) {
20076				$xmax = $tag['borderposition']['xmax'];
20077			}
20078			$w = ($xmax - $tag['borderposition']['x']);
20079		}
20080		if ($w <= 0) {
20081			return;
20082		}
20083		$w += $cellspacing['H'];
20084		$startpage = $tag['borderposition']['page'];
20085		$startcolumn = $tag['borderposition']['column'];
20086		$x = $tag['borderposition']['x'];
20087		$y = $tag['borderposition']['y'];
20088		$endpage = $this->page;
20089		$starty = $tag['borderposition']['y'] - $cellspacing['V'];
20090		$currentY = $this->y;
20091		$this->x = $x;
20092		// get latest column
20093		$endcolumn = $this->current_column;
20094		if ($this->num_columns == 0) {
20095			$this->num_columns = 1;
20096		}
20097		// get border modes
20098		$border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
20099		$border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
20100		$border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
20101		// temporary disable page regions
20102		$temp_page_regions = $this->page_regions;
20103		$this->page_regions = array();
20104		// design borders around HTML cells.
20105		for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
20106			$ccode = '';
20107			$this->setPage($page);
20108			if ($this->num_columns < 2) {
20109				// single-column mode
20110				$this->x = $x;
20111				$this->y = $this->tMargin;
20112			}
20113			// account for margin changes
20114			if ($page > $startpage) {
20115				if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
20116					$this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
20117				} elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
20118					$this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
20119				}
20120			}
20121			if ($startpage == $endpage) {
20122				// single page
20123				for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
20124					$this->selectColumn($column);
20125					if ($startcolumn == $endcolumn) { // single column
20126						$cborder = $border;
20127						$h = ($currentY - $y) + $cellspacing['V'];
20128						$this->y = $starty;
20129					} elseif ($column == $startcolumn) { // first column
20130						$cborder = $border_start;
20131						$this->y = $starty;
20132						$h = $this->h - $this->y - $this->bMargin;
20133					} elseif ($column == $endcolumn) { // end column
20134						$cborder = $border_end;
20135						$h = $currentY - $this->y;
20136					} else { // middle column
20137						$cborder = $border_middle;
20138						$h = $this->h - $this->y - $this->bMargin;
20139					}
20140					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20141				} // end for each column
20142			} elseif ($page == $startpage) { // first page
20143				for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
20144					$this->selectColumn($column);
20145					if ($column == $startcolumn) { // first column
20146						$cborder = $border_start;
20147						$this->y = $starty;
20148						$h = $this->h - $this->y - $this->bMargin;
20149					} else { // middle column
20150						$cborder = $border_middle;
20151						$h = $this->h - $this->y - $this->bMargin;
20152					}
20153					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20154				} // end for each column
20155			} elseif ($page == $endpage) { // last page
20156				for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
20157					$this->selectColumn($column);
20158					if ($column == $endcolumn) {
20159						// end column
20160						$cborder = $border_end;
20161						$h = $currentY - $this->y;
20162					} else {
20163						// middle column
20164						$cborder = $border_middle;
20165						$h = $this->h - $this->y - $this->bMargin;
20166					}
20167					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20168				} // end for each column
20169			} else { // middle page
20170				for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
20171					$this->selectColumn($column);
20172					$cborder = $border_middle;
20173					$h = $this->h - $this->y - $this->bMargin;
20174					$ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20175				} // end for each column
20176			}
20177			if ($cborder OR $fill) {
20178				$offsetlen = strlen($ccode);
20179				// draw border and fill
20180				if ($this->inxobj) {
20181					// we are inside an XObject template
20182					if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
20183						$pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
20184						$pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
20185						$this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
20186					} else {
20187						$pagemark = $this->xobjects[$this->xobjid]['intmrk'];
20188						$this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
20189					}
20190					$pagebuff = $this->xobjects[$this->xobjid]['outdata'];
20191					$pstart = substr($pagebuff, 0, $pagemark);
20192					$pend = substr($pagebuff, $pagemark);
20193					$this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
20194				} else {
20195					if (end($this->transfmrk[$this->page]) !== false) {
20196						$pagemarkkey = key($this->transfmrk[$this->page]);
20197						$pagemark = $this->transfmrk[$this->page][$pagemarkkey];
20198					} elseif ($this->InFooter) {
20199						$pagemark = $this->footerpos[$this->page];
20200					} else {
20201						$pagemark = $this->intmrk[$this->page];
20202					}
20203					$pagebuff = $this->getPageBuffer($this->page);
20204					$pstart = substr($pagebuff, 0, $pagemark);
20205					$pend = substr($pagebuff, $pagemark);
20206					$this->setPageBuffer($this->page, $pstart.$ccode.$pend);
20207					$this->bordermrk[$this->page] += $offsetlen;
20208					$this->cntmrk[$this->page] += $offsetlen;
20209				}
20210			}
20211		} // end for each page
20212		// restore page regions
20213		$this->page_regions = $temp_page_regions;
20214		if (isset($old_bgcolor)) {
20215			// restore background color
20216			$this->SetFillColorArray($old_bgcolor);
20217		}
20218		// restore pointer position
20219		$this->x = $prev_x;
20220		$this->y = $prev_y;
20221		$this->lasth = $prev_lasth;
20222	}
20223
20224	/**
20225	 * Set the default bullet to be used as LI bullet symbol
20226	 * @param $symbol (string) 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')
20227	 * @public
20228	 * @since 4.0.028 (2008-09-26)
20229	 */
20230	public function setLIsymbol($symbol='!') {
20231		// check for custom image symbol
20232		if (substr($symbol, 0, 4) == 'img|') {
20233			$this->lisymbol = $symbol;
20234			return;
20235		}
20236		$symbol = strtolower($symbol);
20237		$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');
20238		if (in_array($symbol, $valid_symbols)) {
20239			$this->lisymbol = $symbol;
20240		} else {
20241			$this->lisymbol = '';
20242		}
20243	}
20244
20245	/**
20246	 * Set the booklet mode for double-sided pages.
20247	 * @param $booklet (boolean) true set the booklet mode on, false otherwise.
20248	 * @param $inner (float) Inner page margin.
20249	 * @param $outer (float) Outer page margin.
20250	 * @public
20251	 * @since 4.2.000 (2008-10-29)
20252	 */
20253	public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
20254		$this->booklet = $booklet;
20255		if ($inner >= 0) {
20256			$this->lMargin = $inner;
20257		}
20258		if ($outer >= 0) {
20259			$this->rMargin = $outer;
20260		}
20261	}
20262
20263	/**
20264	 * Swap the left and right margins.
20265	 * @param $reverse (boolean) if true swap left and right margins.
20266	 * @protected
20267	 * @since 4.2.000 (2008-10-29)
20268	 */
20269	protected function swapMargins($reverse=true) {
20270		if ($reverse) {
20271			// swap left and right margins
20272			$mtemp = $this->original_lMargin;
20273			$this->original_lMargin = $this->original_rMargin;
20274			$this->original_rMargin = $mtemp;
20275			$deltam = $this->original_lMargin - $this->original_rMargin;
20276			$this->lMargin += $deltam;
20277			$this->rMargin -= $deltam;
20278		}
20279	}
20280
20281	/**
20282	 * Set the vertical spaces for HTML tags.
20283	 * The array must have the following structure (example):
20284	 * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1)));
20285	 * The first array level contains the tag names,
20286	 * the second level contains 0 for opening tags or 1 for closing tags,
20287	 * the third level contains the vertical space unit (h) and the number spaces to add (n).
20288	 * If the h parameter is not specified, default values are used.
20289	 * @param $tagvs (array) array of tags and relative vertical spaces.
20290	 * @public
20291	 * @since 4.2.001 (2008-10-30)
20292	 */
20293	public function setHtmlVSpace($tagvs) {
20294		$this->tagvspaces = $tagvs;
20295	}
20296
20297	/**
20298	 * Set custom width for list indentation.
20299	 * @param $width (float) width of the indentation. Use negative value to disable it.
20300	 * @public
20301	 * @since 4.2.007 (2008-11-12)
20302	 */
20303	public function setListIndentWidth($width) {
20304		return $this->customlistindent = floatval($width);
20305	}
20306
20307	/**
20308	 * Set the top/bottom cell sides to be open or closed when the cell cross the page.
20309	 * @param $isopen (boolean) if true keeps the top/bottom border open for the cell sides that cross the page.
20310	 * @public
20311	 * @since 4.2.010 (2008-11-14)
20312	 */
20313	public function setOpenCell($isopen) {
20314		$this->opencell = $isopen;
20315	}
20316
20317	/**
20318	 * Set the color and font style for HTML links.
20319	 * @param $color (array) RGB array of colors
20320	 * @param $fontstyle (string) additional font styles to add
20321	 * @public
20322	 * @since 4.4.003 (2008-12-09)
20323	 */
20324	public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
20325		$this->htmlLinkColorArray = $color;
20326		$this->htmlLinkFontStyle = $fontstyle;
20327	}
20328
20329	/**
20330	 * Convert HTML string containing value and unit of measure to user's units or points.
20331	 * @param $htmlval (string) String containing values and unit.
20332	 * @param $refsize (string) Reference value in points.
20333	 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
20334	 * @param $points (boolean) If true returns points, otherwise returns value in user's units.
20335	 * @return float value in user's unit or point if $points=true
20336	 * @public
20337	 * @since 4.4.004 (2008-12-10)
20338	 */
20339	public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
20340		$supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
20341		$retval = 0;
20342		$value = 0;
20343		$unit = 'px';
20344		if ($points) {
20345			$k = 1;
20346		} else {
20347			$k = $this->k;
20348		}
20349		if (in_array($defaultunit, $supportedunits)) {
20350			$unit = $defaultunit;
20351		}
20352		if (is_numeric($htmlval)) {
20353			$value = floatval($htmlval);
20354		} elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
20355			$value = floatval($mnum[1]);
20356			if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
20357				if (in_array($munit[1], $supportedunits)) {
20358					$unit = $munit[1];
20359				}
20360			}
20361		}
20362		switch ($unit) {
20363			// percentage
20364			case '%': {
20365				$retval = (($value * $refsize) / 100);
20366				break;
20367			}
20368			// relative-size
20369			case 'em': {
20370				$retval = ($value * $refsize);
20371				break;
20372			}
20373			// height of lower case 'x' (about half the font-size)
20374			case 'ex': {
20375				$retval = ($value * ($refsize / 2));
20376				break;
20377			}
20378			// absolute-size
20379			case 'in': {
20380				$retval = (($value * $this->dpi) / $k);
20381				break;
20382			}
20383			// centimeters
20384			case 'cm': {
20385				$retval = (($value / 2.54 * $this->dpi) / $k);
20386				break;
20387			}
20388			// millimeters
20389			case 'mm': {
20390				$retval = (($value / 25.4 * $this->dpi) / $k);
20391				break;
20392			}
20393			// one pica is 12 points
20394			case 'pc': {
20395				$retval = (($value * 12) / $k);
20396				break;
20397			}
20398			// points
20399			case 'pt': {
20400				$retval = ($value / $k);
20401				break;
20402			}
20403			// pixels
20404			case 'px': {
20405				$retval = $this->pixelsToUnits($value);
20406				if ($points) {
20407					$retval *= $this->k;
20408				}
20409				break;
20410			}
20411		}
20412		return $retval;
20413	}
20414
20415	/**
20416	 * Output an HTML list bullet or ordered item symbol
20417	 * @param $listdepth (int) list nesting level
20418	 * @param $listtype (string) type of list
20419	 * @param $size (float) current font size
20420	 * @protected
20421	 * @since 4.4.004 (2008-12-10)
20422	 */
20423	protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
20424		if ($this->state != 2) {
20425			return;
20426		}
20427		$size /= $this->k;
20428		$fill = '';
20429		$bgcolor = $this->bgcolor;
20430		$color = $this->fgcolor;
20431		$strokecolor = $this->strokecolor;
20432		$width = 0;
20433		$textitem = '';
20434		$tmpx = $this->x;
20435		$lspace = $this->GetStringWidth('  ');
20436		if ($listtype == '^') {
20437			// special symbol used for avoid justification of rect bullet
20438			$this->lispacer = '';
20439			return;
20440		} elseif ($listtype == '!') {
20441			// set default list type for unordered list
20442			$deftypes = array('disc', 'circle', 'square');
20443			$listtype = $deftypes[($listdepth - 1) % 3];
20444		} elseif ($listtype == '#') {
20445			// set default list type for ordered list
20446			$listtype = 'decimal';
20447		} elseif (substr($listtype, 0, 4) == 'img|') {
20448			// custom image type ('img|type|width|height|image.ext')
20449			$img = explode('|', $listtype);
20450			$listtype = 'img';
20451		}
20452		switch ($listtype) {
20453			// unordered types
20454			case 'none': {
20455				break;
20456			}
20457			case 'disc': {
20458				$r = $size / 6;
20459				$lspace += (2 * $r);
20460				if ($this->rtl) {
20461					$this->x += $lspace;
20462				} else {
20463					$this->x -= $lspace;
20464				}
20465				$this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8);
20466				break;
20467			}
20468			case 'circle': {
20469				$r = $size / 6;
20470				$lspace += (2 * $r);
20471				if ($this->rtl) {
20472					$this->x += $lspace;
20473				} else {
20474					$this->x -= $lspace;
20475				}
20476				$prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor;
20477				$new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
20478				$this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
20479				$this->_out($prev_line_style); // restore line settings
20480				break;
20481			}
20482			case 'square': {
20483				$l = $size / 3;
20484				$lspace += $l;
20485				if ($this->rtl) {;
20486					$this->x += $lspace;
20487				} else {
20488					$this->x -= $lspace;
20489				}
20490				$this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color);
20491				break;
20492			}
20493			case 'img': {
20494				// 1=>type, 2=>width, 3=>height, 4=>image.ext
20495				$lspace += $img[2];
20496				if ($this->rtl) {;
20497					$this->x += $lspace;
20498				} else {
20499					$this->x -= $lspace;
20500				}
20501				$imgtype = strtolower($img[1]);
20502				$prev_y = $this->y;
20503				switch ($imgtype) {
20504					case 'svg': {
20505						$this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
20506						break;
20507					}
20508					case 'ai':
20509					case 'eps': {
20510						$this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
20511						break;
20512					}
20513					default: {
20514						$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);
20515						break;
20516					}
20517				}
20518				$this->y = $prev_y;
20519				break;
20520			}
20521			// ordered types
20522			// $this->listcount[$this->listnum];
20523			// $textitem
20524			case '1':
20525			case 'decimal': {
20526				$textitem = $this->listcount[$this->listnum];
20527				break;
20528			}
20529			case 'decimal-leading-zero': {
20530				$textitem = sprintf('%02d', $this->listcount[$this->listnum]);
20531				break;
20532			}
20533			case 'i':
20534			case 'lower-roman': {
20535				$textitem = strtolower(TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]));
20536				break;
20537			}
20538			case 'I':
20539			case 'upper-roman': {
20540				$textitem = TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]);
20541				break;
20542			}
20543			case 'a':
20544			case 'lower-alpha':
20545			case 'lower-latin': {
20546				$textitem = chr(97 + $this->listcount[$this->listnum] - 1);
20547				break;
20548			}
20549			case 'A':
20550			case 'upper-alpha':
20551			case 'upper-latin': {
20552				$textitem = chr(65 + $this->listcount[$this->listnum] - 1);
20553				break;
20554			}
20555			case 'lower-greek': {
20556				$textitem = TCPDF_FONTS::unichr((945 + $this->listcount[$this->listnum] - 1), $this->isunicode);
20557				break;
20558			}
20559			/*
20560			// Types to be implemented (special handling)
20561			case 'hebrew': {
20562				break;
20563			}
20564			case 'armenian': {
20565				break;
20566			}
20567			case 'georgian': {
20568				break;
20569			}
20570			case 'cjk-ideographic': {
20571				break;
20572			}
20573			case 'hiragana': {
20574				break;
20575			}
20576			case 'katakana': {
20577				break;
20578			}
20579			case 'hiragana-iroha': {
20580				break;
20581			}
20582			case 'katakana-iroha': {
20583				break;
20584			}
20585			*/
20586			default: {
20587				$textitem = $this->listcount[$this->listnum];
20588			}
20589		}
20590		if (!TCPDF_STATIC::empty_string($textitem)) {
20591			// Check whether we need a new page or new column
20592			$prev_y = $this->y;
20593			$h = $this->getCellHeight($this->FontSize);
20594			if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) {
20595				$tmpx = $this->x;
20596			}
20597			// print ordered item
20598			if ($this->rtl) {
20599				$textitem = '.'.$textitem;
20600			} else {
20601				$textitem = $textitem.'.';
20602			}
20603			$lspace += $this->GetStringWidth($textitem);
20604			if ($this->rtl) {
20605				$this->x += $lspace;
20606			} else {
20607				$this->x -= $lspace;
20608			}
20609			$this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
20610		}
20611		$this->x = $tmpx;
20612		$this->lispacer = '^';
20613		// restore colors
20614		$this->SetFillColorArray($bgcolor);
20615		$this->SetDrawColorArray($strokecolor);
20616		$this->SettextColorArray($color);
20617	}
20618
20619	/**
20620	 * Returns current graphic variables as array.
20621	 * @return array of graphic variables
20622	 * @protected
20623	 * @since 4.2.010 (2008-11-14)
20624	 */
20625	protected function getGraphicVars() {
20626		$grapvars = array(
20627			'FontFamily' => $this->FontFamily,
20628			'FontStyle' => $this->FontStyle,
20629			'FontSizePt' => $this->FontSizePt,
20630			'rMargin' => $this->rMargin,
20631			'lMargin' => $this->lMargin,
20632			'cell_padding' => $this->cell_padding,
20633			'cell_margin' => $this->cell_margin,
20634			'LineWidth' => $this->LineWidth,
20635			'linestyleWidth' => $this->linestyleWidth,
20636			'linestyleCap' => $this->linestyleCap,
20637			'linestyleJoin' => $this->linestyleJoin,
20638			'linestyleDash' => $this->linestyleDash,
20639			'textrendermode' => $this->textrendermode,
20640			'textstrokewidth' => $this->textstrokewidth,
20641			'DrawColor' => $this->DrawColor,
20642			'FillColor' => $this->FillColor,
20643			'TextColor' => $this->TextColor,
20644			'ColorFlag' => $this->ColorFlag,
20645			'bgcolor' => $this->bgcolor,
20646			'fgcolor' => $this->fgcolor,
20647			'htmlvspace' => $this->htmlvspace,
20648			'listindent' => $this->listindent,
20649			'listindentlevel' => $this->listindentlevel,
20650			'listnum' => $this->listnum,
20651			'listordered' => $this->listordered,
20652			'listcount' => $this->listcount,
20653			'lispacer' => $this->lispacer,
20654			'cell_height_ratio' => $this->cell_height_ratio,
20655			'font_stretching' => $this->font_stretching,
20656			'font_spacing' => $this->font_spacing,
20657			'alpha' => $this->alpha,
20658			// extended
20659			'lasth' => $this->lasth,
20660			'tMargin' => $this->tMargin,
20661			'bMargin' => $this->bMargin,
20662			'AutoPageBreak' => $this->AutoPageBreak,
20663			'PageBreakTrigger' => $this->PageBreakTrigger,
20664			'x' => $this->x,
20665			'y' => $this->y,
20666			'w' => $this->w,
20667			'h' => $this->h,
20668			'wPt' => $this->wPt,
20669			'hPt' => $this->hPt,
20670			'fwPt' => $this->fwPt,
20671			'fhPt' => $this->fhPt,
20672			'page' => $this->page,
20673			'current_column' => $this->current_column,
20674			'num_columns' => $this->num_columns
20675			);
20676		return $grapvars;
20677	}
20678
20679	/**
20680	 * Set graphic variables.
20681	 * @param $gvars (array) array of graphic variablesto restore
20682	 * @param $extended (boolean) if true restore extended graphic variables
20683	 * @protected
20684	 * @since 4.2.010 (2008-11-14)
20685	 */
20686	protected function setGraphicVars($gvars, $extended=false) {
20687		if ($this->state != 2) {
20688			 return;
20689		}
20690		$this->FontFamily = $gvars['FontFamily'];
20691		$this->FontStyle = $gvars['FontStyle'];
20692		$this->FontSizePt = $gvars['FontSizePt'];
20693		$this->rMargin = $gvars['rMargin'];
20694		$this->lMargin = $gvars['lMargin'];
20695		$this->cell_padding = $gvars['cell_padding'];
20696		$this->cell_margin = $gvars['cell_margin'];
20697		$this->LineWidth = $gvars['LineWidth'];
20698		$this->linestyleWidth = $gvars['linestyleWidth'];
20699		$this->linestyleCap = $gvars['linestyleCap'];
20700		$this->linestyleJoin = $gvars['linestyleJoin'];
20701		$this->linestyleDash = $gvars['linestyleDash'];
20702		$this->textrendermode = $gvars['textrendermode'];
20703		$this->textstrokewidth = $gvars['textstrokewidth'];
20704		$this->DrawColor = $gvars['DrawColor'];
20705		$this->FillColor = $gvars['FillColor'];
20706		$this->TextColor = $gvars['TextColor'];
20707		$this->ColorFlag = $gvars['ColorFlag'];
20708		$this->bgcolor = $gvars['bgcolor'];
20709		$this->fgcolor = $gvars['fgcolor'];
20710		$this->htmlvspace = $gvars['htmlvspace'];
20711		$this->listindent = $gvars['listindent'];
20712		$this->listindentlevel = $gvars['listindentlevel'];
20713		$this->listnum = $gvars['listnum'];
20714		$this->listordered = $gvars['listordered'];
20715		$this->listcount = $gvars['listcount'];
20716		$this->lispacer = $gvars['lispacer'];
20717		$this->cell_height_ratio = $gvars['cell_height_ratio'];
20718		$this->font_stretching = $gvars['font_stretching'];
20719		$this->font_spacing = $gvars['font_spacing'];
20720		$this->alpha = $gvars['alpha'];
20721		if ($extended) {
20722			// restore extended values
20723			$this->lasth = $gvars['lasth'];
20724			$this->tMargin = $gvars['tMargin'];
20725			$this->bMargin = $gvars['bMargin'];
20726			$this->AutoPageBreak = $gvars['AutoPageBreak'];
20727			$this->PageBreakTrigger = $gvars['PageBreakTrigger'];
20728			$this->x = $gvars['x'];
20729			$this->y = $gvars['y'];
20730			$this->w = $gvars['w'];
20731			$this->h = $gvars['h'];
20732			$this->wPt = $gvars['wPt'];
20733			$this->hPt = $gvars['hPt'];
20734			$this->fwPt = $gvars['fwPt'];
20735			$this->fhPt = $gvars['fhPt'];
20736			$this->page = $gvars['page'];
20737			$this->current_column = $gvars['current_column'];
20738			$this->num_columns = $gvars['num_columns'];
20739		}
20740		$this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
20741		if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
20742			$this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
20743		}
20744	}
20745
20746	/**
20747	 * Outputs the "save graphics state" operator 'q'
20748	 * @protected
20749	 */
20750	protected function _outSaveGraphicsState() {
20751		$this->_out('q');
20752	}
20753
20754	/**
20755	 * Outputs the "restore graphics state" operator 'Q'
20756	 * @protected
20757	 */
20758	protected function _outRestoreGraphicsState() {
20759		$this->_out('Q');
20760	}
20761
20762	/**
20763	 * Writes data to a temporary file on filesystem.
20764	 * @param $filename (string) file name
20765	 * @param $data (mixed) data to write on file
20766	 * @param $append (boolean) if true append data, false replace.
20767	 * @since 4.5.000 (2008-12-31)
20768	 * @protected
20769	 */
20770	protected function writeDiskCache($filename, $data, $append=false) {
20771		if ($append) {
20772			$fmode = 'ab+';
20773		} else {
20774			$fmode = 'wb+';
20775		}
20776		$f = @fopen($filename, $fmode);
20777		if (!$f) {
20778			$this->Error('Unable to write cache file: '.$filename);
20779		} else {
20780			fwrite($f, $data);
20781			fclose($f);
20782		}
20783		// update file length (needed for transactions)
20784		if (!isset($this->cache_file_length['_'.$filename])) {
20785			$this->cache_file_length['_'.$filename] = strlen($data);
20786		} else {
20787			$this->cache_file_length['_'.$filename] += strlen($data);
20788		}
20789	}
20790
20791	/**
20792	 * Read data from a temporary file on filesystem.
20793	 * @param $filename (string) file name
20794	 * @return mixed retrieved data
20795	 * @since 4.5.000 (2008-12-31)
20796	 * @protected
20797	 */
20798	protected function readDiskCache($filename) {
20799		return file_get_contents($filename);
20800	}
20801
20802	/**
20803	 * Set buffer content (always append data).
20804	 * @param $data (string) data
20805	 * @protected
20806	 * @since 4.5.000 (2009-01-02)
20807	 */
20808	protected function setBuffer($data) {
20809		$this->bufferlen += strlen($data);
20810		if ($this->diskcache) {
20811			if (!isset($this->buffer) OR TCPDF_STATIC::empty_string($this->buffer)) {
20812				$this->buffer = TCPDF_STATIC::getObjFilename('buf');
20813			}
20814			$this->writeDiskCache($this->buffer, $data, true);
20815		} else {
20816			$this->buffer .= $data;
20817		}
20818	}
20819
20820	/**
20821	 * Replace the buffer content
20822	 * @param $data (string) data
20823	 * @protected
20824	 * @since 5.5.000 (2010-06-22)
20825	 */
20826	protected function replaceBuffer($data) {
20827		$this->bufferlen = strlen($data);
20828		if ($this->diskcache) {
20829			if (!isset($this->buffer) OR TCPDF_STATIC::empty_string($this->buffer)) {
20830				$this->buffer = TCPDF_STATIC::getObjFilename('buf');
20831			}
20832			$this->writeDiskCache($this->buffer, $data, false);
20833		} else {
20834			$this->buffer = $data;
20835		}
20836	}
20837
20838	/**
20839	 * Get buffer content.
20840	 * @return string buffer content
20841	 * @protected
20842	 * @since 4.5.000 (2009-01-02)
20843	 */
20844	protected function getBuffer() {
20845		if ($this->diskcache) {
20846			return $this->readDiskCache($this->buffer);
20847		} else {
20848			return $this->buffer;
20849		}
20850	}
20851
20852	/**
20853	 * Set page buffer content.
20854	 * @param $page (int) page number
20855	 * @param $data (string) page data
20856	 * @param $append (boolean) if true append data, false replace.
20857	 * @protected
20858	 * @since 4.5.000 (2008-12-31)
20859	 */
20860	protected function setPageBuffer($page, $data, $append=false) {
20861		if ($this->diskcache) {
20862			if (!isset($this->pages[$page])) {
20863				$this->pages[$page] = TCPDF_STATIC::getObjFilename('page');
20864			}
20865			$this->writeDiskCache($this->pages[$page], $data, $append);
20866		} else {
20867			if ($append) {
20868				$this->pages[$page] .= $data;
20869			} else {
20870				$this->pages[$page] = $data;
20871			}
20872		}
20873		if ($append AND isset($this->pagelen[$page])) {
20874			$this->pagelen[$page] += strlen($data);
20875		} else {
20876			$this->pagelen[$page] = strlen($data);
20877		}
20878	}
20879
20880	/**
20881	 * Get page buffer content.
20882	 * @param $page (int) page number
20883	 * @return string page buffer content or false in case of error
20884	 * @protected
20885	 * @since 4.5.000 (2008-12-31)
20886	 */
20887	protected function getPageBuffer($page) {
20888		if ($this->diskcache) {
20889			return $this->readDiskCache($this->pages[$page]);
20890		} elseif (isset($this->pages[$page])) {
20891			return $this->pages[$page];
20892		}
20893		return false;
20894	}
20895
20896	/**
20897	 * Set image buffer content.
20898	 * @param $image (string) image key
20899	 * @param $data (array) image data
20900	 * @return int image index number
20901	 * @protected
20902	 * @since 4.5.000 (2008-12-31)
20903	 */
20904	protected function setImageBuffer($image, $data) {
20905		if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) {
20906			$this->imagekeys[$this->numimages] = $image;
20907			$data['i'] = $this->numimages;
20908			++$this->numimages;
20909		}
20910		if ($this->diskcache) {
20911			if (!isset($this->images[$image])) {
20912				$this->images[$image] = TCPDF_STATIC::getObjFilename('img');
20913			}
20914			$this->writeDiskCache($this->images[$image], serialize($data));
20915		} else {
20916			$this->images[$image] = $data;
20917		}
20918		return $data['i'];
20919	}
20920
20921	/**
20922	 * Set image buffer content for a specified sub-key.
20923	 * @param $image (string) image key
20924	 * @param $key (string) image sub-key
20925	 * @param $data (array) image data
20926	 * @protected
20927	 * @since 4.5.000 (2008-12-31)
20928	 */
20929	protected function setImageSubBuffer($image, $key, $data) {
20930		if (!isset($this->images[$image])) {
20931			$this->setImageBuffer($image, array());
20932		}
20933		if ($this->diskcache) {
20934			$tmpimg = $this->getImageBuffer($image);
20935			$tmpimg[$key] = $data;
20936			$this->writeDiskCache($this->images[$image], serialize($tmpimg));
20937		} else {
20938			$this->images[$image][$key] = $data;
20939		}
20940	}
20941
20942	/**
20943	 * Get image buffer content.
20944	 * @param $image (string) image key
20945	 * @return string image buffer content or false in case of error
20946	 * @protected
20947	 * @since 4.5.000 (2008-12-31)
20948	 */
20949	protected function getImageBuffer($image) {
20950		if ($this->diskcache AND isset($this->images[$image])) {
20951			return unserialize($this->readDiskCache($this->images[$image]));
20952		} elseif (isset($this->images[$image])) {
20953			return $this->images[$image];
20954		}
20955		return false;
20956	}
20957
20958	/**
20959	 * Set font buffer content.
20960	 * @param $font (string) font key
20961	 * @param $data (array) font data
20962	 * @protected
20963	 * @since 4.5.000 (2009-01-02)
20964	 */
20965	protected function setFontBuffer($font, $data) {
20966		if ($this->diskcache) {
20967			if (!isset($this->fonts[$font])) {
20968				$this->fonts[$font] = TCPDF_STATIC::getObjFilename('font');
20969			}
20970			$this->writeDiskCache($this->fonts[$font], serialize($data));
20971		} else {
20972			$this->fonts[$font] = $data;
20973		}
20974		if (!in_array($font, $this->fontkeys)) {
20975			$this->fontkeys[] = $font;
20976			// store object ID for current font
20977			++$this->n;
20978			$this->font_obj_ids[$font] = $this->n;
20979			$this->setFontSubBuffer($font, 'n', $this->n);
20980		}
20981	}
20982
20983	/**
20984	 * Set font buffer content.
20985	 * @param $font (string) font key
20986	 * @param $key (string) font sub-key
20987	 * @param $data (array) font data
20988	 * @protected
20989	 * @since 4.5.000 (2009-01-02)
20990	 */
20991	protected function setFontSubBuffer($font, $key, $data) {
20992		if (!isset($this->fonts[$font])) {
20993			$this->setFontBuffer($font, array());
20994		}
20995		if ($this->diskcache) {
20996			$tmpfont = $this->getFontBuffer($font);
20997			$tmpfont[$key] = $data;
20998			$this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
20999		} else {
21000			$this->fonts[$font][$key] = $data;
21001		}
21002	}
21003
21004	/**
21005	 * Get font buffer content.
21006	 * @param $font (string) font key
21007	 * @return string font buffer content or false in case of error
21008	 * @protected
21009	 * @since 4.5.000 (2009-01-02)
21010	 */
21011	protected function getFontBuffer($font) {
21012		if ($this->diskcache AND isset($this->fonts[$font])) {
21013			return unserialize($this->readDiskCache($this->fonts[$font]));
21014		} elseif (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 $frompage (int) number of the source page
21023	 * @param $topage (int) number of the destination page (must be less than $frompage)
21024	 * @return 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 $page (int) page to remove
21195	 * @return 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 $page (int) number of page to copy (0 = current page)
21385	 * @return 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 $page (int) page number where this TOC should be inserted (leave empty for current page).
21456	 * @param $numbersfont (string) set the font for page numbers (please use monospaced font for better alignment).
21457	 * @param $filler (string) string used to fill the space between text and page number.
21458	 * @param $toc_name (string) name to use for TOC bookmark.
21459	 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
21460	 * @param $color (array) 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='', $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 $page (int) page number where this TOC should be inserted (leave empty for current page).
21666	 * @param $toc_name (string) name to use for TOC bookmark.
21667	 * @param $templates (array) array of html templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number.
21668	 * @param $correct_align (boolean) 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 $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
21670	 * @param $color (array) 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='', $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 ($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			unset($this->objcopy);
21830		}
21831	}
21832
21833	/**
21834	 * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction().
21835	 * @param $self (boolean) if true restores current class object to previous state without the need of reassignment via the returned value.
21836	 * @return TCPDF object.
21837	 * @public
21838	 * @since 4.5.029 (2009-03-19)
21839	 */
21840	public function rollbackTransaction($self=false) {
21841		if (isset($this->objcopy)) {
21842			if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
21843				// truncate files to previous values
21844				foreach ($this->objcopy->cache_file_length as $file => $length) {
21845					$file = substr($file, 1);
21846					$handle = fopen($file, 'r+');
21847					ftruncate($handle, $length);
21848				}
21849			}
21850			$this->_destroy(true, true);
21851			if ($self) {
21852				$objvars = get_object_vars($this->objcopy);
21853				foreach ($objvars as $key => $value) {
21854					$this->$key = $value;
21855				}
21856			}
21857			return $this->objcopy;
21858		}
21859		return $this;
21860	}
21861
21862	// --- MULTI COLUMNS METHODS -----------------------
21863
21864	/**
21865	 * Set multiple columns of the same size
21866	 * @param $numcols (int) number of columns (set to zero to disable columns mode)
21867	 * @param $width (int) column width
21868	 * @param $y (int) column starting Y position (leave empty for current Y position)
21869	 * @public
21870	 * @since 4.9.001 (2010-03-28)
21871	 */
21872	public function setEqualColumns($numcols=0, $width=0, $y='') {
21873		$this->columns = array();
21874		if ($numcols < 2) {
21875			$numcols = 0;
21876			$this->columns = array();
21877		} else {
21878			// maximum column width
21879			$maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
21880			if (($width == 0) OR ($width > $maxwidth)) {
21881				$width = $maxwidth;
21882			}
21883			if (TCPDF_STATIC::empty_string($y)) {
21884				$y = $this->y;
21885			}
21886			// space between columns
21887			$space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
21888			// fill the columns array (with, space, starting Y position)
21889			for ($i = 0; $i < $numcols; ++$i) {
21890				$this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
21891			}
21892		}
21893		$this->num_columns = $numcols;
21894		$this->current_column = 0;
21895		$this->column_start_page = $this->page;
21896		$this->selectColumn(0);
21897	}
21898
21899	/**
21900	 * Remove columns and reset page margins.
21901	 * @public
21902	 * @since 5.9.072 (2011-04-26)
21903	 */
21904	public function resetColumns() {
21905		$this->lMargin = $this->original_lMargin;
21906		$this->rMargin = $this->original_rMargin;
21907		$this->setEqualColumns();
21908	}
21909
21910	/**
21911	 * Set columns array.
21912	 * Each column is represented by an array of arrays with the following keys: (w = width, s = space between columns, y = column top position).
21913	 * @param $columns (array)
21914	 * @public
21915	 * @since 4.9.001 (2010-03-28)
21916	 */
21917	public function setColumnsArray($columns) {
21918		$this->columns = $columns;
21919		$this->num_columns = count($columns);
21920		$this->current_column = 0;
21921		$this->column_start_page = $this->page;
21922		$this->selectColumn(0);
21923	}
21924
21925	/**
21926	 * Set position at a given column
21927	 * @param $col (int) column number (from 0 to getNumberOfColumns()-1); empty string = current column.
21928	 * @public
21929	 * @since 4.9.001 (2010-03-28)
21930	 */
21931	public function selectColumn($col='') {
21932		if (is_string($col)) {
21933			$col = $this->current_column;
21934		} elseif ($col >= $this->num_columns) {
21935			$col = 0;
21936		}
21937		$xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
21938		$enable_thead = false;
21939		if ($this->num_columns > 1) {
21940			if ($col != $this->current_column) {
21941				// move Y pointer at the top of the column
21942				if ($this->column_start_page == $this->page) {
21943					$this->y = $this->columns[$col]['y'];
21944				} else {
21945					$this->y = $this->tMargin;
21946				}
21947				// Avoid to write table headers more than once
21948				if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) {
21949					$enable_thead = true;
21950					$this->maxselcol['page'] = $this->page;
21951					$this->maxselcol['column'] = $col;
21952				}
21953			}
21954			$xshift = $this->colxshift;
21955			// set X position of the current column by case
21956			$listindent = ($this->listindentlevel * $this->listindent);
21957			// calculate column X position
21958			$colpos = 0;
21959			for ($i = 0; $i < $col; ++$i) {
21960				$colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']);
21961			}
21962			if ($this->rtl) {
21963				$x = $this->w - $this->original_rMargin - $colpos;
21964				$this->rMargin = ($this->w - $x + $listindent);
21965				$this->lMargin = ($x - $this->columns[$col]['w']);
21966				$this->x = $x - $listindent;
21967			} else {
21968				$x = $this->original_lMargin + $colpos;
21969				$this->lMargin = ($x + $listindent);
21970				$this->rMargin = ($this->w - $x - $this->columns[$col]['w']);
21971				$this->x = $x + $listindent;
21972			}
21973			$this->columns[$col]['x'] = $x;
21974		}
21975		$this->current_column = $col;
21976		// fix for HTML mode
21977		$this->newline = true;
21978		// print HTML table header (if any)
21979		if ((!TCPDF_STATIC::empty_string($this->thead)) AND (!$this->inthead)) {
21980			if ($enable_thead) {
21981				// print table header
21982				$this->writeHTML($this->thead, false, false, false, false, '');
21983				$this->y += $xshift['s']['V'];
21984				// store end of header position
21985				if (!isset($this->columns[$col]['th'])) {
21986					$this->columns[$col]['th'] = array();
21987				}
21988				$this->columns[$col]['th']['\''.$this->page.'\''] = $this->y;
21989				$this->lasth = 0;
21990			} elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) {
21991				$this->y = $this->columns[$col]['th']['\''.$this->page.'\''];
21992			}
21993		}
21994		// account for an html table cell over multiple columns
21995		if ($this->rtl) {
21996			$this->rMargin += $xshift['x'];
21997			$this->x -= ($xshift['x'] + $xshift['p']['R']);
21998		} else {
21999			$this->lMargin += $xshift['x'];
22000			$this->x += $xshift['x'] + $xshift['p']['L'];
22001		}
22002	}
22003
22004	/**
22005	 * Return the current column number
22006	 * @return int current column number
22007	 * @public
22008	 * @since 5.5.011 (2010-07-08)
22009	 */
22010	public function getColumn() {
22011		return $this->current_column;
22012	}
22013
22014	/**
22015	 * Return the current number of columns.
22016	 * @return int number of columns
22017	 * @public
22018	 * @since 5.8.018 (2010-08-25)
22019	 */
22020	public function getNumberOfColumns() {
22021		return $this->num_columns;
22022	}
22023
22024	/**
22025	 * Set Text rendering mode.
22026	 * @param $stroke (int) outline size in user units (0 = disable).
22027	 * @param $fill (boolean) if true fills the text (default).
22028	 * @param $clip (boolean) if true activate clipping mode
22029	 * @public
22030	 * @since 4.9.008 (2009-04-02)
22031	 */
22032	public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
22033		// Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
22034		// convert text rendering parameters
22035		if ($stroke < 0) {
22036			$stroke = 0;
22037		}
22038		if ($fill === true) {
22039			if ($stroke > 0) {
22040				if ($clip === true) {
22041					// Fill, then stroke text and add to path for clipping
22042					$textrendermode = 6;
22043				} else {
22044					// Fill, then stroke text
22045					$textrendermode = 2;
22046				}
22047				$textstrokewidth = $stroke;
22048			} else {
22049				if ($clip === true) {
22050					// Fill text and add to path for clipping
22051					$textrendermode = 4;
22052				} else {
22053					// Fill text
22054					$textrendermode = 0;
22055				}
22056			}
22057		} else {
22058			if ($stroke > 0) {
22059				if ($clip === true) {
22060					// Stroke text and add to path for clipping
22061					$textrendermode = 5;
22062				} else {
22063					// Stroke text
22064					$textrendermode = 1;
22065				}
22066				$textstrokewidth = $stroke;
22067			} else {
22068				if ($clip === true) {
22069					// Add text to path for clipping
22070					$textrendermode = 7;
22071				} else {
22072					// Neither fill nor stroke text (invisible)
22073					$textrendermode = 3;
22074				}
22075			}
22076		}
22077		$this->textrendermode = $textrendermode;
22078		$this->textstrokewidth = $stroke;
22079	}
22080
22081	/**
22082	 * Set parameters for drop shadow effect for text.
22083	 * @param $params (array) 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.
22084	 * @since 5.9.174 (2012-07-25)
22085	 * @public
22086	*/
22087	public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) {
22088		if (isset($params['enabled'])) {
22089			$this->txtshadow['enabled'] = $params['enabled']?true:false;
22090		} else {
22091			$this->txtshadow['enabled'] = false;
22092		}
22093		if (isset($params['depth_w'])) {
22094			$this->txtshadow['depth_w'] = floatval($params['depth_w']);
22095		} else {
22096			$this->txtshadow['depth_w'] = 0;
22097		}
22098		if (isset($params['depth_h'])) {
22099			$this->txtshadow['depth_h'] = floatval($params['depth_h']);
22100		} else {
22101			$this->txtshadow['depth_h'] = 0;
22102		}
22103		if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) {
22104			$this->txtshadow['color'] = $params['color'];
22105		} else {
22106			$this->txtshadow['color'] = $this->strokecolor;
22107		}
22108		if (isset($params['opacity'])) {
22109			$this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity'])));
22110		} else {
22111			$this->txtshadow['opacity'] = 1;
22112		}
22113		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'))) {
22114			$this->txtshadow['blend_mode'] = $params['blend_mode'];
22115		} else {
22116			$this->txtshadow['blend_mode'] = 'Normal';
22117		}
22118		if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) {
22119			$this->txtshadow['enabled'] = false;
22120		}
22121	}
22122
22123	/**
22124	 * Return the text shadow parameters array.
22125	 * @return Array of parameters.
22126	 * @since 5.9.174 (2012-07-25)
22127	 * @public
22128	 */
22129	public function getTextShadow() {
22130		return $this->txtshadow;
22131	}
22132
22133	/**
22134	 * Returns an array of chars containing soft hyphens.
22135	 * @param $word (array) array of chars
22136	 * @param $patterns (array) Array of hypenation patterns.
22137	 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm.
22138	 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
22139	 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
22140	 * @param $charmin (int) Minimum word length to apply the hyphenation algoritm.
22141	 * @param $charmax (int) Maximum length of broken piece of word.
22142	 * @return array text with soft hyphens
22143	 * @author Nicola Asuni
22144	 * @since 4.9.012 (2010-04-12)
22145	 * @protected
22146	 */
22147	protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22148		$hyphenword = array(); // hyphens positions
22149		$numchars = count($word);
22150		if ($numchars <= $charmin) {
22151			return $word;
22152		}
22153		$word_string = TCPDF_FONTS::UTF8ArrSubString($word, '', '', $this->isunicode);
22154		// some words will be returned as-is
22155		$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})(\]?)$/';
22156		if (preg_match($pattern, $word_string) > 0) {
22157			// email
22158			return $word;
22159		}
22160		$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})(\]?)$/';
22161		if (preg_match($pattern, $word_string) > 0) {
22162			// URL
22163			return $word;
22164		}
22165		if (isset($dictionary[$word_string])) {
22166			return TCPDF_FONTS::UTF8StringToArray($dictionary[$word_string], $this->isunicode, $this->CurrentFont);
22167		}
22168		// surround word with '_' characters
22169		$tmpword = array_merge(array(46), $word, array(46));
22170		$tmpnumchars = $numchars + 2;
22171		$maxpos = $tmpnumchars - $charmin;
22172		for ($pos = 0; $pos < $maxpos; ++$pos) {
22173			$imax = min(($tmpnumchars - $pos), $charmax);
22174			for ($i = $charmin; $i <= $imax; ++$i) {
22175				$subword = strtolower(TCPDF_FONTS::UTF8ArrSubString($tmpword, $pos, ($pos + $i), $this->isunicode));
22176				if (isset($patterns[$subword])) {
22177					$pattern = TCPDF_FONTS::UTF8StringToArray($patterns[$subword], $this->isunicode, $this->CurrentFont);
22178					$pattern_length = count($pattern);
22179					$digits = 1;
22180					for ($j = 0; $j < $pattern_length; ++$j) {
22181						// check if $pattern[$j] is a number = hyphenation level (only numbers from 1 to 5 are valid)
22182						if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
22183							if ($j == 0) {
22184								$zero = $pos - 1;
22185							} else {
22186								$zero = $pos + $j - $digits;
22187							}
22188							// get hyphenation level
22189							$level = ($pattern[$j] - 48);
22190							// if two levels from two different patterns match at the same point, the higher one is selected.
22191							if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] < $level)) {
22192								$hyphenword[$zero] = $level;
22193							}
22194							++$digits;
22195						}
22196					}
22197				}
22198			}
22199		}
22200		$inserted = 0;
22201		$maxpos = $numchars - $rightmin;
22202		for ($i = $leftmin; $i <= $maxpos; ++$i) {
22203			// only odd levels indicate allowed hyphenation points
22204			if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
22205				// 173 = soft hyphen character
22206				array_splice($word, $i + $inserted, 0, 173);
22207				++$inserted;
22208			}
22209		}
22210		return $word;
22211	}
22212
22213	/**
22214	 * Returns text with soft hyphens.
22215	 * @param $text (string) text to process
22216	 * @param $patterns (mixed) 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/
22217	 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm.
22218	 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
22219	 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
22220	 * @param $charmin (int) Minimum word length to apply the hyphenation algoritm.
22221	 * @param $charmax (int) Maximum length of broken piece of word.
22222	 * @return array text with soft hyphens
22223	 * @author Nicola Asuni
22224	 * @since 4.9.012 (2010-04-12)
22225	 * @public
22226	 */
22227	public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22228		$text = $this->unhtmlentities($text);
22229		$word = array(); // last word
22230		$txtarr = array(); // text to be returned
22231		$intag = false; // true if we are inside an HTML tag
22232		$skip = false; // true to skip hyphenation
22233		if (!is_array($patterns)) {
22234			$patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patterns);
22235		}
22236		// get array of characters
22237		$unichars = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
22238		// for each char
22239		foreach ($unichars as $char) {
22240			if ((!$intag) AND (!$skip) AND TCPDF_FONT_DATA::$uni_type[$char] == 'L') {
22241				// letter character
22242				$word[] = $char;
22243			} else {
22244				// other type of character
22245				if (!TCPDF_STATIC::empty_string($word)) {
22246					// hypenate the word
22247					$txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22248					$word = array();
22249				}
22250				$txtarr[] = $char;
22251				if (chr($char) == '<') {
22252					// we are inside an HTML tag
22253					$intag = true;
22254				} elseif ($intag AND (chr($char) == '>')) {
22255					// end of HTML tag
22256					$intag = false;
22257					// check for style tag
22258					$expected = array(115, 116, 121, 108, 101); // = 'style'
22259					$current = array_slice($txtarr, -6, 5); // last 5 chars
22260					$compare = array_diff($expected, $current);
22261					if (empty($compare)) {
22262						// check if it is a closing tag
22263						$expected = array(47); // = '/'
22264						$current = array_slice($txtarr, -7, 1);
22265						$compare = array_diff($expected, $current);
22266						if (empty($compare)) {
22267							// closing style tag
22268							$skip = false;
22269						} else {
22270							// opening style tag
22271							$skip = true;
22272						}
22273					}
22274				}
22275			}
22276		}
22277		if (!TCPDF_STATIC::empty_string($word)) {
22278			// hypenate the word
22279			$txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22280		}
22281		// convert char array to string and return
22282		return TCPDF_FONTS::UTF8ArrSubString($txtarr, '', '', $this->isunicode);
22283	}
22284
22285	/**
22286	 * Enable/disable rasterization of vector images using ImageMagick library.
22287	 * @param $mode (boolean) if true enable rasterization, false otherwise.
22288	 * @public
22289	 * @since 5.0.000 (2010-04-27)
22290	 */
22291	public function setRasterizeVectorImages($mode) {
22292		$this->rasterize_vector_images = $mode;
22293	}
22294
22295	/**
22296	 * Enable or disable default option for font subsetting.
22297	 * @param $enable (boolean) if true enable font subsetting by default.
22298	 * @author Nicola Asuni
22299	 * @public
22300	 * @since 5.3.002 (2010-06-07)
22301	 */
22302	public function setFontSubsetting($enable=true) {
22303		if ($this->pdfa_mode) {
22304			$this->font_subsetting = false;
22305		} else {
22306			$this->font_subsetting = $enable ? true : false;
22307		}
22308	}
22309
22310	/**
22311	 * Return the default option for font subsetting.
22312	 * @return boolean default font subsetting state.
22313	 * @author Nicola Asuni
22314	 * @public
22315	 * @since 5.3.002 (2010-06-07)
22316	 */
22317	public function getFontSubsetting() {
22318		return $this->font_subsetting;
22319	}
22320
22321	/**
22322	 * Left trim the input string
22323	 * @param $str (string) string to trim
22324	 * @param $replace (string) string that replace spaces.
22325	 * @return left trimmed string
22326	 * @author Nicola Asuni
22327	 * @public
22328	 * @since 5.8.000 (2010-08-11)
22329	 */
22330	public function stringLeftTrim($str, $replace='') {
22331		return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str);
22332	}
22333
22334	/**
22335	 * Right trim the input string
22336	 * @param $str (string) string to trim
22337	 * @param $replace (string) string that replace spaces.
22338	 * @return right trimmed string
22339	 * @author Nicola Asuni
22340	 * @public
22341	 * @since 5.8.000 (2010-08-11)
22342	 */
22343	public function stringRightTrim($str, $replace='') {
22344		return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str);
22345	}
22346
22347	/**
22348	 * Trim the input string
22349	 * @param $str (string) string to trim
22350	 * @param $replace (string) string that replace spaces.
22351	 * @return trimmed string
22352	 * @author Nicola Asuni
22353	 * @public
22354	 * @since 5.8.000 (2010-08-11)
22355	 */
22356	public function stringTrim($str, $replace='') {
22357		$str = $this->stringLeftTrim($str, $replace);
22358		$str = $this->stringRightTrim($str, $replace);
22359		return $str;
22360	}
22361
22362	/**
22363	 * Return true if the current font is unicode type.
22364	 * @return true for unicode font, false otherwise.
22365	 * @author Nicola Asuni
22366	 * @public
22367	 * @since 5.8.002 (2010-08-14)
22368	 */
22369	public function isUnicodeFont() {
22370		return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0'));
22371	}
22372
22373	/**
22374	 * Return normalized font name
22375	 * @param $fontfamily (string) property string containing font family names
22376	 * @return string normalized font name
22377	 * @author Nicola Asuni
22378	 * @public
22379	 * @since 5.8.004 (2010-08-17)
22380	 */
22381	public function getFontFamilyName($fontfamily) {
22382		// remove spaces and symbols
22383		$fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily));
22384		// extract all font names
22385		$fontslist = preg_split('/[,]/', $fontfamily);
22386		// find first valid font name
22387		foreach ($fontslist as $font) {
22388			// replace font variations
22389			$font = preg_replace('/regular$/', '', $font);
22390			$font = preg_replace('/italic$/', 'I', $font);
22391			$font = preg_replace('/oblique$/', 'I', $font);
22392			$font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
22393			// replace common family names and core fonts
22394			$pattern = array();
22395			$replacement = array();
22396			$pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
22397			$replacement[] = 'times';
22398			$pattern[] = '/^sansserif/';
22399			$replacement[] = 'helvetica';
22400			$pattern[] = '/^monospace/';
22401			$replacement[] = 'courier';
22402			$font = preg_replace($pattern, $replacement, $font);
22403			if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) {
22404				return $font;
22405			}
22406		}
22407		// return current font as default
22408		return $this->CurrentFont['fontkey'];
22409	}
22410
22411	/**
22412	 * Start a new XObject Template.
22413	 * 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).
22414	 * 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.
22415	 * Note: X,Y coordinates will be reset to 0,0.
22416	 * @param $w (int) Template width in user units (empty string or zero = page width less margins).
22417	 * @param $h (int) Template height in user units (empty string or zero = page height less margins).
22418	 * @param $group (mixed) 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).
22419	 * @return int the XObject Template ID in case of success or false in case of error.
22420	 * @author Nicola Asuni
22421	 * @public
22422	 * @since 5.8.017 (2010-08-24)
22423	 * @see endTemplate(), printTemplate()
22424	 */
22425	public function startTemplate($w=0, $h=0, $group=false) {
22426		if ($this->inxobj) {
22427			// we are already inside an XObject template
22428			return false;
22429		}
22430		$this->inxobj = true;
22431		++$this->n;
22432		// XObject ID
22433		$this->xobjid = 'XT'.$this->n;
22434		// object ID
22435		$this->xobjects[$this->xobjid] = array('n' => $this->n);
22436		// store current graphic state
22437		$this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars();
22438		// initialize data
22439		$this->xobjects[$this->xobjid]['intmrk'] = 0;
22440		$this->xobjects[$this->xobjid]['transfmrk'] = array();
22441		$this->xobjects[$this->xobjid]['outdata'] = '';
22442		$this->xobjects[$this->xobjid]['xobjects'] = array();
22443		$this->xobjects[$this->xobjid]['images'] = array();
22444		$this->xobjects[$this->xobjid]['fonts'] = array();
22445		$this->xobjects[$this->xobjid]['annotations'] = array();
22446		$this->xobjects[$this->xobjid]['extgstates'] = array();
22447		$this->xobjects[$this->xobjid]['gradients'] = array();
22448		$this->xobjects[$this->xobjid]['spot_colors'] = array();
22449		// set new environment
22450		$this->num_columns = 1;
22451		$this->current_column = 0;
22452		$this->SetAutoPageBreak(false);
22453		if (($w === '') OR ($w <= 0)) {
22454			$w = $this->w - $this->lMargin - $this->rMargin;
22455		}
22456		if (($h === '') OR ($h <= 0)) {
22457			$h = $this->h - $this->tMargin - $this->bMargin;
22458		}
22459		$this->xobjects[$this->xobjid]['x'] = 0;
22460		$this->xobjects[$this->xobjid]['y'] = 0;
22461		$this->xobjects[$this->xobjid]['w'] = $w;
22462		$this->xobjects[$this->xobjid]['h'] = $h;
22463		$this->w = $w;
22464		$this->h = $h;
22465		$this->wPt = $this->w * $this->k;
22466		$this->hPt = $this->h * $this->k;
22467		$this->fwPt = $this->wPt;
22468		$this->fhPt = $this->hPt;
22469		$this->x = 0;
22470		$this->y = 0;
22471		$this->lMargin = 0;
22472		$this->rMargin = 0;
22473		$this->tMargin = 0;
22474		$this->bMargin = 0;
22475		// set group mode
22476		$this->xobjects[$this->xobjid]['group'] = $group;
22477		return $this->xobjid;
22478	}
22479
22480	/**
22481	 * End the current XObject Template started with startTemplate() and restore the previous graphic state.
22482	 * 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).
22483	 * 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.
22484	 * @return int the XObject Template ID in case of success or false in case of error.
22485	 * @author Nicola Asuni
22486	 * @public
22487	 * @since 5.8.017 (2010-08-24)
22488	 * @see startTemplate(), printTemplate()
22489	 */
22490	public function endTemplate() {
22491		if (!$this->inxobj) {
22492			// we are not inside a template
22493			return false;
22494		}
22495		$this->inxobj = false;
22496		// restore previous graphic state
22497		$this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true);
22498		return $this->xobjid;
22499	}
22500
22501	/**
22502	 * Print an XObject Template.
22503	 * You can print an XObject Template inside the currently opened Template.
22504	 * 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).
22505	 * 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.
22506	 * @param $id (string) The ID of XObject Template to print.
22507	 * @param $x (int) X position in user units (empty string = current x position)
22508	 * @param $y (int) Y position in user units (empty string = current y position)
22509	 * @param $w (int) Width in user units (zero = remaining page width)
22510	 * @param $h (int) Height in user units (zero = remaining page height)
22511	 * @param $align (string) 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>
22512	 * @param $palign (string) 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>
22513	 * @param $fitonpage (boolean) If true the template is resized to not exceed page dimensions.
22514	 * @author Nicola Asuni
22515	 * @public
22516	 * @since 5.8.017 (2010-08-24)
22517	 * @see startTemplate(), endTemplate()
22518	 */
22519	public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
22520		if ($this->state != 2) {
22521			 return;
22522		}
22523		if (!isset($this->xobjects[$id])) {
22524			$this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
22525		}
22526		if ($this->inxobj) {
22527			if ($id == $this->xobjid) {
22528				// close current template
22529				$this->endTemplate();
22530			} else {
22531				// use the template as resource for the template currently opened
22532				$this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id];
22533			}
22534		}
22535		// set default values
22536		if ($x === '') {
22537			$x = $this->x;
22538		}
22539		if ($y === '') {
22540			$y = $this->y;
22541		}
22542		// check page for no-write regions and adapt page margins if necessary
22543		list($x, $y) = $this->checkPageRegions($h, $x, $y);
22544		$ow = $this->xobjects[$id]['w'];
22545		if ($ow <= 0) {
22546			$ow = 1;
22547		}
22548		$oh = $this->xobjects[$id]['h'];
22549		if ($oh <= 0) {
22550			$oh = 1;
22551		}
22552		// calculate template width and height on document
22553		if (($w <= 0) AND ($h <= 0)) {
22554			$w = $ow;
22555			$h = $oh;
22556		} elseif ($w <= 0) {
22557			$w = $h * $ow / $oh;
22558		} elseif ($h <= 0) {
22559			$h = $w * $oh / $ow;
22560		}
22561		// fit the template on available space
22562		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22563		// set page alignment
22564		$rb_y = $y + $h;
22565		// set alignment
22566		if ($this->rtl) {
22567			if ($palign == 'L') {
22568				$xt = $this->lMargin;
22569			} elseif ($palign == 'C') {
22570				$xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22571			} elseif ($palign == 'R') {
22572				$xt = $this->w - $this->rMargin - $w;
22573			} else {
22574				$xt = $x - $w;
22575			}
22576			$rb_x = $xt;
22577		} else {
22578			if ($palign == 'L') {
22579				$xt = $this->lMargin;
22580			} elseif ($palign == 'C') {
22581				$xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22582			} elseif ($palign == 'R') {
22583				$xt = $this->w - $this->rMargin - $w;
22584			} else {
22585				$xt = $x;
22586			}
22587			$rb_x = $xt + $w;
22588		}
22589		// print XObject Template + Transformation matrix
22590		$this->StartTransform();
22591		// translate and scale
22592		$sx = ($w / $ow);
22593		$sy = ($h / $oh);
22594		$tm = array();
22595		$tm[0] = $sx;
22596		$tm[1] = 0;
22597		$tm[2] = 0;
22598		$tm[3] = $sy;
22599		$tm[4] = $xt * $this->k;
22600		$tm[5] = ($this->h - $h - $y) * $this->k;
22601		$this->Transform($tm);
22602		// set object
22603		$this->_out('/'.$id.' Do');
22604		$this->StopTransform();
22605		// add annotations
22606		if (!empty($this->xobjects[$id]['annotations'])) {
22607			foreach ($this->xobjects[$id]['annotations'] as $annot) {
22608				// transform original coordinates
22609				$coordlt = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k)));
22610				$ax = ($coordlt[4] / $this->k);
22611				$ay = ($this->h - $h - ($coordlt[5] / $this->k));
22612				$coordrb = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k)));
22613				$aw = ($coordrb[4] / $this->k) - $ax;
22614				$ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay;
22615				$this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
22616			}
22617		}
22618		// set pointer to align the next text/objects
22619		switch($align) {
22620			case 'T': {
22621				$this->y = $y;
22622				$this->x = $rb_x;
22623				break;
22624			}
22625			case 'M': {
22626				$this->y = $y + round($h/2);
22627				$this->x = $rb_x;
22628				break;
22629			}
22630			case 'B': {
22631				$this->y = $rb_y;
22632				$this->x = $rb_x;
22633				break;
22634			}
22635			case 'N': {
22636				$this->SetY($rb_y);
22637				break;
22638			}
22639			default:{
22640				break;
22641			}
22642		}
22643	}
22644
22645	/**
22646	 * Set the percentage of character stretching.
22647	 * @param $perc (int) percentage of stretching (100 = no stretching)
22648	 * @author Nicola Asuni
22649	 * @public
22650	 * @since 5.9.000 (2010-09-29)
22651	 */
22652	public function setFontStretching($perc=100) {
22653		$this->font_stretching = $perc;
22654	}
22655
22656	/**
22657	 * Get the percentage of character stretching.
22658	 * @return float stretching value
22659	 * @author Nicola Asuni
22660	 * @public
22661	 * @since 5.9.000 (2010-09-29)
22662	 */
22663	public function getFontStretching() {
22664		return $this->font_stretching;
22665	}
22666
22667	/**
22668	 * Set the amount to increase or decrease the space between characters in a text.
22669	 * @param $spacing (float) amount to increase or decrease the space between characters in a text (0 = default spacing)
22670	 * @author Nicola Asuni
22671	 * @public
22672	 * @since 5.9.000 (2010-09-29)
22673	 */
22674	public function setFontSpacing($spacing=0) {
22675		$this->font_spacing = $spacing;
22676	}
22677
22678	/**
22679	 * Get the amount to increase or decrease the space between characters in a text.
22680	 * @return int font spacing (tracking) value
22681	 * @author Nicola Asuni
22682	 * @public
22683	 * @since 5.9.000 (2010-09-29)
22684	 */
22685	public function getFontSpacing() {
22686		return $this->font_spacing;
22687	}
22688
22689	/**
22690	 * Return an array of no-write page regions
22691	 * @return array of no-write page regions
22692	 * @author Nicola Asuni
22693	 * @public
22694	 * @since 5.9.003 (2010-10-13)
22695	 * @see setPageRegions(), addPageRegion()
22696	 */
22697	public function getPageRegions() {
22698		return $this->page_regions;
22699	}
22700
22701	/**
22702	 * Set no-write regions on page.
22703	 * 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.
22704	 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22705	 * You can set multiple regions for the same page.
22706	 * @param $regions (array) 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.
22707	 * @author Nicola Asuni
22708	 * @public
22709	 * @since 5.9.003 (2010-10-13)
22710	 * @see addPageRegion(), getPageRegions()
22711	 */
22712	public function setPageRegions($regions=array()) {
22713		// empty current regions array
22714		$this->page_regions = array();
22715		// add regions
22716		foreach ($regions as $data) {
22717			$this->addPageRegion($data);
22718		}
22719	}
22720
22721	/**
22722	 * Add a single no-write region on selected page.
22723	 * 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.
22724	 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22725	 * You can set multiple regions for the same page.
22726	 * @param $region (array) 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).
22727	 * @author Nicola Asuni
22728	 * @public
22729	 * @since 5.9.003 (2010-10-13)
22730	 * @see setPageRegions(), getPageRegions()
22731	 */
22732	public function addPageRegion($region) {
22733		if (!isset($region['page']) OR empty($region['page'])) {
22734			$region['page'] = $this->page;
22735		}
22736		if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
22737			AND isset($region['yt'])  AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
22738			AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
22739			$this->page_regions[] = $region;
22740		}
22741	}
22742
22743	/**
22744	 * Remove a single no-write region.
22745	 * @param $key (int) region key
22746	 * @author Nicola Asuni
22747	 * @public
22748	 * @since 5.9.003 (2010-10-13)
22749	 * @see setPageRegions(), getPageRegions()
22750	 */
22751	public function removePageRegion($key) {
22752		if (isset($this->page_regions[$key])) {
22753			unset($this->page_regions[$key]);
22754		}
22755	}
22756
22757	/**
22758	 * Check page for no-write regions and adapt current coordinates and page margins if necessary.
22759	 * 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.
22760	 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22761	 * @param $h (float) height of the text/image/object to print in user units
22762	 * @param $x (float) current X coordinate in user units
22763	 * @param $y (float) current Y coordinate in user units
22764	 * @return array($x, $y)
22765	 * @author Nicola Asuni
22766	 * @protected
22767	 * @since 5.9.003 (2010-10-13)
22768	 */
22769	protected function checkPageRegions($h, $x, $y) {
22770		// set default values
22771		if ($x === '') {
22772			$x = $this->x;
22773		}
22774		if ($y === '') {
22775			$y = $this->y;
22776		}
22777		if (!$this->check_page_regions OR empty($this->page_regions)) {
22778			// no page regions defined
22779			return array($x, $y);
22780		}
22781		if (empty($h)) {
22782			$h = $this->getCellHeight($this->FontSize);
22783		}
22784		// check for page break
22785		if ($this->checkPageBreak($h, $y)) {
22786			// the content will be printed on a new page
22787			$x = $this->x;
22788			$y = $this->y;
22789		}
22790		if ($this->num_columns > 1) {
22791			if ($this->rtl) {
22792				$this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22793			} else {
22794				$this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22795			}
22796		} else {
22797			if ($this->rtl) {
22798				$this->lMargin = max($this->clMargin, $this->original_lMargin);
22799			} else {
22800				$this->rMargin = max($this->crMargin, $this->original_rMargin);
22801			}
22802		}
22803		// adjust coordinates and page margins
22804		foreach ($this->page_regions as $regid => $regdata) {
22805			if ($regdata['page'] == $this->page) {
22806				// check region boundaries
22807				if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
22808					// Y is inside the region
22809					$minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
22810					$yt = max($y, $regdata['yt']);
22811					$yb = min(($yt + $h), $regdata['yb']);
22812					$xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt'];
22813					$xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt'];
22814					if ($regdata['side'] == 'L') { // left side
22815						$new_margin = max($xt, $xb);
22816						if ($this->lMargin < $new_margin) {
22817							if ($this->rtl) {
22818								// adjust left page margin
22819								$this->lMargin = max(0, $new_margin);
22820							}
22821							if ($x < $new_margin) {
22822								// adjust x position
22823								$x = $new_margin;
22824								if ($new_margin > ($this->w - $this->rMargin)) {
22825									// adjust y position
22826									$y = $regdata['yb'] - $h;
22827								}
22828							}
22829						}
22830					} elseif ($regdata['side'] == 'R') { // right side
22831						$new_margin = min($xt, $xb);
22832						if (($this->w - $this->rMargin) > $new_margin) {
22833							if (!$this->rtl) {
22834								// adjust right page margin
22835								$this->rMargin = max(0, ($this->w - $new_margin));
22836							}
22837							if ($x > $new_margin) {
22838								// adjust x position
22839								$x = $new_margin;
22840								if ($new_margin > $this->lMargin) {
22841									// adjust y position
22842									$y = $regdata['yb'] - $h;
22843								}
22844							}
22845						}
22846					}
22847				}
22848			}
22849		}
22850		return array($x, $y);
22851	}
22852
22853	// --- SVG METHODS ---------------------------------------------------------
22854
22855	/**
22856	 * Embedd a Scalable Vector Graphics (SVG) image.
22857	 * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
22858	 * @param $file (string) Name of the SVG file or a '@' character followed by the SVG data string.
22859	 * @param $x (float) Abscissa of the upper-left corner.
22860	 * @param $y (float) Ordinate of the upper-left corner.
22861	 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
22862	 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
22863	 * @param $link (mixed) URL or identifier returned by AddLink().
22864	 * @param $align (string) 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.
22865	 * @param $palign (string) 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>
22866	 * @param $border (mixed) 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)))
22867	 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
22868	 * @author Nicola Asuni
22869	 * @since 5.0.000 (2010-05-02)
22870	 * @public
22871	 */
22872	public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
22873		if ($this->state != 2) {
22874			 return;
22875		}
22876		// reseet SVG vars
22877		$this->svggradients = array();
22878		$this->svggradientid = 0;
22879		$this->svgdefsmode = false;
22880		$this->svgdefs = array();
22881		$this->svgclipmode = false;
22882		$this->svgclippaths = array();
22883		$this->svgcliptm = array();
22884		$this->svgclipid = 0;
22885		$this->svgtext = '';
22886		$this->svgtextmode = array();
22887		if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
22888			// convert SVG to raster image using GD or ImageMagick libraries
22889			return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22890		}
22891		if ($file[0] === '@') { // image from string
22892			$this->svgdir = '';
22893			$svgdata = substr($file, 1);
22894		} else { // SVG file
22895			$this->svgdir = dirname($file);
22896			$svgdata = TCPDF_STATIC::fileGetContents($file);
22897		}
22898		if ($svgdata === FALSE) {
22899			$this->Error('SVG file not found: '.$file);
22900		}
22901		if ($x === '') {
22902			$x = $this->x;
22903		}
22904		if ($y === '') {
22905			$y = $this->y;
22906		}
22907		// check page for no-write regions and adapt page margins if necessary
22908		list($x, $y) = $this->checkPageRegions($h, $x, $y);
22909		$k = $this->k;
22910		$ox = 0;
22911		$oy = 0;
22912		$ow = $w;
22913		$oh = $h;
22914		$aspect_ratio_align = 'xMidYMid';
22915		$aspect_ratio_ms = 'meet';
22916		$regs = array();
22917		// get original image width and height
22918		preg_match('/<svg([^\>]*)>/si', $svgdata, $regs);
22919		if (isset($regs[1]) AND !empty($regs[1])) {
22920			$tmp = array();
22921			if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22922				$ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22923			}
22924			$tmp = array();
22925			if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22926				$oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22927			}
22928			$tmp = array();
22929			if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22930				$ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22931			}
22932			$tmp = array();
22933			if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22934				$oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22935			}
22936			$tmp = array();
22937			$view_box = array();
22938			if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
22939				if (count($tmp) == 5) {
22940					array_shift($tmp);
22941					foreach ($tmp as $key => $val) {
22942						$view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
22943					}
22944					$ox = $view_box[0];
22945					$oy = $view_box[1];
22946				}
22947				// get aspect ratio
22948				$tmp = array();
22949				if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22950					$aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
22951					switch (count($aspect_ratio)) {
22952						case 3: {
22953							$aspect_ratio_align = $aspect_ratio[1];
22954							$aspect_ratio_ms = $aspect_ratio[2];
22955							break;
22956						}
22957						case 2: {
22958							$aspect_ratio_align = $aspect_ratio[0];
22959							$aspect_ratio_ms = $aspect_ratio[1];
22960							break;
22961						}
22962						case 1: {
22963							$aspect_ratio_align = $aspect_ratio[0];
22964							$aspect_ratio_ms = 'meet';
22965							break;
22966						}
22967					}
22968				}
22969			}
22970		}
22971		if ($ow <= 0) {
22972			$ow = 1;
22973		}
22974		if ($oh <= 0) {
22975			$oh = 1;
22976		}
22977		// calculate image width and height on document
22978		if (($w <= 0) AND ($h <= 0)) {
22979			// convert image size to document unit
22980			$w = $ow;
22981			$h = $oh;
22982		} elseif ($w <= 0) {
22983			$w = $h * $ow / $oh;
22984		} elseif ($h <= 0) {
22985			$h = $w * $oh / $ow;
22986		}
22987		// fit the image on available space
22988		list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22989		if ($this->rasterize_vector_images) {
22990			// convert SVG to raster image using GD or ImageMagick libraries
22991			return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22992		}
22993		// set alignment
22994		$this->img_rb_y = $y + $h;
22995		// set alignment
22996		if ($this->rtl) {
22997			if ($palign == 'L') {
22998				$ximg = $this->lMargin;
22999			} elseif ($palign == 'C') {
23000				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
23001			} elseif ($palign == 'R') {
23002				$ximg = $this->w - $this->rMargin - $w;
23003			} else {
23004				$ximg = $x - $w;
23005			}
23006			$this->img_rb_x = $ximg;
23007		} else {
23008			if ($palign == 'L') {
23009				$ximg = $this->lMargin;
23010			} elseif ($palign == 'C') {
23011				$ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
23012			} elseif ($palign == 'R') {
23013				$ximg = $this->w - $this->rMargin - $w;
23014			} else {
23015				$ximg = $x;
23016			}
23017			$this->img_rb_x = $ximg + $w;
23018		}
23019		// store current graphic vars
23020		$gvars = $this->getGraphicVars();
23021		// store SVG position and scale factors
23022		$svgoffset_x = ($ximg - $ox) * $this->k;
23023		$svgoffset_y = -($y - $oy) * $this->k;
23024		if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
23025			$ow = $view_box[2];
23026			$oh = $view_box[3];
23027		} else {
23028			if ($ow <= 0) {
23029				$ow = $w;
23030			}
23031			if ($oh <= 0) {
23032				$oh = $h;
23033			}
23034		}
23035		$svgscale_x = $w / $ow;
23036		$svgscale_y = $h / $oh;
23037		// scaling and alignment
23038		if ($aspect_ratio_align != 'none') {
23039			// store current scaling values
23040			$svgscale_old_x = $svgscale_x;
23041			$svgscale_old_y = $svgscale_y;
23042			// force uniform scaling
23043			if ($aspect_ratio_ms == 'slice') {
23044				// the entire viewport is covered by the viewBox
23045				if ($svgscale_x > $svgscale_y) {
23046					$svgscale_y = $svgscale_x;
23047				} elseif ($svgscale_x < $svgscale_y) {
23048					$svgscale_x = $svgscale_y;
23049				}
23050			} else { // meet
23051				// the entire viewBox is visible within the viewport
23052				if ($svgscale_x < $svgscale_y) {
23053					$svgscale_y = $svgscale_x;
23054				} elseif ($svgscale_x > $svgscale_y) {
23055					$svgscale_x = $svgscale_y;
23056				}
23057			}
23058			// correct X alignment
23059			switch (substr($aspect_ratio_align, 1, 3)) {
23060				case 'Min': {
23061					// do nothing
23062					break;
23063				}
23064				case 'Max': {
23065					$svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
23066					break;
23067				}
23068				default:
23069				case 'Mid': {
23070					$svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
23071					break;
23072				}
23073			}
23074			// correct Y alignment
23075			switch (substr($aspect_ratio_align, 5)) {
23076				case 'Min': {
23077					// do nothing
23078					break;
23079				}
23080				case 'Max': {
23081					$svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
23082					break;
23083				}
23084				default:
23085				case 'Mid': {
23086					$svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
23087					break;
23088				}
23089			}
23090		}
23091		// store current page break mode
23092		$page_break_mode = $this->AutoPageBreak;
23093		$page_break_margin = $this->getBreakMargin();
23094		$cell_padding = $this->cell_padding;
23095		$this->SetCellPadding(0);
23096		$this->SetAutoPageBreak(false);
23097		// save the current graphic state
23098		$this->_out('q'.$this->epsmarker);
23099		// set initial clipping mask
23100		$this->Rect($ximg, $y, $w, $h, 'CNZ', array(), array());
23101		// scale and translate
23102		$e = $ox * $this->k * (1 - $svgscale_x);
23103		$f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
23104		$this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y)));
23105		// creates a new XML parser to be used by the other XML functions
23106		$this->parser = xml_parser_create('UTF-8');
23107		// the following function allows to use parser inside object
23108		xml_set_object($this->parser, $this);
23109		// disable case-folding for this XML parser
23110		xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
23111		// sets the element handler functions for the XML parser
23112		xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler');
23113		// sets the character data handler function for the XML parser
23114		xml_set_character_data_handler($this->parser, 'segSVGContentHandler');
23115		// start parsing an XML document
23116		if (!xml_parse($this->parser, $svgdata)) {
23117			$error_message = sprintf('SVG Error: %s at line %d', xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser));
23118			$this->Error($error_message);
23119		}
23120		// free this XML parser
23121		xml_parser_free($this->parser);
23122		// restore previous graphic state
23123		$this->_out($this->epsmarker.'Q');
23124		// restore graphic vars
23125		$this->setGraphicVars($gvars);
23126		$this->lasth = $gvars['lasth'];
23127		if (!empty($border)) {
23128			$bx = $this->x;
23129			$by = $this->y;
23130			$this->x = $ximg;
23131			if ($this->rtl) {
23132				$this->x += $w;
23133			}
23134			$this->y = $y;
23135			$this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
23136			$this->x = $bx;
23137			$this->y = $by;
23138		}
23139		if ($link) {
23140			$this->Link($ximg, $y, $w, $h, $link, 0);
23141		}
23142		// set pointer to align the next text/objects
23143		switch($align) {
23144			case 'T':{
23145				$this->y = $y;
23146				$this->x = $this->img_rb_x;
23147				break;
23148			}
23149			case 'M':{
23150				$this->y = $y + round($h/2);
23151				$this->x = $this->img_rb_x;
23152				break;
23153			}
23154			case 'B':{
23155				$this->y = $this->img_rb_y;
23156				$this->x = $this->img_rb_x;
23157				break;
23158			}
23159			case 'N':{
23160				$this->SetY($this->img_rb_y);
23161				break;
23162			}
23163			default:{
23164				// restore pointer to starting position
23165				$this->x = $gvars['x'];
23166				$this->y = $gvars['y'];
23167				$this->page = $gvars['page'];
23168				$this->current_column = $gvars['current_column'];
23169				$this->tMargin = $gvars['tMargin'];
23170				$this->bMargin = $gvars['bMargin'];
23171				$this->w = $gvars['w'];
23172				$this->h = $gvars['h'];
23173				$this->wPt = $gvars['wPt'];
23174				$this->hPt = $gvars['hPt'];
23175				$this->fwPt = $gvars['fwPt'];
23176				$this->fhPt = $gvars['fhPt'];
23177				break;
23178			}
23179		}
23180		$this->endlinex = $this->img_rb_x;
23181		// restore page break
23182		$this->SetAutoPageBreak($page_break_mode, $page_break_margin);
23183		$this->cell_padding = $cell_padding;
23184	}
23185
23186	/**
23187	 * Convert SVG transformation matrix to PDF.
23188	 * @param $tm (array) original SVG transformation matrix
23189	 * @return array transformation matrix
23190	 * @protected
23191	 * @since 5.0.000 (2010-05-02)
23192	 */
23193	protected function convertSVGtMatrix($tm) {
23194		$a = $tm[0];
23195		$b = -$tm[1];
23196		$c = -$tm[2];
23197		$d = $tm[3];
23198		$e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
23199		$f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
23200		$x = 0;
23201		$y = $this->h * $this->k;
23202		$e = ($x * (1 - $a)) - ($y * $c) + $e;
23203		$f = ($y * (1 - $d)) - ($x * $b) + $f;
23204		return array($a, $b, $c, $d, $e, $f);
23205	}
23206
23207	/**
23208	 * Apply SVG graphic transformation matrix.
23209	 * @param $tm (array) original SVG transformation matrix
23210	 * @protected
23211	 * @since 5.0.000 (2010-05-02)
23212	 */
23213	protected function SVGTransform($tm) {
23214		$this->Transform($this->convertSVGtMatrix($tm));
23215	}
23216
23217	/**
23218	 * Apply the requested SVG styles (*** TO BE COMPLETED ***)
23219	 * @param $svgstyle (array) array of SVG styles to apply
23220	 * @param $prevsvgstyle (array) array of previous SVG style
23221	 * @param $x (int) X origin of the bounding box
23222	 * @param $y (int) Y origin of the bounding box
23223	 * @param $w (int) width of the bounding box
23224	 * @param $h (int) height of the bounding box
23225	 * @param $clip_function (string) clip function
23226	 * @param $clip_params (array) array of parameters for clipping function
23227	 * @return object style
23228	 * @author Nicola Asuni
23229	 * @since 5.0.000 (2010-05-02)
23230	 * @protected
23231	 */
23232	protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
23233		if ($this->state != 2) {
23234			 return;
23235		}
23236		$objstyle = '';
23237		$minlen = (0.01 / $this->k); // minimum acceptable length
23238		if (!isset($svgstyle['opacity'])) {
23239			return $objstyle;
23240		}
23241		// clip-path
23242		$regs = array();
23243		if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
23244			$clip_path = $this->svgclippaths[$regs[1]];
23245			foreach ($clip_path as $cp) {
23246				$this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
23247			}
23248		}
23249		// opacity
23250		if ($svgstyle['opacity'] != 1) {
23251			$this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false);
23252		}
23253		// color
23254		$fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors);
23255		$this->SetFillColorArray($fill_color);
23256		// text color
23257		$text_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors);
23258		$this->SetTextColorArray($text_color);
23259		// clip
23260		if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
23261			$top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
23262			$right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
23263			$bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
23264			$left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
23265			$cx = $x + $left;
23266			$cy = $y + $top;
23267			$cw = $w - $left - $right;
23268			$ch = $h - $top - $bottom;
23269			if ($svgstyle['clip-rule'] == 'evenodd') {
23270				$clip_rule = 'CNZ';
23271			} else {
23272				$clip_rule = 'CEO';
23273			}
23274			$this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
23275		}
23276		// fill
23277		$regs = array();
23278		if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
23279			// gradient
23280			$gradient = $this->svggradients[$regs[1]];
23281			if (isset($gradient['xref'])) {
23282				// reference to another gradient definition
23283				$newgradient = $this->svggradients[$gradient['xref']];
23284				$newgradient['coords'] = $gradient['coords'];
23285				$newgradient['mode'] = $gradient['mode'];
23286				$newgradient['type'] = $gradient['type'];
23287				$newgradient['gradientUnits'] = $gradient['gradientUnits'];
23288				if (isset($gradient['gradientTransform'])) {
23289					$newgradient['gradientTransform'] = $gradient['gradientTransform'];
23290				}
23291				$gradient = $newgradient;
23292			}
23293			//save current Graphic State
23294			$this->_outSaveGraphicsState();
23295			//set clipping area
23296			if (!empty($clip_function) AND method_exists($this, $clip_function)) {
23297				$bbox = call_user_func_array(array($this, $clip_function), $clip_params);
23298				if ((!isset($gradient['type']) OR ($gradient['type'] != 3)) AND is_array($bbox) AND (count($bbox) == 4)) {
23299					list($x, $y, $w, $h) = $bbox;
23300				}
23301			}
23302			if ($gradient['mode'] == 'measure') {
23303				if (!isset($gradient['coords'][4])) {
23304					$gradient['coords'][4] = 0.5;
23305				}
23306				if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
23307					$gtm = $gradient['gradientTransform'];
23308					// apply transformation matrix
23309					$xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
23310					$ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
23311					$xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
23312					$yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
23313					$r = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
23314					$gradient['coords'][0] = $xa;
23315					$gradient['coords'][1] = $ya;
23316					$gradient['coords'][2] = $xb;
23317					$gradient['coords'][3] = $yb;
23318					$gradient['coords'][4] = $r;
23319				}
23320				// convert SVG coordinates to user units
23321				$gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
23322				$gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
23323				$gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
23324				$gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
23325				$gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
23326				if ($w <= $minlen) {
23327					$w = $minlen;
23328				}
23329				if ($h <= $minlen) {
23330					$h = $minlen;
23331				}
23332				// shift units
23333				if ($gradient['gradientUnits'] == 'objectBoundingBox') {
23334					// convert to SVG coordinate system
23335					$gradient['coords'][0] += $x;
23336					$gradient['coords'][1] += $y;
23337					$gradient['coords'][2] += $x;
23338					$gradient['coords'][3] += $y;
23339				}
23340				// calculate percentages
23341				$gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w);
23342				$gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h);
23343				$gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w);
23344				$gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h);
23345				$gradient['coords'][4] /= $w;
23346			} elseif ($gradient['mode'] == 'percentage') {
23347				foreach($gradient['coords'] as $key => $val) {
23348					$gradient['coords'][$key] = (intval($val) / 100);
23349					if ($val < 0) {
23350						$gradient['coords'][$key] = 0;
23351					} elseif ($val > 1) {
23352						$gradient['coords'][$key] = 1;
23353					}
23354				}
23355			}
23356			if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
23357				// single color (no shading)
23358				$gradient['coords'][0] = 1;
23359				$gradient['coords'][1] = 0;
23360				$gradient['coords'][2] = 0.999;
23361				$gradient['coords'][3] = 0;
23362			}
23363			// swap Y coordinates
23364			$tmp = $gradient['coords'][1];
23365			$gradient['coords'][1] = $gradient['coords'][3];
23366			$gradient['coords'][3] = $tmp;
23367			// set transformation map for gradient
23368			$cy = ($this->h - $y);
23369			if ($gradient['type'] == 3) {
23370				// circular gradient
23371				$cy -= ($gradient['coords'][1] * ($w + $h));
23372			} else {
23373				$cy -= $h;
23374			}
23375			$this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), ($cy * $this->k)));
23376			if (count($gradient['stops']) > 1) {
23377				$this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
23378			}
23379		} elseif ($svgstyle['fill'] != 'none') {
23380			$fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors);
23381			if ($svgstyle['fill-opacity'] != 1) {
23382				$this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false);
23383			}
23384			$this->SetFillColorArray($fill_color);
23385			if ($svgstyle['fill-rule'] == 'evenodd') {
23386				$objstyle .= 'F*';
23387			} else {
23388				$objstyle .= 'F';
23389			}
23390		}
23391		// stroke
23392		if ($svgstyle['stroke'] != 'none') {
23393			if ($svgstyle['stroke-opacity'] != 1) {
23394				$this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false);
23395			} elseif (preg_match('/rgba\(\d+%?,\s*\d+%?,\s*\d+%?,\s*(\d+(?:\.\d+)?)\)/i', $svgstyle['stroke'], $rgba_matches)) {
23396				$this->setAlpha($rgba_matches[1], 'Normal', $this->alpha['ca'], false);
23397			}
23398			$stroke_style = array(
23399				'color' => TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors),
23400				'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
23401				'cap' => $svgstyle['stroke-linecap'],
23402				'join' => $svgstyle['stroke-linejoin']
23403				);
23404			if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
23405				$stroke_style['dash'] = $svgstyle['stroke-dasharray'];
23406			}
23407			$this->SetLineStyle($stroke_style);
23408			$objstyle .= 'D';
23409		}
23410		// font
23411		$regs = array();
23412		if (!empty($svgstyle['font'])) {
23413			if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
23414				$font_family = $this->getFontFamilyName($regs[1]);
23415			} else {
23416				$font_family = $svgstyle['font-family'];
23417			}
23418			if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23419				$font_size = trim($regs[1]);
23420			} else {
23421				$font_size = $svgstyle['font-size'];
23422			}
23423			if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23424				$font_style = trim($regs[1]);
23425			} else {
23426				$font_style = $svgstyle['font-style'];
23427			}
23428			if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23429				$font_weight = trim($regs[1]);
23430			} else {
23431				$font_weight = $svgstyle['font-weight'];
23432			}
23433			if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23434				$font_stretch = trim($regs[1]);
23435			} else {
23436				$font_stretch = $svgstyle['font-stretch'];
23437			}
23438			if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23439				$font_spacing = trim($regs[1]);
23440			} else {
23441				$font_spacing = $svgstyle['letter-spacing'];
23442			}
23443		} else {
23444			$font_family = $this->getFontFamilyName($svgstyle['font-family']);
23445			$font_size = $svgstyle['font-size'];
23446			$font_style = $svgstyle['font-style'];
23447			$font_weight = $svgstyle['font-weight'];
23448			$font_stretch = $svgstyle['font-stretch'];
23449			$font_spacing = $svgstyle['letter-spacing'];
23450		}
23451		$font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit);
23452		$font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
23453		$font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
23454		switch ($font_style) {
23455			case 'italic': {
23456				$font_style = 'I';
23457				break;
23458			}
23459			case 'oblique': {
23460				$font_style = 'I';
23461				break;
23462			}
23463			default:
23464			case 'normal': {
23465				$font_style = '';
23466				break;
23467			}
23468		}
23469		switch ($font_weight) {
23470			case 'bold':
23471			case 'bolder': {
23472				$font_style .= 'B';
23473				break;
23474			}
23475		}
23476		switch ($svgstyle['text-decoration']) {
23477			case 'underline': {
23478				$font_style .= 'U';
23479				break;
23480			}
23481			case 'overline': {
23482				$font_style .= 'O';
23483				break;
23484			}
23485			case 'line-through': {
23486				$font_style .= 'D';
23487				break;
23488			}
23489			default:
23490			case 'none': {
23491				break;
23492			}
23493		}
23494		$this->SetFont($font_family, $font_style, $font_size);
23495		$this->setFontStretching($font_stretch);
23496		$this->setFontSpacing($font_spacing);
23497		return $objstyle;
23498	}
23499
23500	/**
23501	 * Draws an SVG path
23502	 * @param $d (string) attribute d of the path SVG element
23503	 * @param $style (string) Style of rendering. Possible values are:
23504	 * <ul>
23505	 *	 <li>D or empty string: Draw (default).</li>
23506	 *	 <li>F: Fill.</li>
23507	 *	 <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
23508	 *	 <li>DF or FD: Draw and fill.</li>
23509	 *	 <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
23510	 *	 <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
23511	 *	 <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
23512	 * </ul>
23513	 * @return array of container box measures (x, y, w, h)
23514	 * @author Nicola Asuni
23515	 * @since 5.0.000 (2010-05-02)
23516	 * @protected
23517	 */
23518	protected function SVGPath($d, $style='') {
23519		if ($this->state != 2) {
23520			 return;
23521		}
23522		// set fill/stroke style
23523		$op = TCPDF_STATIC::getPathPaintOperator($style, '');
23524		if (empty($op)) {
23525			return;
23526		}
23527		$paths = array();
23528		$d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
23529		preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER);
23530		$x = 0;
23531		$y = 0;
23532		$x1 = 0;
23533		$y1 = 0;
23534		$x2 = 0;
23535		$y2 = 0;
23536		$xmin = 2147483647;
23537		$xmax = 0;
23538		$ymin = 2147483647;
23539		$ymax = 0;
23540		$relcoord = false;
23541		$minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
23542		$firstcmd = true; // used to print first point
23543		// draw curve pieces
23544		foreach ($paths as $key => $val) {
23545			// get curve type
23546			$cmd = trim($val[1]);
23547			if (strtolower($cmd) == $cmd) {
23548				// use relative coordinated instead of absolute
23549				$relcoord = true;
23550				$xoffset = $x;
23551				$yoffset = $y;
23552			} else {
23553				$relcoord = false;
23554				$xoffset = 0;
23555				$yoffset = 0;
23556			}
23557			$params = array();
23558			if (isset($val[2])) {
23559				// get curve parameters
23560				$rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
23561				$params = array();
23562				foreach ($rawparams as $ck => $cp) {
23563					$params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
23564					if (abs($params[$ck]) < $minlen) {
23565						// aproximate little values to zero
23566						$params[$ck] = 0;
23567					}
23568				}
23569			}
23570			// store current origin point
23571			$x0 = $x;
23572			$y0 = $y;
23573			switch (strtoupper($cmd)) {
23574				case 'M': { // moveto
23575					foreach ($params as $ck => $cp) {
23576						if (($ck % 2) == 0) {
23577							$x = $cp + $xoffset;
23578						} else {
23579							$y = $cp + $yoffset;
23580							if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23581								if ($ck == 1) {
23582									$this->_outPoint($x, $y);
23583									$firstcmd = false;
23584								} else {
23585									$this->_outLine($x, $y);
23586								}
23587								$x0 = $x;
23588								$y0 = $y;
23589							}
23590							$xmin = min($xmin, $x);
23591							$ymin = min($ymin, $y);
23592							$xmax = max($xmax, $x);
23593							$ymax = max($ymax, $y);
23594							if ($relcoord) {
23595								$xoffset = $x;
23596								$yoffset = $y;
23597							}
23598						}
23599					}
23600					break;
23601				}
23602				case 'L': { // lineto
23603					foreach ($params as $ck => $cp) {
23604						if (($ck % 2) == 0) {
23605							$x = $cp + $xoffset;
23606						} else {
23607							$y = $cp + $yoffset;
23608							if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23609								$this->_outLine($x, $y);
23610								$x0 = $x;
23611								$y0 = $y;
23612							}
23613							$xmin = min($xmin, $x);
23614							$ymin = min($ymin, $y);
23615							$xmax = max($xmax, $x);
23616							$ymax = max($ymax, $y);
23617							if ($relcoord) {
23618								$xoffset = $x;
23619								$yoffset = $y;
23620							}
23621						}
23622					}
23623					break;
23624				}
23625				case 'H': { // horizontal lineto
23626					foreach ($params as $ck => $cp) {
23627						$x = $cp + $xoffset;
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						$xmax = max($xmax, $x);
23635						if ($relcoord) {
23636							$xoffset = $x;
23637						}
23638					}
23639					break;
23640				}
23641				case 'V': { // vertical lineto
23642					foreach ($params as $ck => $cp) {
23643						$y = $cp + $yoffset;
23644						if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23645							$this->_outLine($x, $y);
23646							$x0 = $x;
23647							$y0 = $y;
23648						}
23649						$ymin = min($ymin, $y);
23650						$ymax = max($ymax, $y);
23651						if ($relcoord) {
23652							$yoffset = $y;
23653						}
23654					}
23655					break;
23656				}
23657				case 'C': { // curveto
23658					foreach ($params as $ck => $cp) {
23659						$params[$ck] = $cp;
23660						if ((($ck + 1) % 6) == 0) {
23661							$x1 = $params[($ck - 5)] + $xoffset;
23662							$y1 = $params[($ck - 4)] + $yoffset;
23663							$x2 = $params[($ck - 3)] + $xoffset;
23664							$y2 = $params[($ck - 2)] + $yoffset;
23665							$x = $params[($ck - 1)] + $xoffset;
23666							$y = $params[($ck)] + $yoffset;
23667							$this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23668							$xmin = min($xmin, $x, $x1, $x2);
23669							$ymin = min($ymin, $y, $y1, $y2);
23670							$xmax = max($xmax, $x, $x1, $x2);
23671							$ymax = max($ymax, $y, $y1, $y2);
23672							if ($relcoord) {
23673								$xoffset = $x;
23674								$yoffset = $y;
23675							}
23676						}
23677					}
23678					break;
23679				}
23680				case 'S': { // shorthand/smooth curveto
23681					foreach ($params as $ck => $cp) {
23682						$params[$ck] = $cp;
23683						if ((($ck + 1) % 4) == 0) {
23684							if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
23685								$x1 = (2 * $x) - $x2;
23686								$y1 = (2 * $y) - $y2;
23687							} else {
23688								$x1 = $x;
23689								$y1 = $y;
23690							}
23691							$x2 = $params[($ck - 3)] + $xoffset;
23692							$y2 = $params[($ck - 2)] + $yoffset;
23693							$x = $params[($ck - 1)] + $xoffset;
23694							$y = $params[($ck)] + $yoffset;
23695							$this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23696							$xmin = min($xmin, $x, $x1, $x2);
23697							$ymin = min($ymin, $y, $y1, $y2);
23698							$xmax = max($xmax, $x, $x1, $x2);
23699							$ymax = max($ymax, $y, $y1, $y2);
23700							if ($relcoord) {
23701								$xoffset = $x;
23702								$yoffset = $y;
23703							}
23704						}
23705					}
23706					break;
23707				}
23708				case 'Q': { // quadratic Bezier curveto
23709					foreach ($params as $ck => $cp) {
23710						$params[$ck] = $cp;
23711						if ((($ck + 1) % 4) == 0) {
23712							// convert quadratic points to cubic points
23713							$x1 = $params[($ck - 3)] + $xoffset;
23714							$y1 = $params[($ck - 2)] + $yoffset;
23715							$xa = ($x + (2 * $x1)) / 3;
23716							$ya = ($y + (2 * $y1)) / 3;
23717							$x = $params[($ck - 1)] + $xoffset;
23718							$y = $params[($ck)] + $yoffset;
23719							$xb = ($x + (2 * $x1)) / 3;
23720							$yb = ($y + (2 * $y1)) / 3;
23721							$this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23722							$xmin = min($xmin, $x, $xa, $xb);
23723							$ymin = min($ymin, $y, $ya, $yb);
23724							$xmax = max($xmax, $x, $xa, $xb);
23725							$ymax = max($ymax, $y, $ya, $yb);
23726							if ($relcoord) {
23727								$xoffset = $x;
23728								$yoffset = $y;
23729							}
23730						}
23731					}
23732					break;
23733				}
23734				case 'T': { // shorthand/smooth quadratic Bezier curveto
23735					foreach ($params as $ck => $cp) {
23736						$params[$ck] = $cp;
23737						if (($ck % 2) != 0) {
23738							if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
23739								$x1 = (2 * $x) - $x1;
23740								$y1 = (2 * $y) - $y1;
23741							} else {
23742								$x1 = $x;
23743								$y1 = $y;
23744							}
23745							// convert quadratic points to cubic points
23746							$xa = ($x + (2 * $x1)) / 3;
23747							$ya = ($y + (2 * $y1)) / 3;
23748							$x = $params[($ck - 1)] + $xoffset;
23749							$y = $params[($ck)] + $yoffset;
23750							$xb = ($x + (2 * $x1)) / 3;
23751							$yb = ($y + (2 * $y1)) / 3;
23752							$this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23753							$xmin = min($xmin, $x, $xa, $xb);
23754							$ymin = min($ymin, $y, $ya, $yb);
23755							$xmax = max($xmax, $x, $xa, $xb);
23756							$ymax = max($ymax, $y, $ya, $yb);
23757							if ($relcoord) {
23758								$xoffset = $x;
23759								$yoffset = $y;
23760							}
23761						}
23762					}
23763					break;
23764				}
23765				case 'A': { // elliptical arc
23766					foreach ($params as $ck => $cp) {
23767						$params[$ck] = $cp;
23768						if ((($ck + 1) % 7) == 0) {
23769							$x0 = $x;
23770							$y0 = $y;
23771							$rx = abs($params[($ck - 6)]);
23772							$ry = abs($params[($ck - 5)]);
23773							$ang = -$rawparams[($ck - 4)];
23774							$angle = deg2rad($ang);
23775							$fa = $rawparams[($ck - 3)]; // large-arc-flag
23776							$fs = $rawparams[($ck - 2)]; // sweep-flag
23777							$x = $params[($ck - 1)] + $xoffset;
23778							$y = $params[$ck] + $yoffset;
23779							if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
23780								// endpoints are almost identical
23781								$xmin = min($xmin, $x);
23782								$ymin = min($ymin, $y);
23783								$xmax = max($xmax, $x);
23784								$ymax = max($ymax, $y);
23785							} else {
23786								$cos_ang = cos($angle);
23787								$sin_ang = sin($angle);
23788								$a = (($x0 - $x) / 2);
23789								$b = (($y0 - $y) / 2);
23790								$xa = ($a * $cos_ang) - ($b * $sin_ang);
23791								$ya = ($a * $sin_ang) + ($b * $cos_ang);
23792								$rx2 = $rx * $rx;
23793								$ry2 = $ry * $ry;
23794								$xa2 = $xa * $xa;
23795								$ya2 = $ya * $ya;
23796								$delta = ($xa2 / $rx2) + ($ya2 / $ry2);
23797								if ($delta > 1) {
23798									$rx *= sqrt($delta);
23799									$ry *= sqrt($delta);
23800									$rx2 = $rx * $rx;
23801									$ry2 = $ry * $ry;
23802								}
23803								$numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
23804								if ($numerator < 0) {
23805									$root = 0;
23806								} else {
23807									$root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
23808								}
23809								if ($fa == $fs){
23810									$root *= -1;
23811								}
23812								$cax = $root * (($rx * $ya) / $ry);
23813								$cay = -$root * (($ry * $xa) / $rx);
23814								// coordinates of ellipse center
23815								$cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
23816								$cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
23817								// get angles
23818								$angs = TCPDF_STATIC::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
23819								$dang = TCPDF_STATIC::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
23820								if (($fs == 0) AND ($dang > 0)) {
23821									$dang -= (2 * M_PI);
23822								} elseif (($fs == 1) AND ($dang < 0)) {
23823									$dang += (2 * M_PI);
23824								}
23825								$angf = $angs - $dang;
23826								if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
23827									// reverse angles
23828									$tmp = $angs;
23829									$angs = $angf;
23830									$angf = $tmp;
23831								}
23832								$angs = round(rad2deg($angs), 6);
23833								$angf = round(rad2deg($angf), 6);
23834								// covent angles to positive values
23835								if (($angs < 0) AND ($angf < 0)) {
23836									$angs += 360;
23837									$angf += 360;
23838								}
23839								$pie = false;
23840								if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
23841									$pie = true;
23842								}
23843								list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
23844								$xmin = min($xmin, $x, $axmin);
23845								$ymin = min($ymin, $y, $aymin);
23846								$xmax = max($xmax, $x, $axmax);
23847								$ymax = max($ymax, $y, $aymax);
23848							}
23849							if ($relcoord) {
23850								$xoffset = $x;
23851								$yoffset = $y;
23852							}
23853						}
23854					}
23855					break;
23856				}
23857				case 'Z': {
23858					$this->_out('h');
23859					break;
23860				}
23861			}
23862			$firstcmd = false;
23863		} // end foreach
23864		if (!empty($op)) {
23865			$this->_out($op);
23866		}
23867		return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
23868	}
23869
23870	/**
23871	 * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***)
23872	 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
23873	 * @param $name (string) 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.
23874	 * @param $attribs (array) 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.
23875	 * @param $ctm (array) tranformation matrix for clipping mode (starting transformation matrix).
23876	 * @author Nicola Asuni
23877	 * @since 5.0.000 (2010-05-02)
23878	 * @protected
23879	 */
23880	protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
23881		// check if we are in clip mode
23882		if ($this->svgclipmode) {
23883			$this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]);
23884			return;
23885		}
23886		if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
23887			if (isset($attribs['id'])) {
23888				$attribs['child_elements'] = array();
23889				$this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23890				return;
23891			}
23892			if (end($this->svgdefs) !== FALSE) {
23893				$last_svgdefs_id = key($this->svgdefs);
23894				if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
23895					$attribs['id'] = 'DF_'.(count($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements']) + 1);
23896					$this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23897					return;
23898				}
23899			}
23900			return;
23901		}
23902		$clipping = false;
23903		if ($parser == 'clip-path') {
23904			// set clipping mode
23905			$clipping = true;
23906		}
23907		// get styling properties
23908		$prev_svgstyle = $this->svgstyles[max(0,(count($this->svgstyles) - 1))]; // previous style
23909		$svgstyle = $this->svgstyles[0]; // set default style
23910		if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) {
23911			// default fill attribute for clipping
23912			$attribs['fill'] = 'none';
23913		}
23914		if (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) {
23915			// fix style for regular expression
23916			$attribs['style'] = ';'.$attribs['style'];
23917		}
23918		foreach ($prev_svgstyle as $key => $val) {
23919			if (in_array($key, TCPDF_IMAGES::$svginheritprop)) {
23920				// inherit previous value
23921				$svgstyle[$key] = $val;
23922			}
23923			if (isset($attribs[$key]) AND !TCPDF_STATIC::empty_string($attribs[$key])) {
23924				// specific attribute settings
23925				if ($attribs[$key] == 'inherit') {
23926					$svgstyle[$key] = $val;
23927				} else {
23928					$svgstyle[$key] = $attribs[$key];
23929				}
23930			} elseif (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) {
23931				// CSS style syntax
23932				$attrval = array();
23933				if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
23934					if ($attrval[1] == 'inherit') {
23935						$svgstyle[$key] = $val;
23936					} else {
23937						$svgstyle[$key] = $attrval[1];
23938					}
23939				}
23940			}
23941		}
23942		// transformation matrix
23943		if (!empty($ctm)) {
23944			$tm = $ctm;
23945		} else {
23946			$tm = array(1,0,0,1,0,0);
23947		}
23948		if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
23949			$tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, TCPDF_STATIC::getSVGTransformMatrix($attribs['transform']));
23950		}
23951		$svgstyle['transfmatrix'] = $tm;
23952		$invisible = false;
23953		if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
23954			// the current graphics element is invisible (nothing is painted)
23955			$invisible = true;
23956		}
23957		// process tag
23958		switch($name) {
23959			case 'defs': {
23960				$this->svgdefsmode = true;
23961				break;
23962			}
23963			// clipPath
23964			case 'clipPath': {
23965				if ($invisible) {
23966					break;
23967				}
23968				$this->svgclipmode = true;
23969				if (!isset($attribs['id'])) {
23970					$attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1);
23971				}
23972				$this->svgclipid = $attribs['id'];
23973				$this->svgclippaths[$this->svgclipid] = array();
23974				$this->svgcliptm[$this->svgclipid] = $tm;
23975				break;
23976			}
23977			case 'svg': {
23978				// start of SVG object
23979				break;
23980			}
23981			case 'g': {
23982				// group together related graphics elements
23983				array_push($this->svgstyles, $svgstyle);
23984				$this->StartTransform();
23985				$x = (isset($attribs['x'])?$attribs['x']:0);
23986				$y = (isset($attribs['y'])?$attribs['y']:0);
23987				$w = 1;//(isset($attribs['width'])?$attribs['width']:1);
23988				$h = 1;//(isset($attribs['height'])?$attribs['height']:1);
23989				$tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
23990				$this->SVGTransform($tm);
23991				$this->setSVGStyles($svgstyle, $prev_svgstyle);
23992				break;
23993			}
23994			case 'linearGradient': {
23995				if ($this->pdfa_mode) {
23996					break;
23997				}
23998				if (!isset($attribs['id'])) {
23999					$attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
24000				}
24001				$this->svggradientid = $attribs['id'];
24002				$this->svggradients[$this->svggradientid] = array();
24003				$this->svggradients[$this->svggradientid]['type'] = 2;
24004				$this->svggradients[$this->svggradientid]['stops'] = array();
24005				if (isset($attribs['gradientUnits'])) {
24006					$this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
24007				} else {
24008					$this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
24009				}
24010				//$attribs['spreadMethod']
24011				if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2'])))
24012					OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%'))
24013						OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%'))
24014						OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%'))
24015						OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) {
24016					$this->svggradients[$this->svggradientid]['mode'] = 'percentage';
24017				} else {
24018					$this->svggradients[$this->svggradientid]['mode'] = 'measure';
24019				}
24020				$x1 = (isset($attribs['x1'])?$attribs['x1']:'0');
24021				$y1 = (isset($attribs['y1'])?$attribs['y1']:'0');
24022				$x2 = (isset($attribs['x2'])?$attribs['x2']:'100');
24023				$y2 = (isset($attribs['y2'])?$attribs['y2']:'0');
24024				if (isset($attribs['gradientTransform'])) {
24025					$this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
24026				}
24027				$this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
24028				if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24029					// gradient is defined on another place
24030					$this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
24031				}
24032				break;
24033			}
24034			case 'radialGradient': {
24035				if ($this->pdfa_mode) {
24036					break;
24037				}
24038				if (!isset($attribs['id'])) {
24039					$attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
24040				}
24041				$this->svggradientid = $attribs['id'];
24042				$this->svggradients[$this->svggradientid] = array();
24043				$this->svggradients[$this->svggradientid]['type'] = 3;
24044				$this->svggradients[$this->svggradientid]['stops'] = array();
24045				if (isset($attribs['gradientUnits'])) {
24046					$this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
24047				} else {
24048					$this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
24049				}
24050				//$attribs['spreadMethod']
24051				if (((!isset($attribs['cx'])) AND (!isset($attribs['cy'])))
24052					OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%'))
24053						OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')) )) {
24054					$this->svggradients[$this->svggradientid]['mode'] = 'percentage';
24055				} else {
24056					$this->svggradients[$this->svggradientid]['mode'] = 'measure';
24057				}
24058				$cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5);
24059				$cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5);
24060				$fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx);
24061				$fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy);
24062				$r = (isset($attribs['r']) ? $attribs['r'] : 0.5);
24063				if (isset($attribs['gradientTransform'])) {
24064					$this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
24065				}
24066				$this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
24067				if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24068					// gradient is defined on another place
24069					$this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
24070				}
24071				break;
24072			}
24073			case 'stop': {
24074				// gradient stops
24075				if (substr($attribs['offset'], -1) == '%') {
24076					$offset = floatval(substr($attribs['offset'], -1)) / 100;
24077				} else {
24078					$offset = floatval($attribs['offset']);
24079					if ($offset > 1) {
24080						$offset /= 100;
24081					}
24082				}
24083				$stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors):'black';
24084				$opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
24085				$this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
24086				break;
24087			}
24088			// paths
24089			case 'path': {
24090				if ($invisible) {
24091					break;
24092				}
24093				if (isset($attribs['d'])) {
24094					$d = trim($attribs['d']);
24095					if (!empty($d)) {
24096						$x = (isset($attribs['x'])?$attribs['x']:0);
24097						$y = (isset($attribs['y'])?$attribs['y']:0);
24098						$w = (isset($attribs['width'])?$attribs['width']:1);
24099						$h = (isset($attribs['height'])?$attribs['height']:1);
24100						$tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
24101						if ($clipping) {
24102							$this->SVGTransform($tm);
24103							$this->SVGPath($d, 'CNZ');
24104						} else {
24105							$this->StartTransform();
24106							$this->SVGTransform($tm);
24107							$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ'));
24108							if (!empty($obstyle)) {
24109								$this->SVGPath($d, $obstyle);
24110							}
24111							$this->StopTransform();
24112						}
24113					}
24114				}
24115				break;
24116			}
24117			// shapes
24118			case 'rect': {
24119				if ($invisible) {
24120					break;
24121				}
24122				$x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24123				$y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24124				$w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24125				$h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24126				$rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
24127				$ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
24128				if ($clipping) {
24129					$this->SVGTransform($tm);
24130					$this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
24131				} else {
24132					$this->StartTransform();
24133					$this->SVGTransform($tm);
24134					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
24135					if (!empty($obstyle)) {
24136						$this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
24137					}
24138					$this->StopTransform();
24139				}
24140				break;
24141			}
24142			case 'circle': {
24143				if ($invisible) {
24144					break;
24145				}
24146				$r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0);
24147				$cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
24148				$cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
24149				$x = ($cx - $r);
24150				$y = ($cy - $r);
24151				$w = (2 * $r);
24152				$h = $w;
24153				if ($clipping) {
24154					$this->SVGTransform($tm);
24155					$this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
24156				} else {
24157					$this->StartTransform();
24158					$this->SVGTransform($tm);
24159					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
24160					if (!empty($obstyle)) {
24161						$this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
24162					}
24163					$this->StopTransform();
24164				}
24165				break;
24166			}
24167			case 'ellipse': {
24168				if ($invisible) {
24169					break;
24170				}
24171				$rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0);
24172				$ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0);
24173				$cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
24174				$cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
24175				$x = ($cx - $rx);
24176				$y = ($cy - $ry);
24177				$w = (2 * $rx);
24178				$h = (2 * $ry);
24179				if ($clipping) {
24180					$this->SVGTransform($tm);
24181					$this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
24182				} else {
24183					$this->StartTransform();
24184					$this->SVGTransform($tm);
24185					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
24186					if (!empty($obstyle)) {
24187						$this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
24188					}
24189					$this->StopTransform();
24190				}
24191				break;
24192			}
24193			case 'line': {
24194				if ($invisible) {
24195					break;
24196				}
24197				$x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
24198				$y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
24199				$x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
24200				$y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
24201				$x = $x1;
24202				$y = $y1;
24203				$w = abs($x2 - $x1);
24204				$h = abs($y2 - $y1);
24205				if (!$clipping) {
24206					$this->StartTransform();
24207					$this->SVGTransform($tm);
24208					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
24209					$this->Line($x1, $y1, $x2, $y2);
24210					$this->StopTransform();
24211				}
24212				break;
24213			}
24214			case 'polyline':
24215			case 'polygon': {
24216				if ($invisible) {
24217					break;
24218				}
24219				$points = (isset($attribs['points'])?$attribs['points']:'0 0');
24220				$points = trim($points);
24221				// note that point may use a complex syntax not covered here
24222				$points = preg_split('/[\,\s]+/si', $points);
24223				if (count($points) < 4) {
24224					break;
24225				}
24226				$p = array();
24227				$xmin = 2147483647;
24228				$xmax = 0;
24229				$ymin = 2147483647;
24230				$ymax = 0;
24231				foreach ($points as $key => $val) {
24232					$p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
24233					if (($key % 2) == 0) {
24234						// X coordinate
24235						$xmin = min($xmin, $p[$key]);
24236						$xmax = max($xmax, $p[$key]);
24237					} else {
24238						// Y coordinate
24239						$ymin = min($ymin, $p[$key]);
24240						$ymax = max($ymax, $p[$key]);
24241					}
24242				}
24243				$x = $xmin;
24244				$y = $ymin;
24245				$w = ($xmax - $xmin);
24246				$h = ($ymax - $ymin);
24247				if ($name == 'polyline') {
24248					$this->StartTransform();
24249					$this->SVGTransform($tm);
24250					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
24251					if (!empty($obstyle)) {
24252						$this->PolyLine($p, $obstyle, array(), array());
24253					}
24254					$this->StopTransform();
24255				} else { // polygon
24256					if ($clipping) {
24257						$this->SVGTransform($tm);
24258						$this->Polygon($p, 'CNZ', array(), array(), true);
24259					} else {
24260						$this->StartTransform();
24261						$this->SVGTransform($tm);
24262						$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
24263						if (!empty($obstyle)) {
24264							$this->Polygon($p, $obstyle, array(), array(), true);
24265						}
24266						$this->StopTransform();
24267					}
24268				}
24269				break;
24270			}
24271			// image
24272			case 'image': {
24273				if ($invisible) {
24274					break;
24275				}
24276				if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
24277					break;
24278				}
24279				$x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24280				$y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24281				$w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24282				$h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24283				$img = $attribs['xlink:href'];
24284				if (!$clipping) {
24285					$this->StartTransform();
24286					$this->SVGTransform($tm);
24287					$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
24288					if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
24289						// embedded image encoded as base64
24290						$img = '@'.base64_decode(substr($img, strlen($m[0])));
24291					} else {
24292						// fix image path
24293						if (!TCPDF_STATIC::empty_string($this->svgdir) AND (($img[0] == '.') OR (basename($img) == $img))) {
24294							// replace relative path with full server path
24295							$img = $this->svgdir.'/'.$img;
24296						}
24297						if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
24298							$findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
24299							if (($findroot === false) OR ($findroot > 1)) {
24300								if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
24301									$img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
24302								} else {
24303									$img = $_SERVER['DOCUMENT_ROOT'].$img;
24304								}
24305							}
24306						}
24307						$img = urldecode($img);
24308						$testscrtype = @parse_url($img);
24309						if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
24310							// convert URL to server path
24311							$img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
24312						}
24313					}
24314					// get image type
24315					$imgtype = TCPDF_IMAGES::getImageFileType($img);
24316					if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
24317						$this->ImageEps($img, $x, $y, $w, $h);
24318					} elseif ($imgtype == 'svg') {
24319						$this->ImageSVG($img, $x, $y, $w, $h);
24320					} else {
24321						$this->Image($img, $x, $y, $w, $h);
24322					}
24323					$this->StopTransform();
24324				}
24325				break;
24326			}
24327			// text
24328			case 'text':
24329			case 'tspan': {
24330				// only basic support - advanced features must be implemented
24331				$this->svgtextmode['invisible'] = $invisible;
24332				if ($invisible) {
24333					break;
24334				}
24335				array_push($this->svgstyles, $svgstyle);
24336				if (isset($attribs['x'])) {
24337					$x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false);
24338				} elseif ($name == 'tspan') {
24339					$x = $this->x;
24340				} else {
24341					$x = 0;
24342				}
24343				if (isset($attribs['dx'])) {
24344					$x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false);
24345				}
24346				if (isset($attribs['y'])) {
24347					$y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false);
24348				} elseif ($name == 'tspan') {
24349					$y = $this->y;
24350				} else {
24351					$y = 0;
24352				}
24353				if (isset($attribs['dy'])) {
24354					$y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false);
24355				}
24356				$svgstyle['text-color'] = $svgstyle['fill'];
24357				$this->svgtext = '';
24358				if (isset($svgstyle['text-anchor'])) {
24359					$this->svgtextmode['text-anchor'] = $svgstyle['text-anchor'];
24360				} else {
24361					$this->svgtextmode['text-anchor'] = 'start';
24362				}
24363				if (isset($svgstyle['direction'])) {
24364					if ($svgstyle['direction'] == 'rtl') {
24365						$this->svgtextmode['rtl'] = true;
24366					} else {
24367						$this->svgtextmode['rtl'] = false;
24368					}
24369				} else {
24370					$this->svgtextmode['rtl'] = false;
24371				}
24372				if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
24373					$this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false);
24374				} else {
24375					$this->svgtextmode['stroke'] = false;
24376				}
24377				$this->StartTransform();
24378				$this->SVGTransform($tm);
24379				$obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
24380				$this->x = $x;
24381				$this->y = $y;
24382				break;
24383			}
24384			// use
24385			case 'use': {
24386				if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24387					$svgdefid = substr($attribs['xlink:href'], 1);
24388					if (isset($this->svgdefs[$svgdefid])) {
24389						$use = $this->svgdefs[$svgdefid];
24390						if (isset($attribs['xlink:href'])) {
24391							unset($attribs['xlink:href']);
24392						}
24393						if (isset($attribs['id'])) {
24394							unset($attribs['id']);
24395						}
24396						if (isset($use['attribs']['x']) AND isset($attribs['x'])) {
24397							$attribs['x'] += $use['attribs']['x'];
24398						}
24399						if (isset($use['attribs']['y']) AND isset($attribs['y'])) {
24400							$attribs['y'] += $use['attribs']['y'];
24401						}
24402						if (empty($attribs['style'])) {
24403							$attribs['style'] = '';
24404						}
24405						if (!empty($use['attribs']['style'])) {
24406							// merge styles
24407							$attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']);
24408						}
24409						$attribs = array_merge($use['attribs'], $attribs);
24410						$this->startSVGElementHandler($parser, $use['name'], $attribs);
24411						return;
24412					}
24413				}
24414				break;
24415			}
24416			default: {
24417				break;
24418			}
24419		} // end of switch
24420		// process child elements
24421		if (!empty($attribs['child_elements'])) {
24422			$child_elements = $attribs['child_elements'];
24423			unset($attribs['child_elements']);
24424			foreach($child_elements as $child_element) {
24425				if (empty($child_element['attribs']['closing_tag'])) {
24426					$this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']);
24427				} else {
24428					if (isset($child_element['attribs']['content'])) {
24429						$this->svgtext = $child_element['attribs']['content'];
24430					}
24431					$this->endSVGElementHandler('child-tag', $child_element['name']);
24432				}
24433			}
24434		}
24435	}
24436
24437	/**
24438	 * Sets the closing SVG element handler function for the XML parser.
24439	 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
24440	 * @param $name (string) 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.
24441	 * @author Nicola Asuni
24442	 * @since 5.0.000 (2010-05-02)
24443	 * @protected
24444	 */
24445	protected function endSVGElementHandler($parser, $name) {
24446		if ($this->svgdefsmode AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {;
24447			if (end($this->svgdefs) !== FALSE) {
24448				$last_svgdefs_id = key($this->svgdefs);
24449				if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
24450					foreach($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) {
24451						if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) {
24452							$this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24453							return;
24454						}
24455					}
24456					if ($this->svgdefs[$last_svgdefs_id]['name'] == $name) {
24457						$this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24458						return;
24459					}
24460				}
24461			}
24462			return;
24463		}
24464		switch($name) {
24465			case 'defs': {
24466				$this->svgdefsmode = false;
24467				break;
24468			}
24469			// clipPath
24470			case 'clipPath': {
24471				$this->svgclipmode = false;
24472				break;
24473			}
24474			case 'g': {
24475				// ungroup: remove last style from array
24476				array_pop($this->svgstyles);
24477				$this->StopTransform();
24478				break;
24479			}
24480			case 'text':
24481			case 'tspan': {
24482				if ($this->svgtextmode['invisible']) {
24483					// This implementation must be fixed to following the rule:
24484					// 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.
24485					break;
24486				}
24487				// print text
24488				$text = $this->svgtext;
24489				//$text = $this->stringTrim($text);
24490				$textlen = $this->GetStringWidth($text);
24491				if ($this->svgtextmode['text-anchor'] != 'start') {
24492					// check if string is RTL text
24493					if ($this->svgtextmode['text-anchor'] == 'end') {
24494						if ($this->svgtextmode['rtl']) {
24495							$this->x += $textlen;
24496						} else {
24497							$this->x -= $textlen;
24498						}
24499					} elseif ($this->svgtextmode['text-anchor'] == 'middle') {
24500						if ($this->svgtextmode['rtl']) {
24501							$this->x += ($textlen / 2);
24502						} else {
24503							$this->x -= ($textlen / 2);
24504						}
24505					}
24506				}
24507				$textrendermode = $this->textrendermode;
24508				$textstrokewidth = $this->textstrokewidth;
24509				$this->setTextRenderingMode($this->svgtextmode['stroke'], true, false);
24510				if ($name == 'text') {
24511					// store current coordinates
24512					$tmpx = $this->x;
24513					$tmpy = $this->y;
24514				}
24515				$this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
24516				if ($name == 'text') {
24517					// restore coordinates
24518					$this->x = $tmpx;
24519					$this->y = $tmpy;
24520				}
24521				// restore previous rendering mode
24522				$this->textrendermode = $textrendermode;
24523				$this->textstrokewidth = $textstrokewidth;
24524				$this->svgtext = '';
24525				$this->StopTransform();
24526				if (!$this->svgdefsmode) {
24527					array_pop($this->svgstyles);
24528				}
24529				break;
24530			}
24531			default: {
24532				break;
24533			}
24534		}
24535	}
24536
24537	/**
24538	 * Sets the character data handler function for the XML parser.
24539	 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
24540	 * @param $data (string) The second parameter, data, contains the character data as a string.
24541	 * @author Nicola Asuni
24542	 * @since 5.0.000 (2010-05-02)
24543	 * @protected
24544	 */
24545	protected function segSVGContentHandler($parser, $data) {
24546		$this->svgtext .= $data;
24547	}
24548
24549	// --- END SVG METHODS -----------------------------------------------------
24550
24551} // END OF TCPDF CLASS
24552
24553//============================================================+
24554// END OF FILE
24555//============================================================+
24556