2 //============================================================+
3 // File name : tcpdf.php
6 // Last Update : 2014-09-02
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
12 // This file is part of TCPDF software library.
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.
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.
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>.
28 // See LICENSE.TXT file for more information.
29 // -------------------------------------------------------------------
32 // This is a PHP class for generating PDF documents without requiring external extensions.
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.
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;
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.
68 //============================================================+
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>
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>
104 * Tools to encode your unicode fonts are on fonts/utils directory.</p>
105 * @package com.tecnick.tcpdf
106 * @author Nicola Asuni
110 // TCPDF configuration
111 require_once(dirname(__FILE__
).'/tcpdf_autoconfig.php');
112 // TCPDF static font methods and data
113 require_once(dirname(__FILE__
).'/include/tcpdf_font_data.php');
114 // TCPDF static font methods and data
115 require_once(dirname(__FILE__
).'/include/tcpdf_fonts.php');
116 // TCPDF static color methods and data
117 require_once(dirname(__FILE__
).'/include/tcpdf_colors.php');
118 // TCPDF static image methods and data
119 require_once(dirname(__FILE__
).'/include/tcpdf_images.php');
120 // TCPDF static methods and data
121 require_once(dirname(__FILE__
).'/include/tcpdf_static.php');
123 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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.
132 * @author Nicola Asuni - info@tecnick.com
136 // Protected properties
139 * Current page number.
145 * Current object number.
151 * Array of object offsets.
154 protected $offsets = array();
157 * Array of object IDs for each page.
160 protected $pageobjects = array();
163 * Buffer holding in-memory PDF.
169 * Array containing pages.
172 protected $pages = array();
175 * Current document state.
187 * Current page orientation (P = Portrait, L = Landscape).
190 protected $CurOrientation;
196 protected $pagedim = array();
199 * Scale factor (number of points in user unit).
205 * Width of page format in points.
211 * Height of page format in points.
217 * Current width of page in points.
223 * Current height of page in points.
229 * Current width of page in user unit.
235 * Current height of page in user unit.
253 * Cell left margin (used by regions).
259 * Cell right margin (used by regions).
277 * Array of cell internal paddings ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
278 * @since 5.9.000 (2010-10-03)
281 protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
284 * Array of cell margins ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
285 * @since 5.9.000 (2010-10-04)
288 protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
291 * Current horizontal position in user unit for cell positioning.
297 * Current vertical position in user unit for cell positioning.
303 * Height of last cell printed.
309 * Line width in user unit.
312 protected $LineWidth;
315 * Array of standard font names.
318 protected $CoreFonts;
321 * Array of used fonts.
324 protected $fonts = array();
327 * Array of font files.
330 protected $FontFiles = array();
333 * Array of encoding differences.
336 protected $diffs = array();
339 * Array of used images.
342 protected $images = array();
345 * Array of cached files.
348 protected $cached_files = array();
351 * Array of Annotations in pages.
354 protected $PageAnnots = array();
357 * Array of internal links.
360 protected $links = array();
363 * Current font family.
366 protected $FontFamily;
369 * Current font style.
372 protected $FontStyle;
375 * Current font ascent (distance between font top and baseline).
377 * @since 2.8.000 (2007-03-29)
379 protected $FontAscent;
382 * Current font descent (distance between font bottom and baseline).
384 * @since 2.8.000 (2007-03-29)
386 protected $FontDescent;
392 protected $underline;
404 protected $CurrentFont;
407 * Current font size in points.
410 protected $FontSizePt;
413 * Current font size in user unit.
419 * Commands for drawing color.
422 protected $DrawColor;
425 * Commands for filling color.
428 protected $FillColor;
431 * Commands for text color.
434 protected $TextColor;
437 * Indicates whether fill and text colors are different.
440 protected $ColorFlag;
443 * Automatic page breaking.
446 protected $AutoPageBreak;
449 * Threshold used to trigger page breaks.
452 protected $PageBreakTrigger;
455 * Flag set when processing page header.
458 protected $InHeader = false;
461 * Flag set when processing page footer.
464 protected $InFooter = false;
473 * Layout display mode.
476 protected $LayoutMode;
479 * If true set the document information dictionary in Unicode.
482 protected $docinfounicode = true;
488 protected $title = '';
494 protected $subject = '';
500 protected $author = '';
506 protected $keywords = '';
512 protected $creator = '';
515 * Starting page number.
518 protected $starting_page_number = 1;
521 * The right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image.
523 * @author Nicola Asuni
529 * The right-bottom corner Y coordinate of last inserted image.
531 * @author Nicola Asuni
537 * Adjusting factor to convert pixels to user units.
539 * @author Nicola Asuni
542 protected $imgscale = 1;
545 * Boolean flag set to true when the input text is unicode (require unicode fonts).
547 * @author Nicola Asuni
550 protected $isunicode = false;
557 protected $PDFVersion = '1.7';
560 * ID of the stored default header template (-1 = not set).
563 protected $header_xobjid = false;
566 * If true reset the Header Xobject template at each page
569 protected $header_xobj_autoreset = false;
572 * Minimum distance between header and top page margin.
575 protected $header_margin;
578 * Minimum distance between footer and bottom page margin.
581 protected $footer_margin;
584 * Original left margin value.
586 * @since 1.53.0.TC013
588 protected $original_lMargin;
591 * Original right margin value.
593 * @since 1.53.0.TC013
595 protected $original_rMargin;
598 * Default font used on page header.
601 protected $header_font;
604 * Default font used on page footer.
607 protected $footer_font;
610 * Language templates.
616 * Barcode to print on page footer (only if set).
619 protected $barcode = false;
622 * Boolean flag to print/hide page header.
625 protected $print_header = true;
628 * Boolean flag to print/hide page footer.
631 protected $print_footer = true;
637 protected $header_logo = '';
640 * Width of header image logo in user units.
643 protected $header_logo_width = 30;
646 * Title to be printed on default page header.
649 protected $header_title = '';
652 * String to pring on page header after title.
655 protected $header_string = '';
658 * Color for header text (RGB array).
659 * @since 5.9.174 (2012-07-25)
662 protected $header_text_color = array(0,0,0);
665 * Color for header line (RGB array).
666 * @since 5.9.174 (2012-07-25)
669 protected $header_line_color = array(0,0,0);
672 * Color for footer text (RGB array).
673 * @since 5.9.174 (2012-07-25)
676 protected $footer_text_color = array(0,0,0);
679 * Color for footer line (RGB array).
680 * @since 5.9.174 (2012-07-25)
683 protected $footer_line_color = array(0,0,0);
686 * Text shadow data array.
687 * @since 5.9.174 (2012-07-25)
690 protected $txtshadow = array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal');
693 * Default number of columns for html table.
696 protected $default_table_columns = 4;
698 // variables for html parser
701 * HTML PARSER: array to store current link and rendering styles.
704 protected $HREF = array();
707 * List of available fonts on filesystem.
710 protected $fontlist = array();
713 * Current foreground color.
719 * HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
722 protected $listordered = array();
725 * HTML PARSER: array count list items on nested lists.
728 protected $listcount = array();
731 * HTML PARSER: current list nesting level.
734 protected $listnum = 0;
737 * HTML PARSER: indent amount for lists.
740 protected $listindent = 0;
743 * HTML PARSER: current list indententation level.
746 protected $listindentlevel = 0;
749 * Current background color.
755 * Temporary font size in points.
758 protected $tempfontsize = 10;
761 * Spacer string for LI tags.
764 protected $lispacer = '';
769 * @since 1.53.0.TC010
771 protected $encoding = 'UTF-8';
774 * PHP internal encoding.
776 * @since 1.53.0.TC016
778 protected $internal_encoding;
781 * Boolean flag to indicate if the document language is Right-To-Left.
785 protected $rtl = false;
788 * Boolean flag used to force RTL or LTR string direction.
792 protected $tmprtl = false;
794 // --- Variables used for document encryption:
797 * IBoolean flag indicating whether document is protected.
799 * @since 2.0.000 (2008-01-02)
801 protected $encrypted;
804 * Array containing encryption settings.
806 * @since 5.0.005 (2010-05-11)
808 protected $encryptdata = array();
811 * Last RC4 key encrypted (cached for optimisation).
813 * @since 2.0.000 (2008-01-02)
815 protected $last_enc_key;
818 * Last RC4 computed key.
820 * @since 2.0.000 (2008-01-02)
822 protected $last_enc_key_c;
825 * File ID (used on document trailer).
827 * @since 5.0.005 (2010-05-12)
834 * Outlines for bookmark.
836 * @since 2.1.002 (2008-02-12)
838 protected $outlines = array();
841 * Outline root for bookmark.
843 * @since 2.1.002 (2008-02-12)
845 protected $OutlineRoot;
847 // --- javascript and form ---
852 * @since 2.1.002 (2008-02-12)
854 protected $javascript = '';
857 * Javascript counter.
859 * @since 2.1.002 (2008-02-12)
866 * @since 2.8.000 (2008-03-19)
868 protected $linethrough;
871 * Array with additional document-wide usage rights for the document.
873 * @since 5.8.014 (2010-08-23)
875 protected $ur = array();
878 * DPI (Dot Per Inch) Document Resolution (do not change).
880 * @since 3.0.000 (2008-03-27)
885 * Array of page numbers were a new page group was started (the page numbers are the keys of the array).
887 * @since 3.0.000 (2008-03-27)
889 protected $newpagegroup = array();
892 * Array that contains the number of pages in each page group.
894 * @since 3.0.000 (2008-03-27)
896 protected $pagegroups = array();
899 * Current page group number.
901 * @since 3.0.000 (2008-03-27)
903 protected $currpagegroup = 0;
906 * Array of transparency objects and parameters.
908 * @since 3.0.000 (2008-03-27)
910 protected $extgstates;
913 * Set the default JPEG compression quality (1-100).
915 * @since 3.0.000 (2008-03-27)
917 protected $jpeg_quality;
920 * Default cell height ratio.
922 * @since 3.0.014 (2008-05-23)
924 protected $cell_height_ratio = K_CELL_HEIGHT_RATIO
;
927 * PDF viewer preferences.
929 * @since 3.1.000 (2008-06-09)
931 protected $viewer_preferences;
934 * A name object specifying how the document should be displayed when opened.
936 * @since 3.1.000 (2008-06-09)
941 * Array for storing gradient information.
943 * @since 3.1.000 (2008-06-09)
945 protected $gradients = array();
948 * Array used to store positions inside the pages buffer (keys are the page numbers).
950 * @since 3.2.000 (2008-06-26)
952 protected $intmrk = array();
955 * Array used to store positions inside the pages buffer (keys are the page numbers).
957 * @since 5.7.000 (2010-08-03)
959 protected $bordermrk = array();
962 * Array used to store page positions to track empty pages (keys are the page numbers).
964 * @since 5.8.007 (2010-08-18)
966 protected $emptypagemrk = array();
969 * Array used to store content positions inside the pages buffer (keys are the page numbers).
971 * @since 4.6.021 (2009-07-20)
973 protected $cntmrk = array();
976 * Array used to store footer positions of each page.
978 * @since 3.2.000 (2008-07-01)
980 protected $footerpos = array();
983 * Array used to store footer length of each page.
985 * @since 4.0.014 (2008-07-29)
987 protected $footerlen = array();
990 * Boolean flag to indicate if a new line is created.
992 * @since 3.2.000 (2008-07-01)
994 protected $newline = true;
997 * End position of the latest inserted line.
999 * @since 3.2.000 (2008-07-01)
1001 protected $endlinex = 0;
1004 * PDF string for width value of the last line.
1006 * @since 4.0.006 (2008-07-16)
1008 protected $linestyleWidth = '';
1011 * PDF string for CAP value of the last line.
1013 * @since 4.0.006 (2008-07-16)
1015 protected $linestyleCap = '0 J';
1018 * PDF string for join value of the last line.
1020 * @since 4.0.006 (2008-07-16)
1022 protected $linestyleJoin = '0 j';
1025 * PDF string for dash value of the last line.
1027 * @since 4.0.006 (2008-07-16)
1029 protected $linestyleDash = '[] 0 d';
1032 * Boolean flag to indicate if marked-content sequence is open.
1034 * @since 4.0.013 (2008-07-28)
1036 protected $openMarkedContent = false;
1039 * Count the latest inserted vertical spaces on HTML.
1041 * @since 4.0.021 (2008-08-24)
1043 protected $htmlvspace = 0;
1046 * Array of Spot colors.
1048 * @since 4.0.024 (2008-09-12)
1050 protected $spot_colors = array();
1053 * Symbol used for HTML unordered list items.
1055 * @since 4.0.028 (2008-09-26)
1057 protected $lisymbol = '';
1060 * String used to mark the beginning and end of EPS image blocks.
1062 * @since 4.1.000 (2008-10-18)
1064 protected $epsmarker = 'x#!#EPS#!#x';
1067 * Array of transformation matrix.
1069 * @since 4.2.000 (2008-10-29)
1071 protected $transfmatrix = array();
1074 * Current key for transformation matrix.
1076 * @since 4.8.005 (2009-09-17)
1078 protected $transfmatrix_key = 0;
1081 * Booklet mode for double-sided pages.
1083 * @since 4.2.000 (2008-10-29)
1085 protected $booklet = false;
1088 * Epsilon value used for float calculations.
1090 * @since 4.2.000 (2008-10-29)
1092 protected $feps = 0.005;
1095 * Array used for custom vertical spaces for HTML tags.
1097 * @since 4.2.001 (2008-10-30)
1099 protected $tagvspaces = array();
1102 * HTML PARSER: custom indent amount for lists. Negative value means disabled.
1104 * @since 4.2.007 (2008-11-12)
1106 protected $customlistindent = -1;
1109 * Boolean flag to indicate if the border of the cell sides that cross the page should be removed.
1111 * @since 4.2.010 (2008-11-14)
1113 protected $opencell = true;
1116 * Array of files to embedd.
1118 * @since 4.4.000 (2008-12-07)
1120 protected $embeddedfiles = array();
1123 * Boolean flag to indicate if we are inside a PRE tag.
1125 * @since 4.4.001 (2008-12-08)
1127 protected $premode = false;
1130 * Array used to store positions of graphics transformation blocks inside the page buffer.
1131 * keys are the page numbers
1133 * @since 4.4.002 (2008-12-09)
1135 protected $transfmrk = array();
1138 * Default color for html links.
1140 * @since 4.4.003 (2008-12-09)
1142 protected $htmlLinkColorArray = array(0, 0, 255);
1145 * Default font style to add to html links.
1147 * @since 4.4.003 (2008-12-09)
1149 protected $htmlLinkFontStyle = 'U';
1152 * Counts the number of pages.
1154 * @since 4.5.000 (2008-12-31)
1156 protected $numpages = 0;
1159 * Array containing page lengths in bytes.
1161 * @since 4.5.000 (2008-12-31)
1163 protected $pagelen = array();
1166 * Counts the number of pages.
1168 * @since 4.5.000 (2008-12-31)
1170 protected $numimages = 0;
1173 * Store the image keys.
1175 * @since 4.5.000 (2008-12-31)
1177 protected $imagekeys = array();
1180 * Length of the buffer in bytes.
1182 * @since 4.5.000 (2008-12-31)
1184 protected $bufferlen = 0;
1187 * If true enables disk caching.
1189 * @since 4.5.000 (2008-12-31)
1191 protected $diskcache = false;
1194 * Counts the number of fonts.
1196 * @since 4.5.000 (2009-01-02)
1198 protected $numfonts = 0;
1201 * Store the font keys.
1203 * @since 4.5.000 (2009-01-02)
1205 protected $fontkeys = array();
1208 * Store the font object IDs.
1210 * @since 4.8.001 (2009-09-09)
1212 protected $font_obj_ids = array();
1215 * Store the fage status (true when opened, false when closed).
1217 * @since 4.5.000 (2009-01-02)
1219 protected $pageopen = array();
1222 * Default monospace font.
1224 * @since 4.5.025 (2009-03-10)
1226 protected $default_monospaced_font = 'courier';
1229 * Cloned copy of the current class object.
1231 * @since 4.5.029 (2009-03-19)
1236 * Array used to store the lengths of cache files.
1238 * @since 4.5.029 (2009-03-19)
1240 protected $cache_file_length = array();
1243 * Table header content to be repeated on each new page.
1245 * @since 4.5.030 (2009-03-20)
1247 protected $thead = '';
1250 * Margins used for table header.
1252 * @since 4.5.030 (2009-03-20)
1254 protected $theadMargins = array();
1257 * Boolean flag to enable document digital signature.
1259 * @since 4.6.005 (2009-04-24)
1261 protected $sign = false;
1264 * Digital signature data.
1266 * @since 4.6.005 (2009-04-24)
1268 protected $signature_data = array();
1271 * Digital signature max length.
1273 * @since 4.6.005 (2009-04-24)
1275 protected $signature_max_length = 11742;
1278 * Data for digital signature appearance.
1280 * @since 5.3.011 (2010-06-16)
1282 protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
1285 * Array of empty digital signature appearances.
1287 * @since 5.9.101 (2011-07-06)
1289 protected $empty_signature_appearance = array();
1292 * Boolean flag to enable document timestamping with TSA.
1294 * @since 6.0.085 (2014-06-19)
1296 protected $tsa_timestamp = false;
1299 * Timestamping data.
1301 * @since 6.0.085 (2014-06-19)
1303 protected $tsa_data = array();
1306 * Regular expression used to find blank characters (required for word-wrapping).
1308 * @since 4.6.006 (2009-04-28)
1310 protected $re_spaces = '/[^\S\xa0]/';
1313 * Array of $re_spaces parts.
1315 * @since 5.5.011 (2010-07-09)
1317 protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
1320 * Digital signature object ID.
1322 * @since 4.6.022 (2009-06-23)
1324 protected $sig_obj_id = 0;
1327 * ID of page objects.
1329 * @since 4.7.000 (2009-08-29)
1331 protected $page_obj_id = array();
1334 * List of form annotations IDs.
1336 * @since 4.8.000 (2009-09-07)
1338 protected $form_obj_id = array();
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.
1343 * @since 4.8.000 (2009-09-07)
1345 protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1348 * Javascript objects array.
1350 * @since 4.8.000 (2009-09-07)
1352 protected $js_objects = array();
1355 * Current form action (used during XHTML rendering).
1357 * @since 4.8.000 (2009-09-07)
1359 protected $form_action = '';
1362 * Current form encryption type (used during XHTML rendering).
1364 * @since 4.8.000 (2009-09-07)
1366 protected $form_enctype = 'application/x-www-form-urlencoded';
1369 * Current method to submit forms.
1371 * @since 4.8.000 (2009-09-07)
1373 protected $form_mode = 'post';
1376 * List of fonts used on form fields (fontname => fontkey).
1378 * @since 4.8.001 (2009-09-09)
1380 protected $annotation_fonts = array();
1383 * List of radio buttons parent objects.
1385 * @since 4.8.001 (2009-09-09)
1387 protected $radiobutton_groups = array();
1390 * List of radio group objects IDs.
1392 * @since 4.8.001 (2009-09-09)
1394 protected $radio_groups = array();
1397 * Text indentation value (used for text-indent CSS attribute).
1399 * @since 4.8.006 (2009-09-23)
1401 protected $textindent = 0;
1404 * Store page number when startTransaction() is called.
1406 * @since 4.8.006 (2009-09-23)
1408 protected $start_transaction_page = 0;
1411 * Store Y position when startTransaction() is called.
1413 * @since 4.9.001 (2010-03-28)
1415 protected $start_transaction_y = 0;
1418 * True when we are printing the thead section on a new page.
1420 * @since 4.8.027 (2010-01-25)
1422 protected $inthead = false;
1425 * Array of column measures (width, space, starting Y position).
1427 * @since 4.9.001 (2010-03-28)
1429 protected $columns = array();
1434 * @since 4.9.001 (2010-03-28)
1436 protected $num_columns = 1;
1439 * Current column number.
1441 * @since 4.9.001 (2010-03-28)
1443 protected $current_column = 0;
1446 * Starting page for columns.
1448 * @since 4.9.001 (2010-03-28)
1450 protected $column_start_page = 0;
1453 * Maximum page and column selected.
1455 * @since 5.8.000 (2010-08-11)
1457 protected $maxselcol = array('page' => 0, 'column' => 0);
1460 * Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding.
1462 * @since 5.8.000 (2010-08-11)
1464 protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
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.
1469 * @since 4.9.008 (2010-04-03)
1471 protected $textrendermode = 0;
1474 * Text stroke width in doc units.
1476 * @since 4.9.008 (2010-04-03)
1478 protected $textstrokewidth = 0;
1481 * Current stroke color.
1483 * @since 4.9.008 (2010-04-03)
1485 protected $strokecolor;
1488 * Default unit of measure for document.
1490 * @since 5.0.000 (2010-04-22)
1492 protected $pdfunit = 'mm';
1495 * Boolean flag true when we are on TOC (Table Of Content) page.
1498 protected $tocpage = false;
1501 * Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library.
1503 * @since 5.0.000 (2010-04-26)
1505 protected $rasterize_vector_images = false;
1508 * Boolean flag: if true enables font subsetting by default.
1510 * @since 5.3.002 (2010-06-07)
1512 protected $font_subsetting = true;
1515 * Array of default graphic settings.
1517 * @since 5.5.008 (2010-07-02)
1519 protected $default_graphic_vars = array();
1522 * Array of XObjects.
1524 * @since 5.8.014 (2010-08-23)
1526 protected $xobjects = array();
1529 * Boolean value true when we are inside an XObject.
1531 * @since 5.8.017 (2010-08-24)
1533 protected $inxobj = false;
1536 * Current XObject ID.
1538 * @since 5.8.017 (2010-08-24)
1540 protected $xobjid = '';
1543 * Percentage of character stretching.
1545 * @since 5.9.000 (2010-09-29)
1547 protected $font_stretching = 100;
1550 * Increases or decreases the space between characters in a text by the specified amount (tracking).
1552 * @since 5.9.000 (2010-09-29)
1554 protected $font_spacing = 0;
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)
1560 * @since 5.9.003 (2010-10-14)
1562 protected $page_regions = array();
1565 * Boolean value true when page region check is active.
1568 protected $check_page_regions = true;
1571 * Array of PDF layers data.
1573 * @since 5.9.102 (2011-07-13)
1575 protected $pdflayers = array();
1578 * A dictionary of names and corresponding destinations (Dests key on document Catalog).
1580 * @since 5.9.097 (2011-06-23)
1582 protected $dests = array();
1585 * Object ID for Named Destinations
1587 * @since 5.9.097 (2011-06-23)
1592 * Embedded Files Names
1594 * @since 5.9.204 (2013-01-23)
1596 protected $efnames = array();
1599 * Directory used for the last SVG image.
1601 * @since 5.0.000 (2010-05-05)
1603 protected $svgdir = '';
1606 * Deafult unit of measure for SVG.
1608 * @since 5.0.000 (2010-05-02)
1610 protected $svgunit = 'px';
1613 * Array of SVG gradients.
1615 * @since 5.0.000 (2010-05-02)
1617 protected $svggradients = array();
1620 * ID of last SVG gradient.
1622 * @since 5.0.000 (2010-05-02)
1624 protected $svggradientid = 0;
1627 * Boolean value true when in SVG defs group.
1629 * @since 5.0.000 (2010-05-02)
1631 protected $svgdefsmode = false;
1634 * Array of SVG defs.
1636 * @since 5.0.000 (2010-05-02)
1638 protected $svgdefs = array();
1641 * Boolean value true when in SVG clipPath tag.
1643 * @since 5.0.000 (2010-04-26)
1645 protected $svgclipmode = false;
1648 * Array of SVG clipPath commands.
1650 * @since 5.0.000 (2010-05-02)
1652 protected $svgclippaths = array();
1655 * Array of SVG clipPath tranformation matrix.
1657 * @since 5.8.022 (2010-08-31)
1659 protected $svgcliptm = array();
1662 * ID of last SVG clipPath.
1664 * @since 5.0.000 (2010-05-02)
1666 protected $svgclipid = 0;
1671 * @since 5.0.000 (2010-05-02)
1673 protected $svgtext = '';
1676 * SVG text properties.
1678 * @since 5.8.013 (2010-08-23)
1680 protected $svgtextmode = array();
1683 * Array of SVG properties.
1685 * @since 5.0.000 (2010-05-02)
1687 protected $svgstyles = array(array(
1688 'alignment-baseline' => 'auto',
1689 'baseline-shift' => 'baseline',
1691 'clip-path' => 'none',
1692 'clip-rule' => 'nonzero',
1694 'color-interpolation' => 'sRGB',
1695 'color-interpolation-filters' => 'linearRGB',
1696 'color-profile' => 'auto',
1697 'color-rendering' => 'auto',
1699 'direction' => 'ltr',
1700 'display' => 'inline',
1701 'dominant-baseline' => 'auto',
1702 'enable-background' => 'accumulate',
1704 'fill-opacity' => 1,
1705 'fill-rule' => 'nonzero',
1707 'flood-color' => 'black',
1708 'flood-opacity' => 1,
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',
1724 'marker-end' => 'none',
1725 'marker-mid' => 'none',
1726 'marker-start' => 'none',
1729 'overflow' => 'auto',
1730 'pointer-events' => 'visiblePainted',
1731 'shape-rendering' => 'auto',
1732 'stop-color' => 'black',
1733 'stop-opacity' => 1,
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)
1754 * If true force sRGB color profile for all document.
1756 * @since 5.9.121 (2011-09-28)
1758 protected $force_srgb = false;
1761 * If true set the document to PDF/A mode.
1763 * @since 5.9.121 (2011-09-27)
1765 protected $pdfa_mode = false;
1768 * Document creation date-time
1770 * @since 5.9.152 (2012-03-22)
1772 protected $doc_creation_timestamp;
1775 * Document modification date-time
1777 * @since 5.9.152 (2012-03-22)
1779 protected $doc_modification_timestamp;
1784 * @since 5.9.128 (2011-10-06)
1786 protected $custom_xmp = '';
1789 * Overprint mode array.
1790 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
1792 * @since 5.9.152 (2012-03-23)
1794 protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0);
1798 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
1800 * @since 5.9.152 (2012-03-23)
1802 protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false);
1805 * Define the page boundaries boxes to be set on document.
1807 * @since 5.9.152 (2012-03-23)
1809 protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
1812 * If true print TCPDF meta link.
1814 * @since 5.9.152 (2012-03-23)
1816 protected $tcpdflink = true;
1819 * Cache array for computed GD gamma values.
1821 * @since 5.9.1632 (2012-06-05)
1823 protected $gdgammacache = array();
1825 //------------------------------------------------------------
1827 //------------------------------------------------------------
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).
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.
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.
1843 * @see getPageSizeFromFormat(), setPageFormat()
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');
1851 // set file ID for trailer
1852 $serformat = (is_array($format) ? json_encode($format) : $format);
1853 $this->file_id
= md5(TCPDF_STATIC
::getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding));
1854 $this->font_obj_ids
= array();
1855 $this->page_obj_id
= array();
1856 $this->form_obj_id
= array();
1858 $this->pdfa_mode
= $pdfa;
1859 $this->force_srgb
= false;
1861 $this->diskcache
= $diskcache ? true : false;
1862 // set language direction
1864 $this->tmprtl
= false;
1867 // initialization of properties
1868 $this->isunicode
= $unicode;
1870 $this->transfmrk
[0] = array();
1871 $this->pagedim
= array();
1874 $this->pages
= array();
1876 $this->fonts
= array();
1877 $this->FontFiles
= array();
1878 $this->diffs
= array();
1879 $this->images
= array();
1880 $this->links
= array();
1881 $this->gradients
= array();
1882 $this->InFooter
= false;
1884 $this->FontFamily
= defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN
:'helvetica';
1885 $this->FontStyle
= '';
1886 $this->FontSizePt
= 12;
1887 $this->underline
= false;
1888 $this->overline
= false;
1889 $this->linethrough
= false;
1890 $this->DrawColor
= '0 G';
1891 $this->FillColor
= '0 g';
1892 $this->TextColor
= '0 g';
1893 $this->ColorFlag
= false;
1894 $this->pdflayers
= array();
1895 // encryption values
1896 $this->encrypted
= false;
1897 $this->last_enc_key
= '';
1898 // standard Unicode fonts
1899 $this->CoreFonts
= array(
1900 'courier'=>'Courier',
1901 'courierB'=>'Courier-Bold',
1902 'courierI'=>'Courier-Oblique',
1903 'courierBI'=>'Courier-BoldOblique',
1904 'helvetica'=>'Helvetica',
1905 'helveticaB'=>'Helvetica-Bold',
1906 'helveticaI'=>'Helvetica-Oblique',
1907 'helveticaBI'=>'Helvetica-BoldOblique',
1908 'times'=>'Times-Roman',
1909 'timesB'=>'Times-Bold',
1910 'timesI'=>'Times-Italic',
1911 'timesBI'=>'Times-BoldItalic',
1913 'zapfdingbats'=>'ZapfDingbats'
1916 $this->setPageUnit($unit);
1917 // set page format and orientation
1918 $this->setPageFormat($format, $orientation);
1919 // page margins (1 cm)
1920 $margin = 28.35 / $this->k
;
1921 $this->SetMargins($margin, $margin);
1922 $this->clMargin
= $this->lMargin
;
1923 $this->crMargin
= $this->rMargin
;
1924 // internal cell padding
1925 $cpadding = $margin / 10;
1926 $this->setCellPaddings($cpadding, 0, $cpadding, 0);
1928 $this->setCellMargins(0, 0, 0, 0);
1929 // line width (0.2 mm)
1930 $this->LineWidth
= 0.57 / $this->k
;
1931 $this->linestyleWidth
= sprintf('%F w', ($this->LineWidth
* $this->k
));
1932 $this->linestyleCap
= '0 J';
1933 $this->linestyleJoin
= '0 j';
1934 $this->linestyleDash
= '[] 0 d';
1935 // automatic page break
1936 $this->SetAutoPageBreak(true, (2 * $margin));
1937 // full width display mode
1938 $this->SetDisplayMode('fullwidth');
1940 $this->SetCompression();
1941 // set default PDF version number
1942 $this->setPDFVersion();
1943 $this->tcpdflink
= true;
1944 $this->encoding
= $encoding;
1945 $this->HREF
= array();
1946 $this->getFontsList();
1947 $this->fgcolor
= array('R' => 0, 'G' => 0, 'B' => 0);
1948 $this->strokecolor
= array('R' => 0, 'G' => 0, 'B' => 0);
1949 $this->bgcolor
= array('R' => 255, 'G' => 255, 'B' => 255);
1950 $this->extgstates
= array();
1951 $this->setTextShadow();
1953 $this->sign
= false;
1954 $this->tsa_timestamp
= false;
1955 $this->tsa_data
= array();
1956 $this->signature_appearance
= array('page' => 1, 'rect' => '0 0 0 0', 'name' => 'Signature');
1957 $this->empty_signature_appearance
= array();
1959 $this->ur
['enabled'] = false;
1960 $this->ur
['document'] = '/FullSave';
1961 $this->ur
['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
1962 $this->ur
['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1963 $this->ur
['signature'] = '/Modify';
1964 $this->ur
['ef'] = '/Create/Delete/Modify/Import';
1965 $this->ur
['formex'] = '';
1966 // set default JPEG quality
1967 $this->jpeg_quality
= 75;
1968 // initialize some settings
1969 TCPDF_FONTS
::utf8Bidi(array(''), '', false, $this->isunicode
, $this->CurrentFont
);
1971 $this->SetFont($this->FontFamily
, $this->FontStyle
, $this->FontSizePt
);
1972 $this->setHeaderFont(array($this->FontFamily
, $this->FontStyle
, $this->FontSizePt
));
1973 $this->setFooterFont(array($this->FontFamily
, $this->FontStyle
, $this->FontSizePt
));
1974 // check if PCRE Unicode support is enabled
1975 if ($this->isunicode
AND (@preg_match('/\pL/u', 'a') == 1)) {
1976 // PCRE unicode support is turned ON
1977 // \s : any whitespace character
1978 // \p{Z} : any separator
1979 // \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words.
1980 // \xa0 : Unicode Character 'NO-BREAK SPACE' (U+00A0)
1981 //$this->setSpacesRE('/(?!\xa0)[\s\p{Z}\p{Lo}]/u');
1982 $this->setSpacesRE('/(?!\xa0)[\s\p{Z}]/u');
1984 // PCRE unicode support is turned OFF
1985 $this->setSpacesRE('/[^\S\xa0]/');
1987 $this->default_form_prop
= array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
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
= '';
1998 * Default destructor.
2000 * @since 1.53.0.TC016
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
);
2007 // unset all class variables
2008 $this->_destroy(true);
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.
2015 * @since 3.0.015 (2008-06-06)
2017 public function setPageUnit($unit) {
2018 $unit = strtolower($unit);
2029 $this->k
= $this->dpi
/ 25.4;
2034 $this->k
= $this->dpi
/ 2.54;
2039 $this->k
= $this->dpi
;
2044 $this->Error('Incorrect unit: '.$unit);
2048 $this->pdfunit
= $unit;
2049 if (isset($this->CurOrientation
)) {
2050 $this->setPageOrientation($this->CurOrientation
);
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>
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>
2106 * @since 3.0.015 (2008-06-06)
2107 * @see getPageSizeFromFormat()
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
]);
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];
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
);
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
));
2129 if (!isset($format['format'])) {
2131 $format['format'] = 'A4';
2133 $pf = TCPDF_STATIC
::getPageSizeFromFormat($format['format']);
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
);
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
);
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
);
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
);
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
);
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'];
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']);
2163 if (isset($format['PZ'])) {
2164 // The page's preferred zoom (magnification) factor
2165 $this->pagedim
[$this->page
]['PZ'] = floatval($format['PZ']);
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']);
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'];
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'];
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']);
2195 if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
2196 $this->pagedim
[$this->page
]['trans']['SS'] = floatval($format['trans']['SS']);
2198 if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
2199 $this->pagedim
[$this->page
]['trans']['B'] = 'true';
2202 $this->pagedim
[$this->page
]['trans']['S'] = 'R';
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']);
2208 $this->pagedim
[$this->page
]['trans']['D'] = 1;
2212 $this->setPageOrientation($orientation);
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.
2221 * @since 3.0.015 (2008-06-06)
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
);
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
);
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
);
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
);
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
);
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;
2248 if (!isset($this->pagedim
[$this->page
]['PZ'])) {
2249 // The page's preferred zoom (magnification) factor
2250 $this->pagedim
[$this->page
]['PZ'] = 1;
2252 if ($this->fwPt
> $this->fhPt
) {
2254 $default_orientation = 'L';
2257 $default_orientation = 'P';
2259 $valid_orientations = array('P', 'L');
2260 if (empty($orientation)) {
2261 $orientation = $default_orientation;
2263 $orientation = strtoupper($orientation[0]);
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
;
2270 $this->CurOrientation
= $default_orientation;
2271 $this->wPt
= $this->fwPt
;
2272 $this->hPt
= $this->fhPt
;
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
);
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
;
2284 $autopagebreak = true;
2287 if (TCPDF_STATIC
::empty_string($bottommargin)) {
2288 if (isset($this->bMargin
)) {
2289 $bottommargin = $this->bMargin
;
2291 // default value = 2 cm
2292 $bottommargin = 2 * 28.35 / $this->k
;
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
;
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:
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)
2325 * @param $re (string) regular expression (leave empty for default).
2327 * @since 4.6.016 (2009-06-15)
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];
2337 $this->re_space
['p'] = '[\s]';
2339 // set pattern modifiers
2340 if (isset($re_parts[2]) AND !empty($re_parts[2])) {
2341 $this->re_space
['m'] = $re_parts[2];
2343 $this->re_space
['m'] = '';
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.
2352 * @since 2.0.000 (2008-01-03)
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;
2365 * Return the RTL status
2368 * @since 4.0.012 (2008-07-24)
2370 public function getRTL() {
2375 * Force temporary RTL language direction
2376 * @param $mode (mixed) can be false, 'L' for LTR or 'R' for RTL
2378 * @since 2.1.000 (2008-01-09)
2380 public function setTempRTL($mode) {
2382 switch (strtoupper($mode)) {
2403 $this->tmprtl
= $newmode;
2407 * Return the current temporary RTL status
2410 * @since 4.8.014 (2009-11-04)
2412 public function isRTLTextDir() {
2413 return ($this->rtl
OR ($this->tmprtl
== 'R'));
2417 * Set the last cell height.
2418 * @param $h (float) cell height.
2419 * @author Nicola Asuni
2421 * @since 1.53.0.TC034
2423 public function setLastH($h) {
2428 * Return the cell height
2429 * @param $fontsize (int) Font size in internal units
2430 * @param $padding (boolean) If true add cell padding
2433 public function getCellHeight($fontsize, $padding=TRUE) {
2434 $height = ($fontsize * $this->cell_height_ratio
);
2436 $height +
= ($this->cell_padding
['T'] +
$this->cell_padding
['B']);
2438 return round($height, 6);
2442 * Reset the last cell height.
2444 * @since 5.9.000 (2010-10-03)
2446 public function resetLastH() {
2447 $this->lasth
= $this->getCellHeight($this->FontSize
);
2451 * Get the last cell height.
2452 * @return last cell height
2454 * @since 4.0.017 (2008-08-05)
2456 public function getLastH() {
2457 return $this->lasth
;
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
2467 public function setImageScale($scale) {
2468 $this->imgscale
= $scale;
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
2478 public function getImageScale() {
2479 return $this->imgscale
;
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
2489 * @since 4.5.027 (2009-03-16)
2491 public function getPageDimensions($pagenum='') {
2492 if (empty($pagenum)) {
2493 $pagenum = $this->page
;
2495 return $this->pagedim
[$pagenum];
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
2505 * @see getPageDimensions()
2507 public function getPageWidth($pagenum='') {
2508 if (empty($pagenum)) {
2511 return $this->pagedim
[$pagenum]['w'];
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
2521 * @see getPageDimensions()
2523 public function getPageHeight($pagenum='') {
2524 if (empty($pagenum)) {
2527 return $this->pagedim
[$pagenum]['h'];
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
2537 * @see getPageDimensions()
2539 public function getBreakMargin($pagenum='') {
2540 if (empty($pagenum)) {
2541 return $this->bMargin
;
2543 return $this->pagedim
[$pagenum]['bm'];
2547 * Returns the scale factor (number of points in user unit).
2548 * @return int scale factor.
2549 * @author Nicola Asuni
2553 public function getScaleFactor() {
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
2565 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
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;
2574 $this->rMargin
= $right;
2576 // overwrite original values
2577 $this->original_lMargin
= $this->lMargin
;
2578 $this->original_rMargin
= $this->rMargin
;
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.
2587 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
2589 public function SetLeftMargin($margin) {
2591 $this->lMargin
= $margin;
2592 if (($this->page
> 0) AND ($this->x
< $margin)) {
2598 * Defines the top margin. The method can be called before creating the first page.
2599 * @param $margin (float) The margin.
2602 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
2604 public function SetTopMargin($margin) {
2606 $this->tMargin
= $margin;
2607 if (($this->page
> 0) AND ($this->y
< $margin)) {
2613 * Defines the right margin. The method can be called before creating the first page.
2614 * @param $margin (float) The margin.
2617 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
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;
2627 * Set the same internal Cell padding for top, right, bottom, left-
2628 * @param $pad (float) internal padding.
2630 * @since 2.1.000 (2008-01-09)
2631 * @see getCellPaddings(), setCellPaddings()
2633 public function SetCellPadding($pad) {
2635 $this->cell_padding
['L'] = $pad;
2636 $this->cell_padding
['T'] = $pad;
2637 $this->cell_padding
['R'] = $pad;
2638 $this->cell_padding
['B'] = $pad;
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
2649 * @since 5.9.000 (2010-10-03)
2650 * @see getCellPaddings(), SetCellPadding()
2652 public function setCellPaddings($left='', $top='', $right='', $bottom='') {
2653 if (($left !== '') AND ($left >= 0)) {
2654 $this->cell_padding
['L'] = $left;
2656 if (($top !== '') AND ($top >= 0)) {
2657 $this->cell_padding
['T'] = $top;
2659 if (($right !== '') AND ($right >= 0)) {
2660 $this->cell_padding
['R'] = $right;
2662 if (($bottom !== '') AND ($bottom >= 0)) {
2663 $this->cell_padding
['B'] = $bottom;
2668 * Get the internal Cell padding array.
2669 * @return array of padding values
2671 * @since 5.9.000 (2010-10-03)
2672 * @see setCellPaddings(), SetCellPadding()
2674 public function getCellPaddings() {
2675 return $this->cell_padding
;
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
2685 * @since 5.9.000 (2010-10-03)
2686 * @see getCellMargins()
2688 public function setCellMargins($left='', $top='', $right='', $bottom='') {
2689 if (($left !== '') AND ($left >= 0)) {
2690 $this->cell_margin
['L'] = $left;
2692 if (($top !== '') AND ($top >= 0)) {
2693 $this->cell_margin
['T'] = $top;
2695 if (($right !== '') AND ($right >= 0)) {
2696 $this->cell_margin
['R'] = $right;
2698 if (($bottom !== '') AND ($bottom >= 0)) {
2699 $this->cell_margin
['B'] = $bottom;
2704 * Get the internal Cell margin array.
2705 * @return array of margin values
2707 * @since 5.9.000 (2010-10-03)
2708 * @see setCellMargins()
2710 public function getCellMargins() {
2711 return $this->cell_margin
;
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
2719 * @since 5.9.000 (2010-10-03)
2721 protected function adjustCellPadding($brd=0) {
2725 if (is_string($brd)) {
2726 // convert string to array
2727 $slen = strlen($brd);
2729 for ($i = 0; $i < $slen; ++
$i) {
2730 $newbrd[$brd[$i]] = true;
2733 } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) {
2734 $brd = array('LRTB' => true);
2736 if (!is_array($brd)) {
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']);
2749 foreach ($brd as $border => $style) {
2750 $line_width = $this->LineWidth
;
2751 if (is_array($style) AND isset($style['width'])) {
2753 $line_width = $style['width'];
2755 $adj = 0; // line width inside the cell
2767 $adj = ($line_width / 2);
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;
2775 if ((strpos($border,'R') !== false) AND ($this->cell_padding
['R'] < $adj)) {
2776 $this->cell_padding
['R'] = $adj;
2778 if ((strpos($border,'B') !== false) AND ($this->cell_padding
['B'] < $adj)) {
2779 $this->cell_padding
['B'] = $adj;
2781 if ((strpos($border,'L') !== false) AND ($this->cell_padding
['L'] < $adj)) {
2782 $this->cell_padding
['L'] = $adj;
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']));
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.
2794 * @see Cell(), MultiCell(), AcceptPageBreak()
2796 public function SetAutoPageBreak($auto, $margin=0) {
2797 $this->AutoPageBreak
= $auto ? true : false;
2798 $this->bMargin
= $margin;
2799 $this->PageBreakTrigger
= $this->h
- $margin;
2803 * Return the auto-page-break mode (true or false).
2804 * @return boolean auto-page-break mode
2808 public function getAutoPageBreak() {
2809 return $this->AutoPageBreak
;
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>
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;
2824 $this->Error('Incorrect zoom display mode: '.$zoom);
2826 $this->LayoutMode
= TCPDF_STATIC
::getPageLayoutMode($layout);
2827 $this->PageMode
= TCPDF_STATIC
::getPageMode($mode);
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.
2837 public function SetCompression($compress=true) {
2838 if (function_exists('gzcompress')) {
2839 $this->compress
= $compress ? true : false;
2841 $this->compress
= false;
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.
2849 * @since 5.9.121 (2011-09-28)
2851 public function setSRGBmode($mode=false) {
2852 $this->force_srgb
= $mode ? true : false;
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)
2862 public function SetDocInfoUnicode($unicode=true) {
2863 $this->docinfounicode
= $unicode ? true : false;
2867 * Defines the title of the document.
2868 * @param $title (string) The title.
2871 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
2873 public function SetTitle($title) {
2874 $this->title
= $title;
2878 * Defines the subject of the document.
2879 * @param $subject (string) The subject.
2882 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
2884 public function SetSubject($subject) {
2885 $this->subject
= $subject;
2889 * Defines the author of the document.
2890 * @param $author (string) The name of the author.
2893 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
2895 public function SetAuthor($author) {
2896 $this->author
= $author;
2900 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
2901 * @param $keywords (string) The list of keywords.
2904 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
2906 public function SetKeywords($keywords) {
2907 $this->keywords
= $keywords;
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.
2915 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
2917 public function SetCreator($creator) {
2918 $this->creator
= $creator;
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
2927 public function Error($msg) {
2928 // unset all class variables
2929 $this->_destroy(true);
2930 throw new Exception('TCPDF ERROR: '.$msg);
2933 I had problems with the constants for some reason, so here we force
2935 $this->_destroy(true);
2936 if (defined('K_TCPDF_THROW_EXCEPTION_ERROR') AND !K_TCPDF_THROW_EXCEPTION_ERROR) {
2937 die('<strong>TCPDF ERROR: </strong>'.$msg);
2939 throw new Exception('TCPDF ERROR: '.$msg);
2944 * This method begins the generation of the PDF document.
2945 * It is not necessary to call it explicitly because AddPage() does it automatically.
2946 * Note: no page is created by this method
2949 * @see AddPage(), Close()
2951 public function Open() {
2956 * Terminates the PDF document.
2957 * It is not necessary to call this method explicitly because Output() does it automatically.
2958 * If the document contains no page, AddPage() is called to prevent from getting an invalid document.
2961 * @see Open(), Output()
2963 public function Close() {
2964 if ($this->state
== 3) {
2967 if ($this->page
== 0) {
2971 if ($this->tcpdflink
) {
2972 // save current graphic settings
2973 $gvars = $this->getGraphicVars();
2974 $this->setEqualColumns();
2975 $this->lastpage(true);
2976 $this->SetAutoPageBreak(false);
2978 $this->y
= $this->h
- (1 / $this->k
);
2980 $this->_outSaveGraphicsState();
2981 $font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN
:'helvetica';
2982 $this->SetFont($font, '', 1);
2983 $this->setTextRenderingMode(0, false, false);
2984 $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";
2985 $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
2986 $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
2987 $this->_outRestoreGraphicsState();
2988 // restore graphic settings
2989 $this->setGraphicVars($gvars);
2995 // unset all class variables (except critical ones)
2996 $this->_destroy(false);
3000 * Move pointer at the specified document page and update page dimensions.
3001 * @param $pnum (int) page number (1 ... numpages)
3002 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
3004 * @since 2.1.000 (2008-01-07)
3005 * @see getPage(), lastpage(), getNumPages()
3007 public function setPage($pnum, $resetmargins=false) {
3008 if (($pnum == $this->page
) AND ($this->state
== 2)) {
3011 if (($pnum > 0) AND ($pnum <= $this->numpages
)) {
3013 // save current graphic settings
3014 //$gvars = $this->getGraphicVars();
3015 $oldpage = $this->page
;
3016 $this->page
= $pnum;
3017 $this->wPt
= $this->pagedim
[$this->page
]['w'];
3018 $this->hPt
= $this->pagedim
[$this->page
]['h'];
3019 $this->w
= $this->pagedim
[$this->page
]['wk'];
3020 $this->h
= $this->pagedim
[$this->page
]['hk'];
3021 $this->tMargin
= $this->pagedim
[$this->page
]['tm'];
3022 $this->bMargin
= $this->pagedim
[$this->page
]['bm'];
3023 $this->original_lMargin
= $this->pagedim
[$this->page
]['olm'];
3024 $this->original_rMargin
= $this->pagedim
[$this->page
]['orm'];
3025 $this->AutoPageBreak
= $this->pagedim
[$this->page
]['pb'];
3026 $this->CurOrientation
= $this->pagedim
[$this->page
]['or'];
3027 $this->SetAutoPageBreak($this->AutoPageBreak
, $this->bMargin
);
3028 // restore graphic settings
3029 //$this->setGraphicVars($gvars);
3030 if ($resetmargins) {
3031 $this->lMargin
= $this->pagedim
[$this->page
]['olm'];
3032 $this->rMargin
= $this->pagedim
[$this->page
]['orm'];
3033 $this->SetY($this->tMargin
);
3035 // account for booklet mode
3036 if ($this->pagedim
[$this->page
]['olm'] != $this->pagedim
[$oldpage]['olm']) {
3037 $deltam = $this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$this->page
]['orm'];
3038 $this->lMargin +
= $deltam;
3039 $this->rMargin
-= $deltam;
3043 $this->Error('Wrong page number on setPage() function: '.$pnum);
3048 * Reset pointer to the last document page.
3049 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
3051 * @since 2.0.000 (2008-01-04)
3052 * @see setPage(), getPage(), getNumPages()
3054 public function lastPage($resetmargins=false) {
3055 $this->setPage($this->getNumPages(), $resetmargins);
3059 * Get current document page number.
3060 * @return int page number
3062 * @since 2.1.000 (2008-01-07)
3063 * @see setPage(), lastpage(), getNumPages()
3065 public function getPage() {
3070 * Get the total number of insered pages.
3071 * @return int number of pages
3073 * @since 2.1.000 (2008-01-07)
3074 * @see setPage(), getPage(), lastpage()
3076 public function getNumPages() {
3077 return $this->numpages
;
3081 * Adds a new TOC (Table Of Content) page to the document.
3082 * @param $orientation (string) page orientation.
3083 * @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().
3084 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
3086 * @since 5.0.001 (2010-05-06)
3087 * @see AddPage(), startPage(), endPage(), endTOCPage()
3089 public function addTOCPage($orientation='', $format='', $keepmargins=false) {
3090 $this->AddPage($orientation, $format, $keepmargins, true);
3094 * Terminate the current TOC (Table Of Content) page
3096 * @since 5.0.001 (2010-05-06)
3097 * @see AddPage(), startPage(), endPage(), addTOCPage()
3099 public function endTOCPage() {
3100 $this->endPage(true);
3104 * 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).
3105 * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.
3106 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3107 * @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().
3108 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
3109 * @param $tocpage (boolean) if true set the tocpage state to true (the added page will be used to display Table Of Content).
3112 * @see startPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3114 public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
3115 if ($this->inxobj
) {
3116 // we are inside an XObject template
3119 if (!isset($this->original_lMargin
) OR $keepmargins) {
3120 $this->original_lMargin
= $this->lMargin
;
3122 if (!isset($this->original_rMargin
) OR $keepmargins) {
3123 $this->original_rMargin
= $this->rMargin
;
3125 // terminate previous page
3128 $this->startPage($orientation, $format, $tocpage);
3132 * Terminate the current page
3133 * @param $tocpage (boolean) if true set the tocpage state to false (end the page used to display Table Of Content).
3135 * @since 4.2.010 (2008-11-14)
3136 * @see AddPage(), startPage(), addTOCPage(), endTOCPage()
3138 public function endPage($tocpage=false) {
3139 // check if page is already closed
3140 if (($this->page
== 0) OR ($this->numpages
> $this->page
) OR (!$this->pageopen
[$this->page
])) {
3143 // print page footer
3147 // mark page as closed
3148 $this->pageopen
[$this->page
] = false;
3150 $this->tocpage
= false;
3155 * Starts a new page to the document. The page must be closed using the endPage() function.
3156 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
3157 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3158 * @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().
3159 * @param $tocpage (boolean) if true the page is designated to contain the Table-Of-Content.
3160 * @since 4.2.010 (2008-11-14)
3161 * @see AddPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3164 public function startPage($orientation='', $format='', $tocpage=false) {
3166 $this->tocpage
= true;
3168 // move page numbers of documents to be attached
3169 if ($this->tocpage
) {
3170 // move reference to unexistent pages (used for page attachments)
3172 $tmpoutlines = $this->outlines
;
3173 foreach ($tmpoutlines as $key => $outline) {
3174 if (!$outline['f'] AND ($outline['p'] > $this->numpages
)) {
3175 $this->outlines
[$key]['p'] = ($outline['p'] +
1);
3179 $tmpdests = $this->dests
;
3180 foreach ($tmpdests as $key => $dest) {
3181 if (!$dest['f'] AND ($dest['p'] > $this->numpages
)) {
3182 $this->dests
[$key]['p'] = ($dest['p'] +
1);
3186 $tmplinks = $this->links
;
3187 foreach ($tmplinks as $key => $link) {
3188 if (!$link['f'] AND ($link['p'] > $this->numpages
)) {
3189 $this->links
[$key]['p'] = ($link['p'] +
1);
3193 if ($this->numpages
> $this->page
) {
3194 // this page has been already added
3195 $this->setPage($this->page +
1);
3196 $this->SetY($this->tMargin
);
3200 if ($this->state
== 0) {
3204 $this->swapMargins($this->booklet
);
3205 // save current graphic settings
3206 $gvars = $this->getGraphicVars();
3208 $this->_beginpage($orientation, $format);
3209 // mark page as open
3210 $this->pageopen
[$this->page
] = true;
3211 // restore graphic settings
3212 $this->setGraphicVars($gvars);
3214 $this->setPageMark();
3215 // print page header
3217 // restore graphic settings
3218 $this->setGraphicVars($gvars);
3220 $this->setPageMark();
3221 // print table header (if any)
3222 $this->setTableHeader();
3223 // set mark for empty page check
3224 $this->emptypagemrk
[$this->page
]= $this->pagelen
[$this->page
];
3228 * Set start-writing mark on current page stream used to put borders and fills.
3229 * Borders and fills are always created after content and inserted on the position marked by this method.
3230 * This function must be called after calling Image() function for a background image.
3231 * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
3233 * @since 4.0.016 (2008-07-30)
3235 public function setPageMark() {
3236 $this->intmrk
[$this->page
] = $this->pagelen
[$this->page
];
3237 $this->bordermrk
[$this->page
] = $this->intmrk
[$this->page
];
3238 $this->setContentMark();
3242 * Set start-writing mark on selected page.
3243 * Borders and fills are always created after content and inserted on the position marked by this method.
3244 * @param $page (int) page number (default is the current page)
3246 * @since 4.6.021 (2009-07-20)
3248 protected function setContentMark($page=0) {
3250 $page = $this->page
;
3252 if (isset($this->footerlen
[$page])) {
3253 $this->cntmrk
[$page] = $this->pagelen
[$page] - $this->footerlen
[$page];
3255 $this->cntmrk
[$page] = $this->pagelen
[$page];
3261 * @param $ln (string) header image logo
3262 * @param $lw (string) header image logo width in mm
3263 * @param $ht (string) string to print as title on document header
3264 * @param $hs (string) string to print on document header
3265 * @param $tc (array) RGB array color for text.
3266 * @param $lc (array) RGB array color for line.
3269 public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) {
3270 $this->header_logo
= $ln;
3271 $this->header_logo_width
= $lw;
3272 $this->header_title
= $ht;
3273 $this->header_string
= $hs;
3274 $this->header_text_color
= $tc;
3275 $this->header_line_color
= $lc;
3280 * @param $tc (array) RGB array color for text.
3281 * @param $lc (array) RGB array color for line.
3284 public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) {
3285 $this->footer_text_color
= $tc;
3286 $this->footer_line_color
= $lc;
3290 * Returns header data:
3291 * <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>
3294 * @since 4.0.012 (2008-07-24)
3296 public function getHeaderData() {
3298 $ret['logo'] = $this->header_logo
;
3299 $ret['logo_width'] = $this->header_logo_width
;
3300 $ret['title'] = $this->header_title
;
3301 $ret['string'] = $this->header_string
;
3302 $ret['text_color'] = $this->header_text_color
;
3303 $ret['line_color'] = $this->header_line_color
;
3308 * Set header margin.
3309 * (minimum distance between header and top page margin)
3310 * @param $hm (int) distance in user units
3313 public function setHeaderMargin($hm=10) {
3314 $this->header_margin
= $hm;
3318 * Returns header margin in user units.
3320 * @since 4.0.012 (2008-07-24)
3323 public function getHeaderMargin() {
3324 return $this->header_margin
;
3328 * Set footer margin.
3329 * (minimum distance between footer and bottom page margin)
3330 * @param $fm (int) distance in user units
3333 public function setFooterMargin($fm=10) {
3334 $this->footer_margin
= $fm;
3338 * Returns footer margin in user units.
3340 * @since 4.0.012 (2008-07-24)
3343 public function getFooterMargin() {
3344 return $this->footer_margin
;
3347 * Set a flag to print page header.
3348 * @param $val (boolean) set to true to print the page header (default), false otherwise.
3351 public function setPrintHeader($val=true) {
3352 $this->print_header
= $val ? true : false;
3356 * Set a flag to print page footer.
3357 * @param $val (boolean) set to true to print the page footer (default), false otherwise.
3360 public function setPrintFooter($val=true) {
3361 $this->print_footer
= $val ? true : false;
3365 * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
3369 public function getImageRBX() {
3370 return $this->img_rb_x
;
3374 * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
3378 public function getImageRBY() {
3379 return $this->img_rb_y
;
3383 * Reset the xobject template used by Header() method.
3386 public function resetHeaderTemplate() {
3387 $this->header_xobjid
= false;
3391 * Set a flag to automatically reset the xobject template used by Header() method at each page.
3392 * @param $val (boolean) set to true to reset Header xobject template at each page, false otherwise.
3395 public function setHeaderTemplateAutoreset($val=true) {
3396 $this->header_xobj_autoreset
= $val ? true : false;
3400 * This method is used to render the page header.
3401 * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3404 public function Header() {
3405 if ($this->header_xobjid
=== false) {
3406 // start a new XObject Template
3407 $this->header_xobjid
= $this->startTemplate($this->w
, $this->tMargin
);
3408 $headerfont = $this->getHeaderFont();
3409 $headerdata = $this->getHeaderData();
3410 $this->y
= $this->header_margin
;
3412 $this->x
= $this->w
- $this->original_rMargin
;
3414 $this->x
= $this->original_lMargin
;
3416 if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE
)) {
3417 $imgtype = TCPDF_IMAGES
::getImageFileType(K_PATH_IMAGES
.$headerdata['logo']);
3418 if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
3419 $this->ImageEps(K_PATH_IMAGES
.$headerdata['logo'], '', '', $headerdata['logo_width']);
3420 } elseif ($imgtype == 'svg') {
3421 $this->ImageSVG(K_PATH_IMAGES
.$headerdata['logo'], '', '', $headerdata['logo_width']);
3423 $this->Image(K_PATH_IMAGES
.$headerdata['logo'], '', '', $headerdata['logo_width']);
3425 $imgy = $this->getImageRBY();
3429 $cell_height = $this->getCellHeight($headerfont[2] / $this->k
);
3430 // set starting margin for text data cell
3431 if ($this->getRTL()) {
3432 $header_x = $this->original_rMargin +
($headerdata['logo_width'] * 1.1);
3434 $header_x = $this->original_lMargin +
($headerdata['logo_width'] * 1.1);
3436 $cw = $this->w
- $this->original_lMargin
- $this->original_rMargin
- ($headerdata['logo_width'] * 1.1);
3437 $this->SetTextColorArray($this->header_text_color
);
3439 $this->SetFont($headerfont[0], 'B', $headerfont[2] +
1);
3440 $this->SetX($header_x);
3441 $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
3443 $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
3444 $this->SetX($header_x);
3445 $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false);
3446 // print an ending header line
3447 $this->SetLineStyle(array('width' => 0.85 / $this->k
, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color']));
3448 $this->SetY((2.835 / $this->k
) +
max($imgy, $this->y
));
3450 $this->SetX($this->original_rMargin
);
3452 $this->SetX($this->original_lMargin
);
3454 $this->Cell(($this->w
- $this->original_lMargin
- $this->original_rMargin
), 0, '', 'T', 0, 'C');
3455 $this->endTemplate();
3457 // print header template
3460 if (!$this->header_xobj_autoreset
AND $this->booklet
AND (($this->page %
2) == 0)) {
3461 // adjust margins for booklet mode
3462 $dx = ($this->original_lMargin
- $this->original_rMargin
);
3465 $x = $this->w +
$dx;
3469 $this->printTemplate($this->header_xobjid
, $x, 0, 0, 0, '', '', false);
3470 if ($this->header_xobj_autoreset
) {
3471 // reset header xobject template at each page
3472 $this->header_xobjid
= false;
3477 * This method is used to render the page footer.
3478 * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3481 public function Footer() {
3483 $this->SetTextColorArray($this->footer_text_color
);
3484 //set style for cell border
3485 $line_width = (0.85 / $this->k
);
3486 $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color
));
3487 //print document barcode
3488 $barcode = $this->getBarcode();
3489 if (!empty($barcode)) {
3490 $this->Ln($line_width);
3491 $barcode_width = round(($this->w
- $this->original_lMargin
- $this->original_rMargin
) / 3);
3493 'position' => $this->rtl
?'R':'L',
3494 'align' => $this->rtl
?'R':'L',
3497 'cellfitalign' => '',
3500 'fgcolor' => array(0,0,0),
3504 $this->write1DBarcode($barcode, 'C128', '', $cur_y +
$line_width, '', (($this->footer_margin
/ 3) - $line_width), 0.3, $style, '');
3506 $w_page = isset($this->l
['w_page']) ? $this->l
['w_page'].' ' : '';
3507 if (empty($this->pagegroups
)) {
3508 $pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
3510 $pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
3512 $this->SetY($cur_y);
3514 if ($this->getRTL()) {
3515 $this->SetX($this->original_rMargin
);
3516 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
3518 $this->SetX($this->original_lMargin
);
3519 $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R');
3524 * This method is used to render the page header.
3526 * @since 4.0.012 (2008-07-24)
3528 protected function setHeader() {
3529 if (!$this->print_header
OR ($this->state
!= 2)) {
3532 $this->InHeader
= true;
3533 $this->setGraphicVars($this->default_graphic_vars
);
3534 $temp_thead = $this->thead
;
3535 $temp_theadMargins = $this->theadMargins
;
3536 $lasth = $this->lasth
;
3537 $newline = $this->newline
;
3538 $this->_outSaveGraphicsState();
3539 $this->rMargin
= $this->original_rMargin
;
3540 $this->lMargin
= $this->original_lMargin
;
3541 $this->SetCellPadding(0);
3542 //set current position
3544 $this->SetXY($this->original_rMargin
, $this->header_margin
);
3546 $this->SetXY($this->original_lMargin
, $this->header_margin
);
3548 $this->SetFont($this->header_font
[0], $this->header_font
[1], $this->header_font
[2]);
3552 $this->SetXY($this->original_rMargin
, $this->tMargin
);
3554 $this->SetXY($this->original_lMargin
, $this->tMargin
);
3556 $this->_outRestoreGraphicsState();
3557 $this->lasth
= $lasth;
3558 $this->thead
= $temp_thead;
3559 $this->theadMargins
= $temp_theadMargins;
3560 $this->newline
= $newline;
3561 $this->InHeader
= false;
3565 * This method is used to render the page footer.
3567 * @since 4.0.012 (2008-07-24)
3569 protected function setFooter() {
3570 if ($this->state
!= 2) {
3573 $this->InFooter
= true;
3574 // save current graphic settings
3575 $gvars = $this->getGraphicVars();
3577 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
];
3579 if ($this->print_footer
) {
3580 $this->setGraphicVars($this->default_graphic_vars
);
3581 $this->current_column
= 0;
3582 $this->num_columns
= 1;
3583 $temp_thead = $this->thead
;
3584 $temp_theadMargins = $this->theadMargins
;
3585 $lasth = $this->lasth
;
3586 $this->_outSaveGraphicsState();
3587 $this->rMargin
= $this->original_rMargin
;
3588 $this->lMargin
= $this->original_lMargin
;
3589 $this->SetCellPadding(0);
3590 //set current position
3591 $footer_y = $this->h
- $this->footer_margin
;
3593 $this->SetXY($this->original_rMargin
, $footer_y);
3595 $this->SetXY($this->original_lMargin
, $footer_y);
3597 $this->SetFont($this->footer_font
[0], $this->footer_font
[1], $this->footer_font
[2]);
3601 $this->SetXY($this->original_rMargin
, $this->tMargin
);
3603 $this->SetXY($this->original_lMargin
, $this->tMargin
);
3605 $this->_outRestoreGraphicsState();
3606 $this->lasth
= $lasth;
3607 $this->thead
= $temp_thead;
3608 $this->theadMargins
= $temp_theadMargins;
3610 // restore graphic settings
3611 $this->setGraphicVars($gvars);
3612 $this->current_column
= $gvars['current_column'];
3613 $this->num_columns
= $gvars['num_columns'];
3614 // calculate footer length
3615 $this->footerlen
[$this->page
] = $this->pagelen
[$this->page
] - $this->footerpos
[$this->page
] +
1;
3616 $this->InFooter
= false;
3620 * Check if we are on the page body (excluding page header and footer).
3621 * @return true if we are not in page header nor in page footer, false otherwise.
3623 * @since 5.9.091 (2011-06-15)
3625 protected function inPageBody() {
3626 return (($this->InHeader
=== false) AND ($this->InFooter
=== false));
3630 * This method is used to render the table header on new page (if any).
3632 * @since 4.5.030 (2009-03-25)
3634 protected function setTableHeader() {
3635 if ($this->num_columns
> 1) {
3636 // multi column mode
3639 if (isset($this->theadMargins
['top'])) {
3640 // restore the original top-margin
3641 $this->tMargin
= $this->theadMargins
['top'];
3642 $this->pagedim
[$this->page
]['tm'] = $this->tMargin
;
3643 $this->y
= $this->tMargin
;
3645 if (!TCPDF_STATIC
::empty_string($this->thead
) AND (!$this->inthead
)) {
3647 $prev_lMargin = $this->lMargin
;
3648 $prev_rMargin = $this->rMargin
;
3649 $prev_cell_padding = $this->cell_padding
;
3650 $this->lMargin
= $this->theadMargins
['lmargin'] +
($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$this->theadMargins
['page']]['olm']);
3651 $this->rMargin
= $this->theadMargins
['rmargin'] +
($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$this->theadMargins
['page']]['orm']);
3652 $this->cell_padding
= $this->theadMargins
['cell_padding'];
3654 $this->x
= $this->w
- $this->rMargin
;
3656 $this->x
= $this->lMargin
;
3658 // account for special "cell" mode
3659 if ($this->theadMargins
['cell']) {
3661 $this->x
-= $this->cell_padding
['R'];
3663 $this->x +
= $this->cell_padding
['L'];
3666 $gvars = $this->getGraphicVars();
3667 if (!empty($this->theadMargins
['gvars'])) {
3668 // set the correct graphic style
3669 $this->setGraphicVars($this->theadMargins
['gvars']);
3670 $this->rMargin
= $gvars['rMargin'];
3671 $this->lMargin
= $gvars['lMargin'];
3673 // print table header
3674 $this->writeHTML($this->thead
, false, false, false, false, '');
3675 $this->setGraphicVars($gvars);
3676 // set new top margin to skip the table headers
3677 if (!isset($this->theadMargins
['top'])) {
3678 $this->theadMargins
['top'] = $this->tMargin
;
3680 // store end of header position
3681 if (!isset($this->columns
[0]['th'])) {
3682 $this->columns
[0]['th'] = array();
3684 $this->columns
[0]['th']['\''.$this->page
.'\''] = $this->y
;
3685 $this->tMargin
= $this->y
;
3686 $this->pagedim
[$this->page
]['tm'] = $this->tMargin
;
3688 $this->lMargin
= $prev_lMargin;
3689 $this->rMargin
= $prev_rMargin;
3690 $this->cell_padding
= $prev_cell_padding;
3695 * Returns the current page number.
3696 * @return int page number
3699 * @see getAliasNbPages()
3701 public function PageNo() {
3706 * Returns the array of spot colors.
3707 * @return (array) Spot colors array.
3709 * @since 6.0.038 (2013-09-30)
3711 public function getAllSpotColors() {
3712 return $this->spot_colors
;
3716 * Defines a new spot color.
3717 * It can be expressed in RGB components or gray scale.
3718 * The method can be called before the first page is created and the value is retained from page to page.
3719 * @param $name (string) Full name of the spot color.
3720 * @param $c (float) Cyan color for CMYK. Value between 0 and 100.
3721 * @param $m (float) Magenta color for CMYK. Value between 0 and 100.
3722 * @param $y (float) Yellow color for CMYK. Value between 0 and 100.
3723 * @param $k (float) Key (Black) color for CMYK. Value between 0 and 100.
3725 * @since 4.0.024 (2008-09-12)
3726 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3728 public function AddSpotColor($name, $c, $m, $y, $k) {
3729 if (!isset($this->spot_colors
[$name])) {
3730 $i = (1 +
count($this->spot_colors
));
3731 $this->spot_colors
[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i);
3736 * Set the spot color for the specified type ('draw', 'fill', 'text').
3737 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3738 * @param $name (string) Name of the spot color.
3739 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3740 * @return (string) PDF color command.
3742 * @since 5.9.125 (2011-10-03)
3744 public function setSpotColor($type, $name, $tint=100) {
3745 $spotcolor = TCPDF_COLORS
::getSpotColor($name, $this->spot_colors
);
3746 if ($spotcolor === false) {
3747 $this->Error('Undefined spot color: '.$name.', you must add it using the AddSpotColor() method.');
3749 $tint = (max(0, min(100, $tint)) / 100);
3750 $pdfcolor = sprintf('/CS%d ', $this->spot_colors
[$name]['i']);
3753 $pdfcolor .= sprintf('CS %F SCN', $tint);
3754 $this->DrawColor
= $pdfcolor;
3755 $this->strokecolor
= $spotcolor;
3759 $pdfcolor .= sprintf('cs %F scn', $tint);
3760 $this->FillColor
= $pdfcolor;
3761 $this->bgcolor
= $spotcolor;
3765 $pdfcolor .= sprintf('cs %F scn', $tint);
3766 $this->TextColor
= $pdfcolor;
3767 $this->fgcolor
= $spotcolor;
3771 $this->ColorFlag
= ($this->FillColor
!= $this->TextColor
);
3772 if ($this->state
== 2) {
3773 $this->_out($pdfcolor);
3775 if ($this->inxobj
) {
3776 // we are inside an XObject template
3777 $this->xobjects
[$this->xobjid
]['spot_colors'][$name] = $this->spot_colors
[$name];
3783 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
3784 * @param $name (string) Name of the spot color.
3785 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3787 * @since 4.0.024 (2008-09-12)
3788 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3790 public function SetDrawSpotColor($name, $tint=100) {
3791 $this->setSpotColor('draw', $name, $tint);
3795 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
3796 * @param $name (string) Name of the spot color.
3797 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3799 * @since 4.0.024 (2008-09-12)
3800 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
3802 public function SetFillSpotColor($name, $tint=100) {
3803 $this->setSpotColor('fill', $name, $tint);
3807 * Defines the spot color used for text.
3808 * @param $name (string) Name of the spot color.
3809 * @param $tint (int) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3811 * @since 4.0.024 (2008-09-12)
3812 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
3814 public function SetTextSpotColor($name, $tint=100) {
3815 $this->setSpotColor('text', $name, $tint);
3819 * Set the color array for the specified type ('draw', 'fill', 'text').
3820 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3821 * The method can be called before the first page is created and the value is retained from page to page.
3822 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3823 * @param $color (array) Array of colors (1=gray, 3=RGB, 4=CMYK or 5=spotcolor=CMYK+name values).
3824 * @param $ret (boolean) If true do not send the PDF command.
3825 * @return (string) The PDF command or empty string.
3827 * @since 3.1.000 (2008-06-11)
3829 public function setColorArray($type, $color, $ret=false) {
3830 if (is_array($color)) {
3831 $color = array_values($color);
3832 // component: grey, RGB red or CMYK cyan
3833 $c = isset($color[0]) ? $color[0] : -1;
3834 // component: RGB green or CMYK magenta
3835 $m = isset($color[1]) ? $color[1] : -1;
3836 // component: RGB blue or CMYK yellow
3837 $y = isset($color[2]) ? $color[2] : -1;
3838 // component: CMYK black
3839 $k = isset($color[3]) ? $color[3] : -1;
3841 $name = isset($color[4]) ? $color[4] : '';
3843 return $this->setColor($type, $c, $m, $y, $k, $ret, $name);
3850 * Defines the color used for all drawing operations (lines, rectangles and cell borders).
3851 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3852 * The method can be called before the first page is created and the value is retained from page to page.
3853 * @param $color (array) Array of colors (1, 3 or 4 values).
3854 * @param $ret (boolean) If true do not send the PDF command.
3855 * @return string the PDF command
3857 * @since 3.1.000 (2008-06-11)
3858 * @see SetDrawColor()
3860 public function SetDrawColorArray($color, $ret=false) {
3861 return $this->setColorArray('draw', $color, $ret);
3865 * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
3866 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3867 * The method can be called before the first page is created and the value is retained from page to page.
3868 * @param $color (array) Array of colors (1, 3 or 4 values).
3869 * @param $ret (boolean) If true do not send the PDF command.
3871 * @since 3.1.000 (2008-6-11)
3872 * @see SetFillColor()
3874 public function SetFillColorArray($color, $ret=false) {
3875 return $this->setColorArray('fill', $color, $ret);
3879 * Defines the color used for text. It can be expressed in RGB components or gray scale.
3880 * The method can be called before the first page is created and the value is retained from page to page.
3881 * @param $color (array) Array of colors (1, 3 or 4 values).
3882 * @param $ret (boolean) If true do not send the PDF command.
3884 * @since 3.1.000 (2008-6-11)
3885 * @see SetFillColor()
3887 public function SetTextColorArray($color, $ret=false) {
3888 return $this->setColorArray('text', $color, $ret);
3892 * Defines the color used by the specified type ('draw', 'fill', 'text').
3893 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3894 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3895 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3896 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3897 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3898 * @param $ret (boolean) If true do not send the command.
3899 * @param $name (string) spot color name (if any)
3900 * @return (string) The PDF command or empty string.
3902 * @since 5.9.125 (2011-10-03)
3904 public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3905 // set default values
3906 if (!is_numeric($col1)) {
3909 if (!is_numeric($col2)) {
3912 if (!is_numeric($col3)) {
3915 if (!is_numeric($col4)) {
3918 // set color by case
3920 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
3922 $col1 = max(0, min(255, $col1));
3923 $intcolor = array('G' => $col1);
3924 $pdfcolor = sprintf('%F ', ($col1 / 255));
3926 } elseif ($col4 == -1) {
3928 $col1 = max(0, min(255, $col1));
3929 $col2 = max(0, min(255, $col2));
3930 $col3 = max(0, min(255, $col3));
3931 $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
3932 $pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255));
3935 $col1 = max(0, min(100, $col1));
3936 $col2 = max(0, min(100, $col2));
3937 $col3 = max(0, min(100, $col3));
3938 $col4 = max(0, min(100, $col4));
3941 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
3942 $pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
3946 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
3947 $this->AddSpotColor($name, $col1, $col2, $col3, $col4);
3948 $pdfcolor = $this->setSpotColor($type, $name, 100);
3953 $pdfcolor .= strtoupper($suffix);
3954 $this->DrawColor
= $pdfcolor;
3955 $this->strokecolor
= $intcolor;
3959 $pdfcolor .= $suffix;
3960 $this->FillColor
= $pdfcolor;
3961 $this->bgcolor
= $intcolor;
3965 $pdfcolor .= $suffix;
3966 $this->TextColor
= $pdfcolor;
3967 $this->fgcolor
= $intcolor;
3971 $this->ColorFlag
= ($this->FillColor
!= $this->TextColor
);
3972 if (($type != 'text') AND ($this->state
== 2)) {
3974 $this->_out($pdfcolor);
3982 * 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.
3983 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3984 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3985 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3986 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3987 * @param $ret (boolean) If true do not send the command.
3988 * @param $name (string) spot color name (if any)
3989 * @return string the PDF command
3992 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
3994 public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3995 return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name);
3999 * 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.
4000 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
4001 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
4002 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
4003 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
4004 * @param $ret (boolean) If true do not send the command.
4005 * @param $name (string) Spot color name (if any).
4006 * @return (string) The PDF command.
4009 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
4011 public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4012 return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name);
4016 * 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.
4017 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
4018 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
4019 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
4020 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
4021 * @param $ret (boolean) If true do not send the command.
4022 * @param $name (string) Spot color name (if any).
4023 * @return (string) Empty string.
4026 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
4028 public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4029 return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name);
4033 * Returns the length of a string in user unit. A font must be selected.<br>
4034 * @param $s (string) The string whose length is to be computed
4035 * @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.
4036 * @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.
4037 * @param $fontsize (float) Font size in points. The default value is the current size.
4038 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
4039 * @return mixed int total string length or array of characted widths
4040 * @author Nicola Asuni
4044 public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4045 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);
4049 * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br>
4050 * @param $sa (string) The array of chars whose total length is to be computed
4051 * @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.
4052 * @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.
4053 * @param $fontsize (float) Font size in points. The default value is the current size.
4054 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
4055 * @return mixed int total string length or array of characted widths
4056 * @author Nicola Asuni
4058 * @since 2.4.000 (2008-03-06)
4060 public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4061 // store current values
4062 if (!TCPDF_STATIC
::empty_string($fontname)) {
4063 $prev_FontFamily = $this->FontFamily
;
4064 $prev_FontStyle = $this->FontStyle
;
4065 $prev_FontSizePt = $this->FontSizePt
;
4066 $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false);
4068 // convert UTF-8 array to Latin1 if required
4069 if ($this->isunicode
AND (!$this->isUnicodeFont())) {
4070 $sa = TCPDF_FONTS
::UTF8ArrToLatin1Arr($sa);
4072 $w = 0; // total width
4073 $wa = array(); // array of characters widths
4074 foreach ($sa as $ck => $char) {
4076 $cw = $this->GetCharWidth($char, isset($sa[($ck +
1)]));
4080 // restore previous values
4081 if (!TCPDF_STATIC
::empty_string($fontname)) {
4082 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
4091 * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking).
4092 * @param $char (int) The char code whose length is to be returned
4093 * @param $notlast (boolean) If false ignore the font-spacing.
4094 * @return float char width
4095 * @author Nicola Asuni
4097 * @since 2.4.000 (2008-03-06)
4099 public function GetCharWidth($char, $notlast=true) {
4101 $chw = $this->getRawCharWidth($char);
4102 if (($this->font_spacing
< 0) OR (($this->font_spacing
> 0) AND $notlast)) {
4103 // increase/decrease font spacing
4104 $chw +
= $this->font_spacing
;
4106 if ($this->font_stretching
!= 100) {
4107 // fixed stretching mode
4108 $chw *= ($this->font_stretching
/ 100);
4114 * Returns the length of the char in user unit for the current font.
4115 * @param $char (int) The char code whose length is to be returned
4116 * @return float char width
4117 * @author Nicola Asuni
4119 * @since 5.9.000 (2010-09-28)
4121 public function getRawCharWidth($char) {
4123 // SHY character will not be printed
4126 if (isset($this->CurrentFont
['cw'][$char])) {
4127 $w = $this->CurrentFont
['cw'][$char];
4128 } elseif (isset($this->CurrentFont
['dw'])) {
4130 $w = $this->CurrentFont
['dw'];
4131 } elseif (isset($this->CurrentFont
['cw'][32])) {
4133 $w = $this->CurrentFont
['cw'][32];
4137 return $this->getAbsFontMeasure($w);
4141 * Returns the numbero of characters in a string.
4142 * @param $s (string) The input string.
4143 * @return int number of characters
4145 * @since 2.0.0001 (2008-01-07)
4147 public function GetNumChars($s) {
4148 if ($this->isUnicodeFont()) {
4149 return count(TCPDF_FONTS
::UTF8StringToArray($s, $this->isunicode
, $this->CurrentFont
));
4155 * Fill the list of available fonts ($this->fontlist).
4157 * @since 4.0.013 (2008-07-28)
4159 protected function getFontsList() {
4160 if (($fontsdir = opendir(TCPDF_FONTS
::_getfontpath())) !== false) {
4161 while (($file = readdir($fontsdir)) !== false) {
4162 if (substr($file, -4) == '.php') {
4163 array_push($this->fontlist
, strtolower(basename($file, '.php')));
4166 closedir($fontsdir);
4171 * Returns the unicode caracter specified by the value
4172 * @param $c (int) UTF-8 value
4173 * @return Returns the specified character.
4174 * @since 2.3.000 (2008-03-05)
4178 public function unichr($c) {
4179 return TCPDF_FONTS
::unichr($c, $this->isunicode
);
4183 * Convert and add the selected TrueType or Type1 font to the fonts folder (that must be writeable).
4184 * @param $fontfile (string) Font file (full path).
4185 * @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.
4186 * @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.
4187 * @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.
4188 * @param $outpath (string) Output path for generated font files (must be writeable by the web server). Leave empty for default font folder.
4189 * @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).
4190 * @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.
4191 * @param $addcbbox (boolean) If true includes the character bounding box information on the php font file.
4192 * @return (string) TCPDF font name.
4193 * @author Nicola Asuni
4194 * @since 5.9.123 (2010-09-30)
4198 public function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false) {
4199 return TCPDF_FONTS
::addTTFfont($fontfile, $fonttype, $enc, $flags, $outpath, $platid, $encid, $addcbbox);
4203 * Imports a TrueType, Type1, core, or CID0 font and makes it available.
4204 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
4205 * 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.
4206 * @param $family (string) Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
4207 * @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>
4208 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4209 * @return array containing the font data, or false in case of error.
4210 * @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.
4213 * @see SetFont(), setFontSubsetting()
4215 public function AddFont($family, $style='', $fontfile='', $subset='default') {
4216 if ($subset === 'default') {
4217 $subset = $this->font_subsetting
;
4219 if ($this->pdfa_mode
) {
4222 if (TCPDF_STATIC
::empty_string($family)) {
4223 if (!TCPDF_STATIC
::empty_string($this->FontFamily
)) {
4224 $family = $this->FontFamily
;
4226 $this->Error('Empty font family');
4229 // move embedded styles on $style
4230 if (substr($family, -1) == 'I') {
4232 $family = substr($family, 0, -1);
4234 if (substr($family, -1) == 'B') {
4236 $family = substr($family, 0, -1);
4238 // normalize family name
4239 $family = strtolower($family);
4240 if ((!$this->isunicode
) AND ($family == 'arial')) {
4241 $family = 'helvetica';
4243 if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
4246 if ($this->pdfa_mode
AND (isset($this->CoreFonts
[$family]))) {
4247 // all fonts must be embedded
4248 $family = 'pdfa'.$family;
4250 $tempstyle = strtoupper($style);
4253 if (strpos($tempstyle, 'U') !== false) {
4254 $this->underline
= true;
4256 $this->underline
= false;
4258 // line-through (deleted)
4259 if (strpos($tempstyle, 'D') !== false) {
4260 $this->linethrough
= true;
4262 $this->linethrough
= false;
4265 if (strpos($tempstyle, 'O') !== false) {
4266 $this->overline
= true;
4268 $this->overline
= false;
4271 if (strpos($tempstyle, 'B') !== false) {
4275 if (strpos($tempstyle, 'I') !== false) {
4279 $fontkey = $family.$style;
4280 $font_style = $style.($this->underline
? 'U' : '').($this->linethrough
? 'D' : '').($this->overline
? 'O' : '');
4281 $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
4282 // check if the font has been already added
4283 $fb = $this->getFontBuffer($fontkey);
4284 if ($fb !== false) {
4285 if ($this->inxobj
) {
4286 // we are inside an XObject template
4287 $this->xobjects
[$this->xobjid
]['fonts'][$fontkey] = $fb['i'];
4291 // get specified font directory (if any)
4293 if (!TCPDF_STATIC
::empty_string($fontfile)) {
4294 $fontdir = dirname($fontfile);
4295 if (TCPDF_STATIC
::empty_string($fontdir) OR ($fontdir == '.')) {
4301 // true when the font style variation is missing
4302 $missing_style = false;
4303 // search and include font file
4304 if (TCPDF_STATIC
::empty_string($fontfile) OR (!@file_exists($fontfile))) {
4305 // build a standard filenames for specified font
4306 $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
4307 $fontfile = TCPDF_FONTS
::getFontFullPath($tmp_fontfile, $fontdir);
4308 if (TCPDF_STATIC
::empty_string($fontfile)) {
4309 $missing_style = true;
4310 // try to remove the style part
4311 $tmp_fontfile = str_replace(' ', '', $family).'.php';
4312 $fontfile = TCPDF_FONTS
::getFontFullPath($tmp_fontfile, $fontdir);
4315 // include font file
4316 if (!TCPDF_STATIC
::empty_string($fontfile) AND (@file_exists($fontfile))) {
4319 $this->Error('Could not include font definition file: '.$family.'');
4321 // check font parameters
4322 if ((!isset($type)) OR (!isset($cw))) {
4323 $this->Error('The font definition file has a bad format: '.$fontfile.'');
4325 // SET default parameters
4326 if (!isset($file) OR TCPDF_STATIC
::empty_string($file)) {
4329 if (!isset($enc) OR TCPDF_STATIC
::empty_string($enc)) {
4332 if (!isset($cidinfo) OR TCPDF_STATIC
::empty_string($cidinfo)) {
4333 $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0);
4334 $cidinfo['uni2cid'] = array();
4336 if (!isset($ctg) OR TCPDF_STATIC
::empty_string($ctg)) {
4339 if (!isset($desc) OR TCPDF_STATIC
::empty_string($desc)) {
4342 if (!isset($up) OR TCPDF_STATIC
::empty_string($up)) {
4345 if (!isset($ut) OR TCPDF_STATIC
::empty_string($ut)) {
4348 if (!isset($cw) OR TCPDF_STATIC
::empty_string($cw)) {
4351 if (!isset($dw) OR TCPDF_STATIC
::empty_string($dw)) {
4352 // set default width
4353 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
4354 $dw = $desc['MissingWidth'];
4355 } elseif (isset($cw[32])) {
4362 if ($type == 'core') {
4363 $name = $this->CoreFonts
[$fontkey];
4365 } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
4367 } elseif ($type == 'TrueTypeUnicode') {
4368 $enc = 'Identity-H';
4369 } elseif ($type == 'cidfont0') {
4370 if ($this->pdfa_mode
) {
4371 $this->Error('All fonts must be embedded in PDF/A mode!');
4374 $this->Error('Unknow font type: '.$type.'');
4376 // set name if unset
4377 if (!isset($name) OR empty($name)) {
4380 // create artificial font style variations if missing (only works with non-embedded fonts)
4381 if (($type != 'core') AND $missing_style) {
4383 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
4384 $name .= $styles[$bistyle];
4386 if (strpos($bistyle, 'B') !== false) {
4387 if (isset($desc['StemV'])) {
4388 // from normal to bold
4389 $desc['StemV'] = round($desc['StemV'] * 1.75);
4392 $desc['StemV'] = 123;
4395 // artificial italic
4396 if (strpos($bistyle, 'I') !== false) {
4397 if (isset($desc['ItalicAngle'])) {
4398 $desc['ItalicAngle'] -= 11;
4400 $desc['ItalicAngle'] = -11;
4402 if (isset($desc['Flags'])) {
4403 $desc['Flags'] |= 64; //bit 7
4405 $desc['Flags'] = 64;
4409 // check if the array of characters bounding boxes is defined
4410 if (!isset($cbbox)) {
4413 // initialize subsetchars
4414 $subsetchars = array_fill(0, 255, true);
4415 $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));
4416 if ($this->inxobj
) {
4417 // we are inside an XObject template
4418 $this->xobjects
[$this->xobjid
]['fonts'][$fontkey] = $this->numfonts
;
4420 if (isset($diff) AND (!empty($diff))) {
4421 //Search existing encodings
4423 $nb = count($this->diffs
);
4424 for ($i=1; $i <= $nb; ++
$i) {
4425 if ($this->diffs
[$i] == $diff) {
4432 $this->diffs
[$d] = $diff;
4434 $this->setFontSubBuffer($fontkey, 'diff', $d);
4436 if (!TCPDF_STATIC
::empty_string($file)) {
4437 if (!isset($this->FontFiles
[$file])) {
4438 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
4439 $this->FontFiles
[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4440 } elseif ($type != 'core') {
4441 $this->FontFiles
[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4444 // update fontkeys that are sharing this font file
4445 $this->FontFiles
[$file]['subset'] = ($this->FontFiles
[$file]['subset'] AND $subset);
4446 if (!in_array($fontkey, $this->FontFiles
[$file]['fontkeys'])) {
4447 $this->FontFiles
[$file]['fontkeys'][] = $fontkey;
4455 * Sets the font used to print character strings.
4456 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
4457 * The method can be called before the first page is created and the font is retained from page to page.
4458 * If you just wish to change the current font size, it is simpler to call SetFontSize().
4459 * 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 />
4460 * @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.
4461 * @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.
4462 * @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
4463 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4464 * @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.
4465 * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
4466 * @author Nicola Asuni
4469 * @see AddFont(), SetFontSize()
4471 public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) {
4472 //Select a font; size given in points
4473 if ($size === null) {
4474 $size = $this->FontSizePt
;
4479 // try to add font (if not already added)
4480 $fontdata = $this->AddFont($family, $style, $fontfile, $subset);
4481 $this->FontFamily
= $fontdata['family'];
4482 $this->FontStyle
= $fontdata['style'];
4483 if (isset($this->CurrentFont
['fontkey']) AND isset($this->CurrentFont
['subsetchars'])) {
4484 // save subset chars of the previous font
4485 $this->setFontSubBuffer($this->CurrentFont
['fontkey'], 'subsetchars', $this->CurrentFont
['subsetchars']);
4487 $this->CurrentFont
= $this->getFontBuffer($fontdata['fontkey']);
4488 $this->SetFontSize($size, $out);
4492 * Defines the size of the current font.
4493 * @param $size (float) The font size in points.
4494 * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
4499 public function SetFontSize($size, $out=true) {
4500 // font size in points
4501 $this->FontSizePt
= $size;
4502 // font size in user units
4503 $this->FontSize
= $size / $this->k
;
4504 // calculate some font metrics
4505 if (isset($this->CurrentFont
['desc']['FontBBox'])) {
4506 $bbox = explode(' ', substr($this->CurrentFont
['desc']['FontBBox'], 1, -1));
4507 $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
4509 $font_height = $size * 1.219;
4511 if (isset($this->CurrentFont
['desc']['Ascent']) AND ($this->CurrentFont
['desc']['Ascent'] > 0)) {
4512 $font_ascent = ($this->CurrentFont
['desc']['Ascent'] * $size / 1000);
4514 if (isset($this->CurrentFont
['desc']['Descent']) AND ($this->CurrentFont
['desc']['Descent'] <= 0)) {
4515 $font_descent = (- $this->CurrentFont
['desc']['Descent'] * $size / 1000);
4517 if (!isset($font_ascent) AND !isset($font_descent)) {
4519 $font_ascent = 0.76 * $font_height;
4520 $font_descent = $font_height - $font_ascent;
4521 } elseif (!isset($font_descent)) {
4522 $font_descent = $font_height - $font_ascent;
4523 } elseif (!isset($font_ascent)) {
4524 $font_ascent = $font_height - $font_descent;
4526 $this->FontAscent
= ($font_ascent / $this->k
);
4527 $this->FontDescent
= ($font_descent / $this->k
);
4528 if ($out AND ($this->page
> 0) AND (isset($this->CurrentFont
['i'])) AND ($this->state
== 2)) {
4529 $this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont
['i'], $this->FontSizePt
));
4534 * Returns the bounding box of the current font in user units.
4537 * @since 5.9.152 (2012-03-23)
4539 public function getFontBBox() {
4541 if (isset($this->CurrentFont
['desc']['FontBBox'])) {
4542 $tmpbbox = explode(' ', substr($this->CurrentFont
['desc']['FontBBox'], 1, -1));
4543 $fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox);
4546 if (isset($this->CurrentFont
['desc']['MaxWidth'])) {
4547 $maxw = $this->getAbsFontMeasure(intval($this->CurrentFont
['desc']['MaxWidth']));
4550 if (isset($this->CurrentFont
['desc']['MissingWidth'])) {
4551 $maxw = max($maxw, $this->CurrentFont
['desc']['MissingWidth']);
4553 if (isset($this->CurrentFont
['desc']['AvgWidth'])) {
4554 $maxw = max($maxw, $this->CurrentFont
['desc']['AvgWidth']);
4556 if (isset($this->CurrentFont
['dw'])) {
4557 $maxw = max($maxw, $this->CurrentFont
['dw']);
4559 foreach ($this->CurrentFont
['cw'] as $char => $w) {
4560 $maxw = max($maxw, $w);
4565 $maxw = $this->getAbsFontMeasure($maxw);
4567 $fbbox = array(0, (0 - $this->FontDescent
), $maxw, $this->FontAscent
);
4573 * Convert a relative font measure into absolute value.
4574 * @param $s (int) Font measure.
4575 * @return float Absolute measure.
4576 * @since 5.9.186 (2012-09-13)
4578 public function getAbsFontMeasure($s) {
4579 return ($s * $this->FontSize
/ 1000);
4583 * Returns the glyph bounding box of the specified character in the current font in user units.
4584 * @param $char (int) Input character code.
4585 * @return mixed array(xMin, yMin, xMax, yMax) or FALSE if not defined.
4586 * @since 5.9.186 (2012-09-13)
4588 public function getCharBBox($char) {
4589 if (isset($this->CurrentFont
['cbbox'][$char])) {
4590 return array_map(array($this,'getAbsFontMeasure'), $this->CurrentFont
['cbbox'][intval($char)]);
4596 * Return the font descent value
4597 * @param $font (string) font name
4598 * @param $style (string) font style
4599 * @param $size (float) The size (in points)
4600 * @return int font descent
4602 * @author Nicola Asuni
4603 * @since 4.9.003 (2010-03-30)
4605 public function getFontDescent($font, $style='', $size=0) {
4606 $fontdata = $this->AddFont($font, $style);
4607 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4608 if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
4609 $descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
4611 $descent = (1.219 * 0.24 * $size);
4613 return ($descent / $this->k
);
4617 * Return the font ascent value.
4618 * @param $font (string) font name
4619 * @param $style (string) font style
4620 * @param $size (float) The size (in points)
4621 * @return int font ascent
4623 * @author Nicola Asuni
4624 * @since 4.9.003 (2010-03-30)
4626 public function getFontAscent($font, $style='', $size=0) {
4627 $fontdata = $this->AddFont($font, $style);
4628 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4629 if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
4630 $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
4632 $ascent = 1.219 * 0.76 * $size;
4634 return ($ascent / $this->k
);
4638 * Return true in the character is present in the specified font.
4639 * @param $char (mixed) Character to check (integer value or string)
4640 * @param $font (string) Font name (family name).
4641 * @param $style (string) Font style.
4642 * @return (boolean) true if the char is defined, false otherwise.
4644 * @since 5.9.153 (2012-03-28)
4646 public function isCharDefined($char, $font='', $style='') {
4647 if (is_string($char)) {
4648 // get character code
4649 $char = TCPDF_FONTS
::UTF8StringToArray($char, $this->isunicode
, $this->CurrentFont
);
4652 if (TCPDF_STATIC
::empty_string($font)) {
4653 if (TCPDF_STATIC
::empty_string($style)) {
4654 return (isset($this->CurrentFont
['cw'][intval($char)]));
4656 $font = $this->FontFamily
;
4658 $fontdata = $this->AddFont($font, $style);
4659 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4660 return (isset($fontinfo['cw'][intval($char)]));
4664 * Replace missing font characters on selected font with specified substitutions.
4665 * @param $text (string) Text to process.
4666 * @param $font (string) Font name (family name).
4667 * @param $style (string) Font style.
4668 * @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.
4669 * @return (string) Processed text.
4671 * @since 5.9.153 (2012-03-28)
4673 public function replaceMissingChars($text, $font='', $style='', $subs=array()) {
4677 if (TCPDF_STATIC
::empty_string($font)) {
4678 $font = $this->FontFamily
;
4680 $fontdata = $this->AddFont($font, $style);
4681 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4682 $uniarr = TCPDF_FONTS
::UTF8StringToArray($text, $this->isunicode
, $this->CurrentFont
);
4683 foreach ($uniarr as $k => $chr) {
4684 if (!isset($fontinfo['cw'][$chr])) {
4685 // this character is missing on the selected font
4686 if (isset($subs[$chr])) {
4687 // we have available substitutions
4688 if (is_array($subs[$chr])) {
4689 foreach($subs[$chr] as $s) {
4690 if (isset($fontinfo['cw'][$s])) {
4695 } elseif (isset($fontinfo['cw'][$subs[$chr]])) {
4696 $uniarr[$k] = $subs[$chr];
4701 return TCPDF_FONTS
::UniArrSubString(TCPDF_FONTS
::UTF8ArrayToUniArray($uniarr, $this->isunicode
));
4705 * Defines the default monospaced font.
4706 * @param $font (string) Font name.
4710 public function SetDefaultMonospacedFont($font) {
4711 $this->default_monospaced_font
= $font;
4715 * 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 />
4716 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
4719 * @see Cell(), Write(), Image(), Link(), SetLink()
4721 public function AddLink() {
4722 // create a new internal link
4723 $n = count($this->links
) +
1;
4724 $this->links
[$n] = array('p' => 0, 'y' => 0, 'f' => false);
4729 * Defines the page and position a link points to.
4730 * @param $link (int) The link identifier returned by AddLink()
4731 * @param $y (float) Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
4732 * @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.
4737 public function SetLink($link, $y=0, $page=-1) {
4739 if (!empty($page) AND ($page[0] == '*')) {
4740 $page = intval(substr($page, 1));
4741 // this page number will not be changed when moving/add/deleting pages
4745 $page = $this->page
;
4750 $this->links
[$link] = array('p' => $page, 'y' => $y, 'f' => $fixed);
4754 * Puts a link on a rectangular area of the page.
4755 * 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.
4756 * @param $x (float) Abscissa of the upper-left corner of the rectangle
4757 * @param $y (float) Ordinate of the upper-left corner of the rectangle
4758 * @param $w (float) Width of the rectangle
4759 * @param $h (float) Height of the rectangle
4760 * @param $link (mixed) URL or identifier returned by AddLink()
4761 * @param $spaces (int) number of spaces on the text to link
4764 * @see AddLink(), Annotation(), Cell(), Write(), Image()
4766 public function Link($x, $y, $w, $h, $link, $spaces=0) {
4767 $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
4771 * Puts a markup annotation on a rectangular area of the page.
4772 * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!
4773 * @param $x (float) Abscissa of the upper-left corner of the rectangle
4774 * @param $y (float) Ordinate of the upper-left corner of the rectangle
4775 * @param $w (float) Width of the rectangle
4776 * @param $h (float) Height of the rectangle
4777 * @param $text (string) annotation text or alternate content
4778 * @param $opt (array) array of options (see section 8.4 of PDF reference 1.7).
4779 * @param $spaces (int) number of spaces on the text to link
4781 * @since 4.0.018 (2008-08-06)
4783 public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
4784 if ($this->inxobj
) {
4785 // store parameters for later use on template
4786 $this->xobjects
[$this->xobjid
]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
4795 // check page for no-write regions and adapt page margins if necessary
4796 list($x, $y) = $this->checkPageRegions($h, $x, $y);
4797 // recalculate coordinates to account for graphic transformations
4798 if (isset($this->transfmatrix
) AND !empty($this->transfmatrix
)) {
4799 for ($i=$this->transfmatrix_key
; $i > 0; --$i) {
4800 $maxid = count($this->transfmatrix
[$i]) - 1;
4801 for ($j=$maxid; $j >= 0; --$j) {
4802 $ctm = $this->transfmatrix
[$i][$j];
4803 if (isset($ctm['a'])) {
4805 $y = ($this->h
- $y) * $this->k
;
4811 $x1 = ($ctm['a'] * $xt) +
($ctm['c'] * $yt) +
$ctm['e'];
4812 $y1 = ($ctm['b'] * $xt) +
($ctm['d'] * $yt) +
$ctm['f'];
4816 $x2 = ($ctm['a'] * $xt) +
($ctm['c'] * $yt) +
$ctm['e'];
4817 $y2 = ($ctm['b'] * $xt) +
($ctm['d'] * $yt) +
$ctm['f'];
4821 $x3 = ($ctm['a'] * $xt) +
($ctm['c'] * $yt) +
$ctm['e'];
4822 $y3 = ($ctm['b'] * $xt) +
($ctm['d'] * $yt) +
$ctm['f'];
4826 $x4 = ($ctm['a'] * $xt) +
($ctm['c'] * $yt) +
$ctm['e'];
4827 $y4 = ($ctm['b'] * $xt) +
($ctm['d'] * $yt) +
$ctm['f'];
4828 // new coordinates (rectangle area)
4829 $x = min($x1, $x2, $x3, $x4);
4830 $y = max($y1, $y2, $y3, $y4);
4831 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k
;
4832 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k
;
4834 $y = $this->h
- ($y / $this->k
);
4839 if ($this->page
<= 0) {
4842 $page = $this->page
;
4844 if (!isset($this->PageAnnots
[$page])) {
4845 $this->PageAnnots
[$page] = array();
4847 $this->PageAnnots
[$page][] = array('n' => ++
$this->n
, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
4848 if (!$this->pdfa_mode
) {
4849 if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!TCPDF_STATIC
::empty_string($opt['FS']))
4850 AND (@file_exists($opt['FS']) OR TCPDF_STATIC
::isValidURL($opt['FS']))
4851 AND (!isset($this->embeddedfiles
[basename($opt['FS'])]))) {
4852 $this->embeddedfiles
[basename($opt['FS'])] = array('f' => ++
$this->n
, 'n' => ++
$this->n
, 'file' => $opt['FS']);
4855 // Add widgets annotation's icons
4856 if (isset($opt['mk']['i']) AND @file_exists($opt['mk']['i'])) {
4857 $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
4859 if (isset($opt['mk']['ri']) AND @file_exists($opt['mk']['ri'])) {
4860 $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4862 if (isset($opt['mk']['ix']) AND @file_exists($opt['mk']['ix'])) {
4863 $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4868 * Embedd the attached files.
4869 * @since 4.4.000 (2008-12-07)
4873 protected function _putEmbeddedFiles() {
4874 if ($this->pdfa_mode
) {
4875 // embedded files are not allowed in PDF/A mode
4878 reset($this->embeddedfiles
);
4879 foreach ($this->embeddedfiles
as $filename => $filedata) {
4880 $data = TCPDF_STATIC
::fileGetContents($filedata['file']);
4881 if ($data !== FALSE) {
4882 $rawsize = strlen($data);
4885 $this->efnames
[$filename] = $filedata['f'].' 0 R';
4886 // embedded file specification object
4887 $out = $this->_getobj($filedata['f'])."\n";
4888 $out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']).' /EF <</F '.$filedata['n'].' 0 R>> >>';
4889 $out .= "\n".'endobj';
4891 // embedded file object
4893 if ($this->compress
) {
4894 $data = gzcompress($data);
4895 $filter = ' /Filter /FlateDecode';
4897 $stream = $this->_getrawstream($data, $filedata['n']);
4898 $out = $this->_getobj($filedata['n'])."\n";
4899 $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>';
4900 $out .= ' stream'."\n".$stream."\n".'endstream';
4901 $out .= "\n".'endobj';
4909 * Prints a text cell at the specified position.
4910 * This method allows to place a string precisely on the page.
4911 * @param $x (float) Abscissa of the cell origin
4912 * @param $y (float) Ordinate of the cell origin
4913 * @param $txt (string) String to print
4914 * @param $fstroke (int) outline size in user units (false = disable)
4915 * @param $fclip (boolean) if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
4916 * @param $ffill (boolean) if true fills the text
4917 * @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)))
4918 * @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.
4919 * @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>
4920 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
4921 * @param $link (mixed) URL or identifier returned by AddLink().
4922 * @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.
4923 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
4924 * @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>
4925 * @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>
4926 * @param $rtloff (boolean) if true uses the page top-left corner as origin of axis for $x and $y initial position.
4929 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()
4931 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) {
4932 $textrendermode = $this->textrendermode
;
4933 $textstrokewidth = $this->textstrokewidth
;
4934 $this->setTextRenderingMode($fstroke, $ffill, $fclip);
4935 $this->SetXY($x, $y, $rtloff);
4936 $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
4937 // restore previous rendering mode
4938 $this->textrendermode
= $textrendermode;
4939 $this->textstrokewidth
= $textstrokewidth;
4943 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
4944 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
4945 * This method is called automatically and should not be called directly by the application.
4949 * @see SetAutoPageBreak()
4951 public function AcceptPageBreak() {
4952 if ($this->num_columns
> 1) {
4953 // multi column mode
4954 if ($this->current_column
< ($this->num_columns
- 1)) {
4955 // go to next column
4956 $this->selectColumn($this->current_column +
1);
4957 } elseif ($this->AutoPageBreak
) {
4961 $this->selectColumn(0);
4963 // avoid page breaking from checkPageBreak()
4966 return $this->AutoPageBreak
;
4970 * Add page if needed.
4971 * @param $h (float) Cell height. Default value: 0.
4972 * @param $y (mixed) starting y position, leave empty for current position.
4973 * @param $addpage (boolean) if true add a page, otherwise only return the true/false state
4974 * @return boolean true in case of page break, false otherwise.
4975 * @since 3.2.000 (2008-07-01)
4978 protected function checkPageBreak($h=0, $y='', $addpage=true) {
4979 if (TCPDF_STATIC
::empty_string($y)) {
4982 $current_page = $this->page
;
4983 if ((($y +
$h) > $this->PageBreakTrigger
) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) {
4985 //Automatic page break
4987 $this->AddPage($this->CurOrientation
);
4988 $this->y
= $this->tMargin
;
4989 $oldpage = $this->page
- 1;
4991 if ($this->pagedim
[$this->page
]['orm'] != $this->pagedim
[$oldpage]['orm']) {
4992 $this->x
= $x - ($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$oldpage]['orm']);
4997 if ($this->pagedim
[$this->page
]['olm'] != $this->pagedim
[$oldpage]['olm']) {
4998 $this->x
= $x +
($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$oldpage]['olm']);
5006 if ($current_page != $this->page
) {
5007 // account for columns mode
5014 * 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 />
5015 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
5016 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
5017 * @param $h (float) Cell height. Default value: 0.
5018 * @param $txt (string) String to print. Default value: empty string.
5019 * @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)))
5020 * @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.
5021 * @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>
5022 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5023 * @param $link (mixed) URL or identifier returned by AddLink().
5024 * @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.
5025 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5026 * @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>
5027 * @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>
5030 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
5032 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') {
5033 $prev_cell_margin = $this->cell_margin
;
5034 $prev_cell_padding = $this->cell_padding
;
5035 $this->adjustCellPadding($border);
5036 if (!$ignore_min_height) {
5037 $min_cell_height = $this->getCellHeight($this->FontSize
);
5038 if ($h < $min_cell_height) {
5039 $h = $min_cell_height;
5042 $this->checkPageBreak($h +
$this->cell_margin
['T'] +
$this->cell_margin
['B']);
5043 // apply text shadow if enabled
5044 if ($this->txtshadow
['enabled']) {
5048 $bc = $this->bgcolor
;
5049 $fc = $this->fgcolor
;
5050 $sc = $this->strokecolor
;
5051 $alpha = $this->alpha
;
5053 $this->x +
= $this->txtshadow
['depth_w'];
5054 $this->y +
= $this->txtshadow
['depth_h'];
5055 $this->SetFillColorArray($this->txtshadow
['color']);
5056 $this->SetTextColorArray($this->txtshadow
['color']);
5057 $this->SetDrawColorArray($this->txtshadow
['color']);
5058 if ($this->txtshadow
['opacity'] != $alpha['CA']) {
5059 $this->setAlpha($this->txtshadow
['opacity'], $this->txtshadow
['blend_mode']);
5061 if ($this->state
== 2) {
5062 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5067 $this->SetFillColorArray($bc);
5068 $this->SetTextColorArray($fc);
5069 $this->SetDrawColorArray($sc);
5070 if ($this->txtshadow
['opacity'] != $alpha['CA']) {
5071 $this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']);
5074 if ($this->state
== 2) {
5075 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5077 $this->cell_padding
= $prev_cell_padding;
5078 $this->cell_margin
= $prev_cell_margin;
5082 * 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 />
5083 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
5084 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
5085 * @param $h (float) Cell height. Default value: 0.
5086 * @param $txt (string) String to print. Default value: empty string.
5087 * @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)))
5088 * @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.
5089 * @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>
5090 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5091 * @param $link (mixed) URL or identifier returned by AddLink().
5092 * @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.
5093 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5094 * @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>
5095 * @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>
5096 * @return string containing cell code
5101 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') {
5102 // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space
5103 $txt = str_replace(TCPDF_FONTS
::unichr(160, $this->isunicode
), ' ', $txt);
5104 $prev_cell_margin = $this->cell_margin
;
5105 $prev_cell_padding = $this->cell_padding
;
5106 $txt = TCPDF_STATIC
::removeSHY($txt, $this->isunicode
);
5107 $rs = ''; //string to be returned
5108 $this->adjustCellPadding($border);
5109 if (!$ignore_min_height) {
5110 $min_cell_height = $this->getCellHeight($this->FontSize
);
5111 if ($h < $min_cell_height) {
5112 $h = $min_cell_height;
5116 // check page for no-write regions and adapt page margins if necessary
5117 list($this->x
, $this->y
) = $this->checkPageRegions($h, $this->x
, $this->y
);
5119 $x = $this->x
- $this->cell_margin
['R'];
5121 $x = $this->x +
$this->cell_margin
['L'];
5123 $y = $this->y +
$this->cell_margin
['T'];
5124 $prev_font_stretching = $this->font_stretching
;
5125 $prev_font_spacing = $this->font_spacing
;
5126 // cell vertical alignment
5133 $y -= $this->cell_padding
['T'];
5138 $y -= ($h - $this->cell_padding
['B'] - $this->FontAscent
- $this->FontDescent
);
5145 $y -= (($h - $this->FontAscent
- $this->FontDescent
) / 2);
5156 $y -= ($this->cell_padding
['T'] +
$this->FontAscent
);
5161 $y -= ($h - $this->cell_padding
['B'] - $this->FontDescent
);
5168 $y -= (($h +
$this->FontAscent
- $this->FontDescent
) / 2);
5179 $y -= ($this->cell_padding
['T'] +
$this->FontAscent +
$this->FontDescent
);
5184 $y -= ($h - $this->cell_padding
['B']);
5191 $y -= (($h +
$this->FontAscent +
$this->FontDescent
) / 2);
5214 // text vertical alignment
5218 $yt = $y +
$this->cell_padding
['T'];
5223 $yt = $y +
$h - $this->cell_padding
['B'] - $this->FontAscent
- $this->FontDescent
;
5230 $yt = $y +
(($h - $this->FontAscent
- $this->FontDescent
) / 2);
5234 $basefonty = $yt +
$this->FontAscent
;
5235 if (TCPDF_STATIC
::empty_string($w) OR ($w <= 0)) {
5237 $w = $x - $this->lMargin
;
5239 $w = $this->w
- $this->rMargin
- $x;
5244 if (is_string($border) AND (strlen($border) == 4)) {
5248 if ($fill OR ($border == 1)) {
5250 $op = ($border == 1) ? 'B' : 'f';
5255 $xk = (($x - $w) * $k);
5259 $s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h
- $y) * $k), ($w * $k), (-$h * $k), $op);
5262 $s .= $this->getCellBorder($x, $y, $w, $h, $border);
5265 if ($this->isunicode
) {
5266 if (($this->CurrentFont
['type'] == 'core') OR ($this->CurrentFont
['type'] == 'TrueType') OR ($this->CurrentFont
['type'] == 'Type1')) {
5267 $txt2 = TCPDF_FONTS
::UTF8ToLatin1($txt2, $this->isunicode
, $this->CurrentFont
);
5269 $unicode = TCPDF_FONTS
::UTF8StringToArray($txt, $this->isunicode
, $this->CurrentFont
); // array of UTF-8 unicode values
5270 $unicode = TCPDF_FONTS
::utf8Bidi($unicode, '', $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
5271 // replace thai chars (if any)
5272 if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS
== true)) {
5274 $numchars = count($unicode);
5275 // po pla, for far, for fan
5276 $longtail = array(0x0e1b, 0x0e1d, 0x0e1f);
5277 // do chada, to patak
5278 $lowtail = array(0x0e0e, 0x0e0f);
5279 // mai hun arkad, sara i, sara ii, sara ue, sara uee
5280 $upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37);
5281 // mai ek, mai tho, mai tri, mai chattawa, karan
5282 $tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c);
5283 // sara u, sara uu, pinthu
5284 $lowvowel = array(0x0e38, 0x0e39, 0x0e3a);
5286 for ($i = 0; $i < $numchars; $i++
) {
5287 if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) {
5288 $ch0 = $unicode[$i];
5289 $ch1 = ($i > 0) ? $unicode[($i - 1)] : 0;
5290 $ch2 = ($i > 1) ? $unicode[($i - 2)] : 0;
5291 $chn = ($i < ($numchars - 1)) ? $unicode[($i +
1)] : 0;
5292 if (in_array($ch0, $tonemark)) {
5293 if ($chn == 0x0e33) {
5295 if (in_array($ch1, $longtail)) {
5296 // tonemark at upper left
5297 $output[] = $this->replaceChar($ch0, (0xf713 +
$ch0 - 0x0e48));
5299 // tonemark at upper right (normal position)
5302 } elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) {
5303 // tonemark at lower left
5304 $output[] = $this->replaceChar($ch0, (0xf705 +
$ch0 - 0x0e48));
5305 } elseif (in_array($ch1, $upvowel)) {
5306 if (in_array($ch2, $longtail)) {
5307 // tonemark at upper left
5308 $output[] = $this->replaceChar($ch0, (0xf713 +
$ch0 - 0x0e48));
5310 // tonemark at upper right (normal position)
5314 // tonemark at lower right
5315 $output[] = $this->replaceChar($ch0, (0xf70a +
$ch0 - 0x0e48));
5317 } elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) {
5318 // add lower left nikhahit and sara aa
5319 if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) {
5321 $this->CurrentFont
['subsetchars'][0xf711] = true;
5323 $this->CurrentFont
['subsetchars'][0x0e32] = true;
5327 } elseif (in_array($ch1, $longtail)) {
5328 if ($ch0 == 0x0e31) {
5329 // lower left mai hun arkad
5330 $output[] = $this->replaceChar($ch0, 0xf710);
5331 } elseif (in_array($ch0, $upvowel)) {
5333 $output[] = $this->replaceChar($ch0, (0xf701 +
$ch0 - 0x0e34));
5334 } elseif ($ch0 == 0x0e47) {
5335 // lower left mai tai koo
5336 $output[] = $this->replaceChar($ch0, 0xf712);
5341 } elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) {
5343 $output[] = $this->replaceChar($ch0, (0xf718 +
$ch0 - 0x0e38));
5344 } elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) {
5345 // yo ying without lower part
5346 $output[] = $this->replaceChar($ch0, 0xf70f);
5347 } elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) {
5348 // tho santan without lower part
5349 $output[] = $this->replaceChar($ch0, 0xf700);
5354 // non-thai character
5355 $output[] = $unicode[$i];
5359 // update font subsetchars
5360 $this->setFontSubBuffer($this->CurrentFont
['fontkey'], 'subsetchars', $this->CurrentFont
['subsetchars']);
5361 } // end of K_THAI_TOPCHARS
5362 $txt2 = TCPDF_FONTS
::arrUTF8ToUTF16BE($unicode, false);
5365 $txt2 = TCPDF_STATIC
::_escape($txt2);
5366 // get current text width (considering general font stretching and spacing)
5367 $txwidth = $this->GetStringWidth($txt);
5369 // check for stretch mode
5371 // calculate ratio between cell width and text width
5375 $ratio = (($w - $this->cell_padding
['L'] - $this->cell_padding
['R']) / $width);
5377 // check if stretching is required
5378 if (($ratio < 1) OR (($ratio > 1) AND (($stretch %
2) == 0))) {
5379 // the text will be stretched to fit cell width
5381 // set new character spacing
5382 $this->font_spacing +
= ($w - $this->cell_padding
['L'] - $this->cell_padding
['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching
/ 100));
5384 // set new horizontal stretching
5385 $this->font_stretching
*= $ratio;
5387 // recalculate text width (the text fills the entire cell)
5388 $width = $w - $this->cell_padding
['L'] - $this->cell_padding
['R'];
5393 if ($this->font_stretching
!= 100) {
5394 // apply font stretching
5395 $rs .= sprintf('BT %F Tz ET ', $this->font_stretching
);
5397 if ($this->font_spacing
!= 0) {
5398 // increase/decrease font spacing
5399 $rs .= sprintf('BT %F Tc ET ', ($this->font_spacing
* $this->k
));
5401 if ($this->ColorFlag
AND ($this->textrendermode
< 4)) {
5402 $s .= 'q '.$this->TextColor
.' ';
5405 $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode
, ($this->textstrokewidth
* $this->k
));
5406 // count number of spaces
5407 $ns = substr_count($txt, chr(32));
5410 if (($align == 'J') AND ($ns > 0)) {
5411 if ($this->isUnicodeFont()) {
5412 // get string width without spaces
5413 $width = $this->GetStringWidth(str_replace(' ', '', $txt));
5414 // calculate average space width
5415 $spacewidth = -1000 * ($w - $width - $this->cell_padding
['L'] - $this->cell_padding
['R']) / ($ns?$ns:1) / ($this->FontSize
?$this->FontSize
:1);
5416 if ($this->font_stretching
!= 100) {
5417 // word spacing is affected by stretching
5418 $spacewidth /= ($this->font_stretching
/ 100);
5420 // set word position to be used with TJ operator
5421 $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2);
5422 $unicode_justification = true;
5427 $spacewidth = (($w - $width - $this->cell_padding
['L'] - $this->cell_padding
['R']) / ($ns?$ns:1)) * $this->k
;
5428 if ($this->font_stretching
!= 100) {
5429 // word spacing (Tw) is affected by stretching
5430 $spacewidth /= ($this->font_stretching
/ 100);
5433 $rs .= sprintf('BT %F Tw ET ', $spacewidth);
5435 $width = $w - $this->cell_padding
['L'] - $this->cell_padding
['R'];
5437 // replace carriage return characters
5438 $txt2 = str_replace("\r", ' ', $txt2);
5441 $dx = ($w - $width) / 2;
5446 $dx = $this->cell_padding
['R'];
5448 $dx = $w - $width - $this->cell_padding
['R'];
5454 $dx = $w - $width - $this->cell_padding
['L'];
5456 $dx = $this->cell_padding
['L'];
5463 $dx = $this->cell_padding
['R'];
5465 $dx = $this->cell_padding
['L'];
5471 $xdx = $x - $dx - $width;
5477 $s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h
- $basefonty) * $k), $txt2);
5478 if (isset($uniblock)) {
5479 // print overlapping characters as separate string
5480 $xshift = 0; // horizontal shift
5481 $ty = (($this->h
- $basefonty +
(0.2 * $this->FontSize
)) * $k);
5482 $spw = (($w - $txwidth - $this->cell_padding
['L'] - $this->cell_padding
['R']) / ($ns?$ns:1));
5483 foreach ($uniblock as $uk => $uniarr) {
5484 if (($uk %
2) == 0) {
5486 if ($spacewidth != 0) {
5487 // justification shift
5488 $xshift +
= (count(array_keys($uniarr, 32)) * $spw);
5490 $xshift +
= $this->GetArrStringWidth($uniarr); // + shift justification
5492 // character to print
5493 $topchr = TCPDF_FONTS
::arrUTF8ToUTF16BE($uniarr, false);
5494 $topchr = TCPDF_STATIC
::_escape($topchr);
5495 $s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk +
($xshift * $k)), $ty, $topchr);
5499 if ($this->underline
) {
5500 $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
5502 if ($this->linethrough
) {
5503 $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
5505 if ($this->overline
) {
5506 $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
5508 if ($this->ColorFlag
AND ($this->textrendermode
< 4)) {
5512 $this->Link($xdx, $yt, $width, ($this->FontAscent +
$this->FontDescent
), $link, $ns);
5519 if ($this->font_spacing
!= 0) {
5520 // reset font spacing mode
5521 $rs .= ' BT 0 Tc ET';
5523 if ($this->font_stretching
!= 100) {
5524 // reset font stretching mode
5525 $rs .= ' BT 100 Tz ET';
5528 // reset word spacing
5529 if (!$this->isUnicodeFont() AND ($align == 'J')) {
5530 $rs .= ' BT 0 Tw ET';
5532 // reset stretching and spacing
5533 $this->font_stretching
= $prev_font_stretching;
5534 $this->font_spacing
= $prev_font_spacing;
5537 //Go to the beginning of the next line
5538 $this->y
= $y +
$h +
$this->cell_margin
['B'];
5541 $this->x
= $this->w
- $this->rMargin
;
5543 $this->x
= $this->lMargin
;
5547 // go left or right by case
5549 $this->x
= $x - $w - $this->cell_margin
['L'];
5551 $this->x
= $x +
$w +
$this->cell_margin
['R'];
5554 $gstyles = ''.$this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
.' '.$this->FillColor
."\n";
5556 $this->cell_padding
= $prev_cell_padding;
5557 $this->cell_margin
= $prev_cell_margin;
5562 * Replace a char if is defined on the current font.
5563 * @param $oldchar (int) Integer code (unicode) of the character to replace.
5564 * @param $newchar (int) Integer code (unicode) of the new character.
5565 * @return int the replaced char or the old char in case the new char i not defined
5567 * @since 5.9.167 (2012-06-22)
5569 protected function replaceChar($oldchar, $newchar) {
5570 if ($this->isCharDefined($newchar)) {
5571 // add the new char on the subset list
5572 $this->CurrentFont
['subsetchars'][$newchar] = true;
5573 // return the new character
5576 // return the old char
5581 * Returns the code to draw the cell border
5582 * @param $x (float) X coordinate.
5583 * @param $y (float) Y coordinate.
5584 * @param $w (float) Cell width.
5585 * @param $h (float) Cell height.
5586 * @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)))
5587 * @return string containing cell border code
5589 * @see SetLineStyle()
5590 * @since 5.7.000 (2010-08-02)
5592 protected function getCellBorder($x, $y, $w, $h, $brd) {
5593 $s = ''; // string to be returned
5598 $brd = array('LRTB' => true);
5600 // calculate coordinates for border
5603 $xeL = ($x - $w) * $k;
5607 $xeR = ($x +
$w) * $k;
5609 $yeL = (($this->h
- ($y +
$h)) * $k);
5610 $yeT = (($this->h
- $y) * $k);
5615 if (is_string($brd)) {
5616 // convert string to array
5617 $slen = strlen($brd);
5619 for ($i = 0; $i < $slen; ++
$i) {
5620 $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
5624 if (isset($brd['mode'])) {
5625 $mode = $brd['mode'];
5626 unset($brd['mode']);
5630 foreach ($brd as $border => $style) {
5631 if (is_array($style) AND !empty($style)) {
5632 // apply border style
5633 $prev_style = $this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
.' ';
5634 $s .= $this->SetLineStyle($style, true)."\n";
5638 $off = (($this->LineWidth
/ 2) * $k);
5647 $w +
= $this->LineWidth
;
5648 $h +
= $this->LineWidth
;
5652 $off = ($this->LineWidth
/ 2) * $k;
5661 $w -= $this->LineWidth
;
5662 $h -= $this->LineWidth
;
5678 // draw borders by case
5679 if (strlen($border) == 4) {
5680 $s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k));
5681 } elseif (strlen($border) == 3) {
5682 if (strpos($border,'B') === false) { // LTR
5683 $s .= sprintf('%F %F m ', $xL, $yL);
5684 $s .= sprintf('%F %F l ', $xT, $yT);
5685 $s .= sprintf('%F %F l ', $xR, $yR);
5686 $s .= sprintf('%F %F l ', $xB, $yB);
5688 } elseif (strpos($border,'L') === false) { // TRB
5689 $s .= sprintf('%F %F m ', $xT, $yT);
5690 $s .= sprintf('%F %F l ', $xR, $yR);
5691 $s .= sprintf('%F %F l ', $xB, $yB);
5692 $s .= sprintf('%F %F l ', $xL, $yL);
5694 } elseif (strpos($border,'T') === false) { // RBL
5695 $s .= sprintf('%F %F m ', $xR, $yR);
5696 $s .= sprintf('%F %F l ', $xB, $yB);
5697 $s .= sprintf('%F %F l ', $xL, $yL);
5698 $s .= sprintf('%F %F l ', $xT, $yT);
5700 } elseif (strpos($border,'R') === false) { // BLT
5701 $s .= sprintf('%F %F m ', $xB, $yB);
5702 $s .= sprintf('%F %F l ', $xL, $yL);
5703 $s .= sprintf('%F %F l ', $xT, $yT);
5704 $s .= sprintf('%F %F l ', $xR, $yR);
5707 } elseif (strlen($border) == 2) {
5708 if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
5709 $s .= sprintf('%F %F m ', $xL, $yL);
5710 $s .= sprintf('%F %F l ', $xT, $yT);
5711 $s .= sprintf('%F %F l ', $xR, $yR);
5713 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
5714 $s .= sprintf('%F %F m ', $xT, $yT);
5715 $s .= sprintf('%F %F l ', $xR, $yR);
5716 $s .= sprintf('%F %F l ', $xB, $yB);
5718 } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
5719 $s .= sprintf('%F %F m ', $xR, $yR);
5720 $s .= sprintf('%F %F l ', $xB, $yB);
5721 $s .= sprintf('%F %F l ', $xL, $yL);
5723 } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
5724 $s .= sprintf('%F %F m ', $xB, $yB);
5725 $s .= sprintf('%F %F l ', $xL, $yL);
5726 $s .= sprintf('%F %F l ', $xT, $yT);
5728 } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
5729 $s .= sprintf('%F %F m ', $xL, $yL);
5730 $s .= sprintf('%F %F l ', $xT, $yT);
5732 $s .= sprintf('%F %F m ', $xR, $yR);
5733 $s .= sprintf('%F %F l ', $xB, $yB);
5735 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
5736 $s .= sprintf('%F %F m ', $xT, $yT);
5737 $s .= sprintf('%F %F l ', $xR, $yR);
5739 $s .= sprintf('%F %F m ', $xB, $yB);
5740 $s .= sprintf('%F %F l ', $xL, $yL);
5743 } else { // strlen($border) == 1
5744 if (strpos($border,'L') !== false) { // L
5745 $s .= sprintf('%F %F m ', $xL, $yL);
5746 $s .= sprintf('%F %F l ', $xT, $yT);
5748 } elseif (strpos($border,'T') !== false) { // T
5749 $s .= sprintf('%F %F m ', $xT, $yT);
5750 $s .= sprintf('%F %F l ', $xR, $yR);
5752 } elseif (strpos($border,'R') !== false) { // R
5753 $s .= sprintf('%F %F m ', $xR, $yR);
5754 $s .= sprintf('%F %F l ', $xB, $yB);
5756 } elseif (strpos($border,'B') !== false) { // B
5757 $s .= sprintf('%F %F m ', $xB, $yB);
5758 $s .= sprintf('%F %F l ', $xL, $yL);
5762 if (is_array($style) AND !empty($style)) {
5763 // reset border style to previous value
5764 $s .= "\n".$this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
."\n";
5771 * This method allows printing text with line breaks.
5772 * 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 />
5773 * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
5774 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
5775 * @param $h (float) Cell minimum height. The cell extends automatically if needed.
5776 * @param $txt (string) String to print
5777 * @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)))
5778 * @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>
5779 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5780 * @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>
5781 * @param $x (float) x position in user units
5782 * @param $y (float) y position in user units
5783 * @param $reseth (boolean) if true reset the last cell height (default true).
5784 * @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.
5785 * @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.
5786 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
5787 * @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.
5788 * @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.
5789 * @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.
5790 * @return int Return the number of cells or 1 for html mode.
5793 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
5795 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) {
5796 $prev_cell_margin = $this->cell_margin
;
5797 $prev_cell_padding = $this->cell_padding
;
5798 // adjust internal padding
5799 $this->adjustCellPadding($border);
5800 $mc_padding = $this->cell_padding
;
5801 $mc_margin = $this->cell_margin
;
5802 $this->cell_padding
['T'] = 0;
5803 $this->cell_padding
['B'] = 0;
5804 $this->setCellMargins(0, 0, 0, 0);
5805 if (TCPDF_STATIC
::empty_string($this->lasth
) OR $reseth) {
5807 $this->resetLastH();
5809 if (!TCPDF_STATIC
::empty_string($y)) {
5815 if (($h > 0) AND $this->inPageBody() AND (($y +
$h +
$mc_margin['T'] +
$mc_margin['B']) > $this->PageBreakTrigger
)) {
5816 // spit cell in more pages/columns
5817 $newh = ($this->PageBreakTrigger
- $y);
5818 $resth = ($h - $newh); // cell to be printed on the next page/column
5821 // get current page number
5822 $startpage = $this->page
;
5823 // get current column
5824 $startcolumn = $this->current_column
;
5825 if (!TCPDF_STATIC
::empty_string($x)) {
5830 // check page for no-write regions and adapt page margins if necessary
5831 list($x, $y) = $this->checkPageRegions(0, $x, $y);
5833 $oy = $y +
$mc_margin['T'];
5835 $ox = ($this->w
- $x - $mc_margin['R']);
5837 $ox = ($x +
$mc_margin['L']);
5842 if (TCPDF_STATIC
::empty_string($w) OR ($w <= 0)) {
5844 $w = ($this->x
- $this->lMargin
- $mc_margin['L']);
5846 $w = ($this->w
- $this->x
- $this->rMargin
- $mc_margin['R']);
5849 // store original margin values
5850 $lMargin = $this->lMargin
;
5851 $rMargin = $this->rMargin
;
5853 $this->rMargin
= ($this->w
- $this->x
);
5854 $this->lMargin
= ($this->x
- $w);
5856 $this->lMargin
= ($this->x
);
5857 $this->rMargin
= ($this->w
- $this->x
- $w);
5859 $this->clMargin
= $this->lMargin
;
5860 $this->crMargin
= $this->rMargin
;
5863 $this->y +
= $mc_padding['T'];
5865 if ($ishtml) { // ******* Write HTML text
5866 $this->writeHTML($txt, true, false, $reseth, true, $align);
5868 } else { // ******* Write simple text
5869 $prev_FontSizePt = $this->FontSizePt
;
5871 // ajust height values
5872 $tobottom = ($this->h
- $this->y
- $this->bMargin
- $this->cell_padding
['T'] - $this->cell_padding
['B']);
5873 $h = $maxh = max(min($h, $tobottom), min($maxh, $tobottom));
5875 // vertical alignment
5878 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5879 if ($fitcell AND ($text_height > $maxh) AND ($this->FontSizePt
> 1)) {
5880 // try to reduce font size to fit text on cell (use a quick search algorithm)
5882 $fmax = $this->FontSizePt
;
5883 $diff_epsilon = (1 / $this->k
); // one point (min resolution)
5884 $maxit = (2 * min(100, max(10, intval($fmax)))); // max number of iterations
5885 while ($maxit >= 0) {
5886 $fmid = (($fmax +
$fmin) / 2);
5887 $this->SetFontSize($fmid, false);
5888 $this->resetLastH();
5889 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5890 $diff = ($maxh - $text_height);
5892 if ($diff <= $diff_epsilon) {
5902 // premature exit, we get the minimum font value to fit the cell
5903 $this->SetFontSize($fmin);
5904 $this->resetLastH();
5905 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5907 $this->SetFontSize($fmid);
5908 $this->resetLastH();
5911 if ($text_height < $maxh) {
5912 if ($valign == 'M') {
5913 // text vertically centered
5914 $this->y +
= (($maxh - $text_height) / 2);
5915 } elseif ($valign == 'B') {
5916 // text vertically aligned on bottom
5917 $this->y +
= ($maxh - $text_height);
5921 $nl = $this->Write($this->lasth
, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
5923 // restore font size
5924 $this->SetFontSize($prev_FontSizePt);
5928 // add bottom padding
5929 $this->y +
= $mc_padding['B'];
5931 // Get end-of-text Y position
5932 $currentY = $this->y
;
5933 // get latest page number
5934 $endpage = $this->page
;
5936 $skip = ($endpage - $startpage);
5938 while ($tmpresth > 0) {
5940 // add a page (or trig AcceptPageBreak() for multicolumn mode)
5941 $this->checkPageBreak($this->PageBreakTrigger +
1);
5943 if ($this->num_columns
> 1) {
5944 $tmpresth -= ($this->h
- $this->y
- $this->bMargin
);
5946 $tmpresth -= ($this->h
- $this->tMargin
- $this->bMargin
);
5950 $currentY = $this->y
;
5951 $endpage = $this->page
;
5953 // get latest column
5954 $endcolumn = $this->current_column
;
5955 if ($this->num_columns
== 0) {
5956 $this->num_columns
= 1;
5958 // disable page regions check
5959 $check_page_regions = $this->check_page_regions
;
5960 $this->check_page_regions
= false;
5962 $border_start = TCPDF_STATIC
::getBorderMode($border, $position='start', $this->opencell
);
5963 $border_end = TCPDF_STATIC
::getBorderMode($border, $position='end', $this->opencell
);
5964 $border_middle = TCPDF_STATIC
::getBorderMode($border, $position='middle', $this->opencell
);
5965 // design borders around HTML cells.
5966 for ($page = $startpage; $page <= $endpage; ++
$page) { // for each page
5968 $this->setPage($page);
5969 if ($this->num_columns
< 2) {
5970 // single-column mode
5972 $this->y
= $this->tMargin
;
5974 // account for margin changes
5975 if ($page > $startpage) {
5976 if (($this->rtl
) AND ($this->pagedim
[$page]['orm'] != $this->pagedim
[$startpage]['orm'])) {
5977 $this->x
-= ($this->pagedim
[$page]['orm'] - $this->pagedim
[$startpage]['orm']);
5978 } elseif ((!$this->rtl
) AND ($this->pagedim
[$page]['olm'] != $this->pagedim
[$startpage]['olm'])) {
5979 $this->x +
= ($this->pagedim
[$page]['olm'] - $this->pagedim
[$startpage]['olm']);
5982 if ($startpage == $endpage) {
5984 for ($column = $startcolumn; $column <= $endcolumn; ++
$column) { // for each column
5985 $this->selectColumn($column);
5987 $this->x
-= $mc_margin['R'];
5989 $this->x +
= $mc_margin['L'];
5991 if ($startcolumn == $endcolumn) { // single column
5993 $h = max($h, ($currentY - $oy));
5995 } elseif ($column == $startcolumn) { // first column
5996 $cborder = $border_start;
5998 $h = $this->h
- $this->y
- $this->bMargin
;
5999 } elseif ($column == $endcolumn) { // end column
6000 $cborder = $border_end;
6001 $h = $currentY - $this->y
;
6005 } else { // middle column
6006 $cborder = $border_middle;
6007 $h = $this->h
- $this->y
- $this->bMargin
;
6010 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6011 } // end for each column
6012 } elseif ($page == $startpage) { // first page
6013 for ($column = $startcolumn; $column < $this->num_columns
; ++
$column) { // for each column
6014 $this->selectColumn($column);
6016 $this->x
-= $mc_margin['R'];
6018 $this->x +
= $mc_margin['L'];
6020 if ($column == $startcolumn) { // first column
6021 $cborder = $border_start;
6023 $h = $this->h
- $this->y
- $this->bMargin
;
6024 } else { // middle column
6025 $cborder = $border_middle;
6026 $h = $this->h
- $this->y
- $this->bMargin
;
6029 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6030 } // end for each column
6031 } elseif ($page == $endpage) { // last page
6032 for ($column = 0; $column <= $endcolumn; ++
$column) { // for each column
6033 $this->selectColumn($column);
6035 $this->x
-= $mc_margin['R'];
6037 $this->x +
= $mc_margin['L'];
6039 if ($column == $endcolumn) {
6041 $cborder = $border_end;
6042 $h = $currentY - $this->y
;
6048 $cborder = $border_middle;
6049 $h = $this->h
- $this->y
- $this->bMargin
;
6052 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6053 } // end for each column
6054 } else { // middle page
6055 for ($column = 0; $column < $this->num_columns
; ++
$column) { // for each column
6056 $this->selectColumn($column);
6058 $this->x
-= $mc_margin['R'];
6060 $this->x +
= $mc_margin['L'];
6062 $cborder = $border_middle;
6063 $h = $this->h
- $this->y
- $this->bMargin
;
6065 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6066 } // end for each column
6068 if ($cborder OR $fill) {
6069 $offsetlen = strlen($ccode);
6070 // draw border and fill
6071 if ($this->inxobj
) {
6072 // we are inside an XObject template
6073 if (end($this->xobjects
[$this->xobjid
]['transfmrk']) !== false) {
6074 $pagemarkkey = key($this->xobjects
[$this->xobjid
]['transfmrk']);
6075 $pagemark = $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey];
6076 $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey] +
= $offsetlen;
6078 $pagemark = $this->xobjects
[$this->xobjid
]['intmrk'];
6079 $this->xobjects
[$this->xobjid
]['intmrk'] +
= $offsetlen;
6081 $pagebuff = $this->xobjects
[$this->xobjid
]['outdata'];
6082 $pstart = substr($pagebuff, 0, $pagemark);
6083 $pend = substr($pagebuff, $pagemark);
6084 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$ccode.$pend;
6086 if (end($this->transfmrk
[$this->page
]) !== false) {
6087 $pagemarkkey = key($this->transfmrk
[$this->page
]);
6088 $pagemark = $this->transfmrk
[$this->page
][$pagemarkkey];
6089 $this->transfmrk
[$this->page
][$pagemarkkey] +
= $offsetlen;
6090 } elseif ($this->InFooter
) {
6091 $pagemark = $this->footerpos
[$this->page
];
6092 $this->footerpos
[$this->page
] +
= $offsetlen;
6094 $pagemark = $this->intmrk
[$this->page
];
6095 $this->intmrk
[$this->page
] +
= $offsetlen;
6097 $pagebuff = $this->getPageBuffer($this->page
);
6098 $pstart = substr($pagebuff, 0, $pagemark);
6099 $pend = substr($pagebuff, $pagemark);
6100 $this->setPageBuffer($this->page
, $pstart.$ccode.$pend);
6103 } // end for each page
6104 // restore page regions check
6105 $this->check_page_regions
= $check_page_regions;
6106 // Get end-of-cell Y position
6107 $currentY = $this->GetY();
6108 // restore previous values
6109 if ($this->num_columns
> 1) {
6110 $this->selectColumn();
6112 // restore original margins
6113 $this->lMargin
= $lMargin;
6114 $this->rMargin
= $rMargin;
6115 if ($this->page
> $startpage) {
6116 // check for margin variations between pages (i.e. booklet mode)
6117 $dl = ($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$startpage]['olm']);
6118 $dr = ($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$startpage]['orm']);
6119 if (($dl != 0) OR ($dr != 0)) {
6120 $this->lMargin +
= $dl;
6121 $this->rMargin +
= $dr;
6126 //Go to the beginning of the next line
6127 $this->SetY($currentY +
$mc_margin['B']);
6129 $this->SetX($x +
$w +
$mc_margin['L'] +
$mc_margin['R']);
6132 // go left or right by case
6133 $this->setPage($startpage);
6135 $this->SetX($x +
$w +
$mc_margin['L'] +
$mc_margin['R']);
6137 $this->setContentMark();
6138 $this->cell_padding
= $prev_cell_padding;
6139 $this->cell_margin
= $prev_cell_margin;
6140 $this->clMargin
= $this->lMargin
;
6141 $this->crMargin
= $this->rMargin
;
6146 * This method return the estimated number of lines for print a simple text string using Multicell() method.
6147 * @param $txt (string) String for calculating his height
6148 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
6149 * @param $reseth (boolean) if true reset the last cell height (default false).
6150 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
6151 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
6152 * @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)))
6153 * @return float Return the minimal height needed for multicell method for printing the $txt param.
6154 * @author Alexander Escalona Fern\E1ndez, Nicola Asuni
6158 public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6159 if ($txt === NULL) {
6166 // adjust internal padding
6167 $prev_cell_padding = $this->cell_padding
;
6168 $prev_lasth = $this->lasth
;
6169 if (is_array($cellpadding)) {
6170 $this->cell_padding
= $cellpadding;
6172 $this->adjustCellPadding($border);
6173 if (TCPDF_STATIC
::empty_string($w) OR ($w <= 0)) {
6175 $w = $this->x
- $this->lMargin
;
6177 $w = $this->w
- $this->rMargin
- $this->x
;
6180 $wmax = $w - $this->cell_padding
['L'] - $this->cell_padding
['R'];
6183 $this->resetLastH();
6187 $chars = TCPDF_FONTS
::utf8Bidi(TCPDF_FONTS
::UTF8StringToArray($txt, $this->isunicode
, $this->CurrentFont
), $txt, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6188 $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
6189 $length = count($chars);
6190 $lastSeparator = -1;
6191 for ($i = 0; $i < $length; ++
$i) {
6193 $charWidth = $charsWidth[$i];
6196 OR preg_match($this->re_spaces
, TCPDF_FONTS
::unichr($c, $this->isunicode
))
6198 AND ($i > 0) AND ($i < ($length - 1))
6199 AND @preg_match('/[\p{L}]/'.$this->re_space
['m'], TCPDF_FONTS
::unichr($chars[($i - 1)], $this->isunicode
))
6200 AND @preg_match('/[\p{L}]/'.$this->re_space
['m'], TCPDF_FONTS
::unichr($chars[($i +
1)], $this->isunicode
))
6204 $lastSeparator = $i;
6206 if ((($sum +
$charWidth) > $wmax) OR ($c == 10)) {
6209 $lastSeparator = -1;
6211 } elseif ($lastSeparator != -1) {
6212 $i = $lastSeparator;
6213 $lastSeparator = -1;
6222 if ($chars[($length - 1)] == 10) {
6225 $this->cell_padding
= $prev_cell_padding;
6226 $this->lasth
= $prev_lasth;
6231 * This method return the estimated height needed for printing a simple text string using the Multicell() method.
6232 * Generally, if you want to know the exact height for a block of content you can use the following alternative technique:
6234 * // store current object
6235 * $pdf->startTransaction();
6236 * // store starting values
6237 * $start_y = $pdf->GetY();
6238 * $start_page = $pdf->getPage();
6239 * // call your printing functions with your parameters
6240 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6241 * $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);
6242 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6244 * $end_y = $pdf->GetY();
6245 * $end_page = $pdf->getPage();
6246 * // calculate height
6248 * if ($end_page == $start_page) {
6249 * $height = $end_y - $start_y;
6251 * for ($page=$start_page; $page <= $end_page; ++$page) {
6252 * $this->setPage($page);
6253 * if ($page == $start_page) {
6255 * $height = $this->h - $start_y - $this->bMargin;
6256 * } elseif ($page == $end_page) {
6258 * $height = $end_y - $this->tMargin;
6260 * $height = $this->h - $this->tMargin - $this->bMargin;
6264 * // restore previous object
6265 * $pdf = $pdf->rollbackTransaction();
6267 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
6268 * @param $txt (string) String for calculating his height
6269 * @param $reseth (boolean) if true reset the last cell height (default false).
6270 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
6271 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
6272 * @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)))
6273 * @return float Return the minimal height needed for multicell method for printing the $txt param.
6274 * @author Nicola Asuni, Alexander Escalona Fern\E1ndez
6277 public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6278 // adjust internal padding
6279 $prev_cell_padding = $this->cell_padding
;
6280 $prev_lasth = $this->lasth
;
6281 if (is_array($cellpadding)) {
6282 $this->cell_padding
= $cellpadding;
6284 $this->adjustCellPadding($border);
6285 $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
6286 $height = $this->getCellHeight(($lines * $this->FontSize
), $autopadding);
6287 $this->cell_padding
= $prev_cell_padding;
6288 $this->lasth
= $prev_lasth;
6293 * This method prints text from the current position.<br />
6294 * @param $h (float) Line height
6295 * @param $txt (string) String to print
6296 * @param $link (mixed) URL or identifier returned by AddLink()
6297 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
6298 * @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>
6299 * @param $ln (boolean) if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
6300 * @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.
6301 * @param $firstline (boolean) if true prints only the first line and return the remaining string.
6302 * @param $firstblock (boolean) if true the string is the starting of a line.
6303 * @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.
6304 * @param $wadj (float) first line width will be reduced by this amount (used in HTML mode).
6305 * @param $margin (array) margin array of the parent container
6306 * @return mixed Return the number of cells or the remaining string if $firstline = true.
6310 public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
6311 // check page for no-write regions and adapt page margins if necessary
6312 list($this->x
, $this->y
) = $this->checkPageRegions($h, $this->x
, $this->y
);
6313 if (strlen($txt) == 0) {
6317 if ($margin === '') {
6318 // set default margins
6319 $margin = $this->cell_margin
;
6321 // remove carriage returns
6322 $s = str_replace("\r", '', $txt);
6323 // check if string contains arabic text
6324 if (preg_match(TCPDF_FONT_DATA
::$uni_RE_PATTERN_ARABIC, $s)) {
6329 // check if string contains RTL text
6330 if ($arabic OR ($this->tmprtl
== 'R') OR preg_match(TCPDF_FONT_DATA
::$uni_RE_PATTERN_RTL, $s)) {
6336 $chrwidth = $this->GetCharWidth(46); // dot character
6337 // get array of unicode values
6338 $chars = TCPDF_FONTS
::UTF8StringToArray($s, $this->isunicode
, $this->CurrentFont
);
6339 // calculate maximum width for a single character on string
6340 $chrw = $this->GetArrStringWidth($chars, '', '', 0, true);
6341 array_walk($chrw, array($this, 'getRawCharWidth'));
6342 $maxchwidth = max($chrw);
6343 // get array of chars
6344 $uchars = TCPDF_FONTS
::UTF8ArrayToUniArray($chars, $this->isunicode
);
6345 // get the number of characters
6346 $nb = count($chars);
6347 // replacement for SHY character (minus symbol)
6348 $shy_replacement = 45;
6349 $shy_replacement_char = TCPDF_FONTS
::unichr($shy_replacement, $this->isunicode
);
6350 // widht for SHY replacement
6351 $shy_replacement_width = $this->GetCharWidth($shy_replacement);
6353 $pw = $w = $this->w
- $this->lMargin
- $this->rMargin
;
6354 // calculate remaining line width ($w)
6356 $w = $this->x
- $this->lMargin
;
6358 $w = $this->w
- $this->rMargin
- $this->x
;
6361 $wmax = ($w - $wadj);
6363 $wmax -= ($this->cell_padding
['L'] +
$this->cell_padding
['R']);
6365 if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) {
6366 // the maximum width character do not fit on column
6369 // minimum row height
6370 $row_height = max($h, $this->getCellHeight($this->FontSize
));
6372 $maxy = $this->y +
$maxh - max($row_height, $h);
6373 $start_page = $this->page
;
6374 $i = 0; // character position
6375 $j = 0; // current starting position
6376 $sep = -1; // position of the last blank space
6377 $prevsep = $sep; // previous separator
6378 $shy = false; // true if the last blank is a soft hypen (SHY)
6379 $prevshy = $shy; // previous shy mode
6380 $l = 0; // current string length
6381 $nl = 0; //number of lines
6383 $pc = 0; // previous character
6384 // for each character
6386 if (($maxh > 0) AND ($this->y
> $maxy) ) {
6389 //Get the current character
6391 if ($c == 10) { // 10 = "\n" = new line
6392 //Explicit line break
6393 if ($align == 'J') {
6402 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, $i);
6405 $tmparr = array_slice($chars, $j, ($i - $j));
6407 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6409 $linew = $this->GetArrStringWidth($tmparr);
6412 $this->endlinex
= $startx - $linew;
6414 $this->endlinex
= $startx +
$linew;
6417 $tmpcellpadding = $this->cell_padding
;
6419 $this->SetCellPadding(0);
6422 if ($firstblock AND $this->isRTLTextDir()) {
6423 $tmpstr = $this->stringRightTrim($tmpstr);
6425 // Skip newlines at the begining of a page or column
6426 if (!empty($tmpstr) OR ($this->y
< ($this->PageBreakTrigger
- $row_height))) {
6427 $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
6431 $this->cell_padding
= $tmpcellpadding;
6432 return (TCPDF_FONTS
::UniArrSubString($uchars, $i));
6440 // account for margin changes
6441 if ((($this->y +
$this->lasth
) > $this->PageBreakTrigger
) AND ($this->inPageBody())) {
6442 $this->AcceptPageBreak();
6444 $this->x
-= $margin['R'];
6446 $this->x +
= $margin['L'];
6448 $this->lMargin +
= $margin['L'];
6449 $this->rMargin +
= $margin['R'];
6451 $w = $this->getRemainingWidth();
6452 $wmax = ($w - $this->cell_padding
['L'] - $this->cell_padding
['R']);
6454 // 160 is the non-breaking space.
6455 // 173 is SHY (Soft Hypen).
6456 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
6457 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
6458 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
6461 OR preg_match($this->re_spaces
, TCPDF_FONTS
::unichr($c, $this->isunicode
))
6463 AND ($i < ($nb - 1))
6464 AND @preg_match('/[\p{L}]/'.$this->re_space
['m'], TCPDF_FONTS
::unichr($pc, $this->isunicode
))
6465 AND @preg_match('/[\p{L}]/'.$this->re_space
['m'], TCPDF_FONTS
::unichr($chars[($i +
1)], $this->isunicode
))
6469 // update last blank space position
6472 // check if is a SHY
6473 if (($c == 173) OR ($c == 45)) {
6477 $tmp_shy_replacement_width = 0;
6478 $tmp_shy_replacement_char = '';
6480 $tmp_shy_replacement_width = $shy_replacement_width;
6481 $tmp_shy_replacement_char = $shy_replacement_char;
6487 // update string length
6488 if ($this->isUnicodeFont() AND ($arabic)) {
6489 // with bidirectional algorithm some chars may be changed affecting the line length
6490 // *** very slow ***
6491 $l = $this->GetArrStringWidth(TCPDF_FONTS
::utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl
, $this->isunicode
, $this->CurrentFont
));
6493 $l +
= $this->GetCharWidth($c);
6495 if (($l > $wmax) OR (($c == 173) AND (($l +
$tmp_shy_replacement_width) >= $wmax))) {
6496 if (($c == 173) AND (($l +
$tmp_shy_replacement_width) > $wmax)) {
6500 // we have reached the end of column
6502 // check if the line was already started
6503 if (($this->rtl
AND ($this->x
<= ($this->w
- $this->rMargin
- $this->cell_padding
['R'] - $margin['R'] - $chrwidth)))
6504 OR ((!$this->rtl
) AND ($this->x
>= ($this->lMargin +
$this->cell_padding
['L'] +
$margin['L'] +
$chrwidth)))) {
6505 // print a void cell and go to next line
6506 $this->Cell($w, $h, '', 0, 1);
6509 return (TCPDF_FONTS
::UniArrSubString($uchars, $j));
6512 // truncate the word because do not fit on column
6513 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, $i);
6516 $tmparr = array_slice($chars, $j, ($i - $j));
6518 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6520 $linew = $this->GetArrStringWidth($tmparr);
6523 $this->endlinex
= $startx - $linew;
6525 $this->endlinex
= $startx +
$linew;
6528 $tmpcellpadding = $this->cell_padding
;
6530 $this->SetCellPadding(0);
6533 if ($firstblock AND $this->isRTLTextDir()) {
6534 $tmpstr = $this->stringRightTrim($tmpstr);
6536 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6539 $this->cell_padding
= $tmpcellpadding;
6540 return (TCPDF_FONTS
::UniArrSubString($uchars, $i));
6547 if ($this->rtl
AND (!$firstblock) AND ($sep < $i)) {
6552 // check the length of the next string
6553 $strrest = TCPDF_FONTS
::UniArrSubString($uchars, ($sep +
$endspace));
6554 $nextstr = TCPDF_STATIC
::pregSplit('/'.$this->re_space
['p'].'/', $this->re_space
['m'], $this->stringTrim($strrest));
6555 if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
6556 // truncate the word because do not fit on a full page width
6557 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, $i);
6560 $tmparr = array_slice($chars, $j, ($i - $j));
6562 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6564 $linew = $this->GetArrStringWidth($tmparr);
6567 $this->endlinex
= ($startx - $linew);
6569 $this->endlinex
= ($startx +
$linew);
6572 $tmpcellpadding = $this->cell_padding
;
6574 $this->SetCellPadding(0);
6577 if ($firstblock AND $this->isRTLTextDir()) {
6578 $tmpstr = $this->stringRightTrim($tmpstr);
6580 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6583 $this->cell_padding
= $tmpcellpadding;
6584 return (TCPDF_FONTS
::UniArrSubString($uchars, $i));
6591 // add hypen (minus symbol) at the end of the line
6592 $shy_width = $tmp_shy_replacement_width;
6594 $shy_char_left = $tmp_shy_replacement_char;
6595 $shy_char_right = '';
6597 $shy_char_left = '';
6598 $shy_char_right = $tmp_shy_replacement_char;
6602 $shy_char_left = '';
6603 $shy_char_right = '';
6605 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, ($sep +
$endspace));
6608 $tmparr = array_slice($chars, $j, (($sep +
$endspace) - $j));
6610 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6612 $linew = $this->GetArrStringWidth($tmparr);
6615 $this->endlinex
= $startx - $linew - $shy_width;
6617 $this->endlinex
= $startx +
$linew +
$shy_width;
6620 $tmpcellpadding = $this->cell_padding
;
6622 $this->SetCellPadding(0);
6626 if ($firstblock AND $this->isRTLTextDir()) {
6627 $tmpstr = $this->stringRightTrim($tmpstr);
6629 $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
6632 if ($chars[$sep] == 45) {
6635 // return the remaining text
6636 $this->cell_padding
= $tmpcellpadding;
6637 return (TCPDF_FONTS
::UniArrSubString($uchars, ($sep +
$endspace)));
6645 // account for margin changes
6646 if ((($this->y +
$this->lasth
) > $this->PageBreakTrigger
) AND ($this->inPageBody())) {
6647 $this->AcceptPageBreak();
6649 $this->x
-= $margin['R'];
6651 $this->x +
= $margin['L'];
6653 $this->lMargin +
= $margin['L'];
6654 $this->rMargin +
= $margin['R'];
6656 $w = $this->getRemainingWidth();
6657 $wmax = $w - $this->cell_padding
['L'] - $this->cell_padding
['R'];
6666 // save last character
6669 } // end while i < nb
6670 // print last substring (if any)
6699 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, $nb);
6702 $tmparr = array_slice($chars, $j, ($nb - $j));
6704 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6706 $linew = $this->GetArrStringWidth($tmparr);
6709 $this->endlinex
= $startx - $linew;
6711 $this->endlinex
= $startx +
$linew;
6714 $tmpcellpadding = $this->cell_padding
;
6716 $this->SetCellPadding(0);
6719 if ($firstblock AND $this->isRTLTextDir()) {
6720 $tmpstr = $this->stringRightTrim($tmpstr);
6722 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
6725 $this->cell_padding
= $tmpcellpadding;
6726 return (TCPDF_FONTS
::UniArrSubString($uchars, $nb));
6737 * Returns the remaining width between the current position and margins.
6738 * @return int Return the remaining width
6741 protected function getRemainingWidth() {
6742 list($this->x
, $this->y
) = $this->checkPageRegions(0, $this->x
, $this->y
);
6744 return ($this->x
- $this->lMargin
);
6746 return ($this->w
- $this->rMargin
- $this->x
);
6751 * Set the block dimensions accounting for page breaks and page/column fitting
6752 * @param $w (float) width
6753 * @param $h (float) height
6754 * @param $x (float) X coordinate
6755 * @param $y (float) Y coodiante
6756 * @param $fitonpage (boolean) if true the block is resized to not exceed page dimensions.
6757 * @return array($w, $h, $x, $y)
6759 * @since 5.5.009 (2010-07-05)
6761 protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
6763 // set maximum width
6764 $w = ($this->w
- $this->lMargin
- $this->rMargin
);
6770 // set maximum height
6771 $h = ($this->PageBreakTrigger
- $this->tMargin
);
6776 // resize the block to be vertically contained on a single page or single column
6777 if ($fitonpage OR $this->AutoPageBreak
) {
6778 $ratio_wh = ($w / $h);
6779 if ($h > ($this->PageBreakTrigger
- $this->tMargin
)) {
6780 $h = $this->PageBreakTrigger
- $this->tMargin
;
6781 $w = ($h * $ratio_wh);
6783 // resize the block to be horizontally contained on a single page or single column
6785 $maxw = ($this->w
- $this->lMargin
- $this->rMargin
);
6788 $h = ($w / $ratio_wh);
6792 // Check whether we need a new page or new column first as this does not fit
6795 if ($this->checkPageBreak($h, $y) OR ($this->y
< $prev_y)) {
6798 $x +
= ($prev_x - $this->x
);
6800 $x +
= ($this->x
- $prev_x);
6802 $this->newline
= true;
6804 // resize the block to be contained on the remaining available page or column space
6806 $ratio_wh = ($w / $h);
6807 if (($y +
$h) > $this->PageBreakTrigger
) {
6808 $h = $this->PageBreakTrigger
- $y;
6809 $w = ($h * $ratio_wh);
6811 if ((!$this->rtl
) AND (($x +
$w) > ($this->w
- $this->rMargin
))) {
6812 $w = $this->w
- $this->rMargin
- $x;
6813 $h = ($w / $ratio_wh);
6814 } elseif (($this->rtl
) AND (($x - $w) < ($this->lMargin
))) {
6815 $w = $x - $this->lMargin
;
6816 $h = ($w / $ratio_wh);
6819 return array($w, $h, $x, $y);
6823 * Puts an image in the page.
6824 * The upper-left corner must be given.
6825 * The dimensions can be specified in different ways:<ul>
6826 * <li>explicit width and height (expressed in user unit)</li>
6827 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
6828 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
6829 * 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;
6830 * The format can be specified explicitly or inferred from the file extension.<br />
6831 * It is possible to put a link on the image.<br />
6832 * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
6833 * @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').
6834 * @param $x (float) Abscissa of the upper-left corner (LTR) or upper-right corner (RTL).
6835 * @param $y (float) Ordinate of the upper-left corner (LTR) or upper-right corner (RTL).
6836 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
6837 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
6838 * @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.
6839 * @param $link (mixed) URL or identifier returned by AddLink().
6840 * @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>
6841 * @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).
6842 * @param $dpi (int) dot-per-inch resolution used on resize
6843 * @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>
6844 * @param $ismask (boolean) true if this image is a mask, false otherwise
6845 * @param $imgmask (mixed) image object returned by this function or false
6846 * @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)))
6847 * @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).
6848 * @param $hidden (boolean) If true do not display the image.
6849 * @param $fitonpage (boolean) If true the image is resized to not exceed page dimensions.
6850 * @param $alt (boolean) If true the image will be added as alternative and not directly printed (the ID of the image will be returned).
6851 * @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.
6852 * @return image information
6856 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()) {
6857 if ($this->state
!= 2) {
6866 // check page for no-write regions and adapt page margins if necessary
6867 list($x, $y) = $this->checkPageRegions($h, $x, $y);
6868 $exurl = ''; // external streams
6870 // check if we are passing an image as file or string
6871 if ($file[0] === '@') {
6872 // image from string
6873 $imgdata = substr($file, 1);
6874 } else { // image file
6875 if ($file[0] === '*') {
6876 // image as external stream
6877 $file = substr($file, 1);
6880 // check if is a local file
6881 if (!@file_exists($file)) {
6882 // try to encode spaces on filename
6883 $tfile = str_replace(' ', '%20', $file);
6884 if (@file_exists($tfile)) {
6888 if (($imsize = @getimagesize($file)) === FALSE) {
6889 if (in_array($file, $this->imagekeys
)) {
6890 // get existing image data
6891 $info = $this->getImageBuffer($file);
6892 $imsize = array($info['w'], $info['h']);
6893 } elseif (strpos($file, '__tcpdf_img') === FALSE) {
6894 $imgdata = TCPDF_STATIC
::fileGetContents($file);
6898 if (!empty($imgdata)) {
6899 // copy image to cache
6900 $original_file = $file;
6901 $file = TCPDF_STATIC
::getObjFilename('img');
6902 $fp = fopen($file, 'w');
6904 $this->Error('Unable to write file: '.$file);
6906 fwrite($fp, $imgdata);
6909 $imsize = @getimagesize($file);
6910 if ($imsize === FALSE) {
6912 $file = $original_file;
6914 $this->cached_files
[] = $file;
6917 if ($imsize === FALSE) {
6918 if (($w > 0) AND ($h > 0)) {
6919 // get measures from specified data
6920 $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit
, true) * $this->imgscale
* $this->k
;
6921 $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit
, true) * $this->imgscale
* $this->k
;
6922 $imsize = array($pw, $ph);
6924 $this->Error('[Image] Unable to fetch image: '.$file);
6928 $filehash = md5($this->file_id
.$file);
6929 // get original image width and height in pixels
6930 list($pixw, $pixh) = $imsize;
6931 // calculate image width and height on document
6932 if (($w <= 0) AND ($h <= 0)) {
6933 // convert image size to document unit
6934 $w = $this->pixelsToUnits($pixw);
6935 $h = $this->pixelsToUnits($pixh);
6936 } elseif ($w <= 0) {
6937 $w = $h * $pixw / $pixh;
6938 } elseif ($h <= 0) {
6939 $h = $w * $pixh / $pixw;
6940 } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
6941 if (strlen($fitbox) !== 2) {
6942 // set default alignment
6945 // scale image dimensions proportionally to fit within the ($w, $h) box
6946 if ((($w * $pixh) / ($h * $pixw)) < 1) {
6947 // store current height
6949 // calculate new height
6950 $h = $w * $pixh / $pixw;
6951 // height difference
6952 $hdiff = ($oldh - $h);
6953 // vertical alignment
6954 switch (strtoupper($fitbox[1])) {
6968 // store current width
6970 // calculate new width
6971 $w = $h * $pixw / $pixh;
6973 $wdiff = ($oldw - $w);
6974 // horizontal alignment
6975 switch (strtoupper($fitbox[0])) {
6999 // fit the image on available space
7000 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
7001 // calculate new minimum dimensions in pixels
7002 $neww = round($w * $this->k
* $dpi / $this->dpi
);
7003 $newh = round($h * $this->k
* $dpi / $this->dpi
);
7004 // check if resize is necessary (resize is used only to reduce the image)
7005 $newsize = ($neww * $newh);
7006 $pixsize = ($pixw * $pixh);
7007 if (intval($resize) == 2) {
7009 } elseif ($newsize >= $pixsize) {
7012 // check if image has been already added on document
7014 if (in_array($file, $this->imagekeys
)) {
7016 // get existing image data
7017 $info = $this->getImageBuffer($file);
7018 if (strpos($file, '__tcpdf_imgmask_') === FALSE) {
7019 // check if the newer image is larger
7020 $oldsize = ($info['w'] * $info['h']);
7021 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7025 } elseif (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_imgmask_') === FALSE)) {
7026 // create temp image file (without alpha channel)
7027 $tempfile_plain = K_PATH_CACHE
.'__tcpdf_imgmask_plain_'.$filehash;
7028 // create temp alpha file
7029 $tempfile_alpha = K_PATH_CACHE
.'__tcpdf_imgmask_alpha_'.$filehash;
7030 // check for cached images
7031 if (in_array($tempfile_plain, $this->imagekeys
)) {
7032 // get existing image data
7033 $info = $this->getImageBuffer($tempfile_plain);
7034 // check if the newer image is larger
7035 $oldsize = ($info['w'] * $info['h']);
7036 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7041 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7042 // embed image, masked with previously embedded mask
7043 return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7048 //First use of image, get info
7049 $type = strtolower($type);
7051 $type = TCPDF_IMAGES
::getImageFileType($file, $imsize);
7052 } elseif ($type == 'jpg') {
7055 $mqr = TCPDF_STATIC
::get_mqr();
7056 TCPDF_STATIC
::set_mqr(false);
7057 // Specific image handlers (defined on TCPDF_IMAGES CLASS)
7058 $mtd = '_parse'.$type;
7059 // GD image handler function
7060 $gdfunction = 'imagecreatefrom'.$type;
7062 if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
7063 // TCPDF image functions
7064 $info = TCPDF_IMAGES
::$mtd($file);
7065 if (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_imgmask_') === FALSE)
7066 AND (($info === 'pngalpha') OR (isset($info['trns']) AND !empty($info['trns'])))) {
7067 return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
7070 if (($info === false) AND function_exists($gdfunction)) {
7073 $img = $gdfunction($file);
7074 if ($img !== false) {
7076 $imgr = imagecreatetruecolor($neww, $newh);
7077 if (($type == 'gif') OR ($type == 'png')) {
7078 $imgr = TCPDF_IMAGES
::setGDImageTransparency($imgr, $img);
7080 imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
7083 if (($type == 'gif') OR ($type == 'png')) {
7084 $info = TCPDF_IMAGES
::_toPNG($img);
7086 $info = TCPDF_IMAGES
::_toJPEG($img, $this->jpeg_quality
);
7089 } catch(Exception
$e) {
7093 if (($info === false) AND extension_loaded('imagick')) {
7095 // ImageMagick library
7096 $img = new Imagick();
7097 if ($type == 'svg') {
7098 if ($file[0] === '@') {
7099 // image from string
7100 $svgimg = substr($file, 1);
7102 // get SVG file content
7103 $svgimg = TCPDF_STATIC
::fileGetContents($file);
7105 if ($svgimg !== FALSE) {
7106 // get width and height
7108 if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) {
7111 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7112 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit
, false);
7113 $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit
;
7114 $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
7119 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7120 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit
, false);
7121 $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit
;
7122 $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
7127 if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
7128 $vbw = ($ow * $this->imgscale
* $this->k
);
7129 $vbh = ($oh * $this->imgscale
* $this->k
);
7130 $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh);
7131 $svgtag = $vbox.$svgtag;
7133 $svgimg = preg_replace('/<svg([^\>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
7135 $img->readImageBlob($svgimg);
7138 $img->readImage($file);
7141 $img->resizeImage($neww, $newh, 10, 1, false);
7143 $img->setCompressionQuality($this->jpeg_quality
);
7144 $img->setImageFormat('jpeg');
7145 $tempname = TCPDF_STATIC
::getObjFilename('img');
7146 $img->writeImage($tempname);
7147 $info = TCPDF_IMAGES
::_parsejpeg($tempname);
7150 } catch(Exception
$e) {
7154 if ($info === false) {
7155 // unable to process image
7158 TCPDF_STATIC
::set_mqr($mqr);
7161 $info['cs'] = 'DeviceGray';
7163 if ($imgmask !== false) {
7164 $info['masked'] = $imgmask;
7166 if (!empty($exurl)) {
7167 $info['exurl'] = $exurl;
7169 // array of alternative images
7170 $info['altimgs'] = $altimgs;
7171 // add image to document
7172 $info['i'] = $this->setImageBuffer($file, $info);
7175 $this->img_rb_y
= $y +
$h;
7178 if ($palign == 'L') {
7179 $ximg = $this->lMargin
;
7180 } elseif ($palign == 'C') {
7181 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
7182 } elseif ($palign == 'R') {
7183 $ximg = $this->w
- $this->rMargin
- $w;
7187 $this->img_rb_x
= $ximg;
7189 if ($palign == 'L') {
7190 $ximg = $this->lMargin
;
7191 } elseif ($palign == 'C') {
7192 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
7193 } elseif ($palign == 'R') {
7194 $ximg = $this->w
- $this->rMargin
- $w;
7198 $this->img_rb_x
= $ximg +
$w;
7200 if ($ismask OR $hidden) {
7201 // image is not displayed
7204 $xkimg = $ximg * $this->k
;
7206 // only non-alternative immages will be set
7207 $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']));
7209 if (!empty($border)) {
7217 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
7222 $this->Link($ximg, $y, $w, $h, $link, 0);
7224 // set pointer to align the next text/objects
7228 $this->x
= $this->img_rb_x
;
7232 $this->y
= $y +
round($h/2);
7233 $this->x
= $this->img_rb_x
;
7237 $this->y
= $this->img_rb_y
;
7238 $this->x
= $this->img_rb_x
;
7242 $this->SetY($this->img_rb_y
);
7249 $this->endlinex
= $this->img_rb_x
;
7250 if ($this->inxobj
) {
7251 // we are inside an XObject template
7252 $this->xobjects
[$this->xobjid
]['images'][] = $info['i'];
7258 * Extract info from a PNG image with alpha channel using the Imagick or GD library.
7259 * @param $file (string) Name of the file containing the image.
7260 * @param $x (float) Abscissa of the upper-left corner.
7261 * @param $y (float) Ordinate of the upper-left corner.
7262 * @param $wpx (float) Original width of the image in pixels.
7263 * @param $hpx (float) original height of the image in pixels.
7264 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
7265 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
7266 * @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.
7267 * @param $link (mixed) URL or identifier returned by AddLink().
7268 * @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>
7269 * @param $resize (boolean) If true resize (reduce) the image to fit $w and $h (requires GD library).
7270 * @param $dpi (int) dot-per-inch resolution used on resize
7271 * @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>
7272 * @param $filehash (string) File hash used to build unique file names.
7273 * @author Nicola Asuni
7275 * @since 4.3.007 (2008-12-04)
7278 protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
7279 // create temp images
7280 if (empty($filehash)) {
7281 $filehash = md5($this->file_id
.$file);
7283 // create temp image file (without alpha channel)
7284 $tempfile_plain = K_PATH_CACHE
.'__tcpdf_imgmask_plain_'.$filehash;
7285 // create temp alpha file
7286 $tempfile_alpha = K_PATH_CACHE
.'__tcpdf_imgmask_alpha_'.$filehash;
7289 // ImageMagick extension
7290 if (($parsed === false) AND extension_loaded('imagick')) {
7292 // ImageMagick library
7293 $img = new Imagick();
7294 $img->readImage($file);
7295 // clone image object
7296 $imga = TCPDF_STATIC
::objclone($img);
7297 // extract alpha channel
7298 if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) {
7299 $img->setImageAlphaChannel(Imagick
::ALPHACHANNEL_EXTRACT
);
7301 $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
7302 $img->negateImage(true);
7304 $img->setImageFormat('png');
7305 $img->writeImage($tempfile_alpha);
7306 // remove alpha channel
7307 if (method_exists($imga, 'setImageMatte')) {
7308 $imga->setImageMatte(false);
7310 $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
7312 $imga->setImageFormat('png');
7313 $imga->writeImage($tempfile_plain);
7315 } catch (Exception
$e) {
7316 // Imagemagick fails, try with GD
7317 $parse_error = 'Imagick library error: '.$e->getMessage();
7321 if (($parsed === false) AND function_exists('imagecreatefrompng')) {
7324 $img = imagecreatefrompng($file);
7325 $imgalpha = imagecreate($wpx, $hpx);
7326 // generate gray scale palette (0 -> 255)
7327 for ($c = 0; $c < 256; ++
$c) {
7328 ImageColorAllocate($imgalpha, $c, $c, $c);
7330 // extract alpha channel
7331 for ($xpx = 0; $xpx < $wpx; ++
$xpx) {
7332 for ($ypx = 0; $ypx < $hpx; ++
$ypx) {
7333 $color = imagecolorat($img, $xpx, $ypx);
7334 // get and correct gamma color
7335 $alpha = $this->getGDgamma($img, $color);
7336 imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
7339 imagepng($imgalpha, $tempfile_alpha);
7340 imagedestroy($imgalpha);
7341 // extract image without alpha channel
7342 $imgplain = imagecreatetruecolor($wpx, $hpx);
7343 imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
7344 imagepng($imgplain, $tempfile_plain);
7345 imagedestroy($imgplain);
7347 } catch (Exception
$e) {
7349 $parse_error = 'GD library error: '.$e->getMessage();
7352 if ($parsed === false) {
7353 if (empty($parse_error)) {
7354 $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
7356 $this->Error($parse_error);
7360 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7361 // embed image, masked with previously embedded mask
7362 $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7363 // remove temp files
7364 unlink($tempfile_alpha);
7365 unlink($tempfile_plain);
7369 * Get the GD-corrected PNG gamma value from alpha color
7370 * @param $img (int) GD image Resource ID.
7371 * @param $c (int) alpha color
7373 * @since 4.3.007 (2008-12-04)
7375 protected function getGDgamma($img, $c) {
7376 if (!isset($this->gdgammacache
['#'.$c])) {
7377 $colors = imagecolorsforindex($img, $c);
7378 // GD alpha is only 7 bit (0 -> 127)
7379 $this->gdgammacache
['#'.$c] = (((127 - $colors['alpha']) / 127) * 255);
7381 $this->gdgammacache
['#'.$c] = (pow(($this->gdgammacache
['#'.$c] / 255), 2.2) * 255);
7382 // store the latest values on cache to improve performances
7383 if (count($this->gdgammacache
) > 8) {
7384 // remove one element from the cache array
7385 array_shift($this->gdgammacache
);
7388 return $this->gdgammacache
['#'.$c];
7392 * Performs a line break.
7393 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
7394 * @param $h (float) The height of the break. By default, the value equals the height of the last printed cell.
7395 * @param $cell (boolean) if true add the current left (or right o for RTL) padding to the X coordinate
7400 public function Ln($h='', $cell=false) {
7401 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'])) {
7402 // revove vertical space from the top of the column
7407 $cellpadding = $this->cell_padding
['R'];
7409 $cellpadding = $this->cell_padding
['L'];
7415 $this->x
= $this->w
- $this->rMargin
- $cellpadding;
7417 $this->x
= $this->lMargin +
$cellpadding;
7419 if (is_string($h)) {
7420 $this->y +
= $this->lasth
;
7424 $this->newline
= true;
7428 * Returns the relative X value of current position.
7429 * The value is relative to the left border for LTR languages and to the right border for RTL languages.
7433 * @see SetX(), GetY(), SetY()
7435 public function GetX() {
7438 return ($this->w
- $this->x
);
7445 * Returns the absolute X value of current position.
7449 * @see SetX(), GetY(), SetY()
7451 public function GetAbsX() {
7456 * Returns the ordinate of the current position.
7460 * @see SetY(), GetX(), SetX()
7462 public function GetY() {
7467 * Defines the abscissa of the current position.
7468 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
7469 * @param $x (float) The value of the abscissa in user units.
7470 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7473 * @see GetX(), GetY(), SetY(), SetXY()
7475 public function SetX($x, $rtloff=false) {
7477 if (!$rtloff AND $this->rtl
) {
7479 $this->x
= $this->w
- $x;
7487 $this->x
= $this->w +
$x;
7493 if ($this->x
> $this->w
) {
7494 $this->x
= $this->w
;
7499 * Moves the current abscissa back to the left margin and sets the ordinate.
7500 * If the passed value is negative, it is relative to the bottom of the page.
7501 * @param $y (float) The value of the ordinate in user units.
7502 * @param $resetx (bool) if true (default) reset the X position.
7503 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7506 * @see GetX(), GetY(), SetY(), SetXY()
7508 public function SetY($y, $resetx=true, $rtloff=false) {
7512 if (!$rtloff AND $this->rtl
) {
7513 $this->x
= $this->w
- $this->rMargin
;
7515 $this->x
= $this->lMargin
;
7521 $this->y
= $this->h +
$y;
7526 if ($this->y
> $this->h
) {
7527 $this->y
= $this->h
;
7532 * Defines the abscissa and ordinate of the current position.
7533 * If the passed values are negative, they are relative respectively to the right and bottom of the page.
7534 * @param $x (float) The value of the abscissa.
7535 * @param $y (float) The value of the ordinate.
7536 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7539 * @see SetX(), SetY()
7541 public function SetXY($x, $y, $rtloff=false) {
7542 $this->SetY($y, false, $rtloff);
7543 $this->SetX($x, $rtloff);
7547 * Set the absolute X coordinate of the current pointer.
7548 * @param $x (float) The value of the abscissa in user units.
7550 * @since 5.9.186 (2012-09-13)
7551 * @see setAbsX(), setAbsY(), SetAbsXY()
7553 public function SetAbsX($x) {
7554 $this->x
= floatval($x);
7558 * Set the absolute Y coordinate of the current pointer.
7559 * @param $y (float) (float) The value of the ordinate in user units.
7561 * @since 5.9.186 (2012-09-13)
7562 * @see setAbsX(), setAbsY(), SetAbsXY()
7564 public function SetAbsY($y) {
7565 $this->y
= floatval($y);
7569 * Set the absolute X and Y coordinates of the current pointer.
7570 * @param $x (float) The value of the abscissa in user units.
7571 * @param $y (float) (float) The value of the ordinate in user units.
7573 * @since 5.9.186 (2012-09-13)
7574 * @see setAbsX(), setAbsY(), SetAbsXY()
7576 public function SetAbsXY($x, $y) {
7582 * Send the document to a given destination: string, local file or browser.
7583 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
7584 * The method first calls Close() if necessary to terminate the document.
7585 * @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.
7586 * @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>
7591 public function Output($name='doc.pdf', $dest='I') {
7592 //Output PDF to some destination
7593 //Finish document if necessary
7594 if ($this->state
< 3) {
7597 //Normalize parameters
7598 if (is_bool($dest)) {
7599 $dest = $dest ? 'D' : 'F';
7601 $dest = strtoupper($dest);
7602 if ($dest[0] != 'F') {
7603 $name = preg_replace('/[\s]+/', '_', $name);
7604 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
7607 // *** apply digital signature to the document ***
7608 // get the document content
7609 $pdfdoc = $this->getBuffer();
7610 // remove last newline
7611 $pdfdoc = substr($pdfdoc, 0, -1);
7612 // Remove the original buffer
7613 if (isset($this->diskcache
) AND $this->diskcache
) {
7614 // remove buffer file from cache
7615 unlink($this->buffer
);
7617 unset($this->buffer
);
7618 // remove filler space
7619 $byterange_string_len = strlen(TCPDF_STATIC
::$byterange_string);
7620 // define the ByteRange
7621 $byte_range = array();
7623 $byte_range[1] = strpos($pdfdoc, TCPDF_STATIC
::$byterange_string) +
$byterange_string_len +
10;
7624 $byte_range[2] = $byte_range[1] +
$this->signature_max_length +
2;
7625 $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
7626 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
7627 // replace the ByteRange
7628 $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
7629 $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
7630 $pdfdoc = str_replace(TCPDF_STATIC
::$byterange_string, $byterange, $pdfdoc);
7631 // write the document to a temporary folder
7632 $tempdoc = TCPDF_STATIC
::getObjFilename('doc');
7633 $f = fopen($tempdoc, 'wb');
7635 $this->Error('Unable to create temporary file: '.$tempdoc);
7637 $pdfdoc_length = strlen($pdfdoc);
7638 fwrite($f, $pdfdoc, $pdfdoc_length);
7640 // get digital signature via openssl library
7641 $tempsign = TCPDF_STATIC
::getObjFilename('sig');
7642 if (empty($this->signature_data
['extracerts'])) {
7643 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data
['signcert'], array($this->signature_data
['privkey'], $this->signature_data
['password']), array(), PKCS7_BINARY
| PKCS7_DETACHED
);
7645 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']);
7649 $signature = file_get_contents($tempsign);
7651 // extract signature
7652 $signature = substr($signature, $pdfdoc_length);
7653 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") +
13));
7654 $tmparr = explode("\n\n", $signature);
7655 $signature = $tmparr[1];
7658 $signature = base64_decode(trim($signature));
7659 // add TSA timestamp to signature
7660 $signature = $this->applyTSA($signature);
7661 // convert signature to hex
7662 $signature = current(unpack('H*', $signature));
7663 $signature = str_pad($signature, $this->signature_max_length
, '0');
7664 // disable disk caching
7665 $this->diskcache
= false;
7666 // Add signature to the document
7667 $this->buffer
= substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
7668 $this->bufferlen
= strlen($this->buffer
);
7672 // Send PDF to the standard output
7673 if (ob_get_contents()) {
7674 $this->Error('Some data has already been output, can\'t send PDF file');
7676 if (php_sapi_name() != 'cli') {
7677 // send output to a browser
7678 header('Content-Type: application/pdf');
7679 if (headers_sent()) {
7680 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7682 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7683 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7684 header('Pragma: public');
7685 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7686 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7687 header('Content-Disposition: inline; filename="'.basename($name).'"');
7688 TCPDF_STATIC
::sendOutputData($this->getBuffer(), $this->bufferlen
);
7690 echo $this->getBuffer();
7695 // download PDF as file
7696 if (ob_get_contents()) {
7697 $this->Error('Some data has already been output, can\'t send PDF file');
7699 header('Content-Description: File Transfer');
7700 if (headers_sent()) {
7701 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7703 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7704 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7705 header('Pragma: public');
7706 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7707 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7708 // force download dialog
7709 if (strpos(php_sapi_name(), 'cgi') === false) {
7710 header('Content-Type: application/force-download');
7711 header('Content-Type: application/octet-stream', false);
7712 header('Content-Type: application/download', false);
7713 header('Content-Type: application/pdf', false);
7715 header('Content-Type: application/pdf');
7717 // use the Content-Disposition header to supply a recommended filename
7718 header('Content-Disposition: attachment; filename="'.basename($name).'"');
7719 header('Content-Transfer-Encoding: binary');
7720 TCPDF_STATIC
::sendOutputData($this->getBuffer(), $this->bufferlen
);
7726 // save PDF to a local file
7727 if ($this->diskcache
) {
7728 copy($this->buffer
, $name);
7730 $f = fopen($name, 'wb');
7732 $this->Error('Unable to create output file: '.$name);
7734 fwrite($f, $this->getBuffer(), $this->bufferlen
);
7737 if ($dest == 'FI') {
7738 // send headers to browser
7739 header('Content-Type: application/pdf');
7740 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7741 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7742 header('Pragma: public');
7743 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7744 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7745 header('Content-Disposition: inline; filename="'.basename($name).'"');
7746 TCPDF_STATIC
::sendOutputData(file_get_contents($name), filesize($name));
7747 } elseif ($dest == 'FD') {
7748 // send headers to browser
7749 if (ob_get_contents()) {
7750 $this->Error('Some data has already been output, can\'t send PDF file');
7752 header('Content-Description: File Transfer');
7753 if (headers_sent()) {
7754 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7756 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7757 header('Pragma: public');
7758 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7759 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7760 // force download dialog
7761 if (strpos(php_sapi_name(), 'cgi') === false) {
7762 header('Content-Type: application/force-download');
7763 header('Content-Type: application/octet-stream', false);
7764 header('Content-Type: application/download', false);
7765 header('Content-Type: application/pdf', false);
7767 header('Content-Type: application/pdf');
7769 // use the Content-Disposition header to supply a recommended filename
7770 header('Content-Disposition: attachment; filename="'.basename($name).'"');
7771 header('Content-Transfer-Encoding: binary');
7772 TCPDF_STATIC
::sendOutputData(file_get_contents($name), filesize($name));
7777 // return PDF as base64 mime multi-part email attachment (RFC 2045)
7778 $retval = 'Content-Type: application/pdf;'."\r\n";
7779 $retval .= ' name="'.$name.'"'."\r\n";
7780 $retval .= 'Content-Transfer-Encoding: base64'."\r\n";
7781 $retval .= 'Content-Disposition: attachment;'."\r\n";
7782 $retval .= ' filename="'.$name.'"'."\r\n\r\n";
7783 $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
7787 // returns PDF as a string
7788 return $this->getBuffer();
7791 $this->Error('Incorrect output destination: '.$dest);
7798 * Unset all class variables except the following critical variables.
7799 * @param $destroyall (boolean) if true destroys all class variables, otherwise preserves critical variables.
7800 * @param $preserve_objcopy (boolean) if true preserves the objcopy variable
7802 * @since 4.5.016 (2009-02-24)
7804 public function _destroy($destroyall=false, $preserve_objcopy=false) {
7805 if ($destroyall AND isset($this->diskcache
) AND $this->diskcache
AND (!$preserve_objcopy) AND (!TCPDF_STATIC
::empty_string($this->buffer
))) {
7806 // remove buffer file from cache
7807 unlink($this->buffer
);
7809 if ($destroyall AND !empty($this->cached_files
)) {
7810 // remove cached files
7811 foreach ($this->cached_files
as $cachefile) {
7812 if (is_file($cachefile)) {
7816 unset($this->cached_files
);
7819 'internal_encoding',
7827 'signature_max_length',
7832 foreach (array_keys(get_object_vars($this)) as $val) {
7833 if ($destroyall OR !in_array($val, $preserve)) {
7834 if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) {
7842 * Check for locale-related bug
7845 protected function _dochecks() {
7846 //Check for locale-related bug
7848 $this->Error('Don\'t alter the locale before including class file');
7850 //Check for decimal separator
7851 if (sprintf('%.1F', 1.0) != '1.0') {
7852 setlocale(LC_NUMERIC
, 'C');
7857 * Return an array containing variations for the basic page number alias.
7858 * @param $a (string) Base alias.
7859 * @return array of page number aliases
7862 protected function getInternalPageNumberAliases($a= '') {
7864 // build array of Unicode + ASCII variants (the order is important)
7865 $alias = array('u' => array(), 'a' => array());
7867 $alias['u'][] = TCPDF_STATIC
::_escape($u);
7868 if ($this->isunicode
) {
7869 $alias['u'][] = TCPDF_STATIC
::_escape(TCPDF_FONTS
::UTF8ToLatin1($u, $this->isunicode
, $this->CurrentFont
));
7870 $alias['u'][] = TCPDF_STATIC
::_escape(TCPDF_FONTS
::utf8StrRev($u, false, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
));
7871 $alias['a'][] = TCPDF_STATIC
::_escape(TCPDF_FONTS
::UTF8ToLatin1($a, $this->isunicode
, $this->CurrentFont
));
7872 $alias['a'][] = TCPDF_STATIC
::_escape(TCPDF_FONTS
::utf8StrRev($a, false, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
));
7874 $alias['a'][] = TCPDF_STATIC
::_escape($a);
7879 * Return an array containing all internal page aliases.
7880 * @return array of page number aliases
7883 protected function getAllInternalPageNumberAliases() {
7884 $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);
7886 foreach($basic_alias as $k => $a) {
7887 $pnalias[$k] = $this->getInternalPageNumberAliases($a);
7893 * Replace right shift page number aliases with spaces to correct right alignment.
7894 * This works perfectly only when using monospaced fonts.
7895 * @param $page (string) Page content.
7896 * @param $aliases (array) Array of page aliases.
7897 * @param $diff (int) initial difference to add.
7898 * @return replaced page content.
7901 protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
7902 foreach ($aliases as $type => $alias) {
7903 foreach ($alias as $a) {
7904 // find position of compensation factor
7905 $startnum = (strpos($a, ':') +
1);
7906 $a = substr($a, 0, $startnum);
7907 if (($pos = strpos($page, $a)) !== false) {
7909 $endnum = strpos($page, '}', $pos);
7910 // string to be replaced
7911 $aa = substr($page, $pos, ($endnum - $pos +
1));
7912 // get compensation factor
7913 $ratio = substr($page, ($pos +
$startnum), ($endnum - $pos - $startnum));
7914 $ratio = preg_replace('/[^0-9\.]/', '', $ratio);
7915 $ratio = floatval($ratio);
7917 $chrdiff = floor(($diff +
12) * $ratio);
7918 $shift = str_repeat(' ', $chrdiff);
7919 $shift = TCPDF_FONTS
::UTF8ToUTF16BE($shift, false, $this->isunicode
, $this->CurrentFont
);
7921 $chrdiff = floor(($diff +
11) * $ratio);
7922 $shift = str_repeat(' ', $chrdiff);
7924 $page = str_replace($aa, $shift, $page);
7932 * Set page boxes to be included on page descriptions.
7933 * @param $boxes (array) Array of page boxes to set on document: ('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox').
7936 protected function setPageBoxTypes($boxes) {
7937 $this->page_boxes
= array();
7938 foreach ($boxes as $box) {
7939 if (in_array($box, TCPDF_STATIC
::$pageboxes)) {
7940 $this->page_boxes
[] = $box;
7946 * Output pages (and replace page number aliases).
7949 protected function _putpages() {
7950 $filter = ($this->compress
) ? '/Filter /FlateDecode ' : '';
7951 // get internal aliases for page numbers
7952 $pnalias = $this->getAllInternalPageNumberAliases();
7953 $num_pages = $this->numpages
;
7954 $ptpa = TCPDF_STATIC
::formatPageNumber(($this->starting_page_number +
$num_pages - 1));
7955 $ptpu = TCPDF_FONTS
::UTF8ToUTF16BE($ptpa, false, $this->isunicode
, $this->CurrentFont
);
7956 $ptp_num_chars = $this->GetNumChars($ptpa);
7962 for ($n = 1; $n <= $num_pages; ++
$n) {
7964 $temppage = $this->getPageBuffer($n);
7965 $pagelen = strlen($temppage);
7966 // set replacements for total pages number
7967 $pnpa = TCPDF_STATIC
::formatPageNumber(($this->starting_page_number +
$n - 1));
7968 $pnpu = TCPDF_FONTS
::UTF8ToUTF16BE($pnpa, false, $this->isunicode
, $this->CurrentFont
);
7969 $pnp_num_chars = $this->GetNumChars($pnpa);
7970 $pdiff = 0; // difference used for right shift alignment of page numbers
7971 $gdiff = 0; // difference used for right shift alignment of page group numbers
7972 if (!empty($this->pagegroups
)) {
7973 if (isset($this->newpagegroup
[$n])) {
7976 $ptga = TCPDF_STATIC
::formatPageNumber($this->pagegroups
[$groupnum]);
7977 $ptgu = TCPDF_FONTS
::UTF8ToUTF16BE($ptga, false, $this->isunicode
, $this->CurrentFont
);
7978 $ptg_num_chars = $this->GetNumChars($ptga);
7981 $pnga = TCPDF_STATIC
::formatPageNumber($pagegroupnum);
7982 $pngu = TCPDF_FONTS
::UTF8ToUTF16BE($pnga, false, $this->isunicode
, $this->CurrentFont
);
7983 $png_num_chars = $this->GetNumChars($pnga);
7984 // replace page numbers
7986 $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
7987 $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
7988 $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
7989 $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
7990 list($temppage, $gdiff) = TCPDF_STATIC
::replacePageNumAliases($temppage, $replace, $gdiff);
7992 // replace page numbers
7994 $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
7995 $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
7996 $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
7997 $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
7998 list($temppage, $pdiff) = TCPDF_STATIC
::replacePageNumAliases($temppage, $replace, $pdiff);
7999 // replace right shift alias
8000 $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
8001 // replace EPS marker
8002 $temppage = str_replace($this->epsmarker
, '', $temppage);
8004 $this->page_obj_id
[$n] = $this->_newobj();
8006 $out .= ' /Type /Page';
8007 $out .= ' /Parent 1 0 R';
8008 if (empty($this->signature_data
['approval']) OR ($this->signature_data
['approval'] != 'A')) {
8009 $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp
);
8011 $out .= ' /Resources 2 0 R';
8012 foreach ($this->page_boxes
as $box) {
8014 $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']);
8016 if (isset($this->pagedim
[$n]['BoxColorInfo']) AND !empty($this->pagedim
[$n]['BoxColorInfo'])) {
8017 $out .= ' /BoxColorInfo <<';
8018 foreach ($this->page_boxes
as $box) {
8019 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box])) {
8020 $out .= ' /'.$box.' <<';
8021 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box]['C'])) {
8022 $color = $this->pagedim
[$n]['BoxColorInfo'][$box]['C'];
8024 $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
8027 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box]['W'])) {
8028 $out .= ' /W '.($this->pagedim
[$n]['BoxColorInfo'][$box]['W'] * $this->k
);
8030 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box]['S'])) {
8031 $out .= ' /S /'.$this->pagedim
[$n]['BoxColorInfo'][$box]['S'];
8033 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box]['D'])) {
8034 $dashes = $this->pagedim
[$n]['BoxColorInfo'][$box]['D'];
8036 foreach ($dashes as $dash) {
8037 $out .= sprintf(' %F', ($dash * $this->k
));
8046 $out .= ' /Contents '.($this->n +
1).' 0 R';
8047 $out .= ' /Rotate '.$this->pagedim
[$n]['Rotate'];
8048 if (!$this->pdfa_mode
) {
8049 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
8051 if (isset($this->pagedim
[$n]['trans']) AND !empty($this->pagedim
[$n]['trans'])) {
8053 if (isset($this->pagedim
[$n]['trans']['Dur'])) {
8054 $out .= ' /Dur '.$this->pagedim
[$n]['trans']['Dur'];
8056 $out .= ' /Trans <<';
8057 $out .= ' /Type /Trans';
8058 if (isset($this->pagedim
[$n]['trans']['S'])) {
8059 $out .= ' /S /'.$this->pagedim
[$n]['trans']['S'];
8061 if (isset($this->pagedim
[$n]['trans']['D'])) {
8062 $out .= ' /D '.$this->pagedim
[$n]['trans']['D'];
8064 if (isset($this->pagedim
[$n]['trans']['Dm'])) {
8065 $out .= ' /Dm /'.$this->pagedim
[$n]['trans']['Dm'];
8067 if (isset($this->pagedim
[$n]['trans']['M'])) {
8068 $out .= ' /M /'.$this->pagedim
[$n]['trans']['M'];
8070 if (isset($this->pagedim
[$n]['trans']['Di'])) {
8071 $out .= ' /Di '.$this->pagedim
[$n]['trans']['Di'];
8073 if (isset($this->pagedim
[$n]['trans']['SS'])) {
8074 $out .= ' /SS '.$this->pagedim
[$n]['trans']['SS'];
8076 if (isset($this->pagedim
[$n]['trans']['B'])) {
8077 $out .= ' /B '.$this->pagedim
[$n]['trans']['B'];
8081 $out .= $this->_getannotsrefs($n);
8082 $out .= ' /PZ '.$this->pagedim
[$n]['PZ'];
8084 $out .= "\n".'endobj';
8087 $p = ($this->compress
) ? gzcompress($temppage) : $temppage;
8089 $p = $this->_getrawstream($p);
8090 $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
8091 if ($this->diskcache
) {
8092 // remove temporary files
8093 unlink($this->pages
[$n]);
8097 $out = $this->_getobj(1)."\n";
8098 $out .= '<< /Type /Pages /Kids [';
8099 foreach($this->page_obj_id
as $page_obj) {
8100 $out .= ' '.$page_obj.' 0 R';
8102 $out .= ' ] /Count '.$num_pages.' >>';
8103 $out .= "\n".'endobj';
8108 * Output references to page annotations
8109 * @param $n (int) page number
8111 * @author Nicola Asuni
8112 * @since 4.7.000 (2008-08-29)
8115 protected function _putannotsrefs($n) {
8116 $this->_out($this->_getannotsrefs($n));
8120 * Get references to page annotations.
8121 * @param $n (int) page number
8124 * @author Nicola Asuni
8125 * @since 5.0.010 (2010-05-17)
8127 protected function _getannotsrefs($n) {
8128 if (!(isset($this->PageAnnots
[$n]) OR ($this->sign
AND isset($this->signature_data
['cert_type'])))) {
8131 $out = ' /Annots [';
8132 if (isset($this->PageAnnots
[$n])) {
8133 foreach ($this->PageAnnots
[$n] as $key => $val) {
8134 if (!in_array($val['n'], $this->radio_groups
)) {
8135 $out .= ' '.$val['n'].' 0 R';
8138 // add radiobutton groups
8139 if (isset($this->radiobutton_groups
[$n])) {
8140 foreach ($this->radiobutton_groups
[$n] as $key => $data) {
8141 if (isset($data['n'])) {
8142 $out .= ' '.$data['n'].' 0 R';
8147 if ($this->sign
AND ($n == $this->signature_appearance
['page']) AND isset($this->signature_data
['cert_type'])) {
8148 // set reference for signature object
8149 $out .= ' '.$this->sig_obj_id
.' 0 R';
8151 if (!empty($this->empty_signature_appearance
)) {
8152 foreach ($this->empty_signature_appearance
as $esa) {
8153 if ($esa['page'] == $n) {
8154 // set reference for empty signature objects
8155 $out .= ' '.$esa['objid'].' 0 R';
8164 * Output annotations objects for all pages.
8165 * !!! THIS METHOD IS NOT YET COMPLETED !!!
8166 * See section 12.5 of PDF 32000_2008 reference.
8168 * @author Nicola Asuni
8169 * @since 4.0.018 (2008-08-06)
8171 protected function _putannotsobjs() {
8172 // reset object counter
8173 for ($n=1; $n <= $this->numpages
; ++
$n) {
8174 if (isset($this->PageAnnots
[$n])) {
8175 // set page annotations
8176 foreach ($this->PageAnnots
[$n] as $key => $pl) {
8177 $annot_obj_id = $this->PageAnnots
[$n][$key]['n'];
8178 // create annotation object for grouping radiobuttons
8179 if (isset($this->radiobutton_groups
[$n][$pl['txt']]) AND is_array($this->radiobutton_groups
[$n][$pl['txt']])) {
8180 $radio_button_obj_id = $this->radiobutton_groups
[$n][$pl['txt']]['n'];
8182 $annots .= ' /Type /Annot';
8183 $annots .= ' /Subtype /Widget';
8184 $annots .= ' /Rect [0 0 0 0]';
8185 if ($this->radiobutton_groups
[$n][$pl['txt']]['#readonly#']) {
8187 $annots .= ' /F 68';
8188 $annots .= ' /Ff 49153';
8190 $annots .= ' /F 4'; // default print for PDF/A
8191 $annots .= ' /Ff 49152';
8193 $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
8194 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8195 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id);
8197 $annots .= ' /FT /Btn';
8198 $annots .= ' /Kids [';
8200 foreach ($this->radiobutton_groups
[$n][$pl['txt']] as $key => $data) {
8201 if (isset($data['kid'])) {
8202 $annots .= ' '.$data['kid'].' 0 R';
8203 if ($data['def'] !== 'Off') {
8204 $defval = $data['def'];
8209 if (!empty($defval)) {
8210 $annots .= ' /V /'.$defval;
8213 $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
8214 $this->form_obj_id
[] = $radio_button_obj_id;
8215 // store object id to be used on Parent entry of Kids
8216 $this->radiobutton_groups
[$n][$pl['txt']] = $radio_button_obj_id;
8219 $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER
);
8220 $a = $pl['x'] * $this->k
;
8221 $b = $this->pagedim
[$n]['h'] - (($pl['y'] +
$pl['h']) * $this->k
);
8222 $c = $pl['w'] * $this->k
;
8223 $d = $pl['h'] * $this->k
;
8224 $rect = sprintf('%F %F %F %F', $a, $b, $a+
$c, $b+
$d);
8225 // create new annotation object
8226 $annots = '<</Type /Annot';
8227 $annots .= ' /Subtype /'.$pl['opt']['subtype'];
8228 $annots .= ' /Rect ['.$rect.']';
8229 $ft = array('Btn', 'Tx', 'Ch', 'Sig');
8230 if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
8231 $annots .= ' /FT /'.$pl['opt']['ft'];
8234 $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
8235 $annots .= ' /P '.$this->page_obj_id
[$n].' 0 R';
8236 $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
8237 $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp
);
8238 if (isset($pl['opt']['f'])) {
8240 if (is_array($pl['opt']['f'])) {
8241 foreach ($pl['opt']['f'] as $f) {
8242 switch (strtolower($f)) {
8275 case 'togglenoview': {
8279 case 'lockedcontents': {
8289 $fval = intval($pl['opt']['f']);
8294 if ($this->pdfa_mode
) {
8295 // force print flag for PDF/A mode
8298 $annots .= ' /F '.intval($fval);
8299 if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
8300 $annots .= ' /AS /'.$pl['opt']['as'];
8302 if (isset($pl['opt']['ap'])) {
8303 // appearance stream
8304 $annots .= ' /AP <<';
8305 if (is_array($pl['opt']['ap'])) {
8306 foreach ($pl['opt']['ap'] as $apmode => $apdef) {
8307 // $apmode can be: n = normal; r = rollover; d = down;
8308 $annots .= ' /'.strtoupper($apmode);
8309 if (is_array($apdef)) {
8311 foreach ($apdef as $apstate => $stream) {
8312 // reference to XObject that define the appearance for this mode-state
8313 $apsobjid = $this->_putAPXObject($c, $d, $stream);
8314 $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
8318 // reference to XObject that define the appearance for this mode
8319 $apsobjid = $this->_putAPXObject($c, $d, $apdef);
8320 $annots .= ' '.$apsobjid.' 0 R';
8324 $annots .= $pl['opt']['ap'];
8328 if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
8329 $annots .= ' /BS <<';
8330 $annots .= ' /Type /Border';
8331 if (isset($pl['opt']['bs']['w'])) {
8332 $annots .= ' /W '.intval($pl['opt']['bs']['w']);
8334 $bstyles = array('S', 'D', 'B', 'I', 'U');
8335 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
8336 $annots .= ' /S /'.$pl['opt']['bs']['s'];
8338 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
8340 foreach ($pl['opt']['bs']['d'] as $cord) {
8341 $annots .= ' '.intval($cord);
8347 $annots .= ' /Border [';
8348 if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
8349 $annots .= intval($pl['opt']['border'][0]).' ';
8350 $annots .= intval($pl['opt']['border'][1]).' ';
8351 $annots .= intval($pl['opt']['border'][2]);
8352 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
8354 foreach ($pl['opt']['border'][3] as $dash) {
8355 $annots .= intval($dash).' ';
8364 if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
8365 $annots .= ' /BE <<';
8366 $bstyles = array('S', 'C');
8367 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) {
8368 $annots .= ' /S /'.$pl['opt']['bs']['s'];
8370 $annots .= ' /S /S';
8372 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
8373 $annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']);
8377 if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
8378 $annots .= ' /C '.TCPDF_COLORS
::getColorStringFromArray($pl['opt']['c']);
8380 //$annots .= ' /StructParent ';
8381 //$annots .= ' /OC ';
8382 $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
8383 if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
8384 // this is a markup type
8385 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8386 $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
8388 //$annots .= ' /Popup ';
8389 if (isset($pl['opt']['ca'])) {
8390 $annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca']));
8392 if (isset($pl['opt']['rc'])) {
8393 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8395 $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp
);
8396 //$annots .= ' /IRT ';
8397 if (isset($pl['opt']['subj'])) {
8398 $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
8400 //$annots .= ' /RT ';
8401 //$annots .= ' /IT ';
8402 //$annots .= ' /ExData ';
8404 $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
8406 switch (strtolower($pl['opt']['subtype'])) {
8408 if (isset($pl['opt']['open'])) {
8409 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
8411 $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
8412 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8413 $annots .= ' /Name /'.$pl['opt']['name'];
8415 $annots .= ' /Name /Note';
8417 $statemodels = array('Marked', 'Review');
8418 if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
8419 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8421 $pl['opt']['statemodel'] = 'Marked';
8422 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8424 if ($pl['opt']['statemodel'] == 'Marked') {
8425 $states = array('Accepted', 'Unmarked');
8427 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
8429 if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
8430 $annots .= ' /State /'.$pl['opt']['state'];
8432 if ($pl['opt']['statemodel'] == 'Marked') {
8433 $annots .= ' /State /Unmarked';
8435 $annots .= ' /State /None';
8441 if (is_string($pl['txt'])) {
8442 if ($pl['txt'][0] == '#') {
8443 // internal destination
8444 $annots .= ' /Dest /'.TCPDF_STATIC
::encodeNameObject(substr($pl['txt'], 1));
8445 } elseif ($pl['txt'][0] == '%') {
8446 // embedded PDF file
8447 $filename = basename(substr($pl['txt'], 1));
8448 $annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles
[$filename]['a'].' >> >>';
8449 } elseif ($pl['txt'][0] == '*') {
8450 // embedded generic file
8451 $filename = basename(substr($pl['txt'], 1));
8452 $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});';
8453 $annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>';
8455 $parsedUrl = parse_url($pl['txt']);
8456 if (empty($parsedUrl['scheme']) AND (strtolower(substr($parsedUrl['path'], -4)) == '.pdf')) {
8457 // relative link to a PDF file
8458 $dest = '[0 /Fit]'; // default page 0
8459 if (!empty($parsedUrl['fragment'])) {
8460 // check for named destination
8461 $tmp = explode('=', $parsedUrl['fragment']);
8462 $dest = '('.((count($tmp) == 2) ? $tmp[1] : $tmp[0]).')';
8464 $annots .= ' /A <</S /GoToR /D '.$dest.' /F '.$this->_datastring($this->unhtmlentities($parsedUrl['path']), $annot_obj_id).' /NewWindow true>>';
8466 // external URI link
8467 $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
8470 } elseif (isset($this->links
[$pl['txt']])) {
8472 $l = $this->links
[$pl['txt']];
8473 if (isset($this->page_obj_id
[($l['p'])])) {
8474 $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
)));
8477 $hmodes = array('N', 'I', 'O', 'P');
8478 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
8479 $annots .= ' /H /'.$pl['opt']['h'];
8481 $annots .= ' /H /I';
8483 //$annots .= ' /PA ';
8484 //$annots .= ' /Quadpoints ';
8488 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8489 $annots .= ' /DA ('.$pl['opt']['da'].')';
8491 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8492 $annots .= ' /Q '.intval($pl['opt']['q']);
8494 if (isset($pl['opt']['rc'])) {
8495 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8497 if (isset($pl['opt']['ds'])) {
8498 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
8500 if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
8501 $annots .= ' /CL [';
8502 foreach ($pl['opt']['cl'] as $cl) {
8503 $annots .= sprintf('%F ', $cl * $this->k
);
8507 $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
8508 if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
8509 $annots .= ' /IT /'.$pl['opt']['it'];
8511 if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
8512 $l = $pl['opt']['rd'][0] * $this->k
;
8513 $r = $pl['opt']['rd'][1] * $this->k
;
8514 $t = $pl['opt']['rd'][2] * $this->k
;
8515 $b = $pl['opt']['rd'][3] * $this->k
;
8516 $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']';
8518 if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
8519 $annots .= ' /LE /'.$pl['opt']['le'];
8562 case 'fileattachment': {
8563 if ($this->pdfa_mode
) {
8564 // embedded files are not allowed in PDF/A mode
8567 if (!isset($pl['opt']['fs'])) {
8570 $filename = basename($pl['opt']['fs']);
8571 if (isset($this->embeddedfiles
[$filename]['f'])) {
8572 $annots .= ' /FS '.$this->embeddedfiles
[$filename]['f'].' 0 R';
8573 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
8574 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8575 $annots .= ' /Name /'.$pl['opt']['name'];
8577 $annots .= ' /Name /PushPin';
8579 // index (zero-based) of the annotation in the Annots array of this page
8580 $this->embeddedfiles
[$filename]['a'] = $key;
8585 if (!isset($pl['opt']['fs'])) {
8588 $filename = basename($pl['opt']['fs']);
8589 if (isset($this->embeddedfiles
[$filename]['f'])) {
8590 // ... TO BE COMPLETED ...
8591 // /R /C /B /E /CO /CP
8592 $annots .= ' /Sound '.$this->embeddedfiles
[$filename]['f'].' 0 R';
8593 $iconsapp = array('Speaker', 'Mic');
8594 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8595 $annots .= ' /Name /'.$pl['opt']['name'];
8597 $annots .= ' /Name /Speaker';
8606 $hmode = array('N', 'I', 'O', 'P', 'T');
8607 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
8608 $annots .= ' /H /'.$pl['opt']['h'];
8610 if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
8611 $annots .= ' /MK <<';
8612 if (isset($pl['opt']['mk']['r'])) {
8613 $annots .= ' /R '.$pl['opt']['mk']['r'];
8615 if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
8616 $annots .= ' /BC '.TCPDF_COLORS
::getColorStringFromArray($pl['opt']['mk']['bc']);
8618 if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
8619 $annots .= ' /BG '.TCPDF_COLORS
::getColorStringFromArray($pl['opt']['mk']['bg']);
8621 if (isset($pl['opt']['mk']['ca'])) {
8622 $annots .= ' /CA '.$pl['opt']['mk']['ca'];
8624 if (isset($pl['opt']['mk']['rc'])) {
8625 $annots .= ' /RC '.$pl['opt']['mk']['rc'];
8627 if (isset($pl['opt']['mk']['ac'])) {
8628 $annots .= ' /AC '.$pl['opt']['mk']['ac'];
8630 if (isset($pl['opt']['mk']['i'])) {
8631 $info = $this->getImageBuffer($pl['opt']['mk']['i']);
8632 if ($info !== false) {
8633 $annots .= ' /I '.$info['n'].' 0 R';
8636 if (isset($pl['opt']['mk']['ri'])) {
8637 $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
8638 if ($info !== false) {
8639 $annots .= ' /RI '.$info['n'].' 0 R';
8642 if (isset($pl['opt']['mk']['ix'])) {
8643 $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
8644 if ($info !== false) {
8645 $annots .= ' /IX '.$info['n'].' 0 R';
8648 if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
8649 $annots .= ' /IF <<';
8650 $if_sw = array('A', 'B', 'S', 'N');
8651 if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
8652 $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
8654 $if_s = array('A', 'P');
8655 if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
8656 $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
8658 if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
8659 $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
8661 if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
8662 $annots .= ' /FB true';
8666 if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
8667 $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
8671 // --- Entries for field dictionaries ---
8672 if (isset($this->radiobutton_groups
[$n][$pl['txt']])) {
8674 $annots .= ' /Parent '.$this->radiobutton_groups
[$n][$pl['txt']].' 0 R';
8676 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8677 $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
8679 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8680 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
8682 if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
8683 $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
8685 if (isset($pl['opt']['ff'])) {
8686 if (is_array($pl['opt']['ff'])) {
8687 // array of bit settings
8689 foreach($pl['opt']['ff'] as $val) {
8690 $flag +
= 1 << ($val - 1);
8693 $flag = intval($pl['opt']['ff']);
8695 $annots .= ' /Ff '.$flag;
8697 if (isset($pl['opt']['maxlen'])) {
8698 $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
8700 if (isset($pl['opt']['v'])) {
8702 if (is_array($pl['opt']['v'])) {
8703 foreach ($pl['opt']['v'] AS $optval) {
8704 if (is_float($optval)) {
8705 $optval = sprintf('%F', $optval);
8707 $annots .= ' '.$optval;
8710 $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
8713 if (isset($pl['opt']['dv'])) {
8715 if (is_array($pl['opt']['dv'])) {
8716 foreach ($pl['opt']['dv'] AS $optval) {
8717 if (is_float($optval)) {
8718 $optval = sprintf('%F', $optval);
8720 $annots .= ' '.$optval;
8723 $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
8726 if (isset($pl['opt']['rv'])) {
8728 if (is_array($pl['opt']['rv'])) {
8729 foreach ($pl['opt']['rv'] AS $optval) {
8730 if (is_float($optval)) {
8731 $optval = sprintf('%F', $optval);
8733 $annots .= ' '.$optval;
8736 $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
8739 if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
8740 $annots .= ' /A << '.$pl['opt']['a'].' >>';
8742 if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
8743 $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
8745 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8746 $annots .= ' /DA ('.$pl['opt']['da'].')';
8748 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8749 $annots .= ' /Q '.intval($pl['opt']['q']);
8751 if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
8752 $annots .= ' /Opt [';
8753 foreach($pl['opt']['opt'] AS $copt) {
8754 if (is_array($copt)) {
8755 $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
8757 $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
8762 if (isset($pl['opt']['ti'])) {
8763 $annots .= ' /TI '.intval($pl['opt']['ti']);
8765 if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
8767 foreach($pl['opt']['i'] AS $copt) {
8768 $annots .= intval($copt).' ';
8777 case 'printermark': {
8794 // create new annotation object
8795 $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
8796 if ($formfield AND !isset($this->radiobutton_groups
[$n][$pl['txt']])) {
8797 // store reference of form object
8798 $this->form_obj_id
[] = $annot_obj_id;
8802 } // end for each page
8806 * Put appearance streams XObject used to define annotation's appearance states.
8807 * @param $w (int) annotation width
8808 * @param $h (int) annotation height
8809 * @param $stream (string) appearance stream
8810 * @return int object ID
8812 * @since 4.8.001 (2009-09-09)
8814 protected function _putAPXObject($w=0, $h=0, $stream='') {
8815 $stream = trim($stream);
8816 $out = $this->_getobj()."\n";
8817 $this->xobjects
['AX'.$this->n
] = array('n' => $this->n
);
8819 $out .= ' /Type /XObject';
8820 $out .= ' /Subtype /Form';
8821 $out .= ' /FormType 1';
8822 if ($this->compress
) {
8823 $stream = gzcompress($stream);
8824 $out .= ' /Filter /FlateDecode';
8826 $rect = sprintf('%F %F', $w, $h);
8827 $out .= ' /BBox [0 0 '.$rect.']';
8828 $out .= ' /Matrix [1 0 0 1 0 0]';
8829 $out .= ' /Resources 2 0 R';
8830 $stream = $this->_getrawstream($stream);
8831 $out .= ' /Length '.strlen($stream);
8833 $out .= ' stream'."\n".$stream."\n".'endstream';
8834 $out .= "\n".'endobj';
8841 * @author Nicola Asuni
8844 protected function _putfonts() {
8846 foreach ($this->diffs
as $diff) {
8849 $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
8851 $mqr = TCPDF_STATIC
::get_mqr();
8852 TCPDF_STATIC
::set_mqr(false);
8853 foreach ($this->FontFiles
as $file => $info) {
8854 // search and get font file to embedd
8855 $fontfile = TCPDF_FONTS
::getFontFullPath($file, $info['fontdir']);
8856 if (!TCPDF_STATIC
::empty_string($fontfile)) {
8857 $font = file_get_contents($fontfile);
8858 $compressed = (substr($file, -2) == '.z');
8859 if ((!$compressed) AND (isset($info['length2']))) {
8860 $header = (ord($font[0]) == 128);
8862 // strip first binary header
8863 $font = substr($font, 6);
8865 if ($header AND (ord($font[$info['length1']]) == 128)) {
8866 // strip second binary header
8867 $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] +
6));
8869 } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
8872 $font = gzuncompress($font);
8874 // merge subset characters
8875 $subsetchars = array(); // used chars
8876 foreach ($info['fontkeys'] as $fontkey) {
8877 $fontinfo = $this->getFontBuffer($fontkey);
8878 $subsetchars +
= $fontinfo['subsetchars'];
8880 // rebuild a font subset
8881 $font = TCPDF_FONTS
::_getTrueTypeFontSubset($font, $subsetchars);
8882 // calculate new font length
8883 $info['length1'] = strlen($font);
8886 $font = gzcompress($font);
8890 $this->FontFiles
[$file]['n'] = $this->n
;
8891 $stream = $this->_getrawstream($font);
8892 $out = '<< /Length '.strlen($stream);
8894 $out .= ' /Filter /FlateDecode';
8896 $out .= ' /Length1 '.$info['length1'];
8897 if (isset($info['length2'])) {
8898 $out .= ' /Length2 '.$info['length2'].' /Length3 0';
8901 $out .= ' stream'."\n".$stream."\n".'endstream';
8902 $out .= "\n".'endobj';
8906 TCPDF_STATIC
::set_mqr($mqr);
8907 foreach ($this->fontkeys
as $k) {
8909 $font = $this->getFontBuffer($k);
8910 $type = $font['type'];
8911 $name = $font['name'];
8912 if ($type == 'core') {
8913 // standard core font
8914 $out = $this->_getobj($this->font_obj_ids
[$k])."\n";
8915 $out .= '<</Type /Font';
8916 $out .= ' /Subtype /Type1';
8917 $out .= ' /BaseFont /'.$name;
8918 $out .= ' /Name /F'.$font['i'];
8919 if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
8920 $out .= ' /Encoding /WinAnsiEncoding';
8922 if ($k == 'helvetica') {
8923 // add default font for annotations
8924 $this->annotation_fonts
[$k] = $font['i'];
8927 $out .= "\n".'endobj';
8929 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
8930 // additional Type1 or TrueType font
8931 $out = $this->_getobj($this->font_obj_ids
[$k])."\n";
8932 $out .= '<</Type /Font';
8933 $out .= ' /Subtype /'.$type;
8934 $out .= ' /BaseFont /'.$name;
8935 $out .= ' /Name /F'.$font['i'];
8936 $out .= ' /FirstChar 32 /LastChar 255';
8937 $out .= ' /Widths '.($this->n +
1).' 0 R';
8938 $out .= ' /FontDescriptor '.($this->n +
2).' 0 R';
8940 if (isset($font['diff'])) {
8941 $out .= ' /Encoding '.($nf +
$font['diff']).' 0 R';
8943 $out .= ' /Encoding /WinAnsiEncoding';
8947 $out .= "\n".'endobj';
8952 for ($i = 32; $i < 256; ++
$i) {
8953 if (isset($font['cw'][$i])) {
8954 $s .= $font['cw'][$i].' ';
8956 $s .= $font['dw'].' ';
8960 $s .= "\n".'endobj';
8964 $s = '<</Type /FontDescriptor /FontName /'.$name;
8965 foreach ($font['desc'] as $fdk => $fdv) {
8966 if (is_float($fdv)) {
8967 $fdv = sprintf('%F', $fdv);
8969 $s .= ' /'.$fdk.' '.$fdv.'';
8971 if (!TCPDF_STATIC
::empty_string($font['file'])) {
8972 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles
[$font['file']]['n'].' 0 R';
8975 $s .= "\n".'endobj';
8979 $mtd = '_put'.strtolower($type);
8980 if (!method_exists($this, $mtd)) {
8981 $this->Error('Unsupported font type: '.$type);
8989 * Adds unicode fonts.<br>
8990 * Based on PDF Reference 1.3 (section 5)
8991 * @param $font (array) font data
8993 * @author Nicola Asuni
8994 * @since 1.52.0.TC005 (2005-01-05)
8996 protected function _puttruetypeunicode($font) {
8998 if ($font['subset']) {
8999 // change name for font subsetting
9000 $subtag = sprintf('%06u', $font['i']);
9001 $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
9002 $fontname .= $subtag.'+';
9004 $fontname .= $font['name'];
9006 // A composite font composed of other fonts, organized hierarchically
9007 $out = $this->_getobj($this->font_obj_ids
[$font['fontkey']])."\n";
9008 $out .= '<< /Type /Font';
9009 $out .= ' /Subtype /Type0';
9010 $out .= ' /BaseFont /'.$fontname;
9011 $out .= ' /Name /F'.$font['i'];
9012 $out .= ' /Encoding /'.$font['enc'];
9013 $out .= ' /ToUnicode '.($this->n +
1).' 0 R';
9014 $out .= ' /DescendantFonts ['.($this->n +
2).' 0 R]';
9016 $out .= "\n".'endobj';
9018 // ToUnicode map for Identity-H
9019 $stream = TCPDF_FONT_DATA
::$uni_identity_h;
9022 $stream = ($this->compress
) ? gzcompress($stream) : $stream;
9023 $filter = ($this->compress
) ? '/Filter /FlateDecode ' : '';
9024 $stream = $this->_getrawstream($stream);
9025 $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
9027 // A CIDFont whose glyph descriptions are based on TrueType font technology
9028 $oid = $this->_newobj();
9029 $out = '<< /Type /Font';
9030 $out .= ' /Subtype /CIDFontType2';
9031 $out .= ' /BaseFont /'.$fontname;
9032 // A dictionary containing entries that define the character collection of the CIDFont.
9033 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9034 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9035 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9036 $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
9037 $out .= ' /FontDescriptor '.($this->n +
1).' 0 R';
9038 $out .= ' /DW '.$font['dw']; // default width
9039 $out .= "\n".TCPDF_FONTS
::_putfontwidths($font, 0);
9040 if (isset($font['ctg']) AND (!TCPDF_STATIC
::empty_string($font['ctg']))) {
9041 $out .= "\n".'/CIDToGIDMap '.($this->n +
2).' 0 R';
9044 $out .= "\n".'endobj';
9047 // A font descriptor describing the CIDFont default metrics other than its glyph widths
9049 $out = '<< /Type /FontDescriptor';
9050 $out .= ' /FontName /'.$fontname;
9051 foreach ($font['desc'] as $key => $value) {
9052 if (is_float($value)) {
9053 $value = sprintf('%F', $value);
9055 $out .= ' /'.$key.' '.$value;
9058 if (!TCPDF_STATIC
::empty_string($font['file'])) {
9059 // A stream containing a TrueType font
9060 $out .= ' /FontFile2 '.$this->FontFiles
[$font['file']]['n'].' 0 R';
9061 $fontdir = $this->FontFiles
[$font['file']]['fontdir'];
9064 $out .= "\n".'endobj';
9066 if (isset($font['ctg']) AND (!TCPDF_STATIC
::empty_string($font['ctg']))) {
9068 // Embed CIDToGIDMap
9069 // A specification of the mapping from CIDs to glyph indices
9070 // search and get CTG font file to embedd
9071 $ctgfile = strtolower($font['ctg']);
9072 // search and get ctg font file to embedd
9073 $fontfile = TCPDF_FONTS
::getFontFullPath($ctgfile, $fontdir);
9074 if (TCPDF_STATIC
::empty_string($fontfile)) {
9075 $this->Error('Font file not found: '.$ctgfile);
9077 $stream = $this->_getrawstream(file_get_contents($fontfile));
9078 $out = '<< /Length '.strlen($stream).'';
9079 if (substr($fontfile, -2) == '.z') { // check file extension
9080 // Decompresses data encoded using the public-domain
9081 // zlib/deflate compression method, reproducing the
9082 // original text or binary data
9083 $out .= ' /Filter /FlateDecode';
9086 $out .= ' stream'."\n".$stream."\n".'endstream';
9087 $out .= "\n".'endobj';
9093 * Output CID-0 fonts.
9094 * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format
9095 * @param $font (array) font data
9097 * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
9098 * @since 3.2.000 (2008-06-23)
9100 protected function _putcidfont0($font) {
9102 if (!isset($font['cw'][1])) {
9105 if (isset($font['cidinfo']['uni2cid'])) {
9106 // convert unicode to cid.
9107 $uni2cid = $font['cidinfo']['uni2cid'];
9109 foreach ($font['cw'] as $uni => $width) {
9110 if (isset($uni2cid[$uni])) {
9111 $cw[($uni2cid[$uni] +
$cidoffset)] = $width;
9112 } elseif ($uni < 256) {
9114 } // else unknown character
9116 $font = array_merge($font, array('cw' => $cw));
9118 $name = $font['name'];
9119 $enc = $font['enc'];
9121 $longname = $name.'-'.$enc;
9125 $out = $this->_getobj($this->font_obj_ids
[$font['fontkey']])."\n";
9126 $out .= '<</Type /Font';
9127 $out .= ' /Subtype /Type0';
9128 $out .= ' /BaseFont /'.$longname;
9129 $out .= ' /Name /F'.$font['i'];
9131 $out .= ' /Encoding /'.$enc;
9133 $out .= ' /DescendantFonts ['.($this->n +
1).' 0 R]';
9135 $out .= "\n".'endobj';
9137 $oid = $this->_newobj();
9138 $out = '<</Type /Font';
9139 $out .= ' /Subtype /CIDFontType0';
9140 $out .= ' /BaseFont /'.$name;
9141 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9142 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9143 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9144 $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
9145 $out .= ' /FontDescriptor '.($this->n +
1).' 0 R';
9146 $out .= ' /DW '.$font['dw'];
9147 $out .= "\n".TCPDF_FONTS
::_putfontwidths($font, $cidoffset);
9149 $out .= "\n".'endobj';
9152 $s = '<</Type /FontDescriptor /FontName /'.$name;
9153 foreach ($font['desc'] as $k => $v) {
9154 if ($k != 'Style') {
9156 $v = sprintf('%F', $v);
9158 $s .= ' /'.$k.' '.$v.'';
9162 $s .= "\n".'endobj';
9170 protected function _putimages() {
9171 $filter = ($this->compress
) ? '/Filter /FlateDecode ' : '';
9172 foreach ($this->imagekeys
as $file) {
9173 $info = $this->getImageBuffer($file);
9174 // set object for alternate images array
9175 if ((!$this->pdfa_mode
) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
9176 $altoid = $this->_newobj();
9178 foreach ($info['altimgs'] as $altimage) {
9179 if (isset($this->xobjects
['I'.$altimage[0]]['n'])) {
9180 $out .= ' << /Image '.$this->xobjects
['I'.$altimage[0]]['n'].' 0 R';
9181 $out .= ' /DefaultForPrinting';
9182 if ($altimage[1] === true) {
9191 $out .= "\n".'endobj';
9195 $oid = $this->_newobj();
9196 $this->xobjects
['I'.$info['i']] = array('n' => $oid);
9197 $this->setImageSubBuffer($file, 'n', $this->n
);
9198 $out = '<</Type /XObject';
9199 $out .= ' /Subtype /Image';
9200 $out .= ' /Width '.$info['w'];
9201 $out .= ' /Height '.$info['h'];
9202 if (array_key_exists('masked', $info)) {
9203 $out .= ' /SMask '.($this->n
- 1).' 0 R';
9207 if (isset($info['icc']) AND ($info['icc'] !== false)) {
9210 $out .= ' /ColorSpace [/ICCBased '.($this->n +
1).' 0 R]';
9211 } elseif ($info['cs'] == 'Indexed') {
9212 // Indexed Colour Space
9213 $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n +
1).' 0 R]';
9215 // Device Colour Space
9216 $out .= ' /ColorSpace /'.$info['cs'];
9218 if ($info['cs'] == 'DeviceCMYK') {
9219 $out .= ' /Decode [1 0 1 0 1 0 1 0]';
9221 $out .= ' /BitsPerComponent '.$info['bpc'];
9222 if (isset($altoid) AND ($altoid > 0)) {
9223 // reference to alternate images dictionary
9224 $out .= ' /Alternates '.$altoid.' 0 R';
9226 if (isset($info['exurl']) AND !empty($info['exurl'])) {
9228 $out .= ' /Length 0';
9229 $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
9230 if (isset($info['f'])) {
9231 $out .= ' /FFilter /'.$info['f'];
9234 $out .= ' stream'."\n".'endstream';
9236 if (isset($info['f'])) {
9237 $out .= ' /Filter /'.$info['f'];
9239 if (isset($info['parms'])) {
9240 $out .= ' '.$info['parms'];
9242 if (isset($info['trns']) AND is_array($info['trns'])) {
9244 $count_info = count($info['trns']);
9245 if ($info['cs'] == 'Indexed') {
9246 $maxval =(pow(2, $info['bpc']) - 1);
9247 for ($i = 0; $i < $count_info; ++
$i) {
9248 if (($info['trns'][$i] != 0) AND ($info['trns'][$i] != $maxval)) {
9249 // this is not a binary type mask @TODO: create a SMask
9252 } elseif (empty($trns) AND ($info['trns'][$i] == 0)) {
9253 // store the first fully transparent value
9254 $trns .= $i.' '.$i.' ';
9259 for ($i = 0; $i < $count_info; ++
$i) {
9260 if ($info['trns'][$i] == 0) {
9261 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
9265 // Colour Key Masking
9266 if (!empty($trns)) {
9267 $out .= ' /Mask ['.$trns.']';
9270 $stream = $this->_getrawstream($info['data']);
9271 $out .= ' /Length '.strlen($stream).' >>';
9272 $out .= ' stream'."\n".$stream."\n".'endstream';
9274 $out .= "\n".'endobj';
9277 // ICC colour profile
9279 $icc = ($this->compress
) ? gzcompress($info['icc']) : $info['icc'];
9280 $icc = $this->_getrawstream($icc);
9281 $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9282 } elseif ($info['cs'] == 'Indexed') {
9285 $pal = ($this->compress
) ? gzcompress($info['pal']) : $info['pal'];
9286 $pal = $this->_getrawstream($pal);
9287 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
9293 * Output Form XObjects Templates.
9294 * @author Nicola Asuni
9295 * @since 5.8.017 (2010-08-24)
9297 * @see startTemplate(), endTemplate(), printTemplate()
9299 protected function _putxobjects() {
9300 foreach ($this->xobjects
as $key => $data) {
9301 if (isset($data['outdata'])) {
9302 $stream = str_replace($this->epsmarker
, '', trim($data['outdata']));
9303 $out = $this->_getobj($data['n'])."\n";
9305 $out .= ' /Type /XObject';
9306 $out .= ' /Subtype /Form';
9307 $out .= ' /FormType 1';
9308 if ($this->compress
) {
9309 $stream = gzcompress($stream);
9310 $out .= ' /Filter /FlateDecode';
9312 $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
));
9313 $out .= ' /Matrix [1 0 0 1 0 0]';
9314 $out .= ' /Resources <<';
9315 $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9316 if (!$this->pdfa_mode
) {
9318 if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
9319 $out .= ' /ExtGState <<';
9320 foreach ($data['extgstates'] as $k => $extgstate) {
9321 if (isset($this->extgstates
[$k]['name'])) {
9322 $out .= ' /'.$this->extgstates
[$k]['name'];
9326 $out .= ' '.$this->extgstates
[$k]['n'].' 0 R';
9330 if (isset($data['gradients']) AND !empty($data['gradients'])) {
9333 foreach ($data['gradients'] as $id => $grad) {
9334 // gradient patterns
9335 $gp .= ' /p'.$id.' '.$this->gradients
[$id]['pattern'].' 0 R';
9336 // gradient shadings
9337 $gs .= ' /Sh'.$id.' '.$this->gradients
[$id]['id'].' 0 R';
9339 $out .= ' /Pattern <<'.$gp.' >>';
9340 $out .= ' /Shading <<'.$gs.' >>';
9344 if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
9345 $out .= ' /ColorSpace <<';
9346 foreach ($data['spot_colors'] as $name => $color) {
9347 $out .= ' /CS'.$color['i'].' '.$this->spot_colors
[$name]['n'].' 0 R';
9352 if (!empty($data['fonts'])) {
9353 $out .= ' /Font <<';
9354 foreach ($data['fonts'] as $fontkey => $fontid) {
9355 $out .= ' /F'.$fontid.' '.$this->font_obj_ids
[$fontkey].' 0 R';
9359 // images or nested xobjects
9360 if (!empty($data['images']) OR !empty($data['xobjects'])) {
9361 $out .= ' /XObject <<';
9362 foreach ($data['images'] as $imgid) {
9363 $out .= ' /I'.$imgid.' '.$this->xobjects
['I'.$imgid]['n'].' 0 R';
9365 foreach ($data['xobjects'] as $sub_id => $sub_objid) {
9366 $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
9370 $out .= ' >>'; //end resources
9371 if (isset($data['group']) AND ($data['group'] !== false)) {
9372 // set transparency group
9373 $out .= ' /Group << /Type /Group /S /Transparency';
9374 if (is_array($data['group'])) {
9375 if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
9376 $out .= ' /CS /'.$data['group']['CS'];
9378 if (isset($data['group']['I'])) {
9379 $out .= ' /I /'.($data['group']['I']===true?'true':'false');
9381 if (isset($data['group']['K'])) {
9382 $out .= ' /K /'.($data['group']['K']===true?'true':'false');
9387 $stream = $this->_getrawstream($stream, $data['n']);
9388 $out .= ' /Length '.strlen($stream);
9390 $out .= ' stream'."\n".$stream."\n".'endstream';
9391 $out .= "\n".'endobj';
9398 * Output Spot Colors Resources.
9400 * @since 4.0.024 (2008-09-12)
9402 protected function _putspotcolors() {
9403 foreach ($this->spot_colors
as $name => $color) {
9405 $this->spot_colors
[$name]['n'] = $this->n
;
9406 $out = '[/Separation /'.str_replace(' ', '#20', $name);
9407 $out .= ' /DeviceCMYK <<';
9408 $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
9409 $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
9410 $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
9411 $out .= "\n".'endobj';
9417 * Return XObjects Dictionary.
9418 * @return string XObjects dictionary
9420 * @since 5.8.014 (2010-08-23)
9422 protected function _getxobjectdict() {
9424 foreach ($this->xobjects
as $id => $objid) {
9425 $out .= ' /'.$id.' '.$objid['n'].' 0 R';
9431 * Output Resources Dictionary.
9434 protected function _putresourcedict() {
9435 $out = $this->_getobj(2)."\n";
9436 $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9437 $out .= ' /Font <<';
9438 foreach ($this->fontkeys
as $fontkey) {
9439 $font = $this->getFontBuffer($fontkey);
9440 $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
9443 $out .= ' /XObject <<';
9444 $out .= $this->_getxobjectdict();
9447 if (!empty($this->pdflayers
)) {
9448 $out .= ' /Properties <<';
9449 foreach ($this->pdflayers
as $layer) {
9450 $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
9454 if (!$this->pdfa_mode
) {
9456 if (isset($this->extgstates
) AND !empty($this->extgstates
)) {
9457 $out .= ' /ExtGState <<';
9458 foreach ($this->extgstates
as $k => $extgstate) {
9459 if (isset($extgstate['name'])) {
9460 $out .= ' /'.$extgstate['name'];
9464 $out .= ' '.$extgstate['n'].' 0 R';
9468 if (isset($this->gradients
) AND !empty($this->gradients
)) {
9471 foreach ($this->gradients
as $id => $grad) {
9472 // gradient patterns
9473 $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
9474 // gradient shadings
9475 $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
9477 $out .= ' /Pattern <<'.$gp.' >>';
9478 $out .= ' /Shading <<'.$gs.' >>';
9482 if (isset($this->spot_colors
) AND !empty($this->spot_colors
)) {
9483 $out .= ' /ColorSpace <<';
9484 foreach ($this->spot_colors
as $color) {
9485 $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
9490 $out .= "\n".'endobj';
9498 protected function _putresources() {
9499 $this->_putextgstates();
9502 $this->_putimages();
9503 $this->_putspotcolors();
9504 $this->_putshaders();
9505 $this->_putxobjects();
9506 $this->_putresourcedict();
9508 $this->_putEmbeddedFiles();
9509 $this->_putannotsobjs();
9510 $this->_putjavascript();
9511 $this->_putbookmarks();
9512 $this->_putencryption();
9516 * Adds some Metadata information (Document Information Dictionary)
9517 * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference)
9518 * @return int object id
9521 protected function _putinfo() {
9522 $oid = $this->_newobj();
9524 // store current isunicode value
9525 $prev_isunicode = $this->isunicode
;
9526 if ($this->docinfounicode
) {
9527 $this->isunicode
= true;
9529 if (!TCPDF_STATIC
::empty_string($this->title
)) {
9530 // The document's title.
9531 $out .= ' /Title '.$this->_textstring($this->title
, $oid);
9533 if (!TCPDF_STATIC
::empty_string($this->author
)) {
9534 // The name of the person who created the document.
9535 $out .= ' /Author '.$this->_textstring($this->author
, $oid);
9537 if (!TCPDF_STATIC
::empty_string($this->subject
)) {
9538 // The subject of the document.
9539 $out .= ' /Subject '.$this->_textstring($this->subject
, $oid);
9541 if (!TCPDF_STATIC
::empty_string($this->keywords
)) {
9542 // Keywords associated with the document.
9543 $out .= ' /Keywords '.$this->_textstring($this->keywords
, $oid);
9545 if (!TCPDF_STATIC
::empty_string($this->creator
)) {
9546 // 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.
9547 $out .= ' /Creator '.$this->_textstring($this->creator
, $oid);
9549 // restore previous isunicode value
9550 $this->isunicode
= $prev_isunicode;
9552 $out .= ' /Producer '.$this->_textstring(TCPDF_STATIC
::getTCPDFProducer(), $oid);
9553 // The date and time the document was created, in human-readable form
9554 $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp
);
9555 // The date and time the document was most recently modified, in human-readable form
9556 $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp
);
9557 // A name object indicating whether the document has been modified to include trapping information
9558 $out .= ' /Trapped /False';
9560 $out .= "\n".'endobj';
9566 * Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag.
9567 * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method!
9568 * @param $xmp (string) Custom XMP data.
9569 * @since 5.9.128 (2011-10-06)
9572 public function setExtraXMP($xmp) {
9573 $this->custom_xmp
= $xmp;
9577 * Put XMP data object and return ID.
9578 * @return (int) The object ID.
9579 * @since 5.9.121 (2011-09-28)
9582 protected function _putXMP() {
9583 $oid = $this->_newobj();
9584 // store current isunicode value
9585 $prev_isunicode = $this->isunicode
;
9586 $this->isunicode
= true;
9587 $prev_encrypted = $this->encrypted
;
9588 $this->encrypted
= false;
9590 $xmp = '<?xpacket begin="'.TCPDF_FONTS
::unichr(0xfeff, $this->isunicode
).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
9591 $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";
9592 $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
9593 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
9594 $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
9595 $xmp .= "\t\t\t".'<dc:title>'."\n";
9596 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9597 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC
::_escapeXML($this->title
).'</rdf:li>'."\n";
9598 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9599 $xmp .= "\t\t\t".'</dc:title>'."\n";
9600 $xmp .= "\t\t\t".'<dc:creator>'."\n";
9601 $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
9602 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC
::_escapeXML($this->author
).'</rdf:li>'."\n";
9603 $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
9604 $xmp .= "\t\t\t".'</dc:creator>'."\n";
9605 $xmp .= "\t\t\t".'<dc:description>'."\n";
9606 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9607 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC
::_escapeXML($this->subject
).'</rdf:li>'."\n";
9608 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9609 $xmp .= "\t\t\t".'</dc:description>'."\n";
9610 $xmp .= "\t\t\t".'<dc:subject>'."\n";
9611 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9612 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC
::_escapeXML($this->keywords
).'</rdf:li>'."\n";
9613 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9614 $xmp .= "\t\t\t".'</dc:subject>'."\n";
9615 $xmp .= "\t\t".'</rdf:Description>'."\n";
9616 // convert doc creation date format
9617 $dcdate = TCPDF_STATIC
::getFormattedDate($this->doc_creation_timestamp
);
9618 $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2);
9619 $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2);
9620 $doccreationdate .= '+'.substr($dcdate, 15, 2).':'.substr($dcdate, 18, 2);
9621 $doccreationdate = TCPDF_STATIC
::_escapeXML($doccreationdate);
9622 // convert doc modification date format
9623 $dmdate = TCPDF_STATIC
::getFormattedDate($this->doc_modification_timestamp
);
9624 $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2);
9625 $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2);
9626 $docmoddate .= '+'.substr($dmdate, 15, 2).':'.substr($dmdate, 18, 2);
9627 $docmoddate = TCPDF_STATIC
::_escapeXML($docmoddate);
9628 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
9629 $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n";
9630 $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator
.'</xmp:CreatorTool>'."\n";
9631 $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n";
9632 $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n";
9633 $xmp .= "\t\t".'</rdf:Description>'."\n";
9634 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
9635 $xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC
::_escapeXML($this->keywords
).'</pdf:Keywords>'."\n";
9636 $xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC
::_escapeXML(TCPDF_STATIC
::getTCPDFProducer()).'</pdf:Producer>'."\n";
9637 $xmp .= "\t\t".'</rdf:Description>'."\n";
9638 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
9639 $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);
9640 $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
9641 $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
9642 $xmp .= "\t\t".'</rdf:Description>'."\n";
9643 if ($this->pdfa_mode
) {
9644 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
9645 $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n";
9646 $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
9647 $xmp .= "\t\t".'</rdf:Description>'."\n";
9649 // XMP extension schemas
9650 $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";
9651 $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
9652 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9653 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9654 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
9655 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
9656 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
9657 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9658 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9659 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
9660 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
9661 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
9662 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9663 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9664 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9665 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9666 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
9667 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9668 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9669 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9670 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9671 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9672 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9673 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9674 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
9675 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
9676 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
9677 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9678 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\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>Part of PDF/A standard</pdfaProperty:description>'."\n";
9682 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
9683 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</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>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
9688 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</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\t".'<rdf:li rdf:parseType="Resource">'."\n";
9692 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9693 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
9694 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
9695 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9696 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9697 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9698 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9699 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9700 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9701 $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
9702 $xmp .= "\t\t".'</rdf:Description>'."\n";
9703 $xmp .= "\t".'</rdf:RDF>'."\n";
9704 $xmp .= $this->custom_xmp
;
9705 $xmp .= '</x:xmpmeta>'."\n";
9706 $xmp .= '<?xpacket end="w"?>';
9707 $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
9708 // restore previous isunicode value
9709 $this->isunicode
= $prev_isunicode;
9710 $this->encrypted
= $prev_encrypted;
9717 * @return int object id
9720 protected function _putcatalog() {
9722 $xmpobj = $this->_putXMP();
9723 // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile
9724 if ($this->pdfa_mode
OR $this->force_srgb
) {
9725 $iccobj = $this->_newobj();
9726 $icc = file_get_contents(dirname(__FILE__
).'/include/sRGB.icc');
9728 if ($this->compress
) {
9729 $filter = ' /Filter /FlateDecode';
9730 $icc = gzcompress($icc);
9732 $icc = $this->_getrawstream($icc);
9733 $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9736 $oid = $this->_newobj();
9737 $out = '<< /Type /Catalog';
9738 $out .= ' /Version /'.$this->PDFVersion
;
9739 //$out .= ' /Extensions <<>>';
9740 $out .= ' /Pages 1 0 R';
9741 //$out .= ' /PageLabels ' //...;
9742 $out .= ' /Names <<';
9743 if ((!$this->pdfa_mode
) AND !empty($this->n_js
)) {
9744 $out .= ' /JavaScript '.$this->n_js
;
9746 if (!empty($this->efnames
)) {
9747 $out .= ' /EmbeddedFiles <</Names [';
9748 foreach ($this->efnames
AS $fn => $fref) {
9749 $out .= ' '.$this->_datastring($fn).' '.$fref;
9754 if (!empty($this->dests
)) {
9755 $out .= ' /Dests '.($this->n_dests
).' 0 R';
9757 $out .= $this->_putviewerpreferences();
9758 if (isset($this->LayoutMode
) AND (!TCPDF_STATIC
::empty_string($this->LayoutMode
))) {
9759 $out .= ' /PageLayout /'.$this->LayoutMode
;
9761 if (isset($this->PageMode
) AND (!TCPDF_STATIC
::empty_string($this->PageMode
))) {
9762 $out .= ' /PageMode /'.$this->PageMode
;
9764 if (count($this->outlines
) > 0) {
9765 $out .= ' /Outlines '.$this->OutlineRoot
.' 0 R';
9766 $out .= ' /PageMode /UseOutlines';
9768 //$out .= ' /Threads []';
9769 if ($this->ZoomMode
== 'fullpage') {
9770 $out .= ' /OpenAction ['.$this->page_obj_id
[1].' 0 R /Fit]';
9771 } elseif ($this->ZoomMode
== 'fullwidth') {
9772 $out .= ' /OpenAction ['.$this->page_obj_id
[1].' 0 R /FitH null]';
9773 } elseif ($this->ZoomMode
== 'real') {
9774 $out .= ' /OpenAction ['.$this->page_obj_id
[1].' 0 R /XYZ null null 1]';
9775 } elseif (!is_string($this->ZoomMode
)) {
9776 $out .= sprintf(' /OpenAction ['.$this->page_obj_id
[1].' 0 R /XYZ null null %F]', ($this->ZoomMode
/ 100));
9778 //$out .= ' /AA <<>>';
9779 //$out .= ' /URI <<>>';
9780 $out .= ' /Metadata '.$xmpobj.' 0 R';
9781 //$out .= ' /StructTreeRoot <<>>';
9782 //$out .= ' /MarkInfo <<>>';
9783 if (isset($this->l
['a_meta_language'])) {
9784 $out .= ' /Lang '.$this->_textstring($this->l
['a_meta_language'], $oid);
9786 //$out .= ' /SpiderInfo <<>>';
9787 // set OutputIntent to sRGB IEC61966-2.1 if required
9788 if ($this->pdfa_mode
OR $this->force_srgb
) {
9789 $out .= ' /OutputIntents [<<';
9790 $out .= ' /Type /OutputIntent';
9791 $out .= ' /S /GTS_PDFA1';
9792 $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9793 $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9794 $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
9795 $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9796 $out .= ' /DestOutputProfile '.$iccobj.' 0 R';
9799 //$out .= ' /PieceInfo <<>>';
9800 if (!empty($this->pdflayers
)) {
9804 foreach ($this->pdflayers
as $layer) {
9805 $layer_obj_ref = ' '.$layer['objid'].' 0 R';
9806 $lyrobjs .= $layer_obj_ref;
9807 if ($layer['view'] === false) {
9808 $lyrobjs_off .= $layer_obj_ref;
9810 if ($layer['lock']) {
9811 $lyrobjs_lock .= $layer_obj_ref;
9814 $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
9816 $out .= ' /Name '.$this->_textstring('Layers', $oid);
9817 $out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
9818 $out .= ' /BaseState /ON';
9819 $out .= ' /OFF ['.$lyrobjs_off.']';
9820 $out .= ' /Locked ['.$lyrobjs_lock.']';
9821 $out .= ' /Intent /View';
9823 $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
9824 $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
9826 $out .= ' /Order ['.$lyrobjs.']';
9827 $out .= ' /ListMode /AllPages';
9828 //$out .= ' /RBGroups ['..']';
9829 //$out .= ' /Locked ['..']';
9834 if (!empty($this->form_obj_id
)
9835 OR ($this->sign
AND isset($this->signature_data
['cert_type']))
9836 OR !empty($this->empty_signature_appearance
)) {
9837 $out .= ' /AcroForm <<';
9839 if ($this->sign
AND isset($this->signature_data
['cert_type'])) {
9840 // set reference for signature object
9841 $objrefs .= $this->sig_obj_id
.' 0 R';
9843 if (!empty($this->empty_signature_appearance
)) {
9844 foreach ($this->empty_signature_appearance
as $esa) {
9845 // set reference for empty signature objects
9846 $objrefs .= ' '.$esa['objid'].' 0 R';
9849 if (!empty($this->form_obj_id
)) {
9850 foreach($this->form_obj_id
as $objid) {
9851 $objrefs .= ' '.$objid.' 0 R';
9854 $out .= ' /Fields ['.$objrefs.']';
9855 // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
9856 if (empty($this->signature_data
['approval']) OR ($this->signature_data
['approval'] != 'A')) {
9857 $out .= ' /NeedAppearances false';
9859 if ($this->sign
AND isset($this->signature_data
['cert_type'])) {
9860 if ($this->signature_data
['cert_type'] > 0) {
9861 $out .= ' /SigFlags 3';
9863 $out .= ' /SigFlags 1';
9867 if (isset($this->annotation_fonts
) AND !empty($this->annotation_fonts
)) {
9869 $out .= ' /Font <<';
9870 foreach ($this->annotation_fonts
as $fontkey => $fontid) {
9871 $out .= ' /F'.$fontid.' '.$this->font_obj_ids
[$fontkey].' 0 R';
9875 $font = $this->getFontBuffer('helvetica');
9876 $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
9877 $out .= ' /Q '.(($this->rtl
)?'2':'0');
9881 if ($this->sign
AND isset($this->signature_data
['cert_type'])
9882 AND (empty($this->signature_data
['approval']) OR ($this->signature_data
['approval'] != 'A'))) {
9883 if ($this->signature_data
['cert_type'] > 0) {
9884 $out .= ' /Perms << /DocMDP '.($this->sig_obj_id +
1).' 0 R >>';
9886 $out .= ' /Perms << /UR3 '.($this->sig_obj_id +
1).' 0 R >>';
9890 //$out .= ' /Legal <<>>';
9891 //$out .= ' /Requirements []';
9892 //$out .= ' /Collection <<>>';
9893 //$out .= ' /NeedsRendering true';
9895 $out .= "\n".'endobj';
9901 * Output viewer preferences.
9902 * @return string for viewer preferences
9903 * @author Nicola asuni
9904 * @since 3.1.000 (2008-06-09)
9907 protected function _putviewerpreferences() {
9908 $vp = $this->viewer_preferences
;
9909 $out = ' /ViewerPreferences <<';
9911 $out .= ' /Direction /R2L';
9913 $out .= ' /Direction /L2R';
9915 if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) {
9916 $out .= ' /HideToolbar true';
9918 if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) {
9919 $out .= ' /HideMenubar true';
9921 if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) {
9922 $out .= ' /HideWindowUI true';
9924 if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) {
9925 $out .= ' /FitWindow true';
9927 if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) {
9928 $out .= ' /CenterWindow true';
9930 if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) {
9931 $out .= ' /DisplayDocTitle true';
9933 if (isset($vp['NonFullScreenPageMode'])) {
9934 $out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode'];
9936 if (isset($vp['ViewArea'])) {
9937 $out .= ' /ViewArea /'.$vp['ViewArea'];
9939 if (isset($vp['ViewClip'])) {
9940 $out .= ' /ViewClip /'.$vp['ViewClip'];
9942 if (isset($vp['PrintArea'])) {
9943 $out .= ' /PrintArea /'.$vp['PrintArea'];
9945 if (isset($vp['PrintClip'])) {
9946 $out .= ' /PrintClip /'.$vp['PrintClip'];
9948 if (isset($vp['PrintScaling'])) {
9949 $out .= ' /PrintScaling /'.$vp['PrintScaling'];
9951 if (isset($vp['Duplex']) AND (!TCPDF_STATIC
::empty_string($vp['Duplex']))) {
9952 $out .= ' /Duplex /'.$vp['Duplex'];
9954 if (isset($vp['PickTrayByPDFSize'])) {
9955 if ($vp['PickTrayByPDFSize']) {
9956 $out .= ' /PickTrayByPDFSize true';
9958 $out .= ' /PickTrayByPDFSize false';
9961 if (isset($vp['PrintPageRange'])) {
9962 $PrintPageRangeNum = '';
9963 foreach ($vp['PrintPageRange'] as $k => $v) {
9964 $PrintPageRangeNum .= ' '.($v - 1).'';
9966 $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
9968 if (isset($vp['NumCopies'])) {
9969 $out .= ' /NumCopies '.intval($vp['NumCopies']);
9976 * Output PDF File Header (7.5.2).
9979 protected function _putheader() {
9980 $this->_out('%PDF-'.$this->PDFVersion
);
9981 $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
9985 * Output end of document (EOF).
9988 protected function _enddoc() {
9989 if (isset($this->CurrentFont
['fontkey']) AND isset($this->CurrentFont
['subsetchars'])) {
9990 // save subset chars of the previous font
9991 $this->setFontSubBuffer($this->CurrentFont
['fontkey'], 'subsetchars', $this->CurrentFont
['subsetchars']);
9994 $this->_putheader();
9996 $this->_putresources();
9997 // empty signature fields
9998 if (!empty($this->empty_signature_appearance
)) {
9999 foreach ($this->empty_signature_appearance
as $key => $esa) {
10000 // widget annotation for empty signature
10001 $out = $this->_getobj($esa['objid'])."\n";
10002 $out .= '<< /Type /Annot';
10003 $out .= ' /Subtype /Widget';
10004 $out .= ' /Rect ['.$esa['rect'].']';
10005 $out .= ' /P '.$this->page_obj_id
[($esa['page'])].' 0 R'; // link to signature appearance page
10007 $out .= ' /FT /Sig';
10008 $signame = $esa['name'].sprintf(' [%03d]', ($key +
1));
10009 $out .= ' /T '.$this->_textstring($signame, $esa['objid']);
10012 $out .= "\n".'endobj';
10017 if ($this->sign
AND isset($this->signature_data
['cert_type'])) {
10018 // widget annotation for signature
10019 $out = $this->_getobj($this->sig_obj_id
)."\n";
10020 $out .= '<< /Type /Annot';
10021 $out .= ' /Subtype /Widget';
10022 $out .= ' /Rect ['.$this->signature_appearance
['rect'].']';
10023 $out .= ' /P '.$this->page_obj_id
[($this->signature_appearance
['page'])].' 0 R'; // link to signature appearance page
10025 $out .= ' /FT /Sig';
10026 $out .= ' /T '.$this->_textstring($this->signature_appearance
['name'], $this->sig_obj_id
);
10028 $out .= ' /V '.($this->sig_obj_id +
1).' 0 R';
10030 $out .= "\n".'endobj';
10033 $this->_putsignature();
10036 $objid_info = $this->_putinfo();
10038 $objid_catalog = $this->_putcatalog();
10040 $o = $this->bufferlen
;
10042 $this->_out('xref');
10043 $this->_out('0 '.($this->n +
1));
10044 $this->_out('0000000000 65535 f ');
10045 $freegen = ($this->n +
2);
10046 for ($i=1; $i <= $this->n
; ++
$i) {
10047 if (!isset($this->offsets
[$i]) AND ($i > 1)) {
10048 $this->_out(sprintf('0000000000 %05d f ', $freegen));
10051 $this->_out(sprintf('%010d 00000 n ', $this->offsets
[$i]));
10055 $out = 'trailer'."\n";
10057 $out .= ' /Size '.($this->n +
1);
10058 $out .= ' /Root '.$objid_catalog.' 0 R';
10059 $out .= ' /Info '.$objid_info.' 0 R';
10060 if ($this->encrypted
) {
10061 $out .= ' /Encrypt '.$this->encryptdata
['objid'].' 0 R';
10063 $out .= ' /ID [ <'.$this->file_id
.'> <'.$this->file_id
.'> ]';
10066 $this->_out('startxref');
10068 $this->_out('%%EOF');
10069 $this->state
= 3; // end-of-doc
10070 if ($this->diskcache
) {
10071 // remove temporary files used for images
10072 foreach ($this->imagekeys
as $key) {
10073 // remove temporary files
10074 unlink($this->images
[$key]);
10076 foreach ($this->fontkeys
as $key) {
10077 // remove temporary files
10078 unlink($this->fonts
[$key]);
10084 * Initialize a new page.
10085 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
10086 * @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().
10088 * @see getPageSizeFromFormat(), setPageFormat()
10090 protected function _beginpage($orientation='', $format='') {
10092 $this->pageobjects
[$this->page
] = array();
10093 $this->setPageBuffer($this->page
, '');
10094 // initialize array for graphics tranformation positions inside a page buffer
10095 $this->transfmrk
[$this->page
] = array();
10097 if (TCPDF_STATIC
::empty_string($orientation)) {
10098 if (isset($this->CurOrientation
)) {
10099 $orientation = $this->CurOrientation
;
10100 } elseif ($this->fwPt
> $this->fhPt
) {
10102 $orientation = 'L';
10105 $orientation = 'P';
10108 if (TCPDF_STATIC
::empty_string($format)) {
10109 $this->pagedim
[$this->page
] = $this->pagedim
[($this->page
- 1)];
10110 $this->setPageOrientation($orientation);
10112 $this->setPageFormat($format, $orientation);
10115 $this->x
= $this->w
- $this->rMargin
;
10117 $this->x
= $this->lMargin
;
10119 $this->y
= $this->tMargin
;
10120 if (isset($this->newpagegroup
[$this->page
])) {
10121 // start a new group
10122 $this->currpagegroup
= $this->newpagegroup
[$this->page
];
10123 $this->pagegroups
[$this->currpagegroup
] = 1;
10124 } elseif (isset($this->currpagegroup
) AND ($this->currpagegroup
> 0)) {
10125 ++
$this->pagegroups
[$this->currpagegroup
];
10130 * Mark end of page.
10133 protected function _endpage() {
10134 $this->setVisibility('all');
10139 * Begin a new object and return the object number.
10140 * @return int object number
10143 protected function _newobj() {
10144 $this->_out($this->_getobj());
10149 * Return the starting object string for the selected object ID.
10150 * @param $objid (int) Object ID (leave empty to get a new ID).
10151 * @return string the starting object string
10153 * @since 5.8.009 (2010-08-20)
10155 protected function _getobj($objid='') {
10156 if ($objid === '') {
10160 $this->offsets
[$objid] = $this->bufferlen
;
10161 $this->pageobjects
[$this->page
][] = $objid;
10162 return $objid.' 0 obj';
10167 * @param $x (int) X coordinate
10168 * @param $y (int) Y coordinate
10169 * @param $txt (string) text to underline
10172 protected function _dounderline($x, $y, $txt) {
10173 $w = $this->GetStringWidth($txt);
10174 return $this->_dounderlinew($x, $y, $w);
10178 * Underline for rectangular text area.
10179 * @param $x (int) X coordinate
10180 * @param $y (int) Y coordinate
10181 * @param $w (int) width to underline
10183 * @since 4.8.008 (2009-09-29)
10185 protected function _dounderlinew($x, $y, $w) {
10186 $linew = - $this->CurrentFont
['ut'] / 1000 * $this->FontSizePt
;
10187 return sprintf('%F %F %F %F re f', $x * $this->k
, ((($this->h
- $y) * $this->k
) +
$linew), $w * $this->k
, $linew);
10191 * Line through text.
10192 * @param $x (int) X coordinate
10193 * @param $y (int) Y coordinate
10194 * @param $txt (string) text to linethrough
10197 protected function _dolinethrough($x, $y, $txt) {
10198 $w = $this->GetStringWidth($txt);
10199 return $this->_dolinethroughw($x, $y, $w);
10203 * Line through for rectangular text area.
10204 * @param $x (int) X coordinate
10205 * @param $y (int) Y coordinate
10206 * @param $w (int) line length (width)
10208 * @since 4.9.008 (2009-09-29)
10210 protected function _dolinethroughw($x, $y, $w) {
10211 $linew = - $this->CurrentFont
['ut'] / 1000 * $this->FontSizePt
;
10212 return sprintf('%F %F %F %F re f', $x * $this->k
, ((($this->h
- $y) * $this->k
) +
$linew +
($this->FontSizePt
/ 3)), $w * $this->k
, $linew);
10217 * @param $x (int) X coordinate
10218 * @param $y (int) Y coordinate
10219 * @param $txt (string) text to overline
10221 * @since 4.9.015 (2010-04-19)
10223 protected function _dooverline($x, $y, $txt) {
10224 $w = $this->GetStringWidth($txt);
10225 return $this->_dooverlinew($x, $y, $w);
10229 * Overline for rectangular text area.
10230 * @param $x (int) X coordinate
10231 * @param $y (int) Y coordinate
10232 * @param $w (int) width to overline
10234 * @since 4.9.015 (2010-04-19)
10236 protected function _dooverlinew($x, $y, $w) {
10237 $linew = - $this->CurrentFont
['ut'] / 1000 * $this->FontSizePt
;
10238 return sprintf('%F %F %F %F re f', $x * $this->k
, (($this->h
- $y +
$this->FontAscent
) * $this->k
) - $linew, $w * $this->k
, $linew);
10243 * Format a data string for meta information
10244 * @param $s (string) data string to escape.
10245 * @param $n (int) object ID
10246 * @return string escaped string.
10249 protected function _datastring($s, $n=0) {
10253 $s = $this->_encrypt_data($n, $s);
10254 return '('. TCPDF_STATIC
::_escape($s).')';
10258 * Set the document creation timestamp
10259 * @param $time (mixed) Document creation timestamp in seconds or date-time string.
10261 * @since 5.9.152 (2012-03-23)
10263 public function setDocCreationTimestamp($time) {
10264 if (is_string($time)) {
10265 $time = TCPDF_STATIC
::getTimestamp($time);
10267 $this->doc_creation_timestamp
= intval($time);
10271 * Set the document modification timestamp
10272 * @param $time (mixed) Document modification timestamp in seconds or date-time string.
10274 * @since 5.9.152 (2012-03-23)
10276 public function setDocModificationTimestamp($time) {
10277 if (is_string($time)) {
10278 $time = TCPDF_STATIC
::getTimestamp($time);
10280 $this->doc_modification_timestamp
= intval($time);
10284 * Returns document creation timestamp in seconds.
10285 * @return (int) Creation timestamp in seconds.
10287 * @since 5.9.152 (2012-03-23)
10289 public function getDocCreationTimestamp() {
10290 return $this->doc_creation_timestamp
;
10294 * Returns document modification timestamp in seconds.
10295 * @return (int) Modfication timestamp in seconds.
10297 * @since 5.9.152 (2012-03-23)
10299 public function getDocModificationTimestamp() {
10300 return $this->doc_modification_timestamp
;
10304 * Returns a formatted date for meta information
10305 * @param $n (int) Object ID.
10306 * @param $timestamp (int) Timestamp to convert.
10307 * @return string escaped date string.
10309 * @since 4.6.028 (2009-08-25)
10311 protected function _datestring($n=0, $timestamp=0) {
10312 if ((empty($timestamp)) OR ($timestamp < 0)) {
10313 $timestamp = $this->doc_creation_timestamp
;
10315 return $this->_datastring('D:'.TCPDF_STATIC
::getFormattedDate($timestamp), $n);
10319 * Format a text string for meta information
10320 * @param $s (string) string to escape.
10321 * @param $n (int) object ID
10322 * @return string escaped string.
10325 protected function _textstring($s, $n=0) {
10326 if ($this->isunicode
) {
10327 //Convert string to UTF-16BE
10328 $s = TCPDF_FONTS
::UTF8ToUTF16BE($s, true, $this->isunicode
, $this->CurrentFont
);
10330 return $this->_datastring($s, $n);
10334 * THIS METHOD IS DEPRECATED
10335 * Format a text string
10336 * @param $s (string) string to escape.
10337 * @return string escaped string.
10341 protected function _escapetext($s) {
10342 if ($this->isunicode
) {
10343 if (($this->CurrentFont
['type'] == 'core') OR ($this->CurrentFont
['type'] == 'TrueType') OR ($this->CurrentFont
['type'] == 'Type1')) {
10344 $s = TCPDF_FONTS
::UTF8ToLatin1($s, $this->isunicode
, $this->CurrentFont
);
10346 //Convert string to UTF-16BE and reverse RTL language
10347 $s = TCPDF_FONTS
::utf8StrRev($s, false, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
10350 return TCPDF_STATIC
::_escape($s);
10354 * get raw output stream.
10355 * @param $s (string) string to output.
10356 * @param $n (int) object reference for encryption mode
10358 * @author Nicola Asuni
10359 * @since 5.5.000 (2010-06-22)
10361 protected function _getrawstream($s, $n=0) {
10363 // default to current object
10366 return $this->_encrypt_data($n, $s);
10370 * Format output stream (DEPRECATED).
10371 * @param $s (string) string to output.
10372 * @param $n (int) object reference for encryption mode
10376 protected function _getstream($s, $n=0) {
10377 return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream';
10381 * Output a stream (DEPRECATED).
10382 * @param $s (string) string to output.
10383 * @param $n (int) object reference for encryption mode
10387 protected function _putstream($s, $n=0) {
10388 $this->_out($this->_getstream($s, $n));
10392 * Output a string to the document.
10393 * @param $s (string) string to output.
10396 protected function _out($s) {
10397 if ($this->state
== 2) {
10398 if ($this->inxobj
) {
10399 // we are inside an XObject template
10400 $this->xobjects
[$this->xobjid
]['outdata'] .= $s."\n";
10401 } elseif ((!$this->InFooter
) AND isset($this->footerlen
[$this->page
]) AND ($this->footerlen
[$this->page
] > 0)) {
10402 // puts data before page footer
10403 $pagebuff = $this->getPageBuffer($this->page
);
10404 $page = substr($pagebuff, 0, -$this->footerlen
[$this->page
]);
10405 $footer = substr($pagebuff, -$this->footerlen
[$this->page
]);
10406 $this->setPageBuffer($this->page
, $page.$s."\n".$footer);
10407 // update footer position
10408 $this->footerpos
[$this->page
] +
= strlen($s."\n");
10411 $this->setPageBuffer($this->page
, $s."\n", true);
10413 } elseif ($this->state
> 0) {
10414 // set general data
10415 $this->setBuffer($s."\n");
10421 * @param $font (array) Array describing the basic font parameters: (family, style, size).
10425 public function setHeaderFont($font) {
10426 $this->header_font
= $font;
10431 * @return array() Array describing the basic font parameters: (family, style, size).
10433 * @since 4.0.012 (2008-07-24)
10435 public function getHeaderFont() {
10436 return $this->header_font
;
10441 * @param $font (array) Array describing the basic font parameters: (family, style, size).
10445 public function setFooterFont($font) {
10446 $this->footer_font
= $font;
10451 * @return array() Array describing the basic font parameters: (family, style, size).
10453 * @since 4.0.012 (2008-07-24)
10455 public function getFooterFont() {
10456 return $this->footer_font
;
10460 * Set language array.
10461 * @param $language (array)
10465 public function setLanguageArray($language) {
10466 $this->l
= $language;
10467 if (isset($this->l
['a_meta_dir'])) {
10468 $this->rtl
= $this->l
['a_meta_dir']=='rtl' ? true : false;
10470 $this->rtl
= false;
10475 * Returns the PDF data.
10478 public function getPDFData() {
10479 if ($this->state
< 3) {
10482 return $this->buffer
;
10486 * Output anchor link.
10487 * @param $url (string) link URL or internal link (i.e.: <a href="#23,4.5">link to page 23 at 4.5 Y position</a>)
10488 * @param $name (string) link name
10489 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
10490 * @param $firstline (boolean) if true prints only the first line and return the remaining string.
10491 * @param $color (array) array of RGB text color
10492 * @param $style (string) font style (U, D, B, I)
10493 * @param $firstblock (boolean) if true the string is the starting of a line.
10494 * @return the number of cells used or the remaining text if $firstline = true;
10497 public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
10498 if (isset($url[1]) AND ($url[0] == '#')) {
10499 // convert url to internal link
10500 $lnkdata = explode(',', $url);
10501 if (isset($lnkdata[0]) ) {
10502 $page = substr($lnkdata[0], 1);
10503 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
10504 $lnky = floatval($lnkdata[1]);
10508 $url = $this->AddLink();
10509 $this->SetLink($url, $lnky, $page);
10512 // store current settings
10513 $prevcolor = $this->fgcolor
;
10514 $prevstyle = $this->FontStyle
;
10515 if (empty($color)) {
10516 $this->SetTextColorArray($this->htmlLinkColorArray
);
10518 $this->SetTextColorArray($color);
10520 if ($style == -1) {
10521 $this->SetFont('', $this->FontStyle
.$this->htmlLinkFontStyle
);
10523 $this->SetFont('', $this->FontStyle
.$style);
10525 $ret = $this->Write($this->lasth
, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
10526 // restore settings
10527 $this->SetFont('', $prevstyle);
10528 $this->SetTextColorArray($prevcolor);
10533 * Converts pixels to User's Units.
10534 * @param $px (int) pixels
10535 * @return float value in user's unit
10537 * @see setImageScale(), getImageScale()
10539 public function pixelsToUnits($px) {
10540 return ($px / ($this->imgscale
* $this->k
));
10544 * Reverse function for htmlentities.
10545 * Convert entities in UTF-8.
10546 * @param $text_to_convert (string) Text to convert.
10547 * @return string converted text string
10550 public function unhtmlentities($text_to_convert) {
10551 return @html_entity_decode($text_to_convert, ENT_QUOTES
, $this->encoding
);
10554 // ENCRYPTION METHODS ----------------------------------
10557 * Compute encryption key depending on object number where the encrypted data is stored.
10558 * This is used for all strings and streams without crypt filter specifier.
10559 * @param $n (int) object number
10560 * @return int object key
10562 * @author Nicola Asuni
10563 * @since 2.0.000 (2008-01-02)
10565 protected function _objectkey($n) {
10566 $objkey = $this->encryptdata
['key'].pack('VXxx', $n);
10567 if ($this->encryptdata
['mode'] == 2) { // AES-128
10569 $objkey .= "\x73\x41\x6C\x54"; // sAlT
10571 $objkey = substr(TCPDF_STATIC
::_md5_16($objkey), 0, (($this->encryptdata
['Length'] / 8) +
5));
10572 $objkey = substr($objkey, 0, 16);
10577 * Encrypt the input string.
10578 * @param $n (int) object number
10579 * @param $s (string) data string to encrypt
10580 * @return encrypted string
10582 * @author Nicola Asuni
10583 * @since 5.0.005 (2010-05-11)
10585 protected function _encrypt_data($n, $s) {
10586 if (!$this->encrypted
) {
10589 switch ($this->encryptdata
['mode']) {
10591 case 1: { // RC4-128
10592 $s = TCPDF_STATIC
::_RC4($this->_objectkey($n), $s, $this->last_enc_key
, $this->last_enc_key_c
);
10595 case 2: { // AES-128
10596 $s = TCPDF_STATIC
::_AES($this->_objectkey($n), $s);
10599 case 3: { // AES-256
10600 $s = TCPDF_STATIC
::_AES($this->encryptdata
['key'], $s);
10608 * Put encryption on PDF document.
10610 * @author Nicola Asuni
10611 * @since 2.0.000 (2008-01-02)
10613 protected function _putencryption() {
10614 if (!$this->encrypted
) {
10617 $this->encryptdata
['objid'] = $this->_newobj();
10619 if (!isset($this->encryptdata
['Filter']) OR empty($this->encryptdata
['Filter'])) {
10620 $this->encryptdata
['Filter'] = 'Standard';
10622 $out .= ' /Filter /'.$this->encryptdata
['Filter'];
10623 if (isset($this->encryptdata
['SubFilter']) AND !empty($this->encryptdata
['SubFilter'])) {
10624 $out .= ' /SubFilter /'.$this->encryptdata
['SubFilter'];
10626 if (!isset($this->encryptdata
['V']) OR empty($this->encryptdata
['V'])) {
10627 $this->encryptdata
['V'] = 1;
10629 // V is a code specifying the algorithm to be used in encrypting and decrypting the document
10630 $out .= ' /V '.$this->encryptdata
['V'];
10631 if (isset($this->encryptdata
['Length']) AND !empty($this->encryptdata
['Length'])) {
10632 // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
10633 $out .= ' /Length '.$this->encryptdata
['Length'];
10635 $out .= ' /Length 40';
10637 if ($this->encryptdata
['V'] >= 4) {
10638 if (!isset($this->encryptdata
['StmF']) OR empty($this->encryptdata
['StmF'])) {
10639 $this->encryptdata
['StmF'] = 'Identity';
10641 if (!isset($this->encryptdata
['StrF']) OR empty($this->encryptdata
['StrF'])) {
10642 // The name of the crypt filter that shall be used when decrypting all strings in the document.
10643 $this->encryptdata
['StrF'] = 'Identity';
10645 // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
10646 if (isset($this->encryptdata
['CF']) AND !empty($this->encryptdata
['CF'])) {
10648 $out .= ' /'.$this->encryptdata
['StmF'].' <<';
10649 $out .= ' /Type /CryptFilter';
10650 if (isset($this->encryptdata
['CF']['CFM']) AND !empty($this->encryptdata
['CF']['CFM'])) {
10652 $out .= ' /CFM /'.$this->encryptdata
['CF']['CFM'];
10653 if ($this->encryptdata
['pubkey']) {
10654 $out .= ' /Recipients [';
10655 foreach ($this->encryptdata
['Recipients'] as $rec) {
10656 $out .= ' <'.$rec.'>';
10659 if (isset($this->encryptdata
['CF']['EncryptMetadata']) AND (!$this->encryptdata
['CF']['EncryptMetadata'])) {
10660 $out .= ' /EncryptMetadata false';
10662 $out .= ' /EncryptMetadata true';
10666 $out .= ' /CFM /None';
10668 if (isset($this->encryptdata
['CF']['AuthEvent']) AND !empty($this->encryptdata
['CF']['AuthEvent'])) {
10669 // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
10670 $out .= ' /AuthEvent /'.$this->encryptdata
['CF']['AuthEvent'];
10672 $out .= ' /AuthEvent /DocOpen';
10674 if (isset($this->encryptdata
['CF']['Length']) AND !empty($this->encryptdata
['CF']['Length'])) {
10675 // The bit length of the encryption key.
10676 $out .= ' /Length '.$this->encryptdata
['CF']['Length'];
10680 // The name of the crypt filter that shall be used by default when decrypting streams.
10681 $out .= ' /StmF /'.$this->encryptdata
['StmF'];
10682 // The name of the crypt filter that shall be used when decrypting all strings in the document.
10683 $out .= ' /StrF /'.$this->encryptdata
['StrF'];
10684 if (isset($this->encryptdata
['EFF']) AND !empty($this->encryptdata
['EFF'])) {
10685 // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
10686 $out .= ' /EFF /'.$this->encryptdata
[''];
10689 // Additional encryption dictionary entries for the standard security handler
10690 if ($this->encryptdata
['pubkey']) {
10691 if (($this->encryptdata
['V'] < 4) AND isset($this->encryptdata
['Recipients']) AND !empty($this->encryptdata
['Recipients'])) {
10692 $out .= ' /Recipients [';
10693 foreach ($this->encryptdata
['Recipients'] as $rec) {
10694 $out .= ' <'.$rec.'>';
10700 if ($this->encryptdata
['V'] == 5) { // AES-256
10702 $out .= ' /OE ('.TCPDF_STATIC
::_escape($this->encryptdata
['OE']).')';
10703 $out .= ' /UE ('.TCPDF_STATIC
::_escape($this->encryptdata
['UE']).')';
10704 $out .= ' /Perms ('.TCPDF_STATIC
::_escape($this->encryptdata
['perms']).')';
10705 } elseif ($this->encryptdata
['V'] == 4) { // AES-128
10707 } elseif ($this->encryptdata
['V'] < 2) { // RC-40
10712 $out .= ' /O ('.TCPDF_STATIC
::_escape($this->encryptdata
['O']).')';
10713 $out .= ' /U ('.TCPDF_STATIC
::_escape($this->encryptdata
['U']).')';
10714 $out .= ' /P '.$this->encryptdata
['P'];
10715 if (isset($this->encryptdata
['EncryptMetadata']) AND (!$this->encryptdata
['EncryptMetadata'])) {
10716 $out .= ' /EncryptMetadata false';
10718 $out .= ' /EncryptMetadata true';
10722 $out .= "\n".'endobj';
10727 * Compute U value (used for encryption)
10728 * @return string U value
10730 * @since 2.0.000 (2008-01-02)
10731 * @author Nicola Asuni
10733 protected function _Uvalue() {
10734 if ($this->encryptdata
['mode'] == 0) { // RC4-40
10735 return TCPDF_STATIC
::_RC4($this->encryptdata
['key'], TCPDF_STATIC
::$enc_padding, $this->last_enc_key
, $this->last_enc_key_c
);
10736 } elseif ($this->encryptdata
['mode'] < 3) { // RC4-128, AES-128
10737 $tmp = TCPDF_STATIC
::_md5_16(TCPDF_STATIC
::$enc_padding.$this->encryptdata
['fileid']);
10738 $enc = TCPDF_STATIC
::_RC4($this->encryptdata
['key'], $tmp, $this->last_enc_key
, $this->last_enc_key_c
);
10739 $len = strlen($tmp);
10740 for ($i = 1; $i <= 19; ++
$i) {
10742 for ($j = 0; $j < $len; ++
$j) {
10743 $ek .= chr(ord($this->encryptdata
['key'][$j]) ^
$i);
10745 $enc = TCPDF_STATIC
::_RC4($ek, $enc, $this->last_enc_key
, $this->last_enc_key_c
);
10747 $enc .= str_repeat("\x00", 16);
10748 return substr($enc, 0, 32);
10749 } elseif ($this->encryptdata
['mode'] == 3) { // AES-256
10750 $seed = TCPDF_STATIC
::_md5_16(TCPDF_STATIC
::getRandomSeed());
10751 // User Validation Salt
10752 $this->encryptdata
['UVS'] = substr($seed, 0, 8);
10754 $this->encryptdata
['UKS'] = substr($seed, 8, 16);
10755 return hash('sha256', $this->encryptdata
['user_password'].$this->encryptdata
['UVS'], true).$this->encryptdata
['UVS'].$this->encryptdata
['UKS'];
10760 * Compute UE value (used for encryption)
10761 * @return string UE value
10763 * @since 5.9.006 (2010-10-19)
10764 * @author Nicola Asuni
10766 protected function _UEvalue() {
10767 $hashkey = hash('sha256', $this->encryptdata
['user_password'].$this->encryptdata
['UKS'], true);
10768 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128
, MCRYPT_MODE_CBC
));
10769 return mcrypt_encrypt(MCRYPT_RIJNDAEL_128
, $hashkey, $this->encryptdata
['key'], MCRYPT_MODE_CBC
, $iv);
10773 * Compute O value (used for encryption)
10774 * @return string O value
10776 * @since 2.0.000 (2008-01-02)
10777 * @author Nicola Asuni
10779 protected function _Ovalue() {
10780 if ($this->encryptdata
['mode'] < 3) { // RC4-40, RC4-128, AES-128
10781 $tmp = TCPDF_STATIC
::_md5_16($this->encryptdata
['owner_password']);
10782 if ($this->encryptdata
['mode'] > 0) {
10783 for ($i = 0; $i < 50; ++
$i) {
10784 $tmp = TCPDF_STATIC
::_md5_16($tmp);
10787 $owner_key = substr($tmp, 0, ($this->encryptdata
['Length'] / 8));
10788 $enc = TCPDF_STATIC
::_RC4($owner_key, $this->encryptdata
['user_password'], $this->last_enc_key
, $this->last_enc_key_c
);
10789 if ($this->encryptdata
['mode'] > 0) {
10790 $len = strlen($owner_key);
10791 for ($i = 1; $i <= 19; ++
$i) {
10793 for ($j = 0; $j < $len; ++
$j) {
10794 $ek .= chr(ord($owner_key[$j]) ^
$i);
10796 $enc = TCPDF_STATIC
::_RC4($ek, $enc, $this->last_enc_key
, $this->last_enc_key_c
);
10800 } elseif ($this->encryptdata
['mode'] == 3) { // AES-256
10801 $seed = TCPDF_STATIC
::_md5_16(TCPDF_STATIC
::getRandomSeed());
10802 // Owner Validation Salt
10803 $this->encryptdata
['OVS'] = substr($seed, 0, 8);
10805 $this->encryptdata
['OKS'] = substr($seed, 8, 16);
10806 return hash('sha256', $this->encryptdata
['owner_password'].$this->encryptdata
['OVS'].$this->encryptdata
['U'], true).$this->encryptdata
['OVS'].$this->encryptdata
['OKS'];
10811 * Compute OE value (used for encryption)
10812 * @return string OE value
10814 * @since 5.9.006 (2010-10-19)
10815 * @author Nicola Asuni
10817 protected function _OEvalue() {
10818 $hashkey = hash('sha256', $this->encryptdata
['owner_password'].$this->encryptdata
['OKS'].$this->encryptdata
['U'], true);
10819 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128
, MCRYPT_MODE_CBC
));
10820 return mcrypt_encrypt(MCRYPT_RIJNDAEL_128
, $hashkey, $this->encryptdata
['key'], MCRYPT_MODE_CBC
, $iv);
10824 * Convert password for AES-256 encryption mode
10825 * @param $password (string) password
10826 * @return string password
10828 * @since 5.9.006 (2010-10-19)
10829 * @author Nicola Asuni
10831 protected function _fixAES256Password($password) {
10832 $psw = ''; // password to be returned
10833 $psw_array = TCPDF_FONTS
::utf8Bidi(TCPDF_FONTS
::UTF8StringToArray($password, $this->isunicode
, $this->CurrentFont
), $password, $this->rtl
, $this->isunicode
, $this->CurrentFont
);
10834 foreach ($psw_array as $c) {
10835 $psw .= TCPDF_FONTS
::unichr($c, $this->isunicode
);
10837 return substr($psw, 0, 127);
10841 * Compute encryption key
10843 * @since 2.0.000 (2008-01-02)
10844 * @author Nicola Asuni
10846 protected function _generateencryptionkey() {
10847 $keybytelen = ($this->encryptdata
['Length'] / 8);
10848 if (!$this->encryptdata
['pubkey']) { // standard mode
10849 if ($this->encryptdata
['mode'] == 3) { // AES-256
10850 // generate 256 bit random key
10851 $this->encryptdata
['key'] = substr(hash('sha256', TCPDF_STATIC
::getRandomSeed(), true), 0, $keybytelen);
10852 // truncate passwords
10853 $this->encryptdata
['user_password'] = $this->_fixAES256Password($this->encryptdata
['user_password']);
10854 $this->encryptdata
['owner_password'] = $this->_fixAES256Password($this->encryptdata
['owner_password']);
10856 $this->encryptdata
['U'] = $this->_Uvalue();
10857 // Compute UE value
10858 $this->encryptdata
['UE'] = $this->_UEvalue();
10860 $this->encryptdata
['O'] = $this->_Ovalue();
10861 // Compute OE value
10862 $this->encryptdata
['OE'] = $this->_OEvalue();
10864 $this->encryptdata
['P'] = $this->encryptdata
['protection'];
10865 // Computing the encryption dictionary's Perms (permissions) value
10866 $perms = TCPDF_STATIC
::getEncPermissionsString($this->encryptdata
['protection']); // bytes 0-3
10867 $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
10868 if (isset($this->encryptdata
['CF']['EncryptMetadata']) AND (!$this->encryptdata
['CF']['EncryptMetadata'])) { // byte 8
10873 $perms .= 'adb'; // bytes 9-11
10874 $perms .= 'nick'; // bytes 12-15
10875 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128
, MCRYPT_MODE_ECB
));
10876 $this->encryptdata
['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128
, $this->encryptdata
['key'], $perms, MCRYPT_MODE_ECB
, $iv);
10877 } else { // RC4-40, RC4-128, AES-128
10879 $this->encryptdata
['user_password'] = substr($this->encryptdata
['user_password'].TCPDF_STATIC
::$enc_padding, 0, 32);
10880 $this->encryptdata
['owner_password'] = substr($this->encryptdata
['owner_password'].TCPDF_STATIC
::$enc_padding, 0, 32);
10882 $this->encryptdata
['O'] = $this->_Ovalue();
10883 // get default permissions (reverse byte order)
10884 $permissions = TCPDF_STATIC
::getEncPermissionsString($this->encryptdata
['protection']);
10885 // Compute encryption key
10886 $tmp = TCPDF_STATIC
::_md5_16($this->encryptdata
['user_password'].$this->encryptdata
['O'].$permissions.$this->encryptdata
['fileid']);
10887 if ($this->encryptdata
['mode'] > 0) {
10888 for ($i = 0; $i < 50; ++
$i) {
10889 $tmp = TCPDF_STATIC
::_md5_16(substr($tmp, 0, $keybytelen));
10892 $this->encryptdata
['key'] = substr($tmp, 0, $keybytelen);
10894 $this->encryptdata
['U'] = $this->_Uvalue();
10896 $this->encryptdata
['P'] = $this->encryptdata
['protection'];
10898 } else { // Public-Key mode
10899 // random 20-byte seed
10900 $seed = sha1(TCPDF_STATIC
::getRandomSeed(), true);
10901 $recipient_bytes = '';
10902 foreach ($this->encryptdata
['pubkeys'] as $pubkey) {
10903 // for each public certificate
10904 if (isset($pubkey['p'])) {
10905 $pkprotection = TCPDF_STATIC
::getUserPermissionCode($pubkey['p'], $this->encryptdata
['mode']);
10907 $pkprotection = $this->encryptdata
['protection'];
10909 // get default permissions (reverse byte order)
10910 $pkpermissions = TCPDF_STATIC
::getEncPermissionsString($pkprotection);
10912 $envelope = $seed.$pkpermissions;
10913 // write the envelope data to a temporary file
10914 $tempkeyfile = TCPDF_STATIC
::getObjFilename('key');
10915 $f = fopen($tempkeyfile, 'wb');
10917 $this->Error('Unable to create temporary key file: '.$tempkeyfile);
10919 $envelope_length = strlen($envelope);
10920 fwrite($f, $envelope, $envelope_length);
10922 $tempencfile = TCPDF_STATIC
::getObjFilename('enc');
10923 if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY
| PKCS7_DETACHED
)) {
10924 $this->Error('Unable to encrypt the file: '.$tempkeyfile);
10926 unlink($tempkeyfile);
10927 // read encryption signature
10928 $signature = file_get_contents($tempencfile, false, null, $envelope_length);
10929 unlink($tempencfile);
10930 // extract signature
10931 $signature = substr($signature, strpos($signature, 'Content-Disposition'));
10932 $tmparr = explode("\n\n", $signature);
10933 $signature = trim($tmparr[1]);
10935 // decode signature
10936 $signature = base64_decode($signature);
10937 // convert signature to hex
10938 $hexsignature = current(unpack('H*', $signature));
10939 // store signature on recipients array
10940 $this->encryptdata
['Recipients'][] = $hexsignature;
10941 // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
10942 $recipient_bytes .= $signature;
10944 // calculate encryption key
10945 if ($this->encryptdata
['mode'] == 3) { // AES-256
10946 $this->encryptdata
['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
10947 } else { // RC4-40, RC4-128, AES-128
10948 $this->encryptdata
['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
10954 * Set document protection
10955 * Remark: the protection against modification is for people who have the full Acrobat product.
10956 * 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.
10957 * 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.
10958 * @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>
10959 * @param $user_pass (String) user password. Empty by default.
10960 * @param $owner_pass (String) owner password. If not specified, a random value is used.
10961 * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
10962 * @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')))
10964 * @since 2.0.000 (2008-01-02)
10965 * @author Nicola Asuni
10967 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) {
10968 if ($this->pdfa_mode
) {
10969 // encryption is not allowed in PDF/A mode
10972 $this->encryptdata
['protection'] = TCPDF_STATIC
::getUserPermissionCode($permissions, $mode);
10973 if (($pubkeys !== null) AND (is_array($pubkeys))) {
10975 $this->encryptdata
['pubkeys'] = $pubkeys;
10977 // public-Key Security requires at least 128 bit
10980 if (!function_exists('openssl_pkcs7_encrypt')) {
10981 $this->Error('Public-Key Security requires openssl library.');
10983 // Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
10984 $this->encryptdata
['pubkey'] = true;
10985 $this->encryptdata
['Filter'] = 'Adobe.PubSec';
10986 $this->encryptdata
['StmF'] = 'DefaultCryptFilter';
10987 $this->encryptdata
['StrF'] = 'DefaultCryptFilter';
10989 // standard mode (password mode)
10990 $this->encryptdata
['pubkey'] = false;
10991 $this->encryptdata
['Filter'] = 'Standard';
10992 $this->encryptdata
['StmF'] = 'StdCF';
10993 $this->encryptdata
['StrF'] = 'StdCF';
10995 if ($mode > 1) { // AES
10996 if (!extension_loaded('mcrypt')) {
10997 $this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).');
10999 if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128
) === false) {
11000 $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
11002 if (($mode == 3) AND !function_exists('hash')) {
11003 // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
11004 $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
11007 if ($owner_pass === null) {
11008 $owner_pass = md5(TCPDF_STATIC
::getRandomSeed());
11010 $this->encryptdata
['user_password'] = $user_pass;
11011 $this->encryptdata
['owner_password'] = $owner_pass;
11012 $this->encryptdata
['mode'] = $mode;
11014 case 0: { // RC4 40 bit
11015 $this->encryptdata
['V'] = 1;
11016 $this->encryptdata
['Length'] = 40;
11017 $this->encryptdata
['CF']['CFM'] = 'V2';
11020 case 1: { // RC4 128 bit
11021 $this->encryptdata
['V'] = 2;
11022 $this->encryptdata
['Length'] = 128;
11023 $this->encryptdata
['CF']['CFM'] = 'V2';
11024 if ($this->encryptdata
['pubkey']) {
11025 $this->encryptdata
['SubFilter'] = 'adbe.pkcs7.s4';
11026 $this->encryptdata
['Recipients'] = array();
11030 case 2: { // AES 128 bit
11031 $this->encryptdata
['V'] = 4;
11032 $this->encryptdata
['Length'] = 128;
11033 $this->encryptdata
['CF']['CFM'] = 'AESV2';
11034 $this->encryptdata
['CF']['Length'] = 128;
11035 if ($this->encryptdata
['pubkey']) {
11036 $this->encryptdata
['SubFilter'] = 'adbe.pkcs7.s5';
11037 $this->encryptdata
['Recipients'] = array();
11041 case 3: { // AES 256 bit
11042 $this->encryptdata
['V'] = 5;
11043 $this->encryptdata
['Length'] = 256;
11044 $this->encryptdata
['CF']['CFM'] = 'AESV3';
11045 $this->encryptdata
['CF']['Length'] = 256;
11046 if ($this->encryptdata
['pubkey']) {
11047 $this->encryptdata
['SubFilter'] = 'adbe.pkcs7.s5';
11048 $this->encryptdata
['Recipients'] = array();
11053 $this->encrypted
= true;
11054 $this->encryptdata
['fileid'] = TCPDF_STATIC
::convertHexStringToString($this->file_id
);
11055 $this->_generateencryptionkey();
11058 // END OF ENCRYPTION FUNCTIONS -------------------------
11060 // START TRANSFORMATIONS SECTION -----------------------
11063 * Starts a 2D tranformation saving current graphic state.
11064 * This function must be called before scaling, mirroring, translation, rotation and skewing.
11065 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
11067 * @since 2.1.000 (2008-01-07)
11068 * @see StartTransform(), StopTransform()
11070 public function StartTransform() {
11071 if ($this->state
!= 2) {
11074 $this->_outSaveGraphicsState();
11075 if ($this->inxobj
) {
11076 // we are inside an XObject template
11077 $this->xobjects
[$this->xobjid
]['transfmrk'][] = strlen($this->xobjects
[$this->xobjid
]['outdata']);
11079 $this->transfmrk
[$this->page
][] = $this->pagelen
[$this->page
];
11081 ++
$this->transfmatrix_key
;
11082 $this->transfmatrix
[$this->transfmatrix_key
] = array();
11086 * Stops a 2D tranformation restoring previous graphic state.
11087 * This function must be called after scaling, mirroring, translation, rotation and skewing.
11088 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
11090 * @since 2.1.000 (2008-01-07)
11091 * @see StartTransform(), StopTransform()
11093 public function StopTransform() {
11094 if ($this->state
!= 2) {
11097 $this->_outRestoreGraphicsState();
11098 if (isset($this->transfmatrix
[$this->transfmatrix_key
])) {
11099 array_pop($this->transfmatrix
[$this->transfmatrix_key
]);
11100 --$this->transfmatrix_key
;
11102 if ($this->inxobj
) {
11103 // we are inside an XObject template
11104 array_pop($this->xobjects
[$this->xobjid
]['transfmrk']);
11106 array_pop($this->transfmrk
[$this->page
]);
11110 * Horizontal Scaling.
11111 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
11112 * @param $x (int) abscissa of the scaling center. Default is current x position
11113 * @param $y (int) ordinate of the scaling center. Default is current y position
11115 * @since 2.1.000 (2008-01-07)
11116 * @see StartTransform(), StopTransform()
11118 public function ScaleX($s_x, $x='', $y='') {
11119 $this->Scale($s_x, 100, $x, $y);
11123 * Vertical Scaling.
11124 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
11125 * @param $x (int) abscissa of the scaling center. Default is current x position
11126 * @param $y (int) ordinate of the scaling center. Default is current y position
11128 * @since 2.1.000 (2008-01-07)
11129 * @see StartTransform(), StopTransform()
11131 public function ScaleY($s_y, $x='', $y='') {
11132 $this->Scale(100, $s_y, $x, $y);
11136 * Vertical and horizontal proportional Scaling.
11137 * @param $s (float) scaling factor for width and height as percent. 0 is not allowed.
11138 * @param $x (int) abscissa of the scaling center. Default is current x position
11139 * @param $y (int) ordinate of the scaling center. Default is current y position
11141 * @since 2.1.000 (2008-01-07)
11142 * @see StartTransform(), StopTransform()
11144 public function ScaleXY($s, $x='', $y='') {
11145 $this->Scale($s, $s, $x, $y);
11149 * Vertical and horizontal non-proportional Scaling.
11150 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
11151 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
11152 * @param $x (int) abscissa of the scaling center. Default is current x position
11153 * @param $y (int) ordinate of the scaling center. Default is current y position
11155 * @since 2.1.000 (2008-01-07)
11156 * @see StartTransform(), StopTransform()
11158 public function Scale($s_x, $s_y, $x='', $y='') {
11165 if (($s_x == 0) OR ($s_y == 0)) {
11166 $this->Error('Please do not use values equal to zero for scaling');
11168 $y = ($this->h
- $y) * $this->k
;
11170 //calculate elements of transformation matrix
11178 $tm[4] = $x * (1 - $s_x);
11179 $tm[5] = $y * (1 - $s_y);
11180 //scale the coordinate system
11181 $this->Transform($tm);
11185 * Horizontal Mirroring.
11186 * @param $x (int) abscissa of the point. Default is current x position
11188 * @since 2.1.000 (2008-01-07)
11189 * @see StartTransform(), StopTransform()
11191 public function MirrorH($x='') {
11192 $this->Scale(-100, 100, $x);
11196 * Verical Mirroring.
11197 * @param $y (int) ordinate of the point. Default is current y position
11199 * @since 2.1.000 (2008-01-07)
11200 * @see StartTransform(), StopTransform()
11202 public function MirrorV($y='') {
11203 $this->Scale(100, -100, '', $y);
11207 * Point reflection mirroring.
11208 * @param $x (int) abscissa of the point. Default is current x position
11209 * @param $y (int) ordinate of the point. Default is current y position
11211 * @since 2.1.000 (2008-01-07)
11212 * @see StartTransform(), StopTransform()
11214 public function MirrorP($x='',$y='') {
11215 $this->Scale(-100, -100, $x, $y);
11219 * Reflection against a straight line through point (x, y) with the gradient angle (angle).
11220 * @param $angle (float) gradient angle of the straight line. Default is 0 (horizontal line).
11221 * @param $x (int) abscissa of the point. Default is current x position
11222 * @param $y (int) ordinate of the point. Default is current y position
11224 * @since 2.1.000 (2008-01-07)
11225 * @see StartTransform(), StopTransform()
11227 public function MirrorL($angle=0, $x='',$y='') {
11228 $this->Scale(-100, 100, $x, $y);
11229 $this->Rotate(-2*($angle-90), $x, $y);
11233 * Translate graphic object horizontally.
11234 * @param $t_x (int) movement to the right (or left for RTL)
11236 * @since 2.1.000 (2008-01-07)
11237 * @see StartTransform(), StopTransform()
11239 public function TranslateX($t_x) {
11240 $this->Translate($t_x, 0);
11244 * Translate graphic object vertically.
11245 * @param $t_y (int) movement to the bottom
11247 * @since 2.1.000 (2008-01-07)
11248 * @see StartTransform(), StopTransform()
11250 public function TranslateY($t_y) {
11251 $this->Translate(0, $t_y);
11255 * Translate graphic object horizontally and vertically.
11256 * @param $t_x (int) movement to the right
11257 * @param $t_y (int) movement to the bottom
11259 * @since 2.1.000 (2008-01-07)
11260 * @see StartTransform(), StopTransform()
11262 public function Translate($t_x, $t_y) {
11263 //calculate elements of transformation matrix
11269 $tm[4] = $t_x * $this->k
;
11270 $tm[5] = -$t_y * $this->k
;
11271 //translate the coordinate system
11272 $this->Transform($tm);
11277 * @param $angle (float) angle in degrees for counter-clockwise rotation
11278 * @param $x (int) abscissa of the rotation center. Default is current x position
11279 * @param $y (int) ordinate of the rotation center. Default is current y position
11281 * @since 2.1.000 (2008-01-07)
11282 * @see StartTransform(), StopTransform()
11284 public function Rotate($angle, $x='', $y='') {
11291 $y = ($this->h
- $y) * $this->k
;
11293 //calculate elements of transformation matrix
11295 $tm[0] = cos(deg2rad($angle));
11296 $tm[1] = sin(deg2rad($angle));
11299 $tm[4] = $x +
($tm[1] * $y) - ($tm[0] * $x);
11300 $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
11301 //rotate the coordinate system around ($x,$y)
11302 $this->Transform($tm);
11306 * Skew horizontally.
11307 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
11308 * @param $x (int) abscissa of the skewing center. default is current x position
11309 * @param $y (int) ordinate of the skewing center. default is current y position
11311 * @since 2.1.000 (2008-01-07)
11312 * @see StartTransform(), StopTransform()
11314 public function SkewX($angle_x, $x='', $y='') {
11315 $this->Skew($angle_x, 0, $x, $y);
11320 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
11321 * @param $x (int) abscissa of the skewing center. default is current x position
11322 * @param $y (int) ordinate of the skewing center. default is current y position
11324 * @since 2.1.000 (2008-01-07)
11325 * @see StartTransform(), StopTransform()
11327 public function SkewY($angle_y, $x='', $y='') {
11328 $this->Skew(0, $angle_y, $x, $y);
11333 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
11334 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
11335 * @param $x (int) abscissa of the skewing center. default is current x position
11336 * @param $y (int) ordinate of the skewing center. default is current y position
11338 * @since 2.1.000 (2008-01-07)
11339 * @see StartTransform(), StopTransform()
11341 public function Skew($angle_x, $angle_y, $x='', $y='') {
11348 if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
11349 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
11352 $y = ($this->h
- $y) * $this->k
;
11353 //calculate elements of transformation matrix
11356 $tm[1] = tan(deg2rad($angle_y));
11357 $tm[2] = tan(deg2rad($angle_x));
11359 $tm[4] = -$tm[2] * $y;
11360 $tm[5] = -$tm[1] * $x;
11361 //skew the coordinate system
11362 $this->Transform($tm);
11366 * Apply graphic transformations.
11367 * @param $tm (array) transformation matrix
11369 * @since 2.1.000 (2008-01-07)
11370 * @see StartTransform(), StopTransform()
11372 protected function Transform($tm) {
11373 if ($this->state
!= 2) {
11376 $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
11377 // add tranformation matrix
11378 $this->transfmatrix
[$this->transfmatrix_key
][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
11379 // update transformation mark
11380 if ($this->inxobj
) {
11381 // we are inside an XObject template
11382 if (end($this->xobjects
[$this->xobjid
]['transfmrk']) !== false) {
11383 $key = key($this->xobjects
[$this->xobjid
]['transfmrk']);
11384 $this->xobjects
[$this->xobjid
]['transfmrk'][$key] = strlen($this->xobjects
[$this->xobjid
]['outdata']);
11386 } elseif (end($this->transfmrk
[$this->page
]) !== false) {
11387 $key = key($this->transfmrk
[$this->page
]);
11388 $this->transfmrk
[$this->page
][$key] = $this->pagelen
[$this->page
];
11392 // END TRANSFORMATIONS SECTION -------------------------
11394 // START GRAPHIC FUNCTIONS SECTION ---------------------
11395 // The following section is based on the code provided by David Hernandez Sanz
11398 * 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.
11399 * @param $width (float) The width.
11402 * @see Line(), Rect(), Cell(), MultiCell()
11404 public function SetLineWidth($width) {
11406 $this->LineWidth
= $width;
11407 $this->linestyleWidth
= sprintf('%F w', ($width * $this->k
));
11408 if ($this->state
== 2) {
11409 $this->_out($this->linestyleWidth
);
11414 * Returns the current the line width.
11415 * @return int Line width
11417 * @since 2.1.000 (2008-01-07)
11418 * @see Line(), SetLineWidth()
11420 public function GetLineWidth() {
11421 return $this->LineWidth
;
11426 * @param $style (array) Line style. Array with keys among the following:
11428 * <li>width (float): Width of the line in user units.</li>
11429 * <li>cap (string): Type of cap to put on the line. Possible values are:
11430 * butt, round, square. The difference between "square" and "butt" is that
11431 * "square" projects a flat end past the end of the line.</li>
11432 * <li>join (string): Type of join. Possible values are: miter, round,
11434 * <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
11435 * series of length values, which are the lengths of the on and off dashes.
11436 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
11437 * 1 off, 2 on, 1 off, ...</li>
11438 * <li>phase (integer): Modifier on the dash pattern which is used to shift
11439 * the point at which the pattern starts.</li>
11440 * <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>
11442 * @param $ret (boolean) if true do not send the command.
11443 * @return string the PDF command
11445 * @since 2.1.000 (2008-01-08)
11447 public function SetLineStyle($style, $ret=false) {
11448 $s = ''; // string to be returned
11449 if (!is_array($style)) {
11452 if (isset($style['width'])) {
11453 $this->LineWidth
= $style['width'];
11454 $this->linestyleWidth
= sprintf('%F w', ($style['width'] * $this->k
));
11455 $s .= $this->linestyleWidth
.' ';
11457 if (isset($style['cap'])) {
11458 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
11459 if (isset($ca[$style['cap']])) {
11460 $this->linestyleCap
= $ca[$style['cap']].' J';
11461 $s .= $this->linestyleCap
.' ';
11464 if (isset($style['join'])) {
11465 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
11466 if (isset($ja[$style['join']])) {
11467 $this->linestyleJoin
= $ja[$style['join']].' j';
11468 $s .= $this->linestyleJoin
.' ';
11471 if (isset($style['dash'])) {
11473 if ($style['dash']) {
11474 if (preg_match('/^.+,/', $style['dash']) > 0) {
11475 $tab = explode(',', $style['dash']);
11477 $tab = array($style['dash']);
11480 foreach ($tab as $i => $v) {
11482 $dash_string .= ' ';
11484 $dash_string .= sprintf('%F', $v);
11487 if (!isset($style['phase']) OR !$style['dash']) {
11488 $style['phase'] = 0;
11490 $this->linestyleDash
= sprintf('[%s] %F d', $dash_string, $style['phase']);
11491 $s .= $this->linestyleDash
.' ';
11493 if (isset($style['color'])) {
11494 $s .= $this->SetDrawColorArray($style['color'], true).' ';
11496 if (!$ret AND ($this->state
== 2)) {
11503 * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment.
11504 * @param $x (float) Abscissa of point.
11505 * @param $y (float) Ordinate of point.
11507 * @since 2.1.000 (2008-01-08)
11509 protected function _outPoint($x, $y) {
11510 if ($this->state
== 2) {
11511 $this->_out(sprintf('%F %F m', ($x * $this->k
), (($this->h
- $y) * $this->k
)));
11516 * Append a straight line segment from the current point to the point (x, y).
11517 * The new current point shall be (x, y).
11518 * @param $x (float) Abscissa of end point.
11519 * @param $y (float) Ordinate of end point.
11521 * @since 2.1.000 (2008-01-08)
11523 protected function _outLine($x, $y) {
11524 if ($this->state
== 2) {
11525 $this->_out(sprintf('%F %F l', ($x * $this->k
), (($this->h
- $y) * $this->k
)));
11530 * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space.
11531 * @param $x (float) Abscissa of upper-left corner.
11532 * @param $y (float) Ordinate of upper-left corner.
11533 * @param $w (float) Width.
11534 * @param $h (float) Height.
11535 * @param $op (string) options
11537 * @since 2.1.000 (2008-01-08)
11539 protected function _outRect($x, $y, $w, $h, $op) {
11540 if ($this->state
== 2) {
11541 $this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k
), (($this->h
- $y) * $this->k
), ($w * $this->k
), (-$h * $this->k
), $op));
11546 * 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.
11547 * The new current point shall be (x3, y3).
11548 * @param $x1 (float) Abscissa of control point 1.
11549 * @param $y1 (float) Ordinate of control point 1.
11550 * @param $x2 (float) Abscissa of control point 2.
11551 * @param $y2 (float) Ordinate of control point 2.
11552 * @param $x3 (float) Abscissa of end point.
11553 * @param $y3 (float) Ordinate of end point.
11555 * @since 2.1.000 (2008-01-08)
11557 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
11558 if ($this->state
== 2) {
11559 $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
)));
11564 * 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.
11565 * The new current point shall be (x3, y3).
11566 * @param $x2 (float) Abscissa of control point 2.
11567 * @param $y2 (float) Ordinate of control point 2.
11568 * @param $x3 (float) Abscissa of end point.
11569 * @param $y3 (float) Ordinate of end point.
11571 * @since 4.9.019 (2010-04-26)
11573 protected function _outCurveV($x2, $y2, $x3, $y3) {
11574 if ($this->state
== 2) {
11575 $this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k
), (($this->h
- $y2) * $this->k
), ($x3 * $this->k
), (($this->h
- $y3) * $this->k
)));
11580 * 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.
11581 * The new current point shall be (x3, y3).
11582 * @param $x1 (float) Abscissa of control point 1.
11583 * @param $y1 (float) Ordinate of control point 1.
11584 * @param $x3 (float) Abscissa of end point.
11585 * @param $y3 (float) Ordinate of end point.
11587 * @since 2.1.000 (2008-01-08)
11589 protected function _outCurveY($x1, $y1, $x3, $y3) {
11590 if ($this->state
== 2) {
11591 $this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k
), (($this->h
- $y1) * $this->k
), ($x3 * $this->k
), (($this->h
- $y3) * $this->k
)));
11596 * Draws a line between two points.
11597 * @param $x1 (float) Abscissa of first point.
11598 * @param $y1 (float) Ordinate of first point.
11599 * @param $x2 (float) Abscissa of second point.
11600 * @param $y2 (float) Ordinate of second point.
11601 * @param $style (array) Line style. Array like for SetLineStyle(). Default value: default line style (empty array).
11604 * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
11606 public function Line($x1, $y1, $x2, $y2, $style=array()) {
11607 if ($this->state
!= 2) {
11610 if (is_array($style)) {
11611 $this->SetLineStyle($style);
11613 $this->_outPoint($x1, $y1);
11614 $this->_outLine($x2, $y2);
11619 * Draws a rectangle.
11620 * @param $x (float) Abscissa of upper-left corner.
11621 * @param $y (float) Ordinate of upper-left corner.
11622 * @param $w (float) Width.
11623 * @param $h (float) Height.
11624 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11625 * @param $border_style (array) Border style of rectangle. Array with keys among the following:
11627 * <li>all: Line style of all borders. Array like for SetLineStyle().</li>
11628 * <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for SetLineStyle().</li>
11630 * If a key is not present or is null, the correspondent border is not drawn. Default value: default line style (empty array).
11631 * @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).
11634 * @see SetLineStyle()
11636 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
11637 if ($this->state
!= 2) {
11640 if (empty($style)) {
11643 if (!(strpos($style, 'F') === false) AND !empty($fill_color)) {
11644 // set background color
11645 $this->SetFillColorArray($fill_color);
11647 if (!empty($border_style)) {
11648 if (isset($border_style['all']) AND !empty($border_style['all'])) {
11649 //set global style for border
11650 $this->SetLineStyle($border_style['all']);
11651 $border_style = array();
11653 // remove stroke operator from style
11654 $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*' );
11655 if (isset($opnostroke[$style])) {
11656 $style = $opnostroke[$style];
11660 if (!empty($style)) {
11661 $op = TCPDF_STATIC
::getPathPaintOperator($style);
11662 $this->_outRect($x, $y, $w, $h, $op);
11664 if (!empty($border_style)) {
11665 $border_style2 = array();
11666 foreach ($border_style as $line => $value) {
11667 $length = strlen($line);
11668 for ($i = 0; $i < $length; ++
$i) {
11669 $border_style2[$line[$i]] = $value;
11672 $border_style = $border_style2;
11673 if (isset($border_style['L']) AND $border_style['L']) {
11674 $this->Line($x, $y, $x, $y +
$h, $border_style['L']);
11676 if (isset($border_style['T']) AND $border_style['T']) {
11677 $this->Line($x, $y, $x +
$w, $y, $border_style['T']);
11679 if (isset($border_style['R']) AND $border_style['R']) {
11680 $this->Line($x +
$w, $y, $x +
$w, $y +
$h, $border_style['R']);
11682 if (isset($border_style['B']) AND $border_style['B']) {
11683 $this->Line($x, $y +
$h, $x +
$w, $y +
$h, $border_style['B']);
11689 * Draws a Bezier curve.
11690 * The Bezier curve is a tangent to the line between the control points at
11691 * either end of the curve.
11692 * @param $x0 (float) Abscissa of start point.
11693 * @param $y0 (float) Ordinate of start point.
11694 * @param $x1 (float) Abscissa of control point 1.
11695 * @param $y1 (float) Ordinate of control point 1.
11696 * @param $x2 (float) Abscissa of control point 2.
11697 * @param $y2 (float) Ordinate of control point 2.
11698 * @param $x3 (float) Abscissa of end point.
11699 * @param $y3 (float) Ordinate of end point.
11700 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11701 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
11702 * @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).
11704 * @see SetLineStyle()
11705 * @since 2.1.000 (2008-01-08)
11707 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
11708 if ($this->state
!= 2) {
11711 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11712 $this->SetFillColorArray($fill_color);
11714 $op = TCPDF_STATIC
::getPathPaintOperator($style);
11716 $this->SetLineStyle($line_style);
11718 $this->_outPoint($x0, $y0);
11719 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11724 * Draws a poly-Bezier curve.
11725 * Each Bezier curve segment is a tangent to the line between the control points at
11726 * either end of the curve.
11727 * @param $x0 (float) Abscissa of start point.
11728 * @param $y0 (float) Ordinate of start point.
11729 * @param $segments (float) An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
11730 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11731 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
11732 * @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).
11734 * @see SetLineStyle()
11735 * @since 3.0008 (2008-05-12)
11737 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
11738 if ($this->state
!= 2) {
11741 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11742 $this->SetFillColorArray($fill_color);
11744 $op = TCPDF_STATIC
::getPathPaintOperator($style);
11746 $line_style = array();
11749 $this->SetLineStyle($line_style);
11751 $this->_outPoint($x0, $y0);
11752 foreach ($segments as $segment) {
11753 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
11754 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11760 * Draws an ellipse.
11761 * An ellipse is formed from n Bezier curves.
11762 * @param $x0 (float) Abscissa of center point.
11763 * @param $y0 (float) Ordinate of center point.
11764 * @param $rx (float) Horizontal radius.
11765 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
11766 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
11767 * @param $astart: (float) Angle start of draw line. Default value: 0.
11768 * @param $afinish: (float) Angle finish of draw line. Default value: 360.
11769 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11770 * @param $line_style (array) Line style of ellipse. Array like for SetLineStyle(). Default value: default line style (empty array).
11771 * @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).
11772 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
11773 * @author Nicola Asuni
11775 * @since 2.1.000 (2008-01-08)
11777 public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11778 if ($this->state
!= 2) {
11781 if (TCPDF_STATIC
::empty_string($ry) OR ($ry == 0)) {
11784 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11785 $this->SetFillColorArray($fill_color);
11787 $op = TCPDF_STATIC
::getPathPaintOperator($style);
11789 $line_style = array();
11792 $this->SetLineStyle($line_style);
11794 $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
11799 * Append an elliptical arc to the current path.
11800 * An ellipse is formed from n Bezier curves.
11801 * @param $xc (float) Abscissa of center point.
11802 * @param $yc (float) Ordinate of center point.
11803 * @param $rx (float) Horizontal radius.
11804 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
11805 * @param $xang: (float) Angle between the X-axis and the major axis of the ellipse. Default value: 0.
11806 * @param $angs: (float) Angle start of draw line. Default value: 0.
11807 * @param $angf: (float) Angle finish of draw line. Default value: 360.
11808 * @param $pie (boolean) if true do not mark the border point (used to draw pie sectors).
11809 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
11810 * @param $startpoint (boolean) if true output a starting point.
11811 * @param $ccw (boolean) if true draws in counter-clockwise.
11812 * @param $svg (boolean) if true the angles are in svg mode (already calculated).
11813 * @return array bounding box coordinates (x min, y min, x max, y max)
11814 * @author Nicola Asuni
11816 * @since 4.9.019 (2010-04-26)
11818 protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
11819 if (($rx <= 0) OR ($ry < 0)) {
11826 $xmin = 2147483647;
11827 $ymin = 2147483647;
11831 // center of the arc
11832 $this->_outPoint($xc, $yc);
11834 $xang = deg2rad((float) $xang);
11835 $angs = deg2rad((float) $angs);
11836 $angf = deg2rad((float) $angf);
11841 $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
11842 $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
11850 if ($ccw AND ($as > $af)) {
11851 // reverse rotation
11853 } elseif (!$ccw AND ($as < $af)) {
11854 // reverse rotation
11857 $total_angle = ($af - $as);
11861 // total arcs to draw
11862 $nc *= (2 * abs($total_angle) / M_PI
);
11863 $nc = round($nc) +
1;
11864 // angle of each arc
11865 $arcang = ($total_angle / $nc);
11866 // center point in PDF coordinates
11868 $y0 = ($this->h
- $yc);
11871 $alpha = sin($arcang) * ((sqrt(4 +
(3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
11872 $cos_xang = cos($xang);
11873 $sin_xang = sin($xang);
11874 $cos_ang = cos($ang);
11875 $sin_ang = sin($ang);
11877 $px1 = $x0 +
($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11878 $py1 = $y0 +
($rx * $sin_xang * $cos_ang) +
($ry * $cos_xang * $sin_ang);
11879 // first Bezier control point
11880 $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11881 $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) +
($ry * $cos_xang * $cos_ang)));
11883 // line from center to arc starting point
11884 $this->_outLine($px1, $this->h
- $py1);
11885 } elseif ($startpoint) {
11886 // arc starting point
11887 $this->_outPoint($px1, $this->h
- $py1);
11890 for ($i = 1; $i <= $nc; ++
$i) {
11892 $ang = $as +
($i * $arcang);
11896 $cos_ang = cos($ang);
11897 $sin_ang = sin($ang);
11898 // second arc point
11899 $px2 = $x0 +
($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11900 $py2 = $y0 +
($rx * $sin_xang * $cos_ang) +
($ry * $cos_xang * $sin_ang);
11901 // second Bezier control point
11902 $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11903 $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) +
($ry * $cos_xang * $cos_ang)));
11905 $cx1 = ($px1 +
$qx1);
11906 $cy1 = ($this->h
- ($py1 +
$qy1));
11907 $cx2 = ($px2 - $qx2);
11908 $cy2 = ($this->h
- ($py2 - $qy2));
11910 $cy3 = ($this->h
- $py2);
11911 $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
11912 // get bounding box coordinates
11913 $xmin = min($xmin, $cx1, $cx2, $cx3);
11914 $ymin = min($ymin, $cy1, $cy2, $cy3);
11915 $xmax = max($xmax, $cx1, $cx2, $cx3);
11916 $ymax = max($ymax, $cy1, $cy2, $cy3);
11917 // move to next point
11924 $this->_outLine($xc, $yc);
11925 // get bounding box coordinates
11926 $xmin = min($xmin, $xc);
11927 $ymin = min($ymin, $yc);
11928 $xmax = max($xmax, $xc);
11929 $ymax = max($ymax, $yc);
11931 return array($xmin, $ymin, $xmax, $ymax);
11936 * A circle is formed from n Bezier curves.
11937 * @param $x0 (float) Abscissa of center point.
11938 * @param $y0 (float) Ordinate of center point.
11939 * @param $r (float) Radius.
11940 * @param $angstr: (float) Angle start of draw line. Default value: 0.
11941 * @param $angend: (float) Angle finish of draw line. Default value: 360.
11942 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11943 * @param $line_style (array) Line style of circle. Array like for SetLineStyle(). Default value: default line style (empty array).
11944 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
11945 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of circle.
11947 * @since 2.1.000 (2008-01-08)
11949 public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11950 $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
11954 * Draws a polygonal line
11955 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
11956 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11957 * @param $line_style (array) Line style of polygon. Array with keys among the following:
11959 * <li>all: Line style of all lines. Array like for SetLineStyle().</li>
11960 * <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
11962 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
11963 * @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).
11964 * @since 4.8.003 (2009-09-15)
11967 public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
11968 $this->Polygon($p, $style, $line_style, $fill_color, false);
11973 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
11974 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11975 * @param $line_style (array) Line style of polygon. Array with keys among the following:
11977 * <li>all: Line style of all lines. Array like for SetLineStyle().</li>
11978 * <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
11980 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
11981 * @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).
11982 * @param $closed (boolean) if true the polygon is closes, otherwise will remain open
11984 * @since 2.1.000 (2008-01-08)
11986 public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
11987 if ($this->state
!= 2) {
11990 $nc = count($p); // number of coordinates
11991 $np = $nc / 2; // number of points
11993 // close polygon by adding the first 2 points at the end (one line)
11994 for ($i = 0; $i < 4; ++
$i) {
11995 $p[$nc +
$i] = $p[$i];
11997 // copy style for the last added line
11998 if (isset($line_style[0])) {
11999 $line_style[$np] = $line_style[0];
12003 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12004 $this->SetFillColorArray($fill_color);
12006 $op = TCPDF_STATIC
::getPathPaintOperator($style);
12008 $line_style = array();
12012 if (isset($line_style['all'])) {
12013 $this->SetLineStyle($line_style['all']);
12019 $this->_outPoint($p[0], $p[1]);
12020 for ($i = 2; $i < $nc; $i = $i +
2) {
12021 $this->_outLine($p[$i], $p[$i +
1]);
12026 $this->_outPoint($p[0], $p[1]);
12027 for ($i = 2; $i < $nc; $i = $i +
2) {
12028 $line_num = ($i / 2) - 1;
12029 if (isset($line_style[$line_num])) {
12030 if ($line_style[$line_num] != 0) {
12031 if (is_array($line_style[$line_num])) {
12033 $this->SetLineStyle($line_style[$line_num]);
12034 $this->_outPoint($p[$i - 2], $p[$i - 1]);
12035 $this->_outLine($p[$i], $p[$i +
1]);
12037 $this->_outPoint($p[$i], $p[$i +
1]);
12039 $this->_outLine($p[$i], $p[$i +
1]);
12043 $this->_outLine($p[$i], $p[$i +
1]);
12050 $this->_outPoint($p[0], $p[1]);
12051 for ($i = 2; $i < $nc; $i = $i +
2) {
12052 $this->_outLine($p[$i], $p[$i +
1]);
12059 * Draws a regular polygon.
12060 * @param $x0 (float) Abscissa of center point.
12061 * @param $y0 (float) Ordinate of center point.
12062 * @param $r: (float) Radius of inscribed circle.
12063 * @param $ns (integer) Number of sides.
12064 * @param $angle (float) Angle oriented (anti-clockwise). Default value: 0.
12065 * @param $draw_circle (boolean) Draw inscribed circle or not. Default value: false.
12066 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12067 * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
12069 * <li>all: Line style of all sides. Array like for SetLineStyle().</li>
12070 * <li>0 to ($ns - 1): Line style of each side. Array like for SetLineStyle().</li>
12072 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
12073 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
12074 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
12076 * <li>D or empty string: Draw (default).</li>
12077 * <li>F: Fill.</li>
12078 * <li>DF or FD: Draw and fill.</li>
12079 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
12080 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
12082 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
12083 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
12085 * @since 2.1.000 (2008-01-08)
12087 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()) {
12091 if ($draw_circle) {
12092 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12095 for ($i = 0; $i < $ns; ++
$i) {
12096 $a = $angle +
($i * 360 / $ns);
12097 $a_rad = deg2rad((float) $a);
12098 $p[] = $x0 +
($r * sin($a_rad));
12099 $p[] = $y0 +
($r * cos($a_rad));
12101 $this->Polygon($p, $style, $line_style, $fill_color);
12105 * Draws a star polygon
12106 * @param $x0 (float) Abscissa of center point.
12107 * @param $y0 (float) Ordinate of center point.
12108 * @param $r (float) Radius of inscribed circle.
12109 * @param $nv (integer) Number of vertices.
12110 * @param $ng (integer) Number of gap (if ($ng % $nv = 1) then is a regular polygon).
12111 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
12112 * @param $draw_circle: (boolean) Draw inscribed circle or not. Default value is false.
12113 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12114 * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
12116 * <li>all: Line style of all sides. Array like for
12117 * SetLineStyle().</li>
12118 * <li>0 to (n - 1): Line style of each side. Array like for SetLineStyle().</li>
12120 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
12121 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
12122 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
12124 * <li>D or empty string: Draw (default).</li>
12125 * <li>F: Fill.</li>
12126 * <li>DF or FD: Draw and fill.</li>
12127 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
12128 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
12130 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
12131 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
12133 * @since 2.1.000 (2008-01-08)
12135 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()) {
12139 if ($draw_circle) {
12140 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12143 $visited = array();
12144 for ($i = 0; $i < $nv; ++
$i) {
12145 $a = $angle +
($i * 360 / $nv);
12146 $a_rad = deg2rad((float) $a);
12147 $p2[] = $x0 +
($r * sin($a_rad));
12148 $p2[] = $y0 +
($r * cos($a_rad));
12149 $visited[] = false;
12154 $p[] = $p2[$i * 2];
12155 $p[] = $p2[($i * 2) +
1];
12156 $visited[$i] = true;
12159 } while (!$visited[$i]);
12160 $this->Polygon($p, $style, $line_style, $fill_color);
12164 * Draws a rounded rectangle.
12165 * @param $x (float) Abscissa of upper-left corner.
12166 * @param $y (float) Ordinate of upper-left corner.
12167 * @param $w (float) Width.
12168 * @param $h (float) Height.
12169 * @param $r (float) the radius of the circle used to round off the corners of the rectangle.
12170 * @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").
12171 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12172 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12173 * @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).
12175 * @since 2.1.000 (2008-01-08)
12177 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12178 $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
12182 * Draws a rounded rectangle.
12183 * @param $x (float) Abscissa of upper-left corner.
12184 * @param $y (float) Ordinate of upper-left corner.
12185 * @param $w (float) Width.
12186 * @param $h (float) Height.
12187 * @param $rx (float) the x-axis radius of the ellipse used to round off the corners of the rectangle.
12188 * @param $ry (float) the y-axis radius of the ellipse used to round off the corners of the rectangle.
12189 * @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").
12190 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12191 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12192 * @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).
12194 * @since 4.9.019 (2010-04-22)
12196 public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12197 if ($this->state
!= 2) {
12200 if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
12202 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
12206 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12207 $this->SetFillColorArray($fill_color);
12209 $op = TCPDF_STATIC
::getPathPaintOperator($style);
12211 $border_style = array();
12213 if ($border_style) {
12214 $this->SetLineStyle($border_style);
12216 $MyArc = 4 / 3 * (sqrt(2) - 1);
12217 $this->_outPoint($x +
$rx, $y);
12218 $xc = $x +
$w - $rx;
12220 $this->_outLine($xc, $y);
12221 if ($round_corner[0]) {
12222 $this->_outCurve($xc +
($rx * $MyArc), $yc - $ry, $xc +
$rx, $yc - ($ry * $MyArc), $xc +
$rx, $yc);
12224 $this->_outLine($x +
$w, $y);
12226 $xc = $x +
$w - $rx;
12227 $yc = $y +
$h - $ry;
12228 $this->_outLine($x +
$w, $yc);
12229 if ($round_corner[1]) {
12230 $this->_outCurve($xc +
$rx, $yc +
($ry * $MyArc), $xc +
($rx * $MyArc), $yc +
$ry, $xc, $yc +
$ry);
12232 $this->_outLine($x +
$w, $y +
$h);
12235 $yc = $y +
$h - $ry;
12236 $this->_outLine($xc, $y +
$h);
12237 if ($round_corner[2]) {
12238 $this->_outCurve($xc - ($rx * $MyArc), $yc +
$ry, $xc - $rx, $yc +
($ry * $MyArc), $xc - $rx, $yc);
12240 $this->_outLine($x, $y +
$h);
12244 $this->_outLine($x, $yc);
12245 if ($round_corner[3]) {
12246 $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
12248 $this->_outLine($x, $y);
12249 $this->_outLine($x +
$rx, $y);
12255 * Draws a grahic arrow.
12256 * @param $x0 (float) Abscissa of first point.
12257 * @param $y0 (float) Ordinate of first point.
12258 * @param $x1 (float) Abscissa of second point.
12259 * @param $y1 (float) Ordinate of second point.
12260 * @param $head_style (int) (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead)
12261 * @param $arm_size (float) length of arrowhead arms
12262 * @param $arm_angle (int) angle between an arm and the shaft
12263 * @author Piotr Galecki, Nicola Asuni, Andy Meier
12264 * @since 4.6.018 (2009-07-10)
12266 public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
12267 // getting arrow direction angle
12268 // 0 deg angle is when both arms go along X axis. angle grows clockwise.
12269 $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
12270 if ($dir_angle < 0) {
12271 $dir_angle +
= (2 * M_PI
);
12273 $arm_angle = deg2rad($arm_angle);
12276 if ($head_style > 0) {
12277 // calculate the stopping point for the arrow shaft
12278 $sx1 = $x1 +
(($arm_size - $this->LineWidth
) * cos($dir_angle));
12279 $sy1 = $y1 +
(($arm_size - $this->LineWidth
) * sin($dir_angle));
12281 // main arrow line / shaft
12282 $this->Line($x0, $y0, $sx1, $sy1);
12283 // left arrowhead arm tip
12284 $x2L = $x1 +
($arm_size * cos($dir_angle +
$arm_angle));
12285 $y2L = $y1 +
($arm_size * sin($dir_angle +
$arm_angle));
12286 // right arrowhead arm tip
12287 $x2R = $x1 +
($arm_size * cos($dir_angle - $arm_angle));
12288 $y2R = $y1 +
($arm_size * sin($dir_angle - $arm_angle));
12291 switch ($head_style) {
12293 // draw only arrowhead arms
12295 $style = array(1, 1, 0);
12299 // draw closed arrowhead, but no fill
12304 // closed and filled arrowhead
12309 // filled arrowhead
12314 $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
12317 // END GRAPHIC FUNCTIONS SECTION -----------------------
12320 * Add a Named Destination.
12321 * NOTE: destination names are unique, so only last entry will be saved.
12322 * @param $name (string) Destination name.
12323 * @param $y (float) Y position in user units of the destiantion on the selected page (default = -1 = current position; 0 = page start;).
12324 * @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.
12325 * @param $x (float) X position in user units of the destiantion on the selected page (default = -1 = current position;).
12326 * @return (string) Stripped named destination identifier or false in case of error.
12328 * @author Christian Deligant, Nicola Asuni
12329 * @since 5.9.097 (2011-06-23)
12331 public function setDestination($name, $y=-1, $page='', $x=-1) {
12332 // remove unsupported characters
12333 $name = TCPDF_STATIC
::encodeNameObject($name);
12334 if (TCPDF_STATIC
::empty_string($name)) {
12338 $y = $this->GetY();
12339 } elseif ($y < 0) {
12341 } elseif ($y > $this->h
) {
12345 $x = $this->GetX();
12346 } elseif ($x < 0) {
12348 } elseif ($x > $this->w
) {
12352 if (!empty($page) AND ($page[0] == '*')) {
12353 $page = intval(substr($page, 1));
12354 // this page number will not be changed when moving/add/deleting pages
12357 if (empty($page)) {
12358 $page = $this->PageNo();
12359 if (empty($page)) {
12363 $this->dests
[$name] = array('x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed);
12368 * Return the Named Destination array.
12369 * @return (array) Named Destination array.
12371 * @author Nicola Asuni
12372 * @since 5.9.097 (2011-06-23)
12374 public function getDestination() {
12375 return $this->dests
;
12379 * Insert Named Destinations.
12381 * @author Johannes G\FCntert, Nicola Asuni
12382 * @since 5.9.098 (2011-06-23)
12384 protected function _putdests() {
12385 if (empty($this->dests
)) {
12388 $this->n_dests
= $this->_newobj();
12390 foreach($this->dests
as $name => $o) {
12391 $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
)));
12394 $out .= "\n".'endobj';
12399 * Adds a bookmark - alias for Bookmark().
12400 * @param $txt (string) Bookmark description.
12401 * @param $level (int) Bookmark level (minimum value is 0).
12402 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
12403 * @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.
12404 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic.
12405 * @param $color (array) RGB color array (values from 0 to 255).
12406 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;).
12407 * @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).
12410 public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12411 $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link);
12416 * @param $txt (string) Bookmark description.
12417 * @param $level (int) Bookmark level (minimum value is 0).
12418 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
12419 * @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.
12420 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic.
12421 * @param $color (array) RGB color array (values from 0 to 255).
12422 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;).
12423 * @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).
12425 * @since 2.1.002 (2008-02-12)
12427 public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12431 if (isset($this->outlines
[0])) {
12432 $lastoutline = end($this->outlines
);
12433 $maxlevel = $lastoutline['l'] +
1;
12437 if ($level > $maxlevel) {
12438 $level = $maxlevel;
12441 $y = $this->GetY();
12442 } elseif ($y < 0) {
12444 } elseif ($y > $this->h
) {
12448 $x = $this->GetX();
12449 } elseif ($x < 0) {
12451 } elseif ($x > $this->w
) {
12455 if (!empty($page) AND ($page[0] == '*')) {
12456 $page = intval(substr($page, 1));
12457 // this page number will not be changed when moving/add/deleting pages
12460 if (empty($page)) {
12461 $page = $this->PageNo();
12462 if (empty($page)) {
12466 $this->outlines
[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed, 's' => strtoupper($style), 'c' => $color, 'u' => $link);
12470 * Sort bookmarks for page and key.
12472 * @since 5.9.119 (2011-09-19)
12474 protected function sortBookmarks() {
12475 // get sorting columns
12476 $outline_p = array();
12477 $outline_y = array();
12478 foreach ($this->outlines
as $key => $row) {
12479 $outline_p[$key] = $row['p'];
12480 $outline_k[$key] = $key;
12482 // sort outlines by page and original position
12483 array_multisort($outline_p, SORT_NUMERIC
, SORT_ASC
, $outline_k, SORT_NUMERIC
, SORT_ASC
, $this->outlines
);
12487 * Create a bookmark PDF string.
12489 * @author Olivier Plathey, Nicola Asuni
12490 * @since 2.1.002 (2008-02-12)
12492 protected function _putbookmarks() {
12493 $nb = count($this->outlines
);
12498 $this->sortBookmarks();
12501 foreach ($this->outlines
as $i => $o) {
12503 $parent = $lru[($o['l'] - 1)];
12504 //Set parent and last pointers
12505 $this->outlines
[$i]['parent'] = $parent;
12506 $this->outlines
[$parent]['last'] = $i;
12507 if ($o['l'] > $level) {
12508 //Level increasing: set first pointer
12509 $this->outlines
[$parent]['first'] = $i;
12512 $this->outlines
[$i]['parent'] = $nb;
12514 if (($o['l'] <= $level) AND ($i > 0)) {
12515 //Set prev and next pointers
12516 $prev = $lru[$o['l']];
12517 $this->outlines
[$prev]['next'] = $i;
12518 $this->outlines
[$i]['prev'] = $prev;
12520 $lru[$o['l']] = $i;
12525 $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';
12526 foreach ($this->outlines
as $i => $o) {
12527 $oid = $this->_newobj();
12528 // covert HTML title to string
12529 $title = preg_replace($nltags, "\n", $o['t']);
12530 $title = preg_replace("/[\r]+/si", '', $title);
12531 $title = preg_replace("/[\n]+/si", "\n", $title);
12532 $title = strip_tags($title);
12533 $title = $this->stringTrim($title);
12534 $out = '<</Title '.$this->_textstring($title, $oid);
12535 $out .= ' /Parent '.($n +
$o['parent']).' 0 R';
12536 if (isset($o['prev'])) {
12537 $out .= ' /Prev '.($n +
$o['prev']).' 0 R';
12539 if (isset($o['next'])) {
12540 $out .= ' /Next '.($n +
$o['next']).' 0 R';
12542 if (isset($o['first'])) {
12543 $out .= ' /First '.($n +
$o['first']).' 0 R';
12545 if (isset($o['last'])) {
12546 $out .= ' /Last '.($n +
$o['last']).' 0 R';
12548 if (isset($o['u']) AND !empty($o['u'])) {
12550 if (is_string($o['u'])) {
12551 if ($o['u'][0] == '#') {
12552 // internal destination
12553 $out .= ' /Dest /'.TCPDF_STATIC
::encodeNameObject(substr($o['u'], 1));
12554 } elseif ($o['u'][0] == '%') {
12555 // embedded PDF file
12556 $filename = basename(substr($o['u'], 1));
12557 $out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles
[$filename]['a'].' >> >>';
12558 } elseif ($o['u'][0] == '*') {
12559 // embedded generic file
12560 $filename = basename(substr($o['u'], 1));
12561 $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});';
12562 $out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>';
12564 // external URI link
12565 $out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>';
12567 } elseif (isset($this->links
[$o['u']])) {
12568 // internal link ID
12569 $l = $this->links
[$o['u']];
12570 if (isset($this->page_obj_id
[($l['p'])])) {
12571 $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
)));
12574 } elseif (isset($this->page_obj_id
[($o['p'])])) {
12576 $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
)));
12580 if (!empty($o['s'])) {
12582 if (strpos($o['s'], 'B') !== false) {
12586 if (strpos($o['s'], 'I') !== false) {
12590 $out .= sprintf(' /F %d', $style);
12591 // set bookmark color
12592 if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
12593 $color = array_values($o['c']);
12594 $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
12597 $out .= ' /C [0.0 0.0 0.0]';
12599 $out .= ' /Count 0'; // normally closed item
12601 $out .= "\n".'endobj';
12605 $this->OutlineRoot
= $this->_newobj();
12606 $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n +
$lru[0]).' 0 R >>'."\n".'endobj');
12609 // --- JAVASCRIPT ------------------------------------------------------
12612 * Adds a javascript
12613 * @param $script (string) Javascript code
12615 * @author Johannes G\FCntert, Nicola Asuni
12616 * @since 2.1.002 (2008-02-12)
12618 public function IncludeJS($script) {
12619 $this->javascript
.= $script;
12623 * Adds a javascript object and return object ID
12624 * @param $script (string) Javascript code
12625 * @param $onload (boolean) if true executes this object when opening the document
12626 * @return int internal object ID
12628 * @author Nicola Asuni
12629 * @since 4.8.000 (2009-09-07)
12631 public function addJavascriptObject($script, $onload=false) {
12632 if ($this->pdfa_mode
) {
12633 // javascript is not allowed in PDF/A mode
12637 $this->js_objects
[$this->n
] = array('n' => $this->n
, 'js' => $script, 'onload' => $onload);
12642 * Create a javascript PDF string.
12644 * @author Johannes G\FCntert, Nicola Asuni
12645 * @since 2.1.002 (2008-02-12)
12647 protected function _putjavascript() {
12648 if ($this->pdfa_mode
OR (empty($this->javascript
) AND empty($this->js_objects
))) {
12651 if (strpos($this->javascript
, 'this.addField') > 0) {
12652 if (!$this->ur
['enabled']) {
12653 //$this->setUserRights();
12655 // the following two lines are used to avoid form fields duplication after saving
12656 // The addField method only works when releasing user rights (UR3)
12657 $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
12658 $jsb = "getField('tcpdfdocsaved').value='saved';";
12659 $this->javascript
= $jsa."\n".$this->javascript
."\n".$jsb;
12661 // name tree for javascript
12662 $this->n_js
= '<< /Names [';
12663 if (!empty($this->javascript
)) {
12664 $this->n_js
.= ' (EmbeddedJS) '.($this->n +
1).' 0 R';
12666 if (!empty($this->js_objects
)) {
12667 foreach ($this->js_objects
as $key => $val) {
12668 if ($val['onload']) {
12669 $this->n_js
.= ' (JS'.$key.') '.$key.' 0 R';
12673 $this->n_js
.= ' ] >>';
12674 // default Javascript object
12675 if (!empty($this->javascript
)) {
12676 $obj_id = $this->_newobj();
12677 $out = '<< /S /JavaScript';
12678 $out .= ' /JS '.$this->_textstring($this->javascript
, $obj_id);
12680 $out .= "\n".'endobj';
12683 // additional Javascript objects
12684 if (!empty($this->js_objects
)) {
12685 foreach ($this->js_objects
as $key => $val) {
12686 $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
12693 * Adds a javascript form field.
12694 * @param $type (string) field type
12695 * @param $name (string) field name
12696 * @param $x (int) horizontal position
12697 * @param $y (int) vertical position
12698 * @param $w (int) width
12699 * @param $h (int) height
12700 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12702 * @author Denis Van Nuffelen, Nicola Asuni
12703 * @since 2.1.002 (2008-02-12)
12705 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
12709 // the followind avoid fields duplication after saving the document
12710 $this->javascript
.= "if (getField('tcpdfdocsaved').value != 'saved') {";
12712 $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";
12713 $this->javascript
.= 'f'.$name.'.textSize='.$this->FontSizePt
.";\n";
12714 while (list($key, $val) = each($prop)) {
12715 if (strcmp(substr($key, -5), 'Color') == 0) {
12716 $val = TCPDF_COLORS
::_JScolor($val);
12718 $val = "'".$val."'";
12720 $this->javascript
.= 'f'.$name.'.'.$key.'='.$val.";\n";
12727 $this->javascript
.= '}';
12730 // --- FORM FIELDS -----------------------------------------------------
12735 * Set default properties for form fields.
12736 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12738 * @author Nicola Asuni
12739 * @since 4.8.000 (2009-09-06)
12741 public function setFormDefaultProp($prop=array()) {
12742 $this->default_form_prop
= $prop;
12746 * Return the default properties for form fields.
12747 * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12749 * @author Nicola Asuni
12750 * @since 4.8.000 (2009-09-06)
12752 public function getFormDefaultProp() {
12753 return $this->default_form_prop
;
12757 * Creates a text field
12758 * @param $name (string) field name
12759 * @param $w (float) Width of the rectangle
12760 * @param $h (float) Height of the rectangle
12761 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12762 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
12763 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12764 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12765 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12767 * @author Nicola Asuni
12768 * @since 4.8.000 (2009-09-07)
12770 public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12777 // check page for no-write regions and adapt page margins if necessary
12778 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12780 $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
12783 // get default style
12784 $prop = array_merge($this->getFormDefaultProp(), $prop);
12785 // get annotation data
12786 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
12787 // set default appearance stream
12788 $this->annotation_fonts
[$this->CurrentFont
['fontkey']] = $this->CurrentFont
['i'];
12789 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont
['i'], $this->FontSizePt
, $this->TextColor
);
12790 $popt['da'] = $fontstyle;
12791 // build appearance stream
12792 $popt['ap'] = array();
12793 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12795 if (isset($prop['value']) AND !empty($prop['value'])) {
12796 $text = $prop['value'];
12797 } elseif (isset($opt['v']) AND !empty($opt['v'])) {
12800 $tmpid = $this->startTemplate($w, $h, false);
12802 if (isset($popt['q'])) {
12803 switch ($popt['q']) {
12822 $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12823 $this->endTemplate();
12825 $popt['ap']['n'] .= $this->xobjects
[$tmpid]['outdata'];
12826 unset($this->xobjects
[$tmpid]);
12827 $popt['ap']['n'] .= 'Q EMC';
12829 $opt = array_merge($popt, $opt);
12830 // remove some conflicting options
12832 // set remaining annotation data
12833 $opt['Subtype'] = 'Widget';
12836 // Additional annotation's parameters (check _putannotsobj() method):
12846 //$opt['mk']['bc'];
12847 //$opt['mk']['bg'];
12848 unset($opt['mk']['ca']);
12849 unset($opt['mk']['rc']);
12850 unset($opt['mk']['ac']);
12851 unset($opt['mk']['i']);
12852 unset($opt['mk']['ri']);
12853 unset($opt['mk']['ix']);
12854 unset($opt['mk']['if']);
12855 //$opt['mk']['if']['sw'];
12856 //$opt['mk']['if']['s'];
12857 //$opt['mk']['if']['a'];
12858 //$opt['mk']['if']['fb'];
12859 unset($opt['mk']['tp']);
12868 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12877 * Creates a RadioButton field.
12878 * @param $name (string) Field name.
12879 * @param $w (int) Width of the radio button.
12880 * @param $prop (array) Javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12881 * @param $opt (array) Annotation parameters. Possible values are described on official PDF32000_2008 reference.
12882 * @param $onvalue (string) Value to be returned if selected.
12883 * @param $checked (boolean) Define the initial state.
12884 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12885 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12886 * @param $js (boolean) If true put the field using JavaScript (requires Acrobat Writer to be rendered).
12888 * @author Nicola Asuni
12889 * @since 4.8.000 (2009-09-07)
12891 public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
12898 // check page for no-write regions and adapt page margins if necessary
12899 list($x, $y) = $this->checkPageRegions($w, $x, $y);
12901 $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
12904 if (TCPDF_STATIC
::empty_string($onvalue)) {
12908 $defval = $onvalue;
12913 $font = 'zapfdingbats';
12914 if ($this->pdfa_mode
) {
12915 // all fonts must be embedded
12916 $font = 'pdfa'.$font;
12918 $this->AddFont($font);
12919 $tmpfont = $this->getFontBuffer($font);
12920 // set data for parent group
12921 if (!isset($this->radiobutton_groups
[$this->page
])) {
12922 $this->radiobutton_groups
[$this->page
] = array();
12924 if (!isset($this->radiobutton_groups
[$this->page
][$name])) {
12925 $this->radiobutton_groups
[$this->page
][$name] = array();
12927 $this->radiobutton_groups
[$this->page
][$name]['n'] = $this->n
;
12928 $this->radio_groups
[] = $this->n
;
12930 $kid = ($this->n +
1);
12931 // save object ID to be added on Kids entry on parent object
12932 $this->radiobutton_groups
[$this->page
][$name][] = array('kid' => $kid, 'def' => $defval);
12933 // get default style
12934 $prop = array_merge($this->getFormDefaultProp(), $prop);
12935 $prop['NoToggleToOff'] = 'true';
12936 $prop['Radio'] = 'true';
12937 $prop['borderStyle'] = 'inset';
12938 // get annotation data
12939 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
12940 // set additional default options
12941 $this->annotation_fonts
[$tmpfont['fontkey']] = $tmpfont['i'];
12942 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt
, $this->TextColor
);
12943 $popt['da'] = $fontstyle;
12944 // build appearance stream
12945 $popt['ap'] = array();
12946 $popt['ap']['n'] = array();
12947 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k
);
12948 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt
/ 1000) / $this->k
)) * $this->k
);
12949 $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);
12950 $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);
12951 if (!isset($popt['mk'])) {
12952 $popt['mk'] = array();
12954 $popt['mk']['ca'] = '(l)';
12956 $opt = array_merge($popt, $opt);
12957 // set remaining annotation data
12958 $opt['Subtype'] = 'Widget';
12959 $opt['ft'] = 'Btn';
12961 $opt['v'] = array('/'.$onvalue);
12962 $opt['as'] = $onvalue;
12964 $opt['as'] = 'Off';
12966 // store readonly flag
12967 if (!isset($this->radiobutton_groups
[$this->page
][$name]['#readonly#'])) {
12968 $this->radiobutton_groups
[$this->page
][$name]['#readonly#'] = false;
12970 $this->radiobutton_groups
[$this->page
][$name]['#readonly#'] |= ($opt['f'] & 64);
12971 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
12980 * Creates a List-box field
12981 * @param $name (string) field name
12982 * @param $w (int) width
12983 * @param $h (int) height
12984 * @param $values (array) array containing the list of values.
12985 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12986 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
12987 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12988 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12989 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12991 * @author Nicola Asuni
12992 * @since 4.8.000 (2009-09-07)
12994 public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13001 // check page for no-write regions and adapt page margins if necessary
13002 list($x, $y) = $this->checkPageRegions($h, $x, $y);
13004 $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
13006 foreach ($values as $value) {
13007 if (is_array($value)) {
13008 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
13010 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
13013 $this->javascript
.= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
13016 // get default style
13017 $prop = array_merge($this->getFormDefaultProp(), $prop);
13018 // get annotation data
13019 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
13020 // set additional default values
13021 $this->annotation_fonts
[$this->CurrentFont
['fontkey']] = $this->CurrentFont
['i'];
13022 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont
['i'], $this->FontSizePt
, $this->TextColor
);
13023 $popt['da'] = $fontstyle;
13024 // build appearance stream
13025 $popt['ap'] = array();
13026 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13028 foreach($values as $item) {
13029 if (is_array($item)) {
13030 $text .= $item[1]."\n";
13032 $text .= $item."\n";
13035 $tmpid = $this->startTemplate($w, $h, false);
13036 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13037 $this->endTemplate();
13039 $popt['ap']['n'] .= $this->xobjects
[$tmpid]['outdata'];
13040 unset($this->xobjects
[$tmpid]);
13041 $popt['ap']['n'] .= 'Q EMC';
13043 $opt = array_merge($popt, $opt);
13044 // set remaining annotation data
13045 $opt['Subtype'] = 'Widget';
13048 $opt['opt'] = $values;
13049 unset($opt['mk']['ca']);
13050 unset($opt['mk']['rc']);
13051 unset($opt['mk']['ac']);
13052 unset($opt['mk']['i']);
13053 unset($opt['mk']['ri']);
13054 unset($opt['mk']['ix']);
13055 unset($opt['mk']['if']);
13056 unset($opt['mk']['tp']);
13057 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13066 * Creates a Combo-box field
13067 * @param $name (string) field name
13068 * @param $w (int) width
13069 * @param $h (int) height
13070 * @param $values (array) array containing the list of values.
13071 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13072 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13073 * @param $x (float) Abscissa of the upper-left corner of the rectangle
13074 * @param $y (float) Ordinate of the upper-left corner of the rectangle
13075 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13077 * @author Nicola Asuni
13078 * @since 4.8.000 (2009-09-07)
13080 public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13087 // check page for no-write regions and adapt page margins if necessary
13088 list($x, $y) = $this->checkPageRegions($h, $x, $y);
13090 $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
13092 foreach ($values as $value) {
13093 if (is_array($value)) {
13094 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
13096 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
13099 $this->javascript
.= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
13102 // get default style
13103 $prop = array_merge($this->getFormDefaultProp(), $prop);
13104 $prop['Combo'] = true;
13105 // get annotation data
13106 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
13107 // set additional default options
13108 $this->annotation_fonts
[$this->CurrentFont
['fontkey']] = $this->CurrentFont
['i'];
13109 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont
['i'], $this->FontSizePt
, $this->TextColor
);
13110 $popt['da'] = $fontstyle;
13111 // build appearance stream
13112 $popt['ap'] = array();
13113 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13115 foreach($values as $item) {
13116 if (is_array($item)) {
13117 $text .= $item[1]."\n";
13119 $text .= $item."\n";
13122 $tmpid = $this->startTemplate($w, $h, false);
13123 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13124 $this->endTemplate();
13126 $popt['ap']['n'] .= $this->xobjects
[$tmpid]['outdata'];
13127 unset($this->xobjects
[$tmpid]);
13128 $popt['ap']['n'] .= 'Q EMC';
13130 $opt = array_merge($popt, $opt);
13131 // set remaining annotation data
13132 $opt['Subtype'] = 'Widget';
13135 $opt['opt'] = $values;
13136 unset($opt['mk']['ca']);
13137 unset($opt['mk']['rc']);
13138 unset($opt['mk']['ac']);
13139 unset($opt['mk']['i']);
13140 unset($opt['mk']['ri']);
13141 unset($opt['mk']['ix']);
13142 unset($opt['mk']['if']);
13143 unset($opt['mk']['tp']);
13144 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13153 * Creates a CheckBox field
13154 * @param $name (string) field name
13155 * @param $w (int) width
13156 * @param $checked (boolean) define the initial state.
13157 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13158 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13159 * @param $onvalue (string) value to be returned if selected.
13160 * @param $x (float) Abscissa of the upper-left corner of the rectangle
13161 * @param $y (float) Ordinate of the upper-left corner of the rectangle
13162 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13164 * @author Nicola Asuni
13165 * @since 4.8.000 (2009-09-07)
13167 public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
13174 // check page for no-write regions and adapt page margins if necessary
13175 list($x, $y) = $this->checkPageRegions($w, $x, $y);
13177 $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
13180 if (!isset($prop['value'])) {
13181 $prop['value'] = array('Yes');
13183 // get default style
13184 $prop = array_merge($this->getFormDefaultProp(), $prop);
13185 $prop['borderStyle'] = 'inset';
13186 // get annotation data
13187 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
13188 // set additional default options
13189 $font = 'zapfdingbats';
13190 if ($this->pdfa_mode
) {
13191 // all fonts must be embedded
13192 $font = 'pdfa'.$font;
13194 $this->AddFont($font);
13195 $tmpfont = $this->getFontBuffer($font);
13196 $this->annotation_fonts
[$tmpfont['fontkey']] = $tmpfont['i'];
13197 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt
, $this->TextColor
);
13198 $popt['da'] = $fontstyle;
13199 // build appearance stream
13200 $popt['ap'] = array();
13201 $popt['ap']['n'] = array();
13202 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k
);
13203 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt
/ 1000) / $this->k
)) * $this->k
);
13204 $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);
13205 $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);
13207 $opt = array_merge($popt, $opt);
13208 // set remaining annotation data
13209 $opt['Subtype'] = 'Widget';
13210 $opt['ft'] = 'Btn';
13212 if (TCPDF_STATIC
::empty_string($onvalue)) {
13215 $opt['opt'] = array($onvalue);
13217 $opt['v'] = array('/Yes');
13218 $opt['as'] = 'Yes';
13220 $opt['v'] = array('/Off');
13221 $opt['as'] = 'Off';
13223 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
13232 * Creates a button field
13233 * @param $name (string) field name
13234 * @param $w (int) width
13235 * @param $h (int) height
13236 * @param $caption (string) caption.
13237 * @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.
13238 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13239 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13240 * @param $x (float) Abscissa of the upper-left corner of the rectangle
13241 * @param $y (float) Ordinate of the upper-left corner of the rectangle
13242 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13244 * @author Nicola Asuni
13245 * @since 4.8.000 (2009-09-07)
13247 public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13254 // check page for no-write regions and adapt page margins if necessary
13255 list($x, $y) = $this->checkPageRegions($h, $x, $y);
13257 $this->_addfield('button', $name, $this->x
, $this->y
, $w, $h, $prop);
13258 $this->javascript
.= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
13259 $this->javascript
.= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
13260 $this->javascript
.= 'f'.$name.".highlight='push';\n";
13261 $this->javascript
.= 'f'.$name.".print=false;\n";
13264 // get default style
13265 $prop = array_merge($this->getFormDefaultProp(), $prop);
13266 $prop['Pushbutton'] = 'true';
13267 $prop['highlight'] = 'push';
13268 $prop['display'] = 'display.noPrint';
13269 // get annotation data
13270 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
13271 $this->annotation_fonts
[$this->CurrentFont
['fontkey']] = $this->CurrentFont
['i'];
13272 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont
['i'], $this->FontSizePt
, $this->TextColor
);
13273 $popt['da'] = $fontstyle;
13274 // build appearance stream
13275 $popt['ap'] = array();
13276 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13277 $tmpid = $this->startTemplate($w, $h, false);
13278 $bw = (2 / $this->k
); // border width
13280 'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13281 'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
13282 'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13283 'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
13284 $this->SetFillColor(204);
13285 $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
13286 $this->endTemplate();
13288 $popt['ap']['n'] .= $this->xobjects
[$tmpid]['outdata'];
13289 unset($this->xobjects
[$tmpid]);
13290 $popt['ap']['n'] .= 'Q EMC';
13291 // set additional default options
13292 if (!isset($popt['mk'])) {
13293 $popt['mk'] = array();
13295 $ann_obj_id = ($this->n +
1);
13296 if (!empty($action) AND !is_array($action)) {
13297 $ann_obj_id = ($this->n +
2);
13299 $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
13300 $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
13301 $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
13303 $opt = array_merge($popt, $opt);
13304 // set remaining annotation data
13305 $opt['Subtype'] = 'Widget';
13306 $opt['ft'] = 'Btn';
13307 $opt['t'] = $caption;
13309 if (!empty($action)) {
13310 if (is_array($action)) {
13311 // form action options as on section 12.7.5 of PDF32000_2008.
13312 $opt['aa'] = '/D <<';
13313 $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
13314 foreach ($action AS $key => $val) {
13315 if (($key == 'S') AND in_array($val, $bmode)) {
13316 $opt['aa'] .= ' /S /'.$val;
13317 } elseif (($key == 'F') AND (!empty($val))) {
13318 $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
13319 } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
13320 $opt['aa'] .= ' /Fields [';
13321 foreach ($val AS $field) {
13322 $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
13325 } elseif (($key == 'Flags')) {
13327 if (is_array($val)) {
13328 foreach ($val AS $flag) {
13330 case 'Include/Exclude': {
13334 case 'IncludeNoValueFields': {
13338 case 'ExportFormat': {
13342 case 'GetMethod': {
13346 case 'SubmitCoordinates': {
13354 case 'IncludeAppendSaves': {
13358 case 'IncludeAnnotations': {
13362 case 'SubmitPDF': {
13366 case 'CanonicalFormat': {
13370 case 'ExclNonUserAnnots': {
13378 case 'EmbedForm': {
13385 $ff = intval($val);
13387 $opt['aa'] .= ' /Flags '.$ff;
13390 $opt['aa'] .= ' >>';
13392 // Javascript action or raw action command
13393 $js_obj_id = $this->addJavascriptObject($action);
13394 $opt['aa'] = '/D '.$js_obj_id.' 0 R';
13397 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13405 // --- END FORMS FIELDS ------------------------------------------------
13408 * Add certification signature (DocMDP or UR3)
13409 * You can set only one signature type
13411 * @author Nicola Asuni
13412 * @since 4.6.008 (2009-05-07)
13414 protected function _putsignature() {
13415 if ((!$this->sign
) OR (!isset($this->signature_data
['cert_type']))) {
13418 $sigobjid = ($this->sig_obj_id +
1);
13419 $out = $this->_getobj($sigobjid)."\n";
13420 $out .= '<< /Type /Sig';
13421 $out .= ' /Filter /Adobe.PPKLite';
13422 $out .= ' /SubFilter /adbe.pkcs7.detached';
13423 $out .= ' '.TCPDF_STATIC
::$byterange_string;
13424 $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length
).'>';
13425 if (empty($this->signature_data
['approval']) OR ($this->signature_data
['approval'] != 'A')) {
13426 $out .= ' /Reference ['; // array of signature reference dictionaries
13427 $out .= ' << /Type /SigRef';
13428 if ($this->signature_data
['cert_type'] > 0) {
13429 $out .= ' /TransformMethod /DocMDP';
13430 $out .= ' /TransformParams <<';
13431 $out .= ' /Type /TransformParams';
13432 $out .= ' /P '.$this->signature_data
['cert_type'];
13433 $out .= ' /V /1.2';
13435 $out .= ' /TransformMethod /UR3';
13436 $out .= ' /TransformParams <<';
13437 $out .= ' /Type /TransformParams';
13438 $out .= ' /V /2.2';
13439 if (!TCPDF_STATIC
::empty_string($this->ur
['document'])) {
13440 $out .= ' /Document['.$this->ur
['document'].']';
13442 if (!TCPDF_STATIC
::empty_string($this->ur
['form'])) {
13443 $out .= ' /Form['.$this->ur
['form'].']';
13445 if (!TCPDF_STATIC
::empty_string($this->ur
['signature'])) {
13446 $out .= ' /Signature['.$this->ur
['signature'].']';
13448 if (!TCPDF_STATIC
::empty_string($this->ur
['annots'])) {
13449 $out .= ' /Annots['.$this->ur
['annots'].']';
13451 if (!TCPDF_STATIC
::empty_string($this->ur
['ef'])) {
13452 $out .= ' /EF['.$this->ur
['ef'].']';
13454 if (!TCPDF_STATIC
::empty_string($this->ur
['formex'])) {
13455 $out .= ' /FormEX['.$this->ur
['formex'].']';
13458 $out .= ' >>'; // close TransformParams
13459 // optional digest data (values must be calculated and replaced later)
13460 //$out .= ' /Data ********** 0 R';
13461 //$out .= ' /DigestMethod/MD5';
13462 //$out .= ' /DigestLocation[********** 34]';
13463 //$out .= ' /DigestValue<********************************>';
13465 $out .= ' ]'; // end of reference
13467 if (isset($this->signature_data
['info']['Name']) AND !TCPDF_STATIC
::empty_string($this->signature_data
['info']['Name'])) {
13468 $out .= ' /Name '.$this->_textstring($this->signature_data
['info']['Name'], $sigobjid);
13470 if (isset($this->signature_data
['info']['Location']) AND !TCPDF_STATIC
::empty_string($this->signature_data
['info']['Location'])) {
13471 $out .= ' /Location '.$this->_textstring($this->signature_data
['info']['Location'], $sigobjid);
13473 if (isset($this->signature_data
['info']['Reason']) AND !TCPDF_STATIC
::empty_string($this->signature_data
['info']['Reason'])) {
13474 $out .= ' /Reason '.$this->_textstring($this->signature_data
['info']['Reason'], $sigobjid);
13476 if (isset($this->signature_data
['info']['ContactInfo']) AND !TCPDF_STATIC
::empty_string($this->signature_data
['info']['ContactInfo'])) {
13477 $out .= ' /ContactInfo '.$this->_textstring($this->signature_data
['info']['ContactInfo'], $sigobjid);
13479 $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp
);
13481 $out .= "\n".'endobj';
13486 * Set User's Rights for PDF Reader
13487 * WARNING: This is experimental and currently do not work.
13488 * Check the PDF Reference 8.7.1 Transform Methods,
13489 * Table 8.105 Entries in the UR transform parameters dictionary
13490 * @param $enable (boolean) if true enable user's rights on PDF reader
13491 * @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.
13492 * @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.
13493 * @param $form (string) Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
13494 * @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.
13495 * @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
13496 Names specifying additional embedded-files-related usage rights for the document.
13497 * @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.
13499 * @author Nicola Asuni
13500 * @since 2.9.000 (2008-03-26)
13502 public function setUserRights(
13504 $document='/FullSave',
13505 $annots='/Create/Delete/Modify/Copy/Import/Export',
13506 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
13507 $signature='/Modify',
13508 $ef='/Create/Delete/Modify/Import',
13510 $this->ur
['enabled'] = $enable;
13511 $this->ur
['document'] = $document;
13512 $this->ur
['annots'] = $annots;
13513 $this->ur
['form'] = $form;
13514 $this->ur
['signature'] = $signature;
13515 $this->ur
['ef'] = $ef;
13516 $this->ur
['formex'] = $formex;
13517 if (!$this->sign
) {
13518 $this->setSignature('', '', '', '', 0, array());
13523 * Enable document signature (requires the OpenSSL Library).
13524 * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
13525 * To create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13526 * To export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13527 * To convert pfx certificate to pem: openssl pkcs12 -in tcpdf.pfx -out tcpdf.crt -nodes
13528 * @param $signing_cert (mixed) signing certificate (string or filename prefixed with 'file://')
13529 * @param $private_key (mixed) private key (string or filename prefixed with 'file://')
13530 * @param $private_key_password (string) password
13531 * @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.
13532 * @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.
13533 * @param $info (array) array of option information: Name, Location, Reason, ContactInfo.
13534 * @param $approval (string) Enable approval signature eg. for PDF incremental update
13536 * @author Nicola Asuni
13537 * @since 4.6.005 (2009-04-24)
13539 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='') {
13540 // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13541 // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13542 // to convert pfx certificate to pem: openssl
13543 // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
13544 $this->sign
= true;
13546 $this->sig_obj_id
= $this->n
; // signature widget
13547 ++
$this->n
; // signature object ($this->sig_obj_id + 1)
13548 $this->signature_data
= array();
13549 if (strlen($signing_cert) == 0) {
13550 $this->Error('Please provide a certificate file and password!');
13552 if (strlen($private_key) == 0) {
13553 $private_key = $signing_cert;
13555 $this->signature_data
['signcert'] = $signing_cert;
13556 $this->signature_data
['privkey'] = $private_key;
13557 $this->signature_data
['password'] = $private_key_password;
13558 $this->signature_data
['extracerts'] = $extracerts;
13559 $this->signature_data
['cert_type'] = $cert_type;
13560 $this->signature_data
['info'] = $info;
13561 $this->signature_data
['approval'] = $approval;
13565 * Set the digital signature appearance (a cliccable rectangle area to get signature properties)
13566 * @param $x (float) Abscissa of the upper-left corner.
13567 * @param $y (float) Ordinate of the upper-left corner.
13568 * @param $w (float) Width of the signature area.
13569 * @param $h (float) Height of the signature area.
13570 * @param $page (int) option page number (if < 0 the current page is used).
13571 * @param $name (string) Name of the signature.
13573 * @author Nicola Asuni
13574 * @since 5.3.011 (2010-06-17)
13576 public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13577 $this->signature_appearance
= $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13581 * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties)
13582 * @param $x (float) Abscissa of the upper-left corner.
13583 * @param $y (float) Ordinate of the upper-left corner.
13584 * @param $w (float) Width of the signature area.
13585 * @param $h (float) Height of the signature area.
13586 * @param $page (int) option page number (if < 0 the current page is used).
13587 * @param $name (string) Name of the signature.
13589 * @author Nicola Asuni
13590 * @since 5.9.101 (2011-07-06)
13592 public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13594 $this->empty_signature_appearance
[] = array('objid' => $this->n
) +
$this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13598 * Get the array that defines the signature appearance (page and rectangle coordinates).
13599 * @param $x (float) Abscissa of the upper-left corner.
13600 * @param $y (float) Ordinate of the upper-left corner.
13601 * @param $w (float) Width of the signature area.
13602 * @param $h (float) Height of the signature area.
13603 * @param $page (int) option page number (if < 0 the current page is used).
13604 * @param $name (string) Name of the signature.
13605 * @return (array) Array defining page and rectangle coordinates of signature appearance.
13607 * @author Nicola Asuni
13608 * @since 5.9.101 (2011-07-06)
13610 protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13612 if (($page < 1) OR ($page > $this->numpages
)) {
13613 $sigapp['page'] = $this->page
;
13615 $sigapp['page'] = intval($page);
13617 if (empty($name)) {
13618 $sigapp['name'] = 'Signature';
13620 $sigapp['name'] = $name;
13622 $a = $x * $this->k
;
13623 $b = $this->pagedim
[($sigapp['page'])]['h'] - (($y +
$h) * $this->k
);
13624 $c = $w * $this->k
;
13625 $d = $h * $this->k
;
13626 $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a +
$c), ($b +
$d));
13631 * Enable document timestamping (requires the OpenSSL Library).
13632 * The trusted timestamping improve document security that means that no one should be able to change the document once it has been recorded.
13633 * Use with digital signature only!
13634 * @param $tsa_host (string) Time Stamping Authority (TSA) server (prefixed with 'https://')
13635 * @param $tsa_username (string) Specifies the username for TSA authorization (optional) OR specifies the TSA authorization PEM file (see: example_66.php, optional)
13636 * @param $tsa_password (string) Specifies the password for TSA authorization (optional)
13637 * @param $tsa_cert (string) Specifies the location of TSA certificate for authorization (optional for cURL)
13639 * @author Richard Stockinger
13640 * @since 6.0.090 (2014-06-16)
13642 public function setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='') {
13643 $this->tsa_data
= array();
13644 if (!function_exists('curl_init')) {
13645 $this->Error('Please enable cURL PHP extension!');
13647 if (strlen($tsa_host) == 0) {
13648 $this->Error('Please specify the host of Time Stamping Authority (TSA)!');
13650 $this->tsa_data
['tsa_host'] = $tsa_host;
13651 if (is_file($tsa_username)) {
13652 $this->tsa_data
['tsa_auth'] = $tsa_username;
13654 $this->tsa_data
['tsa_username'] = $tsa_username;
13656 $this->tsa_data
['tsa_password'] = $tsa_password;
13657 $this->tsa_data
['tsa_cert'] = $tsa_cert;
13658 $this->tsa_timestamp
= true;
13662 * NOT YET IMPLEMENTED
13663 * Request TSA for a timestamp
13664 * @param $signature (string) Digital signature as binary string
13665 * @return (string) Timestamped digital signature
13667 * @author Richard Stockinger
13668 * @since 6.0.090 (2014-06-16)
13670 protected function applyTSA($signature) {
13671 if (!$this->tsa_timestamp
) {
13674 //@TODO: implement this feature
13679 * Create a new page group.
13680 * NOTE: call this function before calling AddPage()
13681 * @param $page (int) starting group page (leave empty for next page).
13683 * @since 3.0.000 (2008-03-27)
13685 public function startPageGroup($page='') {
13686 if (empty($page)) {
13687 $page = $this->page +
1;
13689 $this->newpagegroup
[$page] = sizeof($this->newpagegroup
) +
1;
13693 * Set the starting page number.
13694 * @param $num (int) Starting page number.
13695 * @since 5.9.093 (2011-06-16)
13698 public function setStartingPageNumber($num=1) {
13699 $this->starting_page_number
= max(0, intval($num));
13703 * Returns the string alias used right align page numbers.
13704 * If the current font is unicode type, the returned string wil contain an additional open curly brace.
13706 * @since 5.9.099 (2011-06-27)
13709 public function getAliasRightShift() {
13710 // calculate aproximatively the ratio between widths of aliases and replacements.
13711 $ref = '{'.TCPDF_STATIC::$alias_right_shift.'}{'.TCPDF_STATIC
::$alias_tot_pages
.'}{'.TCPDF_STATIC::$alias_num_page.'}';
13712 $rep = str_repeat(' ', $this->GetNumChars($ref));
13713 $wrep = $this->GetStringWidth($rep);
13715 $wdiff = max(1, ($this->GetStringWidth($ref) / $wrep));
13719 $sdiff = sprintf('%F', $wdiff);
13720 $alias = TCPDF_STATIC
::$alias_right_shift.$sdiff.'}';
13721 if ($this->isUnicodeFont()) {
13722 $alias = '{'.$alias;
13728 * Returns the string alias used for the total number of pages.
13729 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13730 * This alias will be replaced by the total number of pages in the document.
13732 * @since 4.0.018 (2008-08-08)
13735 public function getAliasNbPages() {
13736 if ($this->isUnicodeFont()) {
13737 return '{'.TCPDF_STATIC::$alias_tot_pages.'}';
13739 return TCPDF_STATIC
::$alias_tot_pages;
13743 * Returns the string alias used for the page number.
13744 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13745 * This alias will be replaced by the page number.
13747 * @since 4.5.000 (2009-01-02)
13750 public function getAliasNumPage() {
13751 if ($this->isUnicodeFont()) {
13752 return '{'.TCPDF_STATIC::$alias_num_page.'}';
13754 return TCPDF_STATIC
::$alias_num_page;
13758 * Return the alias for the total number of pages in the current page group.
13759 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13760 * This alias will be replaced by the total number of pages in this group.
13761 * @return alias of the current page group
13763 * @since 3.0.000 (2008-03-27)
13765 public function getPageGroupAlias() {
13766 if ($this->isUnicodeFont()) {
13767 return '{'.TCPDF_STATIC::$alias_group_tot_pages.'}';
13769 return TCPDF_STATIC
::$alias_group_tot_pages;
13773 * Return the alias for the page number on the current page group.
13774 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13775 * This alias will be replaced by the page number (relative to the belonging group).
13776 * @return alias of the current page group
13778 * @since 4.5.000 (2009-01-02)
13780 public function getPageNumGroupAlias() {
13781 if ($this->isUnicodeFont()) {
13782 return '{'.TCPDF_STATIC::$alias_group_num_page.'}';
13784 return TCPDF_STATIC
::$alias_group_num_page;
13788 * Return the current page in the group.
13789 * @return current page in the group
13791 * @since 3.0.000 (2008-03-27)
13793 public function getGroupPageNo() {
13794 return $this->pagegroups
[$this->currpagegroup
];
13798 * Returns the current group page number formatted as a string.
13800 * @since 4.3.003 (2008-11-18)
13801 * @see PaneNo(), formatPageNumber()
13803 public function getGroupPageNoFormatted() {
13804 return TCPDF_STATIC
::formatPageNumber($this->getGroupPageNo());
13808 * Returns the current page number formatted as a string.
13810 * @since 4.2.005 (2008-11-06)
13811 * @see PaneNo(), formatPageNumber()
13813 public function PageNoFormatted() {
13814 return TCPDF_STATIC
::formatPageNumber($this->PageNo());
13820 * @since 3.0.000 (2008-03-27)
13822 protected function _putocg() {
13823 if (empty($this->pdflayers
)) {
13826 foreach ($this->pdflayers
as $key => $layer) {
13827 $this->pdflayers
[$key]['objid'] = $this->_newobj();
13828 $out = '<< /Type /OCG';
13829 $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers
[$key]['objid']);
13830 $out .= ' /Usage <<';
13831 if (isset($layer['print']) AND ($layer['print'] !== NULL)) {
13832 $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>';
13834 $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>';
13836 $out .= "\n".'endobj';
13842 * Start a new pdf layer.
13843 * @param $name (string) Layer name (only a-z letters and numbers). Leave empty for automatic name.
13844 * @param $print (boolean|null) Set to TRUE to print this layer, FALSE to not print and NULL to not set this option
13845 * @param $view (boolean) Set to true to view this layer.
13846 * @param $lock (boolean) If true lock the layer
13848 * @since 5.9.102 (2011-07-13)
13850 public function startLayer($name='', $print=true, $view=true, $lock=true) {
13851 if ($this->state
!= 2) {
13854 $layer = sprintf('LYR%03d', (count($this->pdflayers
) +
1));
13855 if (empty($name)) {
13858 $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
13860 $this->pdflayers
[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view, 'lock' => $lock);
13861 $this->openMarkedContent
= true;
13862 $this->_out('/OC /'.$layer.' BDC');
13866 * End the current PDF layer.
13868 * @since 5.9.102 (2011-07-13)
13870 public function endLayer() {
13871 if ($this->state
!= 2) {
13874 if ($this->openMarkedContent
) {
13875 // close existing open marked-content layer
13876 $this->_out('EMC');
13877 $this->openMarkedContent
= false;
13882 * Set the visibility of the successive elements.
13883 * This can be useful, for instance, to put a background
13884 * image or color that will show on screen but won't print.
13885 * @param $v (string) visibility mode. Legal values are: all, print, screen or view.
13887 * @since 3.0.000 (2008-03-27)
13889 public function setVisibility($v) {
13890 if ($this->state
!= 2) {
13896 $this->startLayer('Print', true, false);
13901 $this->startLayer('View', false, true);
13909 $this->Error('Incorrect visibility: '.$v);
13916 * Add transparency parameters to the current extgstate
13917 * @param $parms (array) parameters
13918 * @return the number of extgstates
13920 * @since 3.0.000 (2008-03-27)
13922 protected function addExtGState($parms) {
13923 if ($this->pdfa_mode
) {
13924 // transparencies are not allowed in PDF/A mode
13927 // check if this ExtGState already exist
13928 foreach ($this->extgstates
as $i => $ext) {
13929 if ($ext['parms'] == $parms) {
13930 if ($this->inxobj
) {
13931 // we are inside an XObject template
13932 $this->xobjects
[$this->xobjid
]['extgstates'][$i] = $ext;
13934 // return reference to existing ExtGState
13938 $n = (count($this->extgstates
) +
1);
13939 $this->extgstates
[$n] = array('parms' => $parms);
13940 if ($this->inxobj
) {
13941 // we are inside an XObject template
13942 $this->xobjects
[$this->xobjid
]['extgstates'][$n] = $this->extgstates
[$n];
13949 * @param $gs (array) extgstate
13951 * @since 3.0.000 (2008-03-27)
13953 protected function setExtGState($gs) {
13954 if ($this->pdfa_mode
OR ($this->state
!= 2)) {
13955 // transparency is not allowed in PDF/A mode
13958 $this->_out(sprintf('/GS%d gs', $gs));
13962 * Put extgstates for object transparency
13964 * @since 3.0.000 (2008-03-27)
13966 protected function _putextgstates() {
13967 foreach ($this->extgstates
as $i => $ext) {
13968 $this->extgstates
[$i]['n'] = $this->_newobj();
13969 $out = '<< /Type /ExtGState';
13970 foreach ($ext['parms'] as $k => $v) {
13971 if (is_float($v)) {
13972 $v = sprintf('%F', $v);
13973 } elseif ($v === true) {
13975 } elseif ($v === false) {
13978 $out .= ' /'.$k.' '.$v;
13981 $out .= "\n".'endobj';
13987 * Set overprint mode for stroking (OP) and non-stroking (op) painting operations.
13988 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
13989 * @param $stroking (boolean) If true apply overprint for stroking operations.
13990 * @param $nonstroking (boolean) If true apply overprint for painting operations other than stroking.
13991 * @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).
13993 * @since 5.9.152 (2012-03-23)
13995 public function setOverprint($stroking=true, $nonstroking='', $mode=0) {
13996 if ($this->state
!= 2) {
13999 $stroking = $stroking ? true : false;
14000 if (TCPDF_STATIC
::empty_string($nonstroking)) {
14001 // default value if not set
14002 $nonstroking = $stroking;
14004 $nonstroking = $nonstroking ? true : false;
14006 if (($mode != 0) AND ($mode != 1)) {
14009 $this->overprint
= array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode);
14010 $gs = $this->addExtGState($this->overprint
);
14011 $this->setExtGState($gs);
14015 * Get the overprint mode array (OP, op, OPM).
14016 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
14019 * @since 5.9.152 (2012-03-23)
14021 public function getOverprint() {
14022 return $this->overprint
;
14026 * Set alpha for stroking (CA) and non-stroking (ca) operations.
14027 * @param $stroking (float) Alpha value for stroking operations: real value from 0 (transparent) to 1 (opaque).
14028 * @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
14029 * @param $nonstroking (float) Alpha value for non-stroking operations: real value from 0 (transparent) to 1 (opaque).
14030 * @param $ais (boolean)
14032 * @since 3.0.000 (2008-03-27)
14034 public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) {
14035 if ($this->pdfa_mode
) {
14036 // transparency is not allowed in PDF/A mode
14039 $stroking = floatval($stroking);
14040 if (TCPDF_STATIC
::empty_string($nonstroking)) {
14041 // default value if not set
14042 $nonstroking = $stroking;
14044 $nonstroking = floatval($nonstroking);
14046 if ($bm[0] == '/') {
14047 // remove trailing slash
14048 $bm = substr($bm, 1);
14050 if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
14053 $ais = $ais ? true : false;
14054 $this->alpha
= array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais);
14055 $gs = $this->addExtGState($this->alpha
);
14056 $this->setExtGState($gs);
14060 * Get the alpha mode array (CA, ca, BM, AIS).
14061 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
14064 * @since 5.9.152 (2012-03-23)
14066 public function getAlpha() {
14067 return $this->alpha
;
14071 * Set the default JPEG compression quality (1-100)
14072 * @param $quality (int) JPEG quality, integer between 1 and 100
14074 * @since 3.0.000 (2008-03-27)
14076 public function setJPEGQuality($quality) {
14077 if (($quality < 1) OR ($quality > 100)) {
14080 $this->jpeg_quality
= intval($quality);
14084 * Set the default number of columns in a row for HTML tables.
14085 * @param $cols (int) number of columns
14087 * @since 3.0.014 (2008-06-04)
14089 public function setDefaultTableColumns($cols=4) {
14090 $this->default_table_columns
= intval($cols);
14094 * Set the height of the cell (line height) respect the font height.
14095 * @param $h (int) cell proportion respect font height (typical value = 1.25).
14097 * @since 3.0.014 (2008-06-04)
14099 public function setCellHeightRatio($h) {
14100 $this->cell_height_ratio
= $h;
14104 * return the height of cell repect font height.
14106 * @since 4.0.012 (2008-07-24)
14108 public function getCellHeightRatio() {
14109 return $this->cell_height_ratio
;
14113 * Set the PDF version (check PDF reference for valid values).
14114 * @param $version (string) PDF document version.
14116 * @since 3.1.000 (2008-06-09)
14118 public function setPDFVersion($version='1.7') {
14119 if ($this->pdfa_mode
) {
14121 $this->PDFVersion
= '1.4';
14123 $this->PDFVersion
= $version;
14128 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
14129 * (see Section 8.1 of PDF reference, "Viewer Preferences").
14130 * <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>
14131 * @param $preferences (array) array of options.
14132 * @author Nicola Asuni
14134 * @since 3.1.000 (2008-06-09)
14136 public function setViewerPreferences($preferences) {
14137 $this->viewer_preferences
= $preferences;
14141 * Paints color transition registration bars
14142 * @param $x (float) abscissa of the top left corner of the rectangle.
14143 * @param $y (float) ordinate of the top left corner of the rectangle.
14144 * @param $w (float) width of the rectangle.
14145 * @param $h (float) height of the rectangle.
14146 * @param $transition (boolean) if true prints tcolor transitions to white.
14147 * @param $vertical (boolean) if true prints bar vertically.
14148 * @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.
14149 * @author Nicola Asuni
14150 * @since 4.9.000 (2010-03-26)
14153 public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
14154 if (strpos($colors, 'ALLSPOT') !== false) {
14155 // expand spot colors
14157 foreach ($this->spot_colors
as $spot_color_name => $v) {
14158 $spot_colors .= ','.$spot_color_name;
14160 if (!empty($spot_colors)) {
14161 $spot_colors = substr($spot_colors, 1);
14162 $colors = str_replace('ALLSPOT', $spot_colors, $colors);
14164 $colors = str_replace('ALLSPOT', 'NONE', $colors);
14167 $bars = explode(',', $colors);
14168 $numbars = count($bars); // number of bars to print
14169 if ($numbars <= 0) {
14172 // set bar measures
14174 $coords = array(0, 0, 0, 1);
14175 $wb = $w / $numbars; // bar width
14176 $hb = $h; // bar height
14177 $xd = $wb; // delta x
14178 $yd = 0; // delta y
14180 $coords = array(1, 0, 0, 0);
14181 $wb = $w; // bar width
14182 $hb = $h / $numbars; // bar height
14183 $xd = 0; // delta x
14184 $yd = $hb; // delta y
14188 foreach ($bars as $col) {
14190 // set transition colors
14191 case 'A': { // BLACK (GRAYSCALE)
14192 $col_a = array(255);
14196 case 'W': { // WHITE (GRAYSCALE)
14198 $col_b = array(255);
14201 case 'R': { // RED (RGB)
14202 $col_a = array(255,255,255);
14203 $col_b = array(255,0,0);
14206 case 'G': { // GREEN (RGB)
14207 $col_a = array(255,255,255);
14208 $col_b = array(0,255,0);
14211 case 'B': { // BLUE (RGB)
14212 $col_a = array(255,255,255);
14213 $col_b = array(0,0,255);
14216 case 'C': { // CYAN (CMYK)
14217 $col_a = array(0,0,0,0);
14218 $col_b = array(100,0,0,0);
14221 case 'M': { // MAGENTA (CMYK)
14222 $col_a = array(0,0,0,0);
14223 $col_b = array(0,100,0,0);
14226 case 'Y': { // YELLOW (CMYK)
14227 $col_a = array(0,0,0,0);
14228 $col_b = array(0,0,100,0);
14231 case 'K': { // KEY - BLACK (CMYK)
14232 $col_a = array(0,0,0,0);
14233 $col_b = array(0,0,0,100);
14236 case 'RGB': { // BLACK REGISTRATION (RGB)
14237 $col_a = array(255,255,255);
14238 $col_b = array(0,0,0);
14241 case 'CMYK': { // BLACK REGISTRATION (CMYK)
14242 $col_a = array(0,0,0,0);
14243 $col_b = array(100,100,100,100);
14246 case 'ALL': { // SPOT COLOR REGISTRATION
14247 $col_a = array(0,0,0,0,'None');
14248 $col_b = array(100,100,100,100,'All');
14251 case 'NONE': { // SKIP THIS COLOR
14252 $col_a = array(0,0,0,0,'None');
14253 $col_b = array(0,0,0,0,'None');
14256 default: { // SPECIFIC SPOT COLOR NAME
14257 $col_a = array(0,0,0,0,'None');
14258 $col_b = TCPDF_COLORS
::getSpotColor($col, $this->spot_colors
);
14259 if ($col_b === false) {
14260 // in case of error defaults to the registration color
14261 $col_b = array(100,100,100,100,'All');
14266 if ($col != 'NONE') {
14269 $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
14271 $this->SetFillColorArray($col_b);
14272 // colored rectangle
14273 $this->Rect($xb, $yb, $wb, $hb, 'F', array());
14282 * Paints crop marks.
14283 * @param $x (float) abscissa of the crop mark center.
14284 * @param $y (float) ordinate of the crop mark center.
14285 * @param $w (float) width of the crop mark.
14286 * @param $h (float) height of the crop mark.
14287 * @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.
14288 * @param $color (array) crop mark color (default spot registration color).
14289 * @author Nicola Asuni
14290 * @since 4.9.000 (2010-03-26)
14293 public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(100,100,100,100,'All')) {
14294 $this->SetLineStyle(array('width' => (0.5 / $this->k
), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
14295 $type = strtoupper($type);
14296 $type = preg_replace('/[^A-Z\-\,]*/', '', $type);
14297 // split type in single components
14298 $type = str_replace('-', ',', $type);
14299 $type = str_replace('TL', 'T,L', $type);
14300 $type = str_replace('TR', 'T,R', $type);
14301 $type = str_replace('BL', 'F,L', $type);
14302 $type = str_replace('BR', 'F,R', $type);
14303 $type = str_replace('A', 'T,L', $type);
14304 $type = str_replace('B', 'T,R', $type);
14305 $type = str_replace('T,RO', 'BO', $type);
14306 $type = str_replace('C', 'F,L', $type);
14307 $type = str_replace('D', 'F,R', $type);
14308 $crops = explode(',', strtoupper($type));
14309 // remove duplicates
14310 $crops = array_unique($crops);
14311 $dw = ($w / 4); // horizontal space to leave before the intersection point
14312 $dh = ($h / 4); // vertical space to leave before the intersection point
14313 foreach ($crops as $crop) {
14348 $this->Line($x1, $y1, $x2, $y2);
14353 * Paints a registration mark
14354 * @param $x (float) abscissa of the registration mark center.
14355 * @param $y (float) ordinate of the registration mark center.
14356 * @param $r (float) radius of the crop mark.
14357 * @param $double (boolean) if true print two concentric crop marks.
14358 * @param $cola (array) crop mark color (default spot registration color 'All').
14359 * @param $colb (array) second crop mark color (default spot registration color 'None').
14360 * @author Nicola Asuni
14361 * @since 4.9.000 (2010-03-26)
14364 public function registrationMark($x, $y, $r, $double=false, $cola=array(100,100,100,100,'All'), $colb=array(0,0,0,0,'None')) {
14365 $line_style = array('width' => max((0.5 / $this->k
),($r / 30)), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
14366 $this->SetFillColorArray($cola);
14367 $this->PieSector($x, $y, $r, 90, 180, 'F');
14368 $this->PieSector($x, $y, $r, 270, 360, 'F');
14369 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14372 $this->SetFillColorArray($colb);
14373 $this->PieSector($x, $y, $ri, 90, 180, 'F');
14374 $this->PieSector($x, $y, $ri, 270, 360, 'F');
14375 $this->SetFillColorArray($cola);
14376 $this->PieSector($x, $y, $ri, 0, 90, 'F');
14377 $this->PieSector($x, $y, $ri, 180, 270, 'F');
14378 $this->Circle($x, $y, $ri, 0, 360, 'C', $line_style, array(), 8);
14383 * Paints a CMYK registration mark
14384 * @param $x (float) abscissa of the registration mark center.
14385 * @param $y (float) ordinate of the registration mark center.
14386 * @param $r (float) radius of the crop mark.
14387 * @author Nicola Asuni
14388 * @since 6.0.038 (2013-09-30)
14391 public function registrationMarkCMYK($x, $y, $r) {
14393 $lw = max((0.5 / $this->k
),($r / 8));
14399 $this->SetFillColorArray(array(100,0,0,0));
14400 $this->PieSector($x, $y, $ri, 270, 360, 'F');
14402 $this->SetFillColorArray(array(0,100,0,0));
14403 $this->PieSector($x, $y, $ri, 0, 90, 'F');
14405 $this->SetFillColorArray(array(0,0,100,0));
14406 $this->PieSector($x, $y, $ri, 90, 180, 'F');
14408 $this->SetFillColorArray(array(0,0,0,100));
14409 $this->PieSector($x, $y, $ri, 180, 270, 'F');
14410 // registration color
14411 $line_style = array('width' => $lw, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(100,100,100,100,'All'));
14412 $this->SetFillColorArray(array(100,100,100,100,'All'));
14414 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14416 $this->Line($x, ($y - $re), $x, ($y - $ri));
14417 $this->Line($x, ($y +
$ri), $x, ($y +
$re));
14418 $this->Line(($x - $re), $y, ($x - $ri), $y);
14419 $this->Line(($x +
$ri), $y, ($x +
$re), $y);
14423 * Paints a linear colour gradient.
14424 * @param $x (float) abscissa of the top left corner of the rectangle.
14425 * @param $y (float) ordinate of the top left corner of the rectangle.
14426 * @param $w (float) width of the rectangle.
14427 * @param $h (float) height of the rectangle.
14428 * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
14429 * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
14430 * @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).
14431 * @author Andreas W\FCrmser, Nicola Asuni
14432 * @since 3.1.000 (2008-06-09)
14435 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
14436 $this->Clip($x, $y, $w, $h);
14437 $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14441 * Paints a radial colour gradient.
14442 * @param $x (float) abscissa of the top left corner of the rectangle.
14443 * @param $y (float) ordinate of the top left corner of the rectangle.
14444 * @param $w (float) width of the rectangle.
14445 * @param $h (float) height of the rectangle.
14446 * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
14447 * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
14448 * @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.
14449 * @author Andreas W\FCrmser, Nicola Asuni
14450 * @since 3.1.000 (2008-06-09)
14453 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
14454 $this->Clip($x, $y, $w, $h);
14455 $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14459 * Paints a coons patch mesh.
14460 * @param $x (float) abscissa of the top left corner of the rectangle.
14461 * @param $y (float) ordinate of the top left corner of the rectangle.
14462 * @param $w (float) width of the rectangle.
14463 * @param $h (float) height of the rectangle.
14464 * @param $col1 (array) first color (lower left corner) (RGB components).
14465 * @param $col2 (array) second color (lower right corner) (RGB components).
14466 * @param $col3 (array) third color (upper right corner) (RGB components).
14467 * @param $col4 (array) fourth color (upper left corner) (RGB components).
14468 * @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>
14469 * @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
14470 * @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
14471 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
14472 * @author Andreas W\FCrmser, Nicola Asuni
14473 * @since 3.1.000 (2008-06-09)
14476 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) {
14477 if ($this->pdfa_mode
OR ($this->state
!= 2)) {
14480 $this->Clip($x, $y, $w, $h);
14481 $n = count($this->gradients
) +
1;
14482 $this->gradients
[$n] = array();
14483 $this->gradients
[$n]['type'] = 6; //coons patch mesh
14484 $this->gradients
[$n]['coords'] = array();
14485 $this->gradients
[$n]['antialias'] = $antialias;
14486 $this->gradients
[$n]['colors'] = array();
14487 $this->gradients
[$n]['transparency'] = false;
14488 //check the coords array if it is the simple array or the multi patch array
14489 if (!isset($coords[0]['f'])) {
14490 //simple array -> convert to multi patch array
14491 if (!isset($col1[1])) {
14492 $col1[1] = $col1[2] = $col1[0];
14494 if (!isset($col2[1])) {
14495 $col2[1] = $col2[2] = $col2[0];
14497 if (!isset($col3[1])) {
14498 $col3[1] = $col3[2] = $col3[0];
14500 if (!isset($col4[1])) {
14501 $col4[1] = $col4[2] = $col4[0];
14503 $patch_array[0]['f'] = 0;
14504 $patch_array[0]['points'] = $coords;
14505 $patch_array[0]['colors'][0]['r'] = $col1[0];
14506 $patch_array[0]['colors'][0]['g'] = $col1[1];
14507 $patch_array[0]['colors'][0]['b'] = $col1[2];
14508 $patch_array[0]['colors'][1]['r'] = $col2[0];
14509 $patch_array[0]['colors'][1]['g'] = $col2[1];
14510 $patch_array[0]['colors'][1]['b'] = $col2[2];
14511 $patch_array[0]['colors'][2]['r'] = $col3[0];
14512 $patch_array[0]['colors'][2]['g'] = $col3[1];
14513 $patch_array[0]['colors'][2]['b'] = $col3[2];
14514 $patch_array[0]['colors'][3]['r'] = $col4[0];
14515 $patch_array[0]['colors'][3]['g'] = $col4[1];
14516 $patch_array[0]['colors'][3]['b'] = $col4[2];
14518 //multi patch array
14519 $patch_array = $coords;
14521 $bpcd = 65535; //16 bits per coordinate
14522 //build the data stream
14523 $this->gradients
[$n]['stream'] = '';
14524 $count_patch = count($patch_array);
14525 for ($i=0; $i < $count_patch; ++
$i) {
14526 $this->gradients
[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
14527 $count_points = count($patch_array[$i]['points']);
14528 for ($j=0; $j < $count_points; ++
$j) {
14529 //each point as 16 bit
14530 $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
14531 if ($patch_array[$i]['points'][$j] < 0) {
14532 $patch_array[$i]['points'][$j] = 0;
14534 if ($patch_array[$i]['points'][$j] > $bpcd) {
14535 $patch_array[$i]['points'][$j] = $bpcd;
14537 $this->gradients
[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
14538 $this->gradients
[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] %
256));
14540 $count_cols = count($patch_array[$i]['colors']);
14541 for ($j=0; $j < $count_cols; ++
$j) {
14542 //each color component as 8 bit
14543 $this->gradients
[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
14544 $this->gradients
[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
14545 $this->gradients
[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
14548 //paint the gradient
14549 $this->_out('/Sh'.$n.' sh');
14550 //restore previous Graphic State
14551 $this->_outRestoreGraphicsState();
14552 if ($this->inxobj
) {
14553 // we are inside an XObject template
14554 $this->xobjects
[$this->xobjid
]['gradients'][$n] = $this->gradients
[$n];
14559 * Set a rectangular clipping area.
14560 * @param $x (float) abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
14561 * @param $y (float) ordinate of the top left corner of the rectangle.
14562 * @param $w (float) width of the rectangle.
14563 * @param $h (float) height of the rectangle.
14564 * @author Andreas W\FCrmser, Nicola Asuni
14565 * @since 3.1.000 (2008-06-09)
14568 protected function Clip($x, $y, $w, $h) {
14569 if ($this->state
!= 2) {
14573 $x = $this->w
- $x - $w;
14575 //save current Graphic State
14577 //set clipping area
14578 $s .= sprintf(' %F %F %F %F re W n', $x*$this->k
, ($this->h
-$y)*$this->k
, $w*$this->k
, -$h*$this->k
);
14579 //set up transformation matrix for gradient
14580 $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k
, $h*$this->k
, $x*$this->k
, ($this->h
-($y+
$h))*$this->k
);
14586 * @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)
14587 * @param $coords (array) array of coordinates.
14588 * @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).
14589 * @param $background (array) An array of colour components appropriate to the colour space, specifying a single background colour value.
14590 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
14591 * @author Nicola Asuni
14592 * @since 3.1.000 (2008-06-09)
14595 public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
14596 if ($this->pdfa_mode
OR ($this->state
!= 2)) {
14599 $n = count($this->gradients
) +
1;
14600 $this->gradients
[$n] = array();
14601 $this->gradients
[$n]['type'] = $type;
14602 $this->gradients
[$n]['coords'] = $coords;
14603 $this->gradients
[$n]['antialias'] = $antialias;
14604 $this->gradients
[$n]['colors'] = array();
14605 $this->gradients
[$n]['transparency'] = false;
14607 $numcolspace = count($stops[0]['color']);
14608 $bcolor = array_values($background);
14609 switch($numcolspace) {
14612 $this->gradients
[$n]['colspace'] = 'DeviceCMYK';
14613 if (!empty($background)) {
14614 $this->gradients
[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
14619 $this->gradients
[$n]['colspace'] = 'DeviceRGB';
14620 if (!empty($background)) {
14621 $this->gradients
[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
14625 case 1: { // GRAY SCALE
14626 $this->gradients
[$n]['colspace'] = 'DeviceGray';
14627 if (!empty($background)) {
14628 $this->gradients
[$n]['background'] = sprintf('%F', $bcolor[0]/255);
14633 $num_stops = count($stops);
14634 $last_stop_id = $num_stops - 1;
14635 foreach ($stops as $key => $stop) {
14636 $this->gradients
[$n]['colors'][$key] = array();
14637 // offset represents a location along the gradient vector
14638 if (isset($stop['offset'])) {
14639 $this->gradients
[$n]['colors'][$key]['offset'] = $stop['offset'];
14642 $this->gradients
[$n]['colors'][$key]['offset'] = 0;
14643 } elseif ($key == $last_stop_id) {
14644 $this->gradients
[$n]['colors'][$key]['offset'] = 1;
14646 $offsetstep = (1 - $this->gradients
[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
14647 $this->gradients
[$n]['colors'][$key]['offset'] = $this->gradients
[$n]['colors'][($key - 1)]['offset'] +
$offsetstep;
14650 if (isset($stop['opacity'])) {
14651 $this->gradients
[$n]['colors'][$key]['opacity'] = $stop['opacity'];
14652 if ((!$this->pdfa_mode
) AND ($stop['opacity'] < 1)) {
14653 $this->gradients
[$n]['transparency'] = true;
14656 $this->gradients
[$n]['colors'][$key]['opacity'] = 1;
14658 // exponent for the exponential interpolation function
14659 if (isset($stop['exponent'])) {
14660 $this->gradients
[$n]['colors'][$key]['exponent'] = $stop['exponent'];
14662 $this->gradients
[$n]['colors'][$key]['exponent'] = 1;
14665 $color = array_values($stop['color']);
14666 switch($numcolspace) {
14669 $this->gradients
[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
14673 $this->gradients
[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255);
14676 case 1: { // GRAY SCALE
14677 $this->gradients
[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255);
14682 if ($this->gradients
[$n]['transparency']) {
14683 // paint luminosity gradient
14684 $this->_out('/TGS'.$n.' gs');
14686 //paint the gradient
14687 $this->_out('/Sh'.$n.' sh');
14688 //restore previous Graphic State
14689 $this->_outRestoreGraphicsState();
14690 if ($this->inxobj
) {
14691 // we are inside an XObject template
14692 $this->xobjects
[$this->xobjid
]['gradients'][$n] = $this->gradients
[$n];
14697 * Output gradient shaders.
14698 * @author Nicola Asuni
14699 * @since 3.1.000 (2008-06-09)
14702 function _putshaders() {
14703 if ($this->pdfa_mode
) {
14706 $idt = count($this->gradients
); //index for transparency gradients
14707 foreach ($this->gradients
as $id => $grad) {
14708 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
14709 $fc = $this->_newobj();
14711 $out .= ' /FunctionType 3';
14712 $out .= ' /Domain [0 1]';
14717 $num_cols = count($grad['colors']);
14718 $lastcols = $num_cols - 1;
14719 for ($i = 1; $i < $num_cols; ++
$i) {
14720 $functions .= ($fc +
$i).' 0 R ';
14721 if ($i < $lastcols) {
14722 $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']);
14726 $out .= ' /Functions ['.trim($functions).']';
14727 $out .= ' /Bounds ['.trim($bounds).']';
14728 $out .= ' /Encode ['.trim($encode).']';
14730 $out .= "\n".'endobj';
14732 for ($i = 1; $i < $num_cols; ++
$i) {
14735 $out .= ' /FunctionType 2';
14736 $out .= ' /Domain [0 1]';
14737 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
14738 $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
14739 $out .= ' /N '.$grad['colors'][$i]['exponent'];
14741 $out .= "\n".'endobj';
14744 // set transparency fuctions
14745 if ($grad['transparency']) {
14746 $ft = $this->_newobj();
14748 $out .= ' /FunctionType 3';
14749 $out .= ' /Domain [0 1]';
14752 $num_cols = count($grad['colors']);
14753 for ($i = 1; $i < $num_cols; ++
$i) {
14754 $functions .= ($ft +
$i).' 0 R ';
14756 $out .= ' /Functions ['.trim($functions).']';
14757 $out .= ' /Bounds ['.trim($bounds).']';
14758 $out .= ' /Encode ['.trim($encode).']';
14760 $out .= "\n".'endobj';
14762 for ($i = 1; $i < $num_cols; ++
$i) {
14765 $out .= ' /FunctionType 2';
14766 $out .= ' /Domain [0 1]';
14767 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
14768 $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
14769 $out .= ' /N '.$grad['colors'][$i]['exponent'];
14771 $out .= "\n".'endobj';
14776 // set shading object
14778 $out = '<< /ShadingType '.$grad['type'];
14779 if (isset($grad['colspace'])) {
14780 $out .= ' /ColorSpace /'.$grad['colspace'];
14782 $out .= ' /ColorSpace /DeviceRGB';
14784 if (isset($grad['background']) AND !empty($grad['background'])) {
14785 $out .= ' /Background ['.$grad['background'].']';
14787 if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
14788 $out .= ' /AntiAlias true';
14790 if ($grad['type'] == 2) {
14791 $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
14792 $out .= ' /Domain [0 1]';
14793 $out .= ' /Function '.$fc.' 0 R';
14794 $out .= ' /Extend [true true]';
14796 } elseif ($grad['type'] == 3) {
14797 //x0, y0, r0, x1, y1, r1
14798 //at this this time radius of inner circle is 0
14799 $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
14800 $out .= ' /Domain [0 1]';
14801 $out .= ' /Function '.$fc.' 0 R';
14802 $out .= ' /Extend [true true]';
14804 } elseif ($grad['type'] == 6) {
14805 $out .= ' /BitsPerCoordinate 16';
14806 $out .= ' /BitsPerComponent 8';
14807 $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
14808 $out .= ' /BitsPerFlag 8';
14809 $stream = $this->_getrawstream($grad['stream']);
14810 $out .= ' /Length '.strlen($stream);
14812 $out .= ' stream'."\n".$stream."\n".'endstream';
14814 $out .= "\n".'endobj';
14816 if ($grad['transparency']) {
14817 $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
14818 $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
14820 $this->gradients
[$id]['id'] = $this->n
;
14821 // set pattern object
14823 $out = '<< /Type /Pattern /PatternType 2';
14824 $out .= ' /Shading '.$this->gradients
[$id]['id'].' 0 R';
14826 $out .= "\n".'endobj';
14828 $this->gradients
[$id]['pattern'] = $this->n
;
14829 // set shading and pattern for transparency mask
14830 if ($grad['transparency']) {
14831 // luminosity pattern
14832 $idgs = $id +
$idt;
14834 $this->_out($shading_transparency);
14835 $this->gradients
[$idgs]['id'] = $this->n
;
14837 $out = '<< /Type /Pattern /PatternType 2';
14838 $out .= ' /Shading '.$this->gradients
[$idgs]['id'].' 0 R';
14840 $out .= "\n".'endobj';
14842 $this->gradients
[$idgs]['pattern'] = $this->n
;
14843 // luminosity XObject
14844 $oid = $this->_newobj();
14845 $this->xobjects
['LX'.$oid] = array('n' => $oid);
14847 $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt
.' '.$this->hPt
.' re f Q';
14848 if ($this->compress
) {
14849 $filter = ' /Filter /FlateDecode';
14850 $stream = gzcompress($stream);
14852 $stream = $this->_getrawstream($stream);
14853 $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
14854 $out .= ' /Length '.strlen($stream);
14855 $rect = sprintf('%F %F', $this->wPt
, $this->hPt
);
14856 $out .= ' /BBox [0 0 '.$rect.']';
14857 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
14858 $out .= ' /Resources <<';
14859 $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
14860 $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients
[$idgs]['pattern'].' 0 R >>';
14863 $out .= ' stream'."\n".$stream."\n".'endstream';
14864 $out .= "\n".'endobj';
14868 $out = '<< /Type /Mask /S /Luminosity /G '.($this->n
- 1).' 0 R >>'."\n".'endobj';
14872 $out = '<< /Type /ExtGState /SMask '.($this->n
- 1).' 0 R /AIS false >>'."\n".'endobj';
14874 $this->extgstates
[] = array('n' => $this->n
, 'name' => 'TGS'.$id);
14880 * Draw the sector of a circle.
14881 * It can be used for instance to render pie charts.
14882 * @param $xc (float) abscissa of the center.
14883 * @param $yc (float) ordinate of the center.
14884 * @param $r (float) radius.
14885 * @param $a (float) start angle (in degrees).
14886 * @param $b (float) end angle (in degrees).
14887 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
14888 * @param $cw: (float) indicates whether to go clockwise (default: true).
14889 * @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.
14890 * @author Maxime Delorme, Nicola Asuni
14891 * @since 3.1.000 (2008-06-09)
14894 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
14895 $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
14899 * Draw the sector of an ellipse.
14900 * It can be used for instance to render pie charts.
14901 * @param $xc (float) abscissa of the center.
14902 * @param $yc (float) ordinate of the center.
14903 * @param $rx (float) the x-axis radius.
14904 * @param $ry (float) the y-axis radius.
14905 * @param $a (float) start angle (in degrees).
14906 * @param $b (float) end angle (in degrees).
14907 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
14908 * @param $cw: (float) indicates whether to go clockwise.
14909 * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock).
14910 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of arc.
14911 * @author Maxime Delorme, Nicola Asuni
14912 * @since 3.1.000 (2008-06-09)
14915 public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
14916 if ($this->state
!= 2) {
14920 $xc = ($this->w
- $xc);
14922 $op = TCPDF_STATIC
::getPathPaintOperator($style);
14924 $line_style = array();
14928 $b = (360 - $a +
$o);
14929 $a = (360 - $d +
$o);
14934 $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
14939 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
14940 * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
14941 * Only vector drawing is supported, not text or bitmap.
14942 * 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).
14943 * @param $file (string) Name of the file containing the image or a '@' character followed by the EPS/AI data string.
14944 * @param $x (float) Abscissa of the upper-left corner.
14945 * @param $y (float) Ordinate of the upper-left corner.
14946 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
14947 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
14948 * @param $link (mixed) URL or identifier returned by AddLink().
14949 * @param $useBoundingBox (boolean) specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
14950 * @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>
14951 * @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>
14952 * @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)))
14953 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
14954 * @param $fixoutvals (boolean) if true remove values outside the bounding box.
14955 * @author Valentin Schmidt, Nicola Asuni
14956 * @since 3.1.000 (2008-06-09)
14959 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
14960 if ($this->state
!= 2) {
14963 if ($this->rasterize_vector_images
AND ($w > 0) AND ($h > 0)) {
14964 // convert EPS to raster image using GD or ImageMagick libraries
14965 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14973 // check page for no-write regions and adapt page margins if necessary
14974 list($x, $y) = $this->checkPageRegions($h, $x, $y);
14976 if ($file[0] === '@') { // image from string
14977 $data = substr($file, 1);
14978 } else { // EPS/AI file
14979 $data = TCPDF_STATIC
::fileGetContents($file);
14981 if ($data === FALSE) {
14982 $this->Error('EPS file not found: '.$file);
14985 // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
14986 preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
14987 if (count($regs) > 1) {
14988 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
14989 if (strpos($version_str, 'Adobe Illustrator') !== false) {
14990 $versexp = explode(' ', $version_str);
14991 $version = (float)array_pop($versexp);
14992 if ($version >= 9) {
14993 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
14997 // strip binary bytes in front of PS-header
14998 $start = strpos($data, '%!PS-Adobe');
15000 $data = substr($data, $start);
15002 // find BoundingBox params
15003 preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
15004 if (count($regs) > 1) {
15005 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
15007 $this->Error('No BoundingBox found in EPS/AI file: '.$file);
15009 $start = strpos($data, '%%EndSetup');
15010 if ($start === false) {
15011 $start = strpos($data, '%%EndProlog');
15013 if ($start === false) {
15014 $start = strpos($data, '%%BoundingBox');
15016 $data = substr($data, $start);
15017 $end = strpos($data, '%%PageTrailer');
15018 if ($end===false) {
15019 $end = strpos($data, 'showpage');
15022 $data = substr($data, 0, $end);
15024 // calculate image width and height on document
15025 if (($w <= 0) AND ($h <= 0)) {
15026 $w = ($x2 - $x1) / $k;
15027 $h = ($y2 - $y1) / $k;
15028 } elseif ($w <= 0) {
15029 $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
15030 } elseif ($h <= 0) {
15031 $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
15033 // fit the image on available space
15034 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
15035 if ($this->rasterize_vector_images
) {
15036 // convert EPS to raster image using GD or ImageMagick libraries
15037 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
15039 // set scaling factors
15040 $scale_x = $w / (($x2 - $x1) / $k);
15041 $scale_y = $h / (($y2 - $y1) / $k);
15043 $this->img_rb_y
= $y +
$h;
15046 if ($palign == 'L') {
15047 $ximg = $this->lMargin
;
15048 } elseif ($palign == 'C') {
15049 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15050 } elseif ($palign == 'R') {
15051 $ximg = $this->w
- $this->rMargin
- $w;
15055 $this->img_rb_x
= $ximg;
15057 if ($palign == 'L') {
15058 $ximg = $this->lMargin
;
15059 } elseif ($palign == 'C') {
15060 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15061 } elseif ($palign == 'R') {
15062 $ximg = $this->w
- $this->rMargin
- $w;
15066 $this->img_rb_x
= $ximg +
$w;
15068 if ($useBoundingBox) {
15069 $dx = $ximg * $k - $x1;
15070 $dy = $y * $k - $y1;
15075 // save the current graphic state
15076 $this->_out('q'.$this->epsmarker
);
15078 $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy +
($this->hPt
- (2 * $y * $k) - ($y2 - $y1))));
15080 if (isset($scale_x)) {
15081 $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
15083 // handle pc/unix/mac line endings
15084 $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY
);
15086 $cnt = count($lines);
15087 for ($i=0; $i < $cnt; ++
$i) {
15088 $line = $lines[$i];
15089 if (($line == '') OR ($line[0] == '%')) {
15092 $len = strlen($line);
15093 // check for spot color names
15095 if (strcasecmp('x', substr(trim($line), -1)) == 0) {
15096 if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) {
15097 // extract spot color name
15098 $color_name = $matches[0];
15099 // remove color name from string
15100 $line = str_replace(' '.$color_name, '', $line);
15101 // remove pharentesis from color name
15102 $color_name = substr($color_name, 1, -1);
15105 $chunks = explode(' ', $line);
15106 $cmd = trim(array_pop($chunks));
15108 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
15109 $b = array_pop($chunks);
15110 $g = array_pop($chunks);
15111 $r = array_pop($chunks);
15112 $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!
15117 // check for values outside the bounding box
15122 // skip values outside bounding box
15123 foreach ($chunks as $key => $val) {
15124 if ((($key %
2) == 0) AND (($val < $x1) OR ($val > $x2))) {
15126 } elseif ((($key %
2) != 0) AND (($val < $y1) OR ($val > $y2))) {
15154 $this->_out($line);
15157 case 'x': {// custom fill color
15158 if (empty($color_name)) {
15160 list($col_c, $col_m, $col_y, $col_k) = $chunks;
15161 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
15163 // Spot Color (CMYK + tint)
15164 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15165 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15166 $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors
[$color_name]['i'], (1 - $col_t));
15167 $this->_out($color_cmd);
15171 case 'X': { // custom stroke color
15172 if (empty($color_name)) {
15174 list($col_c, $col_m, $col_y, $col_k) = $chunks;
15175 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
15177 // Spot Color (CMYK + tint)
15178 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15179 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15180 $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors
[$color_name]['i'], (1 - $col_t));
15181 $this->_out($color_cmd);
15193 $line[($len - 1)] = strtolower($cmd);
15194 $this->_out($line);
15199 $this->_out($cmd . '*');
15206 $max = min(($i +
5), $cnt);
15207 for ($j = ($i +
1); $j < $max; ++
$j) {
15208 $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
15228 // restore previous graphic state
15229 $this->_out($this->epsmarker
.'Q');
15230 if (!empty($border)) {
15238 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
15243 $this->Link($ximg, $y, $w, $h, $link, 0);
15245 // set pointer to align the next text/objects
15249 $this->x
= $this->img_rb_x
;
15253 $this->y
= $y +
round($h/2);
15254 $this->x
= $this->img_rb_x
;
15258 $this->y
= $this->img_rb_y
;
15259 $this->x
= $this->img_rb_x
;
15263 $this->SetY($this->img_rb_y
);
15270 $this->endlinex
= $this->img_rb_x
;
15274 * Set document barcode.
15275 * @param $bc (string) barcode
15278 public function setBarcode($bc='') {
15279 $this->barcode
= $bc;
15283 * Get current barcode.
15286 * @since 4.0.012 (2008-07-24)
15288 public function getBarcode() {
15289 return $this->barcode
;
15293 * Print a Linear Barcode.
15294 * @param $code (string) code to print
15295 * @param $type (string) type of barcode (see tcpdf_barcodes_1d.php for supported formats).
15296 * @param $x (int) x position in user units (empty string = current x position)
15297 * @param $y (int) y position in user units (empty string = current y position)
15298 * @param $w (int) width in user units (empty string = remaining page width)
15299 * @param $h (int) height in user units (empty string = remaining page height)
15300 * @param $xres (float) width of the smallest bar in user units (empty string = default value = 0.4mm)
15301 * @param $style (array) array of options:<ul>
15302 * <li>boolean $style['border'] if true prints a border</li>
15303 * <li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li>
15304 * <li>int $style['hpadding'] horizontal padding in user units (set to 'auto' for automatic padding)</li>
15305 * <li>int $style['vpadding'] vertical padding in user units (set to 'auto' for automatic padding)</li>
15306 * <li>array $style['fgcolor'] color array for bars and text</li>
15307 * <li>mixed $style['bgcolor'] color array for background (set to false for transparent)</li>
15308 * <li>boolean $style['text'] if true prints text below the barcode</li>
15309 * <li>string $style['label'] override default label</li>
15310 * <li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li>
15311 * <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>
15312 * <li>string $style['position'] horizontal position of the containing barcode cell on the page: L = left margin; C = center; R = right margin.</li>
15313 * <li>string $style['align'] horizontal position of the barcode on the containing rectangle: L = left; C = center; R = right.</li>
15314 * <li>string $style['stretch'] if true stretch the barcode to best fit the available width, otherwise uses $xres resolution for a single bar.</li>
15315 * <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>
15316 * <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>
15317 * @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>
15318 * @author Nicola Asuni
15319 * @since 3.1.000 (2008-06-09)
15322 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
15323 if (TCPDF_STATIC
::empty_string(trim($code))) {
15326 require_once(dirname(__FILE__
).'/tcpdf_barcodes_1d.php');
15327 // save current graphic settings
15328 $gvars = $this->getGraphicVars();
15329 // create new barcode object
15330 $barcodeobj = new TCPDFBarcode($code, $type);
15331 $arrcode = $barcodeobj->getBarcodeArray();
15332 if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] <= 0)) {
15333 $this->Error('Error in 1D barcode string');
15335 if ($arrcode['maxh'] <= 0) {
15336 $arrcode['maxh'] = 1;
15338 // set default values
15339 if (!isset($style['position'])) {
15340 $style['position'] = '';
15341 } elseif ($style['position'] == 'S') {
15342 // keep this for backward compatibility
15343 $style['position'] = '';
15344 $style['stretch'] = true;
15346 if (!isset($style['fitwidth'])) {
15347 if (!isset($style['stretch'])) {
15348 $style['fitwidth'] = true;
15350 $style['fitwidth'] = false;
15353 if ($style['fitwidth']) {
15355 $style['stretch'] = false;
15357 if (!isset($style['stretch'])) {
15358 if (($w === '') OR ($w <= 0)) {
15359 $style['stretch'] = false;
15361 $style['stretch'] = true;
15364 if (!isset($style['fgcolor'])) {
15365 $style['fgcolor'] = array(0,0,0); // default black
15367 if (!isset($style['bgcolor'])) {
15368 $style['bgcolor'] = false; // default transparent
15370 if (!isset($style['border'])) {
15371 $style['border'] = false;
15374 if (!isset($style['text'])) {
15375 $style['text'] = false;
15377 if ($style['text'] AND isset($style['font'])) {
15378 if (isset($style['fontsize'])) {
15379 $fontsize = $style['fontsize'];
15381 $this->SetFont($style['font'], '', $fontsize);
15383 if (!isset($style['stretchtext'])) {
15384 $style['stretchtext'] = 4;
15392 // check page for no-write regions and adapt page margins if necessary
15393 list($x, $y) = $this->checkPageRegions($h, $x, $y);
15394 if (($w === '') OR ($w <= 0)) {
15396 $w = $x - $this->lMargin
;
15398 $w = $this->w
- $this->rMargin
- $x;
15402 if (!isset($style['padding'])) {
15404 } elseif ($style['padding'] === 'auto') {
15405 $padding = 10 * ($w / ($arrcode['maxw'] +
20));
15407 $padding = floatval($style['padding']);
15409 // horizontal padding
15410 if (!isset($style['hpadding'])) {
15411 $hpadding = $padding;
15412 } elseif ($style['hpadding'] === 'auto') {
15413 $hpadding = 10 * ($w / ($arrcode['maxw'] +
20));
15415 $hpadding = floatval($style['hpadding']);
15417 // vertical padding
15418 if (!isset($style['vpadding'])) {
15419 $vpadding = $padding;
15420 } elseif ($style['vpadding'] === 'auto') {
15421 $vpadding = ($hpadding / 2);
15423 $vpadding = floatval($style['vpadding']);
15425 // calculate xres (single bar width)
15426 $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
15427 if ($style['stretch']) {
15430 if (TCPDF_STATIC
::empty_string($xres)) {
15431 $xres = (0.141 * $this->k
); // default bar width = 0.4 mm
15433 if ($xres > $max_xres) {
15434 // correct xres to fit on $w
15437 if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
15438 OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
15439 $hpadding = 10 * $xres;
15440 if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
15441 $vpadding = ($hpadding / 2);
15445 if ($style['fitwidth']) {
15447 $w = (($arrcode['maxw'] * $xres) +
(2 * $hpadding));
15448 if (isset($style['cellfitalign'])) {
15449 switch ($style['cellfitalign']) {
15452 $x -= ($wold - $w);
15458 $x +
= ($wold - $w);
15464 $x -= (($wold - $w) / 2);
15466 $x +
= (($wold - $w) / 2);
15476 $text_height = $this->getCellHeight($fontsize / $this->k
);
15478 if (($h === '') OR ($h <= 0)) {
15479 // set default height
15480 $h = (($arrcode['maxw'] * $xres) / 3) +
(2 * $vpadding) +
$text_height;
15482 $barh = $h - $text_height - (2 * $vpadding);
15484 // try to reduce font or padding to fit barcode on available height
15485 if ($text_height > $h) {
15486 $fontsize = (($h * $this->k
) / (4 * $this->cell_height_ratio
));
15487 $text_height = $this->getCellHeight($fontsize / $this->k
);
15488 $this->SetFont($style['font'], '', $fontsize);
15490 if ($vpadding > 0) {
15491 $vpadding = (($h - $text_height) / 4);
15493 $barh = $h - $text_height - (2 * $vpadding);
15495 // fit the barcode on available space
15496 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15498 $this->img_rb_y
= $y +
$h;
15501 if ($style['position'] == 'L') {
15502 $xpos = $this->lMargin
;
15503 } elseif ($style['position'] == 'C') {
15504 $xpos = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15505 } elseif ($style['position'] == 'R') {
15506 $xpos = $this->w
- $this->rMargin
- $w;
15510 $this->img_rb_x
= $xpos;
15512 if ($style['position'] == 'L') {
15513 $xpos = $this->lMargin
;
15514 } elseif ($style['position'] == 'C') {
15515 $xpos = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15516 } elseif ($style['position'] == 'R') {
15517 $xpos = $this->w
- $this->rMargin
- $w;
15521 $this->img_rb_x
= $xpos +
$w;
15523 $xpos_rect = $xpos;
15524 if (!isset($style['align'])) {
15525 $style['align'] = 'C';
15527 switch ($style['align']) {
15529 $xpos = $xpos_rect +
$hpadding;
15533 $xpos = $xpos_rect +
($w - ($arrcode['maxw'] * $xres)) - $hpadding;
15538 $xpos = $xpos_rect +
(($w - ($arrcode['maxw'] * $xres)) / 2);
15542 $xpos_text = $xpos;
15543 // barcode is always printed in LTR direction
15544 $tempRTL = $this->rtl
;
15545 $this->rtl
= false;
15546 // print background color
15547 if ($style['bgcolor']) {
15548 $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15549 } elseif ($style['border']) {
15550 $this->Rect($xpos_rect, $y, $w, $h, 'D');
15552 // set foreground color
15553 $this->SetDrawColorArray($style['fgcolor']);
15554 $this->SetTextColorArray($style['fgcolor']);
15556 foreach ($arrcode['bcode'] as $k => $v) {
15557 $bw = ($v['w'] * $xres);
15559 // draw a vertical bar
15560 $ypos = $y +
$vpadding +
($v['p'] * $barh / $arrcode['maxh']);
15561 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
15566 if ($style['text']) {
15567 if (isset($style['label']) AND !TCPDF_STATIC
::empty_string($style['label'])) {
15568 $label = $style['label'];
15572 $txtwidth = ($arrcode['maxw'] * $xres);
15573 if ($this->GetStringWidth($label) > $txtwidth) {
15574 $style['stretchtext'] = 2;
15577 $this->x
= $xpos_text;
15578 $this->y
= $y +
$vpadding +
$barh;
15579 $cellpadding = $this->cell_padding
;
15580 $this->SetCellPadding(0);
15581 $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
15582 $this->cell_padding
= $cellpadding;
15584 // restore original direction
15585 $this->rtl
= $tempRTL;
15586 // restore previous settings
15587 $this->setGraphicVars($gvars);
15588 // set pointer to align the next text/objects
15592 $this->x
= $this->img_rb_x
;
15596 $this->y
= $y +
round($h / 2);
15597 $this->x
= $this->img_rb_x
;
15601 $this->y
= $this->img_rb_y
;
15602 $this->x
= $this->img_rb_x
;
15606 $this->SetY($this->img_rb_y
);
15613 $this->endlinex
= $this->img_rb_x
;
15617 * Print 2D Barcode.
15618 * @param $code (string) code to print
15619 * @param $type (string) type of barcode (see tcpdf_barcodes_2d.php for supported formats).
15620 * @param $x (int) x position in user units
15621 * @param $y (int) y position in user units
15622 * @param $w (int) width in user units
15623 * @param $h (int) height in user units
15624 * @param $style (array) array of options:<ul>
15625 * <li>boolean $style['border'] if true prints a border around the barcode</li>
15626 * <li>int $style['padding'] padding to leave around the barcode in barcode units (set to 'auto' for automatic padding)</li>
15627 * <li>int $style['hpadding'] horizontal padding in barcode units (set to 'auto' for automatic padding)</li>
15628 * <li>int $style['vpadding'] vertical padding in barcode units (set to 'auto' for automatic padding)</li>
15629 * <li>int $style['module_width'] width of a single module in points</li>
15630 * <li>int $style['module_height'] height of a single module in points</li>
15631 * <li>array $style['fgcolor'] color array for bars and text</li>
15632 * <li>mixed $style['bgcolor'] color array for background or false for transparent</li>
15633 * <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>
15634 * <li>$style['module_height'] height of a single module in points</li></ul>
15635 * @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>
15636 * @param $distort (boolean) if true distort the barcode to fit width and height, otherwise preserve aspect ratio
15637 * @author Nicola Asuni
15638 * @since 4.5.037 (2009-04-07)
15641 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) {
15642 if (TCPDF_STATIC
::empty_string(trim($code))) {
15645 require_once(dirname(__FILE__
).'/tcpdf_barcodes_2d.php');
15646 // save current graphic settings
15647 $gvars = $this->getGraphicVars();
15648 // create new barcode object
15649 $barcodeobj = new TCPDF2DBarcode($code, $type);
15650 $arrcode = $barcodeobj->getBarcodeArray();
15651 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)) {
15652 $this->Error('Error in 2D barcode string');
15654 // set default values
15655 if (!isset($style['position'])) {
15656 $style['position'] = '';
15658 if (!isset($style['fgcolor'])) {
15659 $style['fgcolor'] = array(0,0,0); // default black
15661 if (!isset($style['bgcolor'])) {
15662 $style['bgcolor'] = false; // default transparent
15664 if (!isset($style['border'])) {
15665 $style['border'] = false;
15668 if (!isset($style['padding'])) {
15669 $style['padding'] = 0;
15670 } elseif ($style['padding'] === 'auto') {
15671 $style['padding'] = 4;
15673 if (!isset($style['hpadding'])) {
15674 $style['hpadding'] = $style['padding'];
15675 } elseif ($style['hpadding'] === 'auto') {
15676 $style['hpadding'] = 4;
15678 if (!isset($style['vpadding'])) {
15679 $style['vpadding'] = $style['padding'];
15680 } elseif ($style['vpadding'] === 'auto') {
15681 $style['vpadding'] = 4;
15683 $hpad = (2 * $style['hpadding']);
15684 $vpad = (2 * $style['vpadding']);
15685 // cell (module) dimension
15686 if (!isset($style['module_width'])) {
15687 $style['module_width'] = 1; // width of a single module in points
15689 if (!isset($style['module_height'])) {
15690 $style['module_height'] = 1; // height of a single module in points
15698 // check page for no-write regions and adapt page margins if necessary
15699 list($x, $y) = $this->checkPageRegions($h, $x, $y);
15700 // number of barcode columns and rows
15701 $rows = $arrcode['num_rows'];
15702 $cols = $arrcode['num_cols'];
15703 if (($rows <= 0) || ($cols <= 0)){
15704 $this->Error('Error in 2D barcode string');
15706 // module width and height
15707 $mw = $style['module_width'];
15708 $mh = $style['module_height'];
15709 if (($mw <= 0) OR ($mh <= 0)) {
15710 $this->Error('Error in 2D barcode string');
15712 // get max dimensions
15714 $maxw = $x - $this->lMargin
;
15716 $maxw = $this->w
- $this->rMargin
- $x;
15718 $maxh = ($this->h
- $this->tMargin
- $this->bMargin
);
15719 $ratioHW = ((($rows * $mh) +
$hpad) / (($cols * $mw) +
$vpad));
15720 $ratioWH = ((($cols * $mw) +
$vpad) / (($rows * $mh) +
$hpad));
15722 if (($maxw * $ratioHW) > $maxh) {
15723 $maxw = $maxh * $ratioWH;
15725 if (($maxh * $ratioWH) > $maxw) {
15726 $maxh = $maxw * $ratioHW;
15729 // set maximum dimesions
15737 if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
15738 $w = ($cols +
$hpad) * ($mw / $this->k
);
15739 $h = ($rows +
$vpad) * ($mh / $this->k
);
15740 } elseif (($w === '') OR ($w <= 0)) {
15741 $w = $h * $ratioWH;
15742 } elseif (($h === '') OR ($h <= 0)) {
15743 $h = $w * $ratioHW;
15745 // barcode size (excluding padding)
15746 $bw = ($w * $cols) / ($cols +
$hpad);
15747 $bh = ($h * $rows) / ($rows +
$vpad);
15748 // dimension of single barcode cell unit
15752 if (($cw / $ch) > ($mw / $mh)) {
15753 // correct horizontal distortion
15754 $cw = $ch * $mw / $mh;
15756 $style['hpadding'] = ($w - $bw) / (2 * $cw);
15758 // correct vertical distortion
15759 $ch = $cw * $mh / $mw;
15761 $style['vpadding'] = ($h - $bh) / (2 * $ch);
15764 // fit the barcode on available space
15765 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15767 $this->img_rb_y
= $y +
$h;
15770 if ($style['position'] == 'L') {
15771 $xpos = $this->lMargin
;
15772 } elseif ($style['position'] == 'C') {
15773 $xpos = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15774 } elseif ($style['position'] == 'R') {
15775 $xpos = $this->w
- $this->rMargin
- $w;
15779 $this->img_rb_x
= $xpos;
15781 if ($style['position'] == 'L') {
15782 $xpos = $this->lMargin
;
15783 } elseif ($style['position'] == 'C') {
15784 $xpos = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15785 } elseif ($style['position'] == 'R') {
15786 $xpos = $this->w
- $this->rMargin
- $w;
15790 $this->img_rb_x
= $xpos +
$w;
15792 $xstart = $xpos +
($style['hpadding'] * $cw);
15793 $ystart = $y +
($style['vpadding'] * $ch);
15794 // barcode is always printed in LTR direction
15795 $tempRTL = $this->rtl
;
15796 $this->rtl
= false;
15797 // print background color
15798 if ($style['bgcolor']) {
15799 $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15800 } elseif ($style['border']) {
15801 $this->Rect($xpos, $y, $w, $h, 'D');
15803 // set foreground color
15804 $this->SetDrawColorArray($style['fgcolor']);
15805 // print barcode cells
15807 for ($r = 0; $r < $rows; ++
$r) {
15810 for ($c = 0; $c < $cols; ++
$c) {
15811 if ($arrcode['bcode'][$r][$c] == 1) {
15812 // draw a single barcode cell
15813 $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
15819 // restore original direction
15820 $this->rtl
= $tempRTL;
15821 // restore previous settings
15822 $this->setGraphicVars($gvars);
15823 // set pointer to align the next text/objects
15827 $this->x
= $this->img_rb_x
;
15831 $this->y
= $y +
round($h/2);
15832 $this->x
= $this->img_rb_x
;
15836 $this->y
= $this->img_rb_y
;
15837 $this->x
= $this->img_rb_x
;
15841 $this->SetY($this->img_rb_y
);
15848 $this->endlinex
= $this->img_rb_x
;
15852 * Returns an array containing current margins:
15854 <li>$ret['left'] = left margin</li>
15855 <li>$ret['right'] = right margin</li>
15856 <li>$ret['top'] = top margin</li>
15857 <li>$ret['bottom'] = bottom margin</li>
15858 <li>$ret['header'] = header margin</li>
15859 <li>$ret['footer'] = footer margin</li>
15860 <li>$ret['cell'] = cell padding array</li>
15861 <li>$ret['padding_left'] = cell left padding</li>
15862 <li>$ret['padding_top'] = cell top padding</li>
15863 <li>$ret['padding_right'] = cell right padding</li>
15864 <li>$ret['padding_bottom'] = cell bottom padding</li>
15866 * @return array containing all margins measures
15868 * @since 3.2.000 (2008-06-23)
15870 public function getMargins() {
15872 'left' => $this->lMargin
,
15873 'right' => $this->rMargin
,
15874 'top' => $this->tMargin
,
15875 'bottom' => $this->bMargin
,
15876 'header' => $this->header_margin
,
15877 'footer' => $this->footer_margin
,
15878 'cell' => $this->cell_padding
,
15879 'padding_left' => $this->cell_padding
['L'],
15880 'padding_top' => $this->cell_padding
['T'],
15881 'padding_right' => $this->cell_padding
['R'],
15882 'padding_bottom' => $this->cell_padding
['B']
15888 * Returns an array containing original margins:
15890 <li>$ret['left'] = left margin</li>
15891 <li>$ret['right'] = right margin</li>
15893 * @return array containing all margins measures
15895 * @since 4.0.012 (2008-07-24)
15897 public function getOriginalMargins() {
15899 'left' => $this->original_lMargin
,
15900 'right' => $this->original_rMargin
15906 * Returns the current font size.
15907 * @return current font size
15909 * @since 3.2.000 (2008-06-23)
15911 public function getFontSize() {
15912 return $this->FontSize
;
15916 * Returns the current font size in points unit.
15917 * @return current font size in points unit
15919 * @since 3.2.000 (2008-06-23)
15921 public function getFontSizePt() {
15922 return $this->FontSizePt
;
15926 * Returns the current font family name.
15927 * @return string current font family name
15929 * @since 4.3.008 (2008-12-05)
15931 public function getFontFamily() {
15932 return $this->FontFamily
;
15936 * Returns the current font style.
15937 * @return string current font style
15939 * @since 4.3.008 (2008-12-05)
15941 public function getFontStyle() {
15942 return $this->FontStyle
;
15946 * Cleanup HTML code (requires HTML Tidy library).
15947 * @param $html (string) htmlcode to fix
15948 * @param $default_css (string) CSS commands to add
15949 * @param $tagvs (array) parameters for setHtmlVSpace method
15950 * @param $tidy_options (array) options for tidy_parse_string function
15951 * @return string XHTML code cleaned up
15952 * @author Nicola Asuni
15954 * @since 5.9.017 (2010-11-16)
15955 * @see setHtmlVSpace()
15957 public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
15958 return TCPDF_STATIC
::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces
);
15962 * Returns the border width from CSS property
15963 * @param $width (string) border width
15964 * @return int with in user units
15966 * @since 5.7.000 (2010-08-02)
15968 protected function getCSSBorderWidth($width) {
15969 if ($width == 'thin') {
15970 $width = (2 / $this->k
);
15971 } elseif ($width == 'medium') {
15972 $width = (4 / $this->k
);
15973 } elseif ($width == 'thick') {
15974 $width = (6 / $this->k
);
15976 $width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
15982 * Returns the border dash style from CSS property
15983 * @param $style (string) border style to convert
15984 * @return int sash style (return -1 in case of none or hidden border)
15986 * @since 5.7.000 (2010-08-02)
15988 protected function getCSSBorderDashStyle($style) {
15989 switch (strtolower($style)) {
16018 * Returns the border style array from CSS border properties
16019 * @param $cssborder (string) border properties
16020 * @return array containing border properties
16022 * @since 5.7.000 (2010-08-02)
16024 protected function getCSSBorderStyle($cssborder) {
16025 $bprop = preg_split('/[\s]+/', trim($cssborder));
16026 $border = array(); // value to be returned
16027 switch (count($bprop)) {
16029 $width = $bprop[0];
16030 $style = $bprop[1];
16031 $color = $bprop[2];
16036 $style = $bprop[0];
16037 $color = $bprop[1];
16042 $style = $bprop[0];
16053 if ($style == 'none') {
16056 $border['cap'] = 'square';
16057 $border['join'] = 'miter';
16058 $border['dash'] = $this->getCSSBorderDashStyle($style);
16059 if ($border['dash'] < 0) {
16062 $border['width'] = $this->getCSSBorderWidth($width);
16063 $border['color'] = TCPDF_COLORS
::convertHTMLColorToDec($color, $this->spot_colors
);
16068 * Get the internal Cell padding from CSS attribute.
16069 * @param $csspadding (string) padding properties
16070 * @param $width (float) width of the containing element
16071 * @return array of cell paddings
16073 * @since 5.9.000 (2010-10-04)
16075 public function getCSSPadding($csspadding, $width=0) {
16076 $padding = preg_split('/[\s]+/', trim($csspadding));
16077 $cell_padding = array(); // value to be returned
16078 switch (count($padding)) {
16080 $cell_padding['T'] = $padding[0];
16081 $cell_padding['R'] = $padding[1];
16082 $cell_padding['B'] = $padding[2];
16083 $cell_padding['L'] = $padding[3];
16087 $cell_padding['T'] = $padding[0];
16088 $cell_padding['R'] = $padding[1];
16089 $cell_padding['B'] = $padding[2];
16090 $cell_padding['L'] = $padding[1];
16094 $cell_padding['T'] = $padding[0];
16095 $cell_padding['R'] = $padding[1];
16096 $cell_padding['B'] = $padding[0];
16097 $cell_padding['L'] = $padding[1];
16101 $cell_padding['T'] = $padding[0];
16102 $cell_padding['R'] = $padding[0];
16103 $cell_padding['B'] = $padding[0];
16104 $cell_padding['L'] = $padding[0];
16108 return $this->cell_padding
;
16112 $width = $this->w
- $this->lMargin
- $this->rMargin
;
16114 $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
16115 $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
16116 $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
16117 $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
16118 return $cell_padding;
16122 * Get the internal Cell margin from CSS attribute.
16123 * @param $cssmargin (string) margin properties
16124 * @param $width (float) width of the containing element
16125 * @return array of cell margins
16127 * @since 5.9.000 (2010-10-04)
16129 public function getCSSMargin($cssmargin, $width=0) {
16130 $margin = preg_split('/[\s]+/', trim($cssmargin));
16131 $cell_margin = array(); // value to be returned
16132 switch (count($margin)) {
16134 $cell_margin['T'] = $margin[0];
16135 $cell_margin['R'] = $margin[1];
16136 $cell_margin['B'] = $margin[2];
16137 $cell_margin['L'] = $margin[3];
16141 $cell_margin['T'] = $margin[0];
16142 $cell_margin['R'] = $margin[1];
16143 $cell_margin['B'] = $margin[2];
16144 $cell_margin['L'] = $margin[1];
16148 $cell_margin['T'] = $margin[0];
16149 $cell_margin['R'] = $margin[1];
16150 $cell_margin['B'] = $margin[0];
16151 $cell_margin['L'] = $margin[1];
16155 $cell_margin['T'] = $margin[0];
16156 $cell_margin['R'] = $margin[0];
16157 $cell_margin['B'] = $margin[0];
16158 $cell_margin['L'] = $margin[0];
16162 return $this->cell_margin
;
16166 $width = $this->w
- $this->lMargin
- $this->rMargin
;
16168 $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
16169 $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
16170 $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
16171 $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
16172 return $cell_margin;
16176 * Get the border-spacing from CSS attribute.
16177 * @param $cssbspace (string) border-spacing CSS properties
16178 * @param $width (float) width of the containing element
16179 * @return array of border spacings
16181 * @since 5.9.010 (2010-10-27)
16183 public function getCSSBorderMargin($cssbspace, $width=0) {
16184 $space = preg_split('/[\s]+/', trim($cssbspace));
16185 $border_spacing = array(); // value to be returned
16186 switch (count($space)) {
16188 $border_spacing['H'] = $space[0];
16189 $border_spacing['V'] = $space[1];
16193 $border_spacing['H'] = $space[0];
16194 $border_spacing['V'] = $space[0];
16198 return array('H' => 0, 'V' => 0);
16202 $width = $this->w
- $this->lMargin
- $this->rMargin
;
16204 $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
16205 $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
16206 return $border_spacing;
16210 * Returns the letter-spacing value from CSS value
16211 * @param $spacing (string) letter-spacing value
16212 * @param $parent (float) font spacing (tracking) value of the parent element
16213 * @return float quantity to increases or decreases the space between characters in a text.
16215 * @since 5.9.000 (2010-10-02)
16217 protected function getCSSFontSpacing($spacing, $parent=0) {
16218 $val = 0; // value to be returned
16219 $spacing = trim($spacing);
16220 switch ($spacing) {
16226 if ($parent == 'normal') {
16234 $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
16241 * Returns the percentage of font stretching from CSS value
16242 * @param $stretch (string) stretch mode
16243 * @param $parent (float) stretch value of the parent element
16244 * @return float font stretching percentage
16246 * @since 5.9.000 (2010-10-02)
16248 protected function getCSSFontStretching($stretch, $parent=100) {
16249 $val = 100; // value to be returned
16250 $stretch = trim($stretch);
16251 switch ($stretch) {
16252 case 'ultra-condensed': {
16256 case 'extra-condensed': {
16260 case 'condensed': {
16264 case 'semi-condensed': {
16272 case 'semi-expanded': {
16280 case 'extra-expanded': {
16284 case 'ultra-expanded': {
16289 $val = ($parent +
10);
16293 $val = ($parent - 10);
16297 if ($parent == 'normal') {
16305 $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
16312 * Convert HTML string containing font size value to points
16313 * @param $val (string) String containing font size value and unit.
16314 * @param $refsize (float) Reference font size in points.
16315 * @param $parent_size (float) Parent font size in points.
16316 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
16317 * @return float value in points
16320 public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') {
16321 $refsize = TCPDF_FONTS
::getFontRefSize($refsize);
16322 $parent_size = TCPDF_FONTS
::getFontRefSize($parent_size, $refsize);
16325 $size = ($refsize - 4);
16329 $size = ($refsize - 3);
16333 $size = ($refsize - 2);
16341 $size = ($refsize +
2);
16345 $size = ($refsize +
4);
16349 $size = ($refsize +
6);
16353 $size = ($parent_size - 3);
16357 $size = ($parent_size +
3);
16361 $size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true);
16368 * Returns the HTML DOM array.
16369 * @param $html (string) html code
16372 * @since 3.2.000 (2008-06-20)
16374 protected function getHtmlDomArray($html) {
16375 // array of CSS styles ( selector => properties).
16377 // get CSS array defined at previous call
16378 $matches = array();
16379 if (preg_match_all('/<cssarray>([^\<]*)<\/cssarray>/isU', $html, $matches) > 0) {
16380 if (isset($matches[1][0])) {
16381 $css = array_merge($css, json_decode($this->unhtmlentities($matches[1][0]), true));
16383 $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
16385 // extract external CSS files
16386 $matches = array();
16387 if (preg_match_all('/<link([^\>]*)>/isU', $html, $matches) > 0) {
16388 foreach ($matches[1] as $key => $link) {
16390 if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
16392 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
16393 // get 'all' and 'print' media, other media types are discarded
16394 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16395 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16397 if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
16398 // read CSS data file
16399 $cssdata = TCPDF_STATIC
::fileGetContents(trim($type[1]));
16400 if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) {
16401 $css = array_merge($css, TCPDF_STATIC
::extractCSSproperties($cssdata));
16408 // extract style tags
16409 $matches = array();
16410 if (preg_match_all('/<style([^\>]*)>([^\<]*)<\/style>/isU', $html, $matches) > 0) {
16411 foreach ($matches[1] as $key => $media) {
16413 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
16414 // get 'all' and 'print' media, other media types are discarded
16415 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16416 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16417 $cssdata = $matches[2][$key];
16418 $css = array_merge($css, TCPDF_STATIC
::extractCSSproperties($cssdata));
16422 // create a special tag to contain the CSS array (used for table content)
16423 $csstagarray = '<cssarray>'.htmlentities(json_encode($css)).'</cssarray>';
16424 // remove head and style blocks
16425 $html = preg_replace('/<head([^\>]*)>(.*?)<\/head>/siU', '', $html);
16426 $html = preg_replace('/<style([^\>]*)>([^\<]*)<\/style>/isU', '', $html);
16427 // define block tags
16428 $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
16429 // define self-closing tags
16430 $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
16431 // remove all unsupported tags (the line below lists all supported tags)
16432 $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>');
16433 //replace some blank characters
16434 $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
16435 $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);
16436 $html = preg_replace('@(\r\n|\r)@', "\n", $html);
16437 $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
16438 $html = strtr($html, $repTable);
16440 while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
16441 $html_a = substr($html, 0, $offset);
16442 $html_b = substr($html, $offset, ($pos - $offset +
6));
16443 while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
16444 // preserve newlines on <pre> tag
16445 $html_b = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
16447 while (preg_match("'<xre([^\>]*)>(.*?)".$this->re_space
['p']."(.*?)</pre>'".$this->re_space
['m'], $html_b)) {
16448 // preserve spaces on <pre> tag
16449 $html_b = preg_replace("'<xre([^\>]*)>(.*?)".$this->re_space
['p']."(.*?)</pre>'".$this->re_space
['m'], "<xre\\1>\\2 \\3</pre>", $html_b);
16451 $html = $html_a.$html_b.substr($html, $pos +
6);
16452 $offset = strlen($html_a.$html_b);
16455 while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
16456 $html_a = substr($html, 0, $offset);
16457 $html_b = substr($html, $offset, ($pos - $offset +
11));
16458 while (preg_match("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
16459 // preserve newlines on <textarea> tag
16460 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
16461 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
16463 $html = $html_a.$html_b.substr($html, $pos +
11);
16464 $offset = strlen($html_a.$html_b);
16466 $html = preg_replace('/([\s]*)<option/si', '<option', $html);
16467 $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
16469 while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
16470 $html_a = substr($html, 0, $offset);
16471 $html_b = substr($html, $offset, ($pos - $offset +
9));
16472 while (preg_match("'<option([^\>]*)>(.*?)</option>'si", $html_b)) {
16473 $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
16474 $html_b = preg_replace("'<option([^\>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
16476 $html = $html_a.$html_b.substr($html, $pos +
9);
16477 $offset = strlen($html_a.$html_b);
16479 if (preg_match("'</select'si", $html)) {
16480 $html = preg_replace("'<select([^\>]*)>'si", "<select\\1 opt=\"", $html);
16481 $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
16483 $html = str_replace("\n", ' ', $html);
16484 // restore textarea newlines
16485 $html = str_replace('<TBR>', "\n", $html);
16486 // remove extra spaces from code
16487 $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
16488 $html = preg_replace('/'.$this->re_space
['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space
['m'], '</\\1>', $html);
16489 $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
16490 $html = preg_replace('/'.$this->re_space
['p'].'+<(ul|ol|dl|br)/'.$this->re_space
['m'], '<\\1', $html);
16491 $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);
16492 $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
16493 $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
16494 $html = preg_replace('/'.$this->re_space
['p'].'+<img/'.$this->re_space
['m'], chr(32).'<img', $html);
16495 $html = preg_replace('/<img([^\>]*)>[\s]+([^\<])/xi', '<img\\1> \\2', $html);
16496 $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
16497 $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
16498 $html = preg_replace('/<textarea([^\>]*)>([^\<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
16499 $html = preg_replace('/<li([^\>]*)><\/li>/', '<li\\1> </li>', $html);
16500 $html = preg_replace('/<li([^\>]*)>'.$this->re_space
['p'].'*<img/'.$this->re_space
['m'], '<li\\1><font size="1"> </font><img', $html);
16501 $html = preg_replace('/<([^\>\/]*)>[\s]/', '<\\1> ', $html); // preserve some spaces
16502 $html = preg_replace('/[\s]<\/([^\>]*)>/', ' </\\1>', $html); // preserve some spaces
16503 $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment
16504 $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment
16505 $html = preg_replace('/'.$this->re_space
['p'].'+/'.$this->re_space
['m'], chr(32), $html); // replace multiple spaces with a single space
16507 $html = $this->stringTrim($html);
16508 // fix br tag after li
16509 $html = preg_replace('/<li><br([^\>]*)>/', '<li> <br\\1>', $html);
16510 // fix first image tag alignment
16511 $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
16512 // pattern for generic tag
16513 $tagpattern = '/(<[^>]+>)/';
16514 // explodes the string
16515 $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE
| PREG_SPLIT_NO_EMPTY
);
16517 $maxel = count($a);
16520 // create an array of elements
16522 $dom[$key] = array();
16523 // set inheritable properties fot the first void element
16524 // 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
16525 $dom[$key]['tag'] = false;
16526 $dom[$key]['block'] = false;
16527 $dom[$key]['value'] = '';
16528 $dom[$key]['parent'] = 0;
16529 $dom[$key]['hide'] = false;
16530 $dom[$key]['fontname'] = $this->FontFamily
;
16531 $dom[$key]['fontstyle'] = $this->FontStyle
;
16532 $dom[$key]['fontsize'] = $this->FontSizePt
;
16533 $dom[$key]['font-stretch'] = $this->font_stretching
;
16534 $dom[$key]['letter-spacing'] = $this->font_spacing
;
16535 $dom[$key]['stroke'] = $this->textstrokewidth
;
16536 $dom[$key]['fill'] = (($this->textrendermode %
2) == 0);
16537 $dom[$key]['clip'] = ($this->textrendermode
> 3);
16538 $dom[$key]['line-height'] = $this->cell_height_ratio
;
16539 $dom[$key]['bgcolor'] = false;
16540 $dom[$key]['fgcolor'] = $this->fgcolor
; // color
16541 $dom[$key]['strokecolor'] = $this->strokecolor
;
16542 $dom[$key]['align'] = '';
16543 $dom[$key]['listtype'] = '';
16544 $dom[$key]['text-indent'] = 0;
16545 $dom[$key]['text-transform'] = '';
16546 $dom[$key]['border'] = array();
16547 $dom[$key]['dir'] = $this->rtl
?'rtl':'ltr';
16548 $thead = false; // true when we are inside the THEAD tag
16551 array_push($level, 0); // root
16552 while ($elkey < $maxel) {
16553 $dom[$key] = array();
16554 $element = $a[$elkey];
16555 $dom[$key]['elkey'] = $elkey;
16556 if (preg_match($tagpattern, $element)) {
16558 $element = substr($element, 1, -1);
16560 preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
16561 $tagname = strtolower($tag[1]);
16562 // check if we are inside a table header
16563 if ($tagname == 'thead') {
16564 if ($element[0] == '/') {
16572 $dom[$key]['tag'] = true;
16573 $dom[$key]['value'] = $tagname;
16574 if (in_array($dom[$key]['value'], $blocktags)) {
16575 $dom[$key]['block'] = true;
16577 $dom[$key]['block'] = false;
16579 if ($element[0] == '/') {
16580 // *** closing html tag
16581 $dom[$key]['opening'] = false;
16582 $dom[$key]['parent'] = end($level);
16584 $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
16585 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
16586 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
16587 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
16588 $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
16589 $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
16590 $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
16591 $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
16592 $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
16593 $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
16594 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
16595 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
16596 $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
16597 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
16598 $dom[$key]['text-transform'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['text-transform'];
16599 $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
16600 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
16601 $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
16603 // set the number of columns in table tag
16604 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
16605 $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
16607 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
16608 $dom[($dom[$key]['parent'])]['content'] = $csstagarray;
16609 for ($i = ($dom[$key]['parent'] +
1); $i < $key; ++
$i) {
16610 $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
16613 // mark nested tables
16614 $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
16615 // remove thead sections from nested tables
16616 $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
16617 $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
16619 // store header rows on a new table
16620 if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
16621 if (TCPDF_STATIC
::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
16622 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
16624 for ($i = $dom[$key]['parent']; $i <= $key; ++
$i) {
16625 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
16627 if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
16628 $dom[($dom[$key]['parent'])]['attribute'] = array();
16630 // header elements must be always contained in a single page
16631 $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
16633 if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC
::empty_string($dom[($dom[$key]['parent'])]['thead']))) {
16634 // remove the nobr attributes from the table header
16635 $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
16636 $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
16639 // *** opening or self-closing html tag
16640 $dom[$key]['opening'] = true;
16641 $dom[$key]['parent'] = end($level);
16642 if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
16643 // self-closing tag
16644 $dom[$key]['self'] = true;
16647 array_push($level, $key);
16648 $dom[$key]['self'] = false;
16650 // copy some values from parent
16653 $parentkey = $dom[$key]['parent'];
16654 $dom[$key]['hide'] = $dom[$parentkey]['hide'];
16655 $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
16656 $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
16657 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
16658 $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
16659 $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
16660 $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
16661 $dom[$key]['fill'] = $dom[$parentkey]['fill'];
16662 $dom[$key]['clip'] = $dom[$parentkey]['clip'];
16663 $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16664 $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
16665 $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
16666 $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
16667 $dom[$key]['align'] = $dom[$parentkey]['align'];
16668 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16669 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16670 $dom[$key]['text-transform'] = $dom[$parentkey]['text-transform'];
16671 $dom[$key]['border'] = array();
16672 $dom[$key]['dir'] = $dom[$parentkey]['dir'];
16675 preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER
);
16676 $dom[$key]['attribute'] = array(); // reset attribute array
16677 while (list($id, $name) = each($attr_array[1])) {
16678 $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
16680 if (!empty($css)) {
16681 // merge CSS style to current style
16682 list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC
::getCSSdataArray($dom, $key, $css);
16683 $dom[$key]['attribute']['style'] = TCPDF_STATIC
::getTagStyleFromCSSarray($dom[$key]['cssdata']);
16685 // split style attributes
16686 if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
16687 // get style attributes
16688 preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER
);
16689 $dom[$key]['style'] = array(); // reset style attribute array
16690 while (list($id, $name) = each($style_array[1])) {
16691 // in case of duplicate attribute the last replace the previous
16692 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
16694 // --- get some style attributes ---
16696 if (isset($dom[$key]['style']['direction'])) {
16697 $dom[$key]['dir'] = $dom[$key]['style']['direction'];
16700 if (isset($dom[$key]['style']['display'])) {
16701 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
16704 if (isset($dom[$key]['style']['font-family'])) {
16705 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
16708 if (isset($dom[$key]['style']['list-style-type'])) {
16709 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
16710 if ($dom[$key]['listtype'] == 'inherit') {
16711 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16715 if (isset($dom[$key]['style']['text-indent'])) {
16716 $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
16717 if ($dom[$key]['text-indent'] == 'inherit') {
16718 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16722 if (isset($dom[$key]['style']['text-transform'])) {
16723 $dom[$key]['text-transform'] = $dom[$key]['style']['text-transform'];
16726 if (isset($dom[$key]['style']['font-size'])) {
16727 $fsize = trim($dom[$key]['style']['font-size']);
16728 $dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt');
16731 if (isset($dom[$key]['style']['font-stretch'])) {
16732 $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
16735 if (isset($dom[$key]['style']['letter-spacing'])) {
16736 $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
16738 // line-height (internally is the cell height ratio)
16739 if (isset($dom[$key]['style']['line-height'])) {
16740 $lineheight = trim($dom[$key]['style']['line-height']);
16741 switch ($lineheight) {
16742 // A normal line height. This is default
16744 $dom[$key]['line-height'] = $dom[0]['line-height'];
16748 $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16751 if (is_numeric($lineheight)) {
16752 // convert to percentage of font height
16753 $lineheight = ($lineheight * 100).'%';
16755 $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
16756 if (substr($lineheight, -1) !== '%') {
16757 if ($dom[$key]['fontsize'] <= 0) {
16758 $dom[$key]['line-height'] = 1;
16760 $dom[$key]['line-height'] = (($dom[$key]['line-height'] - $this->cell_padding
['T'] - $this->cell_padding
['B']) / $dom[$key]['fontsize']);
16767 if (isset($dom[$key]['style']['font-weight'])) {
16768 if (strtolower($dom[$key]['style']['font-weight'][0]) == 'n') {
16769 if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
16770 $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
16772 } elseif (strtolower($dom[$key]['style']['font-weight'][0]) == 'b') {
16773 $dom[$key]['fontstyle'] .= 'B';
16776 if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style'][0]) == 'i')) {
16777 $dom[$key]['fontstyle'] .= 'I';
16780 if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['style']['color']))) {
16781 $dom[$key]['fgcolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors
);
16782 } elseif ($dom[$key]['value'] == 'a') {
16783 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray
;
16785 // background color
16786 if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['style']['background-color']))) {
16787 $dom[$key]['bgcolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors
);
16790 if (isset($dom[$key]['style']['text-decoration'])) {
16791 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
16792 foreach ($decors as $dec) {
16794 if (!TCPDF_STATIC
::empty_string($dec)) {
16795 if ($dec[0] == 'u') {
16797 $dom[$key]['fontstyle'] .= 'U';
16798 } elseif ($dec[0] == 'l') {
16800 $dom[$key]['fontstyle'] .= 'D';
16801 } elseif ($dec[0] == 'o') {
16803 $dom[$key]['fontstyle'] .= 'O';
16807 } elseif ($dom[$key]['value'] == 'a') {
16808 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle
;
16810 // check for width attribute
16811 if (isset($dom[$key]['style']['width'])) {
16812 $dom[$key]['width'] = $dom[$key]['style']['width'];
16814 // check for height attribute
16815 if (isset($dom[$key]['style']['height'])) {
16816 $dom[$key]['height'] = $dom[$key]['style']['height'];
16818 // check for text alignment
16819 if (isset($dom[$key]['style']['text-align'])) {
16820 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align'][0]);
16822 // check for CSS border properties
16823 if (isset($dom[$key]['style']['border'])) {
16824 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
16825 if (!empty($borderstyle)) {
16826 $dom[$key]['border']['LTRB'] = $borderstyle;
16829 if (isset($dom[$key]['style']['border-color'])) {
16830 $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
16831 if (isset($brd_colors[3])) {
16832 $dom[$key]['border']['L']['color'] = TCPDF_COLORS
::convertHTMLColorToDec($brd_colors[3], $this->spot_colors
);
16834 if (isset($brd_colors[1])) {
16835 $dom[$key]['border']['R']['color'] = TCPDF_COLORS
::convertHTMLColorToDec($brd_colors[1], $this->spot_colors
);
16837 if (isset($brd_colors[0])) {
16838 $dom[$key]['border']['T']['color'] = TCPDF_COLORS
::convertHTMLColorToDec($brd_colors[0], $this->spot_colors
);
16840 if (isset($brd_colors[2])) {
16841 $dom[$key]['border']['B']['color'] = TCPDF_COLORS
::convertHTMLColorToDec($brd_colors[2], $this->spot_colors
);
16844 if (isset($dom[$key]['style']['border-width'])) {
16845 $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
16846 if (isset($brd_widths[3])) {
16847 $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
16849 if (isset($brd_widths[1])) {
16850 $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
16852 if (isset($brd_widths[0])) {
16853 $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
16855 if (isset($brd_widths[2])) {
16856 $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
16859 if (isset($dom[$key]['style']['border-style'])) {
16860 $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
16861 if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) {
16862 $dom[$key]['border']['L']['cap'] = 'square';
16863 $dom[$key]['border']['L']['join'] = 'miter';
16864 $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
16865 if ($dom[$key]['border']['L']['dash'] < 0) {
16866 $dom[$key]['border']['L'] = array();
16869 if (isset($brd_styles[1])) {
16870 $dom[$key]['border']['R']['cap'] = 'square';
16871 $dom[$key]['border']['R']['join'] = 'miter';
16872 $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
16873 if ($dom[$key]['border']['R']['dash'] < 0) {
16874 $dom[$key]['border']['R'] = array();
16877 if (isset($brd_styles[0])) {
16878 $dom[$key]['border']['T']['cap'] = 'square';
16879 $dom[$key]['border']['T']['join'] = 'miter';
16880 $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
16881 if ($dom[$key]['border']['T']['dash'] < 0) {
16882 $dom[$key]['border']['T'] = array();
16885 if (isset($brd_styles[2])) {
16886 $dom[$key]['border']['B']['cap'] = 'square';
16887 $dom[$key]['border']['B']['join'] = 'miter';
16888 $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
16889 if ($dom[$key]['border']['B']['dash'] < 0) {
16890 $dom[$key]['border']['B'] = array();
16894 $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
16895 foreach ($cellside as $bsk => $bsv) {
16896 if (isset($dom[$key]['style']['border-'.$bsv])) {
16897 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
16898 if (!empty($borderstyle)) {
16899 $dom[$key]['border'][$bsk] = $borderstyle;
16902 if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
16903 $dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors
);
16905 if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
16906 $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
16908 if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
16909 $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
16910 if ($dom[$key]['border'][$bsk]['dash'] < 0) {
16911 $dom[$key]['border'][$bsk] = array();
16915 // check for CSS padding properties
16916 if (isset($dom[$key]['style']['padding'])) {
16917 $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
16919 $dom[$key]['padding'] = $this->cell_padding
;
16921 foreach ($cellside as $psk => $psv) {
16922 if (isset($dom[$key]['style']['padding-'.$psv])) {
16923 $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
16926 // check for CSS margin properties
16927 if (isset($dom[$key]['style']['margin'])) {
16928 $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
16930 $dom[$key]['margin'] = $this->cell_margin
;
16932 foreach ($cellside as $psk => $psv) {
16933 if (isset($dom[$key]['style']['margin-'.$psv])) {
16934 $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
16937 // check for CSS border-spacing properties
16938 if (isset($dom[$key]['style']['border-spacing'])) {
16939 $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
16941 // page-break-inside
16942 if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
16943 $dom[$key]['attribute']['nobr'] = 'true';
16945 // page-break-before
16946 if (isset($dom[$key]['style']['page-break-before'])) {
16947 if ($dom[$key]['style']['page-break-before'] == 'always') {
16948 $dom[$key]['attribute']['pagebreak'] = 'true';
16949 } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
16950 $dom[$key]['attribute']['pagebreak'] = 'left';
16951 } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
16952 $dom[$key]['attribute']['pagebreak'] = 'right';
16955 // page-break-after
16956 if (isset($dom[$key]['style']['page-break-after'])) {
16957 if ($dom[$key]['style']['page-break-after'] == 'always') {
16958 $dom[$key]['attribute']['pagebreakafter'] = 'true';
16959 } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
16960 $dom[$key]['attribute']['pagebreakafter'] = 'left';
16961 } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
16962 $dom[$key]['attribute']['pagebreakafter'] = 'right';
16966 if (isset($dom[$key]['attribute']['display'])) {
16967 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
16969 if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
16970 $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
16971 if (!empty($borderstyle)) {
16972 $dom[$key]['border']['LTRB'] = $borderstyle;
16975 // check for font tag
16976 if ($dom[$key]['value'] == 'font') {
16978 if (isset($dom[$key]['attribute']['face'])) {
16979 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
16982 if (isset($dom[$key]['attribute']['size'])) {
16984 if ($dom[$key]['attribute']['size'][0] == '+') {
16985 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] +
intval(substr($dom[$key]['attribute']['size'], 1));
16986 } elseif ($dom[$key]['attribute']['size'][0] == '-') {
16987 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
16989 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16992 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16996 // force natural alignment for lists
16997 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
16998 AND (!isset($dom[$key]['align']) OR TCPDF_STATIC
::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
17000 $dom[$key]['align'] = 'R';
17002 $dom[$key]['align'] = 'L';
17005 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
17006 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
17007 $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO
;
17010 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
17011 $dom[$key]['fontstyle'] .= 'B';
17013 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
17014 $dom[$key]['fontstyle'] .= 'I';
17016 if ($dom[$key]['value'] == 'u') {
17017 $dom[$key]['fontstyle'] .= 'U';
17019 if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
17020 $dom[$key]['fontstyle'] .= 'D';
17022 if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
17023 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle
;
17025 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
17026 $dom[$key]['fontname'] = $this->default_monospaced_font
;
17028 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)) {
17029 // headings h1, h2, h3, h4, h5, h6
17030 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
17031 $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
17032 $dom[$key]['fontsize'] = $dom[0]['fontsize'] +
$headsize;
17034 if (!isset($dom[$key]['style']['font-weight'])) {
17035 $dom[$key]['fontstyle'] .= 'B';
17038 if (($dom[$key]['value'] == 'table')) {
17039 $dom[$key]['rows'] = 0; // number of rows
17040 $dom[$key]['trids'] = array(); // IDs of TR elements
17041 $dom[$key]['thead'] = ''; // table header rows
17043 if (($dom[$key]['value'] == 'tr')) {
17044 $dom[$key]['cols'] = 0;
17046 $dom[$key]['thead'] = true;
17047 // rows on thead block are printed as a separate table
17049 $dom[$key]['thead'] = false;
17050 // store the number of rows on table element
17051 ++
$dom[($dom[$key]['parent'])]['rows'];
17052 // store the TR elements IDs on table element
17053 array_push($dom[($dom[$key]['parent'])]['trids'], $key);
17056 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
17057 if (isset($dom[$key]['attribute']['colspan'])) {
17058 $colspan = intval($dom[$key]['attribute']['colspan']);
17062 $dom[$key]['attribute']['colspan'] = $colspan;
17063 $dom[($dom[$key]['parent'])]['cols'] +
= $colspan;
17066 if (isset($dom[$key]['attribute']['dir'])) {
17067 $dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
17069 // set foreground color attribute
17070 if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['attribute']['color']))) {
17071 $dom[$key]['fgcolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors
);
17072 } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
17073 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray
;
17075 // set background color attribute
17076 if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['attribute']['bgcolor']))) {
17077 $dom[$key]['bgcolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors
);
17079 // set stroke color attribute
17080 if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['attribute']['strokecolor']))) {
17081 $dom[$key]['strokecolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors
);
17083 // check for width attribute
17084 if (isset($dom[$key]['attribute']['width'])) {
17085 $dom[$key]['width'] = $dom[$key]['attribute']['width'];
17087 // check for height attribute
17088 if (isset($dom[$key]['attribute']['height'])) {
17089 $dom[$key]['height'] = $dom[$key]['attribute']['height'];
17091 // check for text alignment
17092 if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
17093 $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align'][0]);
17095 // check for text rendering mode (the following attributes do not exist in HTML)
17096 if (isset($dom[$key]['attribute']['stroke'])) {
17097 // font stroke width
17098 $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
17100 if (isset($dom[$key]['attribute']['fill'])) {
17102 if ($dom[$key]['attribute']['fill'] == 'true') {
17103 $dom[$key]['fill'] = true;
17105 $dom[$key]['fill'] = false;
17108 if (isset($dom[$key]['attribute']['clip'])) {
17110 if ($dom[$key]['attribute']['clip'] == 'true') {
17111 $dom[$key]['clip'] = true;
17113 $dom[$key]['clip'] = false;
17116 } // end opening tag
17119 $dom[$key]['tag'] = false;
17120 $dom[$key]['block'] = false;
17121 $dom[$key]['parent'] = end($level);
17122 $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
17123 if (!empty($dom[$dom[$key]['parent']]['text-transform'])) {
17124 // text-transform for unicode requires mb_convert_case (Multibyte String Functions)
17125 if (function_exists('mb_convert_case')) {
17126 $ttm = array('capitalize' => MB_CASE_TITLE
, 'uppercase' => MB_CASE_UPPER
, 'lowercase' => MB_CASE_LOWER
);
17127 if (isset($ttm[$dom[$dom[$key]['parent']]['text-transform']])) {
17128 $element = mb_convert_case($element, $ttm[$dom[$dom[$key]['parent']]['text-transform']], $this->encoding
);
17130 } elseif (!$this->isunicode
) {
17131 switch ($dom[$dom[$key]['parent']]['text-transform']) {
17132 case 'capitalize': {
17133 $element = ucwords(strtolower($element));
17136 case 'uppercase': {
17137 $element = strtoupper($element);
17140 case 'lowercase': {
17141 $element = strtolower($element);
17147 $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
17156 * Returns the string used to find spaces
17159 * @author Nicola Asuni
17160 * @since 4.8.024 (2010-01-15)
17162 protected function getSpaceString() {
17163 $spacestr = chr(32);
17164 if ($this->isUnicodeFont()) {
17165 $spacestr = chr(0).chr(32);
17171 * Serialize an array of parameters to be used with TCPDF tag in HTML code.
17172 * @param $pararray (array) parameters array
17173 * @return sting containing serialized data
17174 * @since 4.9.006 (2010-04-02)
17178 public function serializeTCPDFtagParameters($pararray) {
17179 return TCPDF_STATIC
::serializeTCPDFtagParameters($pararray);
17183 * Prints a cell (rectangular area) with optional borders, background color and html text string.
17184 * 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 />
17185 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
17186 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
17187 * 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
17188 * NOTE: all the HTML attributes must be enclosed in double-quote.
17189 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
17190 * @param $h (float) Cell minimum height. The cell extends automatically if needed.
17191 * @param $x (float) upper-left corner X coordinate
17192 * @param $y (float) upper-left corner Y coordinate
17193 * @param $html (string) html text to print. Default value: empty string.
17194 * @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)))
17195 * @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>
17196 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
17197 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
17198 * @param $reseth (boolean) if true reset the last cell height (default true).
17199 * @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>
17200 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
17201 * @see Multicell(), writeHTML()
17204 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
17205 return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
17209 * Allows to preserve some HTML formatting (limited support).<br />
17210 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
17211 * 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
17212 * NOTE: all the HTML attributes must be enclosed in double-quote.
17213 * @param $html (string) text to display
17214 * @param $ln (boolean) if true add a new line after text (default = true)
17215 * @param $fill (boolean) Indicates if the background must be painted (true) or transparent (false).
17216 * @param $reseth (boolean) if true reset the last cell height (default false).
17217 * @param $cell (boolean) if true add the current left (or right for RTL) padding to each Write (default false).
17218 * @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>
17221 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
17222 $gvars = $this->getGraphicVars();
17223 // store current values
17224 $prev_cell_margin = $this->cell_margin
;
17225 $prev_cell_padding = $this->cell_padding
;
17226 $prevPage = $this->page
;
17227 $prevlMargin = $this->lMargin
;
17228 $prevrMargin = $this->rMargin
;
17229 $curfontname = $this->FontFamily
;
17230 $curfontstyle = $this->FontStyle
;
17231 $curfontsize = $this->FontSizePt
;
17232 $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
17233 $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
17234 $curfontstretcing = $this->font_stretching
;
17235 $curfonttracking = $this->font_spacing
;
17236 $this->newline
= true;
17238 $startlinepage = $this->page
;
17239 $minstartliney = $this->y
;
17240 $maxbottomliney = 0;
17241 $startlinex = $this->x
;
17242 $startliney = $this->y
;
17246 $this_method_vars = array();
17248 $fontaligned = false;
17249 $reverse_dir = false; // true when the text direction is reversed
17250 $this->premode
= false;
17251 if ($this->inxobj
) {
17252 // we are inside an XObject template
17253 $pask = count($this->xobjects
[$this->xobjid
]['annotations']);
17254 } elseif (isset($this->PageAnnots
[$this->page
])) {
17255 $pask = count($this->PageAnnots
[$this->page
]);
17259 if ($this->inxobj
) {
17260 // we are inside an XObject template
17261 $startlinepos = strlen($this->xobjects
[$this->xobjid
]['outdata']);
17262 } elseif (!$this->InFooter
) {
17263 if (isset($this->footerlen
[$this->page
])) {
17264 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
] - $this->footerlen
[$this->page
];
17266 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
];
17268 $startlinepos = $this->footerpos
[$this->page
];
17270 // we are inside the footer
17271 $startlinepos = $this->pagelen
[$this->page
];
17276 $w = $this->x
- $this->lMargin
;
17278 $w = $this->w
- $this->rMargin
- $this->x
;
17280 $w -= ($this->cell_padding
['L'] +
$this->cell_padding
['R']);
17283 $this->x
-= $this->cell_padding
['R'];
17284 $this->lMargin +
= $this->cell_padding
['R'];
17286 $this->x +
= $this->cell_padding
['L'];
17287 $this->rMargin +
= $this->cell_padding
['L'];
17290 if ($this->customlistindent
>= 0) {
17291 $this->listindent
= $this->customlistindent
;
17293 $this->listindent
= $this->GetStringWidth('000000');
17295 $this->listindentlevel
= 0;
17296 // save previous states
17297 $prev_cell_height_ratio = $this->cell_height_ratio
;
17298 $prev_listnum = $this->listnum
;
17299 $prev_listordered = $this->listordered
;
17300 $prev_listcount = $this->listcount
;
17301 $prev_lispacer = $this->lispacer
;
17302 $this->listnum
= 0;
17303 $this->listordered
= array();
17304 $this->listcount
= array();
17305 $this->lispacer
= '';
17306 if ((TCPDF_STATIC
::empty_string($this->lasth
)) OR ($reseth)) {
17307 // reset row height
17308 $this->resetLastH();
17310 $dom = $this->getHtmlDomArray($html);
17311 $maxel = count($dom);
17313 while ($key < $maxel) {
17314 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND $dom[$key]['hide']) {
17315 // store the node key
17316 $hidden_node_key = $key;
17317 if ($dom[$key]['self']) {
17318 // skip just this self-closing tag
17321 // skip this and all children tags
17322 while (($key < $maxel) AND (!$dom[$key]['tag'] OR $dom[$key]['opening'] OR ($dom[$key]['parent'] != $hidden_node_key))) {
17323 // skip hidden objects
17329 if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
17330 // check for pagebreak
17331 if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
17332 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17333 $this->checkPageBreak($this->PageBreakTrigger +
1);
17334 $this->htmlvspace
= ($this->PageBreakTrigger +
1);
17336 if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl
) AND (($this->page %
2) == 0)) OR (($this->rtl
) AND (($this->page %
2) != 0))))
17337 OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl
) AND (($this->page %
2) != 0)) OR (($this->rtl
) AND (($this->page %
2) == 0))))) {
17338 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17339 $this->checkPageBreak($this->PageBreakTrigger +
1);
17340 $this->htmlvspace
= ($this->PageBreakTrigger +
1);
17343 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
17344 if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
17345 $dom[$key]['attribute']['nobr'] = false;
17347 // store current object
17348 $this->startTransaction();
17349 // save this method vars
17350 $this_method_vars['html'] = $html;
17351 $this_method_vars['ln'] = $ln;
17352 $this_method_vars['fill'] = $fill;
17353 $this_method_vars['reseth'] = $reseth;
17354 $this_method_vars['cell'] = $cell;
17355 $this_method_vars['align'] = $align;
17356 $this_method_vars['gvars'] = $gvars;
17357 $this_method_vars['prevPage'] = $prevPage;
17358 $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
17359 $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
17360 $this_method_vars['prevlMargin'] = $prevlMargin;
17361 $this_method_vars['prevrMargin'] = $prevrMargin;
17362 $this_method_vars['curfontname'] = $curfontname;
17363 $this_method_vars['curfontstyle'] = $curfontstyle;
17364 $this_method_vars['curfontsize'] = $curfontsize;
17365 $this_method_vars['curfontascent'] = $curfontascent;
17366 $this_method_vars['curfontdescent'] = $curfontdescent;
17367 $this_method_vars['curfontstretcing'] = $curfontstretcing;
17368 $this_method_vars['curfonttracking'] = $curfonttracking;
17369 $this_method_vars['minstartliney'] = $minstartliney;
17370 $this_method_vars['maxbottomliney'] = $maxbottomliney;
17371 $this_method_vars['yshift'] = $yshift;
17372 $this_method_vars['startlinepage'] = $startlinepage;
17373 $this_method_vars['startlinepos'] = $startlinepos;
17374 $this_method_vars['startlinex'] = $startlinex;
17375 $this_method_vars['startliney'] = $startliney;
17376 $this_method_vars['newline'] = $newline;
17377 $this_method_vars['loop'] = $loop;
17378 $this_method_vars['curpos'] = $curpos;
17379 $this_method_vars['pask'] = $pask;
17380 $this_method_vars['lalign'] = $lalign;
17381 $this_method_vars['plalign'] = $plalign;
17382 $this_method_vars['w'] = $w;
17383 $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
17384 $this_method_vars['prev_listnum'] = $prev_listnum;
17385 $this_method_vars['prev_listordered'] = $prev_listordered;
17386 $this_method_vars['prev_listcount'] = $prev_listcount;
17387 $this_method_vars['prev_lispacer'] = $prev_lispacer;
17388 $this_method_vars['fontaligned'] = $fontaligned;
17389 $this_method_vars['key'] = $key;
17390 $this_method_vars['dom'] = $dom;
17393 // print THEAD block
17394 if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
17395 if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC
::empty_string($dom[$dom[$key]['parent']]['thead'])) {
17396 $this->inthead
= true;
17397 // print table header (thead)
17398 $this->writeHTML($this->thead
, false, false, false, false, '');
17399 // check if we are on a new page or on a new column
17400 if (($this->y
< $this->start_transaction_y
) OR ($this->checkPageBreak($this->lasth
, '', false))) {
17401 // we are on a new page or on a new column and the total object height is less than the available vertical space.
17402 // restore previous object
17403 $this->rollbackTransaction(true);
17404 // restore previous values
17405 foreach ($this_method_vars as $vkey => $vval) {
17408 // disable table header
17409 $tmp_thead = $this->thead
;
17411 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17413 if ((!$this->checkPageBreak($this->PageBreakTrigger +
1)) AND ($this->y
< $pre_y)) {
17414 // fix for multicolumn mode
17415 $startliney = $this->y
;
17417 $this->start_transaction_page
= $this->page
;
17418 $this->start_transaction_y
= $this->y
;
17419 // restore table header
17420 $this->thead
= $tmp_thead;
17421 // fix table border properties
17422 if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
17423 $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
17424 } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
17425 $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
17427 $tmp_cellspacing = 0;
17429 $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page
;
17430 $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column
;
17431 $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y +
$tmp_cellspacing;
17432 $xoffset = ($this->x
- $dom[$dom[$key]['parent']]['borderposition']['x']);
17433 $dom[$dom[$key]['parent']]['borderposition']['x'] +
= $xoffset;
17434 $dom[$dom[$key]['parent']]['borderposition']['xmax'] +
= $xoffset;
17435 // print table header (thead)
17436 $this->writeHTML($this->thead
, false, false, false, false, '');
17439 // move $key index forward to skip THEAD block
17440 while ( ($key < $maxel) AND (!(
17441 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
17442 OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
17446 if ($dom[$key]['tag'] OR ($key == 0)) {
17447 if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
17448 $dom[$key]['align'] = ($this->rtl
) ? 'R' : 'L';
17450 // vertically align image in line
17451 if ((!$this->newline
) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
17452 // get image height
17453 $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], ($dom[$key]['fontsize'] / $this->k
), 'px');
17454 $autolinebreak = false;
17455 if (!empty($dom[$key]['width'])) {
17456 $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], ($dom[$key]['fontsize'] / $this->k
), 'px', false);
17457 if (($imgw <= ($this->w
- $this->lMargin
- $this->rMargin
- $this->cell_padding
['L'] - $this->cell_padding
['R']))
17458 AND ((($this->rtl
) AND (($this->x
- $imgw) < ($this->lMargin +
$this->cell_padding
['L'])))
17459 OR ((!$this->rtl
) AND (($this->x +
$imgw) > ($this->w
- $this->rMargin
- $this->cell_padding
['R']))))) {
17460 // add automatic line break
17461 $autolinebreak = true;
17462 $this->Ln('', $cell);
17463 if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
17464 // go back to evaluate this line break
17469 if (!$autolinebreak) {
17470 if ($this->inPageBody()) {
17472 // check for page break
17473 if ((!$this->checkPageBreak($imgh)) AND ($this->y
< $pre_y)) {
17474 // fix for multicolumn mode
17475 $startliney = $this->y
;
17478 if ($this->page
> $startlinepage) {
17479 // fix line splitted over two pages
17480 if (isset($this->footerlen
[$startlinepage])) {
17481 $curpos = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
17483 // line to be moved one page forward
17484 $pagebuff = $this->getPageBuffer($startlinepage);
17485 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17486 $tstart = substr($pagebuff, 0, $startlinepos);
17487 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17488 // remove line from previous page
17489 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17490 $pagebuff = $this->getPageBuffer($this->page
);
17491 $tstart = substr($pagebuff, 0, $this->cntmrk
[$this->page
]);
17492 $tend = substr($pagebuff, $this->cntmrk
[$this->page
]);
17493 // add line start to current page
17494 $yshift = ($minstartliney - $this->y
);
17495 if ($fontaligned) {
17496 $yshift +
= ($curfontsize / $this->k
);
17498 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k
));
17499 $this->setPageBuffer($this->page
, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17500 // shift the annotations and links
17501 if (isset($this->PageAnnots
[$this->page
])) {
17502 $next_pask = count($this->PageAnnots
[$this->page
]);
17506 if (isset($this->PageAnnots
[$startlinepage])) {
17507 foreach ($this->PageAnnots
[$startlinepage] as $pak => $pac) {
17508 if ($pak >= $pask) {
17509 $this->PageAnnots
[$this->page
][] = $pac;
17510 unset($this->PageAnnots
[$startlinepage][$pak]);
17511 $npak = count($this->PageAnnots
[$this->page
]) - 1;
17512 $this->PageAnnots
[$this->page
][$npak]['y'] -= $yshift;
17516 $pask = $next_pask;
17517 $startlinepos = $this->cntmrk
[$this->page
];
17518 $startlinepage = $this->page
;
17519 $startliney = $this->y
;
17520 $this->newline
= false;
17522 $this->y +
= ($this->getCellHeight($curfontsize / $this->k
) - ($curfontdescent * $this->cell_height_ratio
) - $imgh);
17523 $minstartliney = min($this->y
, $minstartliney);
17524 $maxbottomliney = ($startliney +
$this->getCellHeight($curfontsize / $this->k
));
17526 } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
17527 // account for different font size
17528 $pfontname = $curfontname;
17529 $pfontstyle = $curfontstyle;
17530 $pfontsize = $curfontsize;
17531 $fontname = (isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname);
17532 $fontstyle = (isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle);
17533 $fontsize = (isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize);
17534 $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
17535 $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
17536 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
17537 OR ($this->cell_height_ratio
!= $dom[$key]['line-height'])
17538 OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
17539 if (($key < ($maxel - 1)) AND (
17540 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
17541 OR ($this->cell_height_ratio
!= $dom[$key]['line-height'])
17542 OR (!$this->newline
AND is_numeric($fontsize) AND is_numeric($curfontsize)
17543 AND ($fontsize >= 0) AND ($curfontsize >= 0)
17544 AND (($fontsize != $curfontsize) OR ($fontstyle != $curfontstyle) OR ($fontname != $curfontname)))
17546 if ($this->page
> $startlinepage) {
17547 // fix lines splitted over two pages
17548 if (isset($this->footerlen
[$startlinepage])) {
17549 $curpos = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
17551 // line to be moved one page forward
17552 $pagebuff = $this->getPageBuffer($startlinepage);
17553 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17554 $tstart = substr($pagebuff, 0, $startlinepos);
17555 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17556 // remove line start from previous page
17557 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17558 $pagebuff = $this->getPageBuffer($this->page
);
17559 $tstart = substr($pagebuff, 0, $this->cntmrk
[$this->page
]);
17560 $tend = substr($pagebuff, $this->cntmrk
[$this->page
]);
17561 // add line start to current page
17562 $yshift = ($minstartliney - $this->y
);
17563 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k
));
17564 $this->setPageBuffer($this->page
, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17565 // shift the annotations and links
17566 if (isset($this->PageAnnots
[$this->page
])) {
17567 $next_pask = count($this->PageAnnots
[$this->page
]);
17571 if (isset($this->PageAnnots
[$startlinepage])) {
17572 foreach ($this->PageAnnots
[$startlinepage] as $pak => $pac) {
17573 if ($pak >= $pask) {
17574 $this->PageAnnots
[$this->page
][] = $pac;
17575 unset($this->PageAnnots
[$startlinepage][$pak]);
17576 $npak = count($this->PageAnnots
[$this->page
]) - 1;
17577 $this->PageAnnots
[$this->page
][$npak]['y'] -= $yshift;
17581 $pask = $next_pask;
17582 $startlinepos = $this->cntmrk
[$this->page
];
17583 $startlinepage = $this->page
;
17584 $startliney = $this->y
;
17586 if (!isset($dom[$key]['line-height'])) {
17587 $dom[$key]['line-height'] = $this->cell_height_ratio
;
17589 if (!$dom[$key]['block']) {
17590 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']))) {
17591 $this->y +
= (((($curfontsize * $this->cell_height_ratio
) - ($fontsize * $dom[$key]['line-height'])) / $this->k
) +
$curfontascent - $fontascent - $curfontdescent +
$fontdescent) / 2;
17593 if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
17594 $current_line_align_data = array($key, $minstartliney, $maxbottomliney);
17595 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)))) {
17596 $minstartliney = min($this->y
, $line_align_data[1]);
17597 $maxbottomliney = max(($this->y +
$this->getCellHeight($fontsize / $this->k
)), $line_align_data[2]);
17599 $minstartliney = min($this->y
, $minstartliney);
17600 $maxbottomliney = max(($this->y +
$this->getCellHeight($fontsize / $this->k
)), $maxbottomliney);
17602 $line_align_data = $current_line_align_data;
17605 $this->cell_height_ratio
= $dom[$key]['line-height'];
17606 $fontaligned = true;
17608 $this->SetFont($fontname, $fontstyle, $fontsize);
17609 // reset row height
17610 $this->resetLastH();
17611 $curfontname = $fontname;
17612 $curfontstyle = $fontstyle;
17613 $curfontsize = $fontsize;
17614 $curfontascent = $fontascent;
17615 $curfontdescent = $fontdescent;
17618 // set text rendering mode
17619 $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth
;
17620 $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode %
2) == 0);
17621 $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode
> 3);
17622 $this->setTextRenderingMode($textstroke, $textfill, $textclip);
17623 if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
17624 $this->setFontStretching($dom[$key]['font-stretch']);
17626 if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
17627 $this->setFontSpacing($dom[$key]['letter-spacing']);
17629 if (($plalign == 'J') AND $dom[$key]['block']) {
17632 // get current position on page buffer
17633 $curpos = $this->pagelen
[$startlinepage];
17634 if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
17635 $this->SetFillColorArray($dom[$key]['bgcolor']);
17638 $wfill = $fill | false;
17640 if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
17641 $this->SetTextColorArray($dom[$key]['fgcolor']);
17643 if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
17644 $this->SetDrawColorArray($dom[$key]['strokecolor']);
17646 if (isset($dom[$key]['align'])) {
17647 $lalign = $dom[$key]['align'];
17649 if (TCPDF_STATIC
::empty_string($lalign)) {
17654 if ($this->newline
AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
17656 $fontaligned = false;
17657 // we are at the beginning of a new line
17658 if (isset($startlinex)) {
17659 $yshift = ($minstartliney - $startliney);
17660 if (($yshift > 0) OR ($this->page
> $startlinepage)) {
17664 // the last line must be shifted to be aligned as requested
17665 $linew = abs($this->endlinex
- $startlinex);
17666 if ($this->inxobj
) {
17667 // we are inside an XObject template
17668 $pstart = substr($this->xobjects
[$this->xobjid
]['outdata'], 0, $startlinepos);
17669 if (isset($opentagpos)) {
17670 $midpos = $opentagpos;
17675 $pmid = substr($this->xobjects
[$this->xobjid
]['outdata'], $startlinepos, ($midpos - $startlinepos));
17676 $pend = substr($this->xobjects
[$this->xobjid
]['outdata'], $midpos);
17678 $pmid = substr($this->xobjects
[$this->xobjid
]['outdata'], $startlinepos);
17682 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
17683 if (isset($opentagpos) AND isset($this->footerlen
[$startlinepage]) AND (!$this->InFooter
)) {
17684 $this->footerpos
[$startlinepage] = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
17685 $midpos = min($opentagpos, $this->footerpos
[$startlinepage]);
17686 } elseif (isset($opentagpos)) {
17687 $midpos = $opentagpos;
17688 } elseif (isset($this->footerlen
[$startlinepage]) AND (!$this->InFooter
)) {
17689 $this->footerpos
[$startlinepage] = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
17690 $midpos = $this->footerpos
[$startlinepage];
17695 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
17696 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
17698 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
17702 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl
)) OR (($plalign == 'L') AND ($this->rtl
)))))) {
17703 // calculate shifting amount
17705 if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns
> 1)) {
17706 $tw +
= $this->cell_padding
['R'];
17708 if ($this->lMargin
!= $prevlMargin) {
17709 $tw +
= ($prevlMargin - $this->lMargin
);
17711 if ($this->rMargin
!= $prevrMargin) {
17712 $tw +
= ($prevrMargin - $this->rMargin
);
17714 $one_space_width = $this->GetStringWidth(chr(32));
17715 $no = 0; // number of spaces on a line contained on a single block
17716 if ($this->isRTLTextDir()) { // RTL
17717 // remove left space if exist
17718 $pos1 = TCPDF_STATIC
::revstrpos($pmid, '[(');
17720 $pos1 = intval($pos1);
17721 if ($this->isUnicodeFont()) {
17722 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, '[('.chr(0).chr(32)));
17725 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, '[('.chr(32)));
17728 if ($pos1 == $pos2) {
17729 $pmid = substr($pmid, 0, ($pos1 +
2)).substr($pmid, ($pos1 +
2 +
$spacelen));
17730 if (substr($pmid, $pos1, 4) == '[()]') {
17731 $linew -= $one_space_width;
17732 } elseif ($pos1 == strpos($pmid, '[(')) {
17738 // remove right space if exist
17739 $pos1 = TCPDF_STATIC
::revstrpos($pmid, ')]');
17741 $pos1 = intval($pos1);
17742 if ($this->isUnicodeFont()) {
17743 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, chr(0).chr(32).')]')) +
2;
17746 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, chr(32).')]')) +
1;
17749 if ($pos1 == $pos2) {
17750 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
17751 $linew -= $one_space_width;
17755 $mdiff = ($tw - $linew);
17756 if ($plalign == 'C') {
17758 $t_x = -($mdiff / 2);
17760 $t_x = ($mdiff / 2);
17762 } elseif ($plalign == 'R') {
17763 // right alignment on LTR document
17765 } elseif ($plalign == 'L') {
17766 // left alignment on RTL document
17768 } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
17770 if ($this->isRTLTextDir()) {
17771 // align text on the left
17774 $ns = 0; // number of spaces
17776 // escape special characters
17777 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
17778 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
17780 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER
)) {
17781 $spacestr = $this->getSpaceString();
17782 $maxkk = count($lnstring[1]) - 1;
17783 for ($kk=0; $kk <= $maxkk; ++
$kk) {
17784 // restore special characters
17785 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
17786 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
17787 // store number of spaces on the strings
17788 $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
17789 // count total spaces on line
17790 $ns +
= $lnstring[2][$kk];
17791 $lnstring[3][$kk] = $ns;
17796 // calculate additional space to add to each existing space
17797 $spacewidth = ($mdiff / ($ns - $no)) * $this->k
;
17798 if ($this->FontSize
<= 0) {
17799 $this->FontSize
= 1;
17801 $spacewidthu = -1000 * ($mdiff +
(($ns +
$no) * $one_space_width)) / $ns / $this->FontSize
;
17802 if ($this->font_spacing
!= 0) {
17803 // fixed spacing mode
17804 $osw = -1000 * $this->font_spacing
/ $this->FontSize
;
17805 $spacewidthu +
= $osw;
17812 $prev_epsposbeg = 0;
17814 if ($this->isRTLTextDir()) {
17815 $textpos = $this->wPt
;
17817 while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE
, $offset) == 1) {
17818 // check if we are inside a string section '[( ... )]'
17819 $stroffset = strpos($pmid, '[(', $offset);
17820 if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
17821 // set offset to the end of string section
17822 $offset = strpos($pmid, ')]', $stroffset);
17823 while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
17824 $offset = strpos($pmid, ')]', ($offset +
1));
17826 if ($offset === false) {
17827 $this->Error('HTML Justification: malformed PDF code.');
17831 if ($this->isRTLTextDir()) {
17832 $spacew = ($spacewidth * ($nsmax - $ns));
17834 $spacew = ($spacewidth * $ns);
17836 $offset = $strpiece[2][1] +
strlen($strpiece[2][0]);
17837 $epsposend = strpos($pmid, $this->epsmarker
.'Q', $offset);
17838 if ($epsposend !== null) {
17839 $epsposend +
= strlen($this->epsmarker
.'Q');
17840 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker
, $offset);
17841 if ($epsposbeg === null) {
17842 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker
, ($prev_epsposbeg - 6));
17843 $prev_epsposbeg = $epsposbeg;
17845 if (($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) {
17846 // shift EPS images
17847 $trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
17848 $pmid_b = substr($pmid, 0, $epsposbeg);
17849 $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
17850 $pmid_e = substr($pmid, $epsposend);
17851 $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
17852 $offset = $epsposend;
17857 // shift blocks of code
17858 switch ($strpiece[2][0]) {
17863 // get current X position
17864 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
17865 if (!isset($xmatches[1])) {
17868 $currentxpos = $xmatches[1];
17869 $textpos = $currentxpos;
17870 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
17871 $ns = $lnstring[3][$strcount];
17872 if ($this->isRTLTextDir()) {
17873 $spacew = ($spacewidth * ($nsmax - $ns));
17878 if (preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $pmatch) == 1) {
17879 $newpmid = sprintf('%F',(floatval($pmatch[1]) +
$spacew)).' '.$pmatch[2].' x*#!#*x'.$pmatch[3].$pmatch[4];
17880 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17881 unset($pmatch, $newpmid);
17887 if (!TCPDF_STATIC
::empty_string($this->lispacer
)) {
17888 $this->lispacer
= '';
17891 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
17892 if (!isset($xmatches[1])) {
17895 $currentxpos = $xmatches[1];
17898 if ($this->isRTLTextDir()) { // RTL
17899 if ($currentxpos < $textpos) {
17900 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
17901 $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17903 if ($strcount > 0) {
17904 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
17905 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17909 if ($currentxpos > $textpos) {
17910 if ($strcount > 0) {
17911 $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
17913 $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17915 if ($strcount > 1) {
17916 $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
17918 if ($strcount > 0) {
17919 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17923 if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $pmatch) == 1) {
17924 $newx = sprintf('%F',(floatval($pmatch[1]) +
$x_diff));
17925 $neww = sprintf('%F',(floatval($pmatch[3]) +
$w_diff));
17926 $newpmid = $newx.' '.$pmatch[2].' '.$neww.' '.$pmatch[4].' x*#!#*x'.$pmatch[5].$pmatch[6];
17927 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17928 unset($pmatch, $newpmid, $newx, $neww);
17933 // get current X position
17934 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);
17935 if (!isset($xmatches[1])) {
17938 $currentxpos = $xmatches[1];
17940 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) {
17941 $newx1 = sprintf('%F',(floatval($pmatch[1]) +
$spacew));
17942 $newx2 = sprintf('%F',(floatval($pmatch[3]) +
$spacew));
17943 $newx3 = sprintf('%F',(floatval($pmatch[5]) +
$spacew));
17944 $newpmid = $newx1.' '.$pmatch[2].' '.$newx2.' '.$pmatch[4].' '.$newx3.' '.$pmatch[6].' x*#!#*x'.$pmatch[7].$pmatch[8];
17945 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17946 unset($pmatch, $newpmid, $newx1, $newx2, $newx3);
17951 // shift the annotations and links
17952 $cxpos = ($currentxpos / $this->k
);
17953 $lmpos = ($this->lMargin +
$this->cell_padding
['L'] +
$this->feps
);
17954 if ($this->inxobj
) {
17955 // we are inside an XObject template
17956 foreach ($this->xobjects
[$this->xobjid
]['annotations'] as $pak => $pac) {
17957 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k
) >= ($currentxpos - $this->feps
)) AND (($pac['x'] * $this->k
) <= ($currentxpos +
$this->feps
))) {
17958 if ($cxpos > $lmpos) {
17959 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['x'] +
= ($spacew / $this->k
);
17960 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['w'] +
= (($spacewidth * $pac['numspaces']) / $this->k
);
17962 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['w'] +
= (($spacewidth * $pac['numspaces']) / $this->k
);
17967 } elseif (isset($this->PageAnnots
[$this->page
])) {
17968 foreach ($this->PageAnnots
[$this->page
] as $pak => $pac) {
17969 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k
) >= ($currentxpos - $this->feps
)) AND (($pac['x'] * $this->k
) <= ($currentxpos +
$this->feps
))) {
17970 if ($cxpos > $lmpos) {
17971 $this->PageAnnots
[$this->page
][$pak]['x'] +
= ($spacew / $this->k
);
17972 $this->PageAnnots
[$this->page
][$pak]['w'] +
= (($spacewidth * $pac['numspaces']) / $this->k
);
17974 $this->PageAnnots
[$this->page
][$pak]['w'] +
= (($spacewidth * $pac['numspaces']) / $this->k
);
17982 $pmid = str_replace('x*#!#*x', '', $pmid);
17983 if ($this->isUnicodeFont()) {
17984 // multibyte characters
17985 $spacew = $spacewidthu;
17986 if ($this->font_stretching
!= 100) {
17987 // word spacing is affected by stretching
17988 $spacew /= ($this->font_stretching
/ 100);
17990 // escape special characters
17992 $pmid = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmid);
17993 $pmid = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmid);
17994 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmid, $pamatch) > 0) {
17995 foreach($pamatch[0] as $pk => $pmatch) {
17996 $replace = $pamatch[1][$pk];
17997 $replace = str_replace('#!#OP#!#', '(', $replace);
17998 $replace = str_replace('#!#CP#!#', ')', $replace);
17999 $newpmid = '[('.str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacew).' (', $replace).')]';
18000 $pos = strpos($pmid, $pmatch, $pos);
18001 if ($pos !== FALSE) {
18002 $pmid = substr_replace($pmid, $newpmid, $pos, strlen($pmatch));
18008 if ($this->inxobj
) {
18009 // we are inside an XObject template
18010 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart."\n".$pmid."\n".$pend;
18012 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
18014 $endlinepos = strlen($pstart."\n".$pmid."\n");
18016 // non-unicode (single-byte characters)
18017 if ($this->font_stretching
!= 100) {
18018 // word spacing (Tw) is affected by stretching
18019 $spacewidth /= ($this->font_stretching
/ 100);
18021 $rs = sprintf('%F Tw', $spacewidth);
18022 $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
18023 if ($this->inxobj
) {
18024 // we are inside an XObject template
18025 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
18027 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
18029 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
18033 } // end if $startlinex
18034 if (($t_x != 0) OR ($yshift < 0)) {
18036 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k
), ($yshift * $this->k
));
18037 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18038 $endlinepos = strlen($pstart);
18039 if ($this->inxobj
) {
18040 // we are inside an XObject template
18041 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$pend;
18042 foreach ($this->xobjects
[$this->xobjid
]['annotations'] as $pak => $pac) {
18043 if ($pak >= $pask) {
18044 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['x'] +
= $t_x;
18045 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['y'] -= $yshift;
18049 $this->setPageBuffer($startlinepage, $pstart.$pend);
18050 // shift the annotations and links
18051 if (isset($this->PageAnnots
[$this->page
])) {
18052 foreach ($this->PageAnnots
[$this->page
] as $pak => $pac) {
18053 if ($pak >= $pask) {
18054 $this->PageAnnots
[$this->page
][$pak]['x'] +
= $t_x;
18055 $this->PageAnnots
[$this->page
][$pak]['y'] -= $yshift;
18060 $this->y
-= $yshift;
18063 $pbrk = $this->checkPageBreak($this->lasth
);
18064 $this->newline
= false;
18065 $startlinex = $this->x
;
18066 $startliney = $this->y
;
18067 if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
18068 $startliney -= ((0.3 * $this->FontSizePt
) / $this->k
);
18069 } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
18070 $startliney -= (($this->FontSizePt
/ 0.7) / $this->k
);
18072 $minstartliney = $startliney;
18073 $maxbottomliney = ($this->y +
$this->getCellHeight($fontsize / $this->k
));
18075 $startlinepage = $this->page
;
18076 if (isset($endlinepos) AND (!$pbrk)) {
18077 $startlinepos = $endlinepos;
18079 if ($this->inxobj
) {
18080 // we are inside an XObject template
18081 $startlinepos = strlen($this->xobjects
[$this->xobjid
]['outdata']);
18082 } elseif (!$this->InFooter
) {
18083 if (isset($this->footerlen
[$this->page
])) {
18084 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
] - $this->footerlen
[$this->page
];
18086 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
];
18088 $startlinepos = $this->footerpos
[$this->page
];
18090 $startlinepos = $this->pagelen
[$this->page
];
18093 unset($endlinepos);
18094 $plalign = $lalign;
18095 if (isset($this->PageAnnots
[$this->page
])) {
18096 $pask = count($this->PageAnnots
[$this->page
]);
18100 if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
18101 AND (isset($this->emptypagemrk
[$this->page
]))
18102 AND ($this->emptypagemrk
[$this->page
] == $this->pagelen
[$this->page
]))) {
18103 $this->SetFont($fontname, $fontstyle, $fontsize);
18105 $this->SetFillColorArray($this->bgcolor
);
18109 if (isset($opentagpos)) {
18110 unset($opentagpos);
18112 if ($dom[$key]['tag']) {
18113 if ($dom[$key]['opening']) {
18114 // get text indentation (if any)
18115 if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
18116 $this->textindent
= $dom[$key]['text-indent'];
18117 $this->newline
= true;
18120 if (($dom[$key]['value'] == 'table') AND isset($dom[$key]['cols']) AND ($dom[$key]['cols'] > 0)) {
18121 // available page width
18123 $wtmp = $this->x
- $this->lMargin
;
18125 $wtmp = $this->w
- $this->rMargin
- $this->x
;
18127 // get cell spacing
18128 if (isset($dom[$key]['attribute']['cellspacing'])) {
18129 $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
18130 $cellspacing = array('H' => $clsp, 'V' => $clsp);
18131 } elseif (isset($dom[$key]['border-spacing'])) {
18132 $cellspacing = $dom[$key]['border-spacing'];
18134 $cellspacing = array('H' => 0, 'V' => 0);
18137 if (isset($dom[$key]['width'])) {
18138 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
18140 $table_width = $wtmp;
18142 $table_width -= (2 * $cellspacing['H']);
18143 if (!$this->inthead
) {
18144 $this->y +
= $cellspacing['V'];
18147 $cellspacingx = -$cellspacing['H'];
18149 $cellspacingx = $cellspacing['H'];
18151 // total table width without cellspaces
18152 $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
18153 // minimum column width
18154 $table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
18155 // array of custom column widths
18156 $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
18159 if ($dom[$key]['value'] == 'tr') {
18160 // reset column counter
18164 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
18165 $trid = $dom[$key]['parent'];
18166 $table_el = $dom[$trid]['parent'];
18167 if (!isset($dom[$table_el]['cols'])) {
18168 $dom[$table_el]['cols'] = $dom[$trid]['cols'];
18170 // store border info
18172 if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
18173 $tdborder = $dom[$key]['border'];
18175 $colspan = intval($dom[$key]['attribute']['colspan']);
18176 if ($colspan <= 0) {
18179 $old_cell_padding = $this->cell_padding
;
18180 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
18181 $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
18182 $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
18183 } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
18184 $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
18186 $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
18188 $this->cell_padding
= $current_cell_padding;
18189 if (isset($dom[$key]['height'])) {
18190 // minimum cell height
18191 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
18195 if (isset($dom[$key]['content'])) {
18196 $cell_content = stripslashes($dom[$key]['content']);
18198 $cell_content = ' ';
18200 $tagtype = $dom[$key]['value'];
18202 while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
18203 // move $key index forward
18206 if (!isset($dom[$trid]['startpage'])) {
18207 $dom[$trid]['startpage'] = $this->page
;
18209 $this->setPage($dom[$trid]['startpage']);
18211 if (!isset($dom[$trid]['startcolumn'])) {
18212 $dom[$trid]['startcolumn'] = $this->current_column
;
18213 } elseif ($this->current_column
!= $dom[$trid]['startcolumn']) {
18215 $this->selectColumn($dom[$trid]['startcolumn']);
18218 if (!isset($dom[$trid]['starty'])) {
18219 $dom[$trid]['starty'] = $this->y
;
18221 $this->y
= $dom[$trid]['starty'];
18223 if (!isset($dom[$trid]['startx'])) {
18224 $dom[$trid]['startx'] = $this->x
;
18225 $this->x +
= $cellspacingx;
18227 $this->x +
= ($cellspacingx / 2);
18229 if (isset($dom[$parentid]['attribute']['rowspan'])) {
18230 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
18234 // skip row-spanned cells started on the previous rows
18235 if (isset($dom[$table_el]['rowspans'])) {
18237 $rskmax = count($dom[$table_el]['rowspans']);
18238 while ($rsk < $rskmax) {
18239 $trwsp = $dom[$table_el]['rowspans'][$rsk];
18240 $rsstartx = $trwsp['startx'];
18241 $rsendx = $trwsp['endx'];
18242 // account for margin changes
18243 if ($trwsp['startpage'] < $this->page
) {
18244 if (($this->rtl
) AND ($this->pagedim
[$this->page
]['orm'] != $this->pagedim
[$trwsp['startpage']]['orm'])) {
18245 $dl = ($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$trwsp['startpage']]['orm']);
18248 } elseif ((!$this->rtl
) AND ($this->pagedim
[$this->page
]['olm'] != $this->pagedim
[$trwsp['startpage']]['olm'])) {
18249 $dl = ($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$trwsp['startpage']]['olm']);
18254 if (($trwsp['rowspan'] > 0)
18255 AND ($rsstartx > ($this->x
- $cellspacing['H'] - $current_cell_padding['L'] - $this->feps
))
18256 AND ($rsstartx < ($this->x +
$cellspacing['H'] +
$current_cell_padding['R'] +
$this->feps
))
18257 AND (($trwsp['starty'] < ($this->y
- $this->feps
)) OR ($trwsp['startpage'] < $this->page
) OR ($trwsp['startcolumn'] < $this->current_column
))) {
18258 // set the starting X position of the current cell
18259 $this->x
= $rsendx +
$cellspacingx;
18260 // increment column indicator
18261 $colid +
= $trwsp['colspan'];
18262 if (($trwsp['rowspan'] == 1)
18263 AND (isset($dom[$trid]['endy']))
18264 AND (isset($dom[$trid]['endpage']))
18265 AND (isset($dom[$trid]['endcolumn']))
18266 AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
18267 AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18268 // set ending Y position for row
18269 $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18270 $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
18278 if (isset($dom[$parentid]['width'])) {
18279 // user specified width
18280 $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
18281 $tmpcw = ($cellw / $colspan);
18282 for ($i = 0; $i < $colspan; ++
$i) {
18283 $table_colwidths[($colid +
$i)] = $tmpcw;
18286 // inherit column width
18288 for ($i = 0; $i < $colspan; ++
$i) {
18289 $cellw +
= $table_colwidths[($colid +
$i)];
18292 $cellw +
= (($colspan - 1) * $cellspacing['H']);
18293 // increment column indicator
18294 $colid +
= $colspan;
18295 // add rowspan information to table element
18296 if ($rowspan > 1) {
18297 $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
));
18299 $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x
));
18300 if ($rowspan > 1) {
18301 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
18303 // push background colors
18304 if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
18305 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
18307 // store border info
18308 if (isset($tdborder) AND !empty($tdborder)) {
18309 $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
18311 $prevLastH = $this->lasth
;
18312 // store some info for multicolumn mode
18314 $this->colxshift
['x'] = $this->w
- $this->x
- $this->rMargin
;
18316 $this->colxshift
['x'] = $this->x
- $this->lMargin
;
18318 $this->colxshift
['s'] = $cellspacing;
18319 $this->colxshift
['p'] = $current_cell_padding;
18320 // ****** write the cell content ******
18321 $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
18322 // restore some values
18323 $this->colxshift
= array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
18324 $this->lasth
= $prevLastH;
18325 $this->cell_padding
= $old_cell_padding;
18326 $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x
;
18327 // update the end of row position
18328 if ($rowspan <= 1) {
18329 if (isset($dom[$trid]['endy'])) {
18330 if (($this->page
== $dom[$trid]['endpage']) AND ($this->current_column
== $dom[$trid]['endcolumn'])) {
18331 $dom[$trid]['endy'] = max($this->y
, $dom[$trid]['endy']);
18332 } elseif (($this->page
> $dom[$trid]['endpage']) OR ($this->current_column
> $dom[$trid]['endcolumn'])) {
18333 $dom[$trid]['endy'] = $this->y
;
18336 $dom[$trid]['endy'] = $this->y
;
18338 if (isset($dom[$trid]['endpage'])) {
18339 $dom[$trid]['endpage'] = max($this->page
, $dom[$trid]['endpage']);
18341 $dom[$trid]['endpage'] = $this->page
;
18343 if (isset($dom[$trid]['endcolumn'])) {
18344 $dom[$trid]['endcolumn'] = max($this->current_column
, $dom[$trid]['endcolumn']);
18346 $dom[$trid]['endcolumn'] = $this->current_column
;
18349 // account for row-spanned cells
18350 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x
;
18351 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y
;
18352 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page
;
18353 $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column
;
18355 if (isset($dom[$table_el]['rowspans'])) {
18356 // update endy and endpage on rowspanned cells
18357 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
18358 if ($trwsp['rowspan'] > 0) {
18359 if (isset($dom[$trid]['endpage'])) {
18360 if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18361 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18362 } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
18363 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
18364 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
18365 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
18367 $dom[$trid]['endy'] = $this->pagedim
[$dom[$trid]['endpage']]['hk'] - $this->pagedim
[$dom[$trid]['endpage']]['bm'];
18373 $this->x +
= ($cellspacingx / 2);
18375 // opening tag (or self-closing tag)
18376 if (!isset($opentagpos)) {
18377 if ($this->inxobj
) {
18378 // we are inside an XObject template
18379 $opentagpos = strlen($this->xobjects
[$this->xobjid
]['outdata']);
18380 } elseif (!$this->InFooter
) {
18381 if (isset($this->footerlen
[$this->page
])) {
18382 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
] - $this->footerlen
[$this->page
];
18384 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
];
18386 $opentagpos = $this->footerpos
[$this->page
];
18389 $dom = $this->openHTMLTagHandler($dom, $key, $cell);
18391 } else { // closing tag
18392 $prev_numpages = $this->numpages
;
18393 $old_bordermrk = $this->bordermrk
[$this->page
];
18394 $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
18395 if ($this->bordermrk
[$this->page
] > $old_bordermrk) {
18396 $startlinepos +
= ($this->bordermrk
[$this->page
] - $old_bordermrk);
18398 if ($prev_numpages > $this->numpages
) {
18399 $startlinepage = $this->page
;
18402 } elseif (strlen($dom[$key]['value']) > 0) {
18404 if (!TCPDF_STATIC
::empty_string($this->lispacer
) AND ($this->lispacer
!= '^')) {
18405 $this->SetFont($pfontname, $pfontstyle, $pfontsize);
18406 $this->resetLastH();
18407 $minstartliney = $this->y
;
18408 $maxbottomliney = ($startliney +
$this->getCellHeight($this->FontSize
));
18409 if (is_numeric($pfontsize) AND ($pfontsize > 0)) {
18410 $this->putHtmlListBullet($this->listnum
, $this->lispacer
, $pfontsize);
18412 $this->SetFont($curfontname, $curfontstyle, $curfontsize);
18413 $this->resetLastH();
18414 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
18415 $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
18416 $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
18417 $this->y +
= ($this->getCellHeight(($pfontsize - $curfontsize) / $this->k
) +
$pfontascent - $curfontascent - $pfontdescent +
$curfontdescent) / 2;
18418 $minstartliney = min($this->y
, $minstartliney);
18419 $maxbottomliney = max(($this->y +
$this->getCellHeight($pfontsize / $this->k
)), $maxbottomliney);
18423 $this->htmlvspace
= 0;
18424 if ((!$this->premode
) AND $this->isRTLTextDir()) {
18425 // reverse spaces order
18426 $lsp = ''; // left spaces
18427 $rsp = ''; // right spaces
18428 if (preg_match('/^('.$this->re_space
['p'].'+)/'.$this->re_space
['m'], $dom[$key]['value'], $matches)) {
18429 $lsp = $matches[1];
18431 if (preg_match('/('.$this->re_space
['p'].'+)$/'.$this->re_space
['m'], $dom[$key]['value'], $matches)) {
18432 $rsp = $matches[1];
18434 $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
18437 if (!$this->premode
) {
18438 $prelen = strlen($dom[$key]['value']);
18439 if ($this->isRTLTextDir()) {
18440 // right trim except non-breaking space
18441 $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
18443 // left trim except non-breaking space
18444 $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
18446 $postlen = strlen($dom[$key]['value']);
18447 if (($postlen == 0) AND ($prelen > 0)) {
18448 $dom[$key]['trimmed_space'] = true;
18452 $firstblock = true;
18454 $firstblock = false;
18455 // replace empty multiple spaces string with a single space
18456 $dom[$key]['value'] = preg_replace('/^'.$this->re_space
['p'].'+$/'.$this->re_space
['m'], chr(32), $dom[$key]['value']);
18460 $this->x
-= $this->textindent
;
18462 $this->x +
= $this->textindent
;
18464 if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
18465 $strlinelen = $this->GetStringWidth($dom[$key]['value']);
18466 if (!empty($this->HREF
) AND (isset($this->HREF
['url']))) {
18469 if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
18470 $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
18473 if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
18474 $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
18476 $strrest = $this->addHtmlLink($this->HREF
['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
18478 $wadj = 0; // space to leave for block continuity
18480 $cwa = ($this->x
- $this->lMargin
);
18482 $cwa = ($this->w
- $this->rMargin
- $this->x
);
18484 if (($strlinelen < $cwa) AND (isset($dom[($key +
1)])) AND ($dom[($key +
1)]['tag']) AND (!$dom[($key +
1)]['block'])) {
18485 // check the next text blocks for continuity
18486 $nkey = ($key +
1);
18487 $write_block = true;
18488 $same_textdir = true;
18489 $tmp_fontname = $this->FontFamily
;
18490 $tmp_fontstyle = $this->FontStyle
;
18491 $tmp_fontsize = $this->FontSizePt
;
18492 while ($write_block AND isset($dom[$nkey])) {
18493 if ($dom[$nkey]['tag']) {
18494 if ($dom[$nkey]['block']) {
18496 $write_block = false;
18498 $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily
;
18499 $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle
;
18500 $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt
;
18501 $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
18503 $nextstr = TCPDF_STATIC
::pregSplit('/'.$this->re_space
['p'].'+/', $this->re_space
['m'], $dom[$nkey]['value']);
18504 if (isset($nextstr[0]) AND $same_textdir) {
18505 $wadj +
= $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
18506 if (isset($nextstr[1])) {
18507 $write_block = false;
18514 if (($wadj > 0) AND (($strlinelen +
$wadj) >= $cwa)) {
18516 $nextstr = TCPDF_STATIC
::pregSplit('/'.$this->re_space
['p'].'/', $this->re_space
['m'], $dom[$key]['value']);
18517 $numblks = count($nextstr);
18518 if ($numblks > 1) {
18519 // try to split on blank spaces
18520 $wadj = ($cwa - $strlinelen +
$this->GetStringWidth($nextstr[($numblks - 1)]));
18522 // set the entire block on new line
18523 $wadj = $this->GetStringWidth($nextstr[0]);
18526 // check for reversed text direction
18527 if (($wadj > 0) AND (($this->rtl
AND ($this->tmprtl
=== 'L')) OR (!$this->rtl
AND ($this->tmprtl
=== 'R')))) {
18528 // LTR text on RTL direction or RTL text on LTR direction
18529 $reverse_dir = true;
18530 $this->rtl
= !$this->rtl
;
18531 $revshift = ($strlinelen +
$wadj +
0.000001); // add little quantity for rounding problems
18533 $this->x +
= $revshift;
18535 $this->x
-= $revshift;
18539 // ****** write only until the end of the line and get the rest ******
18540 $strrest = $this->Write($this->lasth
, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
18541 // restore default direction
18542 if ($reverse_dir AND ($wadj == 0)) {
18544 $this->rtl
= !$this->rtl
;
18545 $reverse_dir = false;
18549 $this->textindent
= 0;
18550 if (strlen($strrest) > 0) {
18551 // store the remaining string on the previous $key position
18552 $this->newline
= true;
18553 if ($strrest == $dom[$key]['value']) {
18554 // used to avoid infinite loop
18559 $dom[$key]['value'] = $strrest;
18562 $this->x
-= $this->cell_padding
['R'];
18564 $this->x +
= $this->cell_padding
['L'];
18572 // add the positive font spacing of the last character (if any)
18573 if ($this->font_spacing
> 0) {
18575 $this->x
-= $this->font_spacing
;
18577 $this->x +
= $this->font_spacing
;
18583 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')) {
18584 // check if we are on a new page or on a new column
18585 if ((!$undo) AND (($this->y
< $this->start_transaction_y
) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y
)))) {
18586 // we are on a new page or on a new column and the total object height is less than the available vertical space.
18587 // restore previous object
18588 $this->rollbackTransaction(true);
18589 // restore previous values
18590 foreach ($this_method_vars as $vkey => $vval) {
18593 if (!empty($dom[$key]['thead'])) {
18594 $this->inthead
= true;
18596 // add a page (or trig AcceptPageBreak() for multicolumn mode)
18598 if ((!$this->checkPageBreak($this->PageBreakTrigger +
1)) AND ($this->y
< $pre_y)) {
18599 $startliney = $this->y
;
18601 $undo = true; // avoid infinite loop
18606 } // end for each $key
18607 // align the last line
18608 if (isset($startlinex)) {
18609 $yshift = ($minstartliney - $startliney);
18610 if (($yshift > 0) OR ($this->page
> $startlinepage)) {
18614 // the last line must be shifted to be aligned as requested
18615 $linew = abs($this->endlinex
- $startlinex);
18616 if ($this->inxobj
) {
18617 // we are inside an XObject template
18618 $pstart = substr($this->xobjects
[$this->xobjid
]['outdata'], 0, $startlinepos);
18619 if (isset($opentagpos)) {
18620 $midpos = $opentagpos;
18625 $pmid = substr($this->xobjects
[$this->xobjid
]['outdata'], $startlinepos, ($midpos - $startlinepos));
18626 $pend = substr($this->xobjects
[$this->xobjid
]['outdata'], $midpos);
18628 $pmid = substr($this->xobjects
[$this->xobjid
]['outdata'], $startlinepos);
18632 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
18633 if (isset($opentagpos) AND isset($this->footerlen
[$startlinepage]) AND (!$this->InFooter
)) {
18634 $this->footerpos
[$startlinepage] = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
18635 $midpos = min($opentagpos, $this->footerpos
[$startlinepage]);
18636 } elseif (isset($opentagpos)) {
18637 $midpos = $opentagpos;
18638 } elseif (isset($this->footerlen
[$startlinepage]) AND (!$this->InFooter
)) {
18639 $this->footerpos
[$startlinepage] = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
18640 $midpos = $this->footerpos
[$startlinepage];
18645 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
18646 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
18648 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
18652 if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl
)) OR (($plalign == 'L') AND ($this->rtl
)))))) {
18653 // calculate shifting amount
18655 if ($this->lMargin
!= $prevlMargin) {
18656 $tw +
= ($prevlMargin - $this->lMargin
);
18658 if ($this->rMargin
!= $prevrMargin) {
18659 $tw +
= ($prevrMargin - $this->rMargin
);
18661 $one_space_width = $this->GetStringWidth(chr(32));
18662 $no = 0; // number of spaces on a line contained on a single block
18663 if ($this->isRTLTextDir()) { // RTL
18664 // remove left space if exist
18665 $pos1 = TCPDF_STATIC
::revstrpos($pmid, '[(');
18667 $pos1 = intval($pos1);
18668 if ($this->isUnicodeFont()) {
18669 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, '[('.chr(0).chr(32)));
18672 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, '[('.chr(32)));
18675 if ($pos1 == $pos2) {
18676 $pmid = substr($pmid, 0, ($pos1 +
2)).substr($pmid, ($pos1 +
2 +
$spacelen));
18677 if (substr($pmid, $pos1, 4) == '[()]') {
18678 $linew -= $one_space_width;
18679 } elseif ($pos1 == strpos($pmid, '[(')) {
18685 // remove right space if exist
18686 $pos1 = TCPDF_STATIC
::revstrpos($pmid, ')]');
18688 $pos1 = intval($pos1);
18689 if ($this->isUnicodeFont()) {
18690 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, chr(0).chr(32).')]')) +
2;
18693 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, chr(32).')]')) +
1;
18696 if ($pos1 == $pos2) {
18697 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
18698 $linew -= $one_space_width;
18702 $mdiff = ($tw - $linew);
18703 if ($plalign == 'C') {
18705 $t_x = -($mdiff / 2);
18707 $t_x = ($mdiff / 2);
18709 } elseif ($plalign == 'R') {
18710 // right alignment on LTR document
18712 } elseif ($plalign == 'L') {
18713 // left alignment on RTL document
18716 } // end if startlinex
18717 if (($t_x != 0) OR ($yshift < 0)) {
18719 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k
), ($yshift * $this->k
));
18720 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18721 $endlinepos = strlen($pstart);
18722 if ($this->inxobj
) {
18723 // we are inside an XObject template
18724 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$pend;
18725 foreach ($this->xobjects
[$this->xobjid
]['annotations'] as $pak => $pac) {
18726 if ($pak >= $pask) {
18727 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['x'] +
= $t_x;
18728 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['y'] -= $yshift;
18732 $this->setPageBuffer($startlinepage, $pstart.$pend);
18733 // shift the annotations and links
18734 if (isset($this->PageAnnots
[$this->page
])) {
18735 foreach ($this->PageAnnots
[$this->page
] as $pak => $pac) {
18736 if ($pak >= $pask) {
18737 $this->PageAnnots
[$this->page
][$pak]['x'] +
= $t_x;
18738 $this->PageAnnots
[$this->page
][$pak]['y'] -= $yshift;
18743 $this->y
-= $yshift;
18747 // restore previous values
18748 $this->setGraphicVars($gvars);
18749 if ($this->num_columns
> 1) {
18750 $this->selectColumn();
18751 } elseif ($this->page
> $prevPage) {
18752 $this->lMargin
= $this->pagedim
[$this->page
]['olm'];
18753 $this->rMargin
= $this->pagedim
[$this->page
]['orm'];
18755 // restore previous list state
18756 $this->cell_height_ratio
= $prev_cell_height_ratio;
18757 $this->listnum
= $prev_listnum;
18758 $this->listordered
= $prev_listordered;
18759 $this->listcount
= $prev_listcount;
18760 $this->lispacer
= $prev_lispacer;
18761 if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
18762 $this->Ln($this->lasth
);
18763 if ($this->y
< $maxbottomliney) {
18764 $this->y
= $maxbottomliney;
18771 * Process opening tags.
18772 * @param $dom (array) html dom array
18773 * @param $key (int) current element id
18774 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
18775 * @return $dom array
18778 protected function openHTMLTagHandler($dom, $key, $cell) {
18780 $parent = $dom[($dom[$key]['parent'])];
18781 $firsttag = ($key == 1);
18782 // check for text direction attribute
18783 if (isset($tag['dir'])) {
18784 $this->setTempRTL($tag['dir']);
18786 $this->tmprtl
= false;
18788 if ($tag['block']) {
18789 $hbz = 0; // distance from y to line bottom
18790 $hb = 0; // vertical space between block tags
18791 // calculate vertical space for block tags
18792 if (isset($this->tagvspaces
[$tag['value']][0]['h']) AND ($this->tagvspaces
[$tag['value']][0]['h'] >= 0)) {
18793 $cur_h = $this->tagvspaces
[$tag['value']][0]['h'];
18794 } elseif (isset($tag['fontsize'])) {
18795 $cur_h = $this->getCellHeight($tag['fontsize'] / $this->k
);
18797 $cur_h = $this->getCellHeight($this->FontSize
);
18799 if (isset($this->tagvspaces
[$tag['value']][0]['n'])) {
18800 $on = $this->tagvspaces
[$tag['value']][0]['n'];
18801 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18806 if ((!isset($this->tagvspaces
[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br', 'hr')))) {
18809 $hb = ($on * $cur_h);
18811 if (($this->htmlvspace
<= 0) AND ($on > 0)) {
18812 if (isset($parent['fontsize'])) {
18813 $hbz = (($parent['fontsize'] / $this->k
) * $this->cell_height_ratio
);
18815 $hbz = $this->getCellHeight($this->FontSize
);
18818 if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) {
18819 // fix vertical space after table
18822 // closing vertical space
18824 if (isset($this->tagvspaces
[$tag['value']][1]['h']) AND ($this->tagvspaces
[$tag['value']][1]['h'] >= 0)) {
18825 $pre_h = $this->tagvspaces
[$tag['value']][1]['h'];
18826 } elseif (isset($parent['fontsize'])) {
18827 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k
);
18829 $pre_h = $this->getCellHeight($this->FontSize
);
18831 if (isset($this->tagvspaces
[$tag['value']][1]['n'])) {
18832 $cn = $this->tagvspaces
[$tag['value']][1]['n'];
18833 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18838 if (isset($this->tagvspaces
[$tag['value']][1])) {
18839 $hbc = ($cn * $pre_h);
18843 switch($tag['value']) {
18847 $dom[$key]['rowspans'] = array();
18848 if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
18849 $this->htmlvspace
= 0;
18850 // set table header
18851 if (!TCPDF_STATIC
::empty_string($dom[$key]['thead'])) {
18852 // set table header
18853 $this->thead
= $dom[$key]['thead'];
18854 if (!isset($this->theadMargins
) OR (empty($this->theadMargins
))) {
18855 $this->theadMargins
= array();
18856 $this->theadMargins
['cell_padding'] = $this->cell_padding
;
18857 $this->theadMargins
['lmargin'] = $this->lMargin
;
18858 $this->theadMargins
['rmargin'] = $this->rMargin
;
18859 $this->theadMargins
['page'] = $this->page
;
18860 $this->theadMargins
['cell'] = $cell;
18861 $this->theadMargins
['gvars'] = $this->getGraphicVars();
18865 // store current margins and page
18866 $dom[$key]['old_cell_padding'] = $this->cell_padding
;
18867 if (isset($tag['attribute']['cellpadding'])) {
18868 $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
18869 $this->SetCellPadding($pad);
18870 } elseif (isset($tag['padding'])) {
18871 $this->cell_padding
= $tag['padding'];
18873 if (isset($tag['attribute']['cellspacing'])) {
18874 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
18875 } elseif (isset($tag['border-spacing'])) {
18876 $cs = $tag['border-spacing']['V'];
18878 $prev_y = $this->y
;
18879 if ($this->checkPageBreak(((2 * $cp) +
(2 * $cs) +
$this->lasth
), '', false) OR ($this->y
< $prev_y)) {
18880 $this->inthead
= true;
18881 // add a page (or trig AcceptPageBreak() for multicolumn mode)
18882 $this->checkPageBreak($this->PageBreakTrigger +
1);
18887 // array of columns positions
18888 $dom[$key]['cellpos'] = array();
18892 if ((isset($tag['height'])) AND ($tag['height'] != '')) {
18893 $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
18895 $hrHeight = $this->GetLineWidth();
18897 $this->addHTMLVertSpace($hbz, max($hb, ($hrHeight / 2)), $cell, $firsttag);
18898 $x = $this->GetX();
18899 $y = $this->GetY();
18900 $wtmp = $this->w
- $this->lMargin
- $this->rMargin
;
18902 $wtmp -= ($this->cell_padding
['L'] +
$this->cell_padding
['R']);
18904 if ((isset($tag['width'])) AND ($tag['width'] != '')) {
18905 $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
18909 $prevlinewidth = $this->GetLineWidth();
18910 $this->SetLineWidth($hrHeight);
18911 $this->Line($x, $y, $x +
$hrWidth, $y);
18912 $this->SetLineWidth($prevlinewidth);
18913 $this->addHTMLVertSpace(max($hbc, ($hrHeight / 2)), 0, $cell, !isset($dom[($key +
1)]));
18917 if (array_key_exists('href', $tag['attribute'])) {
18918 $this->HREF
['url'] = $tag['attribute']['href'];
18923 if (!empty($tag['attribute']['src'])) {
18924 if ($tag['attribute']['src'][0] === '@') {
18926 $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1));
18930 $type = TCPDF_IMAGES
::getImageFileType($tag['attribute']['src']);
18932 if (!isset($tag['width'])) {
18935 if (!isset($tag['height'])) {
18936 $tag['height'] = 0;
18938 //if (!isset($tag['attribute']['align'])) {
18939 // the only alignment supported is "bottom"
18940 // further development is required for other modes.
18941 $tag['attribute']['align'] = 'bottom';
18943 switch($tag['attribute']['align']) {
18964 if (isset($this->HREF
['url']) AND !TCPDF_STATIC
::empty_string($this->HREF
['url'])) {
18965 $imglink = $this->HREF
['url'];
18966 if ($imglink[0] == '#') {
18967 // convert url to internal link
18968 $lnkdata = explode(',', $imglink);
18969 if (isset($lnkdata[0])) {
18970 $page = intval(substr($lnkdata[0], 1));
18971 if (empty($page) OR ($page <= 0)) {
18972 $page = $this->page
;
18974 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
18975 $lnky = floatval($lnkdata[1]);
18979 $imglink = $this->AddLink();
18980 $this->SetLink($imglink, $lnky, $page);
18985 if (isset($tag['border']) AND !empty($tag['border'])) {
18986 // currently only support 1 (frame) or a combination of 'LTRB'
18987 $border = $tag['border'];
18990 if (isset($tag['width'])) {
18991 $iw = $this->getHTMLUnitToUnits($tag['width'], ($tag['fontsize'] / $this->k
), 'px', false);
18994 if (isset($tag['height'])) {
18995 $ih = $this->getHTMLUnitToUnits($tag['height'], ($tag['fontsize'] / $this->k
), 'px', false);
18997 if (($type == 'eps') OR ($type == 'ai')) {
18998 $this->ImageEps($tag['attribute']['src'], $xpos, $this->y
, $iw, $ih, $imglink, true, $align, '', $border, true);
18999 } elseif ($type == 'svg') {
19000 $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y
, $iw, $ih, $imglink, $align, '', $border, true);
19002 $this->Image($tag['attribute']['src'], $xpos, $this->y
, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
19010 $this->y
= (($this->img_rb_y +
$prevy - ($this->getCellHeight($tag['fontsize'] / $this->k
))) / 2);
19014 $this->y
= $this->img_rb_y
- ($this->getCellHeight($tag['fontsize'] / $this->k
) - ($this->getFontDescent($tag['fontname'], $tag['fontstyle'], $tag['fontsize']) * $this->cell_height_ratio
));
19023 if ($this->listnum
== 1) {
19024 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19026 $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
19031 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19036 $this->rMargin +
= $this->listindent
;
19038 $this->lMargin +
= $this->listindent
;
19040 ++
$this->listindentlevel
;
19041 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19047 if ($tag['value'] == 'ol') {
19048 $this->listordered
[$this->listnum
] = true;
19050 $this->listordered
[$this->listnum
] = false;
19052 if (isset($tag['attribute']['start'])) {
19053 $this->listcount
[$this->listnum
] = intval($tag['attribute']['start']) - 1;
19055 $this->listcount
[$this->listnum
] = 0;
19058 $this->rMargin +
= $this->listindent
;
19059 $this->x
-= $this->listindent
;
19061 $this->lMargin +
= $this->listindent
;
19062 $this->x +
= $this->listindent
;
19064 ++
$this->listindentlevel
;
19065 if ($this->listnum
== 1) {
19067 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19070 $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
19076 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19078 if ($this->listordered
[$this->listnum
]) {
19080 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC
::empty_string($parent['attribute']['type'])) {
19081 $this->lispacer
= $parent['attribute']['type'];
19082 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC
::empty_string($parent['listtype'])) {
19083 $this->lispacer
= $parent['listtype'];
19084 } elseif (isset($this->lisymbol
) AND !TCPDF_STATIC
::empty_string($this->lisymbol
)) {
19085 $this->lispacer
= $this->lisymbol
;
19087 $this->lispacer
= '#';
19089 ++
$this->listcount
[$this->listnum
];
19090 if (isset($tag['attribute']['value'])) {
19091 $this->listcount
[$this->listnum
] = intval($tag['attribute']['value']);
19095 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC
::empty_string($parent['attribute']['type'])) {
19096 $this->lispacer
= $parent['attribute']['type'];
19097 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC
::empty_string($parent['listtype'])) {
19098 $this->lispacer
= $parent['listtype'];
19099 } elseif (isset($this->lisymbol
) AND !TCPDF_STATIC
::empty_string($this->lisymbol
)) {
19100 $this->lispacer
= $this->lisymbol
;
19102 $this->lispacer
= '!';
19107 case 'blockquote': {
19109 $this->rMargin +
= $this->listindent
;
19111 $this->lMargin +
= $this->listindent
;
19113 ++
$this->listindentlevel
;
19114 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19118 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19122 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19126 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19130 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19131 $this->premode
= true;
19135 $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt
) / $this->k
));
19139 $this->SetXY($this->GetX(), $this->GetY() +
((0.3 * $this->FontSizePt
) / $this->k
));
19148 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19151 // Form fields (since 4.8.000 - 2009-09-07)
19153 if (isset($tag['attribute']['action'])) {
19154 $this->form_action
= $tag['attribute']['action'];
19156 $this->Error('Please explicitly set action attribute path!');
19158 if (isset($tag['attribute']['enctype'])) {
19159 $this->form_enctype
= $tag['attribute']['enctype'];
19161 $this->form_enctype
= 'application/x-www-form-urlencoded';
19163 if (isset($tag['attribute']['method'])) {
19164 $this->form_mode
= $tag['attribute']['method'];
19166 $this->form_mode
= 'post';
19171 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['name'])) {
19172 $name = $tag['attribute']['name'];
19178 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['readonly'])) {
19179 $prop['readonly'] = true;
19181 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['value'])) {
19182 $value = $tag['attribute']['value'];
19184 if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['maxlength'])) {
19185 $opt['maxlen'] = intval($tag['attribute']['maxlength']);
19187 $h = $this->getCellHeight($this->FontSize
);
19188 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['size'])) {
19189 $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
19193 if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
19198 if (isset($tag['align'])) {
19199 switch ($tag['align']) {
19214 switch ($tag['attribute']['type']) {
19216 if (isset($value)) {
19217 $opt['v'] = $value;
19219 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19223 if (isset($value)) {
19224 $opt['v'] = $value;
19226 $prop['password'] = 'true';
19227 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19231 if (!isset($value)) {
19234 $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
19238 if (!isset($value)) {
19241 $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
19245 if (!isset($value)) {
19248 $w = $this->GetStringWidth($value) * 1.5;
19250 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19252 $action['S'] = 'SubmitForm';
19253 $action['F'] = $this->form_action
;
19254 if ($this->form_enctype
!= 'FDF') {
19255 $action['Flags'] = array('ExportFormat');
19257 if ($this->form_mode
== 'get') {
19258 $action['Flags'] = array('GetMethod');
19260 $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
19264 if (!isset($value)) {
19267 $w = $this->GetStringWidth($value) * 1.5;
19269 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19270 $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
19274 $prop['fileSelect'] = 'true';
19275 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19276 if (!isset($value)) {
19279 $w = $this->GetStringWidth($value) * 2;
19281 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19282 $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
19283 $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19287 if (isset($value)) {
19288 $opt['v'] = $value;
19290 $opt['f'] = array('invisible', 'hidden');
19291 $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
19295 // THIS TYPE MUST BE FIXED
19296 if (isset($tag['attribute']['src']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['src'])) {
19297 $img = $tag['attribute']['src'];
19302 //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
19303 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19304 $jsaction = $tag['attribute']['onclick'];
19308 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19312 if (!isset($value)) {
19315 $w = $this->GetStringWidth($value) * 1.5;
19317 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19318 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19319 $jsaction = $tag['attribute']['onclick'];
19323 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19332 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['readonly'])) {
19333 $prop['readonly'] = true;
19335 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['name'])) {
19336 $name = $tag['attribute']['name'];
19340 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['value'])) {
19341 $opt['v'] = $tag['attribute']['value'];
19343 if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['cols'])) {
19344 $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
19348 if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['rows'])) {
19349 $h = intval($tag['attribute']['rows']) * $this->getCellHeight($this->FontSize
);
19353 $prop['multiline'] = 'true';
19354 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19358 $h = $this->getCellHeight($this->FontSize
);
19359 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['size'])) {
19360 $h *= ($tag['attribute']['size'] +
1);
19364 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['name'])) {
19365 $name = $tag['attribute']['name'];
19370 if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['opt'])) {
19371 $options = explode('#!NwL!#', $tag['attribute']['opt']);
19373 foreach ($options as $val) {
19374 if (strpos($val, '#!TaB!#') !== false) {
19375 $opts = explode('#!TaB!#', $val);
19377 $w = max($w, $this->GetStringWidth($opts[1]));
19380 $w = max($w, $this->GetStringWidth($val));
19387 if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
19388 $prop['multipleSelection'] = 'true';
19389 $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19391 $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19396 if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML
=== true)) {
19397 // Special tag used to call TCPDF methods
19398 if (isset($tag['attribute']['method'])) {
19399 $tcpdf_method = $tag['attribute']['method'];
19400 if (method_exists($this, $tcpdf_method)) {
19401 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
19402 $params = TCPDF_STATIC
::unserializeTCPDFtagParameters($tag['attribute']['params']);
19403 call_user_func_array(array($this, $tcpdf_method), $params);
19405 $this->$tcpdf_method();
19407 $this->newline
= true;
19417 // define tags that support borders and background colors
19418 $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
19419 if (in_array($tag['value'], $bordertags)) {
19421 $dom[$key]['borderposition'] = $this->getBorderStartPosition();
19423 if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
19424 $pba = $dom[$key]['attribute']['pagebreakafter'];
19425 // check for pagebreak
19426 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19427 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19428 $this->checkPageBreak($this->PageBreakTrigger +
1);
19430 if ((($pba == 'left') AND (((!$this->rtl
) AND (($this->page %
2) == 0)) OR (($this->rtl
) AND (($this->page %
2) != 0))))
19431 OR (($pba == 'right') AND (((!$this->rtl
) AND (($this->page %
2) != 0)) OR (($this->rtl
) AND (($this->page %
2) == 0))))) {
19432 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19433 $this->checkPageBreak($this->PageBreakTrigger +
1);
19440 * Process closing tags.
19441 * @param $dom (array) html dom array
19442 * @param $key (int) current element id
19443 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
19444 * @param $maxbottomliney (int) maximum y value of current line
19445 * @return $dom array
19448 protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
19450 $parent = $dom[($dom[$key]['parent'])];
19451 $lasttag = ((!isset($dom[($key +
1)])) OR ((!isset($dom[($key +
2)])) AND ($dom[($key +
1)]['value'] == 'marker')));
19452 $in_table_head = false;
19453 // maximum x position (used to draw borders)
19459 if ($tag['block']) {
19460 $hbz = 0; // distance from y to line bottom
19461 $hb = 0; // vertical space between block tags
19462 // calculate vertical space for block tags
19463 if (isset($this->tagvspaces
[$tag['value']][1]['h']) AND ($this->tagvspaces
[$tag['value']][1]['h'] >= 0)) {
19464 $pre_h = $this->tagvspaces
[$tag['value']][1]['h'];
19465 } elseif (isset($parent['fontsize'])) {
19466 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k
);
19468 $pre_h = $this->getCellHeight($this->FontSize
);
19470 if (isset($this->tagvspaces
[$tag['value']][1]['n'])) {
19471 $cn = $this->tagvspaces
[$tag['value']][1]['n'];
19472 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
19477 if ((!isset($this->tagvspaces
[$tag['value']])) AND ($tag['value'] == 'div')) {
19480 $hb = ($cn * $pre_h);
19482 if ($maxbottomliney > $this->PageBreakTrigger
) {
19483 $hbz = $this->getCellHeight($this->FontSize
);
19484 } elseif ($this->y
< $maxbottomliney) {
19485 $hbz = ($maxbottomliney - $this->y
);
19489 switch($tag['value']) {
19491 $table_el = $dom[($dom[$key]['parent'])]['parent'];
19492 if (!isset($parent['endy'])) {
19493 $dom[($dom[$key]['parent'])]['endy'] = $this->y
;
19494 $parent['endy'] = $this->y
;
19496 if (!isset($parent['endpage'])) {
19497 $dom[($dom[$key]['parent'])]['endpage'] = $this->page
;
19498 $parent['endpage'] = $this->page
;
19500 if (!isset($parent['endcolumn'])) {
19501 $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column
;
19502 $parent['endcolumn'] = $this->current_column
;
19504 // update row-spanned cells
19505 if (isset($dom[$table_el]['rowspans'])) {
19506 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19507 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
19508 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19509 if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
19510 $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
19511 } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
19512 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19513 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19514 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19518 // report new endy and endpage to the rowspanned cells
19519 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19520 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19521 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
19522 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19523 $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
19524 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19525 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
19526 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19529 // update remaining rowspanned cells
19530 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19531 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19532 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
19533 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
19534 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
19538 $prev_page = $this->page
;
19539 $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
19540 if ($this->num_columns
> 1) {
19541 if ((($this->current_column
== 0) AND ($dom[($dom[$key]['parent'])]['endcolumn'] == ($this->num_columns
- 1)))
19542 OR (($this->current_column
== $dom[($dom[$key]['parent'])]['endcolumn']) AND ($prev_page < $this->page
))) {
19544 $this->selectColumn(0);
19545 $dom[($dom[$key]['parent'])]['endcolumn'] = 0;
19546 $dom[($dom[$key]['parent'])]['endy'] = $this->y
;
19548 $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
19549 $this->y
= $dom[($dom[$key]['parent'])]['endy'];
19552 $this->y
= $dom[($dom[$key]['parent'])]['endy'];
19554 if (isset($dom[$table_el]['attribute']['cellspacing'])) {
19555 $this->y +
= $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
19556 } elseif (isset($dom[$table_el]['border-spacing'])) {
19557 $this->y +
= $dom[$table_el]['border-spacing']['V'];
19559 $this->Ln(0, $cell);
19560 if ($this->current_column
== $parent['startcolumn']) {
19561 $this->x
= $parent['startx'];
19563 // account for booklet mode
19564 if ($this->page
> $parent['startpage']) {
19565 if (($this->rtl
) AND ($this->pagedim
[$this->page
]['orm'] != $this->pagedim
[$parent['startpage']]['orm'])) {
19566 $this->x
-= ($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$parent['startpage']]['orm']);
19567 } elseif ((!$this->rtl
) AND ($this->pagedim
[$this->page
]['olm'] != $this->pagedim
[$parent['startpage']]['olm'])) {
19568 $this->x +
= ($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$parent['startpage']]['olm']);
19574 // closing tag used for the thead part
19575 $in_table_head = true;
19576 $this->inthead
= false;
19578 $table_el = $parent;
19579 // set default border
19580 if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
19581 // set default border
19582 $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
19586 $default_border = $border;
19587 // fix bottom line alignment of last line before page break
19588 foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
19589 // update row-spanned cells
19590 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19591 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19592 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) {
19593 $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
19595 if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) {
19596 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
19600 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
19601 $pgendy = $this->pagedim
[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim
[$dom[$prevtrkey]['endpage']]['bm'];
19602 $dom[$prevtrkey]['endy'] = $pgendy;
19603 // update row-spanned cells
19604 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19605 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19606 if (($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
19607 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
19608 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
19613 $prevtrkey = $trkey;
19614 $table_el = $dom[($dom[$key]['parent'])];
19617 if (count($table_el['trids']) > 0) {
19620 foreach ($table_el['trids'] as $j => $trkey) {
19621 $parent = $dom[$trkey];
19622 if (!isset($xmax)) {
19623 $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
19625 // for each cell on the row
19626 foreach ($parent['cellpos'] as $k => $cellpos) {
19627 if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
19628 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
19629 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
19630 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
19631 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
19632 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
19633 $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
19634 $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
19636 $endy = $parent['endy'];
19637 $startpage = $parent['startpage'];
19638 $endpage = $parent['endpage'];
19639 $startcolumn = $parent['startcolumn'];
19640 $endcolumn = $parent['endcolumn'];
19642 if ($this->num_columns
== 0) {
19643 $this->num_columns
= 1;
19645 if (isset($cellpos['border'])) {
19646 $border = $cellpos['border'];
19648 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
19649 $this->SetFillColorArray($cellpos['bgcolor']);
19654 $x = $cellpos['startx'];
19655 $y = $parent['starty'];
19657 $w = abs($cellpos['endx'] - $cellpos['startx']);
19658 // get border modes
19659 $border_start = TCPDF_STATIC
::getBorderMode($border, $position='start', $this->opencell
);
19660 $border_end = TCPDF_STATIC
::getBorderMode($border, $position='end', $this->opencell
);
19661 $border_middle = TCPDF_STATIC
::getBorderMode($border, $position='middle', $this->opencell
);
19662 // design borders around HTML cells.
19663 for ($page = $startpage; $page <= $endpage; ++
$page) { // for each page
19665 $this->setPage($page);
19666 if ($this->num_columns
< 2) {
19667 // single-column mode
19669 $this->y
= $this->tMargin
;
19671 // account for margin changes
19672 if ($page > $startpage) {
19673 if (($this->rtl
) AND ($this->pagedim
[$page]['orm'] != $this->pagedim
[$startpage]['orm'])) {
19674 $this->x
-= ($this->pagedim
[$page]['orm'] - $this->pagedim
[$startpage]['orm']);
19675 } elseif ((!$this->rtl
) AND ($this->pagedim
[$page]['olm'] != $this->pagedim
[$startpage]['olm'])) {
19676 $this->x +
= ($this->pagedim
[$page]['olm'] - $this->pagedim
[$startpage]['olm']);
19679 if ($startpage == $endpage) { // single page
19682 for ($column = $startcolumn; $column <= $endcolumn; ++
$column) { // for each column
19683 $this->selectColumn($column);
19684 if ($startcolumn == $endcolumn) { // single column
19685 $cborder = $border;
19686 $h = $endy - $parent['starty'];
19689 } elseif ($column == $startcolumn) { // first column
19690 $cborder = $border_start;
19691 $this->y
= $starty;
19693 $h = $this->h
- $this->y
- $this->bMargin
;
19695 $deltacol = $this->x +
$this->rMargin
- $this->w
;
19697 $deltacol = $this->x
- $this->lMargin
;
19699 } elseif ($column == $endcolumn) { // end column
19700 $cborder = $border_end;
19701 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19702 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19704 $this->x +
= $deltacol;
19705 $h = $endy - $this->y
;
19706 } else { // middle column
19707 $cborder = $border_middle;
19708 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19709 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19711 $this->x +
= $deltacol;
19712 $h = $this->h
- $this->y
- $this->bMargin
;
19714 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19715 } // end for each column
19716 } elseif ($page == $startpage) { // first page
19719 for ($column = $startcolumn; $column < $this->num_columns
; ++
$column) { // for each column
19720 $this->selectColumn($column);
19721 if ($column == $startcolumn) { // first column
19722 $cborder = $border_start;
19723 $this->y
= $starty;
19725 $h = $this->h
- $this->y
- $this->bMargin
;
19727 $deltacol = $this->x +
$this->rMargin
- $this->w
;
19729 $deltacol = $this->x
- $this->lMargin
;
19731 } else { // middle column
19732 $cborder = $border_middle;
19733 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19734 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19736 $this->x +
= $deltacol;
19737 $h = $this->h
- $this->y
- $this->bMargin
;
19739 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19740 } // end for each column
19741 } elseif ($page == $endpage) { // last page
19744 for ($column = 0; $column <= $endcolumn; ++
$column) { // for each column
19745 $this->selectColumn($column);
19746 if ($column == $endcolumn) { // end column
19747 $cborder = $border_end;
19748 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19749 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19751 $this->x +
= $deltacol;
19752 $h = $endy - $this->y
;
19753 } else { // middle column
19754 $cborder = $border_middle;
19755 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19756 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19758 $this->x +
= $deltacol;
19759 $h = $this->h
- $this->y
- $this->bMargin
;
19761 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19762 } // end for each column
19763 } else { // middle page
19766 for ($column = 0; $column < $this->num_columns
; ++
$column) { // for each column
19767 $this->selectColumn($column);
19768 $cborder = $border_middle;
19769 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19770 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19772 $this->x +
= $deltacol;
19773 $h = $this->h
- $this->y
- $this->bMargin
;
19774 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19775 } // end for each column
19777 if ($cborder OR $fill) {
19778 $offsetlen = strlen($ccode);
19779 // draw border and fill
19780 if ($this->inxobj
) {
19781 // we are inside an XObject template
19782 if (end($this->xobjects
[$this->xobjid
]['transfmrk']) !== false) {
19783 $pagemarkkey = key($this->xobjects
[$this->xobjid
]['transfmrk']);
19784 $pagemark = $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey];
19785 $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey] +
= $offsetlen;
19787 $pagemark = $this->xobjects
[$this->xobjid
]['intmrk'];
19788 $this->xobjects
[$this->xobjid
]['intmrk'] +
= $offsetlen;
19790 $pagebuff = $this->xobjects
[$this->xobjid
]['outdata'];
19791 $pstart = substr($pagebuff, 0, $pagemark);
19792 $pend = substr($pagebuff, $pagemark);
19793 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$ccode.$pend;
19795 // draw border and fill
19796 if (end($this->transfmrk
[$this->page
]) !== false) {
19797 $pagemarkkey = key($this->transfmrk
[$this->page
]);
19798 $pagemark = $this->transfmrk
[$this->page
][$pagemarkkey];
19799 } elseif ($this->InFooter
) {
19800 $pagemark = $this->footerpos
[$this->page
];
19802 $pagemark = $this->intmrk
[$this->page
];
19804 $pagebuff = $this->getPageBuffer($this->page
);
19805 $pstart = substr($pagebuff, 0, $pagemark);
19806 $pend = substr($pagebuff, $pagemark);
19807 $this->setPageBuffer($this->page
, $pstart.$ccode.$pend);
19810 } // end for each page
19811 // restore default border
19812 $border = $default_border;
19813 } // end for each cell on the row
19814 if (isset($table_el['attribute']['cellspacing'])) {
19815 $this->y +
= $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
19816 } elseif (isset($table_el['border-spacing'])) {
19817 $this->y +
= $table_el['border-spacing']['V'];
19819 $this->Ln(0, $cell);
19820 $this->x
= $parent['startx'];
19821 if ($endpage > $startpage) {
19822 if (($this->rtl
) AND ($this->pagedim
[$endpage]['orm'] != $this->pagedim
[$startpage]['orm'])) {
19823 $this->x +
= ($this->pagedim
[$endpage]['orm'] - $this->pagedim
[$startpage]['orm']);
19824 } elseif ((!$this->rtl
) AND ($this->pagedim
[$endpage]['olm'] != $this->pagedim
[$startpage]['olm'])) {
19825 $this->x +
= ($this->pagedim
[$endpage]['olm'] - $this->pagedim
[$startpage]['olm']);
19829 if (!$in_table_head) { // we are not inside a thead section
19830 $this->cell_padding
= $table_el['old_cell_padding'];
19831 // reset row height
19832 $this->resetLastH();
19833 if (($this->page
== ($this->numpages
- 1)) AND ($this->pageopen
[$this->numpages
])) {
19834 $plendiff = ($this->pagelen
[$this->numpages
] - $this->emptypagemrk
[$this->numpages
]);
19835 if (($plendiff > 0) AND ($plendiff < 60)) {
19836 $pagediff = substr($this->getPageBuffer($this->numpages
), $this->emptypagemrk
[$this->numpages
], $plendiff);
19837 if (substr($pagediff, 0, 5) == 'BT /F') {
19838 // the difference is only a font setting
19842 if ($plendiff == 0) {
19843 // remove last blank page
19844 $this->deletePage($this->numpages
);
19847 if (isset($this->theadMargins
['top'])) {
19848 // restore top margin
19849 $this->tMargin
= $this->theadMargins
['top'];
19851 if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
19852 // reset main table header
19854 $this->theadMargins
= array();
19855 $this->pagedim
[$this->page
]['tm'] = $this->tMargin
;
19858 $parent = $table_el;
19866 $this->SetXY($this->GetX(), $this->GetY() +
((0.7 * $parent['fontsize']) / $this->k
));
19870 $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k
));
19874 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19877 case 'blockquote': {
19879 $this->rMargin
-= $this->listindent
;
19881 $this->lMargin
-= $this->listindent
;
19883 --$this->listindentlevel
;
19884 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19888 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19892 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19893 $this->premode
= false;
19898 if ($this->listnum
<= 0) {
19899 $this->listnum
= 0;
19900 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19902 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19904 $this->resetLastH();
19908 $this->lispacer
= '';
19909 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19913 $this->lispacer
= '';
19915 $this->rMargin
-= $this->listindent
;
19917 $this->lMargin
-= $this->listindent
;
19919 --$this->listindentlevel
;
19920 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19926 $this->lispacer
= '';
19928 $this->rMargin
-= $this->listindent
;
19930 $this->lMargin
-= $this->listindent
;
19932 --$this->listindentlevel
;
19933 if ($this->listnum
<= 0) {
19934 $this->listnum
= 0;
19935 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19937 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19939 $this->resetLastH();
19943 $this->lispacer
= '';
19944 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19953 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19956 // Form fields (since 4.8.000 - 2009-09-07)
19958 $this->form_action
= '';
19959 $this->form_enctype
= 'application/x-www-form-urlencoded';
19966 // draw border and background (if any)
19967 $this->drawHTMLTagBorder($parent, $xmax);
19968 if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
19969 $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
19970 // check for pagebreak
19971 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19972 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19973 $this->checkPageBreak($this->PageBreakTrigger +
1);
19975 if ((($pba == 'left') AND (((!$this->rtl
) AND (($this->page %
2) == 0)) OR (($this->rtl
) AND (($this->page %
2) != 0))))
19976 OR (($pba == 'right') AND (((!$this->rtl
) AND (($this->page %
2) != 0)) OR (($this->rtl
) AND (($this->page %
2) == 0))))) {
19977 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19978 $this->checkPageBreak($this->PageBreakTrigger +
1);
19981 $this->tmprtl
= false;
19986 * Add vertical spaces if needed.
19987 * @param $hbz (string) Distance between current y and line bottom.
19988 * @param $hb (string) The height of the break.
19989 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
19990 * @param $firsttag (boolean) set to true when the tag is the first.
19991 * @param $lasttag (boolean) set to true when the tag is the last.
19994 protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
19996 $this->Ln(0, $cell);
19997 $this->htmlvspace
= 0;
20001 $this->Ln($hbz, $cell);
20002 $this->htmlvspace
= 0;
20005 if ($hb < $this->htmlvspace
) {
20008 $hd = $hb - $this->htmlvspace
;
20009 $this->htmlvspace
= $hb;
20011 $this->Ln(($hbz +
$hd), $cell);
20015 * Return the starting coordinates to draw an html border
20016 * @return array containing top-left border coordinates
20018 * @since 5.7.000 (2010-08-03)
20020 protected function getBorderStartPosition() {
20022 $xmax = $this->lMargin
;
20024 $xmax = $this->w
- $this->rMargin
;
20026 return array('page' => $this->page
, 'column' => $this->current_column
, 'x' => $this->x
, 'y' => $this->y
, 'xmax' => $xmax);
20030 * Draw an HTML block border and fill
20031 * @param $tag (array) array of tag properties.
20032 * @param $xmax (int) end X coordinate for border.
20034 * @since 5.7.000 (2010-08-03)
20036 protected function drawHTMLTagBorder($tag, $xmax) {
20037 if (!isset($tag['borderposition'])) {
20041 $prev_x = $this->x
;
20042 $prev_y = $this->y
;
20043 $prev_lasth = $this->lasth
;
20047 if (isset($tag['border']) AND !empty($tag['border'])) {
20048 // get border style
20049 $border = $tag['border'];
20050 if (!TCPDF_STATIC
::empty_string($this->thead
) AND (!$this->inthead
)) {
20051 // border for table header
20052 $border = TCPDF_STATIC
::getBorderMode($border, $position='middle', $this->opencell
);
20055 if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
20056 // get background color
20057 $old_bgcolor = $this->bgcolor
;
20058 $this->SetFillColorArray($tag['bgcolor']);
20061 if (!$border AND !$fill) {
20065 if (isset($tag['attribute']['cellspacing'])) {
20066 $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
20067 $cellspacing = array('H' => $clsp, 'V' => $clsp);
20068 } elseif (isset($tag['border-spacing'])) {
20069 $cellspacing = $tag['border-spacing'];
20071 $cellspacing = array('H' => 0, 'V' => 0);
20073 if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
20074 // draw the border externally respect the sqare edge.
20075 $border['mode'] = 'ext';
20078 if ($xmax >= $tag['borderposition']['x']) {
20079 $xmax = $tag['borderposition']['xmax'];
20081 $w = ($tag['borderposition']['x'] - $xmax);
20083 if ($xmax <= $tag['borderposition']['x']) {
20084 $xmax = $tag['borderposition']['xmax'];
20086 $w = ($xmax - $tag['borderposition']['x']);
20091 $w +
= $cellspacing['H'];
20092 $startpage = $tag['borderposition']['page'];
20093 $startcolumn = $tag['borderposition']['column'];
20094 $x = $tag['borderposition']['x'];
20095 $y = $tag['borderposition']['y'];
20096 $endpage = $this->page
;
20097 $starty = $tag['borderposition']['y'] - $cellspacing['V'];
20098 $currentY = $this->y
;
20100 // get latest column
20101 $endcolumn = $this->current_column
;
20102 if ($this->num_columns
== 0) {
20103 $this->num_columns
= 1;
20105 // get border modes
20106 $border_start = TCPDF_STATIC
::getBorderMode($border, $position='start', $this->opencell
);
20107 $border_end = TCPDF_STATIC
::getBorderMode($border, $position='end', $this->opencell
);
20108 $border_middle = TCPDF_STATIC
::getBorderMode($border, $position='middle', $this->opencell
);
20109 // temporary disable page regions
20110 $temp_page_regions = $this->page_regions
;
20111 $this->page_regions
= array();
20112 // design borders around HTML cells.
20113 for ($page = $startpage; $page <= $endpage; ++
$page) { // for each page
20115 $this->setPage($page);
20116 if ($this->num_columns
< 2) {
20117 // single-column mode
20119 $this->y
= $this->tMargin
;
20121 // account for margin changes
20122 if ($page > $startpage) {
20123 if (($this->rtl
) AND ($this->pagedim
[$page]['orm'] != $this->pagedim
[$startpage]['orm'])) {
20124 $this->x
-= ($this->pagedim
[$page]['orm'] - $this->pagedim
[$startpage]['orm']);
20125 } elseif ((!$this->rtl
) AND ($this->pagedim
[$page]['olm'] != $this->pagedim
[$startpage]['olm'])) {
20126 $this->x +
= ($this->pagedim
[$page]['olm'] - $this->pagedim
[$startpage]['olm']);
20129 if ($startpage == $endpage) {
20131 for ($column = $startcolumn; $column <= $endcolumn; ++
$column) { // for each column
20132 $this->selectColumn($column);
20133 if ($startcolumn == $endcolumn) { // single column
20134 $cborder = $border;
20135 $h = ($currentY - $y) +
$cellspacing['V'];
20136 $this->y
= $starty;
20137 } elseif ($column == $startcolumn) { // first column
20138 $cborder = $border_start;
20139 $this->y
= $starty;
20140 $h = $this->h
- $this->y
- $this->bMargin
;
20141 } elseif ($column == $endcolumn) { // end column
20142 $cborder = $border_end;
20143 $h = $currentY - $this->y
;
20144 } else { // middle column
20145 $cborder = $border_middle;
20146 $h = $this->h
- $this->y
- $this->bMargin
;
20148 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20149 } // end for each column
20150 } elseif ($page == $startpage) { // first page
20151 for ($column = $startcolumn; $column < $this->num_columns
; ++
$column) { // for each column
20152 $this->selectColumn($column);
20153 if ($column == $startcolumn) { // first column
20154 $cborder = $border_start;
20155 $this->y
= $starty;
20156 $h = $this->h
- $this->y
- $this->bMargin
;
20157 } else { // middle column
20158 $cborder = $border_middle;
20159 $h = $this->h
- $this->y
- $this->bMargin
;
20161 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20162 } // end for each column
20163 } elseif ($page == $endpage) { // last page
20164 for ($column = 0; $column <= $endcolumn; ++
$column) { // for each column
20165 $this->selectColumn($column);
20166 if ($column == $endcolumn) {
20168 $cborder = $border_end;
20169 $h = $currentY - $this->y
;
20172 $cborder = $border_middle;
20173 $h = $this->h
- $this->y
- $this->bMargin
;
20175 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20176 } // end for each column
20177 } else { // middle page
20178 for ($column = 0; $column < $this->num_columns
; ++
$column) { // for each column
20179 $this->selectColumn($column);
20180 $cborder = $border_middle;
20181 $h = $this->h
- $this->y
- $this->bMargin
;
20182 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20183 } // end for each column
20185 if ($cborder OR $fill) {
20186 $offsetlen = strlen($ccode);
20187 // draw border and fill
20188 if ($this->inxobj
) {
20189 // we are inside an XObject template
20190 if (end($this->xobjects
[$this->xobjid
]['transfmrk']) !== false) {
20191 $pagemarkkey = key($this->xobjects
[$this->xobjid
]['transfmrk']);
20192 $pagemark = $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey];
20193 $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey] +
= $offsetlen;
20195 $pagemark = $this->xobjects
[$this->xobjid
]['intmrk'];
20196 $this->xobjects
[$this->xobjid
]['intmrk'] +
= $offsetlen;
20198 $pagebuff = $this->xobjects
[$this->xobjid
]['outdata'];
20199 $pstart = substr($pagebuff, 0, $pagemark);
20200 $pend = substr($pagebuff, $pagemark);
20201 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$ccode.$pend;
20203 if (end($this->transfmrk
[$this->page
]) !== false) {
20204 $pagemarkkey = key($this->transfmrk
[$this->page
]);
20205 $pagemark = $this->transfmrk
[$this->page
][$pagemarkkey];
20206 } elseif ($this->InFooter
) {
20207 $pagemark = $this->footerpos
[$this->page
];
20209 $pagemark = $this->intmrk
[$this->page
];
20211 $pagebuff = $this->getPageBuffer($this->page
);
20212 $pstart = substr($pagebuff, 0, $pagemark);
20213 $pend = substr($pagebuff, $pagemark);
20214 $this->setPageBuffer($this->page
, $pstart.$ccode.$pend);
20215 $this->bordermrk
[$this->page
] +
= $offsetlen;
20216 $this->cntmrk
[$this->page
] +
= $offsetlen;
20219 } // end for each page
20220 // restore page regions
20221 $this->page_regions
= $temp_page_regions;
20222 if (isset($old_bgcolor)) {
20223 // restore background color
20224 $this->SetFillColorArray($old_bgcolor);
20226 // restore pointer position
20227 $this->x
= $prev_x;
20228 $this->y
= $prev_y;
20229 $this->lasth
= $prev_lasth;
20233 * Set the default bullet to be used as LI bullet symbol
20234 * @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')
20236 * @since 4.0.028 (2008-09-26)
20238 public function setLIsymbol($symbol='!') {
20239 // check for custom image symbol
20240 if (substr($symbol, 0, 4) == 'img|') {
20241 $this->lisymbol
= $symbol;
20244 $symbol = strtolower($symbol);
20245 $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');
20246 if (in_array($symbol, $valid_symbols)) {
20247 $this->lisymbol
= $symbol;
20249 $this->lisymbol
= '';
20254 * Set the booklet mode for double-sided pages.
20255 * @param $booklet (boolean) true set the booklet mode on, false otherwise.
20256 * @param $inner (float) Inner page margin.
20257 * @param $outer (float) Outer page margin.
20259 * @since 4.2.000 (2008-10-29)
20261 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
20262 $this->booklet
= $booklet;
20264 $this->lMargin
= $inner;
20267 $this->rMargin
= $outer;
20272 * Swap the left and right margins.
20273 * @param $reverse (boolean) if true swap left and right margins.
20275 * @since 4.2.000 (2008-10-29)
20277 protected function swapMargins($reverse=true) {
20279 // swap left and right margins
20280 $mtemp = $this->original_lMargin
;
20281 $this->original_lMargin
= $this->original_rMargin
;
20282 $this->original_rMargin
= $mtemp;
20283 $deltam = $this->original_lMargin
- $this->original_rMargin
;
20284 $this->lMargin +
= $deltam;
20285 $this->rMargin
-= $deltam;
20290 * Set the vertical spaces for HTML tags.
20291 * The array must have the following structure (example):
20292 * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1)));
20293 * The first array level contains the tag names,
20294 * the second level contains 0 for opening tags or 1 for closing tags,
20295 * the third level contains the vertical space unit (h) and the number spaces to add (n).
20296 * If the h parameter is not specified, default values are used.
20297 * @param $tagvs (array) array of tags and relative vertical spaces.
20299 * @since 4.2.001 (2008-10-30)
20301 public function setHtmlVSpace($tagvs) {
20302 $this->tagvspaces
= $tagvs;
20306 * Set custom width for list indentation.
20307 * @param $width (float) width of the indentation. Use negative value to disable it.
20309 * @since 4.2.007 (2008-11-12)
20311 public function setListIndentWidth($width) {
20312 return $this->customlistindent
= floatval($width);
20316 * Set the top/bottom cell sides to be open or closed when the cell cross the page.
20317 * @param $isopen (boolean) if true keeps the top/bottom border open for the cell sides that cross the page.
20319 * @since 4.2.010 (2008-11-14)
20321 public function setOpenCell($isopen) {
20322 $this->opencell
= $isopen;
20326 * Set the color and font style for HTML links.
20327 * @param $color (array) RGB array of colors
20328 * @param $fontstyle (string) additional font styles to add
20330 * @since 4.4.003 (2008-12-09)
20332 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
20333 $this->htmlLinkColorArray
= $color;
20334 $this->htmlLinkFontStyle
= $fontstyle;
20338 * Convert HTML string containing value and unit of measure to user's units or points.
20339 * @param $htmlval (string) String containing values and unit.
20340 * @param $refsize (string) Reference value in points.
20341 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
20342 * @param $points (boolean) If true returns points, otherwise returns value in user's units.
20343 * @return float value in user's unit or point if $points=true
20345 * @since 4.4.004 (2008-12-10)
20347 public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
20348 $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
20357 if (in_array($defaultunit, $supportedunits)) {
20358 $unit = $defaultunit;
20360 if (is_numeric($htmlval)) {
20361 $value = floatval($htmlval);
20362 } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
20363 $value = floatval($mnum[1]);
20364 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
20365 if (in_array($munit[1], $supportedunits)) {
20373 $retval = (($value * $refsize) / 100);
20378 $retval = ($value * $refsize);
20381 // height of lower case 'x' (about half the font-size)
20383 $retval = ($value * ($refsize / 2));
20388 $retval = (($value * $this->dpi
) / $k);
20393 $retval = (($value / 2.54 * $this->dpi
) / $k);
20398 $retval = (($value / 25.4 * $this->dpi
) / $k);
20401 // one pica is 12 points
20403 $retval = (($value * 12) / $k);
20408 $retval = ($value / $k);
20413 $retval = $this->pixelsToUnits($value);
20415 $retval *= $this->k
;
20424 * Output an HTML list bullet or ordered item symbol
20425 * @param $listdepth (int) list nesting level
20426 * @param $listtype (string) type of list
20427 * @param $size (float) current font size
20429 * @since 4.4.004 (2008-12-10)
20431 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
20432 if ($this->state
!= 2) {
20437 $bgcolor = $this->bgcolor
;
20438 $color = $this->fgcolor
;
20439 $strokecolor = $this->strokecolor
;
20443 $lspace = $this->GetStringWidth(' ');
20444 if ($listtype == '^') {
20445 // special symbol used for avoid justification of rect bullet
20446 $this->lispacer
= '';
20448 } elseif ($listtype == '!') {
20449 // set default list type for unordered list
20450 $deftypes = array('disc', 'circle', 'square');
20451 $listtype = $deftypes[($listdepth - 1) %
3];
20452 } elseif ($listtype == '#') {
20453 // set default list type for ordered list
20454 $listtype = 'decimal';
20455 } elseif (substr($listtype, 0, 4) == 'img|') {
20456 // custom image type ('img|type|width|height|image.ext')
20457 $img = explode('|', $listtype);
20460 switch ($listtype) {
20467 $lspace +
= (2 * $r);
20469 $this->x +
= $lspace;
20471 $this->x
-= $lspace;
20473 $this->Circle(($this->x +
$r), ($this->y +
($this->lasth
/ 2)), $r, 0, 360, 'F', array(), $color, 8);
20478 $lspace +
= (2 * $r);
20480 $this->x +
= $lspace;
20482 $this->x
-= $lspace;
20484 $prev_line_style = $this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
;
20485 $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
20486 $this->Circle(($this->x +
$r), ($this->y +
($this->lasth
/ 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
20487 $this->_out($prev_line_style); // restore line settings
20494 $this->x +
= $lspace;
20496 $this->x
-= $lspace;
20498 $this->Rect($this->x
, ($this->y +
(($this->lasth
- $l) / 2)), $l, $l, 'F', array(), $color);
20502 // 1=>type, 2=>width, 3=>height, 4=>image.ext
20503 $lspace +
= $img[2];
20505 $this->x +
= $lspace;
20507 $this->x
-= $lspace;
20509 $imgtype = strtolower($img[1]);
20510 $prev_y = $this->y
;
20511 switch ($imgtype) {
20513 $this->ImageSVG($img[4], $this->x
, ($this->y +
(($this->lasth
- $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
20518 $this->ImageEps($img[4], $this->x
, ($this->y +
(($this->lasth
- $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
20522 $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);
20526 $this->y
= $prev_y;
20530 // $this->listcount[$this->listnum];
20534 $textitem = $this->listcount
[$this->listnum
];
20537 case 'decimal-leading-zero': {
20538 $textitem = sprintf('%02d', $this->listcount
[$this->listnum
]);
20542 case 'lower-roman': {
20543 $textitem = strtolower(TCPDF_STATIC
::intToRoman($this->listcount
[$this->listnum
]));
20547 case 'upper-roman': {
20548 $textitem = TCPDF_STATIC
::intToRoman($this->listcount
[$this->listnum
]);
20552 case 'lower-alpha':
20553 case 'lower-latin': {
20554 $textitem = chr(97 +
$this->listcount
[$this->listnum
] - 1);
20558 case 'upper-alpha':
20559 case 'upper-latin': {
20560 $textitem = chr(65 +
$this->listcount
[$this->listnum
] - 1);
20563 case 'lower-greek': {
20564 $textitem = TCPDF_FONTS
::unichr((945 +
$this->listcount
[$this->listnum
] - 1), $this->isunicode
);
20568 // Types to be implemented (special handling)
20578 case 'cjk-ideographic': {
20587 case 'hiragana-iroha': {
20590 case 'katakana-iroha': {
20595 $textitem = $this->listcount
[$this->listnum
];
20598 if (!TCPDF_STATIC
::empty_string($textitem)) {
20599 // Check whether we need a new page or new column
20600 $prev_y = $this->y
;
20601 $h = $this->getCellHeight($this->FontSize
);
20602 if ($this->checkPageBreak($h) OR ($this->y
< $prev_y)) {
20605 // print ordered item
20607 $textitem = '.'.$textitem;
20609 $textitem = $textitem.'.';
20611 $lspace +
= $this->GetStringWidth($textitem);
20613 $this->x +
= $lspace;
20615 $this->x
-= $lspace;
20617 $this->Write($this->lasth
, $textitem, '', false, '', false, 0, false);
20620 $this->lispacer
= '^';
20622 $this->SetFillColorArray($bgcolor);
20623 $this->SetDrawColorArray($strokecolor);
20624 $this->SettextColorArray($color);
20628 * Returns current graphic variables as array.
20629 * @return array of graphic variables
20631 * @since 4.2.010 (2008-11-14)
20633 protected function getGraphicVars() {
20635 'FontFamily' => $this->FontFamily
,
20636 'FontStyle' => $this->FontStyle
,
20637 'FontSizePt' => $this->FontSizePt
,
20638 'rMargin' => $this->rMargin
,
20639 'lMargin' => $this->lMargin
,
20640 'cell_padding' => $this->cell_padding
,
20641 'cell_margin' => $this->cell_margin
,
20642 'LineWidth' => $this->LineWidth
,
20643 'linestyleWidth' => $this->linestyleWidth
,
20644 'linestyleCap' => $this->linestyleCap
,
20645 'linestyleJoin' => $this->linestyleJoin
,
20646 'linestyleDash' => $this->linestyleDash
,
20647 'textrendermode' => $this->textrendermode
,
20648 'textstrokewidth' => $this->textstrokewidth
,
20649 'DrawColor' => $this->DrawColor
,
20650 'FillColor' => $this->FillColor
,
20651 'TextColor' => $this->TextColor
,
20652 'ColorFlag' => $this->ColorFlag
,
20653 'bgcolor' => $this->bgcolor
,
20654 'fgcolor' => $this->fgcolor
,
20655 'htmlvspace' => $this->htmlvspace
,
20656 'listindent' => $this->listindent
,
20657 'listindentlevel' => $this->listindentlevel
,
20658 'listnum' => $this->listnum
,
20659 'listordered' => $this->listordered
,
20660 'listcount' => $this->listcount
,
20661 'lispacer' => $this->lispacer
,
20662 'cell_height_ratio' => $this->cell_height_ratio
,
20663 'font_stretching' => $this->font_stretching
,
20664 'font_spacing' => $this->font_spacing
,
20665 'alpha' => $this->alpha
,
20667 'lasth' => $this->lasth
,
20668 'tMargin' => $this->tMargin
,
20669 'bMargin' => $this->bMargin
,
20670 'AutoPageBreak' => $this->AutoPageBreak
,
20671 'PageBreakTrigger' => $this->PageBreakTrigger
,
20676 'wPt' => $this->wPt
,
20677 'hPt' => $this->hPt
,
20678 'fwPt' => $this->fwPt
,
20679 'fhPt' => $this->fhPt
,
20680 'page' => $this->page
,
20681 'current_column' => $this->current_column
,
20682 'num_columns' => $this->num_columns
20688 * Set graphic variables.
20689 * @param $gvars (array) array of graphic variablesto restore
20690 * @param $extended (boolean) if true restore extended graphic variables
20692 * @since 4.2.010 (2008-11-14)
20694 protected function setGraphicVars($gvars, $extended=false) {
20695 if ($this->state
!= 2) {
20698 $this->FontFamily
= $gvars['FontFamily'];
20699 $this->FontStyle
= $gvars['FontStyle'];
20700 $this->FontSizePt
= $gvars['FontSizePt'];
20701 $this->rMargin
= $gvars['rMargin'];
20702 $this->lMargin
= $gvars['lMargin'];
20703 $this->cell_padding
= $gvars['cell_padding'];
20704 $this->cell_margin
= $gvars['cell_margin'];
20705 $this->LineWidth
= $gvars['LineWidth'];
20706 $this->linestyleWidth
= $gvars['linestyleWidth'];
20707 $this->linestyleCap
= $gvars['linestyleCap'];
20708 $this->linestyleJoin
= $gvars['linestyleJoin'];
20709 $this->linestyleDash
= $gvars['linestyleDash'];
20710 $this->textrendermode
= $gvars['textrendermode'];
20711 $this->textstrokewidth
= $gvars['textstrokewidth'];
20712 $this->DrawColor
= $gvars['DrawColor'];
20713 $this->FillColor
= $gvars['FillColor'];
20714 $this->TextColor
= $gvars['TextColor'];
20715 $this->ColorFlag
= $gvars['ColorFlag'];
20716 $this->bgcolor
= $gvars['bgcolor'];
20717 $this->fgcolor
= $gvars['fgcolor'];
20718 $this->htmlvspace
= $gvars['htmlvspace'];
20719 $this->listindent
= $gvars['listindent'];
20720 $this->listindentlevel
= $gvars['listindentlevel'];
20721 $this->listnum
= $gvars['listnum'];
20722 $this->listordered
= $gvars['listordered'];
20723 $this->listcount
= $gvars['listcount'];
20724 $this->lispacer
= $gvars['lispacer'];
20725 $this->cell_height_ratio
= $gvars['cell_height_ratio'];
20726 $this->font_stretching
= $gvars['font_stretching'];
20727 $this->font_spacing
= $gvars['font_spacing'];
20728 $this->alpha
= $gvars['alpha'];
20730 // restore extended values
20731 $this->lasth
= $gvars['lasth'];
20732 $this->tMargin
= $gvars['tMargin'];
20733 $this->bMargin
= $gvars['bMargin'];
20734 $this->AutoPageBreak
= $gvars['AutoPageBreak'];
20735 $this->PageBreakTrigger
= $gvars['PageBreakTrigger'];
20736 $this->x
= $gvars['x'];
20737 $this->y
= $gvars['y'];
20738 $this->w
= $gvars['w'];
20739 $this->h
= $gvars['h'];
20740 $this->wPt
= $gvars['wPt'];
20741 $this->hPt
= $gvars['hPt'];
20742 $this->fwPt
= $gvars['fwPt'];
20743 $this->fhPt
= $gvars['fhPt'];
20744 $this->page
= $gvars['page'];
20745 $this->current_column
= $gvars['current_column'];
20746 $this->num_columns
= $gvars['num_columns'];
20748 $this->_out(''.$this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
.' '.$this->FillColor
.'');
20749 if (!TCPDF_STATIC
::empty_string($this->FontFamily
)) {
20750 $this->SetFont($this->FontFamily
, $this->FontStyle
, $this->FontSizePt
);
20755 * Outputs the "save graphics state" operator 'q'
20758 protected function _outSaveGraphicsState() {
20763 * Outputs the "restore graphics state" operator 'Q'
20766 protected function _outRestoreGraphicsState() {
20771 * Writes data to a temporary file on filesystem.
20772 * @param $filename (string) file name
20773 * @param $data (mixed) data to write on file
20774 * @param $append (boolean) if true append data, false replace.
20775 * @param $serialize (boolean) if true serialize data.
20776 * @since 4.5.000 (2008-12-31)
20779 protected function writeDiskCache($filename, $data, $append=false, $serialize=false) {
20785 $f = @fopen($filename, $fmode);
20787 $this->Error('Unable to write cache file: '.$filename);
20790 $data = $this->file_id
.serialize($data);
20794 // update file length (needed for transactions)
20795 if (!isset($this->cache_file_length
['_'.$filename])) {
20796 $this->cache_file_length
['_'.$filename] = strlen($data);
20798 $this->cache_file_length
['_'.$filename] +
= strlen($data);
20803 * Read data from a temporary file on filesystem.
20804 * @param $filename (string) file name
20805 * @param $unserialize (boolean) if true unserialize data.
20806 * @return mixed retrieved data
20807 * @since 4.5.000 (2008-12-31)
20810 protected function readDiskCache($filename, $unserialize=false) {
20811 $data = file_get_contents($filename);
20812 if ($data === FALSE) {
20813 $this->Error('Unable to read the file: '.$filename);
20815 if ($unserialize) {
20816 if (substr($data, 0, 32) != $this->file_id
) {
20817 $this->Error('Invalid cache file: '.$filename);
20819 $data = unserialize(substr($data, 32));
20825 * Set buffer content (always append data).
20826 * @param $data (string) data
20828 * @since 4.5.000 (2009-01-02)
20830 protected function setBuffer($data) {
20831 $this->bufferlen +
= strlen($data);
20832 if ($this->diskcache
) {
20833 if (!isset($this->buffer
) OR TCPDF_STATIC
::empty_string($this->buffer
)) {
20834 $this->buffer
= TCPDF_STATIC
::getObjFilename('buf');
20836 $this->writeDiskCache($this->buffer
, $data, true, false);
20838 $this->buffer
.= $data;
20843 * Replace the buffer content
20844 * @param $data (string) data
20846 * @since 5.5.000 (2010-06-22)
20848 protected function replaceBuffer($data) {
20849 $this->bufferlen
= strlen($data);
20850 if ($this->diskcache
) {
20851 if (!isset($this->buffer
) OR TCPDF_STATIC
::empty_string($this->buffer
)) {
20852 $this->buffer
= TCPDF_STATIC
::getObjFilename('buf');
20854 $this->writeDiskCache($this->buffer
, $data, false, false);
20856 $this->buffer
= $data;
20861 * Get buffer content.
20862 * @return string buffer content
20864 * @since 4.5.000 (2009-01-02)
20866 protected function getBuffer() {
20867 if ($this->diskcache
) {
20868 return $this->readDiskCache($this->buffer
, false);
20870 return $this->buffer
;
20875 * Set page buffer content.
20876 * @param $page (int) page number
20877 * @param $data (string) page data
20878 * @param $append (boolean) if true append data, false replace.
20880 * @since 4.5.000 (2008-12-31)
20882 protected function setPageBuffer($page, $data, $append=false) {
20883 if ($this->diskcache
) {
20884 if (!isset($this->pages
[$page])) {
20885 $this->pages
[$page] = TCPDF_STATIC
::getObjFilename('page');
20887 $this->writeDiskCache($this->pages
[$page], $data, $append, false);
20890 $this->pages
[$page] .= $data;
20892 $this->pages
[$page] = $data;
20895 if ($append AND isset($this->pagelen
[$page])) {
20896 $this->pagelen
[$page] +
= strlen($data);
20898 $this->pagelen
[$page] = strlen($data);
20903 * Get page buffer content.
20904 * @param $page (int) page number
20905 * @return string page buffer content or false in case of error
20907 * @since 4.5.000 (2008-12-31)
20909 protected function getPageBuffer($page) {
20910 if ($this->diskcache
) {
20911 return $this->readDiskCache($this->pages
[$page], false);
20912 } elseif (isset($this->pages
[$page])) {
20913 return $this->pages
[$page];
20919 * Set image buffer content.
20920 * @param $image (string) image key
20921 * @param $data (array) image data
20922 * @return int image index number
20924 * @since 4.5.000 (2008-12-31)
20926 protected function setImageBuffer($image, $data) {
20927 if (($data['i'] = array_search($image, $this->imagekeys
)) === FALSE) {
20928 $this->imagekeys
[$this->numimages
] = $image;
20929 $data['i'] = $this->numimages
;
20930 ++
$this->numimages
;
20932 if ($this->diskcache
) {
20933 if (!isset($this->images
[$image])) {
20934 $this->images
[$image] = TCPDF_STATIC
::getObjFilename('img');
20936 $this->writeDiskCache($this->images
[$image], $data, false, true);
20938 $this->images
[$image] = $data;
20944 * Set image buffer content for a specified sub-key.
20945 * @param $image (string) image key
20946 * @param $key (string) image sub-key
20947 * @param $data (array) image data
20949 * @since 4.5.000 (2008-12-31)
20951 protected function setImageSubBuffer($image, $key, $data) {
20952 if (!isset($this->images
[$image])) {
20953 $this->setImageBuffer($image, array());
20955 if ($this->diskcache
) {
20956 $tmpimg = $this->getImageBuffer($image);
20957 $tmpimg[$key] = $data;
20958 $this->writeDiskCache($this->images
[$image], $tmpimg, false, true);
20960 $this->images
[$image][$key] = $data;
20965 * Get image buffer content.
20966 * @param $image (string) image key
20967 * @return string image buffer content or false in case of error
20969 * @since 4.5.000 (2008-12-31)
20971 protected function getImageBuffer($image) {
20972 if ($this->diskcache
AND isset($this->images
[$image])) {
20973 return $this->readDiskCache($this->images
[$image], true);
20974 } elseif (isset($this->images
[$image])) {
20975 return $this->images
[$image];
20981 * Set font buffer content.
20982 * @param $font (string) font key
20983 * @param $data (array) font data
20985 * @since 4.5.000 (2009-01-02)
20987 protected function setFontBuffer($font, $data) {
20988 if ($this->diskcache
) {
20989 if (!isset($this->fonts
[$font])) {
20990 $this->fonts
[$font] = TCPDF_STATIC
::getObjFilename('font');
20992 $this->writeDiskCache($this->fonts
[$font], $data, false, true);
20994 $this->fonts
[$font] = $data;
20996 if (!in_array($font, $this->fontkeys
)) {
20997 $this->fontkeys
[] = $font;
20998 // store object ID for current font
21000 $this->font_obj_ids
[$font] = $this->n
;
21001 $this->setFontSubBuffer($font, 'n', $this->n
);
21006 * Set font buffer content.
21007 * @param $font (string) font key
21008 * @param $key (string) font sub-key
21009 * @param $data (array) font data
21011 * @since 4.5.000 (2009-01-02)
21013 protected function setFontSubBuffer($font, $key, $data) {
21014 if (!isset($this->fonts
[$font])) {
21015 $this->setFontBuffer($font, array());
21017 if ($this->diskcache
) {
21018 $tmpfont = $this->getFontBuffer($font);
21019 $tmpfont[$key] = $data;
21020 $this->writeDiskCache($this->fonts
[$font], $tmpfont, false, true);
21022 $this->fonts
[$font][$key] = $data;
21027 * Get font buffer content.
21028 * @param $font (string) font key
21029 * @return string font buffer content or false in case of error
21031 * @since 4.5.000 (2009-01-02)
21033 protected function getFontBuffer($font) {
21034 if ($this->diskcache
AND isset($this->fonts
[$font])) {
21035 return $this->readDiskCache($this->fonts
[$font], true);
21036 } elseif (isset($this->fonts
[$font])) {
21037 return $this->fonts
[$font];
21043 * Move a page to a previous position.
21044 * @param $frompage (int) number of the source page
21045 * @param $topage (int) number of the destination page (must be less than $frompage)
21046 * @return true in case of success, false in case of error.
21048 * @since 4.5.000 (2009-01-02)
21050 public function movePage($frompage, $topage) {
21051 if (($frompage > $this->numpages
) OR ($frompage <= $topage)) {
21054 if ($frompage == $this->page
) {
21055 // close the page before moving it
21058 // move all page-related states
21059 $tmppage = $this->getPageBuffer($frompage);
21060 $tmppagedim = $this->pagedim
[$frompage];
21061 $tmppagelen = $this->pagelen
[$frompage];
21062 $tmpintmrk = $this->intmrk
[$frompage];
21063 $tmpbordermrk = $this->bordermrk
[$frompage];
21064 $tmpcntmrk = $this->cntmrk
[$frompage];
21065 $tmppageobjects = $this->pageobjects
[$frompage];
21066 if (isset($this->footerpos
[$frompage])) {
21067 $tmpfooterpos = $this->footerpos
[$frompage];
21069 if (isset($this->footerlen
[$frompage])) {
21070 $tmpfooterlen = $this->footerlen
[$frompage];
21072 if (isset($this->transfmrk
[$frompage])) {
21073 $tmptransfmrk = $this->transfmrk
[$frompage];
21075 if (isset($this->PageAnnots
[$frompage])) {
21076 $tmpannots = $this->PageAnnots
[$frompage];
21078 if (isset($this->newpagegroup
) AND !empty($this->newpagegroup
)) {
21079 for ($i = $frompage; $i > $topage; --$i) {
21080 if (isset($this->newpagegroup
[$i]) AND (($i +
$this->pagegroups
[$this->newpagegroup
[$i]]) > $frompage)) {
21081 --$this->pagegroups
[$this->newpagegroup
[$i]];
21085 for ($i = $topage; $i > 0; --$i) {
21086 if (isset($this->newpagegroup
[$i]) AND (($i +
$this->pagegroups
[$this->newpagegroup
[$i]]) > $topage)) {
21087 ++
$this->pagegroups
[$this->newpagegroup
[$i]];
21092 for ($i = $frompage; $i > $topage; --$i) {
21094 // shift pages down
21095 $this->setPageBuffer($i, $this->getPageBuffer($j));
21096 $this->pagedim
[$i] = $this->pagedim
[$j];
21097 $this->pagelen
[$i] = $this->pagelen
[$j];
21098 $this->intmrk
[$i] = $this->intmrk
[$j];
21099 $this->bordermrk
[$i] = $this->bordermrk
[$j];
21100 $this->cntmrk
[$i] = $this->cntmrk
[$j];
21101 $this->pageobjects
[$i] = $this->pageobjects
[$j];
21102 if (isset($this->footerpos
[$j])) {
21103 $this->footerpos
[$i] = $this->footerpos
[$j];
21104 } elseif (isset($this->footerpos
[$i])) {
21105 unset($this->footerpos
[$i]);
21107 if (isset($this->footerlen
[$j])) {
21108 $this->footerlen
[$i] = $this->footerlen
[$j];
21109 } elseif (isset($this->footerlen
[$i])) {
21110 unset($this->footerlen
[$i]);
21112 if (isset($this->transfmrk
[$j])) {
21113 $this->transfmrk
[$i] = $this->transfmrk
[$j];
21114 } elseif (isset($this->transfmrk
[$i])) {
21115 unset($this->transfmrk
[$i]);
21117 if (isset($this->PageAnnots
[$j])) {
21118 $this->PageAnnots
[$i] = $this->PageAnnots
[$j];
21119 } elseif (isset($this->PageAnnots
[$i])) {
21120 unset($this->PageAnnots
[$i]);
21122 if (isset($this->newpagegroup
[$j])) {
21123 $this->newpagegroup
[$i] = $this->newpagegroup
[$j];
21124 unset($this->newpagegroup
[$j]);
21126 if ($this->currpagegroup
== $j) {
21127 $this->currpagegroup
= $i;
21130 $this->setPageBuffer($topage, $tmppage);
21131 $this->pagedim
[$topage] = $tmppagedim;
21132 $this->pagelen
[$topage] = $tmppagelen;
21133 $this->intmrk
[$topage] = $tmpintmrk;
21134 $this->bordermrk
[$topage] = $tmpbordermrk;
21135 $this->cntmrk
[$topage] = $tmpcntmrk;
21136 $this->pageobjects
[$topage] = $tmppageobjects;
21137 if (isset($tmpfooterpos)) {
21138 $this->footerpos
[$topage] = $tmpfooterpos;
21139 } elseif (isset($this->footerpos
[$topage])) {
21140 unset($this->footerpos
[$topage]);
21142 if (isset($tmpfooterlen)) {
21143 $this->footerlen
[$topage] = $tmpfooterlen;
21144 } elseif (isset($this->footerlen
[$topage])) {
21145 unset($this->footerlen
[$topage]);
21147 if (isset($tmptransfmrk)) {
21148 $this->transfmrk
[$topage] = $tmptransfmrk;
21149 } elseif (isset($this->transfmrk
[$topage])) {
21150 unset($this->transfmrk
[$topage]);
21152 if (isset($tmpannots)) {
21153 $this->PageAnnots
[$topage] = $tmpannots;
21154 } elseif (isset($this->PageAnnots
[$topage])) {
21155 unset($this->PageAnnots
[$topage]);
21158 $tmpoutlines = $this->outlines
;
21159 foreach ($tmpoutlines as $key => $outline) {
21160 if (!$outline['f']) {
21161 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
21162 $this->outlines
[$key]['p'] = ($outline['p'] +
1);
21163 } elseif ($outline['p'] == $frompage) {
21164 $this->outlines
[$key]['p'] = $topage;
21169 $tmpdests = $this->dests
;
21170 foreach ($tmpdests as $key => $dest) {
21172 if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
21173 $this->dests
[$key]['p'] = ($dest['p'] +
1);
21174 } elseif ($dest['p'] == $frompage) {
21175 $this->dests
[$key]['p'] = $topage;
21180 $tmplinks = $this->links
;
21181 foreach ($tmplinks as $key => $link) {
21183 if (($link['p'] >= $topage) AND ($link['p'] < $frompage)) {
21184 $this->links
[$key]['p'] = ($link['p'] +
1);
21185 } elseif ($link['p'] == $frompage) {
21186 $this->links
[$key]['p'] = $topage;
21190 // adjust javascript
21191 $jfrompage = $frompage;
21192 $jtopage = $topage;
21193 if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript
, $pamatch) > 0) {
21194 foreach($pamatch[0] as $pk => $pmatch) {
21195 $pagenum = intval($pamatch[3][$pk]) +
1;
21196 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
21197 $newpage = ($pagenum +
1);
21198 } elseif ($pagenum == $jfrompage) {
21199 $newpage = $jtopage;
21201 $newpage = $pagenum;
21204 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21205 $this->javascript
= str_replace($pmatch, $newjs, $this->javascript
);
21209 // return to last page
21210 $this->lastPage(true);
21215 * Remove the specified page.
21216 * @param $page (int) page to remove
21217 * @return true in case of success, false in case of error.
21219 * @since 4.6.004 (2009-04-23)
21221 public function deletePage($page) {
21222 if (($page < 1) OR ($page > $this->numpages
)) {
21225 // delete current page
21226 unset($this->pages
[$page]);
21227 unset($this->pagedim
[$page]);
21228 unset($this->pagelen
[$page]);
21229 unset($this->intmrk
[$page]);
21230 unset($this->bordermrk
[$page]);
21231 unset($this->cntmrk
[$page]);
21232 foreach ($this->pageobjects
[$page] as $oid) {
21233 if (isset($this->offsets
[$oid])){
21234 unset($this->offsets
[$oid]);
21237 unset($this->pageobjects
[$page]);
21238 if (isset($this->footerpos
[$page])) {
21239 unset($this->footerpos
[$page]);
21241 if (isset($this->footerlen
[$page])) {
21242 unset($this->footerlen
[$page]);
21244 if (isset($this->transfmrk
[$page])) {
21245 unset($this->transfmrk
[$page]);
21247 if (isset($this->PageAnnots
[$page])) {
21248 unset($this->PageAnnots
[$page]);
21250 if (isset($this->newpagegroup
) AND !empty($this->newpagegroup
)) {
21251 for ($i = $page; $i > 0; --$i) {
21252 if (isset($this->newpagegroup
[$i]) AND (($i +
$this->pagegroups
[$this->newpagegroup
[$i]]) > $page)) {
21253 --$this->pagegroups
[$this->newpagegroup
[$i]];
21258 if (isset($this->pageopen
[$page])) {
21259 unset($this->pageopen
[$page]);
21261 if ($page < $this->numpages
) {
21262 // update remaining pages
21263 for ($i = $page; $i < $this->numpages
; ++
$i) {
21266 $this->setPageBuffer($i, $this->getPageBuffer($j));
21267 $this->pagedim
[$i] = $this->pagedim
[$j];
21268 $this->pagelen
[$i] = $this->pagelen
[$j];
21269 $this->intmrk
[$i] = $this->intmrk
[$j];
21270 $this->bordermrk
[$i] = $this->bordermrk
[$j];
21271 $this->cntmrk
[$i] = $this->cntmrk
[$j];
21272 $this->pageobjects
[$i] = $this->pageobjects
[$j];
21273 if (isset($this->footerpos
[$j])) {
21274 $this->footerpos
[$i] = $this->footerpos
[$j];
21275 } elseif (isset($this->footerpos
[$i])) {
21276 unset($this->footerpos
[$i]);
21278 if (isset($this->footerlen
[$j])) {
21279 $this->footerlen
[$i] = $this->footerlen
[$j];
21280 } elseif (isset($this->footerlen
[$i])) {
21281 unset($this->footerlen
[$i]);
21283 if (isset($this->transfmrk
[$j])) {
21284 $this->transfmrk
[$i] = $this->transfmrk
[$j];
21285 } elseif (isset($this->transfmrk
[$i])) {
21286 unset($this->transfmrk
[$i]);
21288 if (isset($this->PageAnnots
[$j])) {
21289 $this->PageAnnots
[$i] = $this->PageAnnots
[$j];
21290 } elseif (isset($this->PageAnnots
[$i])) {
21291 unset($this->PageAnnots
[$i]);
21293 if (isset($this->newpagegroup
[$j])) {
21294 $this->newpagegroup
[$i] = $this->newpagegroup
[$j];
21295 unset($this->newpagegroup
[$j]);
21297 if ($this->currpagegroup
== $j) {
21298 $this->currpagegroup
= $i;
21300 if (isset($this->pageopen
[$j])) {
21301 $this->pageopen
[$i] = $this->pageopen
[$j];
21302 } elseif (isset($this->pageopen
[$i])) {
21303 unset($this->pageopen
[$i]);
21306 // remove last page
21307 unset($this->pages
[$this->numpages
]);
21308 unset($this->pagedim
[$this->numpages
]);
21309 unset($this->pagelen
[$this->numpages
]);
21310 unset($this->intmrk
[$this->numpages
]);
21311 unset($this->bordermrk
[$this->numpages
]);
21312 unset($this->cntmrk
[$this->numpages
]);
21313 foreach ($this->pageobjects
[$this->numpages
] as $oid) {
21314 if (isset($this->offsets
[$oid])){
21315 unset($this->offsets
[$oid]);
21318 unset($this->pageobjects
[$this->numpages
]);
21319 if (isset($this->footerpos
[$this->numpages
])) {
21320 unset($this->footerpos
[$this->numpages
]);
21322 if (isset($this->footerlen
[$this->numpages
])) {
21323 unset($this->footerlen
[$this->numpages
]);
21325 if (isset($this->transfmrk
[$this->numpages
])) {
21326 unset($this->transfmrk
[$this->numpages
]);
21328 if (isset($this->PageAnnots
[$this->numpages
])) {
21329 unset($this->PageAnnots
[$this->numpages
]);
21331 if (isset($this->newpagegroup
[$this->numpages
])) {
21332 unset($this->newpagegroup
[$this->numpages
]);
21334 if ($this->currpagegroup
== $this->numpages
) {
21335 $this->currpagegroup
= ($this->numpages
- 1);
21337 if (isset($this->pagegroups
[$this->numpages
])) {
21338 unset($this->pagegroups
[$this->numpages
]);
21340 if (isset($this->pageopen
[$this->numpages
])) {
21341 unset($this->pageopen
[$this->numpages
]);
21345 $this->page
= $this->numpages
;
21347 $tmpoutlines = $this->outlines
;
21348 foreach ($tmpoutlines as $key => $outline) {
21349 if (!$outline['f']) {
21350 if ($outline['p'] > $page) {
21351 $this->outlines
[$key]['p'] = $outline['p'] - 1;
21352 } elseif ($outline['p'] == $page) {
21353 unset($this->outlines
[$key]);
21358 $tmpdests = $this->dests
;
21359 foreach ($tmpdests as $key => $dest) {
21361 if ($dest['p'] > $page) {
21362 $this->dests
[$key]['p'] = $dest['p'] - 1;
21363 } elseif ($dest['p'] == $page) {
21364 unset($this->dests
[$key]);
21369 $tmplinks = $this->links
;
21370 foreach ($tmplinks as $key => $link) {
21372 if ($link['p'] > $page) {
21373 $this->links
[$key]['p'] = $link['p'] - 1;
21374 } elseif ($link['p'] == $page) {
21375 unset($this->links
[$key]);
21379 // adjust javascript
21381 if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript
, $pamatch) > 0) {
21382 foreach($pamatch[0] as $pk => $pmatch) {
21383 $pagenum = intval($pamatch[3][$pk]) +
1;
21384 if ($pagenum >= $jpage) {
21385 $newpage = ($pagenum - 1);
21386 } elseif ($pagenum == $jpage) {
21389 $newpage = $pagenum;
21392 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21393 $this->javascript
= str_replace($pmatch, $newjs, $this->javascript
);
21397 // return to last page
21398 if ($this->numpages
> 0) {
21399 $this->lastPage(true);
21405 * Clone the specified page to a new page.
21406 * @param $page (int) number of page to copy (0 = current page)
21407 * @return true in case of success, false in case of error.
21409 * @since 4.9.015 (2010-04-20)
21411 public function copyPage($page=0) {
21414 $page = $this->page
;
21416 if (($page < 1) OR ($page > $this->numpages
)) {
21419 // close the last page
21421 // copy all page-related states
21423 $this->page
= $this->numpages
;
21424 $this->setPageBuffer($this->page
, $this->getPageBuffer($page));
21425 $this->pagedim
[$this->page
] = $this->pagedim
[$page];
21426 $this->pagelen
[$this->page
] = $this->pagelen
[$page];
21427 $this->intmrk
[$this->page
] = $this->intmrk
[$page];
21428 $this->bordermrk
[$this->page
] = $this->bordermrk
[$page];
21429 $this->cntmrk
[$this->page
] = $this->cntmrk
[$page];
21430 $this->pageobjects
[$this->page
] = $this->pageobjects
[$page];
21431 $this->pageopen
[$this->page
] = false;
21432 if (isset($this->footerpos
[$page])) {
21433 $this->footerpos
[$this->page
] = $this->footerpos
[$page];
21435 if (isset($this->footerlen
[$page])) {
21436 $this->footerlen
[$this->page
] = $this->footerlen
[$page];
21438 if (isset($this->transfmrk
[$page])) {
21439 $this->transfmrk
[$this->page
] = $this->transfmrk
[$page];
21441 if (isset($this->PageAnnots
[$page])) {
21442 $this->PageAnnots
[$this->page
] = $this->PageAnnots
[$page];
21444 if (isset($this->newpagegroup
[$page])) {
21445 // start a new group
21446 $this->newpagegroup
[$this->page
] = sizeof($this->newpagegroup
) +
1;
21447 $this->currpagegroup
= $this->newpagegroup
[$this->page
];
21448 $this->pagegroups
[$this->currpagegroup
] = 1;
21449 } elseif (isset($this->currpagegroup
) AND ($this->currpagegroup
> 0)) {
21450 ++
$this->pagegroups
[$this->currpagegroup
];
21453 $tmpoutlines = $this->outlines
;
21454 foreach ($tmpoutlines as $key => $outline) {
21455 if ($outline['p'] == $page) {
21456 $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']);
21460 $tmplinks = $this->links
;
21461 foreach ($tmplinks as $key => $link) {
21462 if ($link['p'] == $page) {
21463 $this->links
[] = array('p' => $this->page
, 'y' => $link['y'], 'f' => $link['f']);
21466 // return to last page
21467 $this->lastPage(true);
21472 * Output a Table of Content Index (TOC).
21473 * This method must be called after all Bookmarks were set.
21474 * Before calling this method you have to open the page using the addTOCPage() method.
21475 * After calling this method you have to call endTOCPage() to close the TOC page.
21476 * You can override this method to achieve different styles.
21477 * @param $page (int) page number where this TOC should be inserted (leave empty for current page).
21478 * @param $numbersfont (string) set the font for page numbers (please use monospaced font for better alignment).
21479 * @param $filler (string) string used to fill the space between text and page number.
21480 * @param $toc_name (string) name to use for TOC bookmark.
21481 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
21482 * @param $color (array) RGB color array for bookmark title (values from 0 to 255).
21484 * @author Nicola Asuni
21485 * @since 4.5.000 (2009-01-02)
21486 * @see addTOCPage(), endTOCPage(), addHTMLTOC()
21488 public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
21489 $fontsize = $this->FontSizePt
;
21490 $fontfamily = $this->FontFamily
;
21491 $fontstyle = $this->FontStyle
;
21492 $w = $this->w
- $this->lMargin
- $this->rMargin
;
21493 $spacer = $this->GetStringWidth(chr(32)) * 4;
21494 $lmargin = $this->lMargin
;
21495 $rmargin = $this->rMargin
;
21496 $x_start = $this->GetX();
21497 $page_first = $this->page
;
21498 $current_page = $this->page
;
21499 $page_fill_start = false;
21500 $page_fill_end = false;
21501 $current_column = $this->current_column
;
21502 if (TCPDF_STATIC
::empty_string($numbersfont)) {
21503 $numbersfont = $this->default_monospaced_font
;
21505 if (TCPDF_STATIC
::empty_string($filler)) {
21508 if (TCPDF_STATIC
::empty_string($page)) {
21516 $this->SetFont($numbersfont, $fontstyle, $fontsize);
21517 $numwidth = $this->GetStringWidth('00000');
21518 $maxpage = 0; //used for pages on attached documents
21519 foreach ($this->outlines
as $key => $outline) {
21520 // check for extra pages (used for attachments)
21521 if (($this->page
> $page_first) AND ($outline['p'] >= $this->numpages
)) {
21522 $outline['p'] +
= ($this->page
- $page_first);
21531 if ($outline['l'] == 0) {
21532 $this->SetFont($fontfamily, $outline['s'].'B', $fontsize);
21534 $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']);
21536 $this->SetTextColorArray($outline['c']);
21537 // check for page break
21538 $this->checkPageBreak(2 * $this->getCellHeight($this->FontSize
));
21539 // set margins and X position
21540 if (($this->page
== $current_page) AND ($this->current_column
== $current_column)) {
21541 $this->lMargin
= $lmargin;
21542 $this->rMargin
= $rmargin;
21544 if ($this->current_column
!= $current_column) {
21546 $x_start = $this->w
- $this->columns
[$this->current_column
]['x'];
21548 $x_start = $this->columns
[$this->current_column
]['x'];
21551 $lmargin = $this->lMargin
;
21552 $rmargin = $this->rMargin
;
21553 $current_page = $this->page
;
21554 $current_column = $this->current_column
;
21556 $this->SetX($x_start);
21557 $indent = ($spacer * $outline['l']);
21559 $this->x
-= $indent;
21560 $this->rMargin
= $this->w
- $this->x
;
21562 $this->x +
= $indent;
21563 $this->lMargin
= $this->x
;
21565 $link = $this->AddLink();
21566 $this->SetLink($link, $outline['y'], $outline['p']);
21569 $txt = ' '.$outline['t'];
21571 $txt = $outline['t'].' ';
21573 $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
21575 $tw = $this->x
- $this->lMargin
;
21577 $tw = $this->w
- $this->rMargin
- $this->x
;
21579 $this->SetFont($numbersfont, $fontstyle, $fontsize);
21580 if (TCPDF_STATIC
::empty_string($page)) {
21581 $pagenum = $outline['p'];
21583 // placemark to be replaced with the correct number
21584 $pagenum = '{#'.($outline['p']).'}';
21585 if ($this->isUnicodeFont()) {
21586 $pagenum = '{'.$pagenum.'}';
21588 $maxpage = max($maxpage, $outline['p']);
21590 $fw = ($tw - $this->GetStringWidth($pagenum.$filler));
21591 $wfiller = $this->GetStringWidth($filler);
21592 if ($wfiller > 0) {
21593 $numfills = floor($fw / $wfiller);
21597 if ($numfills > 0) {
21598 $rowfill = str_repeat($filler, $numfills);
21603 $pagenum = $pagenum.$gap.$rowfill;
21605 $pagenum = $rowfill.$gap.$pagenum;
21607 // write the number
21608 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
21610 $page_last = $this->getPage();
21611 $numpages = ($page_last - $page_first +
1);
21612 // account for booklet mode
21613 if ($this->booklet
) {
21614 // check if a blank page is required before TOC
21615 $page_fill_start = ((($page_first %
2) == 0) XOR (($page %
2) == 0));
21616 $page_fill_end = (!((($numpages %
2) == 0) XOR ($page_fill_start)));
21617 if ($page_fill_start) {
21618 // add a page at the end (to be moved before TOC)
21623 if ($page_fill_end) {
21624 // add a page at the end
21630 $maxpage = max($maxpage, $page_last);
21631 if (!TCPDF_STATIC
::empty_string($page)) {
21632 for ($p = $page_first; $p <= $page_last; ++
$p) {
21634 $temppage = $this->getPageBuffer($p);
21635 for ($n = 1; $n <= $maxpage; ++
$n) {
21636 // update page numbers
21638 // get page number aliases
21639 $pnalias = $this->getInternalPageNumberAliases($a);
21640 // calculate replacement number
21641 if (($n >= $page) AND ($n <= $this->numpages
)) {
21642 $np = $n +
$numpages;
21646 $na = TCPDF_STATIC
::formatTOCPageNumber(($this->starting_page_number +
$np - 1));
21647 $nu = TCPDF_FONTS
::UTF8ToUTF16BE($na, false, $this->isunicode
, $this->CurrentFont
);
21648 // replace aliases with numbers
21649 foreach ($pnalias['u'] as $u) {
21650 $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
21652 $nr = $nu.TCPDF_FONTS
::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode
, $this->CurrentFont
);
21654 $nr = TCPDF_FONTS
::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode
, $this->CurrentFont
).$nu;
21656 $temppage = str_replace($u, $nr, $temppage);
21658 foreach ($pnalias['a'] as $a) {
21659 $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
21661 $nr = $na.' '.$sfill;
21663 $nr = $sfill.' '.$na;
21665 $temppage = str_replace($a, $nr, $temppage);
21669 $this->setPageBuffer($p, $temppage);
21672 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21673 if ($page_fill_start) {
21674 $this->movePage($page_last, $page_first);
21676 for ($i = 0; $i < $numpages; ++
$i) {
21677 $this->movePage($page_last, $page);
21683 * Output a Table Of Content Index (TOC) using HTML templates.
21684 * This method must be called after all Bookmarks were set.
21685 * Before calling this method you have to open the page using the addTOCPage() method.
21686 * After calling this method you have to call endTOCPage() to close the TOC page.
21687 * @param $page (int) page number where this TOC should be inserted (leave empty for current page).
21688 * @param $toc_name (string) name to use for TOC bookmark.
21689 * @param $templates (array) array of html templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number.
21690 * @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)
21691 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
21692 * @param $color (array) RGB color array for title (values from 0 to 255).
21694 * @author Nicola Asuni
21695 * @since 5.0.001 (2010-05-06)
21696 * @see addTOCPage(), endTOCPage(), addTOC()
21698 public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
21700 $prev_htmlLinkColorArray = $this->htmlLinkColorArray
;
21701 $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle
;
21702 // set new style for link
21703 $this->htmlLinkColorArray
= array();
21704 $this->htmlLinkFontStyle
= '';
21705 $page_first = $this->getPage();
21706 $page_fill_start = false;
21707 $page_fill_end = false;
21708 // get the font type used for numbers in each template
21709 $current_font = $this->FontFamily
;
21710 foreach ($templates as $level => $html) {
21711 $dom = $this->getHtmlDomArray($html);
21712 foreach ($dom as $key => $value) {
21713 if ($value['value'] == '#TOC_PAGE_NUMBER#') {
21714 $this->SetFont($dom[($key - 1)]['fontname']);
21715 $templates['F'.$level] = $this->isUnicodeFont();
21719 $this->SetFont($current_font);
21720 $maxpage = 0; //used for pages on attached documents
21721 foreach ($this->outlines
as $key => $outline) {
21722 // get HTML template
21723 $row = $templates[$outline['l']];
21724 if (TCPDF_STATIC
::empty_string($page)) {
21725 $pagenum = $outline['p'];
21727 // placemark to be replaced with the correct number
21728 $pagenum = '{#'.($outline['p']).'}';
21729 if ($templates['F'.$outline['l']]) {
21730 $pagenum = '{'.$pagenum.'}';
21732 $maxpage = max($maxpage, $outline['p']);
21734 // replace templates with current values
21735 $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
21736 $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
21737 // add link to page
21738 $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
21739 // write bookmark entry
21740 $this->writeHTML($row, false, false, true, false, '');
21742 // restore link styles
21743 $this->htmlLinkColorArray
= $prev_htmlLinkColorArray;
21744 $this->htmlLinkFontStyle
= $prev_htmlLinkFontStyle;
21745 // move TOC page and replace numbers
21746 $page_last = $this->getPage();
21747 $numpages = ($page_last - $page_first +
1);
21748 // account for booklet mode
21749 if ($this->booklet
) {
21750 // check if a blank page is required before TOC
21751 $page_fill_start = ((($page_first %
2) == 0) XOR (($page %
2) == 0));
21752 $page_fill_end = (!((($numpages %
2) == 0) XOR ($page_fill_start)));
21753 if ($page_fill_start) {
21754 // add a page at the end (to be moved before TOC)
21759 if ($page_fill_end) {
21760 // add a page at the end
21766 $maxpage = max($maxpage, $page_last);
21767 if (!TCPDF_STATIC
::empty_string($page)) {
21768 for ($p = $page_first; $p <= $page_last; ++
$p) {
21770 $temppage = $this->getPageBuffer($p);
21771 for ($n = 1; $n <= $maxpage; ++
$n) {
21772 // update page numbers
21774 // get page number aliases
21775 $pnalias = $this->getInternalPageNumberAliases($a);
21776 // calculate replacement number
21778 $np = $n +
$numpages;
21782 $na = TCPDF_STATIC
::formatTOCPageNumber(($this->starting_page_number +
$np - 1));
21783 $nu = TCPDF_FONTS
::UTF8ToUTF16BE($na, false, $this->isunicode
, $this->CurrentFont
);
21784 // replace aliases with numbers
21785 foreach ($pnalias['u'] as $u) {
21786 if ($correct_align) {
21787 $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
21789 $nr = $nu.TCPDF_FONTS
::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode
, $this->CurrentFont
);
21791 $nr = TCPDF_FONTS
::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode
, $this->CurrentFont
).$nu;
21796 $temppage = str_replace($u, $nr, $temppage);
21798 foreach ($pnalias['a'] as $a) {
21799 if ($correct_align) {
21800 $sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
21802 $nr = $na.' '.$sfill;
21804 $nr = $sfill.' '.$na;
21809 $temppage = str_replace($a, $nr, $temppage);
21813 $this->setPageBuffer($p, $temppage);
21816 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21817 if ($page_fill_start) {
21818 $this->movePage($page_last, $page_first);
21820 for ($i = 0; $i < $numpages; ++
$i) {
21821 $this->movePage($page_last, $page);
21827 * Stores a copy of the current TCPDF object used for undo operation.
21829 * @since 4.5.029 (2009-03-19)
21831 public function startTransaction() {
21832 if (isset($this->objcopy
)) {
21833 // remove previous copy
21834 $this->commitTransaction();
21836 // record current page number and Y position
21837 $this->start_transaction_page
= $this->page
;
21838 $this->start_transaction_y
= $this->y
;
21839 // clone current object
21840 $this->objcopy
= TCPDF_STATIC
::objclone($this);
21844 * Delete the copy of the current TCPDF object used for undo operation.
21846 * @since 4.5.029 (2009-03-19)
21848 public function commitTransaction() {
21849 if (isset($this->objcopy
)) {
21850 $this->objcopy
->_destroy(true, true);
21851 unset($this->objcopy
);
21856 * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction().
21857 * @param $self (boolean) if true restores current class object to previous state without the need of reassignment via the returned value.
21858 * @return TCPDF object.
21860 * @since 4.5.029 (2009-03-19)
21862 public function rollbackTransaction($self=false) {
21863 if (isset($this->objcopy
)) {
21864 if (isset($this->objcopy
->diskcache
) AND $this->objcopy
->diskcache
) {
21865 // truncate files to previous values
21866 foreach ($this->objcopy
->cache_file_length
as $file => $length) {
21867 $file = substr($file, 1);
21868 $handle = fopen($file, 'r+');
21869 ftruncate($handle, $length);
21872 $this->_destroy(true, true);
21874 $objvars = get_object_vars($this->objcopy
);
21875 foreach ($objvars as $key => $value) {
21876 $this->$key = $value;
21879 return $this->objcopy
;
21884 // --- MULTI COLUMNS METHODS -----------------------
21887 * Set multiple columns of the same size
21888 * @param $numcols (int) number of columns (set to zero to disable columns mode)
21889 * @param $width (int) column width
21890 * @param $y (int) column starting Y position (leave empty for current Y position)
21892 * @since 4.9.001 (2010-03-28)
21894 public function setEqualColumns($numcols=0, $width=0, $y='') {
21895 $this->columns
= array();
21896 if ($numcols < 2) {
21898 $this->columns
= array();
21900 // maximum column width
21901 $maxwidth = ($this->w
- $this->original_lMargin
- $this->original_rMargin
) / $numcols;
21902 if (($width == 0) OR ($width > $maxwidth)) {
21903 $width = $maxwidth;
21905 if (TCPDF_STATIC
::empty_string($y)) {
21908 // space between columns
21909 $space = (($this->w
- $this->original_lMargin
- $this->original_rMargin
- ($numcols * $width)) / ($numcols - 1));
21910 // fill the columns array (with, space, starting Y position)
21911 for ($i = 0; $i < $numcols; ++
$i) {
21912 $this->columns
[$i] = array('w' => $width, 's' => $space, 'y' => $y);
21915 $this->num_columns
= $numcols;
21916 $this->current_column
= 0;
21917 $this->column_start_page
= $this->page
;
21918 $this->selectColumn(0);
21922 * Remove columns and reset page margins.
21924 * @since 5.9.072 (2011-04-26)
21926 public function resetColumns() {
21927 $this->lMargin
= $this->original_lMargin
;
21928 $this->rMargin
= $this->original_rMargin
;
21929 $this->setEqualColumns();
21933 * Set columns array.
21934 * Each column is represented by an array of arrays with the following keys: (w = width, s = space between columns, y = column top position).
21935 * @param $columns (array)
21937 * @since 4.9.001 (2010-03-28)
21939 public function setColumnsArray($columns) {
21940 $this->columns
= $columns;
21941 $this->num_columns
= count($columns);
21942 $this->current_column
= 0;
21943 $this->column_start_page
= $this->page
;
21944 $this->selectColumn(0);
21948 * Set position at a given column
21949 * @param $col (int) column number (from 0 to getNumberOfColumns()-1); empty string = current column.
21951 * @since 4.9.001 (2010-03-28)
21953 public function selectColumn($col='') {
21954 if (is_string($col)) {
21955 $col = $this->current_column
;
21956 } elseif ($col >= $this->num_columns
) {
21959 $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
21960 $enable_thead = false;
21961 if ($this->num_columns
> 1) {
21962 if ($col != $this->current_column
) {
21963 // move Y pointer at the top of the column
21964 if ($this->column_start_page
== $this->page
) {
21965 $this->y
= $this->columns
[$col]['y'];
21967 $this->y
= $this->tMargin
;
21969 // Avoid to write table headers more than once
21970 if (($this->page
> $this->maxselcol
['page']) OR (($this->page
== $this->maxselcol
['page']) AND ($col > $this->maxselcol
['column']))) {
21971 $enable_thead = true;
21972 $this->maxselcol
['page'] = $this->page
;
21973 $this->maxselcol
['column'] = $col;
21976 $xshift = $this->colxshift
;
21977 // set X position of the current column by case
21978 $listindent = ($this->listindentlevel
* $this->listindent
);
21979 // calculate column X position
21981 for ($i = 0; $i < $col; ++
$i) {
21982 $colpos +
= ($this->columns
[$i]['w'] +
$this->columns
[$i]['s']);
21985 $x = $this->w
- $this->original_rMargin
- $colpos;
21986 $this->rMargin
= ($this->w
- $x +
$listindent);
21987 $this->lMargin
= ($x - $this->columns
[$col]['w']);
21988 $this->x
= $x - $listindent;
21990 $x = $this->original_lMargin +
$colpos;
21991 $this->lMargin
= ($x +
$listindent);
21992 $this->rMargin
= ($this->w
- $x - $this->columns
[$col]['w']);
21993 $this->x
= $x +
$listindent;
21995 $this->columns
[$col]['x'] = $x;
21997 $this->current_column
= $col;
21998 // fix for HTML mode
21999 $this->newline
= true;
22000 // print HTML table header (if any)
22001 if ((!TCPDF_STATIC
::empty_string($this->thead
)) AND (!$this->inthead
)) {
22002 if ($enable_thead) {
22003 // print table header
22004 $this->writeHTML($this->thead
, false, false, false, false, '');
22005 $this->y +
= $xshift['s']['V'];
22006 // store end of header position
22007 if (!isset($this->columns
[$col]['th'])) {
22008 $this->columns
[$col]['th'] = array();
22010 $this->columns
[$col]['th']['\''.$this->page
.'\''] = $this->y
;
22012 } elseif (isset($this->columns
[$col]['th']['\''.$this->page
.'\''])) {
22013 $this->y
= $this->columns
[$col]['th']['\''.$this->page
.'\''];
22016 // account for an html table cell over multiple columns
22018 $this->rMargin +
= $xshift['x'];
22019 $this->x
-= ($xshift['x'] +
$xshift['p']['R']);
22021 $this->lMargin +
= $xshift['x'];
22022 $this->x +
= $xshift['x'] +
$xshift['p']['L'];
22027 * Return the current column number
22028 * @return int current column number
22030 * @since 5.5.011 (2010-07-08)
22032 public function getColumn() {
22033 return $this->current_column
;
22037 * Return the current number of columns.
22038 * @return int number of columns
22040 * @since 5.8.018 (2010-08-25)
22042 public function getNumberOfColumns() {
22043 return $this->num_columns
;
22047 * Set Text rendering mode.
22048 * @param $stroke (int) outline size in user units (0 = disable).
22049 * @param $fill (boolean) if true fills the text (default).
22050 * @param $clip (boolean) if true activate clipping mode
22052 * @since 4.9.008 (2009-04-02)
22054 public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
22055 // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
22056 // convert text rendering parameters
22060 if ($fill === true) {
22062 if ($clip === true) {
22063 // Fill, then stroke text and add to path for clipping
22064 $textrendermode = 6;
22066 // Fill, then stroke text
22067 $textrendermode = 2;
22069 $textstrokewidth = $stroke;
22071 if ($clip === true) {
22072 // Fill text and add to path for clipping
22073 $textrendermode = 4;
22076 $textrendermode = 0;
22081 if ($clip === true) {
22082 // Stroke text and add to path for clipping
22083 $textrendermode = 5;
22086 $textrendermode = 1;
22088 $textstrokewidth = $stroke;
22090 if ($clip === true) {
22091 // Add text to path for clipping
22092 $textrendermode = 7;
22094 // Neither fill nor stroke text (invisible)
22095 $textrendermode = 3;
22099 $this->textrendermode
= $textrendermode;
22100 $this->textstrokewidth
= $stroke;
22104 * Set parameters for drop shadow effect for text.
22105 * @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.
22106 * @since 5.9.174 (2012-07-25)
22109 public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) {
22110 if (isset($params['enabled'])) {
22111 $this->txtshadow
['enabled'] = $params['enabled']?true:false;
22113 $this->txtshadow
['enabled'] = false;
22115 if (isset($params['depth_w'])) {
22116 $this->txtshadow
['depth_w'] = floatval($params['depth_w']);
22118 $this->txtshadow
['depth_w'] = 0;
22120 if (isset($params['depth_h'])) {
22121 $this->txtshadow
['depth_h'] = floatval($params['depth_h']);
22123 $this->txtshadow
['depth_h'] = 0;
22125 if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) {
22126 $this->txtshadow
['color'] = $params['color'];
22128 $this->txtshadow
['color'] = $this->strokecolor
;
22130 if (isset($params['opacity'])) {
22131 $this->txtshadow
['opacity'] = min(1, max(0, floatval($params['opacity'])));
22133 $this->txtshadow
['opacity'] = 1;
22135 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'))) {
22136 $this->txtshadow
['blend_mode'] = $params['blend_mode'];
22138 $this->txtshadow
['blend_mode'] = 'Normal';
22140 if ((($this->txtshadow
['depth_w'] == 0) AND ($this->txtshadow
['depth_h'] == 0)) OR ($this->txtshadow
['opacity'] == 0)) {
22141 $this->txtshadow
['enabled'] = false;
22146 * Return the text shadow parameters array.
22147 * @return Array of parameters.
22148 * @since 5.9.174 (2012-07-25)
22151 public function getTextShadow() {
22152 return $this->txtshadow
;
22156 * Returns an array of chars containing soft hyphens.
22157 * @param $word (array) array of chars
22158 * @param $patterns (array) Array of hypenation patterns.
22159 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm.
22160 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
22161 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
22162 * @param $charmin (int) Minimum word length to apply the hyphenation algoritm.
22163 * @param $charmax (int) Maximum length of broken piece of word.
22164 * @return array text with soft hyphens
22165 * @author Nicola Asuni
22166 * @since 4.9.012 (2010-04-12)
22169 protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22170 $hyphenword = array(); // hyphens positions
22171 $numchars = count($word);
22172 if ($numchars <= $charmin) {
22175 $word_string = TCPDF_FONTS
::UTF8ArrSubString($word, '', '', $this->isunicode
);
22176 // some words will be returned as-is
22177 $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})(\]?)$/';
22178 if (preg_match($pattern, $word_string) > 0) {
22182 $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})(\]?)$/';
22183 if (preg_match($pattern, $word_string) > 0) {
22187 if (isset($dictionary[$word_string])) {
22188 return TCPDF_FONTS
::UTF8StringToArray($dictionary[$word_string], $this->isunicode
, $this->CurrentFont
);
22190 // surround word with '_' characters
22191 $tmpword = array_merge(array(46), $word, array(46));
22192 $tmpnumchars = $numchars +
2;
22193 $maxpos = $tmpnumchars - $charmin;
22194 for ($pos = 0; $pos < $maxpos; ++
$pos) {
22195 $imax = min(($tmpnumchars - $pos), $charmax);
22196 for ($i = $charmin; $i <= $imax; ++
$i) {
22197 $subword = strtolower(TCPDF_FONTS
::UTF8ArrSubString($tmpword, $pos, ($pos +
$i), $this->isunicode
));
22198 if (isset($patterns[$subword])) {
22199 $pattern = TCPDF_FONTS
::UTF8StringToArray($patterns[$subword], $this->isunicode
, $this->CurrentFont
);
22200 $pattern_length = count($pattern);
22202 for ($j = 0; $j < $pattern_length; ++
$j) {
22203 // check if $pattern[$j] is a number = hyphenation level (only numbers from 1 to 5 are valid)
22204 if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
22208 $zero = $pos +
$j - $digits;
22210 // get hyphenation level
22211 $level = ($pattern[$j] - 48);
22212 // if two levels from two different patterns match at the same point, the higher one is selected.
22213 if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] < $level)) {
22214 $hyphenword[$zero] = $level;
22223 $maxpos = $numchars - $rightmin;
22224 for ($i = $leftmin; $i <= $maxpos; ++
$i) {
22225 // only odd levels indicate allowed hyphenation points
22226 if (isset($hyphenword[$i]) AND (($hyphenword[$i] %
2) != 0)) {
22227 // 173 = soft hyphen character
22228 array_splice($word, $i +
$inserted, 0, 173);
22236 * Returns text with soft hyphens.
22237 * @param $text (string) text to process
22238 * @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/
22239 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm.
22240 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
22241 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
22242 * @param $charmin (int) Minimum word length to apply the hyphenation algoritm.
22243 * @param $charmax (int) Maximum length of broken piece of word.
22244 * @return array text with soft hyphens
22245 * @author Nicola Asuni
22246 * @since 4.9.012 (2010-04-12)
22249 public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22250 $text = $this->unhtmlentities($text);
22251 $word = array(); // last word
22252 $txtarr = array(); // text to be returned
22253 $intag = false; // true if we are inside an HTML tag
22254 $skip = false; // true to skip hyphenation
22255 if (!is_array($patterns)) {
22256 $patterns = TCPDF_STATIC
::getHyphenPatternsFromTEX($patterns);
22258 // get array of characters
22259 $unichars = TCPDF_FONTS
::UTF8StringToArray($text, $this->isunicode
, $this->CurrentFont
);
22261 foreach ($unichars as $char) {
22262 if ((!$intag) AND (!$skip) AND TCPDF_FONT_DATA
::$uni_type[$char] == 'L') {
22263 // letter character
22266 // other type of character
22267 if (!TCPDF_STATIC
::empty_string($word)) {
22268 // hypenate the word
22269 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22273 if (chr($char) == '<') {
22274 // we are inside an HTML tag
22276 } elseif ($intag AND (chr($char) == '>')) {
22279 // check for style tag
22280 $expected = array(115, 116, 121, 108, 101); // = 'style'
22281 $current = array_slice($txtarr, -6, 5); // last 5 chars
22282 $compare = array_diff($expected, $current);
22283 if (empty($compare)) {
22284 // check if it is a closing tag
22285 $expected = array(47); // = '/'
22286 $current = array_slice($txtarr, -7, 1);
22287 $compare = array_diff($expected, $current);
22288 if (empty($compare)) {
22289 // closing style tag
22292 // opening style tag
22299 if (!TCPDF_STATIC
::empty_string($word)) {
22300 // hypenate the word
22301 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22303 // convert char array to string and return
22304 return TCPDF_FONTS
::UTF8ArrSubString($txtarr, '', '', $this->isunicode
);
22308 * Enable/disable rasterization of vector images using ImageMagick library.
22309 * @param $mode (boolean) if true enable rasterization, false otherwise.
22311 * @since 5.0.000 (2010-04-27)
22313 public function setRasterizeVectorImages($mode) {
22314 $this->rasterize_vector_images
= $mode;
22318 * Enable or disable default option for font subsetting.
22319 * @param $enable (boolean) if true enable font subsetting by default.
22320 * @author Nicola Asuni
22322 * @since 5.3.002 (2010-06-07)
22324 public function setFontSubsetting($enable=true) {
22325 if ($this->pdfa_mode
) {
22326 $this->font_subsetting
= false;
22328 $this->font_subsetting
= $enable ? true : false;
22333 * Return the default option for font subsetting.
22334 * @return boolean default font subsetting state.
22335 * @author Nicola Asuni
22337 * @since 5.3.002 (2010-06-07)
22339 public function getFontSubsetting() {
22340 return $this->font_subsetting
;
22344 * Left trim the input string
22345 * @param $str (string) string to trim
22346 * @param $replace (string) string that replace spaces.
22347 * @return left trimmed string
22348 * @author Nicola Asuni
22350 * @since 5.8.000 (2010-08-11)
22352 public function stringLeftTrim($str, $replace='') {
22353 return preg_replace('/^'.$this->re_space
['p'].'+/'.$this->re_space
['m'], $replace, $str);
22357 * Right trim the input string
22358 * @param $str (string) string to trim
22359 * @param $replace (string) string that replace spaces.
22360 * @return right trimmed string
22361 * @author Nicola Asuni
22363 * @since 5.8.000 (2010-08-11)
22365 public function stringRightTrim($str, $replace='') {
22366 return preg_replace('/'.$this->re_space
['p'].'+$/'.$this->re_space
['m'], $replace, $str);
22370 * Trim the input string
22371 * @param $str (string) string to trim
22372 * @param $replace (string) string that replace spaces.
22373 * @return trimmed string
22374 * @author Nicola Asuni
22376 * @since 5.8.000 (2010-08-11)
22378 public function stringTrim($str, $replace='') {
22379 $str = $this->stringLeftTrim($str, $replace);
22380 $str = $this->stringRightTrim($str, $replace);
22385 * Return true if the current font is unicode type.
22386 * @return true for unicode font, false otherwise.
22387 * @author Nicola Asuni
22389 * @since 5.8.002 (2010-08-14)
22391 public function isUnicodeFont() {
22392 return (($this->CurrentFont
['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont
['type'] == 'cidfont0'));
22396 * Return normalized font name
22397 * @param $fontfamily (string) property string containing font family names
22398 * @return string normalized font name
22399 * @author Nicola Asuni
22401 * @since 5.8.004 (2010-08-17)
22403 public function getFontFamilyName($fontfamily) {
22404 // remove spaces and symbols
22405 $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily));
22406 // extract all font names
22407 $fontslist = preg_split('/[,]/', $fontfamily);
22408 // find first valid font name
22409 foreach ($fontslist as $font) {
22410 // replace font variations
22411 $font = preg_replace('/regular$/', '', $font);
22412 $font = preg_replace('/italic$/', 'I', $font);
22413 $font = preg_replace('/oblique$/', 'I', $font);
22414 $font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
22415 // replace common family names and core fonts
22416 $pattern = array();
22417 $replacement = array();
22418 $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
22419 $replacement[] = 'times';
22420 $pattern[] = '/^sansserif/';
22421 $replacement[] = 'helvetica';
22422 $pattern[] = '/^monospace/';
22423 $replacement[] = 'courier';
22424 $font = preg_replace($pattern, $replacement, $font);
22425 if (in_array(strtolower($font), $this->fontlist
) OR in_array($font, $this->fontkeys
)) {
22429 // return current font as default
22430 return $this->CurrentFont
['fontkey'];
22434 * Start a new XObject Template.
22435 * 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).
22436 * 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.
22437 * Note: X,Y coordinates will be reset to 0,0.
22438 * @param $w (int) Template width in user units (empty string or zero = page width less margins).
22439 * @param $h (int) Template height in user units (empty string or zero = page height less margins).
22440 * @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).
22441 * @return int the XObject Template ID in case of success or false in case of error.
22442 * @author Nicola Asuni
22444 * @since 5.8.017 (2010-08-24)
22445 * @see endTemplate(), printTemplate()
22447 public function startTemplate($w=0, $h=0, $group=false) {
22448 if ($this->inxobj
) {
22449 // we are already inside an XObject template
22452 $this->inxobj
= true;
22455 $this->xobjid
= 'XT'.$this->n
;
22457 $this->xobjects
[$this->xobjid
] = array('n' => $this->n
);
22458 // store current graphic state
22459 $this->xobjects
[$this->xobjid
]['gvars'] = $this->getGraphicVars();
22461 $this->xobjects
[$this->xobjid
]['intmrk'] = 0;
22462 $this->xobjects
[$this->xobjid
]['transfmrk'] = array();
22463 $this->xobjects
[$this->xobjid
]['outdata'] = '';
22464 $this->xobjects
[$this->xobjid
]['xobjects'] = array();
22465 $this->xobjects
[$this->xobjid
]['images'] = array();
22466 $this->xobjects
[$this->xobjid
]['fonts'] = array();
22467 $this->xobjects
[$this->xobjid
]['annotations'] = array();
22468 $this->xobjects
[$this->xobjid
]['extgstates'] = array();
22469 $this->xobjects
[$this->xobjid
]['gradients'] = array();
22470 $this->xobjects
[$this->xobjid
]['spot_colors'] = array();
22471 // set new environment
22472 $this->num_columns
= 1;
22473 $this->current_column
= 0;
22474 $this->SetAutoPageBreak(false);
22475 if (($w === '') OR ($w <= 0)) {
22476 $w = $this->w
- $this->lMargin
- $this->rMargin
;
22478 if (($h === '') OR ($h <= 0)) {
22479 $h = $this->h
- $this->tMargin
- $this->bMargin
;
22481 $this->xobjects
[$this->xobjid
]['x'] = 0;
22482 $this->xobjects
[$this->xobjid
]['y'] = 0;
22483 $this->xobjects
[$this->xobjid
]['w'] = $w;
22484 $this->xobjects
[$this->xobjid
]['h'] = $h;
22487 $this->wPt
= $this->w
* $this->k
;
22488 $this->hPt
= $this->h
* $this->k
;
22489 $this->fwPt
= $this->wPt
;
22490 $this->fhPt
= $this->hPt
;
22493 $this->lMargin
= 0;
22494 $this->rMargin
= 0;
22495 $this->tMargin
= 0;
22496 $this->bMargin
= 0;
22498 $this->xobjects
[$this->xobjid
]['group'] = $group;
22499 return $this->xobjid
;
22503 * End the current XObject Template started with startTemplate() and restore the previous graphic state.
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 * @return int the XObject Template ID in case of success or false in case of error.
22507 * @author Nicola Asuni
22509 * @since 5.8.017 (2010-08-24)
22510 * @see startTemplate(), printTemplate()
22512 public function endTemplate() {
22513 if (!$this->inxobj
) {
22514 // we are not inside a template
22517 $this->inxobj
= false;
22518 // restore previous graphic state
22519 $this->setGraphicVars($this->xobjects
[$this->xobjid
]['gvars'], true);
22520 return $this->xobjid
;
22524 * Print an XObject Template.
22525 * You can print an XObject Template inside the currently opened Template.
22526 * 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).
22527 * 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.
22528 * @param $id (string) The ID of XObject Template to print.
22529 * @param $x (int) X position in user units (empty string = current x position)
22530 * @param $y (int) Y position in user units (empty string = current y position)
22531 * @param $w (int) Width in user units (zero = remaining page width)
22532 * @param $h (int) Height in user units (zero = remaining page height)
22533 * @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>
22534 * @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>
22535 * @param $fitonpage (boolean) If true the template is resized to not exceed page dimensions.
22536 * @author Nicola Asuni
22538 * @since 5.8.017 (2010-08-24)
22539 * @see startTemplate(), endTemplate()
22541 public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
22542 if ($this->state
!= 2) {
22545 if (!isset($this->xobjects
[$id])) {
22546 $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
22548 if ($this->inxobj
) {
22549 if ($id == $this->xobjid
) {
22550 // close current template
22551 $this->endTemplate();
22553 // use the template as resource for the template currently opened
22554 $this->xobjects
[$this->xobjid
]['xobjects'][$id] = $this->xobjects
[$id];
22557 // set default values
22564 // check page for no-write regions and adapt page margins if necessary
22565 list($x, $y) = $this->checkPageRegions($h, $x, $y);
22566 $ow = $this->xobjects
[$id]['w'];
22570 $oh = $this->xobjects
[$id]['h'];
22574 // calculate template width and height on document
22575 if (($w <= 0) AND ($h <= 0)) {
22578 } elseif ($w <= 0) {
22579 $w = $h * $ow / $oh;
22580 } elseif ($h <= 0) {
22581 $h = $w * $oh / $ow;
22583 // fit the template on available space
22584 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22585 // set page alignment
22589 if ($palign == 'L') {
22590 $xt = $this->lMargin
;
22591 } elseif ($palign == 'C') {
22592 $xt = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
22593 } elseif ($palign == 'R') {
22594 $xt = $this->w
- $this->rMargin
- $w;
22600 if ($palign == 'L') {
22601 $xt = $this->lMargin
;
22602 } elseif ($palign == 'C') {
22603 $xt = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
22604 } elseif ($palign == 'R') {
22605 $xt = $this->w
- $this->rMargin
- $w;
22611 // print XObject Template + Transformation matrix
22612 $this->StartTransform();
22613 // translate and scale
22621 $tm[4] = $xt * $this->k
;
22622 $tm[5] = ($this->h
- $h - $y) * $this->k
;
22623 $this->Transform($tm);
22625 $this->_out('/'.$id.' Do');
22626 $this->StopTransform();
22628 if (!empty($this->xobjects
[$id]['annotations'])) {
22629 foreach ($this->xobjects
[$id]['annotations'] as $annot) {
22630 // transform original coordinates
22631 $coordlt = TCPDF_STATIC
::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k
), (-$annot['y'] * $this->k
)));
22632 $ax = ($coordlt[4] / $this->k
);
22633 $ay = ($this->h
- $h - ($coordlt[5] / $this->k
));
22634 $coordrb = TCPDF_STATIC
::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] +
$annot['w']) * $this->k
), ((-$annot['y'] - $annot['h']) * $this->k
)));
22635 $aw = ($coordrb[4] / $this->k
) - $ax;
22636 $ah = ($this->h
- $h - ($coordrb[5] / $this->k
)) - $ay;
22637 $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
22640 // set pointer to align the next text/objects
22648 $this->y
= $y +
round($h/2);
22658 $this->SetY($rb_y);
22668 * Set the percentage of character stretching.
22669 * @param $perc (int) percentage of stretching (100 = no stretching)
22670 * @author Nicola Asuni
22672 * @since 5.9.000 (2010-09-29)
22674 public function setFontStretching($perc=100) {
22675 $this->font_stretching
= $perc;
22679 * Get the percentage of character stretching.
22680 * @return float stretching value
22681 * @author Nicola Asuni
22683 * @since 5.9.000 (2010-09-29)
22685 public function getFontStretching() {
22686 return $this->font_stretching
;
22690 * Set the amount to increase or decrease the space between characters in a text.
22691 * @param $spacing (float) amount to increase or decrease the space between characters in a text (0 = default spacing)
22692 * @author Nicola Asuni
22694 * @since 5.9.000 (2010-09-29)
22696 public function setFontSpacing($spacing=0) {
22697 $this->font_spacing
= $spacing;
22701 * Get the amount to increase or decrease the space between characters in a text.
22702 * @return int font spacing (tracking) value
22703 * @author Nicola Asuni
22705 * @since 5.9.000 (2010-09-29)
22707 public function getFontSpacing() {
22708 return $this->font_spacing
;
22712 * Return an array of no-write page regions
22713 * @return array of no-write page regions
22714 * @author Nicola Asuni
22716 * @since 5.9.003 (2010-10-13)
22717 * @see setPageRegions(), addPageRegion()
22719 public function getPageRegions() {
22720 return $this->page_regions
;
22724 * Set no-write regions on page.
22725 * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code.
22726 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22727 * You can set multiple regions for the same page.
22728 * @param $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.
22729 * @author Nicola Asuni
22731 * @since 5.9.003 (2010-10-13)
22732 * @see addPageRegion(), getPageRegions()
22734 public function setPageRegions($regions=array()) {
22735 // empty current regions array
22736 $this->page_regions
= array();
22738 foreach ($regions as $data) {
22739 $this->addPageRegion($data);
22744 * Add a single no-write region on selected page.
22745 * 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.
22746 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22747 * You can set multiple regions for the same page.
22748 * @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).
22749 * @author Nicola Asuni
22751 * @since 5.9.003 (2010-10-13)
22752 * @see setPageRegions(), getPageRegions()
22754 public function addPageRegion($region) {
22755 if (!isset($region['page']) OR empty($region['page'])) {
22756 $region['page'] = $this->page
;
22758 if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
22759 AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
22760 AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
22761 $this->page_regions
[] = $region;
22766 * Remove a single no-write region.
22767 * @param $key (int) region key
22768 * @author Nicola Asuni
22770 * @since 5.9.003 (2010-10-13)
22771 * @see setPageRegions(), getPageRegions()
22773 public function removePageRegion($key) {
22774 if (isset($this->page_regions
[$key])) {
22775 unset($this->page_regions
[$key]);
22780 * Check page for no-write regions and adapt current coordinates and page margins if necessary.
22781 * 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.
22782 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22783 * @param $h (float) height of the text/image/object to print in user units
22784 * @param $x (float) current X coordinate in user units
22785 * @param $y (float) current Y coordinate in user units
22786 * @return array($x, $y)
22787 * @author Nicola Asuni
22789 * @since 5.9.003 (2010-10-13)
22791 protected function checkPageRegions($h, $x, $y) {
22792 // set default values
22799 if (!$this->check_page_regions
OR empty($this->page_regions
)) {
22800 // no page regions defined
22801 return array($x, $y);
22804 $h = $this->getCellHeight($this->FontSize
);
22806 // check for page break
22807 if ($this->checkPageBreak($h, $y)) {
22808 // the content will be printed on a new page
22812 if ($this->num_columns
> 1) {
22814 $this->lMargin
= ($this->columns
[$this->current_column
]['x'] - $this->columns
[$this->current_column
]['w']);
22816 $this->rMargin
= ($this->w
- $this->columns
[$this->current_column
]['x'] - $this->columns
[$this->current_column
]['w']);
22820 $this->lMargin
= max($this->clMargin
, $this->original_lMargin
);
22822 $this->rMargin
= max($this->crMargin
, $this->original_rMargin
);
22825 // adjust coordinates and page margins
22826 foreach ($this->page_regions
as $regid => $regdata) {
22827 if ($regdata['page'] == $this->page
) {
22828 // check region boundaries
22829 if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
22830 // Y is inside the region
22831 $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
22832 $yt = max($y, $regdata['yt']);
22833 $yb = min(($yt +
$h), $regdata['yb']);
22834 $xt = (($yt - $regdata['yt']) * $minv) +
$regdata['xt'];
22835 $xb = (($yb - $regdata['yt']) * $minv) +
$regdata['xt'];
22836 if ($regdata['side'] == 'L') { // left side
22837 $new_margin = max($xt, $xb);
22838 if ($this->lMargin
< $new_margin) {
22840 // adjust left page margin
22841 $this->lMargin
= max(0, $new_margin);
22843 if ($x < $new_margin) {
22844 // adjust x position
22846 if ($new_margin > ($this->w
- $this->rMargin
)) {
22847 // adjust y position
22848 $y = $regdata['yb'] - $h;
22852 } elseif ($regdata['side'] == 'R') { // right side
22853 $new_margin = min($xt, $xb);
22854 if (($this->w
- $this->rMargin
) > $new_margin) {
22856 // adjust right page margin
22857 $this->rMargin
= max(0, ($this->w
- $new_margin));
22859 if ($x > $new_margin) {
22860 // adjust x position
22862 if ($new_margin > $this->lMargin
) {
22863 // adjust y position
22864 $y = $regdata['yb'] - $h;
22872 return array($x, $y);
22875 // --- SVG METHODS ---------------------------------------------------------
22878 * Embedd a Scalable Vector Graphics (SVG) image.
22879 * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
22880 * @param $file (string) Name of the SVG file or a '@' character followed by the SVG data string.
22881 * @param $x (float) Abscissa of the upper-left corner.
22882 * @param $y (float) Ordinate of the upper-left corner.
22883 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
22884 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
22885 * @param $link (mixed) URL or identifier returned by AddLink().
22886 * @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.
22887 * @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>
22888 * @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)))
22889 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
22890 * @author Nicola Asuni
22891 * @since 5.0.000 (2010-05-02)
22894 public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
22895 if ($this->state
!= 2) {
22899 $this->svggradients
= array();
22900 $this->svggradientid
= 0;
22901 $this->svgdefsmode
= false;
22902 $this->svgdefs
= array();
22903 $this->svgclipmode
= false;
22904 $this->svgclippaths
= array();
22905 $this->svgcliptm
= array();
22906 $this->svgclipid
= 0;
22907 $this->svgtext
= '';
22908 $this->svgtextmode
= array();
22909 if ($this->rasterize_vector_images
AND ($w > 0) AND ($h > 0)) {
22910 // convert SVG to raster image using GD or ImageMagick libraries
22911 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22913 if ($file[0] === '@') { // image from string
22914 $this->svgdir
= '';
22915 $svgdata = substr($file, 1);
22916 } else { // SVG file
22917 $this->svgdir
= dirname($file);
22918 $svgdata = TCPDF_STATIC
::fileGetContents($file);
22920 if ($svgdata === FALSE) {
22921 $this->Error('SVG file not found: '.$file);
22929 // check page for no-write regions and adapt page margins if necessary
22930 list($x, $y) = $this->checkPageRegions($h, $x, $y);
22936 $aspect_ratio_align = 'xMidYMid';
22937 $aspect_ratio_ms = 'meet';
22939 // get original image width and height
22940 preg_match('/<svg([^\>]*)>/si', $svgdata, $regs);
22941 if (isset($regs[1]) AND !empty($regs[1])) {
22943 if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22944 $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit
, false);
22947 if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22948 $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit
, false);
22951 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22952 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit
, false);
22955 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22956 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit
, false);
22959 $view_box = array();
22960 if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
22961 if (count($tmp) == 5) {
22963 foreach ($tmp as $key => $val) {
22964 $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit
, false);
22966 $ox = $view_box[0];
22967 $oy = $view_box[1];
22969 // get aspect ratio
22971 if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22972 $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
22973 switch (count($aspect_ratio)) {
22975 $aspect_ratio_align = $aspect_ratio[1];
22976 $aspect_ratio_ms = $aspect_ratio[2];
22980 $aspect_ratio_align = $aspect_ratio[0];
22981 $aspect_ratio_ms = $aspect_ratio[1];
22985 $aspect_ratio_align = $aspect_ratio[0];
22986 $aspect_ratio_ms = 'meet';
22999 // calculate image width and height on document
23000 if (($w <= 0) AND ($h <= 0)) {
23001 // convert image size to document unit
23004 } elseif ($w <= 0) {
23005 $w = $h * $ow / $oh;
23006 } elseif ($h <= 0) {
23007 $h = $w * $oh / $ow;
23009 // fit the image on available space
23010 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
23011 if ($this->rasterize_vector_images
) {
23012 // convert SVG to raster image using GD or ImageMagick libraries
23013 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
23016 $this->img_rb_y
= $y +
$h;
23019 if ($palign == 'L') {
23020 $ximg = $this->lMargin
;
23021 } elseif ($palign == 'C') {
23022 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
23023 } elseif ($palign == 'R') {
23024 $ximg = $this->w
- $this->rMargin
- $w;
23028 $this->img_rb_x
= $ximg;
23030 if ($palign == 'L') {
23031 $ximg = $this->lMargin
;
23032 } elseif ($palign == 'C') {
23033 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
23034 } elseif ($palign == 'R') {
23035 $ximg = $this->w
- $this->rMargin
- $w;
23039 $this->img_rb_x
= $ximg +
$w;
23041 // store current graphic vars
23042 $gvars = $this->getGraphicVars();
23043 // store SVG position and scale factors
23044 $svgoffset_x = ($ximg - $ox) * $this->k
;
23045 $svgoffset_y = -($y - $oy) * $this->k
;
23046 if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
23047 $ow = $view_box[2];
23048 $oh = $view_box[3];
23057 $svgscale_x = $w / $ow;
23058 $svgscale_y = $h / $oh;
23059 // scaling and alignment
23060 if ($aspect_ratio_align != 'none') {
23061 // store current scaling values
23062 $svgscale_old_x = $svgscale_x;
23063 $svgscale_old_y = $svgscale_y;
23064 // force uniform scaling
23065 if ($aspect_ratio_ms == 'slice') {
23066 // the entire viewport is covered by the viewBox
23067 if ($svgscale_x > $svgscale_y) {
23068 $svgscale_y = $svgscale_x;
23069 } elseif ($svgscale_x < $svgscale_y) {
23070 $svgscale_x = $svgscale_y;
23073 // the entire viewBox is visible within the viewport
23074 if ($svgscale_x < $svgscale_y) {
23075 $svgscale_y = $svgscale_x;
23076 } elseif ($svgscale_x > $svgscale_y) {
23077 $svgscale_x = $svgscale_y;
23080 // correct X alignment
23081 switch (substr($aspect_ratio_align, 1, 3)) {
23087 $svgoffset_x +
= (($w * $this->k
) - ($ow * $this->k
* $svgscale_x));
23092 $svgoffset_x +
= ((($w * $this->k
) - ($ow * $this->k
* $svgscale_x)) / 2);
23096 // correct Y alignment
23097 switch (substr($aspect_ratio_align, 5)) {
23103 $svgoffset_y -= (($h * $this->k
) - ($oh * $this->k
* $svgscale_y));
23108 $svgoffset_y -= ((($h * $this->k
) - ($oh * $this->k
* $svgscale_y)) / 2);
23113 // store current page break mode
23114 $page_break_mode = $this->AutoPageBreak
;
23115 $page_break_margin = $this->getBreakMargin();
23116 $cell_padding = $this->cell_padding
;
23117 $this->SetCellPadding(0);
23118 $this->SetAutoPageBreak(false);
23119 // save the current graphic state
23120 $this->_out('q'.$this->epsmarker
);
23121 // set initial clipping mask
23122 $this->Rect($ximg, $y, $w, $h, 'CNZ', array(), array());
23123 // scale and translate
23124 $e = $ox * $this->k
* (1 - $svgscale_x);
23125 $f = ($this->h
- $oy) * $this->k
* (1 - $svgscale_y);
23126 $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e +
$svgoffset_x), ($f +
$svgoffset_y)));
23127 // creates a new XML parser to be used by the other XML functions
23128 $this->parser
= xml_parser_create('UTF-8');
23129 // the following function allows to use parser inside object
23130 xml_set_object($this->parser
, $this);
23131 // disable case-folding for this XML parser
23132 xml_parser_set_option($this->parser
, XML_OPTION_CASE_FOLDING
, 0);
23133 // sets the element handler functions for the XML parser
23134 xml_set_element_handler($this->parser
, 'startSVGElementHandler', 'endSVGElementHandler');
23135 // sets the character data handler function for the XML parser
23136 xml_set_character_data_handler($this->parser
, 'segSVGContentHandler');
23137 // start parsing an XML document
23138 if (!xml_parse($this->parser
, $svgdata)) {
23139 $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
));
23140 $this->Error($error_message);
23142 // free this XML parser
23143 xml_parser_free($this->parser
);
23144 // restore previous graphic state
23145 $this->_out($this->epsmarker
.'Q');
23146 // restore graphic vars
23147 $this->setGraphicVars($gvars);
23148 $this->lasth
= $gvars['lasth'];
23149 if (!empty($border)) {
23157 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
23162 $this->Link($ximg, $y, $w, $h, $link, 0);
23164 // set pointer to align the next text/objects
23168 $this->x
= $this->img_rb_x
;
23172 $this->y
= $y +
round($h/2);
23173 $this->x
= $this->img_rb_x
;
23177 $this->y
= $this->img_rb_y
;
23178 $this->x
= $this->img_rb_x
;
23182 $this->SetY($this->img_rb_y
);
23186 // restore pointer to starting position
23187 $this->x
= $gvars['x'];
23188 $this->y
= $gvars['y'];
23189 $this->page
= $gvars['page'];
23190 $this->current_column
= $gvars['current_column'];
23191 $this->tMargin
= $gvars['tMargin'];
23192 $this->bMargin
= $gvars['bMargin'];
23193 $this->w
= $gvars['w'];
23194 $this->h
= $gvars['h'];
23195 $this->wPt
= $gvars['wPt'];
23196 $this->hPt
= $gvars['hPt'];
23197 $this->fwPt
= $gvars['fwPt'];
23198 $this->fhPt
= $gvars['fhPt'];
23202 $this->endlinex
= $this->img_rb_x
;
23203 // restore page break
23204 $this->SetAutoPageBreak($page_break_mode, $page_break_margin);
23205 $this->cell_padding
= $cell_padding;
23209 * Convert SVG transformation matrix to PDF.
23210 * @param $tm (array) original SVG transformation matrix
23211 * @return array transformation matrix
23213 * @since 5.0.000 (2010-05-02)
23215 protected function convertSVGtMatrix($tm) {
23220 $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit
, false) * $this->k
;
23221 $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit
, false) * $this->k
;
23223 $y = $this->h
* $this->k
;
23224 $e = ($x * (1 - $a)) - ($y * $c) +
$e;
23225 $f = ($y * (1 - $d)) - ($x * $b) +
$f;
23226 return array($a, $b, $c, $d, $e, $f);
23230 * Apply SVG graphic transformation matrix.
23231 * @param $tm (array) original SVG transformation matrix
23233 * @since 5.0.000 (2010-05-02)
23235 protected function SVGTransform($tm) {
23236 $this->Transform($this->convertSVGtMatrix($tm));
23240 * Apply the requested SVG styles (*** TO BE COMPLETED ***)
23241 * @param $svgstyle (array) array of SVG styles to apply
23242 * @param $prevsvgstyle (array) array of previous SVG style
23243 * @param $x (int) X origin of the bounding box
23244 * @param $y (int) Y origin of the bounding box
23245 * @param $w (int) width of the bounding box
23246 * @param $h (int) height of the bounding box
23247 * @param $clip_function (string) clip function
23248 * @param $clip_params (array) array of parameters for clipping function
23249 * @return object style
23250 * @author Nicola Asuni
23251 * @since 5.0.000 (2010-05-02)
23254 protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
23255 if ($this->state
!= 2) {
23259 $minlen = (0.01 / $this->k
); // minimum acceptable length
23260 if (!isset($svgstyle['opacity'])) {
23265 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
23266 $clip_path = $this->svgclippaths
[$regs[1]];
23267 foreach ($clip_path as $cp) {
23268 $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
23272 if ($svgstyle['opacity'] != 1) {
23273 $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false);
23276 $fill_color = TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors
);
23277 $this->SetFillColorArray($fill_color);
23279 $text_color = TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors
);
23280 $this->SetTextColorArray($text_color);
23282 if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
23283 $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit
, false):0);
23284 $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit
, false):0);
23285 $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit
, false):0);
23286 $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit
, false):0);
23289 $cw = $w - $left - $right;
23290 $ch = $h - $top - $bottom;
23291 if ($svgstyle['clip-rule'] == 'evenodd') {
23292 $clip_rule = 'CNZ';
23294 $clip_rule = 'CEO';
23296 $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
23300 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
23302 $gradient = $this->svggradients
[$regs[1]];
23303 if (isset($gradient['xref'])) {
23304 // reference to another gradient definition
23305 $newgradient = $this->svggradients
[$gradient['xref']];
23306 $newgradient['coords'] = $gradient['coords'];
23307 $newgradient['mode'] = $gradient['mode'];
23308 $newgradient['type'] = $gradient['type'];
23309 $newgradient['gradientUnits'] = $gradient['gradientUnits'];
23310 if (isset($gradient['gradientTransform'])) {
23311 $newgradient['gradientTransform'] = $gradient['gradientTransform'];
23313 $gradient = $newgradient;
23315 //save current Graphic State
23316 $this->_outSaveGraphicsState();
23317 //set clipping area
23318 if (!empty($clip_function) AND method_exists($this, $clip_function)) {
23319 $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
23320 if ((!isset($gradient['type']) OR ($gradient['type'] != 3)) AND is_array($bbox) AND (count($bbox) == 4)) {
23321 list($x, $y, $w, $h) = $bbox;
23324 if ($gradient['mode'] == 'measure') {
23325 if (!isset($gradient['coords'][4])) {
23326 $gradient['coords'][4] = 0.5;
23328 if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
23329 $gtm = $gradient['gradientTransform'];
23330 // apply transformation matrix
23331 $xa = ($gtm[0] * $gradient['coords'][0]) +
($gtm[2] * $gradient['coords'][1]) +
$gtm[4];
23332 $ya = ($gtm[1] * $gradient['coords'][0]) +
($gtm[3] * $gradient['coords'][1]) +
$gtm[5];
23333 $xb = ($gtm[0] * $gradient['coords'][2]) +
($gtm[2] * $gradient['coords'][3]) +
$gtm[4];
23334 $yb = ($gtm[1] * $gradient['coords'][2]) +
($gtm[3] * $gradient['coords'][3]) +
$gtm[5];
23335 $r = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) +
pow(($gtm[1] * $gradient['coords'][4]), 2));
23336 $gradient['coords'][0] = $xa;
23337 $gradient['coords'][1] = $ya;
23338 $gradient['coords'][2] = $xb;
23339 $gradient['coords'][3] = $yb;
23340 $gradient['coords'][4] = $r;
23342 // convert SVG coordinates to user units
23343 $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit
, false);
23344 $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit
, false);
23345 $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit
, false);
23346 $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit
, false);
23347 $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit
, false);
23348 if ($w <= $minlen) {
23351 if ($h <= $minlen) {
23355 if ($gradient['gradientUnits'] == 'objectBoundingBox') {
23356 // convert to SVG coordinate system
23357 $gradient['coords'][0] +
= $x;
23358 $gradient['coords'][1] +
= $y;
23359 $gradient['coords'][2] +
= $x;
23360 $gradient['coords'][3] +
= $y;
23362 // calculate percentages
23363 $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w);
23364 $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h);
23365 $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w);
23366 $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h);
23367 $gradient['coords'][4] /= $w;
23368 } elseif ($gradient['mode'] == 'percentage') {
23369 foreach($gradient['coords'] as $key => $val) {
23370 $gradient['coords'][$key] = (intval($val) / 100);
23372 $gradient['coords'][$key] = 0;
23373 } elseif ($val > 1) {
23374 $gradient['coords'][$key] = 1;
23378 if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
23379 // single color (no shading)
23380 $gradient['coords'][0] = 1;
23381 $gradient['coords'][1] = 0;
23382 $gradient['coords'][2] = 0.999;
23383 $gradient['coords'][3] = 0;
23385 // swap Y coordinates
23386 $tmp = $gradient['coords'][1];
23387 $gradient['coords'][1] = $gradient['coords'][3];
23388 $gradient['coords'][3] = $tmp;
23389 // set transformation map for gradient
23390 $cy = ($this->h
- $y);
23391 if ($gradient['type'] == 3) {
23392 // circular gradient
23393 $cy -= ($gradient['coords'][1] * ($w +
$h));
23397 $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k
), ($h * $this->k
), ($x * $this->k
), ($cy * $this->k
)));
23398 if (count($gradient['stops']) > 1) {
23399 $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
23401 } elseif ($svgstyle['fill'] != 'none') {
23402 $fill_color = TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors
);
23403 if ($svgstyle['fill-opacity'] != 1) {
23404 $this->setAlpha($this->alpha
['CA'], 'Normal', $svgstyle['fill-opacity'], false);
23406 $this->SetFillColorArray($fill_color);
23407 if ($svgstyle['fill-rule'] == 'evenodd') {
23414 if ($svgstyle['stroke'] != 'none') {
23415 if ($svgstyle['stroke-opacity'] != 1) {
23416 $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha
['ca'], false);
23417 } elseif (preg_match('/rgba\(\d+%?,\s*\d+%?,\s*\d+%?,\s*(\d+(?:\.\d+)?)\)/i', $svgstyle['stroke'], $rgba_matches)) {
23418 $this->setAlpha($rgba_matches[1], 'Normal', $this->alpha
['ca'], false);
23420 $stroke_style = array(
23421 'color' => TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors
),
23422 'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit
, false),
23423 'cap' => $svgstyle['stroke-linecap'],
23424 'join' => $svgstyle['stroke-linejoin']
23426 if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
23427 $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
23429 $this->SetLineStyle($stroke_style);
23434 if (!empty($svgstyle['font'])) {
23435 if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
23436 $font_family = $this->getFontFamilyName($regs[1]);
23438 $font_family = $svgstyle['font-family'];
23440 if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23441 $font_size = trim($regs[1]);
23443 $font_size = $svgstyle['font-size'];
23445 if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23446 $font_style = trim($regs[1]);
23448 $font_style = $svgstyle['font-style'];
23450 if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23451 $font_weight = trim($regs[1]);
23453 $font_weight = $svgstyle['font-weight'];
23455 if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23456 $font_stretch = trim($regs[1]);
23458 $font_stretch = $svgstyle['font-stretch'];
23460 if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23461 $font_spacing = trim($regs[1]);
23463 $font_spacing = $svgstyle['letter-spacing'];
23466 $font_family = $this->getFontFamilyName($svgstyle['font-family']);
23467 $font_size = $svgstyle['font-size'];
23468 $font_style = $svgstyle['font-style'];
23469 $font_weight = $svgstyle['font-weight'];
23470 $font_stretch = $svgstyle['font-stretch'];
23471 $font_spacing = $svgstyle['letter-spacing'];
23473 $font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles
[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit
);
23474 $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
23475 $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
23476 switch ($font_style) {
23491 switch ($font_weight) {
23494 $font_style .= 'B';
23498 switch ($svgstyle['text-decoration']) {
23499 case 'underline': {
23500 $font_style .= 'U';
23504 $font_style .= 'O';
23507 case 'line-through': {
23508 $font_style .= 'D';
23516 $this->SetFont($font_family, $font_style, $font_size);
23517 $this->setFontStretching($font_stretch);
23518 $this->setFontSpacing($font_spacing);
23523 * Draws an SVG path
23524 * @param $d (string) attribute d of the path SVG element
23525 * @param $style (string) Style of rendering. Possible values are:
23527 * <li>D or empty string: Draw (default).</li>
23528 * <li>F: Fill.</li>
23529 * <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
23530 * <li>DF or FD: Draw and fill.</li>
23531 * <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
23532 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
23533 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
23535 * @return array of container box measures (x, y, w, h)
23536 * @author Nicola Asuni
23537 * @since 5.0.000 (2010-05-02)
23540 protected function SVGPath($d, $style='') {
23541 if ($this->state
!= 2) {
23544 // set fill/stroke style
23545 $op = TCPDF_STATIC
::getPathPaintOperator($style, '');
23550 $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
23551 preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER
);
23558 $xmin = 2147483647;
23560 $ymin = 2147483647;
23563 $minlen = (0.01 / $this->k
); // minimum acceptable length (3 point)
23564 $firstcmd = true; // used to print first point
23565 // draw curve pieces
23566 foreach ($paths as $key => $val) {
23568 $cmd = trim($val[1]);
23569 if (strtolower($cmd) == $cmd) {
23570 // use relative coordinated instead of absolute
23580 if (isset($val[2])) {
23581 // get curve parameters
23582 $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
23584 foreach ($rawparams as $ck => $cp) {
23585 $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit
, false);
23586 if (abs($params[$ck]) < $minlen) {
23587 // aproximate little values to zero
23592 // store current origin point
23595 switch (strtoupper($cmd)) {
23596 case 'M': { // moveto
23597 foreach ($params as $ck => $cp) {
23598 if (($ck %
2) == 0) {
23599 $x = $cp +
$xoffset;
23601 $y = $cp +
$yoffset;
23602 if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23604 $this->_outPoint($x, $y);
23607 $this->_outLine($x, $y);
23612 $xmin = min($xmin, $x);
23613 $ymin = min($ymin, $y);
23614 $xmax = max($xmax, $x);
23615 $ymax = max($ymax, $y);
23624 case 'L': { // lineto
23625 foreach ($params as $ck => $cp) {
23626 if (($ck %
2) == 0) {
23627 $x = $cp +
$xoffset;
23629 $y = $cp +
$yoffset;
23630 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23631 $this->_outLine($x, $y);
23635 $xmin = min($xmin, $x);
23636 $ymin = min($ymin, $y);
23637 $xmax = max($xmax, $x);
23638 $ymax = max($ymax, $y);
23647 case 'H': { // horizontal lineto
23648 foreach ($params as $ck => $cp) {
23649 $x = $cp +
$xoffset;
23650 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23651 $this->_outLine($x, $y);
23655 $xmin = min($xmin, $x);
23656 $xmax = max($xmax, $x);
23663 case 'V': { // vertical lineto
23664 foreach ($params as $ck => $cp) {
23665 $y = $cp +
$yoffset;
23666 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23667 $this->_outLine($x, $y);
23671 $ymin = min($ymin, $y);
23672 $ymax = max($ymax, $y);
23679 case 'C': { // curveto
23680 foreach ($params as $ck => $cp) {
23681 $params[$ck] = $cp;
23682 if ((($ck +
1) %
6) == 0) {
23683 $x1 = $params[($ck - 5)] +
$xoffset;
23684 $y1 = $params[($ck - 4)] +
$yoffset;
23685 $x2 = $params[($ck - 3)] +
$xoffset;
23686 $y2 = $params[($ck - 2)] +
$yoffset;
23687 $x = $params[($ck - 1)] +
$xoffset;
23688 $y = $params[($ck)] +
$yoffset;
23689 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23690 $xmin = min($xmin, $x, $x1, $x2);
23691 $ymin = min($ymin, $y, $y1, $y2);
23692 $xmax = max($xmax, $x, $x1, $x2);
23693 $ymax = max($ymax, $y, $y1, $y2);
23702 case 'S': { // shorthand/smooth curveto
23703 foreach ($params as $ck => $cp) {
23704 $params[$ck] = $cp;
23705 if ((($ck +
1) %
4) == 0) {
23706 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
23707 $x1 = (2 * $x) - $x2;
23708 $y1 = (2 * $y) - $y2;
23713 $x2 = $params[($ck - 3)] +
$xoffset;
23714 $y2 = $params[($ck - 2)] +
$yoffset;
23715 $x = $params[($ck - 1)] +
$xoffset;
23716 $y = $params[($ck)] +
$yoffset;
23717 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23718 $xmin = min($xmin, $x, $x1, $x2);
23719 $ymin = min($ymin, $y, $y1, $y2);
23720 $xmax = max($xmax, $x, $x1, $x2);
23721 $ymax = max($ymax, $y, $y1, $y2);
23730 case 'Q': { // quadratic Bezier curveto
23731 foreach ($params as $ck => $cp) {
23732 $params[$ck] = $cp;
23733 if ((($ck +
1) %
4) == 0) {
23734 // convert quadratic points to cubic points
23735 $x1 = $params[($ck - 3)] +
$xoffset;
23736 $y1 = $params[($ck - 2)] +
$yoffset;
23737 $xa = ($x +
(2 * $x1)) / 3;
23738 $ya = ($y +
(2 * $y1)) / 3;
23739 $x = $params[($ck - 1)] +
$xoffset;
23740 $y = $params[($ck)] +
$yoffset;
23741 $xb = ($x +
(2 * $x1)) / 3;
23742 $yb = ($y +
(2 * $y1)) / 3;
23743 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23744 $xmin = min($xmin, $x, $xa, $xb);
23745 $ymin = min($ymin, $y, $ya, $yb);
23746 $xmax = max($xmax, $x, $xa, $xb);
23747 $ymax = max($ymax, $y, $ya, $yb);
23756 case 'T': { // shorthand/smooth quadratic Bezier curveto
23757 foreach ($params as $ck => $cp) {
23758 $params[$ck] = $cp;
23759 if (($ck %
2) != 0) {
23760 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
23761 $x1 = (2 * $x) - $x1;
23762 $y1 = (2 * $y) - $y1;
23767 // convert quadratic points to cubic points
23768 $xa = ($x +
(2 * $x1)) / 3;
23769 $ya = ($y +
(2 * $y1)) / 3;
23770 $x = $params[($ck - 1)] +
$xoffset;
23771 $y = $params[($ck)] +
$yoffset;
23772 $xb = ($x +
(2 * $x1)) / 3;
23773 $yb = ($y +
(2 * $y1)) / 3;
23774 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23775 $xmin = min($xmin, $x, $xa, $xb);
23776 $ymin = min($ymin, $y, $ya, $yb);
23777 $xmax = max($xmax, $x, $xa, $xb);
23778 $ymax = max($ymax, $y, $ya, $yb);
23787 case 'A': { // elliptical arc
23788 foreach ($params as $ck => $cp) {
23789 $params[$ck] = $cp;
23790 if ((($ck +
1) %
7) == 0) {
23793 $rx = abs($params[($ck - 6)]);
23794 $ry = abs($params[($ck - 5)]);
23795 $ang = -$rawparams[($ck - 4)];
23796 $angle = deg2rad($ang);
23797 $fa = $rawparams[($ck - 3)]; // large-arc-flag
23798 $fs = $rawparams[($ck - 2)]; // sweep-flag
23799 $x = $params[($ck - 1)] +
$xoffset;
23800 $y = $params[$ck] +
$yoffset;
23801 if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
23802 // endpoints are almost identical
23803 $xmin = min($xmin, $x);
23804 $ymin = min($ymin, $y);
23805 $xmax = max($xmax, $x);
23806 $ymax = max($ymax, $y);
23808 $cos_ang = cos($angle);
23809 $sin_ang = sin($angle);
23810 $a = (($x0 - $x) / 2);
23811 $b = (($y0 - $y) / 2);
23812 $xa = ($a * $cos_ang) - ($b * $sin_ang);
23813 $ya = ($a * $sin_ang) +
($b * $cos_ang);
23818 $delta = ($xa2 / $rx2) +
($ya2 / $ry2);
23820 $rx *= sqrt($delta);
23821 $ry *= sqrt($delta);
23825 $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
23826 if ($numerator < 0) {
23829 $root = sqrt($numerator / (($rx2 * $ya2) +
($ry2 * $xa2)));
23834 $cax = $root * (($rx * $ya) / $ry);
23835 $cay = -$root * (($ry * $xa) / $rx);
23836 // coordinates of ellipse center
23837 $cx = ($cax * $cos_ang) - ($cay * $sin_ang) +
(($x0 +
$x) / 2);
23838 $cy = ($cax * $sin_ang) +
($cay * $cos_ang) +
(($y0 +
$y) / 2);
23840 $angs = TCPDF_STATIC
::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
23841 $dang = TCPDF_STATIC
::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
23842 if (($fs == 0) AND ($dang > 0)) {
23843 $dang -= (2 * M_PI
);
23844 } elseif (($fs == 1) AND ($dang < 0)) {
23845 $dang +
= (2 * M_PI
);
23847 $angf = $angs - $dang;
23848 if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
23854 $angs = round(rad2deg($angs), 6);
23855 $angf = round(rad2deg($angf), 6);
23856 // covent angles to positive values
23857 if (($angs < 0) AND ($angf < 0)) {
23862 if (($key == 0) AND (isset($paths[($key +
1)][1])) AND (trim($paths[($key +
1)][1]) == 'z')) {
23865 list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
23866 $xmin = min($xmin, $x, $axmin);
23867 $ymin = min($ymin, $y, $aymin);
23868 $xmax = max($xmax, $x, $axmax);
23869 $ymax = max($ymax, $y, $aymax);
23889 return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
23893 * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***)
23894 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
23895 * @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.
23896 * @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.
23897 * @param $ctm (array) tranformation matrix for clipping mode (starting transformation matrix).
23898 * @author Nicola Asuni
23899 * @since 5.0.000 (2010-05-02)
23902 protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
23903 // check if we are in clip mode
23904 if ($this->svgclipmode
) {
23905 $this->svgclippaths
[$this->svgclipid
][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm
[$this->svgclipid
]);
23908 if ($this->svgdefsmode
AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
23909 if (isset($attribs['id'])) {
23910 $attribs['child_elements'] = array();
23911 $this->svgdefs
[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23914 if (end($this->svgdefs
) !== FALSE) {
23915 $last_svgdefs_id = key($this->svgdefs
);
23916 if (isset($this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'])) {
23917 $attribs['id'] = 'DF_'.(count($this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements']) +
1);
23918 $this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23925 if ($parser == 'clip-path') {
23926 // set clipping mode
23929 // get styling properties
23930 $prev_svgstyle = $this->svgstyles
[max(0,(count($this->svgstyles
) - 1))]; // previous style
23931 $svgstyle = $this->svgstyles
[0]; // set default style
23932 if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) {
23933 // default fill attribute for clipping
23934 $attribs['fill'] = 'none';
23936 if (isset($attribs['style']) AND !TCPDF_STATIC
::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) {
23937 // fix style for regular expression
23938 $attribs['style'] = ';'.$attribs['style'];
23940 foreach ($prev_svgstyle as $key => $val) {
23941 if (in_array($key, TCPDF_IMAGES
::$svginheritprop)) {
23942 // inherit previous value
23943 $svgstyle[$key] = $val;
23945 if (isset($attribs[$key]) AND !TCPDF_STATIC
::empty_string($attribs[$key])) {
23946 // specific attribute settings
23947 if ($attribs[$key] == 'inherit') {
23948 $svgstyle[$key] = $val;
23950 $svgstyle[$key] = $attribs[$key];
23952 } elseif (isset($attribs['style']) AND !TCPDF_STATIC
::empty_string($attribs['style'])) {
23953 // CSS style syntax
23954 $attrval = array();
23955 if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
23956 if ($attrval[1] == 'inherit') {
23957 $svgstyle[$key] = $val;
23959 $svgstyle[$key] = $attrval[1];
23964 // transformation matrix
23965 if (!empty($ctm)) {
23968 $tm = array(1,0,0,1,0,0);
23970 if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
23971 $tm = TCPDF_STATIC
::getTransformationMatrixProduct($tm, TCPDF_STATIC
::getSVGTransformMatrix($attribs['transform']));
23973 $svgstyle['transfmatrix'] = $tm;
23974 $invisible = false;
23975 if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
23976 // the current graphics element is invisible (nothing is painted)
23982 $this->svgdefsmode
= true;
23990 $this->svgclipmode
= true;
23991 if (!isset($attribs['id'])) {
23992 $attribs['id'] = 'CP_'.(count($this->svgcliptm
) +
1);
23994 $this->svgclipid
= $attribs['id'];
23995 $this->svgclippaths
[$this->svgclipid
] = array();
23996 $this->svgcliptm
[$this->svgclipid
] = $tm;
24000 // start of SVG object
24004 // group together related graphics elements
24005 array_push($this->svgstyles
, $svgstyle);
24006 $this->StartTransform();
24007 $x = (isset($attribs['x'])?$attribs['x']:0);
24008 $y = (isset($attribs['y'])?$attribs['y']:0);
24009 $w = 1;//(isset($attribs['width'])?$attribs['width']:1);
24010 $h = 1;//(isset($attribs['height'])?$attribs['height']:1);
24011 $tm = TCPDF_STATIC
::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
24012 $this->SVGTransform($tm);
24013 $this->setSVGStyles($svgstyle, $prev_svgstyle);
24016 case 'linearGradient': {
24017 if ($this->pdfa_mode
) {
24020 if (!isset($attribs['id'])) {
24021 $attribs['id'] = 'GR_'.(count($this->svggradients
) +
1);
24023 $this->svggradientid
= $attribs['id'];
24024 $this->svggradients
[$this->svggradientid
] = array();
24025 $this->svggradients
[$this->svggradientid
]['type'] = 2;
24026 $this->svggradients
[$this->svggradientid
]['stops'] = array();
24027 if (isset($attribs['gradientUnits'])) {
24028 $this->svggradients
[$this->svggradientid
]['gradientUnits'] = $attribs['gradientUnits'];
24030 $this->svggradients
[$this->svggradientid
]['gradientUnits'] = 'objectBoundingBox';
24032 //$attribs['spreadMethod']
24033 if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2'])))
24034 OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%'))
24035 OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%'))
24036 OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%'))
24037 OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) {
24038 $this->svggradients
[$this->svggradientid
]['mode'] = 'percentage';
24040 $this->svggradients
[$this->svggradientid
]['mode'] = 'measure';
24042 $x1 = (isset($attribs['x1'])?$attribs['x1']:'0');
24043 $y1 = (isset($attribs['y1'])?$attribs['y1']:'0');
24044 $x2 = (isset($attribs['x2'])?$attribs['x2']:'100');
24045 $y2 = (isset($attribs['y2'])?$attribs['y2']:'0');
24046 if (isset($attribs['gradientTransform'])) {
24047 $this->svggradients
[$this->svggradientid
]['gradientTransform'] = TCPDF_STATIC
::getSVGTransformMatrix($attribs['gradientTransform']);
24049 $this->svggradients
[$this->svggradientid
]['coords'] = array($x1, $y1, $x2, $y2);
24050 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24051 // gradient is defined on another place
24052 $this->svggradients
[$this->svggradientid
]['xref'] = substr($attribs['xlink:href'], 1);
24056 case 'radialGradient': {
24057 if ($this->pdfa_mode
) {
24060 if (!isset($attribs['id'])) {
24061 $attribs['id'] = 'GR_'.(count($this->svggradients
) +
1);
24063 $this->svggradientid
= $attribs['id'];
24064 $this->svggradients
[$this->svggradientid
] = array();
24065 $this->svggradients
[$this->svggradientid
]['type'] = 3;
24066 $this->svggradients
[$this->svggradientid
]['stops'] = array();
24067 if (isset($attribs['gradientUnits'])) {
24068 $this->svggradients
[$this->svggradientid
]['gradientUnits'] = $attribs['gradientUnits'];
24070 $this->svggradients
[$this->svggradientid
]['gradientUnits'] = 'objectBoundingBox';
24072 //$attribs['spreadMethod']
24073 if (((!isset($attribs['cx'])) AND (!isset($attribs['cy'])))
24074 OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%'))
24075 OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')))) {
24076 $this->svggradients
[$this->svggradientid
]['mode'] = 'percentage';
24077 } elseif (isset($attribs['r']) AND is_numeric($attribs['r']) AND ($attribs['r']) <= 1) {
24078 $this->svggradients
[$this->svggradientid
]['mode'] = 'ratio';
24080 $this->svggradients
[$this->svggradientid
]['mode'] = 'measure';
24082 $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5);
24083 $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5);
24084 $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx);
24085 $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy);
24086 $r = (isset($attribs['r']) ? $attribs['r'] : 0.5);
24087 if (isset($attribs['gradientTransform'])) {
24088 $this->svggradients
[$this->svggradientid
]['gradientTransform'] = TCPDF_STATIC
::getSVGTransformMatrix($attribs['gradientTransform']);
24090 $this->svggradients
[$this->svggradientid
]['coords'] = array($cx, $cy, $fx, $fy, $r);
24091 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24092 // gradient is defined on another place
24093 $this->svggradients
[$this->svggradientid
]['xref'] = substr($attribs['xlink:href'], 1);
24099 if (substr($attribs['offset'], -1) == '%') {
24100 $offset = floatval(substr($attribs['offset'], -1)) / 100;
24102 $offset = floatval($attribs['offset']);
24107 $stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors
):'black';
24108 $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
24109 $this->svggradients
[$this->svggradientid
]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
24117 if (isset($attribs['d'])) {
24118 $d = trim($attribs['d']);
24120 $x = (isset($attribs['x'])?$attribs['x']:0);
24121 $y = (isset($attribs['y'])?$attribs['y']:0);
24122 $w = (isset($attribs['width'])?$attribs['width']:1);
24123 $h = (isset($attribs['height'])?$attribs['height']:1);
24124 $tm = TCPDF_STATIC
::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
24126 $this->SVGTransform($tm);
24127 $this->SVGPath($d, 'CNZ');
24129 $this->StartTransform();
24130 $this->SVGTransform($tm);
24131 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ'));
24132 if (!empty($obstyle)) {
24133 $this->SVGPath($d, $obstyle);
24135 $this->StopTransform();
24146 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false):0);
24147 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false):0);
24148 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit
, false):0);
24149 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit
, false):0);
24150 $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit
, false):0);
24151 $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit
, false):$rx);
24153 $this->SVGTransform($tm);
24154 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
24156 $this->StartTransform();
24157 $this->SVGTransform($tm);
24158 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
24159 if (!empty($obstyle)) {
24160 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
24162 $this->StopTransform();
24170 $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit
, false) : 0);
24171 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit
, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false) : 0));
24172 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit
, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false) : 0));
24178 $this->SVGTransform($tm);
24179 $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
24181 $this->StartTransform();
24182 $this->SVGTransform($tm);
24183 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
24184 if (!empty($obstyle)) {
24185 $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
24187 $this->StopTransform();
24195 $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit
, false) : 0);
24196 $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit
, false) : 0);
24197 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit
, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false) : 0));
24198 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit
, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false) : 0));
24204 $this->SVGTransform($tm);
24205 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
24207 $this->StartTransform();
24208 $this->SVGTransform($tm);
24209 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
24210 if (!empty($obstyle)) {
24211 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
24213 $this->StopTransform();
24221 $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit
, false):0);
24222 $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit
, false):0);
24223 $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit
, false):0);
24224 $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit
, false):0);
24227 $w = abs($x2 - $x1);
24228 $h = abs($y2 - $y1);
24230 $this->StartTransform();
24231 $this->SVGTransform($tm);
24232 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
24233 $this->Line($x1, $y1, $x2, $y2);
24234 $this->StopTransform();
24243 $points = (isset($attribs['points'])?$attribs['points']:'0 0');
24244 $points = trim($points);
24245 // note that point may use a complex syntax not covered here
24246 $points = preg_split('/[\,\s]+/si', $points);
24247 if (count($points) < 4) {
24251 $xmin = 2147483647;
24253 $ymin = 2147483647;
24255 foreach ($points as $key => $val) {
24256 $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit
, false);
24257 if (($key %
2) == 0) {
24259 $xmin = min($xmin, $p[$key]);
24260 $xmax = max($xmax, $p[$key]);
24263 $ymin = min($ymin, $p[$key]);
24264 $ymax = max($ymax, $p[$key]);
24269 $w = ($xmax - $xmin);
24270 $h = ($ymax - $ymin);
24271 if ($name == 'polyline') {
24272 $this->StartTransform();
24273 $this->SVGTransform($tm);
24274 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
24275 if (!empty($obstyle)) {
24276 $this->PolyLine($p, $obstyle, array(), array());
24278 $this->StopTransform();
24279 } else { // polygon
24281 $this->SVGTransform($tm);
24282 $this->Polygon($p, 'CNZ', array(), array(), true);
24284 $this->StartTransform();
24285 $this->SVGTransform($tm);
24286 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
24287 if (!empty($obstyle)) {
24288 $this->Polygon($p, $obstyle, array(), array(), true);
24290 $this->StopTransform();
24300 if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
24303 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false):0);
24304 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false):0);
24305 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit
, false):0);
24306 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit
, false):0);
24307 $img = $attribs['xlink:href'];
24309 $this->StartTransform();
24310 $this->SVGTransform($tm);
24311 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
24312 if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
24313 // embedded image encoded as base64
24314 $img = '@'.base64_decode(substr($img, strlen($m[0])));
24317 if (!TCPDF_STATIC
::empty_string($this->svgdir
) AND (($img[0] == '.') OR (basename($img) == $img))) {
24318 // replace relative path with full server path
24319 $img = $this->svgdir
.'/'.$img;
24321 if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
24322 $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
24323 if (($findroot === false) OR ($findroot > 1)) {
24324 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
24325 $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
24327 $img = $_SERVER['DOCUMENT_ROOT'].$img;
24331 $img = urldecode($img);
24332 $testscrtype = @parse_url($img);
24333 if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
24334 // convert URL to server path
24335 $img = str_replace(K_PATH_URL
, K_PATH_MAIN
, $img);
24339 $imgtype = TCPDF_IMAGES
::getImageFileType($img);
24340 if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
24341 $this->ImageEps($img, $x, $y, $w, $h);
24342 } elseif ($imgtype == 'svg') {
24343 $this->ImageSVG($img, $x, $y, $w, $h);
24345 $this->Image($img, $x, $y, $w, $h);
24347 $this->StopTransform();
24354 // only basic support - advanced features must be implemented
24355 $this->svgtextmode
['invisible'] = $invisible;
24359 array_push($this->svgstyles
, $svgstyle);
24360 if (isset($attribs['x'])) {
24361 $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false);
24362 } elseif ($name == 'tspan') {
24367 if (isset($attribs['dx'])) {
24368 $x +
= $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit
, false);
24370 if (isset($attribs['y'])) {
24371 $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false);
24372 } elseif ($name == 'tspan') {
24377 if (isset($attribs['dy'])) {
24378 $y +
= $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit
, false);
24380 $svgstyle['text-color'] = $svgstyle['fill'];
24381 $this->svgtext
= '';
24382 if (isset($svgstyle['text-anchor'])) {
24383 $this->svgtextmode
['text-anchor'] = $svgstyle['text-anchor'];
24385 $this->svgtextmode
['text-anchor'] = 'start';
24387 if (isset($svgstyle['direction'])) {
24388 if ($svgstyle['direction'] == 'rtl') {
24389 $this->svgtextmode
['rtl'] = true;
24391 $this->svgtextmode
['rtl'] = false;
24394 $this->svgtextmode
['rtl'] = false;
24396 if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
24397 $this->svgtextmode
['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit
, false);
24399 $this->svgtextmode
['stroke'] = false;
24401 $this->StartTransform();
24402 $this->SVGTransform($tm);
24403 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
24410 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24411 $svgdefid = substr($attribs['xlink:href'], 1);
24412 if (isset($this->svgdefs
[$svgdefid])) {
24413 $use = $this->svgdefs
[$svgdefid];
24414 if (isset($attribs['xlink:href'])) {
24415 unset($attribs['xlink:href']);
24417 if (isset($attribs['id'])) {
24418 unset($attribs['id']);
24420 if (isset($use['attribs']['x']) AND isset($attribs['x'])) {
24421 $attribs['x'] +
= $use['attribs']['x'];
24423 if (isset($use['attribs']['y']) AND isset($attribs['y'])) {
24424 $attribs['y'] +
= $use['attribs']['y'];
24426 if (empty($attribs['style'])) {
24427 $attribs['style'] = '';
24429 if (!empty($use['attribs']['style'])) {
24431 $attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']);
24433 $attribs = array_merge($use['attribs'], $attribs);
24434 $this->startSVGElementHandler($parser, $use['name'], $attribs);
24444 // process child elements
24445 if (!empty($attribs['child_elements'])) {
24446 $child_elements = $attribs['child_elements'];
24447 unset($attribs['child_elements']);
24448 foreach($child_elements as $child_element) {
24449 if (empty($child_element['attribs']['closing_tag'])) {
24450 $this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']);
24452 if (isset($child_element['attribs']['content'])) {
24453 $this->svgtext
= $child_element['attribs']['content'];
24455 $this->endSVGElementHandler('child-tag', $child_element['name']);
24462 * Sets the closing SVG element handler function for the XML parser.
24463 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
24464 * @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.
24465 * @author Nicola Asuni
24466 * @since 5.0.000 (2010-05-02)
24469 protected function endSVGElementHandler($parser, $name) {
24470 if ($this->svgdefsmode
AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {;
24471 if (end($this->svgdefs
) !== FALSE) {
24472 $last_svgdefs_id = key($this->svgdefs
);
24473 if (isset($this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'])) {
24474 foreach($this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) {
24475 if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) {
24476 $this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext
));
24480 if ($this->svgdefs
[$last_svgdefs_id]['name'] == $name) {
24481 $this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext
));
24490 $this->svgdefsmode
= false;
24495 $this->svgclipmode
= false;
24499 // ungroup: remove last style from array
24500 array_pop($this->svgstyles
);
24501 $this->StopTransform();
24506 if ($this->svgtextmode
['invisible']) {
24507 // This implementation must be fixed to following the rule:
24508 // 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.
24512 $text = $this->svgtext
;
24513 //$text = $this->stringTrim($text);
24514 $textlen = $this->GetStringWidth($text);
24515 if ($this->svgtextmode
['text-anchor'] != 'start') {
24516 // check if string is RTL text
24517 if ($this->svgtextmode
['text-anchor'] == 'end') {
24518 if ($this->svgtextmode
['rtl']) {
24519 $this->x +
= $textlen;
24521 $this->x
-= $textlen;
24523 } elseif ($this->svgtextmode
['text-anchor'] == 'middle') {
24524 if ($this->svgtextmode
['rtl']) {
24525 $this->x +
= ($textlen / 2);
24527 $this->x
-= ($textlen / 2);
24531 $textrendermode = $this->textrendermode
;
24532 $textstrokewidth = $this->textstrokewidth
;
24533 $this->setTextRenderingMode($this->svgtextmode
['stroke'], true, false);
24534 if ($name == 'text') {
24535 // store current coordinates
24539 $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
24540 if ($name == 'text') {
24541 // restore coordinates
24545 // restore previous rendering mode
24546 $this->textrendermode
= $textrendermode;
24547 $this->textstrokewidth
= $textstrokewidth;
24548 $this->svgtext
= '';
24549 $this->StopTransform();
24550 if (!$this->svgdefsmode
) {
24551 array_pop($this->svgstyles
);
24562 * Sets the character data handler function for the XML parser.
24563 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
24564 * @param $data (string) The second parameter, data, contains the character data as a string.
24565 * @author Nicola Asuni
24566 * @since 5.0.000 (2010-05-02)
24569 protected function segSVGContentHandler($parser, $data) {
24570 $this->svgtext
.= $data;
24573 // --- END SVG METHODS -----------------------------------------------------
24575 } // END OF TCPDF CLASS
24577 //============================================================+
24579 //============================================================+