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 if (defined('K_TCPDF_THROW_EXCEPTION_ERROR') AND !K_TCPDF_THROW_EXCEPTION_ERROR
) {
2931 die('<strong>TCPDF ERROR: </strong>'.$msg);
2933 throw new Exception('TCPDF ERROR: '.$msg);
2938 * This method begins the generation of the PDF document.
2939 * It is not necessary to call it explicitly because AddPage() does it automatically.
2940 * Note: no page is created by this method
2943 * @see AddPage(), Close()
2945 public function Open() {
2950 * Terminates the PDF document.
2951 * It is not necessary to call this method explicitly because Output() does it automatically.
2952 * If the document contains no page, AddPage() is called to prevent from getting an invalid document.
2955 * @see Open(), Output()
2957 public function Close() {
2958 if ($this->state
== 3) {
2961 if ($this->page
== 0) {
2965 if ($this->tcpdflink
) {
2966 // save current graphic settings
2967 $gvars = $this->getGraphicVars();
2968 $this->setEqualColumns();
2969 $this->lastpage(true);
2970 $this->SetAutoPageBreak(false);
2972 $this->y
= $this->h
- (1 / $this->k
);
2974 $this->_outSaveGraphicsState();
2975 $font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN
:'helvetica';
2976 $this->SetFont($font, '', 1);
2977 $this->setTextRenderingMode(0, false, false);
2978 $msg = "\x50\x6f\x77\x65\x72\x65\x64\x20\x62\x79\x20\x54\x43\x50\x44\x46\x20\x28\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29";
2979 $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
2980 $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
2981 $this->_outRestoreGraphicsState();
2982 // restore graphic settings
2983 $this->setGraphicVars($gvars);
2989 // unset all class variables (except critical ones)
2990 $this->_destroy(false);
2994 * Move pointer at the specified document page and update page dimensions.
2995 * @param $pnum (int) page number (1 ... numpages)
2996 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
2998 * @since 2.1.000 (2008-01-07)
2999 * @see getPage(), lastpage(), getNumPages()
3001 public function setPage($pnum, $resetmargins=false) {
3002 if (($pnum == $this->page
) AND ($this->state
== 2)) {
3005 if (($pnum > 0) AND ($pnum <= $this->numpages
)) {
3007 // save current graphic settings
3008 //$gvars = $this->getGraphicVars();
3009 $oldpage = $this->page
;
3010 $this->page
= $pnum;
3011 $this->wPt
= $this->pagedim
[$this->page
]['w'];
3012 $this->hPt
= $this->pagedim
[$this->page
]['h'];
3013 $this->w
= $this->pagedim
[$this->page
]['wk'];
3014 $this->h
= $this->pagedim
[$this->page
]['hk'];
3015 $this->tMargin
= $this->pagedim
[$this->page
]['tm'];
3016 $this->bMargin
= $this->pagedim
[$this->page
]['bm'];
3017 $this->original_lMargin
= $this->pagedim
[$this->page
]['olm'];
3018 $this->original_rMargin
= $this->pagedim
[$this->page
]['orm'];
3019 $this->AutoPageBreak
= $this->pagedim
[$this->page
]['pb'];
3020 $this->CurOrientation
= $this->pagedim
[$this->page
]['or'];
3021 $this->SetAutoPageBreak($this->AutoPageBreak
, $this->bMargin
);
3022 // restore graphic settings
3023 //$this->setGraphicVars($gvars);
3024 if ($resetmargins) {
3025 $this->lMargin
= $this->pagedim
[$this->page
]['olm'];
3026 $this->rMargin
= $this->pagedim
[$this->page
]['orm'];
3027 $this->SetY($this->tMargin
);
3029 // account for booklet mode
3030 if ($this->pagedim
[$this->page
]['olm'] != $this->pagedim
[$oldpage]['olm']) {
3031 $deltam = $this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$this->page
]['orm'];
3032 $this->lMargin +
= $deltam;
3033 $this->rMargin
-= $deltam;
3037 $this->Error('Wrong page number on setPage() function: '.$pnum);
3042 * Reset pointer to the last document page.
3043 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
3045 * @since 2.0.000 (2008-01-04)
3046 * @see setPage(), getPage(), getNumPages()
3048 public function lastPage($resetmargins=false) {
3049 $this->setPage($this->getNumPages(), $resetmargins);
3053 * Get current document page number.
3054 * @return int page number
3056 * @since 2.1.000 (2008-01-07)
3057 * @see setPage(), lastpage(), getNumPages()
3059 public function getPage() {
3064 * Get the total number of insered pages.
3065 * @return int number of pages
3067 * @since 2.1.000 (2008-01-07)
3068 * @see setPage(), getPage(), lastpage()
3070 public function getNumPages() {
3071 return $this->numpages
;
3075 * Adds a new TOC (Table Of Content) page to the document.
3076 * @param $orientation (string) page orientation.
3077 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
3078 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
3080 * @since 5.0.001 (2010-05-06)
3081 * @see AddPage(), startPage(), endPage(), endTOCPage()
3083 public function addTOCPage($orientation='', $format='', $keepmargins=false) {
3084 $this->AddPage($orientation, $format, $keepmargins, true);
3088 * Terminate the current TOC (Table Of Content) page
3090 * @since 5.0.001 (2010-05-06)
3091 * @see AddPage(), startPage(), endPage(), addTOCPage()
3093 public function endTOCPage() {
3094 $this->endPage(true);
3098 * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer (if enabled). Then the page is added, the current position set to the top-left corner according to the left and top margins (or top-right if in RTL mode), and Header() is called to display the header (if enabled).
3099 * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.
3100 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3101 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
3102 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
3103 * @param $tocpage (boolean) if true set the tocpage state to true (the added page will be used to display Table Of Content).
3106 * @see startPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3108 public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
3109 if ($this->inxobj
) {
3110 // we are inside an XObject template
3113 if (!isset($this->original_lMargin
) OR $keepmargins) {
3114 $this->original_lMargin
= $this->lMargin
;
3116 if (!isset($this->original_rMargin
) OR $keepmargins) {
3117 $this->original_rMargin
= $this->rMargin
;
3119 // terminate previous page
3122 $this->startPage($orientation, $format, $tocpage);
3126 * Terminate the current page
3127 * @param $tocpage (boolean) if true set the tocpage state to false (end the page used to display Table Of Content).
3129 * @since 4.2.010 (2008-11-14)
3130 * @see AddPage(), startPage(), addTOCPage(), endTOCPage()
3132 public function endPage($tocpage=false) {
3133 // check if page is already closed
3134 if (($this->page
== 0) OR ($this->numpages
> $this->page
) OR (!$this->pageopen
[$this->page
])) {
3137 // print page footer
3141 // mark page as closed
3142 $this->pageopen
[$this->page
] = false;
3144 $this->tocpage
= false;
3149 * Starts a new page to the document. The page must be closed using the endPage() function.
3150 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
3151 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3152 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
3153 * @param $tocpage (boolean) if true the page is designated to contain the Table-Of-Content.
3154 * @since 4.2.010 (2008-11-14)
3155 * @see AddPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3158 public function startPage($orientation='', $format='', $tocpage=false) {
3160 $this->tocpage
= true;
3162 // move page numbers of documents to be attached
3163 if ($this->tocpage
) {
3164 // move reference to unexistent pages (used for page attachments)
3166 $tmpoutlines = $this->outlines
;
3167 foreach ($tmpoutlines as $key => $outline) {
3168 if (!$outline['f'] AND ($outline['p'] > $this->numpages
)) {
3169 $this->outlines
[$key]['p'] = ($outline['p'] +
1);
3173 $tmpdests = $this->dests
;
3174 foreach ($tmpdests as $key => $dest) {
3175 if (!$dest['f'] AND ($dest['p'] > $this->numpages
)) {
3176 $this->dests
[$key]['p'] = ($dest['p'] +
1);
3180 $tmplinks = $this->links
;
3181 foreach ($tmplinks as $key => $link) {
3182 if (!$link['f'] AND ($link['p'] > $this->numpages
)) {
3183 $this->links
[$key]['p'] = ($link['p'] +
1);
3187 if ($this->numpages
> $this->page
) {
3188 // this page has been already added
3189 $this->setPage($this->page +
1);
3190 $this->SetY($this->tMargin
);
3194 if ($this->state
== 0) {
3198 $this->swapMargins($this->booklet
);
3199 // save current graphic settings
3200 $gvars = $this->getGraphicVars();
3202 $this->_beginpage($orientation, $format);
3203 // mark page as open
3204 $this->pageopen
[$this->page
] = true;
3205 // restore graphic settings
3206 $this->setGraphicVars($gvars);
3208 $this->setPageMark();
3209 // print page header
3211 // restore graphic settings
3212 $this->setGraphicVars($gvars);
3214 $this->setPageMark();
3215 // print table header (if any)
3216 $this->setTableHeader();
3217 // set mark for empty page check
3218 $this->emptypagemrk
[$this->page
]= $this->pagelen
[$this->page
];
3222 * Set start-writing mark on current page stream used to put borders and fills.
3223 * Borders and fills are always created after content and inserted on the position marked by this method.
3224 * This function must be called after calling Image() function for a background image.
3225 * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
3227 * @since 4.0.016 (2008-07-30)
3229 public function setPageMark() {
3230 $this->intmrk
[$this->page
] = $this->pagelen
[$this->page
];
3231 $this->bordermrk
[$this->page
] = $this->intmrk
[$this->page
];
3232 $this->setContentMark();
3236 * Set start-writing mark on selected page.
3237 * Borders and fills are always created after content and inserted on the position marked by this method.
3238 * @param $page (int) page number (default is the current page)
3240 * @since 4.6.021 (2009-07-20)
3242 protected function setContentMark($page=0) {
3244 $page = $this->page
;
3246 if (isset($this->footerlen
[$page])) {
3247 $this->cntmrk
[$page] = $this->pagelen
[$page] - $this->footerlen
[$page];
3249 $this->cntmrk
[$page] = $this->pagelen
[$page];
3255 * @param $ln (string) header image logo
3256 * @param $lw (string) header image logo width in mm
3257 * @param $ht (string) string to print as title on document header
3258 * @param $hs (string) string to print on document header
3259 * @param $tc (array) RGB array color for text.
3260 * @param $lc (array) RGB array color for line.
3263 public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) {
3264 $this->header_logo
= $ln;
3265 $this->header_logo_width
= $lw;
3266 $this->header_title
= $ht;
3267 $this->header_string
= $hs;
3268 $this->header_text_color
= $tc;
3269 $this->header_line_color
= $lc;
3274 * @param $tc (array) RGB array color for text.
3275 * @param $lc (array) RGB array color for line.
3278 public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) {
3279 $this->footer_text_color
= $tc;
3280 $this->footer_line_color
= $lc;
3284 * Returns header data:
3285 * <ul><li>$ret['logo'] = logo image</li><li>$ret['logo_width'] = width of the image logo in user units</li><li>$ret['title'] = header title</li><li>$ret['string'] = header description string</li></ul>
3288 * @since 4.0.012 (2008-07-24)
3290 public function getHeaderData() {
3292 $ret['logo'] = $this->header_logo
;
3293 $ret['logo_width'] = $this->header_logo_width
;
3294 $ret['title'] = $this->header_title
;
3295 $ret['string'] = $this->header_string
;
3296 $ret['text_color'] = $this->header_text_color
;
3297 $ret['line_color'] = $this->header_line_color
;
3302 * Set header margin.
3303 * (minimum distance between header and top page margin)
3304 * @param $hm (int) distance in user units
3307 public function setHeaderMargin($hm=10) {
3308 $this->header_margin
= $hm;
3312 * Returns header margin in user units.
3314 * @since 4.0.012 (2008-07-24)
3317 public function getHeaderMargin() {
3318 return $this->header_margin
;
3322 * Set footer margin.
3323 * (minimum distance between footer and bottom page margin)
3324 * @param $fm (int) distance in user units
3327 public function setFooterMargin($fm=10) {
3328 $this->footer_margin
= $fm;
3332 * Returns footer margin in user units.
3334 * @since 4.0.012 (2008-07-24)
3337 public function getFooterMargin() {
3338 return $this->footer_margin
;
3341 * Set a flag to print page header.
3342 * @param $val (boolean) set to true to print the page header (default), false otherwise.
3345 public function setPrintHeader($val=true) {
3346 $this->print_header
= $val ?
true : false;
3350 * Set a flag to print page footer.
3351 * @param $val (boolean) set to true to print the page footer (default), false otherwise.
3354 public function setPrintFooter($val=true) {
3355 $this->print_footer
= $val ?
true : false;
3359 * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
3363 public function getImageRBX() {
3364 return $this->img_rb_x
;
3368 * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
3372 public function getImageRBY() {
3373 return $this->img_rb_y
;
3377 * Reset the xobject template used by Header() method.
3380 public function resetHeaderTemplate() {
3381 $this->header_xobjid
= false;
3385 * Set a flag to automatically reset the xobject template used by Header() method at each page.
3386 * @param $val (boolean) set to true to reset Header xobject template at each page, false otherwise.
3389 public function setHeaderTemplateAutoreset($val=true) {
3390 $this->header_xobj_autoreset
= $val ?
true : false;
3394 * This method is used to render the page header.
3395 * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3398 public function Header() {
3399 if ($this->header_xobjid
=== false) {
3400 // start a new XObject Template
3401 $this->header_xobjid
= $this->startTemplate($this->w
, $this->tMargin
);
3402 $headerfont = $this->getHeaderFont();
3403 $headerdata = $this->getHeaderData();
3404 $this->y
= $this->header_margin
;
3406 $this->x
= $this->w
- $this->original_rMargin
;
3408 $this->x
= $this->original_lMargin
;
3410 if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE
)) {
3411 $imgtype = TCPDF_IMAGES
::getImageFileType(K_PATH_IMAGES
.$headerdata['logo']);
3412 if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
3413 $this->ImageEps(K_PATH_IMAGES
.$headerdata['logo'], '', '', $headerdata['logo_width']);
3414 } elseif ($imgtype == 'svg') {
3415 $this->ImageSVG(K_PATH_IMAGES
.$headerdata['logo'], '', '', $headerdata['logo_width']);
3417 $this->Image(K_PATH_IMAGES
.$headerdata['logo'], '', '', $headerdata['logo_width']);
3419 $imgy = $this->getImageRBY();
3423 $cell_height = $this->getCellHeight($headerfont[2] / $this->k
);
3424 // set starting margin for text data cell
3425 if ($this->getRTL()) {
3426 $header_x = $this->original_rMargin +
($headerdata['logo_width'] * 1.1);
3428 $header_x = $this->original_lMargin +
($headerdata['logo_width'] * 1.1);
3430 $cw = $this->w
- $this->original_lMargin
- $this->original_rMargin
- ($headerdata['logo_width'] * 1.1);
3431 $this->SetTextColorArray($this->header_text_color
);
3433 $this->SetFont($headerfont[0], 'B', $headerfont[2] +
1);
3434 $this->SetX($header_x);
3435 $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
3437 $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
3438 $this->SetX($header_x);
3439 $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false);
3440 // print an ending header line
3441 $this->SetLineStyle(array('width' => 0.85 / $this->k
, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color']));
3442 $this->SetY((2.835 / $this->k
) +
max($imgy, $this->y
));
3444 $this->SetX($this->original_rMargin
);
3446 $this->SetX($this->original_lMargin
);
3448 $this->Cell(($this->w
- $this->original_lMargin
- $this->original_rMargin
), 0, '', 'T', 0, 'C');
3449 $this->endTemplate();
3451 // print header template
3454 if (!$this->header_xobj_autoreset
AND $this->booklet
AND (($this->page %
2) == 0)) {
3455 // adjust margins for booklet mode
3456 $dx = ($this->original_lMargin
- $this->original_rMargin
);
3459 $x = $this->w +
$dx;
3463 $this->printTemplate($this->header_xobjid
, $x, 0, 0, 0, '', '', false);
3464 if ($this->header_xobj_autoreset
) {
3465 // reset header xobject template at each page
3466 $this->header_xobjid
= false;
3471 * This method is used to render the page footer.
3472 * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3475 public function Footer() {
3477 $this->SetTextColorArray($this->footer_text_color
);
3478 //set style for cell border
3479 $line_width = (0.85 / $this->k
);
3480 $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color
));
3481 //print document barcode
3482 $barcode = $this->getBarcode();
3483 if (!empty($barcode)) {
3484 $this->Ln($line_width);
3485 $barcode_width = round(($this->w
- $this->original_lMargin
- $this->original_rMargin
) / 3);
3487 'position' => $this->rtl?
'R':'L',
3488 'align' => $this->rtl?
'R':'L',
3491 'cellfitalign' => '',
3494 'fgcolor' => array(0,0,0),
3498 $this->write1DBarcode($barcode, 'C128', '', $cur_y +
$line_width, '', (($this->footer_margin
/ 3) - $line_width), 0.3, $style, '');
3500 $w_page = isset($this->l
['w_page']) ?
$this->l
['w_page'].' ' : '';
3501 if (empty($this->pagegroups
)) {
3502 $pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
3504 $pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
3506 $this->SetY($cur_y);
3508 if ($this->getRTL()) {
3509 $this->SetX($this->original_rMargin
);
3510 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
3512 $this->SetX($this->original_lMargin
);
3513 $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R');
3518 * This method is used to render the page header.
3520 * @since 4.0.012 (2008-07-24)
3522 protected function setHeader() {
3523 if (!$this->print_header
OR ($this->state
!= 2)) {
3526 $this->InHeader
= true;
3527 $this->setGraphicVars($this->default_graphic_vars
);
3528 $temp_thead = $this->thead
;
3529 $temp_theadMargins = $this->theadMargins
;
3530 $lasth = $this->lasth
;
3531 $newline = $this->newline
;
3532 $this->_outSaveGraphicsState();
3533 $this->rMargin
= $this->original_rMargin
;
3534 $this->lMargin
= $this->original_lMargin
;
3535 $this->SetCellPadding(0);
3536 //set current position
3538 $this->SetXY($this->original_rMargin
, $this->header_margin
);
3540 $this->SetXY($this->original_lMargin
, $this->header_margin
);
3542 $this->SetFont($this->header_font
[0], $this->header_font
[1], $this->header_font
[2]);
3546 $this->SetXY($this->original_rMargin
, $this->tMargin
);
3548 $this->SetXY($this->original_lMargin
, $this->tMargin
);
3550 $this->_outRestoreGraphicsState();
3551 $this->lasth
= $lasth;
3552 $this->thead
= $temp_thead;
3553 $this->theadMargins
= $temp_theadMargins;
3554 $this->newline
= $newline;
3555 $this->InHeader
= false;
3559 * This method is used to render the page footer.
3561 * @since 4.0.012 (2008-07-24)
3563 protected function setFooter() {
3564 if ($this->state
!= 2) {
3567 $this->InFooter
= true;
3568 // save current graphic settings
3569 $gvars = $this->getGraphicVars();
3571 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
];
3573 if ($this->print_footer
) {
3574 $this->setGraphicVars($this->default_graphic_vars
);
3575 $this->current_column
= 0;
3576 $this->num_columns
= 1;
3577 $temp_thead = $this->thead
;
3578 $temp_theadMargins = $this->theadMargins
;
3579 $lasth = $this->lasth
;
3580 $this->_outSaveGraphicsState();
3581 $this->rMargin
= $this->original_rMargin
;
3582 $this->lMargin
= $this->original_lMargin
;
3583 $this->SetCellPadding(0);
3584 //set current position
3585 $footer_y = $this->h
- $this->footer_margin
;
3587 $this->SetXY($this->original_rMargin
, $footer_y);
3589 $this->SetXY($this->original_lMargin
, $footer_y);
3591 $this->SetFont($this->footer_font
[0], $this->footer_font
[1], $this->footer_font
[2]);
3595 $this->SetXY($this->original_rMargin
, $this->tMargin
);
3597 $this->SetXY($this->original_lMargin
, $this->tMargin
);
3599 $this->_outRestoreGraphicsState();
3600 $this->lasth
= $lasth;
3601 $this->thead
= $temp_thead;
3602 $this->theadMargins
= $temp_theadMargins;
3604 // restore graphic settings
3605 $this->setGraphicVars($gvars);
3606 $this->current_column
= $gvars['current_column'];
3607 $this->num_columns
= $gvars['num_columns'];
3608 // calculate footer length
3609 $this->footerlen
[$this->page
] = $this->pagelen
[$this->page
] - $this->footerpos
[$this->page
] +
1;
3610 $this->InFooter
= false;
3614 * Check if we are on the page body (excluding page header and footer).
3615 * @return true if we are not in page header nor in page footer, false otherwise.
3617 * @since 5.9.091 (2011-06-15)
3619 protected function inPageBody() {
3620 return (($this->InHeader
=== false) AND ($this->InFooter
=== false));
3624 * This method is used to render the table header on new page (if any).
3626 * @since 4.5.030 (2009-03-25)
3628 protected function setTableHeader() {
3629 if ($this->num_columns
> 1) {
3630 // multi column mode
3633 if (isset($this->theadMargins
['top'])) {
3634 // restore the original top-margin
3635 $this->tMargin
= $this->theadMargins
['top'];
3636 $this->pagedim
[$this->page
]['tm'] = $this->tMargin
;
3637 $this->y
= $this->tMargin
;
3639 if (!TCPDF_STATIC
::empty_string($this->thead
) AND (!$this->inthead
)) {
3641 $prev_lMargin = $this->lMargin
;
3642 $prev_rMargin = $this->rMargin
;
3643 $prev_cell_padding = $this->cell_padding
;
3644 $this->lMargin
= $this->theadMargins
['lmargin'] +
($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$this->theadMargins
['page']]['olm']);
3645 $this->rMargin
= $this->theadMargins
['rmargin'] +
($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$this->theadMargins
['page']]['orm']);
3646 $this->cell_padding
= $this->theadMargins
['cell_padding'];
3648 $this->x
= $this->w
- $this->rMargin
;
3650 $this->x
= $this->lMargin
;
3652 // account for special "cell" mode
3653 if ($this->theadMargins
['cell']) {
3655 $this->x
-= $this->cell_padding
['R'];
3657 $this->x +
= $this->cell_padding
['L'];
3660 $gvars = $this->getGraphicVars();
3661 if (!empty($this->theadMargins
['gvars'])) {
3662 // set the correct graphic style
3663 $this->setGraphicVars($this->theadMargins
['gvars']);
3664 $this->rMargin
= $gvars['rMargin'];
3665 $this->lMargin
= $gvars['lMargin'];
3667 // print table header
3668 $this->writeHTML($this->thead
, false, false, false, false, '');
3669 $this->setGraphicVars($gvars);
3670 // set new top margin to skip the table headers
3671 if (!isset($this->theadMargins
['top'])) {
3672 $this->theadMargins
['top'] = $this->tMargin
;
3674 // store end of header position
3675 if (!isset($this->columns
[0]['th'])) {
3676 $this->columns
[0]['th'] = array();
3678 $this->columns
[0]['th']['\''.$this->page
.'\''] = $this->y
;
3679 $this->tMargin
= $this->y
;
3680 $this->pagedim
[$this->page
]['tm'] = $this->tMargin
;
3682 $this->lMargin
= $prev_lMargin;
3683 $this->rMargin
= $prev_rMargin;
3684 $this->cell_padding
= $prev_cell_padding;
3689 * Returns the current page number.
3690 * @return int page number
3693 * @see getAliasNbPages()
3695 public function PageNo() {
3700 * Returns the array of spot colors.
3701 * @return (array) Spot colors array.
3703 * @since 6.0.038 (2013-09-30)
3705 public function getAllSpotColors() {
3706 return $this->spot_colors
;
3710 * Defines a new spot color.
3711 * It can be expressed in RGB components or gray scale.
3712 * The method can be called before the first page is created and the value is retained from page to page.
3713 * @param $name (string) Full name of the spot color.
3714 * @param $c (float) Cyan color for CMYK. Value between 0 and 100.
3715 * @param $m (float) Magenta color for CMYK. Value between 0 and 100.
3716 * @param $y (float) Yellow color for CMYK. Value between 0 and 100.
3717 * @param $k (float) Key (Black) color for CMYK. Value between 0 and 100.
3719 * @since 4.0.024 (2008-09-12)
3720 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3722 public function AddSpotColor($name, $c, $m, $y, $k) {
3723 if (!isset($this->spot_colors
[$name])) {
3724 $i = (1 +
count($this->spot_colors
));
3725 $this->spot_colors
[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i);
3730 * Set the spot color for the specified type ('draw', 'fill', 'text').
3731 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3732 * @param $name (string) Name of the spot color.
3733 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3734 * @return (string) PDF color command.
3736 * @since 5.9.125 (2011-10-03)
3738 public function setSpotColor($type, $name, $tint=100) {
3739 $spotcolor = TCPDF_COLORS
::getSpotColor($name, $this->spot_colors
);
3740 if ($spotcolor === false) {
3741 $this->Error('Undefined spot color: '.$name.', you must add it using the AddSpotColor() method.');
3743 $tint = (max(0, min(100, $tint)) / 100);
3744 $pdfcolor = sprintf('/CS%d ', $this->spot_colors
[$name]['i']);
3747 $pdfcolor .= sprintf('CS %F SCN', $tint);
3748 $this->DrawColor
= $pdfcolor;
3749 $this->strokecolor
= $spotcolor;
3753 $pdfcolor .= sprintf('cs %F scn', $tint);
3754 $this->FillColor
= $pdfcolor;
3755 $this->bgcolor
= $spotcolor;
3759 $pdfcolor .= sprintf('cs %F scn', $tint);
3760 $this->TextColor
= $pdfcolor;
3761 $this->fgcolor
= $spotcolor;
3765 $this->ColorFlag
= ($this->FillColor
!= $this->TextColor
);
3766 if ($this->state
== 2) {
3767 $this->_out($pdfcolor);
3769 if ($this->inxobj
) {
3770 // we are inside an XObject template
3771 $this->xobjects
[$this->xobjid
]['spot_colors'][$name] = $this->spot_colors
[$name];
3777 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
3778 * @param $name (string) Name of the spot color.
3779 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3781 * @since 4.0.024 (2008-09-12)
3782 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3784 public function SetDrawSpotColor($name, $tint=100) {
3785 $this->setSpotColor('draw', $name, $tint);
3789 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
3790 * @param $name (string) Name of the spot color.
3791 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3793 * @since 4.0.024 (2008-09-12)
3794 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
3796 public function SetFillSpotColor($name, $tint=100) {
3797 $this->setSpotColor('fill', $name, $tint);
3801 * Defines the spot color used for text.
3802 * @param $name (string) Name of the spot color.
3803 * @param $tint (int) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3805 * @since 4.0.024 (2008-09-12)
3806 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
3808 public function SetTextSpotColor($name, $tint=100) {
3809 $this->setSpotColor('text', $name, $tint);
3813 * Set the color array for the specified type ('draw', 'fill', 'text').
3814 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3815 * The method can be called before the first page is created and the value is retained from page to page.
3816 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3817 * @param $color (array) Array of colors (1=gray, 3=RGB, 4=CMYK or 5=spotcolor=CMYK+name values).
3818 * @param $ret (boolean) If true do not send the PDF command.
3819 * @return (string) The PDF command or empty string.
3821 * @since 3.1.000 (2008-06-11)
3823 public function setColorArray($type, $color, $ret=false) {
3824 if (is_array($color)) {
3825 $color = array_values($color);
3826 // component: grey, RGB red or CMYK cyan
3827 $c = isset($color[0]) ?
$color[0] : -1;
3828 // component: RGB green or CMYK magenta
3829 $m = isset($color[1]) ?
$color[1] : -1;
3830 // component: RGB blue or CMYK yellow
3831 $y = isset($color[2]) ?
$color[2] : -1;
3832 // component: CMYK black
3833 $k = isset($color[3]) ?
$color[3] : -1;
3835 $name = isset($color[4]) ?
$color[4] : '';
3837 return $this->setColor($type, $c, $m, $y, $k, $ret, $name);
3844 * Defines the color used for all drawing operations (lines, rectangles and cell borders).
3845 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3846 * The method can be called before the first page is created and the value is retained from page to page.
3847 * @param $color (array) Array of colors (1, 3 or 4 values).
3848 * @param $ret (boolean) If true do not send the PDF command.
3849 * @return string the PDF command
3851 * @since 3.1.000 (2008-06-11)
3852 * @see SetDrawColor()
3854 public function SetDrawColorArray($color, $ret=false) {
3855 return $this->setColorArray('draw', $color, $ret);
3859 * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
3860 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3861 * The method can be called before the first page is created and the value is retained from page to page.
3862 * @param $color (array) Array of colors (1, 3 or 4 values).
3863 * @param $ret (boolean) If true do not send the PDF command.
3865 * @since 3.1.000 (2008-6-11)
3866 * @see SetFillColor()
3868 public function SetFillColorArray($color, $ret=false) {
3869 return $this->setColorArray('fill', $color, $ret);
3873 * Defines the color used for text. It can be expressed in RGB components or gray scale.
3874 * The method can be called before the first page is created and the value is retained from page to page.
3875 * @param $color (array) Array of colors (1, 3 or 4 values).
3876 * @param $ret (boolean) If true do not send the PDF command.
3878 * @since 3.1.000 (2008-6-11)
3879 * @see SetFillColor()
3881 public function SetTextColorArray($color, $ret=false) {
3882 return $this->setColorArray('text', $color, $ret);
3886 * Defines the color used by the specified type ('draw', 'fill', 'text').
3887 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3888 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3889 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3890 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3891 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3892 * @param $ret (boolean) If true do not send the command.
3893 * @param $name (string) spot color name (if any)
3894 * @return (string) The PDF command or empty string.
3896 * @since 5.9.125 (2011-10-03)
3898 public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3899 // set default values
3900 if (!is_numeric($col1)) {
3903 if (!is_numeric($col2)) {
3906 if (!is_numeric($col3)) {
3909 if (!is_numeric($col4)) {
3912 // set color by case
3914 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
3916 $col1 = max(0, min(255, $col1));
3917 $intcolor = array('G' => $col1);
3918 $pdfcolor = sprintf('%F ', ($col1 / 255));
3920 } elseif ($col4 == -1) {
3922 $col1 = max(0, min(255, $col1));
3923 $col2 = max(0, min(255, $col2));
3924 $col3 = max(0, min(255, $col3));
3925 $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
3926 $pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255));
3929 $col1 = max(0, min(100, $col1));
3930 $col2 = max(0, min(100, $col2));
3931 $col3 = max(0, min(100, $col3));
3932 $col4 = max(0, min(100, $col4));
3935 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
3936 $pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
3940 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
3941 $this->AddSpotColor($name, $col1, $col2, $col3, $col4);
3942 $pdfcolor = $this->setSpotColor($type, $name, 100);
3947 $pdfcolor .= strtoupper($suffix);
3948 $this->DrawColor
= $pdfcolor;
3949 $this->strokecolor
= $intcolor;
3953 $pdfcolor .= $suffix;
3954 $this->FillColor
= $pdfcolor;
3955 $this->bgcolor
= $intcolor;
3959 $pdfcolor .= $suffix;
3960 $this->TextColor
= $pdfcolor;
3961 $this->fgcolor
= $intcolor;
3965 $this->ColorFlag
= ($this->FillColor
!= $this->TextColor
);
3966 if (($type != 'text') AND ($this->state
== 2)) {
3968 $this->_out($pdfcolor);
3976 * Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
3977 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3978 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3979 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3980 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3981 * @param $ret (boolean) If true do not send the command.
3982 * @param $name (string) spot color name (if any)
3983 * @return string the PDF command
3986 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
3988 public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3989 return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name);
3993 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
3994 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3995 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3996 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3997 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3998 * @param $ret (boolean) If true do not send the command.
3999 * @param $name (string) Spot color name (if any).
4000 * @return (string) The PDF command.
4003 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
4005 public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4006 return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name);
4010 * Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
4011 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
4012 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
4013 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
4014 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
4015 * @param $ret (boolean) If true do not send the command.
4016 * @param $name (string) Spot color name (if any).
4017 * @return (string) Empty string.
4020 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
4022 public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4023 return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name);
4027 * Returns the length of a string in user unit. A font must be selected.<br>
4028 * @param $s (string) The string whose length is to be computed
4029 * @param $fontname (string) Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
4030 * @param $fontstyle (string) Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line-through</li><li>O: overline</li></ul> or any combination. The default value is regular.
4031 * @param $fontsize (float) Font size in points. The default value is the current size.
4032 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
4033 * @return mixed int total string length or array of characted widths
4034 * @author Nicola Asuni
4038 public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4039 return $this->GetArrStringWidth(TCPDF_FONTS
::utf8Bidi(TCPDF_FONTS
::UTF8StringToArray($s, $this->isunicode
, $this->CurrentFont
), $s, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
), $fontname, $fontstyle, $fontsize, $getarray);
4043 * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br>
4044 * @param $sa (string) The array of chars whose total length is to be computed
4045 * @param $fontname (string) Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
4046 * @param $fontstyle (string) Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line through</li><li>O: overline</li></ul> or any combination. The default value is regular.
4047 * @param $fontsize (float) Font size in points. The default value is the current size.
4048 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
4049 * @return mixed int total string length or array of characted widths
4050 * @author Nicola Asuni
4052 * @since 2.4.000 (2008-03-06)
4054 public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4055 // store current values
4056 if (!TCPDF_STATIC
::empty_string($fontname)) {
4057 $prev_FontFamily = $this->FontFamily
;
4058 $prev_FontStyle = $this->FontStyle
;
4059 $prev_FontSizePt = $this->FontSizePt
;
4060 $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false);
4062 // convert UTF-8 array to Latin1 if required
4063 if ($this->isunicode
AND (!$this->isUnicodeFont())) {
4064 $sa = TCPDF_FONTS
::UTF8ArrToLatin1Arr($sa);
4066 $w = 0; // total width
4067 $wa = array(); // array of characters widths
4068 foreach ($sa as $ck => $char) {
4070 $cw = $this->GetCharWidth($char, isset($sa[($ck +
1)]));
4074 // restore previous values
4075 if (!TCPDF_STATIC
::empty_string($fontname)) {
4076 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
4085 * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking).
4086 * @param $char (int) The char code whose length is to be returned
4087 * @param $notlast (boolean) If false ignore the font-spacing.
4088 * @return float char width
4089 * @author Nicola Asuni
4091 * @since 2.4.000 (2008-03-06)
4093 public function GetCharWidth($char, $notlast=true) {
4095 $chw = $this->getRawCharWidth($char);
4096 if (($this->font_spacing
< 0) OR (($this->font_spacing
> 0) AND $notlast)) {
4097 // increase/decrease font spacing
4098 $chw +
= $this->font_spacing
;
4100 if ($this->font_stretching
!= 100) {
4101 // fixed stretching mode
4102 $chw *= ($this->font_stretching
/ 100);
4108 * Returns the length of the char in user unit for the current font.
4109 * @param $char (int) The char code whose length is to be returned
4110 * @return float char width
4111 * @author Nicola Asuni
4113 * @since 5.9.000 (2010-09-28)
4115 public function getRawCharWidth($char) {
4117 // SHY character will not be printed
4120 if (isset($this->CurrentFont
['cw'][$char])) {
4121 $w = $this->CurrentFont
['cw'][$char];
4122 } elseif (isset($this->CurrentFont
['dw'])) {
4124 $w = $this->CurrentFont
['dw'];
4125 } elseif (isset($this->CurrentFont
['cw'][32])) {
4127 $w = $this->CurrentFont
['cw'][32];
4131 return $this->getAbsFontMeasure($w);
4135 * Returns the numbero of characters in a string.
4136 * @param $s (string) The input string.
4137 * @return int number of characters
4139 * @since 2.0.0001 (2008-01-07)
4141 public function GetNumChars($s) {
4142 if ($this->isUnicodeFont()) {
4143 return count(TCPDF_FONTS
::UTF8StringToArray($s, $this->isunicode
, $this->CurrentFont
));
4149 * Fill the list of available fonts ($this->fontlist).
4151 * @since 4.0.013 (2008-07-28)
4153 protected function getFontsList() {
4154 if (($fontsdir = opendir(TCPDF_FONTS
::_getfontpath())) !== false) {
4155 while (($file = readdir($fontsdir)) !== false) {
4156 if (substr($file, -4) == '.php') {
4157 array_push($this->fontlist
, strtolower(basename($file, '.php')));
4160 closedir($fontsdir);
4165 * Returns the unicode caracter specified by the value
4166 * @param $c (int) UTF-8 value
4167 * @return Returns the specified character.
4168 * @since 2.3.000 (2008-03-05)
4172 public function unichr($c) {
4173 return TCPDF_FONTS
::unichr($c, $this->isunicode
);
4177 * Convert and add the selected TrueType or Type1 font to the fonts folder (that must be writeable).
4178 * @param $fontfile (string) Font file (full path).
4179 * @param $fonttype (string) Font type. Leave empty for autodetect mode. Valid values are: TrueTypeUnicode, TrueType, Type1, CID0JP = CID-0 Japanese, CID0KR = CID-0 Korean, CID0CS = CID-0 Chinese Simplified, CID0CT = CID-0 Chinese Traditional.
4180 * @param $enc (string) Name of the encoding table to use. Leave empty for default mode. Omit this parameter for TrueType Unicode and symbolic fonts like Symbol or ZapfDingBats.
4181 * @param $flags (int) Unsigned 32-bit integer containing flags specifying various characteristics of the font (PDF32000:2008 - 9.8.2 Font Descriptor Flags): +1 for fixed font; +4 for symbol or +32 for non-symbol; +64 for italic. Fixed and Italic mode are generally autodetected so you have to set it to 32 = non-symbolic font (default) or 4 = symbolic font.
4182 * @param $outpath (string) Output path for generated font files (must be writeable by the web server). Leave empty for default font folder.
4183 * @param $platid (int) Platform ID for CMAP table to extract (when building a Unicode font for Windows this value should be 3, for Macintosh should be 1).
4184 * @param $encid (int) Encoding ID for CMAP table to extract (when building a Unicode font for Windows this value should be 1, for Macintosh should be 0). When Platform ID is 3, legal values for Encoding ID are: 0=Symbol, 1=Unicode, 2=ShiftJIS, 3=PRC, 4=Big5, 5=Wansung, 6=Johab, 7=Reserved, 8=Reserved, 9=Reserved, 10=UCS-4.
4185 * @param $addcbbox (boolean) If true includes the character bounding box information on the php font file.
4186 * @return (string) TCPDF font name.
4187 * @author Nicola Asuni
4188 * @since 5.9.123 (2010-09-30)
4192 public function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false) {
4193 return TCPDF_FONTS
::addTTFfont($fontfile, $fonttype, $enc, $flags, $outpath, $platid, $encid, $addcbbox);
4197 * Imports a TrueType, Type1, core, or CID0 font and makes it available.
4198 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
4199 * The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by K_PATH_FONTS if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated.
4200 * @param $family (string) Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
4201 * @param $style (string) Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul>
4202 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4203 * @return array containing the font data, or false in case of error.
4204 * @param $subset (mixed) if true embedd only a subset of the font (stores only the information related to the used characters); if false embedd full font; if 'default' uses the default value set using setFontSubsetting(). This option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font.
4207 * @see SetFont(), setFontSubsetting()
4209 public function AddFont($family, $style='', $fontfile='', $subset='default') {
4210 if ($subset === 'default') {
4211 $subset = $this->font_subsetting
;
4213 if ($this->pdfa_mode
) {
4216 if (TCPDF_STATIC
::empty_string($family)) {
4217 if (!TCPDF_STATIC
::empty_string($this->FontFamily
)) {
4218 $family = $this->FontFamily
;
4220 $this->Error('Empty font family');
4223 // move embedded styles on $style
4224 if (substr($family, -1) == 'I') {
4226 $family = substr($family, 0, -1);
4228 if (substr($family, -1) == 'B') {
4230 $family = substr($family, 0, -1);
4232 // normalize family name
4233 $family = strtolower($family);
4234 if ((!$this->isunicode
) AND ($family == 'arial')) {
4235 $family = 'helvetica';
4237 if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
4240 if ($this->pdfa_mode
AND (isset($this->CoreFonts
[$family]))) {
4241 // all fonts must be embedded
4242 $family = 'pdfa'.$family;
4244 $tempstyle = strtoupper($style);
4247 if (strpos($tempstyle, 'U') !== false) {
4248 $this->underline
= true;
4250 $this->underline
= false;
4252 // line-through (deleted)
4253 if (strpos($tempstyle, 'D') !== false) {
4254 $this->linethrough
= true;
4256 $this->linethrough
= false;
4259 if (strpos($tempstyle, 'O') !== false) {
4260 $this->overline
= true;
4262 $this->overline
= false;
4265 if (strpos($tempstyle, 'B') !== false) {
4269 if (strpos($tempstyle, 'I') !== false) {
4273 $fontkey = $family.$style;
4274 $font_style = $style.($this->underline ?
'U' : '').($this->linethrough ?
'D' : '').($this->overline ?
'O' : '');
4275 $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
4276 // check if the font has been already added
4277 $fb = $this->getFontBuffer($fontkey);
4278 if ($fb !== false) {
4279 if ($this->inxobj
) {
4280 // we are inside an XObject template
4281 $this->xobjects
[$this->xobjid
]['fonts'][$fontkey] = $fb['i'];
4285 // get specified font directory (if any)
4287 if (!TCPDF_STATIC
::empty_string($fontfile)) {
4288 $fontdir = dirname($fontfile);
4289 if (TCPDF_STATIC
::empty_string($fontdir) OR ($fontdir == '.')) {
4295 // true when the font style variation is missing
4296 $missing_style = false;
4297 // search and include font file
4298 if (TCPDF_STATIC
::empty_string($fontfile) OR (!@file_exists
($fontfile))) {
4299 // build a standard filenames for specified font
4300 $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
4301 $fontfile = TCPDF_FONTS
::getFontFullPath($tmp_fontfile, $fontdir);
4302 if (TCPDF_STATIC
::empty_string($fontfile)) {
4303 $missing_style = true;
4304 // try to remove the style part
4305 $tmp_fontfile = str_replace(' ', '', $family).'.php';
4306 $fontfile = TCPDF_FONTS
::getFontFullPath($tmp_fontfile, $fontdir);
4309 // include font file
4310 if (!TCPDF_STATIC
::empty_string($fontfile) AND (@file_exists
($fontfile))) {
4313 $this->Error('Could not include font definition file: '.$family.'');
4315 // check font parameters
4316 if ((!isset($type)) OR (!isset($cw))) {
4317 $this->Error('The font definition file has a bad format: '.$fontfile.'');
4319 // SET default parameters
4320 if (!isset($file) OR TCPDF_STATIC
::empty_string($file)) {
4323 if (!isset($enc) OR TCPDF_STATIC
::empty_string($enc)) {
4326 if (!isset($cidinfo) OR TCPDF_STATIC
::empty_string($cidinfo)) {
4327 $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0);
4328 $cidinfo['uni2cid'] = array();
4330 if (!isset($ctg) OR TCPDF_STATIC
::empty_string($ctg)) {
4333 if (!isset($desc) OR TCPDF_STATIC
::empty_string($desc)) {
4336 if (!isset($up) OR TCPDF_STATIC
::empty_string($up)) {
4339 if (!isset($ut) OR TCPDF_STATIC
::empty_string($ut)) {
4342 if (!isset($cw) OR TCPDF_STATIC
::empty_string($cw)) {
4345 if (!isset($dw) OR TCPDF_STATIC
::empty_string($dw)) {
4346 // set default width
4347 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
4348 $dw = $desc['MissingWidth'];
4349 } elseif (isset($cw[32])) {
4356 if ($type == 'core') {
4357 $name = $this->CoreFonts
[$fontkey];
4359 } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
4361 } elseif ($type == 'TrueTypeUnicode') {
4362 $enc = 'Identity-H';
4363 } elseif ($type == 'cidfont0') {
4364 if ($this->pdfa_mode
) {
4365 $this->Error('All fonts must be embedded in PDF/A mode!');
4368 $this->Error('Unknow font type: '.$type.'');
4370 // set name if unset
4371 if (!isset($name) OR empty($name)) {
4374 // create artificial font style variations if missing (only works with non-embedded fonts)
4375 if (($type != 'core') AND $missing_style) {
4377 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
4378 $name .= $styles[$bistyle];
4380 if (strpos($bistyle, 'B') !== false) {
4381 if (isset($desc['StemV'])) {
4382 // from normal to bold
4383 $desc['StemV'] = round($desc['StemV'] * 1.75);
4386 $desc['StemV'] = 123;
4389 // artificial italic
4390 if (strpos($bistyle, 'I') !== false) {
4391 if (isset($desc['ItalicAngle'])) {
4392 $desc['ItalicAngle'] -= 11;
4394 $desc['ItalicAngle'] = -11;
4396 if (isset($desc['Flags'])) {
4397 $desc['Flags'] |
= 64; //bit 7
4399 $desc['Flags'] = 64;
4403 // check if the array of characters bounding boxes is defined
4404 if (!isset($cbbox)) {
4407 // initialize subsetchars
4408 $subsetchars = array_fill(0, 255, true);
4409 $this->setFontBuffer($fontkey, array('fontkey' => $fontkey, 'i' => $this->numfonts
, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'cbbox' => $cbbox, 'dw' => $dw, 'enc' => $enc, 'cidinfo' => $cidinfo, 'file' => $file, 'ctg' => $ctg, 'subset' => $subset, 'subsetchars' => $subsetchars));
4410 if ($this->inxobj
) {
4411 // we are inside an XObject template
4412 $this->xobjects
[$this->xobjid
]['fonts'][$fontkey] = $this->numfonts
;
4414 if (isset($diff) AND (!empty($diff))) {
4415 //Search existing encodings
4417 $nb = count($this->diffs
);
4418 for ($i=1; $i <= $nb; ++
$i) {
4419 if ($this->diffs
[$i] == $diff) {
4426 $this->diffs
[$d] = $diff;
4428 $this->setFontSubBuffer($fontkey, 'diff', $d);
4430 if (!TCPDF_STATIC
::empty_string($file)) {
4431 if (!isset($this->FontFiles
[$file])) {
4432 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
4433 $this->FontFiles
[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4434 } elseif ($type != 'core') {
4435 $this->FontFiles
[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4438 // update fontkeys that are sharing this font file
4439 $this->FontFiles
[$file]['subset'] = ($this->FontFiles
[$file]['subset'] AND $subset);
4440 if (!in_array($fontkey, $this->FontFiles
[$file]['fontkeys'])) {
4441 $this->FontFiles
[$file]['fontkeys'][] = $fontkey;
4449 * Sets the font used to print character strings.
4450 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
4451 * The method can be called before the first page is created and the font is retained from page to page.
4452 * If you just wish to change the current font size, it is simpler to call SetFontSize().
4453 * Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:<ul><li>They are in the current directory (the one where the running script lies)</li><li>They are in one of the directories defined by the include_path parameter</li><li>They are in the directory defined by the K_PATH_FONTS constant</li></ul><br />
4454 * @param $family (string) Family font. It can be either a name defined by AddFont() or one of the standard Type1 families (case insensitive):<ul><li>times (Times-Roman)</li><li>timesb (Times-Bold)</li><li>timesi (Times-Italic)</li><li>timesbi (Times-BoldItalic)</li><li>helvetica (Helvetica)</li><li>helveticab (Helvetica-Bold)</li><li>helveticai (Helvetica-Oblique)</li><li>helveticabi (Helvetica-BoldOblique)</li><li>courier (Courier)</li><li>courierb (Courier-Bold)</li><li>courieri (Courier-Oblique)</li><li>courierbi (Courier-BoldOblique)</li><li>symbol (Symbol)</li><li>zapfdingbats (ZapfDingbats)</li></ul> It is also possible to pass an empty string. In that case, the current family is retained.
4455 * @param $style (string) Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line through</li><li>O: overline</li></ul> or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined.
4456 * @param $size (float) Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12
4457 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4458 * @param $subset (mixed) if true embedd only a subset of the font (stores only the information related to the used characters); if false embedd full font; if 'default' uses the default value set using setFontSubsetting(). This option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font.
4459 * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
4460 * @author Nicola Asuni
4463 * @see AddFont(), SetFontSize()
4465 public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) {
4466 //Select a font; size given in points
4467 if ($size === null) {
4468 $size = $this->FontSizePt
;
4473 // try to add font (if not already added)
4474 $fontdata = $this->AddFont($family, $style, $fontfile, $subset);
4475 $this->FontFamily
= $fontdata['family'];
4476 $this->FontStyle
= $fontdata['style'];
4477 if (isset($this->CurrentFont
['fontkey']) AND isset($this->CurrentFont
['subsetchars'])) {
4478 // save subset chars of the previous font
4479 $this->setFontSubBuffer($this->CurrentFont
['fontkey'], 'subsetchars', $this->CurrentFont
['subsetchars']);
4481 $this->CurrentFont
= $this->getFontBuffer($fontdata['fontkey']);
4482 $this->SetFontSize($size, $out);
4486 * Defines the size of the current font.
4487 * @param $size (float) The font size in points.
4488 * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
4493 public function SetFontSize($size, $out=true) {
4494 // font size in points
4495 $this->FontSizePt
= $size;
4496 // font size in user units
4497 $this->FontSize
= $size / $this->k
;
4498 // calculate some font metrics
4499 if (isset($this->CurrentFont
['desc']['FontBBox'])) {
4500 $bbox = explode(' ', substr($this->CurrentFont
['desc']['FontBBox'], 1, -1));
4501 $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
4503 $font_height = $size * 1.219;
4505 if (isset($this->CurrentFont
['desc']['Ascent']) AND ($this->CurrentFont
['desc']['Ascent'] > 0)) {
4506 $font_ascent = ($this->CurrentFont
['desc']['Ascent'] * $size / 1000);
4508 if (isset($this->CurrentFont
['desc']['Descent']) AND ($this->CurrentFont
['desc']['Descent'] <= 0)) {
4509 $font_descent = (- $this->CurrentFont
['desc']['Descent'] * $size / 1000);
4511 if (!isset($font_ascent) AND !isset($font_descent)) {
4513 $font_ascent = 0.76 * $font_height;
4514 $font_descent = $font_height - $font_ascent;
4515 } elseif (!isset($font_descent)) {
4516 $font_descent = $font_height - $font_ascent;
4517 } elseif (!isset($font_ascent)) {
4518 $font_ascent = $font_height - $font_descent;
4520 $this->FontAscent
= ($font_ascent / $this->k
);
4521 $this->FontDescent
= ($font_descent / $this->k
);
4522 if ($out AND ($this->page
> 0) AND (isset($this->CurrentFont
['i'])) AND ($this->state
== 2)) {
4523 $this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont
['i'], $this->FontSizePt
));
4528 * Returns the bounding box of the current font in user units.
4531 * @since 5.9.152 (2012-03-23)
4533 public function getFontBBox() {
4535 if (isset($this->CurrentFont
['desc']['FontBBox'])) {
4536 $tmpbbox = explode(' ', substr($this->CurrentFont
['desc']['FontBBox'], 1, -1));
4537 $fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox);
4540 if (isset($this->CurrentFont
['desc']['MaxWidth'])) {
4541 $maxw = $this->getAbsFontMeasure(intval($this->CurrentFont
['desc']['MaxWidth']));
4544 if (isset($this->CurrentFont
['desc']['MissingWidth'])) {
4545 $maxw = max($maxw, $this->CurrentFont
['desc']['MissingWidth']);
4547 if (isset($this->CurrentFont
['desc']['AvgWidth'])) {
4548 $maxw = max($maxw, $this->CurrentFont
['desc']['AvgWidth']);
4550 if (isset($this->CurrentFont
['dw'])) {
4551 $maxw = max($maxw, $this->CurrentFont
['dw']);
4553 foreach ($this->CurrentFont
['cw'] as $char => $w) {
4554 $maxw = max($maxw, $w);
4559 $maxw = $this->getAbsFontMeasure($maxw);
4561 $fbbox = array(0, (0 - $this->FontDescent
), $maxw, $this->FontAscent
);
4567 * Convert a relative font measure into absolute value.
4568 * @param $s (int) Font measure.
4569 * @return float Absolute measure.
4570 * @since 5.9.186 (2012-09-13)
4572 public function getAbsFontMeasure($s) {
4573 return ($s * $this->FontSize
/ 1000);
4577 * Returns the glyph bounding box of the specified character in the current font in user units.
4578 * @param $char (int) Input character code.
4579 * @return mixed array(xMin, yMin, xMax, yMax) or FALSE if not defined.
4580 * @since 5.9.186 (2012-09-13)
4582 public function getCharBBox($char) {
4583 if (isset($this->CurrentFont
['cbbox'][$char])) {
4584 return array_map(array($this,'getAbsFontMeasure'), $this->CurrentFont
['cbbox'][intval($char)]);
4590 * Return the font descent value
4591 * @param $font (string) font name
4592 * @param $style (string) font style
4593 * @param $size (float) The size (in points)
4594 * @return int font descent
4596 * @author Nicola Asuni
4597 * @since 4.9.003 (2010-03-30)
4599 public function getFontDescent($font, $style='', $size=0) {
4600 $fontdata = $this->AddFont($font, $style);
4601 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4602 if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
4603 $descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
4605 $descent = (1.219 * 0.24 * $size);
4607 return ($descent / $this->k
);
4611 * Return the font ascent value.
4612 * @param $font (string) font name
4613 * @param $style (string) font style
4614 * @param $size (float) The size (in points)
4615 * @return int font ascent
4617 * @author Nicola Asuni
4618 * @since 4.9.003 (2010-03-30)
4620 public function getFontAscent($font, $style='', $size=0) {
4621 $fontdata = $this->AddFont($font, $style);
4622 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4623 if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
4624 $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
4626 $ascent = 1.219 * 0.76 * $size;
4628 return ($ascent / $this->k
);
4632 * Return true in the character is present in the specified font.
4633 * @param $char (mixed) Character to check (integer value or string)
4634 * @param $font (string) Font name (family name).
4635 * @param $style (string) Font style.
4636 * @return (boolean) true if the char is defined, false otherwise.
4638 * @since 5.9.153 (2012-03-28)
4640 public function isCharDefined($char, $font='', $style='') {
4641 if (is_string($char)) {
4642 // get character code
4643 $char = TCPDF_FONTS
::UTF8StringToArray($char, $this->isunicode
, $this->CurrentFont
);
4646 if (TCPDF_STATIC
::empty_string($font)) {
4647 if (TCPDF_STATIC
::empty_string($style)) {
4648 return (isset($this->CurrentFont
['cw'][intval($char)]));
4650 $font = $this->FontFamily
;
4652 $fontdata = $this->AddFont($font, $style);
4653 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4654 return (isset($fontinfo['cw'][intval($char)]));
4658 * Replace missing font characters on selected font with specified substitutions.
4659 * @param $text (string) Text to process.
4660 * @param $font (string) Font name (family name).
4661 * @param $style (string) Font style.
4662 * @param $subs (array) Array of possible character substitutions. The key is the character to check (integer value) and the value is a single intege value or an array of possible substitutes.
4663 * @return (string) Processed text.
4665 * @since 5.9.153 (2012-03-28)
4667 public function replaceMissingChars($text, $font='', $style='', $subs=array()) {
4671 if (TCPDF_STATIC
::empty_string($font)) {
4672 $font = $this->FontFamily
;
4674 $fontdata = $this->AddFont($font, $style);
4675 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4676 $uniarr = TCPDF_FONTS
::UTF8StringToArray($text, $this->isunicode
, $this->CurrentFont
);
4677 foreach ($uniarr as $k => $chr) {
4678 if (!isset($fontinfo['cw'][$chr])) {
4679 // this character is missing on the selected font
4680 if (isset($subs[$chr])) {
4681 // we have available substitutions
4682 if (is_array($subs[$chr])) {
4683 foreach($subs[$chr] as $s) {
4684 if (isset($fontinfo['cw'][$s])) {
4689 } elseif (isset($fontinfo['cw'][$subs[$chr]])) {
4690 $uniarr[$k] = $subs[$chr];
4695 return TCPDF_FONTS
::UniArrSubString(TCPDF_FONTS
::UTF8ArrayToUniArray($uniarr, $this->isunicode
));
4699 * Defines the default monospaced font.
4700 * @param $font (string) Font name.
4704 public function SetDefaultMonospacedFont($font) {
4705 $this->default_monospaced_font
= $font;
4709 * Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.<br />
4710 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
4713 * @see Cell(), Write(), Image(), Link(), SetLink()
4715 public function AddLink() {
4716 // create a new internal link
4717 $n = count($this->links
) +
1;
4718 $this->links
[$n] = array('p' => 0, 'y' => 0, 'f' => false);
4723 * Defines the page and position a link points to.
4724 * @param $link (int) The link identifier returned by AddLink()
4725 * @param $y (float) Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
4726 * @param $page (int) Number of target page; -1 indicates the current page (default value). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages.
4731 public function SetLink($link, $y=0, $page=-1) {
4733 if (!empty($page) AND ($page[0] == '*')) {
4734 $page = intval(substr($page, 1));
4735 // this page number will not be changed when moving/add/deleting pages
4739 $page = $this->page
;
4744 $this->links
[$link] = array('p' => $page, 'y' => $y, 'f' => $fixed);
4748 * Puts a link on a rectangular area of the page.
4749 * Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image.
4750 * @param $x (float) Abscissa of the upper-left corner of the rectangle
4751 * @param $y (float) Ordinate of the upper-left corner of the rectangle
4752 * @param $w (float) Width of the rectangle
4753 * @param $h (float) Height of the rectangle
4754 * @param $link (mixed) URL or identifier returned by AddLink()
4755 * @param $spaces (int) number of spaces on the text to link
4758 * @see AddLink(), Annotation(), Cell(), Write(), Image()
4760 public function Link($x, $y, $w, $h, $link, $spaces=0) {
4761 $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
4765 * Puts a markup annotation on a rectangular area of the page.
4766 * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!
4767 * @param $x (float) Abscissa of the upper-left corner of the rectangle
4768 * @param $y (float) Ordinate of the upper-left corner of the rectangle
4769 * @param $w (float) Width of the rectangle
4770 * @param $h (float) Height of the rectangle
4771 * @param $text (string) annotation text or alternate content
4772 * @param $opt (array) array of options (see section 8.4 of PDF reference 1.7).
4773 * @param $spaces (int) number of spaces on the text to link
4775 * @since 4.0.018 (2008-08-06)
4777 public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
4778 if ($this->inxobj
) {
4779 // store parameters for later use on template
4780 $this->xobjects
[$this->xobjid
]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
4789 // check page for no-write regions and adapt page margins if necessary
4790 list($x, $y) = $this->checkPageRegions($h, $x, $y);
4791 // recalculate coordinates to account for graphic transformations
4792 if (isset($this->transfmatrix
) AND !empty($this->transfmatrix
)) {
4793 for ($i=$this->transfmatrix_key
; $i > 0; --$i) {
4794 $maxid = count($this->transfmatrix
[$i]) - 1;
4795 for ($j=$maxid; $j >= 0; --$j) {
4796 $ctm = $this->transfmatrix
[$i][$j];
4797 if (isset($ctm['a'])) {
4799 $y = ($this->h
- $y) * $this->k
;
4805 $x1 = ($ctm['a'] * $xt) +
($ctm['c'] * $yt) +
$ctm['e'];
4806 $y1 = ($ctm['b'] * $xt) +
($ctm['d'] * $yt) +
$ctm['f'];
4810 $x2 = ($ctm['a'] * $xt) +
($ctm['c'] * $yt) +
$ctm['e'];
4811 $y2 = ($ctm['b'] * $xt) +
($ctm['d'] * $yt) +
$ctm['f'];
4815 $x3 = ($ctm['a'] * $xt) +
($ctm['c'] * $yt) +
$ctm['e'];
4816 $y3 = ($ctm['b'] * $xt) +
($ctm['d'] * $yt) +
$ctm['f'];
4820 $x4 = ($ctm['a'] * $xt) +
($ctm['c'] * $yt) +
$ctm['e'];
4821 $y4 = ($ctm['b'] * $xt) +
($ctm['d'] * $yt) +
$ctm['f'];
4822 // new coordinates (rectangle area)
4823 $x = min($x1, $x2, $x3, $x4);
4824 $y = max($y1, $y2, $y3, $y4);
4825 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k
;
4826 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k
;
4828 $y = $this->h
- ($y / $this->k
);
4833 if ($this->page
<= 0) {
4836 $page = $this->page
;
4838 if (!isset($this->PageAnnots
[$page])) {
4839 $this->PageAnnots
[$page] = array();
4841 $this->PageAnnots
[$page][] = array('n' => ++
$this->n
, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
4842 if (!$this->pdfa_mode
) {
4843 if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!TCPDF_STATIC
::empty_string($opt['FS']))
4844 AND (@file_exists
($opt['FS']) OR TCPDF_STATIC
::isValidURL($opt['FS']))
4845 AND (!isset($this->embeddedfiles
[basename($opt['FS'])]))) {
4846 $this->embeddedfiles
[basename($opt['FS'])] = array('f' => ++
$this->n
, 'n' => ++
$this->n
, 'file' => $opt['FS']);
4849 // Add widgets annotation's icons
4850 if (isset($opt['mk']['i']) AND @file_exists
($opt['mk']['i'])) {
4851 $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
4853 if (isset($opt['mk']['ri']) AND @file_exists
($opt['mk']['ri'])) {
4854 $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4856 if (isset($opt['mk']['ix']) AND @file_exists
($opt['mk']['ix'])) {
4857 $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4862 * Embedd the attached files.
4863 * @since 4.4.000 (2008-12-07)
4867 protected function _putEmbeddedFiles() {
4868 if ($this->pdfa_mode
) {
4869 // embedded files are not allowed in PDF/A mode
4872 reset($this->embeddedfiles
);
4873 foreach ($this->embeddedfiles
as $filename => $filedata) {
4874 $data = TCPDF_STATIC
::fileGetContents($filedata['file']);
4875 if ($data !== FALSE) {
4876 $rawsize = strlen($data);
4879 $this->efnames
[$filename] = $filedata['f'].' 0 R';
4880 // embedded file specification object
4881 $out = $this->_getobj($filedata['f'])."\n";
4882 $out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']).' /EF <</F '.$filedata['n'].' 0 R>> >>';
4883 $out .= "\n".'endobj';
4885 // embedded file object
4887 if ($this->compress
) {
4888 $data = gzcompress($data);
4889 $filter = ' /Filter /FlateDecode';
4891 $stream = $this->_getrawstream($data, $filedata['n']);
4892 $out = $this->_getobj($filedata['n'])."\n";
4893 $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>';
4894 $out .= ' stream'."\n".$stream."\n".'endstream';
4895 $out .= "\n".'endobj';
4903 * Prints a text cell at the specified position.
4904 * This method allows to place a string precisely on the page.
4905 * @param $x (float) Abscissa of the cell origin
4906 * @param $y (float) Ordinate of the cell origin
4907 * @param $txt (string) String to print
4908 * @param $fstroke (int) outline size in user units (false = disable)
4909 * @param $fclip (boolean) if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
4910 * @param $ffill (boolean) if true fills the text
4911 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
4912 * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
4913 * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
4914 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
4915 * @param $link (mixed) URL or identifier returned by AddLink().
4916 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
4917 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
4918 * @param $calign (string) cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li><li>B : cell bottom</li></ul>
4919 * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
4920 * @param $rtloff (boolean) if true uses the page top-left corner as origin of axis for $x and $y initial position.
4923 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()
4925 public function Text($x, $y, $txt, $fstroke=false, $fclip=false, $ffill=true, $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M', $rtloff=false) {
4926 $textrendermode = $this->textrendermode
;
4927 $textstrokewidth = $this->textstrokewidth
;
4928 $this->setTextRenderingMode($fstroke, $ffill, $fclip);
4929 $this->SetXY($x, $y, $rtloff);
4930 $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
4931 // restore previous rendering mode
4932 $this->textrendermode
= $textrendermode;
4933 $this->textstrokewidth
= $textstrokewidth;
4937 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
4938 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
4939 * This method is called automatically and should not be called directly by the application.
4943 * @see SetAutoPageBreak()
4945 public function AcceptPageBreak() {
4946 if ($this->num_columns
> 1) {
4947 // multi column mode
4948 if ($this->current_column
< ($this->num_columns
- 1)) {
4949 // go to next column
4950 $this->selectColumn($this->current_column +
1);
4951 } elseif ($this->AutoPageBreak
) {
4955 $this->selectColumn(0);
4957 // avoid page breaking from checkPageBreak()
4960 return $this->AutoPageBreak
;
4964 * Add page if needed.
4965 * @param $h (float) Cell height. Default value: 0.
4966 * @param $y (mixed) starting y position, leave empty for current position.
4967 * @param $addpage (boolean) if true add a page, otherwise only return the true/false state
4968 * @return boolean true in case of page break, false otherwise.
4969 * @since 3.2.000 (2008-07-01)
4972 protected function checkPageBreak($h=0, $y='', $addpage=true) {
4973 if (TCPDF_STATIC
::empty_string($y)) {
4976 $current_page = $this->page
;
4977 if ((($y +
$h) > $this->PageBreakTrigger
) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) {
4979 //Automatic page break
4981 $this->AddPage($this->CurOrientation
);
4982 $this->y
= $this->tMargin
;
4983 $oldpage = $this->page
- 1;
4985 if ($this->pagedim
[$this->page
]['orm'] != $this->pagedim
[$oldpage]['orm']) {
4986 $this->x
= $x - ($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$oldpage]['orm']);
4991 if ($this->pagedim
[$this->page
]['olm'] != $this->pagedim
[$oldpage]['olm']) {
4992 $this->x
= $x +
($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$oldpage]['olm']);
5000 if ($current_page != $this->page
) {
5001 // account for columns mode
5008 * Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
5009 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
5010 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
5011 * @param $h (float) Cell height. Default value: 0.
5012 * @param $txt (string) String to print. Default value: empty string.
5013 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5014 * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul> Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
5015 * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
5016 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5017 * @param $link (mixed) URL or identifier returned by AddLink().
5018 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
5019 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5020 * @param $calign (string) cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul>
5021 * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
5024 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
5026 public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') {
5027 $prev_cell_margin = $this->cell_margin
;
5028 $prev_cell_padding = $this->cell_padding
;
5029 $this->adjustCellPadding($border);
5030 if (!$ignore_min_height) {
5031 $min_cell_height = $this->getCellHeight($this->FontSize
);
5032 if ($h < $min_cell_height) {
5033 $h = $min_cell_height;
5036 $this->checkPageBreak($h +
$this->cell_margin
['T'] +
$this->cell_margin
['B']);
5037 // apply text shadow if enabled
5038 if ($this->txtshadow
['enabled']) {
5042 $bc = $this->bgcolor
;
5043 $fc = $this->fgcolor
;
5044 $sc = $this->strokecolor
;
5045 $alpha = $this->alpha
;
5047 $this->x +
= $this->txtshadow
['depth_w'];
5048 $this->y +
= $this->txtshadow
['depth_h'];
5049 $this->SetFillColorArray($this->txtshadow
['color']);
5050 $this->SetTextColorArray($this->txtshadow
['color']);
5051 $this->SetDrawColorArray($this->txtshadow
['color']);
5052 if ($this->txtshadow
['opacity'] != $alpha['CA']) {
5053 $this->setAlpha($this->txtshadow
['opacity'], $this->txtshadow
['blend_mode']);
5055 if ($this->state
== 2) {
5056 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5061 $this->SetFillColorArray($bc);
5062 $this->SetTextColorArray($fc);
5063 $this->SetDrawColorArray($sc);
5064 if ($this->txtshadow
['opacity'] != $alpha['CA']) {
5065 $this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']);
5068 if ($this->state
== 2) {
5069 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5071 $this->cell_padding
= $prev_cell_padding;
5072 $this->cell_margin
= $prev_cell_margin;
5076 * Returns the PDF string code to print a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
5077 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
5078 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
5079 * @param $h (float) Cell height. Default value: 0.
5080 * @param $txt (string) String to print. Default value: empty string.
5081 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5082 * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
5083 * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
5084 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5085 * @param $link (mixed) URL or identifier returned by AddLink().
5086 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
5087 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5088 * @param $calign (string) cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul>
5089 * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>M : middle</li><li>B : bottom</li></ul>
5090 * @return string containing cell code
5095 protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') {
5096 // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space
5097 $txt = str_replace(TCPDF_FONTS
::unichr(160, $this->isunicode
), ' ', $txt);
5098 $prev_cell_margin = $this->cell_margin
;
5099 $prev_cell_padding = $this->cell_padding
;
5100 $txt = TCPDF_STATIC
::removeSHY($txt, $this->isunicode
);
5101 $rs = ''; //string to be returned
5102 $this->adjustCellPadding($border);
5103 if (!$ignore_min_height) {
5104 $min_cell_height = $this->getCellHeight($this->FontSize
);
5105 if ($h < $min_cell_height) {
5106 $h = $min_cell_height;
5110 // check page for no-write regions and adapt page margins if necessary
5111 list($this->x
, $this->y
) = $this->checkPageRegions($h, $this->x
, $this->y
);
5113 $x = $this->x
- $this->cell_margin
['R'];
5115 $x = $this->x +
$this->cell_margin
['L'];
5117 $y = $this->y +
$this->cell_margin
['T'];
5118 $prev_font_stretching = $this->font_stretching
;
5119 $prev_font_spacing = $this->font_spacing
;
5120 // cell vertical alignment
5127 $y -= $this->cell_padding
['T'];
5132 $y -= ($h - $this->cell_padding
['B'] - $this->FontAscent
- $this->FontDescent
);
5139 $y -= (($h - $this->FontAscent
- $this->FontDescent
) / 2);
5150 $y -= ($this->cell_padding
['T'] +
$this->FontAscent
);
5155 $y -= ($h - $this->cell_padding
['B'] - $this->FontDescent
);
5162 $y -= (($h +
$this->FontAscent
- $this->FontDescent
) / 2);
5173 $y -= ($this->cell_padding
['T'] +
$this->FontAscent +
$this->FontDescent
);
5178 $y -= ($h - $this->cell_padding
['B']);
5185 $y -= (($h +
$this->FontAscent +
$this->FontDescent
) / 2);
5208 // text vertical alignment
5212 $yt = $y +
$this->cell_padding
['T'];
5217 $yt = $y +
$h - $this->cell_padding
['B'] - $this->FontAscent
- $this->FontDescent
;
5224 $yt = $y +
(($h - $this->FontAscent
- $this->FontDescent
) / 2);
5228 $basefonty = $yt +
$this->FontAscent
;
5229 if (TCPDF_STATIC
::empty_string($w) OR ($w <= 0)) {
5231 $w = $x - $this->lMargin
;
5233 $w = $this->w
- $this->rMargin
- $x;
5238 if (is_string($border) AND (strlen($border) == 4)) {
5242 if ($fill OR ($border == 1)) {
5244 $op = ($border == 1) ?
'B' : 'f';
5249 $xk = (($x - $w) * $k);
5253 $s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h
- $y) * $k), ($w * $k), (-$h * $k), $op);
5256 $s .= $this->getCellBorder($x, $y, $w, $h, $border);
5259 if ($this->isunicode
) {
5260 if (($this->CurrentFont
['type'] == 'core') OR ($this->CurrentFont
['type'] == 'TrueType') OR ($this->CurrentFont
['type'] == 'Type1')) {
5261 $txt2 = TCPDF_FONTS
::UTF8ToLatin1($txt2, $this->isunicode
, $this->CurrentFont
);
5263 $unicode = TCPDF_FONTS
::UTF8StringToArray($txt, $this->isunicode
, $this->CurrentFont
); // array of UTF-8 unicode values
5264 $unicode = TCPDF_FONTS
::utf8Bidi($unicode, '', $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
5265 // replace thai chars (if any)
5266 if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS
== true)) {
5268 $numchars = count($unicode);
5269 // po pla, for far, for fan
5270 $longtail = array(0x0e1b, 0x0e1d, 0x0e1f);
5271 // do chada, to patak
5272 $lowtail = array(0x0e0e, 0x0e0f);
5273 // mai hun arkad, sara i, sara ii, sara ue, sara uee
5274 $upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37);
5275 // mai ek, mai tho, mai tri, mai chattawa, karan
5276 $tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c);
5277 // sara u, sara uu, pinthu
5278 $lowvowel = array(0x0e38, 0x0e39, 0x0e3a);
5280 for ($i = 0; $i < $numchars; $i++
) {
5281 if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) {
5282 $ch0 = $unicode[$i];
5283 $ch1 = ($i > 0) ?
$unicode[($i - 1)] : 0;
5284 $ch2 = ($i > 1) ?
$unicode[($i - 2)] : 0;
5285 $chn = ($i < ($numchars - 1)) ?
$unicode[($i +
1)] : 0;
5286 if (in_array($ch0, $tonemark)) {
5287 if ($chn == 0x0e33) {
5289 if (in_array($ch1, $longtail)) {
5290 // tonemark at upper left
5291 $output[] = $this->replaceChar($ch0, (0xf713 +
$ch0 - 0x0e48));
5293 // tonemark at upper right (normal position)
5296 } elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) {
5297 // tonemark at lower left
5298 $output[] = $this->replaceChar($ch0, (0xf705 +
$ch0 - 0x0e48));
5299 } elseif (in_array($ch1, $upvowel)) {
5300 if (in_array($ch2, $longtail)) {
5301 // tonemark at upper left
5302 $output[] = $this->replaceChar($ch0, (0xf713 +
$ch0 - 0x0e48));
5304 // tonemark at upper right (normal position)
5308 // tonemark at lower right
5309 $output[] = $this->replaceChar($ch0, (0xf70a +
$ch0 - 0x0e48));
5311 } elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) {
5312 // add lower left nikhahit and sara aa
5313 if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) {
5315 $this->CurrentFont
['subsetchars'][0xf711] = true;
5317 $this->CurrentFont
['subsetchars'][0x0e32] = true;
5321 } elseif (in_array($ch1, $longtail)) {
5322 if ($ch0 == 0x0e31) {
5323 // lower left mai hun arkad
5324 $output[] = $this->replaceChar($ch0, 0xf710);
5325 } elseif (in_array($ch0, $upvowel)) {
5327 $output[] = $this->replaceChar($ch0, (0xf701 +
$ch0 - 0x0e34));
5328 } elseif ($ch0 == 0x0e47) {
5329 // lower left mai tai koo
5330 $output[] = $this->replaceChar($ch0, 0xf712);
5335 } elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) {
5337 $output[] = $this->replaceChar($ch0, (0xf718 +
$ch0 - 0x0e38));
5338 } elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) {
5339 // yo ying without lower part
5340 $output[] = $this->replaceChar($ch0, 0xf70f);
5341 } elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) {
5342 // tho santan without lower part
5343 $output[] = $this->replaceChar($ch0, 0xf700);
5348 // non-thai character
5349 $output[] = $unicode[$i];
5353 // update font subsetchars
5354 $this->setFontSubBuffer($this->CurrentFont
['fontkey'], 'subsetchars', $this->CurrentFont
['subsetchars']);
5355 } // end of K_THAI_TOPCHARS
5356 $txt2 = TCPDF_FONTS
::arrUTF8ToUTF16BE($unicode, false);
5359 $txt2 = TCPDF_STATIC
::_escape($txt2);
5360 // get current text width (considering general font stretching and spacing)
5361 $txwidth = $this->GetStringWidth($txt);
5363 // check for stretch mode
5365 // calculate ratio between cell width and text width
5369 $ratio = (($w - $this->cell_padding
['L'] - $this->cell_padding
['R']) / $width);
5371 // check if stretching is required
5372 if (($ratio < 1) OR (($ratio > 1) AND (($stretch %
2) == 0))) {
5373 // the text will be stretched to fit cell width
5375 // set new character spacing
5376 $this->font_spacing +
= ($w - $this->cell_padding
['L'] - $this->cell_padding
['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching
/ 100));
5378 // set new horizontal stretching
5379 $this->font_stretching
*= $ratio;
5381 // recalculate text width (the text fills the entire cell)
5382 $width = $w - $this->cell_padding
['L'] - $this->cell_padding
['R'];
5387 if ($this->font_stretching
!= 100) {
5388 // apply font stretching
5389 $rs .= sprintf('BT %F Tz ET ', $this->font_stretching
);
5391 if ($this->font_spacing
!= 0) {
5392 // increase/decrease font spacing
5393 $rs .= sprintf('BT %F Tc ET ', ($this->font_spacing
* $this->k
));
5395 if ($this->ColorFlag
AND ($this->textrendermode
< 4)) {
5396 $s .= 'q '.$this->TextColor
.' ';
5399 $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode
, ($this->textstrokewidth
* $this->k
));
5400 // count number of spaces
5401 $ns = substr_count($txt, chr(32));
5404 if (($align == 'J') AND ($ns > 0)) {
5405 if ($this->isUnicodeFont()) {
5406 // get string width without spaces
5407 $width = $this->GetStringWidth(str_replace(' ', '', $txt));
5408 // calculate average space width
5409 $spacewidth = -1000 * ($w - $width - $this->cell_padding
['L'] - $this->cell_padding
['R']) / ($ns?
$ns:1) / ($this->FontSize?
$this->FontSize
:1);
5410 if ($this->font_stretching
!= 100) {
5411 // word spacing is affected by stretching
5412 $spacewidth /= ($this->font_stretching
/ 100);
5414 // set word position to be used with TJ operator
5415 $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2);
5416 $unicode_justification = true;
5421 $spacewidth = (($w - $width - $this->cell_padding
['L'] - $this->cell_padding
['R']) / ($ns?
$ns:1)) * $this->k
;
5422 if ($this->font_stretching
!= 100) {
5423 // word spacing (Tw) is affected by stretching
5424 $spacewidth /= ($this->font_stretching
/ 100);
5427 $rs .= sprintf('BT %F Tw ET ', $spacewidth);
5429 $width = $w - $this->cell_padding
['L'] - $this->cell_padding
['R'];
5431 // replace carriage return characters
5432 $txt2 = str_replace("\r", ' ', $txt2);
5435 $dx = ($w - $width) / 2;
5440 $dx = $this->cell_padding
['R'];
5442 $dx = $w - $width - $this->cell_padding
['R'];
5448 $dx = $w - $width - $this->cell_padding
['L'];
5450 $dx = $this->cell_padding
['L'];
5457 $dx = $this->cell_padding
['R'];
5459 $dx = $this->cell_padding
['L'];
5465 $xdx = $x - $dx - $width;
5471 $s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h
- $basefonty) * $k), $txt2);
5472 if (isset($uniblock)) {
5473 // print overlapping characters as separate string
5474 $xshift = 0; // horizontal shift
5475 $ty = (($this->h
- $basefonty +
(0.2 * $this->FontSize
)) * $k);
5476 $spw = (($w - $txwidth - $this->cell_padding
['L'] - $this->cell_padding
['R']) / ($ns?
$ns:1));
5477 foreach ($uniblock as $uk => $uniarr) {
5478 if (($uk %
2) == 0) {
5480 if ($spacewidth != 0) {
5481 // justification shift
5482 $xshift +
= (count(array_keys($uniarr, 32)) * $spw);
5484 $xshift +
= $this->GetArrStringWidth($uniarr); // + shift justification
5486 // character to print
5487 $topchr = TCPDF_FONTS
::arrUTF8ToUTF16BE($uniarr, false);
5488 $topchr = TCPDF_STATIC
::_escape($topchr);
5489 $s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk +
($xshift * $k)), $ty, $topchr);
5493 if ($this->underline
) {
5494 $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
5496 if ($this->linethrough
) {
5497 $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
5499 if ($this->overline
) {
5500 $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
5502 if ($this->ColorFlag
AND ($this->textrendermode
< 4)) {
5506 $this->Link($xdx, $yt, $width, ($this->FontAscent +
$this->FontDescent
), $link, $ns);
5513 if ($this->font_spacing
!= 0) {
5514 // reset font spacing mode
5515 $rs .= ' BT 0 Tc ET';
5517 if ($this->font_stretching
!= 100) {
5518 // reset font stretching mode
5519 $rs .= ' BT 100 Tz ET';
5522 // reset word spacing
5523 if (!$this->isUnicodeFont() AND ($align == 'J')) {
5524 $rs .= ' BT 0 Tw ET';
5526 // reset stretching and spacing
5527 $this->font_stretching
= $prev_font_stretching;
5528 $this->font_spacing
= $prev_font_spacing;
5531 //Go to the beginning of the next line
5532 $this->y
= $y +
$h +
$this->cell_margin
['B'];
5535 $this->x
= $this->w
- $this->rMargin
;
5537 $this->x
= $this->lMargin
;
5541 // go left or right by case
5543 $this->x
= $x - $w - $this->cell_margin
['L'];
5545 $this->x
= $x +
$w +
$this->cell_margin
['R'];
5548 $gstyles = ''.$this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
.' '.$this->FillColor
."\n";
5550 $this->cell_padding
= $prev_cell_padding;
5551 $this->cell_margin
= $prev_cell_margin;
5556 * Replace a char if is defined on the current font.
5557 * @param $oldchar (int) Integer code (unicode) of the character to replace.
5558 * @param $newchar (int) Integer code (unicode) of the new character.
5559 * @return int the replaced char or the old char in case the new char i not defined
5561 * @since 5.9.167 (2012-06-22)
5563 protected function replaceChar($oldchar, $newchar) {
5564 if ($this->isCharDefined($newchar)) {
5565 // add the new char on the subset list
5566 $this->CurrentFont
['subsetchars'][$newchar] = true;
5567 // return the new character
5570 // return the old char
5575 * Returns the code to draw the cell border
5576 * @param $x (float) X coordinate.
5577 * @param $y (float) Y coordinate.
5578 * @param $w (float) Cell width.
5579 * @param $h (float) Cell height.
5580 * @param $brd (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5581 * @return string containing cell border code
5583 * @see SetLineStyle()
5584 * @since 5.7.000 (2010-08-02)
5586 protected function getCellBorder($x, $y, $w, $h, $brd) {
5587 $s = ''; // string to be returned
5592 $brd = array('LRTB' => true);
5594 // calculate coordinates for border
5597 $xeL = ($x - $w) * $k;
5601 $xeR = ($x +
$w) * $k;
5603 $yeL = (($this->h
- ($y +
$h)) * $k);
5604 $yeT = (($this->h
- $y) * $k);
5609 if (is_string($brd)) {
5610 // convert string to array
5611 $slen = strlen($brd);
5613 for ($i = 0; $i < $slen; ++
$i) {
5614 $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
5618 if (isset($brd['mode'])) {
5619 $mode = $brd['mode'];
5620 unset($brd['mode']);
5624 foreach ($brd as $border => $style) {
5625 if (is_array($style) AND !empty($style)) {
5626 // apply border style
5627 $prev_style = $this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
.' ';
5628 $s .= $this->SetLineStyle($style, true)."\n";
5632 $off = (($this->LineWidth
/ 2) * $k);
5641 $w +
= $this->LineWidth
;
5642 $h +
= $this->LineWidth
;
5646 $off = ($this->LineWidth
/ 2) * $k;
5655 $w -= $this->LineWidth
;
5656 $h -= $this->LineWidth
;
5672 // draw borders by case
5673 if (strlen($border) == 4) {
5674 $s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k));
5675 } elseif (strlen($border) == 3) {
5676 if (strpos($border,'B') === false) { // LTR
5677 $s .= sprintf('%F %F m ', $xL, $yL);
5678 $s .= sprintf('%F %F l ', $xT, $yT);
5679 $s .= sprintf('%F %F l ', $xR, $yR);
5680 $s .= sprintf('%F %F l ', $xB, $yB);
5682 } elseif (strpos($border,'L') === false) { // TRB
5683 $s .= sprintf('%F %F m ', $xT, $yT);
5684 $s .= sprintf('%F %F l ', $xR, $yR);
5685 $s .= sprintf('%F %F l ', $xB, $yB);
5686 $s .= sprintf('%F %F l ', $xL, $yL);
5688 } elseif (strpos($border,'T') === false) { // RBL
5689 $s .= sprintf('%F %F m ', $xR, $yR);
5690 $s .= sprintf('%F %F l ', $xB, $yB);
5691 $s .= sprintf('%F %F l ', $xL, $yL);
5692 $s .= sprintf('%F %F l ', $xT, $yT);
5694 } elseif (strpos($border,'R') === false) { // BLT
5695 $s .= sprintf('%F %F m ', $xB, $yB);
5696 $s .= sprintf('%F %F l ', $xL, $yL);
5697 $s .= sprintf('%F %F l ', $xT, $yT);
5698 $s .= sprintf('%F %F l ', $xR, $yR);
5701 } elseif (strlen($border) == 2) {
5702 if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
5703 $s .= sprintf('%F %F m ', $xL, $yL);
5704 $s .= sprintf('%F %F l ', $xT, $yT);
5705 $s .= sprintf('%F %F l ', $xR, $yR);
5707 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
5708 $s .= sprintf('%F %F m ', $xT, $yT);
5709 $s .= sprintf('%F %F l ', $xR, $yR);
5710 $s .= sprintf('%F %F l ', $xB, $yB);
5712 } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
5713 $s .= sprintf('%F %F m ', $xR, $yR);
5714 $s .= sprintf('%F %F l ', $xB, $yB);
5715 $s .= sprintf('%F %F l ', $xL, $yL);
5717 } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
5718 $s .= sprintf('%F %F m ', $xB, $yB);
5719 $s .= sprintf('%F %F l ', $xL, $yL);
5720 $s .= sprintf('%F %F l ', $xT, $yT);
5722 } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
5723 $s .= sprintf('%F %F m ', $xL, $yL);
5724 $s .= sprintf('%F %F l ', $xT, $yT);
5726 $s .= sprintf('%F %F m ', $xR, $yR);
5727 $s .= sprintf('%F %F l ', $xB, $yB);
5729 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
5730 $s .= sprintf('%F %F m ', $xT, $yT);
5731 $s .= sprintf('%F %F l ', $xR, $yR);
5733 $s .= sprintf('%F %F m ', $xB, $yB);
5734 $s .= sprintf('%F %F l ', $xL, $yL);
5737 } else { // strlen($border) == 1
5738 if (strpos($border,'L') !== false) { // L
5739 $s .= sprintf('%F %F m ', $xL, $yL);
5740 $s .= sprintf('%F %F l ', $xT, $yT);
5742 } elseif (strpos($border,'T') !== false) { // T
5743 $s .= sprintf('%F %F m ', $xT, $yT);
5744 $s .= sprintf('%F %F l ', $xR, $yR);
5746 } elseif (strpos($border,'R') !== false) { // R
5747 $s .= sprintf('%F %F m ', $xR, $yR);
5748 $s .= sprintf('%F %F l ', $xB, $yB);
5750 } elseif (strpos($border,'B') !== false) { // B
5751 $s .= sprintf('%F %F m ', $xB, $yB);
5752 $s .= sprintf('%F %F l ', $xL, $yL);
5756 if (is_array($style) AND !empty($style)) {
5757 // reset border style to previous value
5758 $s .= "\n".$this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
."\n";
5765 * This method allows printing text with line breaks.
5766 * They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.<br />
5767 * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
5768 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
5769 * @param $h (float) Cell minimum height. The cell extends automatically if needed.
5770 * @param $txt (string) String to print
5771 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5772 * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value when $ishtml=false)</li></ul>
5773 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5774 * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul>
5775 * @param $x (float) x position in user units
5776 * @param $y (float) y position in user units
5777 * @param $reseth (boolean) if true reset the last cell height (default true).
5778 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
5779 * @param $ishtml (boolean) INTERNAL USE ONLY -- set to true if $txt is HTML content (default = false). Never set this parameter to true, use instead writeHTMLCell() or writeHTML() methods.
5780 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
5781 * @param $maxh (float) maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature. This feature works only when $ishtml=false.
5782 * @param $valign (string) Vertical alignment of text (requires $maxh = $h > 0). Possible values are:<ul><li>T: TOP</li><li>M: middle</li><li>B: bottom</li></ul>. This feature works only when $ishtml=false and the cell must fit in a single page.
5783 * @param $fitcell (boolean) if true attempt to fit all the text within the cell by reducing the font size (do not work in HTML mode). $maxh must be greater than 0 and wqual to $h.
5784 * @return int Return the number of cells or 1 for html mode.
5787 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
5789 public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $valign='T', $fitcell=false) {
5790 $prev_cell_margin = $this->cell_margin
;
5791 $prev_cell_padding = $this->cell_padding
;
5792 // adjust internal padding
5793 $this->adjustCellPadding($border);
5794 $mc_padding = $this->cell_padding
;
5795 $mc_margin = $this->cell_margin
;
5796 $this->cell_padding
['T'] = 0;
5797 $this->cell_padding
['B'] = 0;
5798 $this->setCellMargins(0, 0, 0, 0);
5799 if (TCPDF_STATIC
::empty_string($this->lasth
) OR $reseth) {
5801 $this->resetLastH();
5803 if (!TCPDF_STATIC
::empty_string($y)) {
5809 if (($h > 0) AND $this->inPageBody() AND (($y +
$h +
$mc_margin['T'] +
$mc_margin['B']) > $this->PageBreakTrigger
)) {
5810 // spit cell in more pages/columns
5811 $newh = ($this->PageBreakTrigger
- $y);
5812 $resth = ($h - $newh); // cell to be printed on the next page/column
5815 // get current page number
5816 $startpage = $this->page
;
5817 // get current column
5818 $startcolumn = $this->current_column
;
5819 if (!TCPDF_STATIC
::empty_string($x)) {
5824 // check page for no-write regions and adapt page margins if necessary
5825 list($x, $y) = $this->checkPageRegions(0, $x, $y);
5827 $oy = $y +
$mc_margin['T'];
5829 $ox = ($this->w
- $x - $mc_margin['R']);
5831 $ox = ($x +
$mc_margin['L']);
5836 if (TCPDF_STATIC
::empty_string($w) OR ($w <= 0)) {
5838 $w = ($this->x
- $this->lMargin
- $mc_margin['L']);
5840 $w = ($this->w
- $this->x
- $this->rMargin
- $mc_margin['R']);
5843 // store original margin values
5844 $lMargin = $this->lMargin
;
5845 $rMargin = $this->rMargin
;
5847 $this->rMargin
= ($this->w
- $this->x
);
5848 $this->lMargin
= ($this->x
- $w);
5850 $this->lMargin
= ($this->x
);
5851 $this->rMargin
= ($this->w
- $this->x
- $w);
5853 $this->clMargin
= $this->lMargin
;
5854 $this->crMargin
= $this->rMargin
;
5857 $this->y +
= $mc_padding['T'];
5859 if ($ishtml) { // ******* Write HTML text
5860 $this->writeHTML($txt, true, false, $reseth, true, $align);
5862 } else { // ******* Write simple text
5863 $prev_FontSizePt = $this->FontSizePt
;
5865 // ajust height values
5866 $tobottom = ($this->h
- $this->y
- $this->bMargin
- $this->cell_padding
['T'] - $this->cell_padding
['B']);
5867 $h = $maxh = max(min($h, $tobottom), min($maxh, $tobottom));
5869 // vertical alignment
5872 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5873 if ($fitcell AND ($text_height > $maxh) AND ($this->FontSizePt
> 1)) {
5874 // try to reduce font size to fit text on cell (use a quick search algorithm)
5876 $fmax = $this->FontSizePt
;
5877 $diff_epsilon = (1 / $this->k
); // one point (min resolution)
5878 $maxit = (2 * min(100, max(10, intval($fmax)))); // max number of iterations
5879 while ($maxit >= 0) {
5880 $fmid = (($fmax +
$fmin) / 2);
5881 $this->SetFontSize($fmid, false);
5882 $this->resetLastH();
5883 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5884 $diff = ($maxh - $text_height);
5886 if ($diff <= $diff_epsilon) {
5896 // premature exit, we get the minimum font value to fit the cell
5897 $this->SetFontSize($fmin);
5898 $this->resetLastH();
5899 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5901 $this->SetFontSize($fmid);
5902 $this->resetLastH();
5905 if ($text_height < $maxh) {
5906 if ($valign == 'M') {
5907 // text vertically centered
5908 $this->y +
= (($maxh - $text_height) / 2);
5909 } elseif ($valign == 'B') {
5910 // text vertically aligned on bottom
5911 $this->y +
= ($maxh - $text_height);
5915 $nl = $this->Write($this->lasth
, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
5917 // restore font size
5918 $this->SetFontSize($prev_FontSizePt);
5922 // add bottom padding
5923 $this->y +
= $mc_padding['B'];
5925 // Get end-of-text Y position
5926 $currentY = $this->y
;
5927 // get latest page number
5928 $endpage = $this->page
;
5930 $skip = ($endpage - $startpage);
5932 while ($tmpresth > 0) {
5934 // add a page (or trig AcceptPageBreak() for multicolumn mode)
5935 $this->checkPageBreak($this->PageBreakTrigger +
1);
5937 if ($this->num_columns
> 1) {
5938 $tmpresth -= ($this->h
- $this->y
- $this->bMargin
);
5940 $tmpresth -= ($this->h
- $this->tMargin
- $this->bMargin
);
5944 $currentY = $this->y
;
5945 $endpage = $this->page
;
5947 // get latest column
5948 $endcolumn = $this->current_column
;
5949 if ($this->num_columns
== 0) {
5950 $this->num_columns
= 1;
5952 // disable page regions check
5953 $check_page_regions = $this->check_page_regions
;
5954 $this->check_page_regions
= false;
5956 $border_start = TCPDF_STATIC
::getBorderMode($border, $position='start', $this->opencell
);
5957 $border_end = TCPDF_STATIC
::getBorderMode($border, $position='end', $this->opencell
);
5958 $border_middle = TCPDF_STATIC
::getBorderMode($border, $position='middle', $this->opencell
);
5959 // design borders around HTML cells.
5960 for ($page = $startpage; $page <= $endpage; ++
$page) { // for each page
5962 $this->setPage($page);
5963 if ($this->num_columns
< 2) {
5964 // single-column mode
5966 $this->y
= $this->tMargin
;
5968 // account for margin changes
5969 if ($page > $startpage) {
5970 if (($this->rtl
) AND ($this->pagedim
[$page]['orm'] != $this->pagedim
[$startpage]['orm'])) {
5971 $this->x
-= ($this->pagedim
[$page]['orm'] - $this->pagedim
[$startpage]['orm']);
5972 } elseif ((!$this->rtl
) AND ($this->pagedim
[$page]['olm'] != $this->pagedim
[$startpage]['olm'])) {
5973 $this->x +
= ($this->pagedim
[$page]['olm'] - $this->pagedim
[$startpage]['olm']);
5976 if ($startpage == $endpage) {
5978 for ($column = $startcolumn; $column <= $endcolumn; ++
$column) { // for each column
5979 $this->selectColumn($column);
5981 $this->x
-= $mc_margin['R'];
5983 $this->x +
= $mc_margin['L'];
5985 if ($startcolumn == $endcolumn) { // single column
5987 $h = max($h, ($currentY - $oy));
5989 } elseif ($column == $startcolumn) { // first column
5990 $cborder = $border_start;
5992 $h = $this->h
- $this->y
- $this->bMargin
;
5993 } elseif ($column == $endcolumn) { // end column
5994 $cborder = $border_end;
5995 $h = $currentY - $this->y
;
5999 } else { // middle column
6000 $cborder = $border_middle;
6001 $h = $this->h
- $this->y
- $this->bMargin
;
6004 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6005 } // end for each column
6006 } elseif ($page == $startpage) { // first page
6007 for ($column = $startcolumn; $column < $this->num_columns
; ++
$column) { // for each column
6008 $this->selectColumn($column);
6010 $this->x
-= $mc_margin['R'];
6012 $this->x +
= $mc_margin['L'];
6014 if ($column == $startcolumn) { // first column
6015 $cborder = $border_start;
6017 $h = $this->h
- $this->y
- $this->bMargin
;
6018 } else { // middle column
6019 $cborder = $border_middle;
6020 $h = $this->h
- $this->y
- $this->bMargin
;
6023 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6024 } // end for each column
6025 } elseif ($page == $endpage) { // last page
6026 for ($column = 0; $column <= $endcolumn; ++
$column) { // for each column
6027 $this->selectColumn($column);
6029 $this->x
-= $mc_margin['R'];
6031 $this->x +
= $mc_margin['L'];
6033 if ($column == $endcolumn) {
6035 $cborder = $border_end;
6036 $h = $currentY - $this->y
;
6042 $cborder = $border_middle;
6043 $h = $this->h
- $this->y
- $this->bMargin
;
6046 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6047 } // end for each column
6048 } else { // middle page
6049 for ($column = 0; $column < $this->num_columns
; ++
$column) { // for each column
6050 $this->selectColumn($column);
6052 $this->x
-= $mc_margin['R'];
6054 $this->x +
= $mc_margin['L'];
6056 $cborder = $border_middle;
6057 $h = $this->h
- $this->y
- $this->bMargin
;
6059 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6060 } // end for each column
6062 if ($cborder OR $fill) {
6063 $offsetlen = strlen($ccode);
6064 // draw border and fill
6065 if ($this->inxobj
) {
6066 // we are inside an XObject template
6067 if (end($this->xobjects
[$this->xobjid
]['transfmrk']) !== false) {
6068 $pagemarkkey = key($this->xobjects
[$this->xobjid
]['transfmrk']);
6069 $pagemark = $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey];
6070 $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey] +
= $offsetlen;
6072 $pagemark = $this->xobjects
[$this->xobjid
]['intmrk'];
6073 $this->xobjects
[$this->xobjid
]['intmrk'] +
= $offsetlen;
6075 $pagebuff = $this->xobjects
[$this->xobjid
]['outdata'];
6076 $pstart = substr($pagebuff, 0, $pagemark);
6077 $pend = substr($pagebuff, $pagemark);
6078 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$ccode.$pend;
6080 if (end($this->transfmrk
[$this->page
]) !== false) {
6081 $pagemarkkey = key($this->transfmrk
[$this->page
]);
6082 $pagemark = $this->transfmrk
[$this->page
][$pagemarkkey];
6083 $this->transfmrk
[$this->page
][$pagemarkkey] +
= $offsetlen;
6084 } elseif ($this->InFooter
) {
6085 $pagemark = $this->footerpos
[$this->page
];
6086 $this->footerpos
[$this->page
] +
= $offsetlen;
6088 $pagemark = $this->intmrk
[$this->page
];
6089 $this->intmrk
[$this->page
] +
= $offsetlen;
6091 $pagebuff = $this->getPageBuffer($this->page
);
6092 $pstart = substr($pagebuff, 0, $pagemark);
6093 $pend = substr($pagebuff, $pagemark);
6094 $this->setPageBuffer($this->page
, $pstart.$ccode.$pend);
6097 } // end for each page
6098 // restore page regions check
6099 $this->check_page_regions
= $check_page_regions;
6100 // Get end-of-cell Y position
6101 $currentY = $this->GetY();
6102 // restore previous values
6103 if ($this->num_columns
> 1) {
6104 $this->selectColumn();
6106 // restore original margins
6107 $this->lMargin
= $lMargin;
6108 $this->rMargin
= $rMargin;
6109 if ($this->page
> $startpage) {
6110 // check for margin variations between pages (i.e. booklet mode)
6111 $dl = ($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$startpage]['olm']);
6112 $dr = ($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$startpage]['orm']);
6113 if (($dl != 0) OR ($dr != 0)) {
6114 $this->lMargin +
= $dl;
6115 $this->rMargin +
= $dr;
6120 //Go to the beginning of the next line
6121 $this->SetY($currentY +
$mc_margin['B']);
6123 $this->SetX($x +
$w +
$mc_margin['L'] +
$mc_margin['R']);
6126 // go left or right by case
6127 $this->setPage($startpage);
6129 $this->SetX($x +
$w +
$mc_margin['L'] +
$mc_margin['R']);
6131 $this->setContentMark();
6132 $this->cell_padding
= $prev_cell_padding;
6133 $this->cell_margin
= $prev_cell_margin;
6134 $this->clMargin
= $this->lMargin
;
6135 $this->crMargin
= $this->rMargin
;
6140 * This method return the estimated number of lines for print a simple text string using Multicell() method.
6141 * @param $txt (string) String for calculating his height
6142 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
6143 * @param $reseth (boolean) if true reset the last cell height (default false).
6144 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
6145 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
6146 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
6147 * @return float Return the minimal height needed for multicell method for printing the $txt param.
6148 * @author Alexander Escalona Fern\E1ndez, Nicola Asuni
6152 public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6153 if ($txt === NULL) {
6160 // adjust internal padding
6161 $prev_cell_padding = $this->cell_padding
;
6162 $prev_lasth = $this->lasth
;
6163 if (is_array($cellpadding)) {
6164 $this->cell_padding
= $cellpadding;
6166 $this->adjustCellPadding($border);
6167 if (TCPDF_STATIC
::empty_string($w) OR ($w <= 0)) {
6169 $w = $this->x
- $this->lMargin
;
6171 $w = $this->w
- $this->rMargin
- $this->x
;
6174 $wmax = $w - $this->cell_padding
['L'] - $this->cell_padding
['R'];
6177 $this->resetLastH();
6181 $chars = TCPDF_FONTS
::utf8Bidi(TCPDF_FONTS
::UTF8StringToArray($txt, $this->isunicode
, $this->CurrentFont
), $txt, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6182 $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
6183 $length = count($chars);
6184 $lastSeparator = -1;
6185 for ($i = 0; $i < $length; ++
$i) {
6187 $charWidth = $charsWidth[$i];
6190 OR preg_match($this->re_spaces
, TCPDF_FONTS
::unichr($c, $this->isunicode
))
6192 AND ($i > 0) AND ($i < ($length - 1))
6193 AND @preg_match
('/[\p{L}]/'.$this->re_space
['m'], TCPDF_FONTS
::unichr($chars[($i - 1)], $this->isunicode
))
6194 AND @preg_match
('/[\p{L}]/'.$this->re_space
['m'], TCPDF_FONTS
::unichr($chars[($i +
1)], $this->isunicode
))
6198 $lastSeparator = $i;
6200 if ((($sum +
$charWidth) > $wmax) OR ($c == 10)) {
6203 $lastSeparator = -1;
6205 } elseif ($lastSeparator != -1) {
6206 $i = $lastSeparator;
6207 $lastSeparator = -1;
6216 if ($chars[($length - 1)] == 10) {
6219 $this->cell_padding
= $prev_cell_padding;
6220 $this->lasth
= $prev_lasth;
6225 * This method return the estimated height needed for printing a simple text string using the Multicell() method.
6226 * Generally, if you want to know the exact height for a block of content you can use the following alternative technique:
6228 * // store current object
6229 * $pdf->startTransaction();
6230 * // store starting values
6231 * $start_y = $pdf->GetY();
6232 * $start_page = $pdf->getPage();
6233 * // call your printing functions with your parameters
6234 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6235 * $pdf->MultiCell($w=0, $h=0, $txt, $border=1, $align='L', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0);
6236 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6238 * $end_y = $pdf->GetY();
6239 * $end_page = $pdf->getPage();
6240 * // calculate height
6242 * if ($end_page == $start_page) {
6243 * $height = $end_y - $start_y;
6245 * for ($page=$start_page; $page <= $end_page; ++$page) {
6246 * $this->setPage($page);
6247 * if ($page == $start_page) {
6249 * $height = $this->h - $start_y - $this->bMargin;
6250 * } elseif ($page == $end_page) {
6252 * $height = $end_y - $this->tMargin;
6254 * $height = $this->h - $this->tMargin - $this->bMargin;
6258 * // restore previous object
6259 * $pdf = $pdf->rollbackTransaction();
6261 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
6262 * @param $txt (string) String for calculating his height
6263 * @param $reseth (boolean) if true reset the last cell height (default false).
6264 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
6265 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
6266 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
6267 * @return float Return the minimal height needed for multicell method for printing the $txt param.
6268 * @author Nicola Asuni, Alexander Escalona Fern\E1ndez
6271 public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6272 // adjust internal padding
6273 $prev_cell_padding = $this->cell_padding
;
6274 $prev_lasth = $this->lasth
;
6275 if (is_array($cellpadding)) {
6276 $this->cell_padding
= $cellpadding;
6278 $this->adjustCellPadding($border);
6279 $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
6280 $height = $this->getCellHeight(($lines * $this->FontSize
), $autopadding);
6281 $this->cell_padding
= $prev_cell_padding;
6282 $this->lasth
= $prev_lasth;
6287 * This method prints text from the current position.<br />
6288 * @param $h (float) Line height
6289 * @param $txt (string) String to print
6290 * @param $link (mixed) URL or identifier returned by AddLink()
6291 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
6292 * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
6293 * @param $ln (boolean) if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
6294 * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
6295 * @param $firstline (boolean) if true prints only the first line and return the remaining string.
6296 * @param $firstblock (boolean) if true the string is the starting of a line.
6297 * @param $maxh (float) maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature.
6298 * @param $wadj (float) first line width will be reduced by this amount (used in HTML mode).
6299 * @param $margin (array) margin array of the parent container
6300 * @return mixed Return the number of cells or the remaining string if $firstline = true.
6304 public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
6305 // check page for no-write regions and adapt page margins if necessary
6306 list($this->x
, $this->y
) = $this->checkPageRegions($h, $this->x
, $this->y
);
6307 if (strlen($txt) == 0) {
6311 if ($margin === '') {
6312 // set default margins
6313 $margin = $this->cell_margin
;
6315 // remove carriage returns
6316 $s = str_replace("\r", '', $txt);
6317 // check if string contains arabic text
6318 if (preg_match(TCPDF_FONT_DATA
::$uni_RE_PATTERN_ARABIC, $s)) {
6323 // check if string contains RTL text
6324 if ($arabic OR ($this->tmprtl
== 'R') OR preg_match(TCPDF_FONT_DATA
::$uni_RE_PATTERN_RTL, $s)) {
6330 $chrwidth = $this->GetCharWidth(46); // dot character
6331 // get array of unicode values
6332 $chars = TCPDF_FONTS
::UTF8StringToArray($s, $this->isunicode
, $this->CurrentFont
);
6333 // calculate maximum width for a single character on string
6334 $chrw = $this->GetArrStringWidth($chars, '', '', 0, true);
6335 array_walk($chrw, array($this, 'getRawCharWidth'));
6336 $maxchwidth = max($chrw);
6337 // get array of chars
6338 $uchars = TCPDF_FONTS
::UTF8ArrayToUniArray($chars, $this->isunicode
);
6339 // get the number of characters
6340 $nb = count($chars);
6341 // replacement for SHY character (minus symbol)
6342 $shy_replacement = 45;
6343 $shy_replacement_char = TCPDF_FONTS
::unichr($shy_replacement, $this->isunicode
);
6344 // widht for SHY replacement
6345 $shy_replacement_width = $this->GetCharWidth($shy_replacement);
6347 $pw = $w = $this->w
- $this->lMargin
- $this->rMargin
;
6348 // calculate remaining line width ($w)
6350 $w = $this->x
- $this->lMargin
;
6352 $w = $this->w
- $this->rMargin
- $this->x
;
6355 $wmax = ($w - $wadj);
6357 $wmax -= ($this->cell_padding
['L'] +
$this->cell_padding
['R']);
6359 if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) {
6360 // the maximum width character do not fit on column
6363 // minimum row height
6364 $row_height = max($h, $this->getCellHeight($this->FontSize
));
6366 $maxy = $this->y +
$maxh - max($row_height, $h);
6367 $start_page = $this->page
;
6368 $i = 0; // character position
6369 $j = 0; // current starting position
6370 $sep = -1; // position of the last blank space
6371 $prevsep = $sep; // previous separator
6372 $shy = false; // true if the last blank is a soft hypen (SHY)
6373 $prevshy = $shy; // previous shy mode
6374 $l = 0; // current string length
6375 $nl = 0; //number of lines
6377 $pc = 0; // previous character
6378 // for each character
6380 if (($maxh > 0) AND ($this->y
> $maxy) ) {
6383 //Get the current character
6385 if ($c == 10) { // 10 = "\n" = new line
6386 //Explicit line break
6387 if ($align == 'J') {
6396 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, $i);
6399 $tmparr = array_slice($chars, $j, ($i - $j));
6401 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6403 $linew = $this->GetArrStringWidth($tmparr);
6406 $this->endlinex
= $startx - $linew;
6408 $this->endlinex
= $startx +
$linew;
6411 $tmpcellpadding = $this->cell_padding
;
6413 $this->SetCellPadding(0);
6416 if ($firstblock AND $this->isRTLTextDir()) {
6417 $tmpstr = $this->stringRightTrim($tmpstr);
6419 // Skip newlines at the begining of a page or column
6420 if (!empty($tmpstr) OR ($this->y
< ($this->PageBreakTrigger
- $row_height))) {
6421 $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
6425 $this->cell_padding
= $tmpcellpadding;
6426 return (TCPDF_FONTS
::UniArrSubString($uchars, $i));
6434 // account for margin changes
6435 if ((($this->y +
$this->lasth
) > $this->PageBreakTrigger
) AND ($this->inPageBody())) {
6436 $this->AcceptPageBreak();
6438 $this->x
-= $margin['R'];
6440 $this->x +
= $margin['L'];
6442 $this->lMargin +
= $margin['L'];
6443 $this->rMargin +
= $margin['R'];
6445 $w = $this->getRemainingWidth();
6446 $wmax = ($w - $this->cell_padding
['L'] - $this->cell_padding
['R']);
6448 // 160 is the non-breaking space.
6449 // 173 is SHY (Soft Hypen).
6450 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
6451 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
6452 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
6455 OR preg_match($this->re_spaces
, TCPDF_FONTS
::unichr($c, $this->isunicode
))
6457 AND ($i < ($nb - 1))
6458 AND @preg_match
('/[\p{L}]/'.$this->re_space
['m'], TCPDF_FONTS
::unichr($pc, $this->isunicode
))
6459 AND @preg_match
('/[\p{L}]/'.$this->re_space
['m'], TCPDF_FONTS
::unichr($chars[($i +
1)], $this->isunicode
))
6463 // update last blank space position
6466 // check if is a SHY
6467 if (($c == 173) OR ($c == 45)) {
6471 $tmp_shy_replacement_width = 0;
6472 $tmp_shy_replacement_char = '';
6474 $tmp_shy_replacement_width = $shy_replacement_width;
6475 $tmp_shy_replacement_char = $shy_replacement_char;
6481 // update string length
6482 if ($this->isUnicodeFont() AND ($arabic)) {
6483 // with bidirectional algorithm some chars may be changed affecting the line length
6484 // *** very slow ***
6485 $l = $this->GetArrStringWidth(TCPDF_FONTS
::utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl
, $this->isunicode
, $this->CurrentFont
));
6487 $l +
= $this->GetCharWidth($c);
6489 if (($l > $wmax) OR (($c == 173) AND (($l +
$tmp_shy_replacement_width) >= $wmax))) {
6490 if (($c == 173) AND (($l +
$tmp_shy_replacement_width) > $wmax)) {
6494 // we have reached the end of column
6496 // check if the line was already started
6497 if (($this->rtl
AND ($this->x
<= ($this->w
- $this->rMargin
- $this->cell_padding
['R'] - $margin['R'] - $chrwidth)))
6498 OR ((!$this->rtl
) AND ($this->x
>= ($this->lMargin +
$this->cell_padding
['L'] +
$margin['L'] +
$chrwidth)))) {
6499 // print a void cell and go to next line
6500 $this->Cell($w, $h, '', 0, 1);
6503 return (TCPDF_FONTS
::UniArrSubString($uchars, $j));
6506 // truncate the word because do not fit on column
6507 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, $i);
6510 $tmparr = array_slice($chars, $j, ($i - $j));
6512 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6514 $linew = $this->GetArrStringWidth($tmparr);
6517 $this->endlinex
= $startx - $linew;
6519 $this->endlinex
= $startx +
$linew;
6522 $tmpcellpadding = $this->cell_padding
;
6524 $this->SetCellPadding(0);
6527 if ($firstblock AND $this->isRTLTextDir()) {
6528 $tmpstr = $this->stringRightTrim($tmpstr);
6530 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6533 $this->cell_padding
= $tmpcellpadding;
6534 return (TCPDF_FONTS
::UniArrSubString($uchars, $i));
6541 if ($this->rtl
AND (!$firstblock) AND ($sep < $i)) {
6546 // check the length of the next string
6547 $strrest = TCPDF_FONTS
::UniArrSubString($uchars, ($sep +
$endspace));
6548 $nextstr = TCPDF_STATIC
::pregSplit('/'.$this->re_space
['p'].'/', $this->re_space
['m'], $this->stringTrim($strrest));
6549 if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
6550 // truncate the word because do not fit on a full page width
6551 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, $i);
6554 $tmparr = array_slice($chars, $j, ($i - $j));
6556 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6558 $linew = $this->GetArrStringWidth($tmparr);
6561 $this->endlinex
= ($startx - $linew);
6563 $this->endlinex
= ($startx +
$linew);
6566 $tmpcellpadding = $this->cell_padding
;
6568 $this->SetCellPadding(0);
6571 if ($firstblock AND $this->isRTLTextDir()) {
6572 $tmpstr = $this->stringRightTrim($tmpstr);
6574 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6577 $this->cell_padding
= $tmpcellpadding;
6578 return (TCPDF_FONTS
::UniArrSubString($uchars, $i));
6585 // add hypen (minus symbol) at the end of the line
6586 $shy_width = $tmp_shy_replacement_width;
6588 $shy_char_left = $tmp_shy_replacement_char;
6589 $shy_char_right = '';
6591 $shy_char_left = '';
6592 $shy_char_right = $tmp_shy_replacement_char;
6596 $shy_char_left = '';
6597 $shy_char_right = '';
6599 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, ($sep +
$endspace));
6602 $tmparr = array_slice($chars, $j, (($sep +
$endspace) - $j));
6604 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6606 $linew = $this->GetArrStringWidth($tmparr);
6609 $this->endlinex
= $startx - $linew - $shy_width;
6611 $this->endlinex
= $startx +
$linew +
$shy_width;
6614 $tmpcellpadding = $this->cell_padding
;
6616 $this->SetCellPadding(0);
6620 if ($firstblock AND $this->isRTLTextDir()) {
6621 $tmpstr = $this->stringRightTrim($tmpstr);
6623 $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
6626 if ($chars[$sep] == 45) {
6629 // return the remaining text
6630 $this->cell_padding
= $tmpcellpadding;
6631 return (TCPDF_FONTS
::UniArrSubString($uchars, ($sep +
$endspace)));
6639 // account for margin changes
6640 if ((($this->y +
$this->lasth
) > $this->PageBreakTrigger
) AND ($this->inPageBody())) {
6641 $this->AcceptPageBreak();
6643 $this->x
-= $margin['R'];
6645 $this->x +
= $margin['L'];
6647 $this->lMargin +
= $margin['L'];
6648 $this->rMargin +
= $margin['R'];
6650 $w = $this->getRemainingWidth();
6651 $wmax = $w - $this->cell_padding
['L'] - $this->cell_padding
['R'];
6660 // save last character
6663 } // end while i < nb
6664 // print last substring (if any)
6693 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, $nb);
6696 $tmparr = array_slice($chars, $j, ($nb - $j));
6698 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6700 $linew = $this->GetArrStringWidth($tmparr);
6703 $this->endlinex
= $startx - $linew;
6705 $this->endlinex
= $startx +
$linew;
6708 $tmpcellpadding = $this->cell_padding
;
6710 $this->SetCellPadding(0);
6713 if ($firstblock AND $this->isRTLTextDir()) {
6714 $tmpstr = $this->stringRightTrim($tmpstr);
6716 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
6719 $this->cell_padding
= $tmpcellpadding;
6720 return (TCPDF_FONTS
::UniArrSubString($uchars, $nb));
6731 * Returns the remaining width between the current position and margins.
6732 * @return int Return the remaining width
6735 protected function getRemainingWidth() {
6736 list($this->x
, $this->y
) = $this->checkPageRegions(0, $this->x
, $this->y
);
6738 return ($this->x
- $this->lMargin
);
6740 return ($this->w
- $this->rMargin
- $this->x
);
6745 * Set the block dimensions accounting for page breaks and page/column fitting
6746 * @param $w (float) width
6747 * @param $h (float) height
6748 * @param $x (float) X coordinate
6749 * @param $y (float) Y coodiante
6750 * @param $fitonpage (boolean) if true the block is resized to not exceed page dimensions.
6751 * @return array($w, $h, $x, $y)
6753 * @since 5.5.009 (2010-07-05)
6755 protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
6757 // set maximum width
6758 $w = ($this->w
- $this->lMargin
- $this->rMargin
);
6764 // set maximum height
6765 $h = ($this->PageBreakTrigger
- $this->tMargin
);
6770 // resize the block to be vertically contained on a single page or single column
6771 if ($fitonpage OR $this->AutoPageBreak
) {
6772 $ratio_wh = ($w / $h);
6773 if ($h > ($this->PageBreakTrigger
- $this->tMargin
)) {
6774 $h = $this->PageBreakTrigger
- $this->tMargin
;
6775 $w = ($h * $ratio_wh);
6777 // resize the block to be horizontally contained on a single page or single column
6779 $maxw = ($this->w
- $this->lMargin
- $this->rMargin
);
6782 $h = ($w / $ratio_wh);
6786 // Check whether we need a new page or new column first as this does not fit
6789 if ($this->checkPageBreak($h, $y) OR ($this->y
< $prev_y)) {
6792 $x +
= ($prev_x - $this->x
);
6794 $x +
= ($this->x
- $prev_x);
6796 $this->newline
= true;
6798 // resize the block to be contained on the remaining available page or column space
6800 $ratio_wh = ($w / $h);
6801 if (($y +
$h) > $this->PageBreakTrigger
) {
6802 $h = $this->PageBreakTrigger
- $y;
6803 $w = ($h * $ratio_wh);
6805 if ((!$this->rtl
) AND (($x +
$w) > ($this->w
- $this->rMargin
))) {
6806 $w = $this->w
- $this->rMargin
- $x;
6807 $h = ($w / $ratio_wh);
6808 } elseif (($this->rtl
) AND (($x - $w) < ($this->lMargin
))) {
6809 $w = $x - $this->lMargin
;
6810 $h = ($w / $ratio_wh);
6813 return array($w, $h, $x, $y);
6817 * Puts an image in the page.
6818 * The upper-left corner must be given.
6819 * The dimensions can be specified in different ways:<ul>
6820 * <li>explicit width and height (expressed in user unit)</li>
6821 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
6822 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
6823 * Supported formats are JPEG and PNG images whitout GD library and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;
6824 * The format can be specified explicitly or inferred from the file extension.<br />
6825 * It is possible to put a link on the image.<br />
6826 * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
6827 * @param $file (string) Name of the file containing the image or a '@' character followed by the image data string. To link an image without embedding it on the document, set an asterisk character before the URL (i.e.: '*http://www.example.com/image.jpg').
6828 * @param $x (float) Abscissa of the upper-left corner (LTR) or upper-right corner (RTL).
6829 * @param $y (float) Ordinate of the upper-left corner (LTR) or upper-right corner (RTL).
6830 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
6831 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
6832 * @param $type (string) Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension.
6833 * @param $link (mixed) URL or identifier returned by AddLink().
6834 * @param $align (string) Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
6835 * @param $resize (mixed) If true resize (reduce) the image to fit $w and $h (requires GD or ImageMagick library); if false do not resize; if 2 force resize in all cases (upscaling and downscaling).
6836 * @param $dpi (int) dot-per-inch resolution used on resize
6837 * @param $palign (string) Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
6838 * @param $ismask (boolean) true if this image is a mask, false otherwise
6839 * @param $imgmask (mixed) image object returned by this function or false
6840 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
6841 * @param $fitbox (mixed) If not false scale image dimensions proportionally to fit within the ($w, $h) box. $fitbox can be true or a 2 characters string indicating the image alignment inside the box. The first character indicate the horizontal alignment (L = left, C = center, R = right) the second character indicate the vertical algnment (T = top, M = middle, B = bottom).
6842 * @param $hidden (boolean) If true do not display the image.
6843 * @param $fitonpage (boolean) If true the image is resized to not exceed page dimensions.
6844 * @param $alt (boolean) If true the image will be added as alternative and not directly printed (the ID of the image will be returned).
6845 * @param $altimgs (array) Array of alternate images IDs. Each alternative image must be an array with two values: an integer representing the image ID (the value returned by the Image method) and a boolean value to indicate if the image is the default for printing.
6846 * @return image information
6850 public function Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false, $hidden=false, $fitonpage=false, $alt=false, $altimgs=array()) {
6851 if ($this->state
!= 2) {
6860 // check page for no-write regions and adapt page margins if necessary
6861 list($x, $y) = $this->checkPageRegions($h, $x, $y);
6862 $exurl = ''; // external streams
6864 // check if we are passing an image as file or string
6865 if ($file[0] === '@') {
6866 // image from string
6867 $imgdata = substr($file, 1);
6868 } else { // image file
6869 if ($file[0] === '*') {
6870 // image as external stream
6871 $file = substr($file, 1);
6874 // check if is a local file
6875 if (!@file_exists
($file)) {
6876 // try to encode spaces on filename
6877 $tfile = str_replace(' ', '%20', $file);
6878 if (@file_exists
($tfile)) {
6882 if (($imsize = @getimagesize
($file)) === FALSE) {
6883 if (in_array($file, $this->imagekeys
)) {
6884 // get existing image data
6885 $info = $this->getImageBuffer($file);
6886 $imsize = array($info['w'], $info['h']);
6887 } elseif (strpos($file, '__tcpdf_img') === FALSE) {
6888 $imgdata = TCPDF_STATIC
::fileGetContents($file);
6892 if (!empty($imgdata)) {
6893 // copy image to cache
6894 $original_file = $file;
6895 $file = TCPDF_STATIC
::getObjFilename('img');
6896 $fp = fopen($file, 'w');
6898 $this->Error('Unable to write file: '.$file);
6900 fwrite($fp, $imgdata);
6903 $imsize = @getimagesize
($file);
6904 if ($imsize === FALSE) {
6906 $file = $original_file;
6908 $this->cached_files
[] = $file;
6911 if ($imsize === FALSE) {
6912 if (($w > 0) AND ($h > 0)) {
6913 // get measures from specified data
6914 $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit
, true) * $this->imgscale
* $this->k
;
6915 $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit
, true) * $this->imgscale
* $this->k
;
6916 $imsize = array($pw, $ph);
6918 $this->Error('[Image] Unable to get the size of the image: '.$file);
6922 $filehash = md5($this->file_id
.$file);
6923 // get original image width and height in pixels
6924 list($pixw, $pixh) = $imsize;
6925 // calculate image width and height on document
6926 if (($w <= 0) AND ($h <= 0)) {
6927 // convert image size to document unit
6928 $w = $this->pixelsToUnits($pixw);
6929 $h = $this->pixelsToUnits($pixh);
6930 } elseif ($w <= 0) {
6931 $w = $h * $pixw / $pixh;
6932 } elseif ($h <= 0) {
6933 $h = $w * $pixh / $pixw;
6934 } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
6935 if (strlen($fitbox) !== 2) {
6936 // set default alignment
6939 // scale image dimensions proportionally to fit within the ($w, $h) box
6940 if ((($w * $pixh) / ($h * $pixw)) < 1) {
6941 // store current height
6943 // calculate new height
6944 $h = $w * $pixh / $pixw;
6945 // height difference
6946 $hdiff = ($oldh - $h);
6947 // vertical alignment
6948 switch (strtoupper($fitbox[1])) {
6962 // store current width
6964 // calculate new width
6965 $w = $h * $pixw / $pixh;
6967 $wdiff = ($oldw - $w);
6968 // horizontal alignment
6969 switch (strtoupper($fitbox[0])) {
6993 // fit the image on available space
6994 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
6995 // calculate new minimum dimensions in pixels
6996 $neww = round($w * $this->k
* $dpi / $this->dpi
);
6997 $newh = round($h * $this->k
* $dpi / $this->dpi
);
6998 // check if resize is necessary (resize is used only to reduce the image)
6999 $newsize = ($neww * $newh);
7000 $pixsize = ($pixw * $pixh);
7001 if (intval($resize) == 2) {
7003 } elseif ($newsize >= $pixsize) {
7006 // check if image has been already added on document
7008 if (in_array($file, $this->imagekeys
)) {
7010 // get existing image data
7011 $info = $this->getImageBuffer($file);
7012 if (strpos($file, '__tcpdf_imgmask_') === FALSE) {
7013 // check if the newer image is larger
7014 $oldsize = ($info['w'] * $info['h']);
7015 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7019 } elseif (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_imgmask_') === FALSE)) {
7020 // create temp image file (without alpha channel)
7021 $tempfile_plain = K_PATH_CACHE
.'__tcpdf_imgmask_plain_'.$filehash;
7022 // create temp alpha file
7023 $tempfile_alpha = K_PATH_CACHE
.'__tcpdf_imgmask_alpha_'.$filehash;
7024 // check for cached images
7025 if (in_array($tempfile_plain, $this->imagekeys
)) {
7026 // get existing image data
7027 $info = $this->getImageBuffer($tempfile_plain);
7028 // check if the newer image is larger
7029 $oldsize = ($info['w'] * $info['h']);
7030 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7035 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7036 // embed image, masked with previously embedded mask
7037 return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7042 //First use of image, get info
7043 $type = strtolower($type);
7045 $type = TCPDF_IMAGES
::getImageFileType($file, $imsize);
7046 } elseif ($type == 'jpg') {
7049 $mqr = TCPDF_STATIC
::get_mqr();
7050 TCPDF_STATIC
::set_mqr(false);
7051 // Specific image handlers (defined on TCPDF_IMAGES CLASS)
7052 $mtd = '_parse'.$type;
7053 // GD image handler function
7054 $gdfunction = 'imagecreatefrom'.$type;
7056 if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
7057 // TCPDF image functions
7058 $info = TCPDF_IMAGES
::$mtd($file);
7059 if (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_imgmask_') === FALSE)
7060 AND (($info === 'pngalpha') OR (isset($info['trns']) AND !empty($info['trns'])))) {
7061 return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
7064 if (($info === false) AND function_exists($gdfunction)) {
7067 $img = $gdfunction($file);
7068 if ($img !== false) {
7070 $imgr = imagecreatetruecolor($neww, $newh);
7071 if (($type == 'gif') OR ($type == 'png')) {
7072 $imgr = TCPDF_IMAGES
::setGDImageTransparency($imgr, $img);
7074 imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
7077 if (($type == 'gif') OR ($type == 'png')) {
7078 $info = TCPDF_IMAGES
::_toPNG($img);
7080 $info = TCPDF_IMAGES
::_toJPEG($img, $this->jpeg_quality
);
7083 } catch(Exception
$e) {
7087 if (($info === false) AND extension_loaded('imagick')) {
7089 // ImageMagick library
7090 $img = new Imagick();
7091 if ($type == 'svg') {
7092 if ($file[0] === '@') {
7093 // image from string
7094 $svgimg = substr($file, 1);
7096 // get SVG file content
7097 $svgimg = TCPDF_STATIC
::fileGetContents($file);
7099 if ($svgimg !== FALSE) {
7100 // get width and height
7102 if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) {
7105 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7106 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit
, false);
7107 $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit
;
7108 $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
7113 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7114 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit
, false);
7115 $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit
;
7116 $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
7121 if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
7122 $vbw = ($ow * $this->imgscale
* $this->k
);
7123 $vbh = ($oh * $this->imgscale
* $this->k
);
7124 $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh);
7125 $svgtag = $vbox.$svgtag;
7127 $svgimg = preg_replace('/<svg([^\>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
7129 $img->readImageBlob($svgimg);
7132 $img->readImage($file);
7135 $img->resizeImage($neww, $newh, 10, 1, false);
7137 $img->setCompressionQuality($this->jpeg_quality
);
7138 $img->setImageFormat('jpeg');
7139 $tempname = TCPDF_STATIC
::getObjFilename('img');
7140 $img->writeImage($tempname);
7141 $info = TCPDF_IMAGES
::_parsejpeg($tempname);
7144 } catch(Exception
$e) {
7148 if ($info === false) {
7149 // unable to process image
7152 TCPDF_STATIC
::set_mqr($mqr);
7155 $info['cs'] = 'DeviceGray';
7157 if ($imgmask !== false) {
7158 $info['masked'] = $imgmask;
7160 if (!empty($exurl)) {
7161 $info['exurl'] = $exurl;
7163 // array of alternative images
7164 $info['altimgs'] = $altimgs;
7165 // add image to document
7166 $info['i'] = $this->setImageBuffer($file, $info);
7169 $this->img_rb_y
= $y +
$h;
7172 if ($palign == 'L') {
7173 $ximg = $this->lMargin
;
7174 } elseif ($palign == 'C') {
7175 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
7176 } elseif ($palign == 'R') {
7177 $ximg = $this->w
- $this->rMargin
- $w;
7181 $this->img_rb_x
= $ximg;
7183 if ($palign == 'L') {
7184 $ximg = $this->lMargin
;
7185 } elseif ($palign == 'C') {
7186 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
7187 } elseif ($palign == 'R') {
7188 $ximg = $this->w
- $this->rMargin
- $w;
7192 $this->img_rb_x
= $ximg +
$w;
7194 if ($ismask OR $hidden) {
7195 // image is not displayed
7198 $xkimg = $ximg * $this->k
;
7200 // only non-alternative immages will be set
7201 $this->_out(sprintf('q %F 0 0 %F %F %F cm /I%u Do Q', ($w * $this->k
), ($h * $this->k
), $xkimg, (($this->h
- ($y +
$h)) * $this->k
), $info['i']));
7203 if (!empty($border)) {
7211 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
7216 $this->Link($ximg, $y, $w, $h, $link, 0);
7218 // set pointer to align the next text/objects
7222 $this->x
= $this->img_rb_x
;
7226 $this->y
= $y +
round($h/2);
7227 $this->x
= $this->img_rb_x
;
7231 $this->y
= $this->img_rb_y
;
7232 $this->x
= $this->img_rb_x
;
7236 $this->SetY($this->img_rb_y
);
7243 $this->endlinex
= $this->img_rb_x
;
7244 if ($this->inxobj
) {
7245 // we are inside an XObject template
7246 $this->xobjects
[$this->xobjid
]['images'][] = $info['i'];
7252 * Extract info from a PNG image with alpha channel using the Imagick or GD library.
7253 * @param $file (string) Name of the file containing the image.
7254 * @param $x (float) Abscissa of the upper-left corner.
7255 * @param $y (float) Ordinate of the upper-left corner.
7256 * @param $wpx (float) Original width of the image in pixels.
7257 * @param $hpx (float) original height of the image in pixels.
7258 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
7259 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
7260 * @param $type (string) Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension.
7261 * @param $link (mixed) URL or identifier returned by AddLink().
7262 * @param $align (string) Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
7263 * @param $resize (boolean) If true resize (reduce) the image to fit $w and $h (requires GD library).
7264 * @param $dpi (int) dot-per-inch resolution used on resize
7265 * @param $palign (string) Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
7266 * @param $filehash (string) File hash used to build unique file names.
7267 * @author Nicola Asuni
7269 * @since 4.3.007 (2008-12-04)
7272 protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
7273 // create temp images
7274 if (empty($filehash)) {
7275 $filehash = md5($this->file_id
.$file);
7277 // create temp image file (without alpha channel)
7278 $tempfile_plain = K_PATH_CACHE
.'__tcpdf_imgmask_plain_'.$filehash;
7279 // create temp alpha file
7280 $tempfile_alpha = K_PATH_CACHE
.'__tcpdf_imgmask_alpha_'.$filehash;
7283 // ImageMagick extension
7284 if (($parsed === false) AND extension_loaded('imagick')) {
7286 // ImageMagick library
7287 $img = new Imagick();
7288 $img->readImage($file);
7289 // clone image object
7290 $imga = TCPDF_STATIC
::objclone($img);
7291 // extract alpha channel
7292 if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) {
7293 $img->setImageAlphaChannel(Imagick
::ALPHACHANNEL_EXTRACT
);
7295 $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
7296 $img->negateImage(true);
7298 $img->setImageFormat('png');
7299 $img->writeImage($tempfile_alpha);
7300 // remove alpha channel
7301 if (method_exists($imga, 'setImageMatte')) {
7302 $imga->setImageMatte(false);
7304 $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
7306 $imga->setImageFormat('png');
7307 $imga->writeImage($tempfile_plain);
7309 } catch (Exception
$e) {
7310 // Imagemagick fails, try with GD
7311 $parse_error = 'Imagick library error: '.$e->getMessage();
7315 if (($parsed === false) AND function_exists('imagecreatefrompng')) {
7318 $img = imagecreatefrompng($file);
7319 $imgalpha = imagecreate($wpx, $hpx);
7320 // generate gray scale palette (0 -> 255)
7321 for ($c = 0; $c < 256; ++
$c) {
7322 ImageColorAllocate($imgalpha, $c, $c, $c);
7324 // extract alpha channel
7325 for ($xpx = 0; $xpx < $wpx; ++
$xpx) {
7326 for ($ypx = 0; $ypx < $hpx; ++
$ypx) {
7327 $color = imagecolorat($img, $xpx, $ypx);
7328 // get and correct gamma color
7329 $alpha = $this->getGDgamma($img, $color);
7330 imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
7333 imagepng($imgalpha, $tempfile_alpha);
7334 imagedestroy($imgalpha);
7335 // extract image without alpha channel
7336 $imgplain = imagecreatetruecolor($wpx, $hpx);
7337 imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
7338 imagepng($imgplain, $tempfile_plain);
7339 imagedestroy($imgplain);
7341 } catch (Exception
$e) {
7343 $parse_error = 'GD library error: '.$e->getMessage();
7346 if ($parsed === false) {
7347 if (empty($parse_error)) {
7348 $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
7350 $this->Error($parse_error);
7354 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7355 // embed image, masked with previously embedded mask
7356 $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7357 // remove temp files
7358 unlink($tempfile_alpha);
7359 unlink($tempfile_plain);
7363 * Get the GD-corrected PNG gamma value from alpha color
7364 * @param $img (int) GD image Resource ID.
7365 * @param $c (int) alpha color
7367 * @since 4.3.007 (2008-12-04)
7369 protected function getGDgamma($img, $c) {
7370 if (!isset($this->gdgammacache
['#'.$c])) {
7371 $colors = imagecolorsforindex($img, $c);
7372 // GD alpha is only 7 bit (0 -> 127)
7373 $this->gdgammacache
['#'.$c] = (((127 - $colors['alpha']) / 127) * 255);
7375 $this->gdgammacache
['#'.$c] = (pow(($this->gdgammacache
['#'.$c] / 255), 2.2) * 255);
7376 // store the latest values on cache to improve performances
7377 if (count($this->gdgammacache
) > 8) {
7378 // remove one element from the cache array
7379 array_shift($this->gdgammacache
);
7382 return $this->gdgammacache
['#'.$c];
7386 * Performs a line break.
7387 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
7388 * @param $h (float) The height of the break. By default, the value equals the height of the last printed cell.
7389 * @param $cell (boolean) if true add the current left (or right o for RTL) padding to the X coordinate
7394 public function Ln($h='', $cell=false) {
7395 if (($this->num_columns
> 1) AND ($this->y
== $this->columns
[$this->current_column
]['y']) AND isset($this->columns
[$this->current_column
]['x']) AND ($this->x
== $this->columns
[$this->current_column
]['x'])) {
7396 // revove vertical space from the top of the column
7401 $cellpadding = $this->cell_padding
['R'];
7403 $cellpadding = $this->cell_padding
['L'];
7409 $this->x
= $this->w
- $this->rMargin
- $cellpadding;
7411 $this->x
= $this->lMargin +
$cellpadding;
7413 if (is_string($h)) {
7414 $this->y +
= $this->lasth
;
7418 $this->newline
= true;
7422 * Returns the relative X value of current position.
7423 * The value is relative to the left border for LTR languages and to the right border for RTL languages.
7427 * @see SetX(), GetY(), SetY()
7429 public function GetX() {
7432 return ($this->w
- $this->x
);
7439 * Returns the absolute X value of current position.
7443 * @see SetX(), GetY(), SetY()
7445 public function GetAbsX() {
7450 * Returns the ordinate of the current position.
7454 * @see SetY(), GetX(), SetX()
7456 public function GetY() {
7461 * Defines the abscissa of the current position.
7462 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
7463 * @param $x (float) The value of the abscissa in user units.
7464 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7467 * @see GetX(), GetY(), SetY(), SetXY()
7469 public function SetX($x, $rtloff=false) {
7471 if (!$rtloff AND $this->rtl
) {
7473 $this->x
= $this->w
- $x;
7481 $this->x
= $this->w +
$x;
7487 if ($this->x
> $this->w
) {
7488 $this->x
= $this->w
;
7493 * Moves the current abscissa back to the left margin and sets the ordinate.
7494 * If the passed value is negative, it is relative to the bottom of the page.
7495 * @param $y (float) The value of the ordinate in user units.
7496 * @param $resetx (bool) if true (default) reset the X position.
7497 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7500 * @see GetX(), GetY(), SetY(), SetXY()
7502 public function SetY($y, $resetx=true, $rtloff=false) {
7506 if (!$rtloff AND $this->rtl
) {
7507 $this->x
= $this->w
- $this->rMargin
;
7509 $this->x
= $this->lMargin
;
7515 $this->y
= $this->h +
$y;
7520 if ($this->y
> $this->h
) {
7521 $this->y
= $this->h
;
7526 * Defines the abscissa and ordinate of the current position.
7527 * If the passed values are negative, they are relative respectively to the right and bottom of the page.
7528 * @param $x (float) The value of the abscissa.
7529 * @param $y (float) The value of the ordinate.
7530 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7533 * @see SetX(), SetY()
7535 public function SetXY($x, $y, $rtloff=false) {
7536 $this->SetY($y, false, $rtloff);
7537 $this->SetX($x, $rtloff);
7541 * Set the absolute X coordinate of the current pointer.
7542 * @param $x (float) The value of the abscissa in user units.
7544 * @since 5.9.186 (2012-09-13)
7545 * @see setAbsX(), setAbsY(), SetAbsXY()
7547 public function SetAbsX($x) {
7548 $this->x
= floatval($x);
7552 * Set the absolute Y coordinate of the current pointer.
7553 * @param $y (float) (float) The value of the ordinate in user units.
7555 * @since 5.9.186 (2012-09-13)
7556 * @see setAbsX(), setAbsY(), SetAbsXY()
7558 public function SetAbsY($y) {
7559 $this->y
= floatval($y);
7563 * Set the absolute X and Y coordinates of the current pointer.
7564 * @param $x (float) The value of the abscissa in user units.
7565 * @param $y (float) (float) The value of the ordinate in user units.
7567 * @since 5.9.186 (2012-09-13)
7568 * @see setAbsX(), setAbsY(), SetAbsXY()
7570 public function SetAbsXY($x, $y) {
7576 * Send the document to a given destination: string, local file or browser.
7577 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
7578 * The method first calls Close() if necessary to terminate the document.
7579 * @param $name (string) The name of the file when saved. Note that special characters are removed and blanks characters are replaced with the underscore character.
7580 * @param $dest (string) Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser (default). The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local server file with the name given by name.</li><li>S: return the document as a string (name is ignored).</li><li>FI: equivalent to F + I option</li><li>FD: equivalent to F + D option</li><li>E: return the document as base64 mime multi-part email attachment (RFC 2045)</li></ul>
7585 public function Output($name='doc.pdf', $dest='I') {
7586 //Output PDF to some destination
7587 //Finish document if necessary
7588 if ($this->state
< 3) {
7591 //Normalize parameters
7592 if (is_bool($dest)) {
7593 $dest = $dest ?
'D' : 'F';
7595 $dest = strtoupper($dest);
7596 if ($dest[0] != 'F') {
7597 $name = preg_replace('/[\s]+/', '_', $name);
7598 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
7601 // *** apply digital signature to the document ***
7602 // get the document content
7603 $pdfdoc = $this->getBuffer();
7604 // remove last newline
7605 $pdfdoc = substr($pdfdoc, 0, -1);
7606 // Remove the original buffer
7607 if (isset($this->diskcache
) AND $this->diskcache
) {
7608 // remove buffer file from cache
7609 unlink($this->buffer
);
7611 unset($this->buffer
);
7612 // remove filler space
7613 $byterange_string_len = strlen(TCPDF_STATIC
::$byterange_string);
7614 // define the ByteRange
7615 $byte_range = array();
7617 $byte_range[1] = strpos($pdfdoc, TCPDF_STATIC
::$byterange_string) +
$byterange_string_len +
10;
7618 $byte_range[2] = $byte_range[1] +
$this->signature_max_length +
2;
7619 $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
7620 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
7621 // replace the ByteRange
7622 $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
7623 $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
7624 $pdfdoc = str_replace(TCPDF_STATIC
::$byterange_string, $byterange, $pdfdoc);
7625 // write the document to a temporary folder
7626 $tempdoc = TCPDF_STATIC
::getObjFilename('doc');
7627 $f = fopen($tempdoc, 'wb');
7629 $this->Error('Unable to create temporary file: '.$tempdoc);
7631 $pdfdoc_length = strlen($pdfdoc);
7632 fwrite($f, $pdfdoc, $pdfdoc_length);
7634 // get digital signature via openssl library
7635 $tempsign = TCPDF_STATIC
::getObjFilename('sig');
7636 if (empty($this->signature_data
['extracerts'])) {
7637 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data
['signcert'], array($this->signature_data
['privkey'], $this->signature_data
['password']), array(), PKCS7_BINARY | PKCS7_DETACHED
);
7639 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data
['signcert'], array($this->signature_data
['privkey'], $this->signature_data
['password']), array(), PKCS7_BINARY | PKCS7_DETACHED
, $this->signature_data
['extracerts']);
7643 $signature = file_get_contents($tempsign);
7645 // extract signature
7646 $signature = substr($signature, $pdfdoc_length);
7647 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") +
13));
7648 $tmparr = explode("\n\n", $signature);
7649 $signature = $tmparr[1];
7652 $signature = base64_decode(trim($signature));
7653 // add TSA timestamp to signature
7654 $signature = $this->applyTSA($signature);
7655 // convert signature to hex
7656 $signature = current(unpack('H*', $signature));
7657 $signature = str_pad($signature, $this->signature_max_length
, '0');
7658 // disable disk caching
7659 $this->diskcache
= false;
7660 // Add signature to the document
7661 $this->buffer
= substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
7662 $this->bufferlen
= strlen($this->buffer
);
7666 // Send PDF to the standard output
7667 if (ob_get_contents()) {
7668 $this->Error('Some data has already been output, can\'t send PDF file');
7670 if (php_sapi_name() != 'cli') {
7671 // send output to a browser
7672 header('Content-Type: application/pdf');
7673 if (headers_sent()) {
7674 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7676 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7677 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7678 header('Pragma: public');
7679 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7680 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7681 header('Content-Disposition: inline; filename="'.basename($name).'"');
7682 TCPDF_STATIC
::sendOutputData($this->getBuffer(), $this->bufferlen
);
7684 echo $this->getBuffer();
7689 // download PDF as file
7690 if (ob_get_contents()) {
7691 $this->Error('Some data has already been output, can\'t send PDF file');
7693 header('Content-Description: File Transfer');
7694 if (headers_sent()) {
7695 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7697 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7698 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7699 header('Pragma: public');
7700 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7701 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7702 // force download dialog
7703 if (strpos(php_sapi_name(), 'cgi') === false) {
7704 header('Content-Type: application/force-download');
7705 header('Content-Type: application/octet-stream', false);
7706 header('Content-Type: application/download', false);
7707 header('Content-Type: application/pdf', false);
7709 header('Content-Type: application/pdf');
7711 // use the Content-Disposition header to supply a recommended filename
7712 header('Content-Disposition: attachment; filename="'.basename($name).'"');
7713 header('Content-Transfer-Encoding: binary');
7714 TCPDF_STATIC
::sendOutputData($this->getBuffer(), $this->bufferlen
);
7720 // save PDF to a local file
7721 if ($this->diskcache
) {
7722 copy($this->buffer
, $name);
7724 $f = fopen($name, 'wb');
7726 $this->Error('Unable to create output file: '.$name);
7728 fwrite($f, $this->getBuffer(), $this->bufferlen
);
7731 if ($dest == 'FI') {
7732 // send headers to browser
7733 header('Content-Type: application/pdf');
7734 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7735 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7736 header('Pragma: public');
7737 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7738 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7739 header('Content-Disposition: inline; filename="'.basename($name).'"');
7740 TCPDF_STATIC
::sendOutputData(file_get_contents($name), filesize($name));
7741 } elseif ($dest == 'FD') {
7742 // send headers to browser
7743 if (ob_get_contents()) {
7744 $this->Error('Some data has already been output, can\'t send PDF file');
7746 header('Content-Description: File Transfer');
7747 if (headers_sent()) {
7748 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7750 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7751 header('Pragma: public');
7752 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7753 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7754 // force download dialog
7755 if (strpos(php_sapi_name(), 'cgi') === false) {
7756 header('Content-Type: application/force-download');
7757 header('Content-Type: application/octet-stream', false);
7758 header('Content-Type: application/download', false);
7759 header('Content-Type: application/pdf', false);
7761 header('Content-Type: application/pdf');
7763 // use the Content-Disposition header to supply a recommended filename
7764 header('Content-Disposition: attachment; filename="'.basename($name).'"');
7765 header('Content-Transfer-Encoding: binary');
7766 TCPDF_STATIC
::sendOutputData(file_get_contents($name), filesize($name));
7771 // return PDF as base64 mime multi-part email attachment (RFC 2045)
7772 $retval = 'Content-Type: application/pdf;'."\r\n";
7773 $retval .= ' name="'.$name.'"'."\r\n";
7774 $retval .= 'Content-Transfer-Encoding: base64'."\r\n";
7775 $retval .= 'Content-Disposition: attachment;'."\r\n";
7776 $retval .= ' filename="'.$name.'"'."\r\n\r\n";
7777 $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
7781 // returns PDF as a string
7782 return $this->getBuffer();
7785 $this->Error('Incorrect output destination: '.$dest);
7792 * Unset all class variables except the following critical variables.
7793 * @param $destroyall (boolean) if true destroys all class variables, otherwise preserves critical variables.
7794 * @param $preserve_objcopy (boolean) if true preserves the objcopy variable
7796 * @since 4.5.016 (2009-02-24)
7798 public function _destroy($destroyall=false, $preserve_objcopy=false) {
7799 if ($destroyall AND isset($this->diskcache
) AND $this->diskcache
AND (!$preserve_objcopy) AND (!TCPDF_STATIC
::empty_string($this->buffer
))) {
7800 // remove buffer file from cache
7801 unlink($this->buffer
);
7803 if ($destroyall AND !empty($this->cached_files
)) {
7804 // remove cached files
7805 foreach ($this->cached_files
as $cachefile) {
7806 if (is_file($cachefile)) {
7810 unset($this->cached_files
);
7813 'internal_encoding',
7821 'signature_max_length',
7826 foreach (array_keys(get_object_vars($this)) as $val) {
7827 if ($destroyall OR !in_array($val, $preserve)) {
7828 if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) {
7836 * Check for locale-related bug
7839 protected function _dochecks() {
7840 //Check for locale-related bug
7842 $this->Error('Don\'t alter the locale before including class file');
7844 //Check for decimal separator
7845 if (sprintf('%.1F', 1.0) != '1.0') {
7846 setlocale(LC_NUMERIC
, 'C');
7851 * Return an array containing variations for the basic page number alias.
7852 * @param $a (string) Base alias.
7853 * @return array of page number aliases
7856 protected function getInternalPageNumberAliases($a= '') {
7858 // build array of Unicode + ASCII variants (the order is important)
7859 $alias = array('u' => array(), 'a' => array());
7861 $alias['u'][] = TCPDF_STATIC
::_escape($u);
7862 if ($this->isunicode
) {
7863 $alias['u'][] = TCPDF_STATIC
::_escape(TCPDF_FONTS
::UTF8ToLatin1($u, $this->isunicode
, $this->CurrentFont
));
7864 $alias['u'][] = TCPDF_STATIC
::_escape(TCPDF_FONTS
::utf8StrRev($u, false, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
));
7865 $alias['a'][] = TCPDF_STATIC
::_escape(TCPDF_FONTS
::UTF8ToLatin1($a, $this->isunicode
, $this->CurrentFont
));
7866 $alias['a'][] = TCPDF_STATIC
::_escape(TCPDF_FONTS
::utf8StrRev($a, false, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
));
7868 $alias['a'][] = TCPDF_STATIC
::_escape($a);
7873 * Return an array containing all internal page aliases.
7874 * @return array of page number aliases
7877 protected function getAllInternalPageNumberAliases() {
7878 $basic_alias = array(TCPDF_STATIC
::$alias_tot_pages, TCPDF_STATIC
::$alias_num_page, TCPDF_STATIC
::$alias_group_tot_pages, TCPDF_STATIC
::$alias_group_num_page, TCPDF_STATIC
::$alias_right_shift);
7880 foreach($basic_alias as $k => $a) {
7881 $pnalias[$k] = $this->getInternalPageNumberAliases($a);
7887 * Replace right shift page number aliases with spaces to correct right alignment.
7888 * This works perfectly only when using monospaced fonts.
7889 * @param $page (string) Page content.
7890 * @param $aliases (array) Array of page aliases.
7891 * @param $diff (int) initial difference to add.
7892 * @return replaced page content.
7895 protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
7896 foreach ($aliases as $type => $alias) {
7897 foreach ($alias as $a) {
7898 // find position of compensation factor
7899 $startnum = (strpos($a, ':') +
1);
7900 $a = substr($a, 0, $startnum);
7901 if (($pos = strpos($page, $a)) !== false) {
7903 $endnum = strpos($page, '}', $pos);
7904 // string to be replaced
7905 $aa = substr($page, $pos, ($endnum - $pos +
1));
7906 // get compensation factor
7907 $ratio = substr($page, ($pos +
$startnum), ($endnum - $pos - $startnum));
7908 $ratio = preg_replace('/[^0-9\.]/', '', $ratio);
7909 $ratio = floatval($ratio);
7911 $chrdiff = floor(($diff +
12) * $ratio);
7912 $shift = str_repeat(' ', $chrdiff);
7913 $shift = TCPDF_FONTS
::UTF8ToUTF16BE($shift, false, $this->isunicode
, $this->CurrentFont
);
7915 $chrdiff = floor(($diff +
11) * $ratio);
7916 $shift = str_repeat(' ', $chrdiff);
7918 $page = str_replace($aa, $shift, $page);
7926 * Set page boxes to be included on page descriptions.
7927 * @param $boxes (array) Array of page boxes to set on document: ('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox').
7930 protected function setPageBoxTypes($boxes) {
7931 $this->page_boxes
= array();
7932 foreach ($boxes as $box) {
7933 if (in_array($box, TCPDF_STATIC
::$pageboxes)) {
7934 $this->page_boxes
[] = $box;
7940 * Output pages (and replace page number aliases).
7943 protected function _putpages() {
7944 $filter = ($this->compress
) ?
'/Filter /FlateDecode ' : '';
7945 // get internal aliases for page numbers
7946 $pnalias = $this->getAllInternalPageNumberAliases();
7947 $num_pages = $this->numpages
;
7948 $ptpa = TCPDF_STATIC
::formatPageNumber(($this->starting_page_number +
$num_pages - 1));
7949 $ptpu = TCPDF_FONTS
::UTF8ToUTF16BE($ptpa, false, $this->isunicode
, $this->CurrentFont
);
7950 $ptp_num_chars = $this->GetNumChars($ptpa);
7956 for ($n = 1; $n <= $num_pages; ++
$n) {
7958 $temppage = $this->getPageBuffer($n);
7959 $pagelen = strlen($temppage);
7960 // set replacements for total pages number
7961 $pnpa = TCPDF_STATIC
::formatPageNumber(($this->starting_page_number +
$n - 1));
7962 $pnpu = TCPDF_FONTS
::UTF8ToUTF16BE($pnpa, false, $this->isunicode
, $this->CurrentFont
);
7963 $pnp_num_chars = $this->GetNumChars($pnpa);
7964 $pdiff = 0; // difference used for right shift alignment of page numbers
7965 $gdiff = 0; // difference used for right shift alignment of page group numbers
7966 if (!empty($this->pagegroups
)) {
7967 if (isset($this->newpagegroup
[$n])) {
7970 $ptga = TCPDF_STATIC
::formatPageNumber($this->pagegroups
[$groupnum]);
7971 $ptgu = TCPDF_FONTS
::UTF8ToUTF16BE($ptga, false, $this->isunicode
, $this->CurrentFont
);
7972 $ptg_num_chars = $this->GetNumChars($ptga);
7975 $pnga = TCPDF_STATIC
::formatPageNumber($pagegroupnum);
7976 $pngu = TCPDF_FONTS
::UTF8ToUTF16BE($pnga, false, $this->isunicode
, $this->CurrentFont
);
7977 $png_num_chars = $this->GetNumChars($pnga);
7978 // replace page numbers
7980 $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
7981 $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
7982 $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
7983 $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
7984 list($temppage, $gdiff) = TCPDF_STATIC
::replacePageNumAliases($temppage, $replace, $gdiff);
7986 // replace page numbers
7988 $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
7989 $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
7990 $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
7991 $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
7992 list($temppage, $pdiff) = TCPDF_STATIC
::replacePageNumAliases($temppage, $replace, $pdiff);
7993 // replace right shift alias
7994 $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
7995 // replace EPS marker
7996 $temppage = str_replace($this->epsmarker
, '', $temppage);
7998 $this->page_obj_id
[$n] = $this->_newobj();
8000 $out .= ' /Type /Page';
8001 $out .= ' /Parent 1 0 R';
8002 if (empty($this->signature_data
['approval']) OR ($this->signature_data
['approval'] != 'A')) {
8003 $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp
);
8005 $out .= ' /Resources 2 0 R';
8006 foreach ($this->page_boxes
as $box) {
8008 $out .= sprintf(' [%F %F %F %F]', $this->pagedim
[$n][$box]['llx'], $this->pagedim
[$n][$box]['lly'], $this->pagedim
[$n][$box]['urx'], $this->pagedim
[$n][$box]['ury']);
8010 if (isset($this->pagedim
[$n]['BoxColorInfo']) AND !empty($this->pagedim
[$n]['BoxColorInfo'])) {
8011 $out .= ' /BoxColorInfo <<';
8012 foreach ($this->page_boxes
as $box) {
8013 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box])) {
8014 $out .= ' /'.$box.' <<';
8015 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box]['C'])) {
8016 $color = $this->pagedim
[$n]['BoxColorInfo'][$box]['C'];
8018 $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
8021 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box]['W'])) {
8022 $out .= ' /W '.($this->pagedim
[$n]['BoxColorInfo'][$box]['W'] * $this->k
);
8024 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box]['S'])) {
8025 $out .= ' /S /'.$this->pagedim
[$n]['BoxColorInfo'][$box]['S'];
8027 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box]['D'])) {
8028 $dashes = $this->pagedim
[$n]['BoxColorInfo'][$box]['D'];
8030 foreach ($dashes as $dash) {
8031 $out .= sprintf(' %F', ($dash * $this->k
));
8040 $out .= ' /Contents '.($this->n +
1).' 0 R';
8041 $out .= ' /Rotate '.$this->pagedim
[$n]['Rotate'];
8042 if (!$this->pdfa_mode
) {
8043 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
8045 if (isset($this->pagedim
[$n]['trans']) AND !empty($this->pagedim
[$n]['trans'])) {
8047 if (isset($this->pagedim
[$n]['trans']['Dur'])) {
8048 $out .= ' /Dur '.$this->pagedim
[$n]['trans']['Dur'];
8050 $out .= ' /Trans <<';
8051 $out .= ' /Type /Trans';
8052 if (isset($this->pagedim
[$n]['trans']['S'])) {
8053 $out .= ' /S /'.$this->pagedim
[$n]['trans']['S'];
8055 if (isset($this->pagedim
[$n]['trans']['D'])) {
8056 $out .= ' /D '.$this->pagedim
[$n]['trans']['D'];
8058 if (isset($this->pagedim
[$n]['trans']['Dm'])) {
8059 $out .= ' /Dm /'.$this->pagedim
[$n]['trans']['Dm'];
8061 if (isset($this->pagedim
[$n]['trans']['M'])) {
8062 $out .= ' /M /'.$this->pagedim
[$n]['trans']['M'];
8064 if (isset($this->pagedim
[$n]['trans']['Di'])) {
8065 $out .= ' /Di '.$this->pagedim
[$n]['trans']['Di'];
8067 if (isset($this->pagedim
[$n]['trans']['SS'])) {
8068 $out .= ' /SS '.$this->pagedim
[$n]['trans']['SS'];
8070 if (isset($this->pagedim
[$n]['trans']['B'])) {
8071 $out .= ' /B '.$this->pagedim
[$n]['trans']['B'];
8075 $out .= $this->_getannotsrefs($n);
8076 $out .= ' /PZ '.$this->pagedim
[$n]['PZ'];
8078 $out .= "\n".'endobj';
8081 $p = ($this->compress
) ?
gzcompress($temppage) : $temppage;
8083 $p = $this->_getrawstream($p);
8084 $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
8085 if ($this->diskcache
) {
8086 // remove temporary files
8087 unlink($this->pages
[$n]);
8091 $out = $this->_getobj(1)."\n";
8092 $out .= '<< /Type /Pages /Kids [';
8093 foreach($this->page_obj_id
as $page_obj) {
8094 $out .= ' '.$page_obj.' 0 R';
8096 $out .= ' ] /Count '.$num_pages.' >>';
8097 $out .= "\n".'endobj';
8102 * Output references to page annotations
8103 * @param $n (int) page number
8105 * @author Nicola Asuni
8106 * @since 4.7.000 (2008-08-29)
8109 protected function _putannotsrefs($n) {
8110 $this->_out($this->_getannotsrefs($n));
8114 * Get references to page annotations.
8115 * @param $n (int) page number
8118 * @author Nicola Asuni
8119 * @since 5.0.010 (2010-05-17)
8121 protected function _getannotsrefs($n) {
8122 if (!(isset($this->PageAnnots
[$n]) OR ($this->sign
AND isset($this->signature_data
['cert_type'])))) {
8125 $out = ' /Annots [';
8126 if (isset($this->PageAnnots
[$n])) {
8127 foreach ($this->PageAnnots
[$n] as $key => $val) {
8128 if (!in_array($val['n'], $this->radio_groups
)) {
8129 $out .= ' '.$val['n'].' 0 R';
8132 // add radiobutton groups
8133 if (isset($this->radiobutton_groups
[$n])) {
8134 foreach ($this->radiobutton_groups
[$n] as $key => $data) {
8135 if (isset($data['n'])) {
8136 $out .= ' '.$data['n'].' 0 R';
8141 if ($this->sign
AND ($n == $this->signature_appearance
['page']) AND isset($this->signature_data
['cert_type'])) {
8142 // set reference for signature object
8143 $out .= ' '.$this->sig_obj_id
.' 0 R';
8145 if (!empty($this->empty_signature_appearance
)) {
8146 foreach ($this->empty_signature_appearance
as $esa) {
8147 if ($esa['page'] == $n) {
8148 // set reference for empty signature objects
8149 $out .= ' '.$esa['objid'].' 0 R';
8158 * Output annotations objects for all pages.
8159 * !!! THIS METHOD IS NOT YET COMPLETED !!!
8160 * See section 12.5 of PDF 32000_2008 reference.
8162 * @author Nicola Asuni
8163 * @since 4.0.018 (2008-08-06)
8165 protected function _putannotsobjs() {
8166 // reset object counter
8167 for ($n=1; $n <= $this->numpages
; ++
$n) {
8168 if (isset($this->PageAnnots
[$n])) {
8169 // set page annotations
8170 foreach ($this->PageAnnots
[$n] as $key => $pl) {
8171 $annot_obj_id = $this->PageAnnots
[$n][$key]['n'];
8172 // create annotation object for grouping radiobuttons
8173 if (isset($this->radiobutton_groups
[$n][$pl['txt']]) AND is_array($this->radiobutton_groups
[$n][$pl['txt']])) {
8174 $radio_button_obj_id = $this->radiobutton_groups
[$n][$pl['txt']]['n'];
8176 $annots .= ' /Type /Annot';
8177 $annots .= ' /Subtype /Widget';
8178 $annots .= ' /Rect [0 0 0 0]';
8179 if ($this->radiobutton_groups
[$n][$pl['txt']]['#readonly#']) {
8181 $annots .= ' /F 68';
8182 $annots .= ' /Ff 49153';
8184 $annots .= ' /F 4'; // default print for PDF/A
8185 $annots .= ' /Ff 49152';
8187 $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
8188 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8189 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id);
8191 $annots .= ' /FT /Btn';
8192 $annots .= ' /Kids [';
8194 foreach ($this->radiobutton_groups
[$n][$pl['txt']] as $key => $data) {
8195 if (isset($data['kid'])) {
8196 $annots .= ' '.$data['kid'].' 0 R';
8197 if ($data['def'] !== 'Off') {
8198 $defval = $data['def'];
8203 if (!empty($defval)) {
8204 $annots .= ' /V /'.$defval;
8207 $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
8208 $this->form_obj_id
[] = $radio_button_obj_id;
8209 // store object id to be used on Parent entry of Kids
8210 $this->radiobutton_groups
[$n][$pl['txt']] = $radio_button_obj_id;
8213 $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER
);
8214 $a = $pl['x'] * $this->k
;
8215 $b = $this->pagedim
[$n]['h'] - (($pl['y'] +
$pl['h']) * $this->k
);
8216 $c = $pl['w'] * $this->k
;
8217 $d = $pl['h'] * $this->k
;
8218 $rect = sprintf('%F %F %F %F', $a, $b, $a+
$c, $b+
$d);
8219 // create new annotation object
8220 $annots = '<</Type /Annot';
8221 $annots .= ' /Subtype /'.$pl['opt']['subtype'];
8222 $annots .= ' /Rect ['.$rect.']';
8223 $ft = array('Btn', 'Tx', 'Ch', 'Sig');
8224 if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
8225 $annots .= ' /FT /'.$pl['opt']['ft'];
8228 $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
8229 $annots .= ' /P '.$this->page_obj_id
[$n].' 0 R';
8230 $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
8231 $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp
);
8232 if (isset($pl['opt']['f'])) {
8234 if (is_array($pl['opt']['f'])) {
8235 foreach ($pl['opt']['f'] as $f) {
8236 switch (strtolower($f)) {
8269 case 'togglenoview': {
8273 case 'lockedcontents': {
8283 $fval = intval($pl['opt']['f']);
8288 if ($this->pdfa_mode
) {
8289 // force print flag for PDF/A mode
8292 $annots .= ' /F '.intval($fval);
8293 if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
8294 $annots .= ' /AS /'.$pl['opt']['as'];
8296 if (isset($pl['opt']['ap'])) {
8297 // appearance stream
8298 $annots .= ' /AP <<';
8299 if (is_array($pl['opt']['ap'])) {
8300 foreach ($pl['opt']['ap'] as $apmode => $apdef) {
8301 // $apmode can be: n = normal; r = rollover; d = down;
8302 $annots .= ' /'.strtoupper($apmode);
8303 if (is_array($apdef)) {
8305 foreach ($apdef as $apstate => $stream) {
8306 // reference to XObject that define the appearance for this mode-state
8307 $apsobjid = $this->_putAPXObject($c, $d, $stream);
8308 $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
8312 // reference to XObject that define the appearance for this mode
8313 $apsobjid = $this->_putAPXObject($c, $d, $apdef);
8314 $annots .= ' '.$apsobjid.' 0 R';
8318 $annots .= $pl['opt']['ap'];
8322 if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
8323 $annots .= ' /BS <<';
8324 $annots .= ' /Type /Border';
8325 if (isset($pl['opt']['bs']['w'])) {
8326 $annots .= ' /W '.intval($pl['opt']['bs']['w']);
8328 $bstyles = array('S', 'D', 'B', 'I', 'U');
8329 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
8330 $annots .= ' /S /'.$pl['opt']['bs']['s'];
8332 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
8334 foreach ($pl['opt']['bs']['d'] as $cord) {
8335 $annots .= ' '.intval($cord);
8341 $annots .= ' /Border [';
8342 if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
8343 $annots .= intval($pl['opt']['border'][0]).' ';
8344 $annots .= intval($pl['opt']['border'][1]).' ';
8345 $annots .= intval($pl['opt']['border'][2]);
8346 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
8348 foreach ($pl['opt']['border'][3] as $dash) {
8349 $annots .= intval($dash).' ';
8358 if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
8359 $annots .= ' /BE <<';
8360 $bstyles = array('S', 'C');
8361 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) {
8362 $annots .= ' /S /'.$pl['opt']['bs']['s'];
8364 $annots .= ' /S /S';
8366 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
8367 $annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']);
8371 if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
8372 $annots .= ' /C '.TCPDF_COLORS
::getColorStringFromArray($pl['opt']['c']);
8374 //$annots .= ' /StructParent ';
8375 //$annots .= ' /OC ';
8376 $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
8377 if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
8378 // this is a markup type
8379 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8380 $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
8382 //$annots .= ' /Popup ';
8383 if (isset($pl['opt']['ca'])) {
8384 $annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca']));
8386 if (isset($pl['opt']['rc'])) {
8387 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8389 $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp
);
8390 //$annots .= ' /IRT ';
8391 if (isset($pl['opt']['subj'])) {
8392 $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
8394 //$annots .= ' /RT ';
8395 //$annots .= ' /IT ';
8396 //$annots .= ' /ExData ';
8398 $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
8400 switch (strtolower($pl['opt']['subtype'])) {
8402 if (isset($pl['opt']['open'])) {
8403 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ?
'true' : 'false');
8405 $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
8406 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8407 $annots .= ' /Name /'.$pl['opt']['name'];
8409 $annots .= ' /Name /Note';
8411 $statemodels = array('Marked', 'Review');
8412 if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
8413 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8415 $pl['opt']['statemodel'] = 'Marked';
8416 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8418 if ($pl['opt']['statemodel'] == 'Marked') {
8419 $states = array('Accepted', 'Unmarked');
8421 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
8423 if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
8424 $annots .= ' /State /'.$pl['opt']['state'];
8426 if ($pl['opt']['statemodel'] == 'Marked') {
8427 $annots .= ' /State /Unmarked';
8429 $annots .= ' /State /None';
8435 if (is_string($pl['txt'])) {
8436 if ($pl['txt'][0] == '#') {
8437 // internal destination
8438 $annots .= ' /Dest /'.TCPDF_STATIC
::encodeNameObject(substr($pl['txt'], 1));
8439 } elseif ($pl['txt'][0] == '%') {
8440 // embedded PDF file
8441 $filename = basename(substr($pl['txt'], 1));
8442 $annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles
[$filename]['a'].' >> >>';
8443 } elseif ($pl['txt'][0] == '*') {
8444 // embedded generic file
8445 $filename = basename(substr($pl['txt'], 1));
8446 $jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});';
8447 $annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>';
8449 $parsedUrl = parse_url($pl['txt']);
8450 if (empty($parsedUrl['scheme']) AND (strtolower(substr($parsedUrl['path'], -4)) == '.pdf')) {
8451 // relative link to a PDF file
8452 $dest = '[0 /Fit]'; // default page 0
8453 if (!empty($parsedUrl['fragment'])) {
8454 // check for named destination
8455 $tmp = explode('=', $parsedUrl['fragment']);
8456 $dest = '('.((count($tmp) == 2) ?
$tmp[1] : $tmp[0]).')';
8458 $annots .= ' /A <</S /GoToR /D '.$dest.' /F '.$this->_datastring($this->unhtmlentities($parsedUrl['path']), $annot_obj_id).' /NewWindow true>>';
8460 // external URI link
8461 $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
8464 } elseif (isset($this->links
[$pl['txt']])) {
8466 $l = $this->links
[$pl['txt']];
8467 if (isset($this->page_obj_id
[($l['p'])])) {
8468 $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id
[($l['p'])], ($this->pagedim
[$l['p']]['h'] - ($l['y'] * $this->k
)));
8471 $hmodes = array('N', 'I', 'O', 'P');
8472 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
8473 $annots .= ' /H /'.$pl['opt']['h'];
8475 $annots .= ' /H /I';
8477 //$annots .= ' /PA ';
8478 //$annots .= ' /Quadpoints ';
8482 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8483 $annots .= ' /DA ('.$pl['opt']['da'].')';
8485 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8486 $annots .= ' /Q '.intval($pl['opt']['q']);
8488 if (isset($pl['opt']['rc'])) {
8489 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8491 if (isset($pl['opt']['ds'])) {
8492 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
8494 if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
8495 $annots .= ' /CL [';
8496 foreach ($pl['opt']['cl'] as $cl) {
8497 $annots .= sprintf('%F ', $cl * $this->k
);
8501 $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
8502 if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
8503 $annots .= ' /IT /'.$pl['opt']['it'];
8505 if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
8506 $l = $pl['opt']['rd'][0] * $this->k
;
8507 $r = $pl['opt']['rd'][1] * $this->k
;
8508 $t = $pl['opt']['rd'][2] * $this->k
;
8509 $b = $pl['opt']['rd'][3] * $this->k
;
8510 $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']';
8512 if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
8513 $annots .= ' /LE /'.$pl['opt']['le'];
8556 case 'fileattachment': {
8557 if ($this->pdfa_mode
) {
8558 // embedded files are not allowed in PDF/A mode
8561 if (!isset($pl['opt']['fs'])) {
8564 $filename = basename($pl['opt']['fs']);
8565 if (isset($this->embeddedfiles
[$filename]['f'])) {
8566 $annots .= ' /FS '.$this->embeddedfiles
[$filename]['f'].' 0 R';
8567 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
8568 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8569 $annots .= ' /Name /'.$pl['opt']['name'];
8571 $annots .= ' /Name /PushPin';
8573 // index (zero-based) of the annotation in the Annots array of this page
8574 $this->embeddedfiles
[$filename]['a'] = $key;
8579 if (!isset($pl['opt']['fs'])) {
8582 $filename = basename($pl['opt']['fs']);
8583 if (isset($this->embeddedfiles
[$filename]['f'])) {
8584 // ... TO BE COMPLETED ...
8585 // /R /C /B /E /CO /CP
8586 $annots .= ' /Sound '.$this->embeddedfiles
[$filename]['f'].' 0 R';
8587 $iconsapp = array('Speaker', 'Mic');
8588 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8589 $annots .= ' /Name /'.$pl['opt']['name'];
8591 $annots .= ' /Name /Speaker';
8600 $hmode = array('N', 'I', 'O', 'P', 'T');
8601 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
8602 $annots .= ' /H /'.$pl['opt']['h'];
8604 if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
8605 $annots .= ' /MK <<';
8606 if (isset($pl['opt']['mk']['r'])) {
8607 $annots .= ' /R '.$pl['opt']['mk']['r'];
8609 if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
8610 $annots .= ' /BC '.TCPDF_COLORS
::getColorStringFromArray($pl['opt']['mk']['bc']);
8612 if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
8613 $annots .= ' /BG '.TCPDF_COLORS
::getColorStringFromArray($pl['opt']['mk']['bg']);
8615 if (isset($pl['opt']['mk']['ca'])) {
8616 $annots .= ' /CA '.$pl['opt']['mk']['ca'];
8618 if (isset($pl['opt']['mk']['rc'])) {
8619 $annots .= ' /RC '.$pl['opt']['mk']['rc'];
8621 if (isset($pl['opt']['mk']['ac'])) {
8622 $annots .= ' /AC '.$pl['opt']['mk']['ac'];
8624 if (isset($pl['opt']['mk']['i'])) {
8625 $info = $this->getImageBuffer($pl['opt']['mk']['i']);
8626 if ($info !== false) {
8627 $annots .= ' /I '.$info['n'].' 0 R';
8630 if (isset($pl['opt']['mk']['ri'])) {
8631 $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
8632 if ($info !== false) {
8633 $annots .= ' /RI '.$info['n'].' 0 R';
8636 if (isset($pl['opt']['mk']['ix'])) {
8637 $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
8638 if ($info !== false) {
8639 $annots .= ' /IX '.$info['n'].' 0 R';
8642 if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
8643 $annots .= ' /IF <<';
8644 $if_sw = array('A', 'B', 'S', 'N');
8645 if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
8646 $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
8648 $if_s = array('A', 'P');
8649 if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
8650 $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
8652 if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
8653 $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
8655 if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
8656 $annots .= ' /FB true';
8660 if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
8661 $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
8665 // --- Entries for field dictionaries ---
8666 if (isset($this->radiobutton_groups
[$n][$pl['txt']])) {
8668 $annots .= ' /Parent '.$this->radiobutton_groups
[$n][$pl['txt']].' 0 R';
8670 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8671 $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
8673 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8674 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
8676 if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
8677 $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
8679 if (isset($pl['opt']['ff'])) {
8680 if (is_array($pl['opt']['ff'])) {
8681 // array of bit settings
8683 foreach($pl['opt']['ff'] as $val) {
8684 $flag +
= 1 << ($val - 1);
8687 $flag = intval($pl['opt']['ff']);
8689 $annots .= ' /Ff '.$flag;
8691 if (isset($pl['opt']['maxlen'])) {
8692 $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
8694 if (isset($pl['opt']['v'])) {
8696 if (is_array($pl['opt']['v'])) {
8697 foreach ($pl['opt']['v'] AS $optval) {
8698 if (is_float($optval)) {
8699 $optval = sprintf('%F', $optval);
8701 $annots .= ' '.$optval;
8704 $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
8707 if (isset($pl['opt']['dv'])) {
8709 if (is_array($pl['opt']['dv'])) {
8710 foreach ($pl['opt']['dv'] AS $optval) {
8711 if (is_float($optval)) {
8712 $optval = sprintf('%F', $optval);
8714 $annots .= ' '.$optval;
8717 $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
8720 if (isset($pl['opt']['rv'])) {
8722 if (is_array($pl['opt']['rv'])) {
8723 foreach ($pl['opt']['rv'] AS $optval) {
8724 if (is_float($optval)) {
8725 $optval = sprintf('%F', $optval);
8727 $annots .= ' '.$optval;
8730 $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
8733 if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
8734 $annots .= ' /A << '.$pl['opt']['a'].' >>';
8736 if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
8737 $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
8739 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8740 $annots .= ' /DA ('.$pl['opt']['da'].')';
8742 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8743 $annots .= ' /Q '.intval($pl['opt']['q']);
8745 if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
8746 $annots .= ' /Opt [';
8747 foreach($pl['opt']['opt'] AS $copt) {
8748 if (is_array($copt)) {
8749 $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
8751 $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
8756 if (isset($pl['opt']['ti'])) {
8757 $annots .= ' /TI '.intval($pl['opt']['ti']);
8759 if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
8761 foreach($pl['opt']['i'] AS $copt) {
8762 $annots .= intval($copt).' ';
8771 case 'printermark': {
8788 // create new annotation object
8789 $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
8790 if ($formfield AND !isset($this->radiobutton_groups
[$n][$pl['txt']])) {
8791 // store reference of form object
8792 $this->form_obj_id
[] = $annot_obj_id;
8796 } // end for each page
8800 * Put appearance streams XObject used to define annotation's appearance states.
8801 * @param $w (int) annotation width
8802 * @param $h (int) annotation height
8803 * @param $stream (string) appearance stream
8804 * @return int object ID
8806 * @since 4.8.001 (2009-09-09)
8808 protected function _putAPXObject($w=0, $h=0, $stream='') {
8809 $stream = trim($stream);
8810 $out = $this->_getobj()."\n";
8811 $this->xobjects
['AX'.$this->n
] = array('n' => $this->n
);
8813 $out .= ' /Type /XObject';
8814 $out .= ' /Subtype /Form';
8815 $out .= ' /FormType 1';
8816 if ($this->compress
) {
8817 $stream = gzcompress($stream);
8818 $out .= ' /Filter /FlateDecode';
8820 $rect = sprintf('%F %F', $w, $h);
8821 $out .= ' /BBox [0 0 '.$rect.']';
8822 $out .= ' /Matrix [1 0 0 1 0 0]';
8823 $out .= ' /Resources 2 0 R';
8824 $stream = $this->_getrawstream($stream);
8825 $out .= ' /Length '.strlen($stream);
8827 $out .= ' stream'."\n".$stream."\n".'endstream';
8828 $out .= "\n".'endobj';
8835 * @author Nicola Asuni
8838 protected function _putfonts() {
8840 foreach ($this->diffs
as $diff) {
8843 $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
8845 $mqr = TCPDF_STATIC
::get_mqr();
8846 TCPDF_STATIC
::set_mqr(false);
8847 foreach ($this->FontFiles
as $file => $info) {
8848 // search and get font file to embedd
8849 $fontfile = TCPDF_FONTS
::getFontFullPath($file, $info['fontdir']);
8850 if (!TCPDF_STATIC
::empty_string($fontfile)) {
8851 $font = file_get_contents($fontfile);
8852 $compressed = (substr($file, -2) == '.z');
8853 if ((!$compressed) AND (isset($info['length2']))) {
8854 $header = (ord($font[0]) == 128);
8856 // strip first binary header
8857 $font = substr($font, 6);
8859 if ($header AND (ord($font[$info['length1']]) == 128)) {
8860 // strip second binary header
8861 $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] +
6));
8863 } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
8866 $font = gzuncompress($font);
8868 // merge subset characters
8869 $subsetchars = array(); // used chars
8870 foreach ($info['fontkeys'] as $fontkey) {
8871 $fontinfo = $this->getFontBuffer($fontkey);
8872 $subsetchars +
= $fontinfo['subsetchars'];
8874 // rebuild a font subset
8875 $font = TCPDF_FONTS
::_getTrueTypeFontSubset($font, $subsetchars);
8876 // calculate new font length
8877 $info['length1'] = strlen($font);
8880 $font = gzcompress($font);
8884 $this->FontFiles
[$file]['n'] = $this->n
;
8885 $stream = $this->_getrawstream($font);
8886 $out = '<< /Length '.strlen($stream);
8888 $out .= ' /Filter /FlateDecode';
8890 $out .= ' /Length1 '.$info['length1'];
8891 if (isset($info['length2'])) {
8892 $out .= ' /Length2 '.$info['length2'].' /Length3 0';
8895 $out .= ' stream'."\n".$stream."\n".'endstream';
8896 $out .= "\n".'endobj';
8900 TCPDF_STATIC
::set_mqr($mqr);
8901 foreach ($this->fontkeys
as $k) {
8903 $font = $this->getFontBuffer($k);
8904 $type = $font['type'];
8905 $name = $font['name'];
8906 if ($type == 'core') {
8907 // standard core font
8908 $out = $this->_getobj($this->font_obj_ids
[$k])."\n";
8909 $out .= '<</Type /Font';
8910 $out .= ' /Subtype /Type1';
8911 $out .= ' /BaseFont /'.$name;
8912 $out .= ' /Name /F'.$font['i'];
8913 if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
8914 $out .= ' /Encoding /WinAnsiEncoding';
8916 if ($k == 'helvetica') {
8917 // add default font for annotations
8918 $this->annotation_fonts
[$k] = $font['i'];
8921 $out .= "\n".'endobj';
8923 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
8924 // additional Type1 or TrueType font
8925 $out = $this->_getobj($this->font_obj_ids
[$k])."\n";
8926 $out .= '<</Type /Font';
8927 $out .= ' /Subtype /'.$type;
8928 $out .= ' /BaseFont /'.$name;
8929 $out .= ' /Name /F'.$font['i'];
8930 $out .= ' /FirstChar 32 /LastChar 255';
8931 $out .= ' /Widths '.($this->n +
1).' 0 R';
8932 $out .= ' /FontDescriptor '.($this->n +
2).' 0 R';
8934 if (isset($font['diff'])) {
8935 $out .= ' /Encoding '.($nf +
$font['diff']).' 0 R';
8937 $out .= ' /Encoding /WinAnsiEncoding';
8941 $out .= "\n".'endobj';
8946 for ($i = 32; $i < 256; ++
$i) {
8947 if (isset($font['cw'][$i])) {
8948 $s .= $font['cw'][$i].' ';
8950 $s .= $font['dw'].' ';
8954 $s .= "\n".'endobj';
8958 $s = '<</Type /FontDescriptor /FontName /'.$name;
8959 foreach ($font['desc'] as $fdk => $fdv) {
8960 if (is_float($fdv)) {
8961 $fdv = sprintf('%F', $fdv);
8963 $s .= ' /'.$fdk.' '.$fdv.'';
8965 if (!TCPDF_STATIC
::empty_string($font['file'])) {
8966 $s .= ' /FontFile'.($type == 'Type1' ?
'' : '2').' '.$this->FontFiles
[$font['file']]['n'].' 0 R';
8969 $s .= "\n".'endobj';
8973 $mtd = '_put'.strtolower($type);
8974 if (!method_exists($this, $mtd)) {
8975 $this->Error('Unsupported font type: '.$type);
8983 * Adds unicode fonts.<br>
8984 * Based on PDF Reference 1.3 (section 5)
8985 * @param $font (array) font data
8987 * @author Nicola Asuni
8988 * @since 1.52.0.TC005 (2005-01-05)
8990 protected function _puttruetypeunicode($font) {
8992 if ($font['subset']) {
8993 // change name for font subsetting
8994 $subtag = sprintf('%06u', $font['i']);
8995 $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
8996 $fontname .= $subtag.'+';
8998 $fontname .= $font['name'];
9000 // A composite font composed of other fonts, organized hierarchically
9001 $out = $this->_getobj($this->font_obj_ids
[$font['fontkey']])."\n";
9002 $out .= '<< /Type /Font';
9003 $out .= ' /Subtype /Type0';
9004 $out .= ' /BaseFont /'.$fontname;
9005 $out .= ' /Name /F'.$font['i'];
9006 $out .= ' /Encoding /'.$font['enc'];
9007 $out .= ' /ToUnicode '.($this->n +
1).' 0 R';
9008 $out .= ' /DescendantFonts ['.($this->n +
2).' 0 R]';
9010 $out .= "\n".'endobj';
9012 // ToUnicode map for Identity-H
9013 $stream = TCPDF_FONT_DATA
::$uni_identity_h;
9016 $stream = ($this->compress
) ?
gzcompress($stream) : $stream;
9017 $filter = ($this->compress
) ?
'/Filter /FlateDecode ' : '';
9018 $stream = $this->_getrawstream($stream);
9019 $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
9021 // A CIDFont whose glyph descriptions are based on TrueType font technology
9022 $oid = $this->_newobj();
9023 $out = '<< /Type /Font';
9024 $out .= ' /Subtype /CIDFontType2';
9025 $out .= ' /BaseFont /'.$fontname;
9026 // A dictionary containing entries that define the character collection of the CIDFont.
9027 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9028 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9029 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9030 $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
9031 $out .= ' /FontDescriptor '.($this->n +
1).' 0 R';
9032 $out .= ' /DW '.$font['dw']; // default width
9033 $out .= "\n".TCPDF_FONTS
::_putfontwidths($font, 0);
9034 if (isset($font['ctg']) AND (!TCPDF_STATIC
::empty_string($font['ctg']))) {
9035 $out .= "\n".'/CIDToGIDMap '.($this->n +
2).' 0 R';
9038 $out .= "\n".'endobj';
9041 // A font descriptor describing the CIDFont default metrics other than its glyph widths
9043 $out = '<< /Type /FontDescriptor';
9044 $out .= ' /FontName /'.$fontname;
9045 foreach ($font['desc'] as $key => $value) {
9046 if (is_float($value)) {
9047 $value = sprintf('%F', $value);
9049 $out .= ' /'.$key.' '.$value;
9052 if (!TCPDF_STATIC
::empty_string($font['file'])) {
9053 // A stream containing a TrueType font
9054 $out .= ' /FontFile2 '.$this->FontFiles
[$font['file']]['n'].' 0 R';
9055 $fontdir = $this->FontFiles
[$font['file']]['fontdir'];
9058 $out .= "\n".'endobj';
9060 if (isset($font['ctg']) AND (!TCPDF_STATIC
::empty_string($font['ctg']))) {
9062 // Embed CIDToGIDMap
9063 // A specification of the mapping from CIDs to glyph indices
9064 // search and get CTG font file to embedd
9065 $ctgfile = strtolower($font['ctg']);
9066 // search and get ctg font file to embedd
9067 $fontfile = TCPDF_FONTS
::getFontFullPath($ctgfile, $fontdir);
9068 if (TCPDF_STATIC
::empty_string($fontfile)) {
9069 $this->Error('Font file not found: '.$ctgfile);
9071 $stream = $this->_getrawstream(file_get_contents($fontfile));
9072 $out = '<< /Length '.strlen($stream).'';
9073 if (substr($fontfile, -2) == '.z') { // check file extension
9074 // Decompresses data encoded using the public-domain
9075 // zlib/deflate compression method, reproducing the
9076 // original text or binary data
9077 $out .= ' /Filter /FlateDecode';
9080 $out .= ' stream'."\n".$stream."\n".'endstream';
9081 $out .= "\n".'endobj';
9087 * Output CID-0 fonts.
9088 * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format
9089 * @param $font (array) font data
9091 * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
9092 * @since 3.2.000 (2008-06-23)
9094 protected function _putcidfont0($font) {
9096 if (!isset($font['cw'][1])) {
9099 if (isset($font['cidinfo']['uni2cid'])) {
9100 // convert unicode to cid.
9101 $uni2cid = $font['cidinfo']['uni2cid'];
9103 foreach ($font['cw'] as $uni => $width) {
9104 if (isset($uni2cid[$uni])) {
9105 $cw[($uni2cid[$uni] +
$cidoffset)] = $width;
9106 } elseif ($uni < 256) {
9108 } // else unknown character
9110 $font = array_merge($font, array('cw' => $cw));
9112 $name = $font['name'];
9113 $enc = $font['enc'];
9115 $longname = $name.'-'.$enc;
9119 $out = $this->_getobj($this->font_obj_ids
[$font['fontkey']])."\n";
9120 $out .= '<</Type /Font';
9121 $out .= ' /Subtype /Type0';
9122 $out .= ' /BaseFont /'.$longname;
9123 $out .= ' /Name /F'.$font['i'];
9125 $out .= ' /Encoding /'.$enc;
9127 $out .= ' /DescendantFonts ['.($this->n +
1).' 0 R]';
9129 $out .= "\n".'endobj';
9131 $oid = $this->_newobj();
9132 $out = '<</Type /Font';
9133 $out .= ' /Subtype /CIDFontType0';
9134 $out .= ' /BaseFont /'.$name;
9135 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9136 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9137 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9138 $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
9139 $out .= ' /FontDescriptor '.($this->n +
1).' 0 R';
9140 $out .= ' /DW '.$font['dw'];
9141 $out .= "\n".TCPDF_FONTS
::_putfontwidths($font, $cidoffset);
9143 $out .= "\n".'endobj';
9146 $s = '<</Type /FontDescriptor /FontName /'.$name;
9147 foreach ($font['desc'] as $k => $v) {
9148 if ($k != 'Style') {
9150 $v = sprintf('%F', $v);
9152 $s .= ' /'.$k.' '.$v.'';
9156 $s .= "\n".'endobj';
9164 protected function _putimages() {
9165 $filter = ($this->compress
) ?
'/Filter /FlateDecode ' : '';
9166 foreach ($this->imagekeys
as $file) {
9167 $info = $this->getImageBuffer($file);
9168 // set object for alternate images array
9169 if ((!$this->pdfa_mode
) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
9170 $altoid = $this->_newobj();
9172 foreach ($info['altimgs'] as $altimage) {
9173 if (isset($this->xobjects
['I'.$altimage[0]]['n'])) {
9174 $out .= ' << /Image '.$this->xobjects
['I'.$altimage[0]]['n'].' 0 R';
9175 $out .= ' /DefaultForPrinting';
9176 if ($altimage[1] === true) {
9185 $out .= "\n".'endobj';
9189 $oid = $this->_newobj();
9190 $this->xobjects
['I'.$info['i']] = array('n' => $oid);
9191 $this->setImageSubBuffer($file, 'n', $this->n
);
9192 $out = '<</Type /XObject';
9193 $out .= ' /Subtype /Image';
9194 $out .= ' /Width '.$info['w'];
9195 $out .= ' /Height '.$info['h'];
9196 if (array_key_exists('masked', $info)) {
9197 $out .= ' /SMask '.($this->n
- 1).' 0 R';
9201 if (isset($info['icc']) AND ($info['icc'] !== false)) {
9204 $out .= ' /ColorSpace [/ICCBased '.($this->n +
1).' 0 R]';
9205 } elseif ($info['cs'] == 'Indexed') {
9206 // Indexed Colour Space
9207 $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n +
1).' 0 R]';
9209 // Device Colour Space
9210 $out .= ' /ColorSpace /'.$info['cs'];
9212 if ($info['cs'] == 'DeviceCMYK') {
9213 $out .= ' /Decode [1 0 1 0 1 0 1 0]';
9215 $out .= ' /BitsPerComponent '.$info['bpc'];
9216 if (isset($altoid) AND ($altoid > 0)) {
9217 // reference to alternate images dictionary
9218 $out .= ' /Alternates '.$altoid.' 0 R';
9220 if (isset($info['exurl']) AND !empty($info['exurl'])) {
9222 $out .= ' /Length 0';
9223 $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
9224 if (isset($info['f'])) {
9225 $out .= ' /FFilter /'.$info['f'];
9228 $out .= ' stream'."\n".'endstream';
9230 if (isset($info['f'])) {
9231 $out .= ' /Filter /'.$info['f'];
9233 if (isset($info['parms'])) {
9234 $out .= ' '.$info['parms'];
9236 if (isset($info['trns']) AND is_array($info['trns'])) {
9238 $count_info = count($info['trns']);
9239 if ($info['cs'] == 'Indexed') {
9240 $maxval =(pow(2, $info['bpc']) - 1);
9241 for ($i = 0; $i < $count_info; ++
$i) {
9242 if (($info['trns'][$i] != 0) AND ($info['trns'][$i] != $maxval)) {
9243 // this is not a binary type mask @TODO: create a SMask
9246 } elseif (empty($trns) AND ($info['trns'][$i] == 0)) {
9247 // store the first fully transparent value
9248 $trns .= $i.' '.$i.' ';
9253 for ($i = 0; $i < $count_info; ++
$i) {
9254 if ($info['trns'][$i] == 0) {
9255 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
9259 // Colour Key Masking
9260 if (!empty($trns)) {
9261 $out .= ' /Mask ['.$trns.']';
9264 $stream = $this->_getrawstream($info['data']);
9265 $out .= ' /Length '.strlen($stream).' >>';
9266 $out .= ' stream'."\n".$stream."\n".'endstream';
9268 $out .= "\n".'endobj';
9271 // ICC colour profile
9273 $icc = ($this->compress
) ?
gzcompress($info['icc']) : $info['icc'];
9274 $icc = $this->_getrawstream($icc);
9275 $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9276 } elseif ($info['cs'] == 'Indexed') {
9279 $pal = ($this->compress
) ?
gzcompress($info['pal']) : $info['pal'];
9280 $pal = $this->_getrawstream($pal);
9281 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
9287 * Output Form XObjects Templates.
9288 * @author Nicola Asuni
9289 * @since 5.8.017 (2010-08-24)
9291 * @see startTemplate(), endTemplate(), printTemplate()
9293 protected function _putxobjects() {
9294 foreach ($this->xobjects
as $key => $data) {
9295 if (isset($data['outdata'])) {
9296 $stream = str_replace($this->epsmarker
, '', trim($data['outdata']));
9297 $out = $this->_getobj($data['n'])."\n";
9299 $out .= ' /Type /XObject';
9300 $out .= ' /Subtype /Form';
9301 $out .= ' /FormType 1';
9302 if ($this->compress
) {
9303 $stream = gzcompress($stream);
9304 $out .= ' /Filter /FlateDecode';
9306 $out .= sprintf(' /BBox [%F %F %F %F]', ($data['x'] * $this->k
), (-$data['y'] * $this->k
), (($data['w'] +
$data['x']) * $this->k
), (($data['h'] - $data['y']) * $this->k
));
9307 $out .= ' /Matrix [1 0 0 1 0 0]';
9308 $out .= ' /Resources <<';
9309 $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9310 if (!$this->pdfa_mode
) {
9312 if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
9313 $out .= ' /ExtGState <<';
9314 foreach ($data['extgstates'] as $k => $extgstate) {
9315 if (isset($this->extgstates
[$k]['name'])) {
9316 $out .= ' /'.$this->extgstates
[$k]['name'];
9320 $out .= ' '.$this->extgstates
[$k]['n'].' 0 R';
9324 if (isset($data['gradients']) AND !empty($data['gradients'])) {
9327 foreach ($data['gradients'] as $id => $grad) {
9328 // gradient patterns
9329 $gp .= ' /p'.$id.' '.$this->gradients
[$id]['pattern'].' 0 R';
9330 // gradient shadings
9331 $gs .= ' /Sh'.$id.' '.$this->gradients
[$id]['id'].' 0 R';
9333 $out .= ' /Pattern <<'.$gp.' >>';
9334 $out .= ' /Shading <<'.$gs.' >>';
9338 if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
9339 $out .= ' /ColorSpace <<';
9340 foreach ($data['spot_colors'] as $name => $color) {
9341 $out .= ' /CS'.$color['i'].' '.$this->spot_colors
[$name]['n'].' 0 R';
9346 if (!empty($data['fonts'])) {
9347 $out .= ' /Font <<';
9348 foreach ($data['fonts'] as $fontkey => $fontid) {
9349 $out .= ' /F'.$fontid.' '.$this->font_obj_ids
[$fontkey].' 0 R';
9353 // images or nested xobjects
9354 if (!empty($data['images']) OR !empty($data['xobjects'])) {
9355 $out .= ' /XObject <<';
9356 foreach ($data['images'] as $imgid) {
9357 $out .= ' /I'.$imgid.' '.$this->xobjects
['I'.$imgid]['n'].' 0 R';
9359 foreach ($data['xobjects'] as $sub_id => $sub_objid) {
9360 $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
9364 $out .= ' >>'; //end resources
9365 if (isset($data['group']) AND ($data['group'] !== false)) {
9366 // set transparency group
9367 $out .= ' /Group << /Type /Group /S /Transparency';
9368 if (is_array($data['group'])) {
9369 if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
9370 $out .= ' /CS /'.$data['group']['CS'];
9372 if (isset($data['group']['I'])) {
9373 $out .= ' /I /'.($data['group']['I']===true?
'true':'false');
9375 if (isset($data['group']['K'])) {
9376 $out .= ' /K /'.($data['group']['K']===true?
'true':'false');
9381 $stream = $this->_getrawstream($stream, $data['n']);
9382 $out .= ' /Length '.strlen($stream);
9384 $out .= ' stream'."\n".$stream."\n".'endstream';
9385 $out .= "\n".'endobj';
9392 * Output Spot Colors Resources.
9394 * @since 4.0.024 (2008-09-12)
9396 protected function _putspotcolors() {
9397 foreach ($this->spot_colors
as $name => $color) {
9399 $this->spot_colors
[$name]['n'] = $this->n
;
9400 $out = '[/Separation /'.str_replace(' ', '#20', $name);
9401 $out .= ' /DeviceCMYK <<';
9402 $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
9403 $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
9404 $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
9405 $out .= "\n".'endobj';
9411 * Return XObjects Dictionary.
9412 * @return string XObjects dictionary
9414 * @since 5.8.014 (2010-08-23)
9416 protected function _getxobjectdict() {
9418 foreach ($this->xobjects
as $id => $objid) {
9419 $out .= ' /'.$id.' '.$objid['n'].' 0 R';
9425 * Output Resources Dictionary.
9428 protected function _putresourcedict() {
9429 $out = $this->_getobj(2)."\n";
9430 $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9431 $out .= ' /Font <<';
9432 foreach ($this->fontkeys
as $fontkey) {
9433 $font = $this->getFontBuffer($fontkey);
9434 $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
9437 $out .= ' /XObject <<';
9438 $out .= $this->_getxobjectdict();
9441 if (!empty($this->pdflayers
)) {
9442 $out .= ' /Properties <<';
9443 foreach ($this->pdflayers
as $layer) {
9444 $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
9448 if (!$this->pdfa_mode
) {
9450 if (isset($this->extgstates
) AND !empty($this->extgstates
)) {
9451 $out .= ' /ExtGState <<';
9452 foreach ($this->extgstates
as $k => $extgstate) {
9453 if (isset($extgstate['name'])) {
9454 $out .= ' /'.$extgstate['name'];
9458 $out .= ' '.$extgstate['n'].' 0 R';
9462 if (isset($this->gradients
) AND !empty($this->gradients
)) {
9465 foreach ($this->gradients
as $id => $grad) {
9466 // gradient patterns
9467 $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
9468 // gradient shadings
9469 $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
9471 $out .= ' /Pattern <<'.$gp.' >>';
9472 $out .= ' /Shading <<'.$gs.' >>';
9476 if (isset($this->spot_colors
) AND !empty($this->spot_colors
)) {
9477 $out .= ' /ColorSpace <<';
9478 foreach ($this->spot_colors
as $color) {
9479 $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
9484 $out .= "\n".'endobj';
9492 protected function _putresources() {
9493 $this->_putextgstates();
9496 $this->_putimages();
9497 $this->_putspotcolors();
9498 $this->_putshaders();
9499 $this->_putxobjects();
9500 $this->_putresourcedict();
9502 $this->_putEmbeddedFiles();
9503 $this->_putannotsobjs();
9504 $this->_putjavascript();
9505 $this->_putbookmarks();
9506 $this->_putencryption();
9510 * Adds some Metadata information (Document Information Dictionary)
9511 * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference)
9512 * @return int object id
9515 protected function _putinfo() {
9516 $oid = $this->_newobj();
9518 // store current isunicode value
9519 $prev_isunicode = $this->isunicode
;
9520 if ($this->docinfounicode
) {
9521 $this->isunicode
= true;
9523 if (!TCPDF_STATIC
::empty_string($this->title
)) {
9524 // The document's title.
9525 $out .= ' /Title '.$this->_textstring($this->title
, $oid);
9527 if (!TCPDF_STATIC
::empty_string($this->author
)) {
9528 // The name of the person who created the document.
9529 $out .= ' /Author '.$this->_textstring($this->author
, $oid);
9531 if (!TCPDF_STATIC
::empty_string($this->subject
)) {
9532 // The subject of the document.
9533 $out .= ' /Subject '.$this->_textstring($this->subject
, $oid);
9535 if (!TCPDF_STATIC
::empty_string($this->keywords
)) {
9536 // Keywords associated with the document.
9537 $out .= ' /Keywords '.$this->_textstring($this->keywords
, $oid);
9539 if (!TCPDF_STATIC
::empty_string($this->creator
)) {
9540 // If the document was converted to PDF from another format, the name of the conforming product that created the original document from which it was converted.
9541 $out .= ' /Creator '.$this->_textstring($this->creator
, $oid);
9543 // restore previous isunicode value
9544 $this->isunicode
= $prev_isunicode;
9546 $out .= ' /Producer '.$this->_textstring(TCPDF_STATIC
::getTCPDFProducer(), $oid);
9547 // The date and time the document was created, in human-readable form
9548 $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp
);
9549 // The date and time the document was most recently modified, in human-readable form
9550 $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp
);
9551 // A name object indicating whether the document has been modified to include trapping information
9552 $out .= ' /Trapped /False';
9554 $out .= "\n".'endobj';
9560 * Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag.
9561 * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method!
9562 * @param $xmp (string) Custom XMP data.
9563 * @since 5.9.128 (2011-10-06)
9566 public function setExtraXMP($xmp) {
9567 $this->custom_xmp
= $xmp;
9571 * Put XMP data object and return ID.
9572 * @return (int) The object ID.
9573 * @since 5.9.121 (2011-09-28)
9576 protected function _putXMP() {
9577 $oid = $this->_newobj();
9578 // store current isunicode value
9579 $prev_isunicode = $this->isunicode
;
9580 $this->isunicode
= true;
9581 $prev_encrypted = $this->encrypted
;
9582 $this->encrypted
= false;
9584 $xmp = '<?xpacket begin="'.TCPDF_FONTS
::unichr(0xfeff, $this->isunicode
).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
9585 $xmp .= '<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.1-c043 52.372728, 2009/01/18-15:08:04">'."\n";
9586 $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
9587 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
9588 $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
9589 $xmp .= "\t\t\t".'<dc:title>'."\n";
9590 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9591 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC
::_escapeXML($this->title
).'</rdf:li>'."\n";
9592 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9593 $xmp .= "\t\t\t".'</dc:title>'."\n";
9594 $xmp .= "\t\t\t".'<dc:creator>'."\n";
9595 $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
9596 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC
::_escapeXML($this->author
).'</rdf:li>'."\n";
9597 $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
9598 $xmp .= "\t\t\t".'</dc:creator>'."\n";
9599 $xmp .= "\t\t\t".'<dc:description>'."\n";
9600 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9601 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC
::_escapeXML($this->subject
).'</rdf:li>'."\n";
9602 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9603 $xmp .= "\t\t\t".'</dc:description>'."\n";
9604 $xmp .= "\t\t\t".'<dc:subject>'."\n";
9605 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9606 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC
::_escapeXML($this->keywords
).'</rdf:li>'."\n";
9607 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9608 $xmp .= "\t\t\t".'</dc:subject>'."\n";
9609 $xmp .= "\t\t".'</rdf:Description>'."\n";
9610 // convert doc creation date format
9611 $dcdate = TCPDF_STATIC
::getFormattedDate($this->doc_creation_timestamp
);
9612 $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2);
9613 $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2);
9614 $doccreationdate .= '+'.substr($dcdate, 15, 2).':'.substr($dcdate, 18, 2);
9615 $doccreationdate = TCPDF_STATIC
::_escapeXML($doccreationdate);
9616 // convert doc modification date format
9617 $dmdate = TCPDF_STATIC
::getFormattedDate($this->doc_modification_timestamp
);
9618 $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2);
9619 $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2);
9620 $docmoddate .= '+'.substr($dmdate, 15, 2).':'.substr($dmdate, 18, 2);
9621 $docmoddate = TCPDF_STATIC
::_escapeXML($docmoddate);
9622 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
9623 $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n";
9624 $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator
.'</xmp:CreatorTool>'."\n";
9625 $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n";
9626 $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n";
9627 $xmp .= "\t\t".'</rdf:Description>'."\n";
9628 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
9629 $xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC
::_escapeXML($this->keywords
).'</pdf:Keywords>'."\n";
9630 $xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC
::_escapeXML(TCPDF_STATIC
::getTCPDFProducer()).'</pdf:Producer>'."\n";
9631 $xmp .= "\t\t".'</rdf:Description>'."\n";
9632 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
9633 $uuid = 'uuid:'.substr($this->file_id
, 0, 8).'-'.substr($this->file_id
, 8, 4).'-'.substr($this->file_id
, 12, 4).'-'.substr($this->file_id
, 16, 4).'-'.substr($this->file_id
, 20, 12);
9634 $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
9635 $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
9636 $xmp .= "\t\t".'</rdf:Description>'."\n";
9637 if ($this->pdfa_mode
) {
9638 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
9639 $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n";
9640 $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
9641 $xmp .= "\t\t".'</rdf:Description>'."\n";
9643 // XMP extension schemas
9644 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/" xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#" xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">'."\n";
9645 $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
9646 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9647 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9648 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
9649 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
9650 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
9651 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9652 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9653 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
9654 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
9655 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
9656 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9657 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9658 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9659 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9660 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
9661 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9662 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9663 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9664 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9665 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9666 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9667 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9668 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
9669 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
9670 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
9671 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9672 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9673 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9674 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9675 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
9676 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
9677 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
9678 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9679 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9680 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9681 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
9682 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
9683 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9684 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9685 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9686 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9687 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
9688 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
9689 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9690 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9691 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9692 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9693 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9694 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9695 $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
9696 $xmp .= "\t\t".'</rdf:Description>'."\n";
9697 $xmp .= "\t".'</rdf:RDF>'."\n";
9698 $xmp .= $this->custom_xmp
;
9699 $xmp .= '</x:xmpmeta>'."\n";
9700 $xmp .= '<?xpacket end="w"?>';
9701 $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
9702 // restore previous isunicode value
9703 $this->isunicode
= $prev_isunicode;
9704 $this->encrypted
= $prev_encrypted;
9711 * @return int object id
9714 protected function _putcatalog() {
9716 $xmpobj = $this->_putXMP();
9717 // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile
9718 if ($this->pdfa_mode
OR $this->force_srgb
) {
9719 $iccobj = $this->_newobj();
9720 $icc = file_get_contents(dirname(__FILE__
).'/include/sRGB.icc');
9722 if ($this->compress
) {
9723 $filter = ' /Filter /FlateDecode';
9724 $icc = gzcompress($icc);
9726 $icc = $this->_getrawstream($icc);
9727 $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9730 $oid = $this->_newobj();
9731 $out = '<< /Type /Catalog';
9732 $out .= ' /Version /'.$this->PDFVersion
;
9733 //$out .= ' /Extensions <<>>';
9734 $out .= ' /Pages 1 0 R';
9735 //$out .= ' /PageLabels ' //...;
9736 $out .= ' /Names <<';
9737 if ((!$this->pdfa_mode
) AND !empty($this->n_js
)) {
9738 $out .= ' /JavaScript '.$this->n_js
;
9740 if (!empty($this->efnames
)) {
9741 $out .= ' /EmbeddedFiles <</Names [';
9742 foreach ($this->efnames
AS $fn => $fref) {
9743 $out .= ' '.$this->_datastring($fn).' '.$fref;
9748 if (!empty($this->dests
)) {
9749 $out .= ' /Dests '.($this->n_dests
).' 0 R';
9751 $out .= $this->_putviewerpreferences();
9752 if (isset($this->LayoutMode
) AND (!TCPDF_STATIC
::empty_string($this->LayoutMode
))) {
9753 $out .= ' /PageLayout /'.$this->LayoutMode
;
9755 if (isset($this->PageMode
) AND (!TCPDF_STATIC
::empty_string($this->PageMode
))) {
9756 $out .= ' /PageMode /'.$this->PageMode
;
9758 if (count($this->outlines
) > 0) {
9759 $out .= ' /Outlines '.$this->OutlineRoot
.' 0 R';
9760 $out .= ' /PageMode /UseOutlines';
9762 //$out .= ' /Threads []';
9763 if ($this->ZoomMode
== 'fullpage') {
9764 $out .= ' /OpenAction ['.$this->page_obj_id
[1].' 0 R /Fit]';
9765 } elseif ($this->ZoomMode
== 'fullwidth') {
9766 $out .= ' /OpenAction ['.$this->page_obj_id
[1].' 0 R /FitH null]';
9767 } elseif ($this->ZoomMode
== 'real') {
9768 $out .= ' /OpenAction ['.$this->page_obj_id
[1].' 0 R /XYZ null null 1]';
9769 } elseif (!is_string($this->ZoomMode
)) {
9770 $out .= sprintf(' /OpenAction ['.$this->page_obj_id
[1].' 0 R /XYZ null null %F]', ($this->ZoomMode
/ 100));
9772 //$out .= ' /AA <<>>';
9773 //$out .= ' /URI <<>>';
9774 $out .= ' /Metadata '.$xmpobj.' 0 R';
9775 //$out .= ' /StructTreeRoot <<>>';
9776 //$out .= ' /MarkInfo <<>>';
9777 if (isset($this->l
['a_meta_language'])) {
9778 $out .= ' /Lang '.$this->_textstring($this->l
['a_meta_language'], $oid);
9780 //$out .= ' /SpiderInfo <<>>';
9781 // set OutputIntent to sRGB IEC61966-2.1 if required
9782 if ($this->pdfa_mode
OR $this->force_srgb
) {
9783 $out .= ' /OutputIntents [<<';
9784 $out .= ' /Type /OutputIntent';
9785 $out .= ' /S /GTS_PDFA1';
9786 $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9787 $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9788 $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
9789 $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9790 $out .= ' /DestOutputProfile '.$iccobj.' 0 R';
9793 //$out .= ' /PieceInfo <<>>';
9794 if (!empty($this->pdflayers
)) {
9798 foreach ($this->pdflayers
as $layer) {
9799 $layer_obj_ref = ' '.$layer['objid'].' 0 R';
9800 $lyrobjs .= $layer_obj_ref;
9801 if ($layer['view'] === false) {
9802 $lyrobjs_off .= $layer_obj_ref;
9804 if ($layer['lock']) {
9805 $lyrobjs_lock .= $layer_obj_ref;
9808 $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
9810 $out .= ' /Name '.$this->_textstring('Layers', $oid);
9811 $out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
9812 $out .= ' /BaseState /ON';
9813 $out .= ' /OFF ['.$lyrobjs_off.']';
9814 $out .= ' /Locked ['.$lyrobjs_lock.']';
9815 $out .= ' /Intent /View';
9817 $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
9818 $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
9820 $out .= ' /Order ['.$lyrobjs.']';
9821 $out .= ' /ListMode /AllPages';
9822 //$out .= ' /RBGroups ['..']';
9823 //$out .= ' /Locked ['..']';
9828 if (!empty($this->form_obj_id
)
9829 OR ($this->sign
AND isset($this->signature_data
['cert_type']))
9830 OR !empty($this->empty_signature_appearance
)) {
9831 $out .= ' /AcroForm <<';
9833 if ($this->sign
AND isset($this->signature_data
['cert_type'])) {
9834 // set reference for signature object
9835 $objrefs .= $this->sig_obj_id
.' 0 R';
9837 if (!empty($this->empty_signature_appearance
)) {
9838 foreach ($this->empty_signature_appearance
as $esa) {
9839 // set reference for empty signature objects
9840 $objrefs .= ' '.$esa['objid'].' 0 R';
9843 if (!empty($this->form_obj_id
)) {
9844 foreach($this->form_obj_id
as $objid) {
9845 $objrefs .= ' '.$objid.' 0 R';
9848 $out .= ' /Fields ['.$objrefs.']';
9849 // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
9850 if (empty($this->signature_data
['approval']) OR ($this->signature_data
['approval'] != 'A')) {
9851 $out .= ' /NeedAppearances false';
9853 if ($this->sign
AND isset($this->signature_data
['cert_type'])) {
9854 if ($this->signature_data
['cert_type'] > 0) {
9855 $out .= ' /SigFlags 3';
9857 $out .= ' /SigFlags 1';
9861 if (isset($this->annotation_fonts
) AND !empty($this->annotation_fonts
)) {
9863 $out .= ' /Font <<';
9864 foreach ($this->annotation_fonts
as $fontkey => $fontid) {
9865 $out .= ' /F'.$fontid.' '.$this->font_obj_ids
[$fontkey].' 0 R';
9869 $font = $this->getFontBuffer('helvetica');
9870 $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
9871 $out .= ' /Q '.(($this->rtl
)?
'2':'0');
9875 if ($this->sign
AND isset($this->signature_data
['cert_type'])
9876 AND (empty($this->signature_data
['approval']) OR ($this->signature_data
['approval'] != 'A'))) {
9877 if ($this->signature_data
['cert_type'] > 0) {
9878 $out .= ' /Perms << /DocMDP '.($this->sig_obj_id +
1).' 0 R >>';
9880 $out .= ' /Perms << /UR3 '.($this->sig_obj_id +
1).' 0 R >>';
9884 //$out .= ' /Legal <<>>';
9885 //$out .= ' /Requirements []';
9886 //$out .= ' /Collection <<>>';
9887 //$out .= ' /NeedsRendering true';
9889 $out .= "\n".'endobj';
9895 * Output viewer preferences.
9896 * @return string for viewer preferences
9897 * @author Nicola asuni
9898 * @since 3.1.000 (2008-06-09)
9901 protected function _putviewerpreferences() {
9902 $vp = $this->viewer_preferences
;
9903 $out = ' /ViewerPreferences <<';
9905 $out .= ' /Direction /R2L';
9907 $out .= ' /Direction /L2R';
9909 if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) {
9910 $out .= ' /HideToolbar true';
9912 if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) {
9913 $out .= ' /HideMenubar true';
9915 if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) {
9916 $out .= ' /HideWindowUI true';
9918 if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) {
9919 $out .= ' /FitWindow true';
9921 if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) {
9922 $out .= ' /CenterWindow true';
9924 if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) {
9925 $out .= ' /DisplayDocTitle true';
9927 if (isset($vp['NonFullScreenPageMode'])) {
9928 $out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode'];
9930 if (isset($vp['ViewArea'])) {
9931 $out .= ' /ViewArea /'.$vp['ViewArea'];
9933 if (isset($vp['ViewClip'])) {
9934 $out .= ' /ViewClip /'.$vp['ViewClip'];
9936 if (isset($vp['PrintArea'])) {
9937 $out .= ' /PrintArea /'.$vp['PrintArea'];
9939 if (isset($vp['PrintClip'])) {
9940 $out .= ' /PrintClip /'.$vp['PrintClip'];
9942 if (isset($vp['PrintScaling'])) {
9943 $out .= ' /PrintScaling /'.$vp['PrintScaling'];
9945 if (isset($vp['Duplex']) AND (!TCPDF_STATIC
::empty_string($vp['Duplex']))) {
9946 $out .= ' /Duplex /'.$vp['Duplex'];
9948 if (isset($vp['PickTrayByPDFSize'])) {
9949 if ($vp['PickTrayByPDFSize']) {
9950 $out .= ' /PickTrayByPDFSize true';
9952 $out .= ' /PickTrayByPDFSize false';
9955 if (isset($vp['PrintPageRange'])) {
9956 $PrintPageRangeNum = '';
9957 foreach ($vp['PrintPageRange'] as $k => $v) {
9958 $PrintPageRangeNum .= ' '.($v - 1).'';
9960 $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
9962 if (isset($vp['NumCopies'])) {
9963 $out .= ' /NumCopies '.intval($vp['NumCopies']);
9970 * Output PDF File Header (7.5.2).
9973 protected function _putheader() {
9974 $this->_out('%PDF-'.$this->PDFVersion
);
9975 $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
9979 * Output end of document (EOF).
9982 protected function _enddoc() {
9983 if (isset($this->CurrentFont
['fontkey']) AND isset($this->CurrentFont
['subsetchars'])) {
9984 // save subset chars of the previous font
9985 $this->setFontSubBuffer($this->CurrentFont
['fontkey'], 'subsetchars', $this->CurrentFont
['subsetchars']);
9988 $this->_putheader();
9990 $this->_putresources();
9991 // empty signature fields
9992 if (!empty($this->empty_signature_appearance
)) {
9993 foreach ($this->empty_signature_appearance
as $key => $esa) {
9994 // widget annotation for empty signature
9995 $out = $this->_getobj($esa['objid'])."\n";
9996 $out .= '<< /Type /Annot';
9997 $out .= ' /Subtype /Widget';
9998 $out .= ' /Rect ['.$esa['rect'].']';
9999 $out .= ' /P '.$this->page_obj_id
[($esa['page'])].' 0 R'; // link to signature appearance page
10001 $out .= ' /FT /Sig';
10002 $signame = $esa['name'].sprintf(' [%03d]', ($key +
1));
10003 $out .= ' /T '.$this->_textstring($signame, $esa['objid']);
10006 $out .= "\n".'endobj';
10011 if ($this->sign
AND isset($this->signature_data
['cert_type'])) {
10012 // widget annotation for signature
10013 $out = $this->_getobj($this->sig_obj_id
)."\n";
10014 $out .= '<< /Type /Annot';
10015 $out .= ' /Subtype /Widget';
10016 $out .= ' /Rect ['.$this->signature_appearance
['rect'].']';
10017 $out .= ' /P '.$this->page_obj_id
[($this->signature_appearance
['page'])].' 0 R'; // link to signature appearance page
10019 $out .= ' /FT /Sig';
10020 $out .= ' /T '.$this->_textstring($this->signature_appearance
['name'], $this->sig_obj_id
);
10022 $out .= ' /V '.($this->sig_obj_id +
1).' 0 R';
10024 $out .= "\n".'endobj';
10027 $this->_putsignature();
10030 $objid_info = $this->_putinfo();
10032 $objid_catalog = $this->_putcatalog();
10034 $o = $this->bufferlen
;
10036 $this->_out('xref');
10037 $this->_out('0 '.($this->n +
1));
10038 $this->_out('0000000000 65535 f ');
10039 $freegen = ($this->n +
2);
10040 for ($i=1; $i <= $this->n
; ++
$i) {
10041 if (!isset($this->offsets
[$i]) AND ($i > 1)) {
10042 $this->_out(sprintf('0000000000 %05d f ', $freegen));
10045 $this->_out(sprintf('%010d 00000 n ', $this->offsets
[$i]));
10049 $out = 'trailer'."\n";
10051 $out .= ' /Size '.($this->n +
1);
10052 $out .= ' /Root '.$objid_catalog.' 0 R';
10053 $out .= ' /Info '.$objid_info.' 0 R';
10054 if ($this->encrypted
) {
10055 $out .= ' /Encrypt '.$this->encryptdata
['objid'].' 0 R';
10057 $out .= ' /ID [ <'.$this->file_id
.'> <'.$this->file_id
.'> ]';
10060 $this->_out('startxref');
10062 $this->_out('%%EOF');
10063 $this->state
= 3; // end-of-doc
10064 if ($this->diskcache
) {
10065 // remove temporary files used for images
10066 foreach ($this->imagekeys
as $key) {
10067 // remove temporary files
10068 unlink($this->images
[$key]);
10070 foreach ($this->fontkeys
as $key) {
10071 // remove temporary files
10072 unlink($this->fonts
[$key]);
10078 * Initialize a new page.
10079 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
10080 * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
10082 * @see getPageSizeFromFormat(), setPageFormat()
10084 protected function _beginpage($orientation='', $format='') {
10086 $this->pageobjects
[$this->page
] = array();
10087 $this->setPageBuffer($this->page
, '');
10088 // initialize array for graphics tranformation positions inside a page buffer
10089 $this->transfmrk
[$this->page
] = array();
10091 if (TCPDF_STATIC
::empty_string($orientation)) {
10092 if (isset($this->CurOrientation
)) {
10093 $orientation = $this->CurOrientation
;
10094 } elseif ($this->fwPt
> $this->fhPt
) {
10096 $orientation = 'L';
10099 $orientation = 'P';
10102 if (TCPDF_STATIC
::empty_string($format)) {
10103 $this->pagedim
[$this->page
] = $this->pagedim
[($this->page
- 1)];
10104 $this->setPageOrientation($orientation);
10106 $this->setPageFormat($format, $orientation);
10109 $this->x
= $this->w
- $this->rMargin
;
10111 $this->x
= $this->lMargin
;
10113 $this->y
= $this->tMargin
;
10114 if (isset($this->newpagegroup
[$this->page
])) {
10115 // start a new group
10116 $this->currpagegroup
= $this->newpagegroup
[$this->page
];
10117 $this->pagegroups
[$this->currpagegroup
] = 1;
10118 } elseif (isset($this->currpagegroup
) AND ($this->currpagegroup
> 0)) {
10119 ++
$this->pagegroups
[$this->currpagegroup
];
10124 * Mark end of page.
10127 protected function _endpage() {
10128 $this->setVisibility('all');
10133 * Begin a new object and return the object number.
10134 * @return int object number
10137 protected function _newobj() {
10138 $this->_out($this->_getobj());
10143 * Return the starting object string for the selected object ID.
10144 * @param $objid (int) Object ID (leave empty to get a new ID).
10145 * @return string the starting object string
10147 * @since 5.8.009 (2010-08-20)
10149 protected function _getobj($objid='') {
10150 if ($objid === '') {
10154 $this->offsets
[$objid] = $this->bufferlen
;
10155 $this->pageobjects
[$this->page
][] = $objid;
10156 return $objid.' 0 obj';
10161 * @param $x (int) X coordinate
10162 * @param $y (int) Y coordinate
10163 * @param $txt (string) text to underline
10166 protected function _dounderline($x, $y, $txt) {
10167 $w = $this->GetStringWidth($txt);
10168 return $this->_dounderlinew($x, $y, $w);
10172 * Underline for rectangular text area.
10173 * @param $x (int) X coordinate
10174 * @param $y (int) Y coordinate
10175 * @param $w (int) width to underline
10177 * @since 4.8.008 (2009-09-29)
10179 protected function _dounderlinew($x, $y, $w) {
10180 $linew = - $this->CurrentFont
['ut'] / 1000 * $this->FontSizePt
;
10181 return sprintf('%F %F %F %F re f', $x * $this->k
, ((($this->h
- $y) * $this->k
) +
$linew), $w * $this->k
, $linew);
10185 * Line through text.
10186 * @param $x (int) X coordinate
10187 * @param $y (int) Y coordinate
10188 * @param $txt (string) text to linethrough
10191 protected function _dolinethrough($x, $y, $txt) {
10192 $w = $this->GetStringWidth($txt);
10193 return $this->_dolinethroughw($x, $y, $w);
10197 * Line through for rectangular text area.
10198 * @param $x (int) X coordinate
10199 * @param $y (int) Y coordinate
10200 * @param $w (int) line length (width)
10202 * @since 4.9.008 (2009-09-29)
10204 protected function _dolinethroughw($x, $y, $w) {
10205 $linew = - $this->CurrentFont
['ut'] / 1000 * $this->FontSizePt
;
10206 return sprintf('%F %F %F %F re f', $x * $this->k
, ((($this->h
- $y) * $this->k
) +
$linew +
($this->FontSizePt
/ 3)), $w * $this->k
, $linew);
10211 * @param $x (int) X coordinate
10212 * @param $y (int) Y coordinate
10213 * @param $txt (string) text to overline
10215 * @since 4.9.015 (2010-04-19)
10217 protected function _dooverline($x, $y, $txt) {
10218 $w = $this->GetStringWidth($txt);
10219 return $this->_dooverlinew($x, $y, $w);
10223 * Overline for rectangular text area.
10224 * @param $x (int) X coordinate
10225 * @param $y (int) Y coordinate
10226 * @param $w (int) width to overline
10228 * @since 4.9.015 (2010-04-19)
10230 protected function _dooverlinew($x, $y, $w) {
10231 $linew = - $this->CurrentFont
['ut'] / 1000 * $this->FontSizePt
;
10232 return sprintf('%F %F %F %F re f', $x * $this->k
, (($this->h
- $y +
$this->FontAscent
) * $this->k
) - $linew, $w * $this->k
, $linew);
10237 * Format a data string for meta information
10238 * @param $s (string) data string to escape.
10239 * @param $n (int) object ID
10240 * @return string escaped string.
10243 protected function _datastring($s, $n=0) {
10247 $s = $this->_encrypt_data($n, $s);
10248 return '('. TCPDF_STATIC
::_escape($s).')';
10252 * Set the document creation timestamp
10253 * @param $time (mixed) Document creation timestamp in seconds or date-time string.
10255 * @since 5.9.152 (2012-03-23)
10257 public function setDocCreationTimestamp($time) {
10258 if (is_string($time)) {
10259 $time = TCPDF_STATIC
::getTimestamp($time);
10261 $this->doc_creation_timestamp
= intval($time);
10265 * Set the document modification timestamp
10266 * @param $time (mixed) Document modification timestamp in seconds or date-time string.
10268 * @since 5.9.152 (2012-03-23)
10270 public function setDocModificationTimestamp($time) {
10271 if (is_string($time)) {
10272 $time = TCPDF_STATIC
::getTimestamp($time);
10274 $this->doc_modification_timestamp
= intval($time);
10278 * Returns document creation timestamp in seconds.
10279 * @return (int) Creation timestamp in seconds.
10281 * @since 5.9.152 (2012-03-23)
10283 public function getDocCreationTimestamp() {
10284 return $this->doc_creation_timestamp
;
10288 * Returns document modification timestamp in seconds.
10289 * @return (int) Modfication timestamp in seconds.
10291 * @since 5.9.152 (2012-03-23)
10293 public function getDocModificationTimestamp() {
10294 return $this->doc_modification_timestamp
;
10298 * Returns a formatted date for meta information
10299 * @param $n (int) Object ID.
10300 * @param $timestamp (int) Timestamp to convert.
10301 * @return string escaped date string.
10303 * @since 4.6.028 (2009-08-25)
10305 protected function _datestring($n=0, $timestamp=0) {
10306 if ((empty($timestamp)) OR ($timestamp < 0)) {
10307 $timestamp = $this->doc_creation_timestamp
;
10309 return $this->_datastring('D:'.TCPDF_STATIC
::getFormattedDate($timestamp), $n);
10313 * Format a text string for meta information
10314 * @param $s (string) string to escape.
10315 * @param $n (int) object ID
10316 * @return string escaped string.
10319 protected function _textstring($s, $n=0) {
10320 if ($this->isunicode
) {
10321 //Convert string to UTF-16BE
10322 $s = TCPDF_FONTS
::UTF8ToUTF16BE($s, true, $this->isunicode
, $this->CurrentFont
);
10324 return $this->_datastring($s, $n);
10328 * THIS METHOD IS DEPRECATED
10329 * Format a text string
10330 * @param $s (string) string to escape.
10331 * @return string escaped string.
10335 protected function _escapetext($s) {
10336 if ($this->isunicode
) {
10337 if (($this->CurrentFont
['type'] == 'core') OR ($this->CurrentFont
['type'] == 'TrueType') OR ($this->CurrentFont
['type'] == 'Type1')) {
10338 $s = TCPDF_FONTS
::UTF8ToLatin1($s, $this->isunicode
, $this->CurrentFont
);
10340 //Convert string to UTF-16BE and reverse RTL language
10341 $s = TCPDF_FONTS
::utf8StrRev($s, false, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
10344 return TCPDF_STATIC
::_escape($s);
10348 * get raw output stream.
10349 * @param $s (string) string to output.
10350 * @param $n (int) object reference for encryption mode
10352 * @author Nicola Asuni
10353 * @since 5.5.000 (2010-06-22)
10355 protected function _getrawstream($s, $n=0) {
10357 // default to current object
10360 return $this->_encrypt_data($n, $s);
10364 * Format output stream (DEPRECATED).
10365 * @param $s (string) string to output.
10366 * @param $n (int) object reference for encryption mode
10370 protected function _getstream($s, $n=0) {
10371 return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream';
10375 * Output a stream (DEPRECATED).
10376 * @param $s (string) string to output.
10377 * @param $n (int) object reference for encryption mode
10381 protected function _putstream($s, $n=0) {
10382 $this->_out($this->_getstream($s, $n));
10386 * Output a string to the document.
10387 * @param $s (string) string to output.
10390 protected function _out($s) {
10391 if ($this->state
== 2) {
10392 if ($this->inxobj
) {
10393 // we are inside an XObject template
10394 $this->xobjects
[$this->xobjid
]['outdata'] .= $s."\n";
10395 } elseif ((!$this->InFooter
) AND isset($this->footerlen
[$this->page
]) AND ($this->footerlen
[$this->page
] > 0)) {
10396 // puts data before page footer
10397 $pagebuff = $this->getPageBuffer($this->page
);
10398 $page = substr($pagebuff, 0, -$this->footerlen
[$this->page
]);
10399 $footer = substr($pagebuff, -$this->footerlen
[$this->page
]);
10400 $this->setPageBuffer($this->page
, $page.$s."\n".$footer);
10401 // update footer position
10402 $this->footerpos
[$this->page
] +
= strlen($s."\n");
10405 $this->setPageBuffer($this->page
, $s."\n", true);
10407 } elseif ($this->state
> 0) {
10408 // set general data
10409 $this->setBuffer($s."\n");
10415 * @param $font (array) Array describing the basic font parameters: (family, style, size).
10419 public function setHeaderFont($font) {
10420 $this->header_font
= $font;
10425 * @return array() Array describing the basic font parameters: (family, style, size).
10427 * @since 4.0.012 (2008-07-24)
10429 public function getHeaderFont() {
10430 return $this->header_font
;
10435 * @param $font (array) Array describing the basic font parameters: (family, style, size).
10439 public function setFooterFont($font) {
10440 $this->footer_font
= $font;
10445 * @return array() Array describing the basic font parameters: (family, style, size).
10447 * @since 4.0.012 (2008-07-24)
10449 public function getFooterFont() {
10450 return $this->footer_font
;
10454 * Set language array.
10455 * @param $language (array)
10459 public function setLanguageArray($language) {
10460 $this->l
= $language;
10461 if (isset($this->l
['a_meta_dir'])) {
10462 $this->rtl
= $this->l
['a_meta_dir']=='rtl' ?
true : false;
10464 $this->rtl
= false;
10469 * Returns the PDF data.
10472 public function getPDFData() {
10473 if ($this->state
< 3) {
10476 return $this->buffer
;
10480 * Output anchor link.
10481 * @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>)
10482 * @param $name (string) link name
10483 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
10484 * @param $firstline (boolean) if true prints only the first line and return the remaining string.
10485 * @param $color (array) array of RGB text color
10486 * @param $style (string) font style (U, D, B, I)
10487 * @param $firstblock (boolean) if true the string is the starting of a line.
10488 * @return the number of cells used or the remaining text if $firstline = true;
10491 public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
10492 if (isset($url[1]) AND ($url[0] == '#')) {
10493 // convert url to internal link
10494 $lnkdata = explode(',', $url);
10495 if (isset($lnkdata[0]) ) {
10496 $page = substr($lnkdata[0], 1);
10497 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
10498 $lnky = floatval($lnkdata[1]);
10502 $url = $this->AddLink();
10503 $this->SetLink($url, $lnky, $page);
10506 // store current settings
10507 $prevcolor = $this->fgcolor
;
10508 $prevstyle = $this->FontStyle
;
10509 if (empty($color)) {
10510 $this->SetTextColorArray($this->htmlLinkColorArray
);
10512 $this->SetTextColorArray($color);
10514 if ($style == -1) {
10515 $this->SetFont('', $this->FontStyle
.$this->htmlLinkFontStyle
);
10517 $this->SetFont('', $this->FontStyle
.$style);
10519 $ret = $this->Write($this->lasth
, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
10520 // restore settings
10521 $this->SetFont('', $prevstyle);
10522 $this->SetTextColorArray($prevcolor);
10527 * Converts pixels to User's Units.
10528 * @param $px (int) pixels
10529 * @return float value in user's unit
10531 * @see setImageScale(), getImageScale()
10533 public function pixelsToUnits($px) {
10534 return ($px / ($this->imgscale
* $this->k
));
10538 * Reverse function for htmlentities.
10539 * Convert entities in UTF-8.
10540 * @param $text_to_convert (string) Text to convert.
10541 * @return string converted text string
10544 public function unhtmlentities($text_to_convert) {
10545 return @html_entity_decode
($text_to_convert, ENT_QUOTES
, $this->encoding
);
10548 // ENCRYPTION METHODS ----------------------------------
10551 * Compute encryption key depending on object number where the encrypted data is stored.
10552 * This is used for all strings and streams without crypt filter specifier.
10553 * @param $n (int) object number
10554 * @return int object key
10556 * @author Nicola Asuni
10557 * @since 2.0.000 (2008-01-02)
10559 protected function _objectkey($n) {
10560 $objkey = $this->encryptdata
['key'].pack('VXxx', $n);
10561 if ($this->encryptdata
['mode'] == 2) { // AES-128
10563 $objkey .= "\x73\x41\x6C\x54"; // sAlT
10565 $objkey = substr(TCPDF_STATIC
::_md5_16($objkey), 0, (($this->encryptdata
['Length'] / 8) +
5));
10566 $objkey = substr($objkey, 0, 16);
10571 * Encrypt the input string.
10572 * @param $n (int) object number
10573 * @param $s (string) data string to encrypt
10574 * @return encrypted string
10576 * @author Nicola Asuni
10577 * @since 5.0.005 (2010-05-11)
10579 protected function _encrypt_data($n, $s) {
10580 if (!$this->encrypted
) {
10583 switch ($this->encryptdata
['mode']) {
10585 case 1: { // RC4-128
10586 $s = TCPDF_STATIC
::_RC4($this->_objectkey($n), $s, $this->last_enc_key
, $this->last_enc_key_c
);
10589 case 2: { // AES-128
10590 $s = TCPDF_STATIC
::_AES($this->_objectkey($n), $s);
10593 case 3: { // AES-256
10594 $s = TCPDF_STATIC
::_AES($this->encryptdata
['key'], $s);
10602 * Put encryption on PDF document.
10604 * @author Nicola Asuni
10605 * @since 2.0.000 (2008-01-02)
10607 protected function _putencryption() {
10608 if (!$this->encrypted
) {
10611 $this->encryptdata
['objid'] = $this->_newobj();
10613 if (!isset($this->encryptdata
['Filter']) OR empty($this->encryptdata
['Filter'])) {
10614 $this->encryptdata
['Filter'] = 'Standard';
10616 $out .= ' /Filter /'.$this->encryptdata
['Filter'];
10617 if (isset($this->encryptdata
['SubFilter']) AND !empty($this->encryptdata
['SubFilter'])) {
10618 $out .= ' /SubFilter /'.$this->encryptdata
['SubFilter'];
10620 if (!isset($this->encryptdata
['V']) OR empty($this->encryptdata
['V'])) {
10621 $this->encryptdata
['V'] = 1;
10623 // V is a code specifying the algorithm to be used in encrypting and decrypting the document
10624 $out .= ' /V '.$this->encryptdata
['V'];
10625 if (isset($this->encryptdata
['Length']) AND !empty($this->encryptdata
['Length'])) {
10626 // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
10627 $out .= ' /Length '.$this->encryptdata
['Length'];
10629 $out .= ' /Length 40';
10631 if ($this->encryptdata
['V'] >= 4) {
10632 if (!isset($this->encryptdata
['StmF']) OR empty($this->encryptdata
['StmF'])) {
10633 $this->encryptdata
['StmF'] = 'Identity';
10635 if (!isset($this->encryptdata
['StrF']) OR empty($this->encryptdata
['StrF'])) {
10636 // The name of the crypt filter that shall be used when decrypting all strings in the document.
10637 $this->encryptdata
['StrF'] = 'Identity';
10639 // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
10640 if (isset($this->encryptdata
['CF']) AND !empty($this->encryptdata
['CF'])) {
10642 $out .= ' /'.$this->encryptdata
['StmF'].' <<';
10643 $out .= ' /Type /CryptFilter';
10644 if (isset($this->encryptdata
['CF']['CFM']) AND !empty($this->encryptdata
['CF']['CFM'])) {
10646 $out .= ' /CFM /'.$this->encryptdata
['CF']['CFM'];
10647 if ($this->encryptdata
['pubkey']) {
10648 $out .= ' /Recipients [';
10649 foreach ($this->encryptdata
['Recipients'] as $rec) {
10650 $out .= ' <'.$rec.'>';
10653 if (isset($this->encryptdata
['CF']['EncryptMetadata']) AND (!$this->encryptdata
['CF']['EncryptMetadata'])) {
10654 $out .= ' /EncryptMetadata false';
10656 $out .= ' /EncryptMetadata true';
10660 $out .= ' /CFM /None';
10662 if (isset($this->encryptdata
['CF']['AuthEvent']) AND !empty($this->encryptdata
['CF']['AuthEvent'])) {
10663 // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
10664 $out .= ' /AuthEvent /'.$this->encryptdata
['CF']['AuthEvent'];
10666 $out .= ' /AuthEvent /DocOpen';
10668 if (isset($this->encryptdata
['CF']['Length']) AND !empty($this->encryptdata
['CF']['Length'])) {
10669 // The bit length of the encryption key.
10670 $out .= ' /Length '.$this->encryptdata
['CF']['Length'];
10674 // The name of the crypt filter that shall be used by default when decrypting streams.
10675 $out .= ' /StmF /'.$this->encryptdata
['StmF'];
10676 // The name of the crypt filter that shall be used when decrypting all strings in the document.
10677 $out .= ' /StrF /'.$this->encryptdata
['StrF'];
10678 if (isset($this->encryptdata
['EFF']) AND !empty($this->encryptdata
['EFF'])) {
10679 // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
10680 $out .= ' /EFF /'.$this->encryptdata
[''];
10683 // Additional encryption dictionary entries for the standard security handler
10684 if ($this->encryptdata
['pubkey']) {
10685 if (($this->encryptdata
['V'] < 4) AND isset($this->encryptdata
['Recipients']) AND !empty($this->encryptdata
['Recipients'])) {
10686 $out .= ' /Recipients [';
10687 foreach ($this->encryptdata
['Recipients'] as $rec) {
10688 $out .= ' <'.$rec.'>';
10694 if ($this->encryptdata
['V'] == 5) { // AES-256
10696 $out .= ' /OE ('.TCPDF_STATIC
::_escape($this->encryptdata
['OE']).')';
10697 $out .= ' /UE ('.TCPDF_STATIC
::_escape($this->encryptdata
['UE']).')';
10698 $out .= ' /Perms ('.TCPDF_STATIC
::_escape($this->encryptdata
['perms']).')';
10699 } elseif ($this->encryptdata
['V'] == 4) { // AES-128
10701 } elseif ($this->encryptdata
['V'] < 2) { // RC-40
10706 $out .= ' /O ('.TCPDF_STATIC
::_escape($this->encryptdata
['O']).')';
10707 $out .= ' /U ('.TCPDF_STATIC
::_escape($this->encryptdata
['U']).')';
10708 $out .= ' /P '.$this->encryptdata
['P'];
10709 if (isset($this->encryptdata
['EncryptMetadata']) AND (!$this->encryptdata
['EncryptMetadata'])) {
10710 $out .= ' /EncryptMetadata false';
10712 $out .= ' /EncryptMetadata true';
10716 $out .= "\n".'endobj';
10721 * Compute U value (used for encryption)
10722 * @return string U value
10724 * @since 2.0.000 (2008-01-02)
10725 * @author Nicola Asuni
10727 protected function _Uvalue() {
10728 if ($this->encryptdata
['mode'] == 0) { // RC4-40
10729 return TCPDF_STATIC
::_RC4($this->encryptdata
['key'], TCPDF_STATIC
::$enc_padding, $this->last_enc_key
, $this->last_enc_key_c
);
10730 } elseif ($this->encryptdata
['mode'] < 3) { // RC4-128, AES-128
10731 $tmp = TCPDF_STATIC
::_md5_16(TCPDF_STATIC
::$enc_padding.$this->encryptdata
['fileid']);
10732 $enc = TCPDF_STATIC
::_RC4($this->encryptdata
['key'], $tmp, $this->last_enc_key
, $this->last_enc_key_c
);
10733 $len = strlen($tmp);
10734 for ($i = 1; $i <= 19; ++
$i) {
10736 for ($j = 0; $j < $len; ++
$j) {
10737 $ek .= chr(ord($this->encryptdata
['key'][$j]) ^
$i);
10739 $enc = TCPDF_STATIC
::_RC4($ek, $enc, $this->last_enc_key
, $this->last_enc_key_c
);
10741 $enc .= str_repeat("\x00", 16);
10742 return substr($enc, 0, 32);
10743 } elseif ($this->encryptdata
['mode'] == 3) { // AES-256
10744 $seed = TCPDF_STATIC
::_md5_16(TCPDF_STATIC
::getRandomSeed());
10745 // User Validation Salt
10746 $this->encryptdata
['UVS'] = substr($seed, 0, 8);
10748 $this->encryptdata
['UKS'] = substr($seed, 8, 16);
10749 return hash('sha256', $this->encryptdata
['user_password'].$this->encryptdata
['UVS'], true).$this->encryptdata
['UVS'].$this->encryptdata
['UKS'];
10754 * Compute UE value (used for encryption)
10755 * @return string UE value
10757 * @since 5.9.006 (2010-10-19)
10758 * @author Nicola Asuni
10760 protected function _UEvalue() {
10761 $hashkey = hash('sha256', $this->encryptdata
['user_password'].$this->encryptdata
['UKS'], true);
10762 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128
, MCRYPT_MODE_CBC
));
10763 return mcrypt_encrypt(MCRYPT_RIJNDAEL_128
, $hashkey, $this->encryptdata
['key'], MCRYPT_MODE_CBC
, $iv);
10767 * Compute O value (used for encryption)
10768 * @return string O value
10770 * @since 2.0.000 (2008-01-02)
10771 * @author Nicola Asuni
10773 protected function _Ovalue() {
10774 if ($this->encryptdata
['mode'] < 3) { // RC4-40, RC4-128, AES-128
10775 $tmp = TCPDF_STATIC
::_md5_16($this->encryptdata
['owner_password']);
10776 if ($this->encryptdata
['mode'] > 0) {
10777 for ($i = 0; $i < 50; ++
$i) {
10778 $tmp = TCPDF_STATIC
::_md5_16($tmp);
10781 $owner_key = substr($tmp, 0, ($this->encryptdata
['Length'] / 8));
10782 $enc = TCPDF_STATIC
::_RC4($owner_key, $this->encryptdata
['user_password'], $this->last_enc_key
, $this->last_enc_key_c
);
10783 if ($this->encryptdata
['mode'] > 0) {
10784 $len = strlen($owner_key);
10785 for ($i = 1; $i <= 19; ++
$i) {
10787 for ($j = 0; $j < $len; ++
$j) {
10788 $ek .= chr(ord($owner_key[$j]) ^
$i);
10790 $enc = TCPDF_STATIC
::_RC4($ek, $enc, $this->last_enc_key
, $this->last_enc_key_c
);
10794 } elseif ($this->encryptdata
['mode'] == 3) { // AES-256
10795 $seed = TCPDF_STATIC
::_md5_16(TCPDF_STATIC
::getRandomSeed());
10796 // Owner Validation Salt
10797 $this->encryptdata
['OVS'] = substr($seed, 0, 8);
10799 $this->encryptdata
['OKS'] = substr($seed, 8, 16);
10800 return hash('sha256', $this->encryptdata
['owner_password'].$this->encryptdata
['OVS'].$this->encryptdata
['U'], true).$this->encryptdata
['OVS'].$this->encryptdata
['OKS'];
10805 * Compute OE value (used for encryption)
10806 * @return string OE value
10808 * @since 5.9.006 (2010-10-19)
10809 * @author Nicola Asuni
10811 protected function _OEvalue() {
10812 $hashkey = hash('sha256', $this->encryptdata
['owner_password'].$this->encryptdata
['OKS'].$this->encryptdata
['U'], true);
10813 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128
, MCRYPT_MODE_CBC
));
10814 return mcrypt_encrypt(MCRYPT_RIJNDAEL_128
, $hashkey, $this->encryptdata
['key'], MCRYPT_MODE_CBC
, $iv);
10818 * Convert password for AES-256 encryption mode
10819 * @param $password (string) password
10820 * @return string password
10822 * @since 5.9.006 (2010-10-19)
10823 * @author Nicola Asuni
10825 protected function _fixAES256Password($password) {
10826 $psw = ''; // password to be returned
10827 $psw_array = TCPDF_FONTS
::utf8Bidi(TCPDF_FONTS
::UTF8StringToArray($password, $this->isunicode
, $this->CurrentFont
), $password, $this->rtl
, $this->isunicode
, $this->CurrentFont
);
10828 foreach ($psw_array as $c) {
10829 $psw .= TCPDF_FONTS
::unichr($c, $this->isunicode
);
10831 return substr($psw, 0, 127);
10835 * Compute encryption key
10837 * @since 2.0.000 (2008-01-02)
10838 * @author Nicola Asuni
10840 protected function _generateencryptionkey() {
10841 $keybytelen = ($this->encryptdata
['Length'] / 8);
10842 if (!$this->encryptdata
['pubkey']) { // standard mode
10843 if ($this->encryptdata
['mode'] == 3) { // AES-256
10844 // generate 256 bit random key
10845 $this->encryptdata
['key'] = substr(hash('sha256', TCPDF_STATIC
::getRandomSeed(), true), 0, $keybytelen);
10846 // truncate passwords
10847 $this->encryptdata
['user_password'] = $this->_fixAES256Password($this->encryptdata
['user_password']);
10848 $this->encryptdata
['owner_password'] = $this->_fixAES256Password($this->encryptdata
['owner_password']);
10850 $this->encryptdata
['U'] = $this->_Uvalue();
10851 // Compute UE value
10852 $this->encryptdata
['UE'] = $this->_UEvalue();
10854 $this->encryptdata
['O'] = $this->_Ovalue();
10855 // Compute OE value
10856 $this->encryptdata
['OE'] = $this->_OEvalue();
10858 $this->encryptdata
['P'] = $this->encryptdata
['protection'];
10859 // Computing the encryption dictionary's Perms (permissions) value
10860 $perms = TCPDF_STATIC
::getEncPermissionsString($this->encryptdata
['protection']); // bytes 0-3
10861 $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
10862 if (isset($this->encryptdata
['CF']['EncryptMetadata']) AND (!$this->encryptdata
['CF']['EncryptMetadata'])) { // byte 8
10867 $perms .= 'adb'; // bytes 9-11
10868 $perms .= 'nick'; // bytes 12-15
10869 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128
, MCRYPT_MODE_ECB
));
10870 $this->encryptdata
['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128
, $this->encryptdata
['key'], $perms, MCRYPT_MODE_ECB
, $iv);
10871 } else { // RC4-40, RC4-128, AES-128
10873 $this->encryptdata
['user_password'] = substr($this->encryptdata
['user_password'].TCPDF_STATIC
::$enc_padding, 0, 32);
10874 $this->encryptdata
['owner_password'] = substr($this->encryptdata
['owner_password'].TCPDF_STATIC
::$enc_padding, 0, 32);
10876 $this->encryptdata
['O'] = $this->_Ovalue();
10877 // get default permissions (reverse byte order)
10878 $permissions = TCPDF_STATIC
::getEncPermissionsString($this->encryptdata
['protection']);
10879 // Compute encryption key
10880 $tmp = TCPDF_STATIC
::_md5_16($this->encryptdata
['user_password'].$this->encryptdata
['O'].$permissions.$this->encryptdata
['fileid']);
10881 if ($this->encryptdata
['mode'] > 0) {
10882 for ($i = 0; $i < 50; ++
$i) {
10883 $tmp = TCPDF_STATIC
::_md5_16(substr($tmp, 0, $keybytelen));
10886 $this->encryptdata
['key'] = substr($tmp, 0, $keybytelen);
10888 $this->encryptdata
['U'] = $this->_Uvalue();
10890 $this->encryptdata
['P'] = $this->encryptdata
['protection'];
10892 } else { // Public-Key mode
10893 // random 20-byte seed
10894 $seed = sha1(TCPDF_STATIC
::getRandomSeed(), true);
10895 $recipient_bytes = '';
10896 foreach ($this->encryptdata
['pubkeys'] as $pubkey) {
10897 // for each public certificate
10898 if (isset($pubkey['p'])) {
10899 $pkprotection = TCPDF_STATIC
::getUserPermissionCode($pubkey['p'], $this->encryptdata
['mode']);
10901 $pkprotection = $this->encryptdata
['protection'];
10903 // get default permissions (reverse byte order)
10904 $pkpermissions = TCPDF_STATIC
::getEncPermissionsString($pkprotection);
10906 $envelope = $seed.$pkpermissions;
10907 // write the envelope data to a temporary file
10908 $tempkeyfile = TCPDF_STATIC
::getObjFilename('key');
10909 $f = fopen($tempkeyfile, 'wb');
10911 $this->Error('Unable to create temporary key file: '.$tempkeyfile);
10913 $envelope_length = strlen($envelope);
10914 fwrite($f, $envelope, $envelope_length);
10916 $tempencfile = TCPDF_STATIC
::getObjFilename('enc');
10917 if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED
)) {
10918 $this->Error('Unable to encrypt the file: '.$tempkeyfile);
10920 unlink($tempkeyfile);
10921 // read encryption signature
10922 $signature = file_get_contents($tempencfile, false, null, $envelope_length);
10923 unlink($tempencfile);
10924 // extract signature
10925 $signature = substr($signature, strpos($signature, 'Content-Disposition'));
10926 $tmparr = explode("\n\n", $signature);
10927 $signature = trim($tmparr[1]);
10929 // decode signature
10930 $signature = base64_decode($signature);
10931 // convert signature to hex
10932 $hexsignature = current(unpack('H*', $signature));
10933 // store signature on recipients array
10934 $this->encryptdata
['Recipients'][] = $hexsignature;
10935 // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
10936 $recipient_bytes .= $signature;
10938 // calculate encryption key
10939 if ($this->encryptdata
['mode'] == 3) { // AES-256
10940 $this->encryptdata
['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
10941 } else { // RC4-40, RC4-128, AES-128
10942 $this->encryptdata
['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
10948 * Set document protection
10949 * Remark: the protection against modification is for people who have the full Acrobat product.
10950 * If you don't set any password, the document will open as usual. If you set a user password, the PDF viewer will ask for it before displaying the document. The master password, if different from the user one, can be used to get full access.
10951 * Note: protecting a document requires to encrypt it, which increases the processing time a lot. This can cause a PHP time-out in some cases, especially if the document contains images or fonts.
10952 * @param $permissions (Array) the set of permissions (specify the ones you want to block):<ul><li>print : Print the document;</li><li>modify : Modify the contents of the document by operations other than those controlled by 'fill-forms', 'extract' and 'assemble';</li><li>copy : Copy or otherwise extract text and graphics from the document;</li><li>annot-forms : Add or modify text annotations, fill in interactive form fields, and, if 'modify' is also set, create or modify interactive form fields (including signature fields);</li><li>fill-forms : Fill in existing interactive form fields (including signature fields), even if 'annot-forms' is not specified;</li><li>extract : Extract text and graphics (in support of accessibility to users with disabilities or for other purposes);</li><li>assemble : Assemble the document (insert, rotate, or delete pages and create bookmarks or thumbnail images), even if 'modify' is not set;</li><li>print-high : Print the document to a representation from which a faithful digital copy of the PDF content could be generated. When this is not set, printing is limited to a low-level representation of the appearance, possibly of degraded quality.</li><li>owner : (inverted logic - only for public-key) when set permits change of encryption and enables all other permissions.</li></ul>
10953 * @param $user_pass (String) user password. Empty by default.
10954 * @param $owner_pass (String) owner password. If not specified, a random value is used.
10955 * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
10956 * @param $pubkeys (String) array of recipients containing public-key certificates ('c') and permissions ('p'). For example: array(array('c' => 'file://../examples/data/cert/tcpdf.crt', 'p' => array('print')))
10958 * @since 2.0.000 (2008-01-02)
10959 * @author Nicola Asuni
10961 public function SetProtection($permissions=array('print', 'modify', 'copy', 'annot-forms', 'fill-forms', 'extract', 'assemble', 'print-high'), $user_pass='', $owner_pass=null, $mode=0, $pubkeys=null) {
10962 if ($this->pdfa_mode
) {
10963 // encryption is not allowed in PDF/A mode
10966 $this->encryptdata
['protection'] = TCPDF_STATIC
::getUserPermissionCode($permissions, $mode);
10967 if (($pubkeys !== null) AND (is_array($pubkeys))) {
10969 $this->encryptdata
['pubkeys'] = $pubkeys;
10971 // public-Key Security requires at least 128 bit
10974 if (!function_exists('openssl_pkcs7_encrypt')) {
10975 $this->Error('Public-Key Security requires openssl library.');
10977 // Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
10978 $this->encryptdata
['pubkey'] = true;
10979 $this->encryptdata
['Filter'] = 'Adobe.PubSec';
10980 $this->encryptdata
['StmF'] = 'DefaultCryptFilter';
10981 $this->encryptdata
['StrF'] = 'DefaultCryptFilter';
10983 // standard mode (password mode)
10984 $this->encryptdata
['pubkey'] = false;
10985 $this->encryptdata
['Filter'] = 'Standard';
10986 $this->encryptdata
['StmF'] = 'StdCF';
10987 $this->encryptdata
['StrF'] = 'StdCF';
10989 if ($mode > 1) { // AES
10990 if (!extension_loaded('mcrypt')) {
10991 $this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).');
10993 if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128
) === false) {
10994 $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
10996 if (($mode == 3) AND !function_exists('hash')) {
10997 // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
10998 $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
11001 if ($owner_pass === null) {
11002 $owner_pass = md5(TCPDF_STATIC
::getRandomSeed());
11004 $this->encryptdata
['user_password'] = $user_pass;
11005 $this->encryptdata
['owner_password'] = $owner_pass;
11006 $this->encryptdata
['mode'] = $mode;
11008 case 0: { // RC4 40 bit
11009 $this->encryptdata
['V'] = 1;
11010 $this->encryptdata
['Length'] = 40;
11011 $this->encryptdata
['CF']['CFM'] = 'V2';
11014 case 1: { // RC4 128 bit
11015 $this->encryptdata
['V'] = 2;
11016 $this->encryptdata
['Length'] = 128;
11017 $this->encryptdata
['CF']['CFM'] = 'V2';
11018 if ($this->encryptdata
['pubkey']) {
11019 $this->encryptdata
['SubFilter'] = 'adbe.pkcs7.s4';
11020 $this->encryptdata
['Recipients'] = array();
11024 case 2: { // AES 128 bit
11025 $this->encryptdata
['V'] = 4;
11026 $this->encryptdata
['Length'] = 128;
11027 $this->encryptdata
['CF']['CFM'] = 'AESV2';
11028 $this->encryptdata
['CF']['Length'] = 128;
11029 if ($this->encryptdata
['pubkey']) {
11030 $this->encryptdata
['SubFilter'] = 'adbe.pkcs7.s5';
11031 $this->encryptdata
['Recipients'] = array();
11035 case 3: { // AES 256 bit
11036 $this->encryptdata
['V'] = 5;
11037 $this->encryptdata
['Length'] = 256;
11038 $this->encryptdata
['CF']['CFM'] = 'AESV3';
11039 $this->encryptdata
['CF']['Length'] = 256;
11040 if ($this->encryptdata
['pubkey']) {
11041 $this->encryptdata
['SubFilter'] = 'adbe.pkcs7.s5';
11042 $this->encryptdata
['Recipients'] = array();
11047 $this->encrypted
= true;
11048 $this->encryptdata
['fileid'] = TCPDF_STATIC
::convertHexStringToString($this->file_id
);
11049 $this->_generateencryptionkey();
11052 // END OF ENCRYPTION FUNCTIONS -------------------------
11054 // START TRANSFORMATIONS SECTION -----------------------
11057 * Starts a 2D tranformation saving current graphic state.
11058 * This function must be called before scaling, mirroring, translation, rotation and skewing.
11059 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
11061 * @since 2.1.000 (2008-01-07)
11062 * @see StartTransform(), StopTransform()
11064 public function StartTransform() {
11065 if ($this->state
!= 2) {
11068 $this->_outSaveGraphicsState();
11069 if ($this->inxobj
) {
11070 // we are inside an XObject template
11071 $this->xobjects
[$this->xobjid
]['transfmrk'][] = strlen($this->xobjects
[$this->xobjid
]['outdata']);
11073 $this->transfmrk
[$this->page
][] = $this->pagelen
[$this->page
];
11075 ++
$this->transfmatrix_key
;
11076 $this->transfmatrix
[$this->transfmatrix_key
] = array();
11080 * Stops a 2D tranformation restoring previous graphic state.
11081 * This function must be called after scaling, mirroring, translation, rotation and skewing.
11082 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
11084 * @since 2.1.000 (2008-01-07)
11085 * @see StartTransform(), StopTransform()
11087 public function StopTransform() {
11088 if ($this->state
!= 2) {
11091 $this->_outRestoreGraphicsState();
11092 if (isset($this->transfmatrix
[$this->transfmatrix_key
])) {
11093 array_pop($this->transfmatrix
[$this->transfmatrix_key
]);
11094 --$this->transfmatrix_key
;
11096 if ($this->inxobj
) {
11097 // we are inside an XObject template
11098 array_pop($this->xobjects
[$this->xobjid
]['transfmrk']);
11100 array_pop($this->transfmrk
[$this->page
]);
11104 * Horizontal Scaling.
11105 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
11106 * @param $x (int) abscissa of the scaling center. Default is current x position
11107 * @param $y (int) ordinate of the scaling center. Default is current y position
11109 * @since 2.1.000 (2008-01-07)
11110 * @see StartTransform(), StopTransform()
11112 public function ScaleX($s_x, $x='', $y='') {
11113 $this->Scale($s_x, 100, $x, $y);
11117 * Vertical Scaling.
11118 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
11119 * @param $x (int) abscissa of the scaling center. Default is current x position
11120 * @param $y (int) ordinate of the scaling center. Default is current y position
11122 * @since 2.1.000 (2008-01-07)
11123 * @see StartTransform(), StopTransform()
11125 public function ScaleY($s_y, $x='', $y='') {
11126 $this->Scale(100, $s_y, $x, $y);
11130 * Vertical and horizontal proportional Scaling.
11131 * @param $s (float) scaling factor for width and height as percent. 0 is not allowed.
11132 * @param $x (int) abscissa of the scaling center. Default is current x position
11133 * @param $y (int) ordinate of the scaling center. Default is current y position
11135 * @since 2.1.000 (2008-01-07)
11136 * @see StartTransform(), StopTransform()
11138 public function ScaleXY($s, $x='', $y='') {
11139 $this->Scale($s, $s, $x, $y);
11143 * Vertical and horizontal non-proportional Scaling.
11144 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
11145 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
11146 * @param $x (int) abscissa of the scaling center. Default is current x position
11147 * @param $y (int) ordinate of the scaling center. Default is current y position
11149 * @since 2.1.000 (2008-01-07)
11150 * @see StartTransform(), StopTransform()
11152 public function Scale($s_x, $s_y, $x='', $y='') {
11159 if (($s_x == 0) OR ($s_y == 0)) {
11160 $this->Error('Please do not use values equal to zero for scaling');
11162 $y = ($this->h
- $y) * $this->k
;
11164 //calculate elements of transformation matrix
11172 $tm[4] = $x * (1 - $s_x);
11173 $tm[5] = $y * (1 - $s_y);
11174 //scale the coordinate system
11175 $this->Transform($tm);
11179 * Horizontal Mirroring.
11180 * @param $x (int) abscissa of the point. Default is current x position
11182 * @since 2.1.000 (2008-01-07)
11183 * @see StartTransform(), StopTransform()
11185 public function MirrorH($x='') {
11186 $this->Scale(-100, 100, $x);
11190 * Verical Mirroring.
11191 * @param $y (int) ordinate of the point. Default is current y position
11193 * @since 2.1.000 (2008-01-07)
11194 * @see StartTransform(), StopTransform()
11196 public function MirrorV($y='') {
11197 $this->Scale(100, -100, '', $y);
11201 * Point reflection mirroring.
11202 * @param $x (int) abscissa of the point. Default is current x position
11203 * @param $y (int) ordinate of the point. Default is current y position
11205 * @since 2.1.000 (2008-01-07)
11206 * @see StartTransform(), StopTransform()
11208 public function MirrorP($x='',$y='') {
11209 $this->Scale(-100, -100, $x, $y);
11213 * Reflection against a straight line through point (x, y) with the gradient angle (angle).
11214 * @param $angle (float) gradient angle of the straight line. Default is 0 (horizontal line).
11215 * @param $x (int) abscissa of the point. Default is current x position
11216 * @param $y (int) ordinate of the point. Default is current y position
11218 * @since 2.1.000 (2008-01-07)
11219 * @see StartTransform(), StopTransform()
11221 public function MirrorL($angle=0, $x='',$y='') {
11222 $this->Scale(-100, 100, $x, $y);
11223 $this->Rotate(-2*($angle-90), $x, $y);
11227 * Translate graphic object horizontally.
11228 * @param $t_x (int) movement to the right (or left for RTL)
11230 * @since 2.1.000 (2008-01-07)
11231 * @see StartTransform(), StopTransform()
11233 public function TranslateX($t_x) {
11234 $this->Translate($t_x, 0);
11238 * Translate graphic object vertically.
11239 * @param $t_y (int) movement to the bottom
11241 * @since 2.1.000 (2008-01-07)
11242 * @see StartTransform(), StopTransform()
11244 public function TranslateY($t_y) {
11245 $this->Translate(0, $t_y);
11249 * Translate graphic object horizontally and vertically.
11250 * @param $t_x (int) movement to the right
11251 * @param $t_y (int) movement to the bottom
11253 * @since 2.1.000 (2008-01-07)
11254 * @see StartTransform(), StopTransform()
11256 public function Translate($t_x, $t_y) {
11257 //calculate elements of transformation matrix
11263 $tm[4] = $t_x * $this->k
;
11264 $tm[5] = -$t_y * $this->k
;
11265 //translate the coordinate system
11266 $this->Transform($tm);
11271 * @param $angle (float) angle in degrees for counter-clockwise rotation
11272 * @param $x (int) abscissa of the rotation center. Default is current x position
11273 * @param $y (int) ordinate of the rotation center. Default is current y position
11275 * @since 2.1.000 (2008-01-07)
11276 * @see StartTransform(), StopTransform()
11278 public function Rotate($angle, $x='', $y='') {
11285 $y = ($this->h
- $y) * $this->k
;
11287 //calculate elements of transformation matrix
11289 $tm[0] = cos(deg2rad($angle));
11290 $tm[1] = sin(deg2rad($angle));
11293 $tm[4] = $x +
($tm[1] * $y) - ($tm[0] * $x);
11294 $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
11295 //rotate the coordinate system around ($x,$y)
11296 $this->Transform($tm);
11300 * Skew horizontally.
11301 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
11302 * @param $x (int) abscissa of the skewing center. default is current x position
11303 * @param $y (int) ordinate of the skewing center. default is current y position
11305 * @since 2.1.000 (2008-01-07)
11306 * @see StartTransform(), StopTransform()
11308 public function SkewX($angle_x, $x='', $y='') {
11309 $this->Skew($angle_x, 0, $x, $y);
11314 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
11315 * @param $x (int) abscissa of the skewing center. default is current x position
11316 * @param $y (int) ordinate of the skewing center. default is current y position
11318 * @since 2.1.000 (2008-01-07)
11319 * @see StartTransform(), StopTransform()
11321 public function SkewY($angle_y, $x='', $y='') {
11322 $this->Skew(0, $angle_y, $x, $y);
11327 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
11328 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
11329 * @param $x (int) abscissa of the skewing center. default is current x position
11330 * @param $y (int) ordinate of the skewing center. default is current y position
11332 * @since 2.1.000 (2008-01-07)
11333 * @see StartTransform(), StopTransform()
11335 public function Skew($angle_x, $angle_y, $x='', $y='') {
11342 if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
11343 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
11346 $y = ($this->h
- $y) * $this->k
;
11347 //calculate elements of transformation matrix
11350 $tm[1] = tan(deg2rad($angle_y));
11351 $tm[2] = tan(deg2rad($angle_x));
11353 $tm[4] = -$tm[2] * $y;
11354 $tm[5] = -$tm[1] * $x;
11355 //skew the coordinate system
11356 $this->Transform($tm);
11360 * Apply graphic transformations.
11361 * @param $tm (array) transformation matrix
11363 * @since 2.1.000 (2008-01-07)
11364 * @see StartTransform(), StopTransform()
11366 protected function Transform($tm) {
11367 if ($this->state
!= 2) {
11370 $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
11371 // add tranformation matrix
11372 $this->transfmatrix
[$this->transfmatrix_key
][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
11373 // update transformation mark
11374 if ($this->inxobj
) {
11375 // we are inside an XObject template
11376 if (end($this->xobjects
[$this->xobjid
]['transfmrk']) !== false) {
11377 $key = key($this->xobjects
[$this->xobjid
]['transfmrk']);
11378 $this->xobjects
[$this->xobjid
]['transfmrk'][$key] = strlen($this->xobjects
[$this->xobjid
]['outdata']);
11380 } elseif (end($this->transfmrk
[$this->page
]) !== false) {
11381 $key = key($this->transfmrk
[$this->page
]);
11382 $this->transfmrk
[$this->page
][$key] = $this->pagelen
[$this->page
];
11386 // END TRANSFORMATIONS SECTION -------------------------
11388 // START GRAPHIC FUNCTIONS SECTION ---------------------
11389 // The following section is based on the code provided by David Hernandez Sanz
11392 * Defines the line width. By default, the value equals 0.2 mm. The method can be called before the first page is created and the value is retained from page to page.
11393 * @param $width (float) The width.
11396 * @see Line(), Rect(), Cell(), MultiCell()
11398 public function SetLineWidth($width) {
11400 $this->LineWidth
= $width;
11401 $this->linestyleWidth
= sprintf('%F w', ($width * $this->k
));
11402 if ($this->state
== 2) {
11403 $this->_out($this->linestyleWidth
);
11408 * Returns the current the line width.
11409 * @return int Line width
11411 * @since 2.1.000 (2008-01-07)
11412 * @see Line(), SetLineWidth()
11414 public function GetLineWidth() {
11415 return $this->LineWidth
;
11420 * @param $style (array) Line style. Array with keys among the following:
11422 * <li>width (float): Width of the line in user units.</li>
11423 * <li>cap (string): Type of cap to put on the line. Possible values are:
11424 * butt, round, square. The difference between "square" and "butt" is that
11425 * "square" projects a flat end past the end of the line.</li>
11426 * <li>join (string): Type of join. Possible values are: miter, round,
11428 * <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
11429 * series of length values, which are the lengths of the on and off dashes.
11430 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
11431 * 1 off, 2 on, 1 off, ...</li>
11432 * <li>phase (integer): Modifier on the dash pattern which is used to shift
11433 * the point at which the pattern starts.</li>
11434 * <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName).</li>
11436 * @param $ret (boolean) if true do not send the command.
11437 * @return string the PDF command
11439 * @since 2.1.000 (2008-01-08)
11441 public function SetLineStyle($style, $ret=false) {
11442 $s = ''; // string to be returned
11443 if (!is_array($style)) {
11446 if (isset($style['width'])) {
11447 $this->LineWidth
= $style['width'];
11448 $this->linestyleWidth
= sprintf('%F w', ($style['width'] * $this->k
));
11449 $s .= $this->linestyleWidth
.' ';
11451 if (isset($style['cap'])) {
11452 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
11453 if (isset($ca[$style['cap']])) {
11454 $this->linestyleCap
= $ca[$style['cap']].' J';
11455 $s .= $this->linestyleCap
.' ';
11458 if (isset($style['join'])) {
11459 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
11460 if (isset($ja[$style['join']])) {
11461 $this->linestyleJoin
= $ja[$style['join']].' j';
11462 $s .= $this->linestyleJoin
.' ';
11465 if (isset($style['dash'])) {
11467 if ($style['dash']) {
11468 if (preg_match('/^.+,/', $style['dash']) > 0) {
11469 $tab = explode(',', $style['dash']);
11471 $tab = array($style['dash']);
11474 foreach ($tab as $i => $v) {
11476 $dash_string .= ' ';
11478 $dash_string .= sprintf('%F', $v);
11481 if (!isset($style['phase']) OR !$style['dash']) {
11482 $style['phase'] = 0;
11484 $this->linestyleDash
= sprintf('[%s] %F d', $dash_string, $style['phase']);
11485 $s .= $this->linestyleDash
.' ';
11487 if (isset($style['color'])) {
11488 $s .= $this->SetDrawColorArray($style['color'], true).' ';
11490 if (!$ret AND ($this->state
== 2)) {
11497 * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment.
11498 * @param $x (float) Abscissa of point.
11499 * @param $y (float) Ordinate of point.
11501 * @since 2.1.000 (2008-01-08)
11503 protected function _outPoint($x, $y) {
11504 if ($this->state
== 2) {
11505 $this->_out(sprintf('%F %F m', ($x * $this->k
), (($this->h
- $y) * $this->k
)));
11510 * Append a straight line segment from the current point to the point (x, y).
11511 * The new current point shall be (x, y).
11512 * @param $x (float) Abscissa of end point.
11513 * @param $y (float) Ordinate of end point.
11515 * @since 2.1.000 (2008-01-08)
11517 protected function _outLine($x, $y) {
11518 if ($this->state
== 2) {
11519 $this->_out(sprintf('%F %F l', ($x * $this->k
), (($this->h
- $y) * $this->k
)));
11524 * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space.
11525 * @param $x (float) Abscissa of upper-left corner.
11526 * @param $y (float) Ordinate of upper-left corner.
11527 * @param $w (float) Width.
11528 * @param $h (float) Height.
11529 * @param $op (string) options
11531 * @since 2.1.000 (2008-01-08)
11533 protected function _outRect($x, $y, $w, $h, $op) {
11534 if ($this->state
== 2) {
11535 $this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k
), (($this->h
- $y) * $this->k
), ($w * $this->k
), (-$h * $this->k
), $op));
11540 * Append a cubic Bezier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x2, y2) as the Bezier control points.
11541 * The new current point shall be (x3, y3).
11542 * @param $x1 (float) Abscissa of control point 1.
11543 * @param $y1 (float) Ordinate of control point 1.
11544 * @param $x2 (float) Abscissa of control point 2.
11545 * @param $y2 (float) Ordinate of control point 2.
11546 * @param $x3 (float) Abscissa of end point.
11547 * @param $y3 (float) Ordinate of end point.
11549 * @since 2.1.000 (2008-01-08)
11551 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
11552 if ($this->state
== 2) {
11553 $this->_out(sprintf('%F %F %F %F %F %F c', ($x1 * $this->k
), (($this->h
- $y1) * $this->k
), ($x2 * $this->k
), (($this->h
- $y2) * $this->k
), ($x3 * $this->k
), (($this->h
- $y3) * $this->k
)));
11558 * Append a cubic Bezier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using the current point and (x2, y2) as the Bezier control points.
11559 * The new current point shall be (x3, y3).
11560 * @param $x2 (float) Abscissa of control point 2.
11561 * @param $y2 (float) Ordinate of control point 2.
11562 * @param $x3 (float) Abscissa of end point.
11563 * @param $y3 (float) Ordinate of end point.
11565 * @since 4.9.019 (2010-04-26)
11567 protected function _outCurveV($x2, $y2, $x3, $y3) {
11568 if ($this->state
== 2) {
11569 $this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k
), (($this->h
- $y2) * $this->k
), ($x3 * $this->k
), (($this->h
- $y3) * $this->k
)));
11574 * Append a cubic Bezier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x3, y3) as the Bezier control points.
11575 * The new current point shall be (x3, y3).
11576 * @param $x1 (float) Abscissa of control point 1.
11577 * @param $y1 (float) Ordinate of control point 1.
11578 * @param $x3 (float) Abscissa of end point.
11579 * @param $y3 (float) Ordinate of end point.
11581 * @since 2.1.000 (2008-01-08)
11583 protected function _outCurveY($x1, $y1, $x3, $y3) {
11584 if ($this->state
== 2) {
11585 $this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k
), (($this->h
- $y1) * $this->k
), ($x3 * $this->k
), (($this->h
- $y3) * $this->k
)));
11590 * Draws a line between two points.
11591 * @param $x1 (float) Abscissa of first point.
11592 * @param $y1 (float) Ordinate of first point.
11593 * @param $x2 (float) Abscissa of second point.
11594 * @param $y2 (float) Ordinate of second point.
11595 * @param $style (array) Line style. Array like for SetLineStyle(). Default value: default line style (empty array).
11598 * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
11600 public function Line($x1, $y1, $x2, $y2, $style=array()) {
11601 if ($this->state
!= 2) {
11604 if (is_array($style)) {
11605 $this->SetLineStyle($style);
11607 $this->_outPoint($x1, $y1);
11608 $this->_outLine($x2, $y2);
11613 * Draws a rectangle.
11614 * @param $x (float) Abscissa of upper-left corner.
11615 * @param $y (float) Ordinate of upper-left corner.
11616 * @param $w (float) Width.
11617 * @param $h (float) Height.
11618 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11619 * @param $border_style (array) Border style of rectangle. Array with keys among the following:
11621 * <li>all: Line style of all borders. Array like for SetLineStyle().</li>
11622 * <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for SetLineStyle().</li>
11624 * If a key is not present or is null, the correspondent border is not drawn. Default value: default line style (empty array).
11625 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11628 * @see SetLineStyle()
11630 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
11631 if ($this->state
!= 2) {
11634 if (empty($style)) {
11637 if (!(strpos($style, 'F') === false) AND !empty($fill_color)) {
11638 // set background color
11639 $this->SetFillColorArray($fill_color);
11641 if (!empty($border_style)) {
11642 if (isset($border_style['all']) AND !empty($border_style['all'])) {
11643 //set global style for border
11644 $this->SetLineStyle($border_style['all']);
11645 $border_style = array();
11647 // remove stroke operator from style
11648 $opnostroke = array('S' => '', 'D' => '', 's' => '', 'd' => '', 'B' => 'F', 'FD' => 'F', 'DF' => 'F', 'B*' => 'F*', 'F*D' => 'F*', 'DF*' => 'F*', 'b' => 'f', 'fd' => 'f', 'df' => 'f', 'b*' => 'f*', 'f*d' => 'f*', 'df*' => 'f*' );
11649 if (isset($opnostroke[$style])) {
11650 $style = $opnostroke[$style];
11654 if (!empty($style)) {
11655 $op = TCPDF_STATIC
::getPathPaintOperator($style);
11656 $this->_outRect($x, $y, $w, $h, $op);
11658 if (!empty($border_style)) {
11659 $border_style2 = array();
11660 foreach ($border_style as $line => $value) {
11661 $length = strlen($line);
11662 for ($i = 0; $i < $length; ++
$i) {
11663 $border_style2[$line[$i]] = $value;
11666 $border_style = $border_style2;
11667 if (isset($border_style['L']) AND $border_style['L']) {
11668 $this->Line($x, $y, $x, $y +
$h, $border_style['L']);
11670 if (isset($border_style['T']) AND $border_style['T']) {
11671 $this->Line($x, $y, $x +
$w, $y, $border_style['T']);
11673 if (isset($border_style['R']) AND $border_style['R']) {
11674 $this->Line($x +
$w, $y, $x +
$w, $y +
$h, $border_style['R']);
11676 if (isset($border_style['B']) AND $border_style['B']) {
11677 $this->Line($x, $y +
$h, $x +
$w, $y +
$h, $border_style['B']);
11683 * Draws a Bezier curve.
11684 * The Bezier curve is a tangent to the line between the control points at
11685 * either end of the curve.
11686 * @param $x0 (float) Abscissa of start point.
11687 * @param $y0 (float) Ordinate of start point.
11688 * @param $x1 (float) Abscissa of control point 1.
11689 * @param $y1 (float) Ordinate of control point 1.
11690 * @param $x2 (float) Abscissa of control point 2.
11691 * @param $y2 (float) Ordinate of control point 2.
11692 * @param $x3 (float) Abscissa of end point.
11693 * @param $y3 (float) Ordinate of end point.
11694 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11695 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
11696 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11698 * @see SetLineStyle()
11699 * @since 2.1.000 (2008-01-08)
11701 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
11702 if ($this->state
!= 2) {
11705 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11706 $this->SetFillColorArray($fill_color);
11708 $op = TCPDF_STATIC
::getPathPaintOperator($style);
11710 $this->SetLineStyle($line_style);
11712 $this->_outPoint($x0, $y0);
11713 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11718 * Draws a poly-Bezier curve.
11719 * Each Bezier curve segment is a tangent to the line between the control points at
11720 * either end of the curve.
11721 * @param $x0 (float) Abscissa of start point.
11722 * @param $y0 (float) Ordinate of start point.
11723 * @param $segments (float) An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
11724 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11725 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
11726 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11728 * @see SetLineStyle()
11729 * @since 3.0008 (2008-05-12)
11731 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
11732 if ($this->state
!= 2) {
11735 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11736 $this->SetFillColorArray($fill_color);
11738 $op = TCPDF_STATIC
::getPathPaintOperator($style);
11740 $line_style = array();
11743 $this->SetLineStyle($line_style);
11745 $this->_outPoint($x0, $y0);
11746 foreach ($segments as $segment) {
11747 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
11748 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11754 * Draws an ellipse.
11755 * An ellipse is formed from n Bezier curves.
11756 * @param $x0 (float) Abscissa of center point.
11757 * @param $y0 (float) Ordinate of center point.
11758 * @param $rx (float) Horizontal radius.
11759 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
11760 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
11761 * @param $astart: (float) Angle start of draw line. Default value: 0.
11762 * @param $afinish: (float) Angle finish of draw line. Default value: 360.
11763 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11764 * @param $line_style (array) Line style of ellipse. Array like for SetLineStyle(). Default value: default line style (empty array).
11765 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11766 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
11767 * @author Nicola Asuni
11769 * @since 2.1.000 (2008-01-08)
11771 public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11772 if ($this->state
!= 2) {
11775 if (TCPDF_STATIC
::empty_string($ry) OR ($ry == 0)) {
11778 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11779 $this->SetFillColorArray($fill_color);
11781 $op = TCPDF_STATIC
::getPathPaintOperator($style);
11783 $line_style = array();
11786 $this->SetLineStyle($line_style);
11788 $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
11793 * Append an elliptical arc to the current path.
11794 * An ellipse is formed from n Bezier curves.
11795 * @param $xc (float) Abscissa of center point.
11796 * @param $yc (float) Ordinate of center point.
11797 * @param $rx (float) Horizontal radius.
11798 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
11799 * @param $xang: (float) Angle between the X-axis and the major axis of the ellipse. Default value: 0.
11800 * @param $angs: (float) Angle start of draw line. Default value: 0.
11801 * @param $angf: (float) Angle finish of draw line. Default value: 360.
11802 * @param $pie (boolean) if true do not mark the border point (used to draw pie sectors).
11803 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
11804 * @param $startpoint (boolean) if true output a starting point.
11805 * @param $ccw (boolean) if true draws in counter-clockwise.
11806 * @param $svg (boolean) if true the angles are in svg mode (already calculated).
11807 * @return array bounding box coordinates (x min, y min, x max, y max)
11808 * @author Nicola Asuni
11810 * @since 4.9.019 (2010-04-26)
11812 protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
11813 if (($rx <= 0) OR ($ry < 0)) {
11820 $xmin = 2147483647;
11821 $ymin = 2147483647;
11825 // center of the arc
11826 $this->_outPoint($xc, $yc);
11828 $xang = deg2rad((float) $xang);
11829 $angs = deg2rad((float) $angs);
11830 $angf = deg2rad((float) $angf);
11835 $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
11836 $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
11844 if ($ccw AND ($as > $af)) {
11845 // reverse rotation
11847 } elseif (!$ccw AND ($as < $af)) {
11848 // reverse rotation
11851 $total_angle = ($af - $as);
11855 // total arcs to draw
11856 $nc *= (2 * abs($total_angle) / M_PI
);
11857 $nc = round($nc) +
1;
11858 // angle of each arc
11859 $arcang = ($total_angle / $nc);
11860 // center point in PDF coordinates
11862 $y0 = ($this->h
- $yc);
11865 $alpha = sin($arcang) * ((sqrt(4 +
(3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
11866 $cos_xang = cos($xang);
11867 $sin_xang = sin($xang);
11868 $cos_ang = cos($ang);
11869 $sin_ang = sin($ang);
11871 $px1 = $x0 +
($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11872 $py1 = $y0 +
($rx * $sin_xang * $cos_ang) +
($ry * $cos_xang * $sin_ang);
11873 // first Bezier control point
11874 $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11875 $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) +
($ry * $cos_xang * $cos_ang)));
11877 // line from center to arc starting point
11878 $this->_outLine($px1, $this->h
- $py1);
11879 } elseif ($startpoint) {
11880 // arc starting point
11881 $this->_outPoint($px1, $this->h
- $py1);
11884 for ($i = 1; $i <= $nc; ++
$i) {
11886 $ang = $as +
($i * $arcang);
11890 $cos_ang = cos($ang);
11891 $sin_ang = sin($ang);
11892 // second arc point
11893 $px2 = $x0 +
($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11894 $py2 = $y0 +
($rx * $sin_xang * $cos_ang) +
($ry * $cos_xang * $sin_ang);
11895 // second Bezier control point
11896 $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11897 $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) +
($ry * $cos_xang * $cos_ang)));
11899 $cx1 = ($px1 +
$qx1);
11900 $cy1 = ($this->h
- ($py1 +
$qy1));
11901 $cx2 = ($px2 - $qx2);
11902 $cy2 = ($this->h
- ($py2 - $qy2));
11904 $cy3 = ($this->h
- $py2);
11905 $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
11906 // get bounding box coordinates
11907 $xmin = min($xmin, $cx1, $cx2, $cx3);
11908 $ymin = min($ymin, $cy1, $cy2, $cy3);
11909 $xmax = max($xmax, $cx1, $cx2, $cx3);
11910 $ymax = max($ymax, $cy1, $cy2, $cy3);
11911 // move to next point
11918 $this->_outLine($xc, $yc);
11919 // get bounding box coordinates
11920 $xmin = min($xmin, $xc);
11921 $ymin = min($ymin, $yc);
11922 $xmax = max($xmax, $xc);
11923 $ymax = max($ymax, $yc);
11925 return array($xmin, $ymin, $xmax, $ymax);
11930 * A circle is formed from n Bezier curves.
11931 * @param $x0 (float) Abscissa of center point.
11932 * @param $y0 (float) Ordinate of center point.
11933 * @param $r (float) Radius.
11934 * @param $angstr: (float) Angle start of draw line. Default value: 0.
11935 * @param $angend: (float) Angle finish of draw line. Default value: 360.
11936 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11937 * @param $line_style (array) Line style of circle. Array like for SetLineStyle(). Default value: default line style (empty array).
11938 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
11939 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of circle.
11941 * @since 2.1.000 (2008-01-08)
11943 public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11944 $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
11948 * Draws a polygonal line
11949 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
11950 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11951 * @param $line_style (array) Line style of polygon. Array with keys among the following:
11953 * <li>all: Line style of all lines. Array like for SetLineStyle().</li>
11954 * <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
11956 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
11957 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11958 * @since 4.8.003 (2009-09-15)
11961 public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
11962 $this->Polygon($p, $style, $line_style, $fill_color, false);
11967 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
11968 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11969 * @param $line_style (array) Line style of polygon. Array with keys among the following:
11971 * <li>all: Line style of all lines. Array like for SetLineStyle().</li>
11972 * <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
11974 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
11975 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11976 * @param $closed (boolean) if true the polygon is closes, otherwise will remain open
11978 * @since 2.1.000 (2008-01-08)
11980 public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
11981 if ($this->state
!= 2) {
11984 $nc = count($p); // number of coordinates
11985 $np = $nc / 2; // number of points
11987 // close polygon by adding the first 2 points at the end (one line)
11988 for ($i = 0; $i < 4; ++
$i) {
11989 $p[$nc +
$i] = $p[$i];
11991 // copy style for the last added line
11992 if (isset($line_style[0])) {
11993 $line_style[$np] = $line_style[0];
11997 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11998 $this->SetFillColorArray($fill_color);
12000 $op = TCPDF_STATIC
::getPathPaintOperator($style);
12002 $line_style = array();
12006 if (isset($line_style['all'])) {
12007 $this->SetLineStyle($line_style['all']);
12013 $this->_outPoint($p[0], $p[1]);
12014 for ($i = 2; $i < $nc; $i = $i +
2) {
12015 $this->_outLine($p[$i], $p[$i +
1]);
12020 $this->_outPoint($p[0], $p[1]);
12021 for ($i = 2; $i < $nc; $i = $i +
2) {
12022 $line_num = ($i / 2) - 1;
12023 if (isset($line_style[$line_num])) {
12024 if ($line_style[$line_num] != 0) {
12025 if (is_array($line_style[$line_num])) {
12027 $this->SetLineStyle($line_style[$line_num]);
12028 $this->_outPoint($p[$i - 2], $p[$i - 1]);
12029 $this->_outLine($p[$i], $p[$i +
1]);
12031 $this->_outPoint($p[$i], $p[$i +
1]);
12033 $this->_outLine($p[$i], $p[$i +
1]);
12037 $this->_outLine($p[$i], $p[$i +
1]);
12044 $this->_outPoint($p[0], $p[1]);
12045 for ($i = 2; $i < $nc; $i = $i +
2) {
12046 $this->_outLine($p[$i], $p[$i +
1]);
12053 * Draws a regular polygon.
12054 * @param $x0 (float) Abscissa of center point.
12055 * @param $y0 (float) Ordinate of center point.
12056 * @param $r: (float) Radius of inscribed circle.
12057 * @param $ns (integer) Number of sides.
12058 * @param $angle (float) Angle oriented (anti-clockwise). Default value: 0.
12059 * @param $draw_circle (boolean) Draw inscribed circle or not. Default value: false.
12060 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12061 * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
12063 * <li>all: Line style of all sides. Array like for SetLineStyle().</li>
12064 * <li>0 to ($ns - 1): Line style of each side. Array like for SetLineStyle().</li>
12066 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
12067 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
12068 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
12070 * <li>D or empty string: Draw (default).</li>
12071 * <li>F: Fill.</li>
12072 * <li>DF or FD: Draw and fill.</li>
12073 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
12074 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
12076 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
12077 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
12079 * @since 2.1.000 (2008-01-08)
12081 public function RegularPolygon($x0, $y0, $r, $ns, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
12085 if ($draw_circle) {
12086 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12089 for ($i = 0; $i < $ns; ++
$i) {
12090 $a = $angle +
($i * 360 / $ns);
12091 $a_rad = deg2rad((float) $a);
12092 $p[] = $x0 +
($r * sin($a_rad));
12093 $p[] = $y0 +
($r * cos($a_rad));
12095 $this->Polygon($p, $style, $line_style, $fill_color);
12099 * Draws a star polygon
12100 * @param $x0 (float) Abscissa of center point.
12101 * @param $y0 (float) Ordinate of center point.
12102 * @param $r (float) Radius of inscribed circle.
12103 * @param $nv (integer) Number of vertices.
12104 * @param $ng (integer) Number of gap (if ($ng % $nv = 1) then is a regular polygon).
12105 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
12106 * @param $draw_circle: (boolean) Draw inscribed circle or not. Default value is false.
12107 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12108 * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
12110 * <li>all: Line style of all sides. Array like for
12111 * SetLineStyle().</li>
12112 * <li>0 to (n - 1): Line style of each side. Array like for SetLineStyle().</li>
12114 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
12115 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
12116 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
12118 * <li>D or empty string: Draw (default).</li>
12119 * <li>F: Fill.</li>
12120 * <li>DF or FD: Draw and fill.</li>
12121 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
12122 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
12124 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
12125 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
12127 * @since 2.1.000 (2008-01-08)
12129 public function StarPolygon($x0, $y0, $r, $nv, $ng, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
12133 if ($draw_circle) {
12134 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12137 $visited = array();
12138 for ($i = 0; $i < $nv; ++
$i) {
12139 $a = $angle +
($i * 360 / $nv);
12140 $a_rad = deg2rad((float) $a);
12141 $p2[] = $x0 +
($r * sin($a_rad));
12142 $p2[] = $y0 +
($r * cos($a_rad));
12143 $visited[] = false;
12148 $p[] = $p2[$i * 2];
12149 $p[] = $p2[($i * 2) +
1];
12150 $visited[$i] = true;
12153 } while (!$visited[$i]);
12154 $this->Polygon($p, $style, $line_style, $fill_color);
12158 * Draws a rounded rectangle.
12159 * @param $x (float) Abscissa of upper-left corner.
12160 * @param $y (float) Ordinate of upper-left corner.
12161 * @param $w (float) Width.
12162 * @param $h (float) Height.
12163 * @param $r (float) the radius of the circle used to round off the corners of the rectangle.
12164 * @param $round_corner (string) Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top right, bottom right, bottom left and top left. Default value: all rounded corner ("1111").
12165 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12166 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12167 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
12169 * @since 2.1.000 (2008-01-08)
12171 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12172 $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
12176 * Draws a rounded rectangle.
12177 * @param $x (float) Abscissa of upper-left corner.
12178 * @param $y (float) Ordinate of upper-left corner.
12179 * @param $w (float) Width.
12180 * @param $h (float) Height.
12181 * @param $rx (float) the x-axis radius of the ellipse used to round off the corners of the rectangle.
12182 * @param $ry (float) the y-axis radius of the ellipse used to round off the corners of the rectangle.
12183 * @param $round_corner (string) Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top right, bottom right, bottom left and top left. Default value: all rounded corner ("1111").
12184 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12185 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12186 * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
12188 * @since 4.9.019 (2010-04-22)
12190 public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12191 if ($this->state
!= 2) {
12194 if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
12196 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
12200 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12201 $this->SetFillColorArray($fill_color);
12203 $op = TCPDF_STATIC
::getPathPaintOperator($style);
12205 $border_style = array();
12207 if ($border_style) {
12208 $this->SetLineStyle($border_style);
12210 $MyArc = 4 / 3 * (sqrt(2) - 1);
12211 $this->_outPoint($x +
$rx, $y);
12212 $xc = $x +
$w - $rx;
12214 $this->_outLine($xc, $y);
12215 if ($round_corner[0]) {
12216 $this->_outCurve($xc +
($rx * $MyArc), $yc - $ry, $xc +
$rx, $yc - ($ry * $MyArc), $xc +
$rx, $yc);
12218 $this->_outLine($x +
$w, $y);
12220 $xc = $x +
$w - $rx;
12221 $yc = $y +
$h - $ry;
12222 $this->_outLine($x +
$w, $yc);
12223 if ($round_corner[1]) {
12224 $this->_outCurve($xc +
$rx, $yc +
($ry * $MyArc), $xc +
($rx * $MyArc), $yc +
$ry, $xc, $yc +
$ry);
12226 $this->_outLine($x +
$w, $y +
$h);
12229 $yc = $y +
$h - $ry;
12230 $this->_outLine($xc, $y +
$h);
12231 if ($round_corner[2]) {
12232 $this->_outCurve($xc - ($rx * $MyArc), $yc +
$ry, $xc - $rx, $yc +
($ry * $MyArc), $xc - $rx, $yc);
12234 $this->_outLine($x, $y +
$h);
12238 $this->_outLine($x, $yc);
12239 if ($round_corner[3]) {
12240 $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
12242 $this->_outLine($x, $y);
12243 $this->_outLine($x +
$rx, $y);
12249 * Draws a grahic arrow.
12250 * @param $x0 (float) Abscissa of first point.
12251 * @param $y0 (float) Ordinate of first point.
12252 * @param $x1 (float) Abscissa of second point.
12253 * @param $y1 (float) Ordinate of second point.
12254 * @param $head_style (int) (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead)
12255 * @param $arm_size (float) length of arrowhead arms
12256 * @param $arm_angle (int) angle between an arm and the shaft
12257 * @author Piotr Galecki, Nicola Asuni, Andy Meier
12258 * @since 4.6.018 (2009-07-10)
12260 public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
12261 // getting arrow direction angle
12262 // 0 deg angle is when both arms go along X axis. angle grows clockwise.
12263 $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
12264 if ($dir_angle < 0) {
12265 $dir_angle +
= (2 * M_PI
);
12267 $arm_angle = deg2rad($arm_angle);
12270 if ($head_style > 0) {
12271 // calculate the stopping point for the arrow shaft
12272 $sx1 = $x1 +
(($arm_size - $this->LineWidth
) * cos($dir_angle));
12273 $sy1 = $y1 +
(($arm_size - $this->LineWidth
) * sin($dir_angle));
12275 // main arrow line / shaft
12276 $this->Line($x0, $y0, $sx1, $sy1);
12277 // left arrowhead arm tip
12278 $x2L = $x1 +
($arm_size * cos($dir_angle +
$arm_angle));
12279 $y2L = $y1 +
($arm_size * sin($dir_angle +
$arm_angle));
12280 // right arrowhead arm tip
12281 $x2R = $x1 +
($arm_size * cos($dir_angle - $arm_angle));
12282 $y2R = $y1 +
($arm_size * sin($dir_angle - $arm_angle));
12285 switch ($head_style) {
12287 // draw only arrowhead arms
12289 $style = array(1, 1, 0);
12293 // draw closed arrowhead, but no fill
12298 // closed and filled arrowhead
12303 // filled arrowhead
12308 $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
12311 // END GRAPHIC FUNCTIONS SECTION -----------------------
12314 * Add a Named Destination.
12315 * NOTE: destination names are unique, so only last entry will be saved.
12316 * @param $name (string) Destination name.
12317 * @param $y (float) Y position in user units of the destiantion on the selected page (default = -1 = current position; 0 = page start;).
12318 * @param $page (int|string) Target page number (leave empty for current page). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages.
12319 * @param $x (float) X position in user units of the destiantion on the selected page (default = -1 = current position;).
12320 * @return (string) Stripped named destination identifier or false in case of error.
12322 * @author Christian Deligant, Nicola Asuni
12323 * @since 5.9.097 (2011-06-23)
12325 public function setDestination($name, $y=-1, $page='', $x=-1) {
12326 // remove unsupported characters
12327 $name = TCPDF_STATIC
::encodeNameObject($name);
12328 if (TCPDF_STATIC
::empty_string($name)) {
12332 $y = $this->GetY();
12333 } elseif ($y < 0) {
12335 } elseif ($y > $this->h
) {
12339 $x = $this->GetX();
12340 } elseif ($x < 0) {
12342 } elseif ($x > $this->w
) {
12346 if (!empty($page) AND ($page[0] == '*')) {
12347 $page = intval(substr($page, 1));
12348 // this page number will not be changed when moving/add/deleting pages
12351 if (empty($page)) {
12352 $page = $this->PageNo();
12353 if (empty($page)) {
12357 $this->dests
[$name] = array('x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed);
12362 * Return the Named Destination array.
12363 * @return (array) Named Destination array.
12365 * @author Nicola Asuni
12366 * @since 5.9.097 (2011-06-23)
12368 public function getDestination() {
12369 return $this->dests
;
12373 * Insert Named Destinations.
12375 * @author Johannes G\FCntert, Nicola Asuni
12376 * @since 5.9.098 (2011-06-23)
12378 protected function _putdests() {
12379 if (empty($this->dests
)) {
12382 $this->n_dests
= $this->_newobj();
12384 foreach($this->dests
as $name => $o) {
12385 $out .= ' /'.$name.' '.sprintf('[%u 0 R /XYZ %F %F null]', $this->page_obj_id
[($o['p'])], ($o['x'] * $this->k
), ($this->pagedim
[$o['p']]['h'] - ($o['y'] * $this->k
)));
12388 $out .= "\n".'endobj';
12393 * Adds a bookmark - alias for Bookmark().
12394 * @param $txt (string) Bookmark description.
12395 * @param $level (int) Bookmark level (minimum value is 0).
12396 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
12397 * @param $page (int|string) Target page number (leave empty for current page). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages.
12398 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic.
12399 * @param $color (array) RGB color array (values from 0 to 255).
12400 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;).
12401 * @param $link (mixed) URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name).
12404 public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12405 $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link);
12410 * @param $txt (string) Bookmark description.
12411 * @param $level (int) Bookmark level (minimum value is 0).
12412 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
12413 * @param $page (int|string) Target page number (leave empty for current page). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages.
12414 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic.
12415 * @param $color (array) RGB color array (values from 0 to 255).
12416 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;).
12417 * @param $link (mixed) URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name).
12419 * @since 2.1.002 (2008-02-12)
12421 public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12425 if (isset($this->outlines
[0])) {
12426 $lastoutline = end($this->outlines
);
12427 $maxlevel = $lastoutline['l'] +
1;
12431 if ($level > $maxlevel) {
12432 $level = $maxlevel;
12435 $y = $this->GetY();
12436 } elseif ($y < 0) {
12438 } elseif ($y > $this->h
) {
12442 $x = $this->GetX();
12443 } elseif ($x < 0) {
12445 } elseif ($x > $this->w
) {
12449 if (!empty($page) AND ($page[0] == '*')) {
12450 $page = intval(substr($page, 1));
12451 // this page number will not be changed when moving/add/deleting pages
12454 if (empty($page)) {
12455 $page = $this->PageNo();
12456 if (empty($page)) {
12460 $this->outlines
[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed, 's' => strtoupper($style), 'c' => $color, 'u' => $link);
12464 * Sort bookmarks for page and key.
12466 * @since 5.9.119 (2011-09-19)
12468 protected function sortBookmarks() {
12469 // get sorting columns
12470 $outline_p = array();
12471 $outline_y = array();
12472 foreach ($this->outlines
as $key => $row) {
12473 $outline_p[$key] = $row['p'];
12474 $outline_k[$key] = $key;
12476 // sort outlines by page and original position
12477 array_multisort($outline_p, SORT_NUMERIC
, SORT_ASC
, $outline_k, SORT_NUMERIC
, SORT_ASC
, $this->outlines
);
12481 * Create a bookmark PDF string.
12483 * @author Olivier Plathey, Nicola Asuni
12484 * @since 2.1.002 (2008-02-12)
12486 protected function _putbookmarks() {
12487 $nb = count($this->outlines
);
12492 $this->sortBookmarks();
12495 foreach ($this->outlines
as $i => $o) {
12497 $parent = $lru[($o['l'] - 1)];
12498 //Set parent and last pointers
12499 $this->outlines
[$i]['parent'] = $parent;
12500 $this->outlines
[$parent]['last'] = $i;
12501 if ($o['l'] > $level) {
12502 //Level increasing: set first pointer
12503 $this->outlines
[$parent]['first'] = $i;
12506 $this->outlines
[$i]['parent'] = $nb;
12508 if (($o['l'] <= $level) AND ($i > 0)) {
12509 //Set prev and next pointers
12510 $prev = $lru[$o['l']];
12511 $this->outlines
[$prev]['next'] = $i;
12512 $this->outlines
[$i]['prev'] = $prev;
12514 $lru[$o['l']] = $i;
12519 $nltags = '/<br[\s]?\/>|<\/(blockquote|dd|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|p|pre|ul|tcpdf|table|tr|td)>/si';
12520 foreach ($this->outlines
as $i => $o) {
12521 $oid = $this->_newobj();
12522 // covert HTML title to string
12523 $title = preg_replace($nltags, "\n", $o['t']);
12524 $title = preg_replace("/[\r]+/si", '', $title);
12525 $title = preg_replace("/[\n]+/si", "\n", $title);
12526 $title = strip_tags($title);
12527 $title = $this->stringTrim($title);
12528 $out = '<</Title '.$this->_textstring($title, $oid);
12529 $out .= ' /Parent '.($n +
$o['parent']).' 0 R';
12530 if (isset($o['prev'])) {
12531 $out .= ' /Prev '.($n +
$o['prev']).' 0 R';
12533 if (isset($o['next'])) {
12534 $out .= ' /Next '.($n +
$o['next']).' 0 R';
12536 if (isset($o['first'])) {
12537 $out .= ' /First '.($n +
$o['first']).' 0 R';
12539 if (isset($o['last'])) {
12540 $out .= ' /Last '.($n +
$o['last']).' 0 R';
12542 if (isset($o['u']) AND !empty($o['u'])) {
12544 if (is_string($o['u'])) {
12545 if ($o['u'][0] == '#') {
12546 // internal destination
12547 $out .= ' /Dest /'.TCPDF_STATIC
::encodeNameObject(substr($o['u'], 1));
12548 } elseif ($o['u'][0] == '%') {
12549 // embedded PDF file
12550 $filename = basename(substr($o['u'], 1));
12551 $out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles
[$filename]['a'].' >> >>';
12552 } elseif ($o['u'][0] == '*') {
12553 // embedded generic file
12554 $filename = basename(substr($o['u'], 1));
12555 $jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});';
12556 $out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>';
12558 // external URI link
12559 $out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>';
12561 } elseif (isset($this->links
[$o['u']])) {
12562 // internal link ID
12563 $l = $this->links
[$o['u']];
12564 if (isset($this->page_obj_id
[($l['p'])])) {
12565 $out .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id
[($l['p'])], ($this->pagedim
[$l['p']]['h'] - ($l['y'] * $this->k
)));
12568 } elseif (isset($this->page_obj_id
[($o['p'])])) {
12570 $out .= ' '.sprintf('/Dest [%u 0 R /XYZ %F %F null]', $this->page_obj_id
[($o['p'])], ($o['x'] * $this->k
), ($this->pagedim
[$o['p']]['h'] - ($o['y'] * $this->k
)));
12574 if (!empty($o['s'])) {
12576 if (strpos($o['s'], 'B') !== false) {
12580 if (strpos($o['s'], 'I') !== false) {
12584 $out .= sprintf(' /F %d', $style);
12585 // set bookmark color
12586 if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
12587 $color = array_values($o['c']);
12588 $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
12591 $out .= ' /C [0.0 0.0 0.0]';
12593 $out .= ' /Count 0'; // normally closed item
12595 $out .= "\n".'endobj';
12599 $this->OutlineRoot
= $this->_newobj();
12600 $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n +
$lru[0]).' 0 R >>'."\n".'endobj');
12603 // --- JAVASCRIPT ------------------------------------------------------
12606 * Adds a javascript
12607 * @param $script (string) Javascript code
12609 * @author Johannes G\FCntert, Nicola Asuni
12610 * @since 2.1.002 (2008-02-12)
12612 public function IncludeJS($script) {
12613 $this->javascript
.= $script;
12617 * Adds a javascript object and return object ID
12618 * @param $script (string) Javascript code
12619 * @param $onload (boolean) if true executes this object when opening the document
12620 * @return int internal object ID
12622 * @author Nicola Asuni
12623 * @since 4.8.000 (2009-09-07)
12625 public function addJavascriptObject($script, $onload=false) {
12626 if ($this->pdfa_mode
) {
12627 // javascript is not allowed in PDF/A mode
12631 $this->js_objects
[$this->n
] = array('n' => $this->n
, 'js' => $script, 'onload' => $onload);
12636 * Create a javascript PDF string.
12638 * @author Johannes G\FCntert, Nicola Asuni
12639 * @since 2.1.002 (2008-02-12)
12641 protected function _putjavascript() {
12642 if ($this->pdfa_mode
OR (empty($this->javascript
) AND empty($this->js_objects
))) {
12645 if (strpos($this->javascript
, 'this.addField') > 0) {
12646 if (!$this->ur
['enabled']) {
12647 //$this->setUserRights();
12649 // the following two lines are used to avoid form fields duplication after saving
12650 // The addField method only works when releasing user rights (UR3)
12651 $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
12652 $jsb = "getField('tcpdfdocsaved').value='saved';";
12653 $this->javascript
= $jsa."\n".$this->javascript
."\n".$jsb;
12655 // name tree for javascript
12656 $this->n_js
= '<< /Names [';
12657 if (!empty($this->javascript
)) {
12658 $this->n_js
.= ' (EmbeddedJS) '.($this->n +
1).' 0 R';
12660 if (!empty($this->js_objects
)) {
12661 foreach ($this->js_objects
as $key => $val) {
12662 if ($val['onload']) {
12663 $this->n_js
.= ' (JS'.$key.') '.$key.' 0 R';
12667 $this->n_js
.= ' ] >>';
12668 // default Javascript object
12669 if (!empty($this->javascript
)) {
12670 $obj_id = $this->_newobj();
12671 $out = '<< /S /JavaScript';
12672 $out .= ' /JS '.$this->_textstring($this->javascript
, $obj_id);
12674 $out .= "\n".'endobj';
12677 // additional Javascript objects
12678 if (!empty($this->js_objects
)) {
12679 foreach ($this->js_objects
as $key => $val) {
12680 $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
12687 * Adds a javascript form field.
12688 * @param $type (string) field type
12689 * @param $name (string) field name
12690 * @param $x (int) horizontal position
12691 * @param $y (int) vertical position
12692 * @param $w (int) width
12693 * @param $h (int) height
12694 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12696 * @author Denis Van Nuffelen, Nicola Asuni
12697 * @since 2.1.002 (2008-02-12)
12699 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
12703 // the followind avoid fields duplication after saving the document
12704 $this->javascript
.= "if (getField('tcpdfdocsaved').value != 'saved') {";
12706 $this->javascript
.= sprintf("f".$name."=this.addField('%s','%s',%u,[%F,%F,%F,%F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h
-$y)*$k+
1, ($x+
$w)*$k, ($this->h
-$y-$h)*$k+
1)."\n";
12707 $this->javascript
.= 'f'.$name.'.textSize='.$this->FontSizePt
.";\n";
12708 while (list($key, $val) = each($prop)) {
12709 if (strcmp(substr($key, -5), 'Color') == 0) {
12710 $val = TCPDF_COLORS
::_JScolor($val);
12712 $val = "'".$val."'";
12714 $this->javascript
.= 'f'.$name.'.'.$key.'='.$val.";\n";
12721 $this->javascript
.= '}';
12724 // --- FORM FIELDS -----------------------------------------------------
12729 * Set default properties for form fields.
12730 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12732 * @author Nicola Asuni
12733 * @since 4.8.000 (2009-09-06)
12735 public function setFormDefaultProp($prop=array()) {
12736 $this->default_form_prop
= $prop;
12740 * Return the default properties for form fields.
12741 * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12743 * @author Nicola Asuni
12744 * @since 4.8.000 (2009-09-06)
12746 public function getFormDefaultProp() {
12747 return $this->default_form_prop
;
12751 * Creates a text field
12752 * @param $name (string) field name
12753 * @param $w (float) Width of the rectangle
12754 * @param $h (float) Height of the rectangle
12755 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12756 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
12757 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12758 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12759 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12761 * @author Nicola Asuni
12762 * @since 4.8.000 (2009-09-07)
12764 public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12771 // check page for no-write regions and adapt page margins if necessary
12772 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12774 $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
12777 // get default style
12778 $prop = array_merge($this->getFormDefaultProp(), $prop);
12779 // get annotation data
12780 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
12781 // set default appearance stream
12782 $this->annotation_fonts
[$this->CurrentFont
['fontkey']] = $this->CurrentFont
['i'];
12783 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont
['i'], $this->FontSizePt
, $this->TextColor
);
12784 $popt['da'] = $fontstyle;
12785 // build appearance stream
12786 $popt['ap'] = array();
12787 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12789 if (isset($prop['value']) AND !empty($prop['value'])) {
12790 $text = $prop['value'];
12791 } elseif (isset($opt['v']) AND !empty($opt['v'])) {
12794 $tmpid = $this->startTemplate($w, $h, false);
12796 if (isset($popt['q'])) {
12797 switch ($popt['q']) {
12816 $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12817 $this->endTemplate();
12819 $popt['ap']['n'] .= $this->xobjects
[$tmpid]['outdata'];
12820 unset($this->xobjects
[$tmpid]);
12821 $popt['ap']['n'] .= 'Q EMC';
12823 $opt = array_merge($popt, $opt);
12824 // remove some conflicting options
12826 // set remaining annotation data
12827 $opt['Subtype'] = 'Widget';
12830 // Additional annotation's parameters (check _putannotsobj() method):
12840 //$opt['mk']['bc'];
12841 //$opt['mk']['bg'];
12842 unset($opt['mk']['ca']);
12843 unset($opt['mk']['rc']);
12844 unset($opt['mk']['ac']);
12845 unset($opt['mk']['i']);
12846 unset($opt['mk']['ri']);
12847 unset($opt['mk']['ix']);
12848 unset($opt['mk']['if']);
12849 //$opt['mk']['if']['sw'];
12850 //$opt['mk']['if']['s'];
12851 //$opt['mk']['if']['a'];
12852 //$opt['mk']['if']['fb'];
12853 unset($opt['mk']['tp']);
12862 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12871 * Creates a RadioButton field.
12872 * @param $name (string) Field name.
12873 * @param $w (int) Width of the radio button.
12874 * @param $prop (array) Javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12875 * @param $opt (array) Annotation parameters. Possible values are described on official PDF32000_2008 reference.
12876 * @param $onvalue (string) Value to be returned if selected.
12877 * @param $checked (boolean) Define the initial state.
12878 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12879 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12880 * @param $js (boolean) If true put the field using JavaScript (requires Acrobat Writer to be rendered).
12882 * @author Nicola Asuni
12883 * @since 4.8.000 (2009-09-07)
12885 public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
12892 // check page for no-write regions and adapt page margins if necessary
12893 list($x, $y) = $this->checkPageRegions($w, $x, $y);
12895 $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
12898 if (TCPDF_STATIC
::empty_string($onvalue)) {
12902 $defval = $onvalue;
12907 $font = 'zapfdingbats';
12908 if ($this->pdfa_mode
) {
12909 // all fonts must be embedded
12910 $font = 'pdfa'.$font;
12912 $this->AddFont($font);
12913 $tmpfont = $this->getFontBuffer($font);
12914 // set data for parent group
12915 if (!isset($this->radiobutton_groups
[$this->page
])) {
12916 $this->radiobutton_groups
[$this->page
] = array();
12918 if (!isset($this->radiobutton_groups
[$this->page
][$name])) {
12919 $this->radiobutton_groups
[$this->page
][$name] = array();
12921 $this->radiobutton_groups
[$this->page
][$name]['n'] = $this->n
;
12922 $this->radio_groups
[] = $this->n
;
12924 $kid = ($this->n +
1);
12925 // save object ID to be added on Kids entry on parent object
12926 $this->radiobutton_groups
[$this->page
][$name][] = array('kid' => $kid, 'def' => $defval);
12927 // get default style
12928 $prop = array_merge($this->getFormDefaultProp(), $prop);
12929 $prop['NoToggleToOff'] = 'true';
12930 $prop['Radio'] = 'true';
12931 $prop['borderStyle'] = 'inset';
12932 // get annotation data
12933 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
12934 // set additional default options
12935 $this->annotation_fonts
[$tmpfont['fontkey']] = $tmpfont['i'];
12936 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt
, $this->TextColor
);
12937 $popt['da'] = $fontstyle;
12938 // build appearance stream
12939 $popt['ap'] = array();
12940 $popt['ap']['n'] = array();
12941 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k
);
12942 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt
/ 1000) / $this->k
)) * $this->k
);
12943 $popt['ap']['n'][$onvalue] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(108).') Tj ET Q', $this->TextColor
, $tmpfont['i'], $this->FontSizePt
, $fx, $fy);
12944 $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(109).') Tj ET Q', $this->TextColor
, $tmpfont['i'], $this->FontSizePt
, $fx, $fy);
12945 if (!isset($popt['mk'])) {
12946 $popt['mk'] = array();
12948 $popt['mk']['ca'] = '(l)';
12950 $opt = array_merge($popt, $opt);
12951 // set remaining annotation data
12952 $opt['Subtype'] = 'Widget';
12953 $opt['ft'] = 'Btn';
12955 $opt['v'] = array('/'.$onvalue);
12956 $opt['as'] = $onvalue;
12958 $opt['as'] = 'Off';
12960 // store readonly flag
12961 if (!isset($this->radiobutton_groups
[$this->page
][$name]['#readonly#'])) {
12962 $this->radiobutton_groups
[$this->page
][$name]['#readonly#'] = false;
12964 $this->radiobutton_groups
[$this->page
][$name]['#readonly#'] |
= ($opt['f'] & 64);
12965 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
12974 * Creates a List-box field
12975 * @param $name (string) field name
12976 * @param $w (int) width
12977 * @param $h (int) height
12978 * @param $values (array) array containing the list of values.
12979 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12980 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
12981 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12982 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12983 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12985 * @author Nicola Asuni
12986 * @since 4.8.000 (2009-09-07)
12988 public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12995 // check page for no-write regions and adapt page margins if necessary
12996 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12998 $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
13000 foreach ($values as $value) {
13001 if (is_array($value)) {
13002 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
13004 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
13007 $this->javascript
.= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
13010 // get default style
13011 $prop = array_merge($this->getFormDefaultProp(), $prop);
13012 // get annotation data
13013 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
13014 // set additional default values
13015 $this->annotation_fonts
[$this->CurrentFont
['fontkey']] = $this->CurrentFont
['i'];
13016 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont
['i'], $this->FontSizePt
, $this->TextColor
);
13017 $popt['da'] = $fontstyle;
13018 // build appearance stream
13019 $popt['ap'] = array();
13020 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13022 foreach($values as $item) {
13023 if (is_array($item)) {
13024 $text .= $item[1]."\n";
13026 $text .= $item."\n";
13029 $tmpid = $this->startTemplate($w, $h, false);
13030 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13031 $this->endTemplate();
13033 $popt['ap']['n'] .= $this->xobjects
[$tmpid]['outdata'];
13034 unset($this->xobjects
[$tmpid]);
13035 $popt['ap']['n'] .= 'Q EMC';
13037 $opt = array_merge($popt, $opt);
13038 // set remaining annotation data
13039 $opt['Subtype'] = 'Widget';
13042 $opt['opt'] = $values;
13043 unset($opt['mk']['ca']);
13044 unset($opt['mk']['rc']);
13045 unset($opt['mk']['ac']);
13046 unset($opt['mk']['i']);
13047 unset($opt['mk']['ri']);
13048 unset($opt['mk']['ix']);
13049 unset($opt['mk']['if']);
13050 unset($opt['mk']['tp']);
13051 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13060 * Creates a Combo-box field
13061 * @param $name (string) field name
13062 * @param $w (int) width
13063 * @param $h (int) height
13064 * @param $values (array) array containing the list of values.
13065 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13066 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13067 * @param $x (float) Abscissa of the upper-left corner of the rectangle
13068 * @param $y (float) Ordinate of the upper-left corner of the rectangle
13069 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13071 * @author Nicola Asuni
13072 * @since 4.8.000 (2009-09-07)
13074 public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13081 // check page for no-write regions and adapt page margins if necessary
13082 list($x, $y) = $this->checkPageRegions($h, $x, $y);
13084 $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
13086 foreach ($values as $value) {
13087 if (is_array($value)) {
13088 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
13090 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
13093 $this->javascript
.= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
13096 // get default style
13097 $prop = array_merge($this->getFormDefaultProp(), $prop);
13098 $prop['Combo'] = true;
13099 // get annotation data
13100 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
13101 // set additional default options
13102 $this->annotation_fonts
[$this->CurrentFont
['fontkey']] = $this->CurrentFont
['i'];
13103 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont
['i'], $this->FontSizePt
, $this->TextColor
);
13104 $popt['da'] = $fontstyle;
13105 // build appearance stream
13106 $popt['ap'] = array();
13107 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13109 foreach($values as $item) {
13110 if (is_array($item)) {
13111 $text .= $item[1]."\n";
13113 $text .= $item."\n";
13116 $tmpid = $this->startTemplate($w, $h, false);
13117 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13118 $this->endTemplate();
13120 $popt['ap']['n'] .= $this->xobjects
[$tmpid]['outdata'];
13121 unset($this->xobjects
[$tmpid]);
13122 $popt['ap']['n'] .= 'Q EMC';
13124 $opt = array_merge($popt, $opt);
13125 // set remaining annotation data
13126 $opt['Subtype'] = 'Widget';
13129 $opt['opt'] = $values;
13130 unset($opt['mk']['ca']);
13131 unset($opt['mk']['rc']);
13132 unset($opt['mk']['ac']);
13133 unset($opt['mk']['i']);
13134 unset($opt['mk']['ri']);
13135 unset($opt['mk']['ix']);
13136 unset($opt['mk']['if']);
13137 unset($opt['mk']['tp']);
13138 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13147 * Creates a CheckBox field
13148 * @param $name (string) field name
13149 * @param $w (int) width
13150 * @param $checked (boolean) define the initial state.
13151 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13152 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13153 * @param $onvalue (string) value to be returned if selected.
13154 * @param $x (float) Abscissa of the upper-left corner of the rectangle
13155 * @param $y (float) Ordinate of the upper-left corner of the rectangle
13156 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13158 * @author Nicola Asuni
13159 * @since 4.8.000 (2009-09-07)
13161 public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
13168 // check page for no-write regions and adapt page margins if necessary
13169 list($x, $y) = $this->checkPageRegions($w, $x, $y);
13171 $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
13174 if (!isset($prop['value'])) {
13175 $prop['value'] = array('Yes');
13177 // get default style
13178 $prop = array_merge($this->getFormDefaultProp(), $prop);
13179 $prop['borderStyle'] = 'inset';
13180 // get annotation data
13181 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
13182 // set additional default options
13183 $font = 'zapfdingbats';
13184 if ($this->pdfa_mode
) {
13185 // all fonts must be embedded
13186 $font = 'pdfa'.$font;
13188 $this->AddFont($font);
13189 $tmpfont = $this->getFontBuffer($font);
13190 $this->annotation_fonts
[$tmpfont['fontkey']] = $tmpfont['i'];
13191 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt
, $this->TextColor
);
13192 $popt['da'] = $fontstyle;
13193 // build appearance stream
13194 $popt['ap'] = array();
13195 $popt['ap']['n'] = array();
13196 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k
);
13197 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt
/ 1000) / $this->k
)) * $this->k
);
13198 $popt['ap']['n']['Yes'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(110).') Tj ET Q', $this->TextColor
, $tmpfont['i'], $this->FontSizePt
, $fx, $fy);
13199 $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(111).') Tj ET Q', $this->TextColor
, $tmpfont['i'], $this->FontSizePt
, $fx, $fy);
13201 $opt = array_merge($popt, $opt);
13202 // set remaining annotation data
13203 $opt['Subtype'] = 'Widget';
13204 $opt['ft'] = 'Btn';
13206 if (TCPDF_STATIC
::empty_string($onvalue)) {
13209 $opt['opt'] = array($onvalue);
13211 $opt['v'] = array('/Yes');
13212 $opt['as'] = 'Yes';
13214 $opt['v'] = array('/Off');
13215 $opt['as'] = 'Off';
13217 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
13226 * Creates a button field
13227 * @param $name (string) field name
13228 * @param $w (int) width
13229 * @param $h (int) height
13230 * @param $caption (string) caption.
13231 * @param $action (mixed) action triggered by pressing the button. Use a string to specify a javascript action. Use an array to specify a form action options as on section 12.7.5 of PDF32000_2008.
13232 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13233 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13234 * @param $x (float) Abscissa of the upper-left corner of the rectangle
13235 * @param $y (float) Ordinate of the upper-left corner of the rectangle
13236 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13238 * @author Nicola Asuni
13239 * @since 4.8.000 (2009-09-07)
13241 public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13248 // check page for no-write regions and adapt page margins if necessary
13249 list($x, $y) = $this->checkPageRegions($h, $x, $y);
13251 $this->_addfield('button', $name, $this->x
, $this->y
, $w, $h, $prop);
13252 $this->javascript
.= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
13253 $this->javascript
.= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
13254 $this->javascript
.= 'f'.$name.".highlight='push';\n";
13255 $this->javascript
.= 'f'.$name.".print=false;\n";
13258 // get default style
13259 $prop = array_merge($this->getFormDefaultProp(), $prop);
13260 $prop['Pushbutton'] = 'true';
13261 $prop['highlight'] = 'push';
13262 $prop['display'] = 'display.noPrint';
13263 // get annotation data
13264 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
13265 $this->annotation_fonts
[$this->CurrentFont
['fontkey']] = $this->CurrentFont
['i'];
13266 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont
['i'], $this->FontSizePt
, $this->TextColor
);
13267 $popt['da'] = $fontstyle;
13268 // build appearance stream
13269 $popt['ap'] = array();
13270 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13271 $tmpid = $this->startTemplate($w, $h, false);
13272 $bw = (2 / $this->k
); // border width
13274 'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13275 'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
13276 'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13277 'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
13278 $this->SetFillColor(204);
13279 $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
13280 $this->endTemplate();
13282 $popt['ap']['n'] .= $this->xobjects
[$tmpid]['outdata'];
13283 unset($this->xobjects
[$tmpid]);
13284 $popt['ap']['n'] .= 'Q EMC';
13285 // set additional default options
13286 if (!isset($popt['mk'])) {
13287 $popt['mk'] = array();
13289 $ann_obj_id = ($this->n +
1);
13290 if (!empty($action) AND !is_array($action)) {
13291 $ann_obj_id = ($this->n +
2);
13293 $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
13294 $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
13295 $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
13297 $opt = array_merge($popt, $opt);
13298 // set remaining annotation data
13299 $opt['Subtype'] = 'Widget';
13300 $opt['ft'] = 'Btn';
13301 $opt['t'] = $caption;
13303 if (!empty($action)) {
13304 if (is_array($action)) {
13305 // form action options as on section 12.7.5 of PDF32000_2008.
13306 $opt['aa'] = '/D <<';
13307 $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
13308 foreach ($action AS $key => $val) {
13309 if (($key == 'S') AND in_array($val, $bmode)) {
13310 $opt['aa'] .= ' /S /'.$val;
13311 } elseif (($key == 'F') AND (!empty($val))) {
13312 $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
13313 } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
13314 $opt['aa'] .= ' /Fields [';
13315 foreach ($val AS $field) {
13316 $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
13319 } elseif (($key == 'Flags')) {
13321 if (is_array($val)) {
13322 foreach ($val AS $flag) {
13324 case 'Include/Exclude': {
13328 case 'IncludeNoValueFields': {
13332 case 'ExportFormat': {
13336 case 'GetMethod': {
13340 case 'SubmitCoordinates': {
13348 case 'IncludeAppendSaves': {
13352 case 'IncludeAnnotations': {
13356 case 'SubmitPDF': {
13360 case 'CanonicalFormat': {
13364 case 'ExclNonUserAnnots': {
13372 case 'EmbedForm': {
13379 $ff = intval($val);
13381 $opt['aa'] .= ' /Flags '.$ff;
13384 $opt['aa'] .= ' >>';
13386 // Javascript action or raw action command
13387 $js_obj_id = $this->addJavascriptObject($action);
13388 $opt['aa'] = '/D '.$js_obj_id.' 0 R';
13391 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13399 // --- END FORMS FIELDS ------------------------------------------------
13402 * Add certification signature (DocMDP or UR3)
13403 * You can set only one signature type
13405 * @author Nicola Asuni
13406 * @since 4.6.008 (2009-05-07)
13408 protected function _putsignature() {
13409 if ((!$this->sign
) OR (!isset($this->signature_data
['cert_type']))) {
13412 $sigobjid = ($this->sig_obj_id +
1);
13413 $out = $this->_getobj($sigobjid)."\n";
13414 $out .= '<< /Type /Sig';
13415 $out .= ' /Filter /Adobe.PPKLite';
13416 $out .= ' /SubFilter /adbe.pkcs7.detached';
13417 $out .= ' '.TCPDF_STATIC
::$byterange_string;
13418 $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length
).'>';
13419 if (empty($this->signature_data
['approval']) OR ($this->signature_data
['approval'] != 'A')) {
13420 $out .= ' /Reference ['; // array of signature reference dictionaries
13421 $out .= ' << /Type /SigRef';
13422 if ($this->signature_data
['cert_type'] > 0) {
13423 $out .= ' /TransformMethod /DocMDP';
13424 $out .= ' /TransformParams <<';
13425 $out .= ' /Type /TransformParams';
13426 $out .= ' /P '.$this->signature_data
['cert_type'];
13427 $out .= ' /V /1.2';
13429 $out .= ' /TransformMethod /UR3';
13430 $out .= ' /TransformParams <<';
13431 $out .= ' /Type /TransformParams';
13432 $out .= ' /V /2.2';
13433 if (!TCPDF_STATIC
::empty_string($this->ur
['document'])) {
13434 $out .= ' /Document['.$this->ur
['document'].']';
13436 if (!TCPDF_STATIC
::empty_string($this->ur
['form'])) {
13437 $out .= ' /Form['.$this->ur
['form'].']';
13439 if (!TCPDF_STATIC
::empty_string($this->ur
['signature'])) {
13440 $out .= ' /Signature['.$this->ur
['signature'].']';
13442 if (!TCPDF_STATIC
::empty_string($this->ur
['annots'])) {
13443 $out .= ' /Annots['.$this->ur
['annots'].']';
13445 if (!TCPDF_STATIC
::empty_string($this->ur
['ef'])) {
13446 $out .= ' /EF['.$this->ur
['ef'].']';
13448 if (!TCPDF_STATIC
::empty_string($this->ur
['formex'])) {
13449 $out .= ' /FormEX['.$this->ur
['formex'].']';
13452 $out .= ' >>'; // close TransformParams
13453 // optional digest data (values must be calculated and replaced later)
13454 //$out .= ' /Data ********** 0 R';
13455 //$out .= ' /DigestMethod/MD5';
13456 //$out .= ' /DigestLocation[********** 34]';
13457 //$out .= ' /DigestValue<********************************>';
13459 $out .= ' ]'; // end of reference
13461 if (isset($this->signature_data
['info']['Name']) AND !TCPDF_STATIC
::empty_string($this->signature_data
['info']['Name'])) {
13462 $out .= ' /Name '.$this->_textstring($this->signature_data
['info']['Name'], $sigobjid);
13464 if (isset($this->signature_data
['info']['Location']) AND !TCPDF_STATIC
::empty_string($this->signature_data
['info']['Location'])) {
13465 $out .= ' /Location '.$this->_textstring($this->signature_data
['info']['Location'], $sigobjid);
13467 if (isset($this->signature_data
['info']['Reason']) AND !TCPDF_STATIC
::empty_string($this->signature_data
['info']['Reason'])) {
13468 $out .= ' /Reason '.$this->_textstring($this->signature_data
['info']['Reason'], $sigobjid);
13470 if (isset($this->signature_data
['info']['ContactInfo']) AND !TCPDF_STATIC
::empty_string($this->signature_data
['info']['ContactInfo'])) {
13471 $out .= ' /ContactInfo '.$this->_textstring($this->signature_data
['info']['ContactInfo'], $sigobjid);
13473 $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp
);
13475 $out .= "\n".'endobj';
13480 * Set User's Rights for PDF Reader
13481 * WARNING: This is experimental and currently do not work.
13482 * Check the PDF Reference 8.7.1 Transform Methods,
13483 * Table 8.105 Entries in the UR transform parameters dictionary
13484 * @param $enable (boolean) if true enable user's rights on PDF reader
13485 * @param $document (string) Names specifying additional document-wide usage rights for the document. The only defined value is "/FullSave", which permits a user to save the document along with modified form and/or annotation data.
13486 * @param $annots (string) Names specifying additional annotation-related usage rights for the document. Valid names in PDF 1.5 and later are /Create/Delete/Modify/Copy/Import/Export, which permit the user to perform the named operation on annotations.
13487 * @param $form (string) Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
13488 * @param $signature (string) Names specifying additional signature-related usage rights for the document. The only defined value is /Modify, which permits a user to apply a digital signature to an existing signature form field or clear a signed signature form field.
13489 * @param $ef (string) Names specifying additional usage rights for named embedded files in the document. Valid names are /Create/Delete/Modify/Import, which permit the user to perform the named operation on named embedded files
13490 Names specifying additional embedded-files-related usage rights for the document.
13491 * @param $formex (string) Names specifying additional form-field-related usage rights. The only valid name is BarcodePlaintext, which permits text form field data to be encoded as a plaintext two-dimensional barcode.
13493 * @author Nicola Asuni
13494 * @since 2.9.000 (2008-03-26)
13496 public function setUserRights(
13498 $document='/FullSave',
13499 $annots='/Create/Delete/Modify/Copy/Import/Export',
13500 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
13501 $signature='/Modify',
13502 $ef='/Create/Delete/Modify/Import',
13504 $this->ur
['enabled'] = $enable;
13505 $this->ur
['document'] = $document;
13506 $this->ur
['annots'] = $annots;
13507 $this->ur
['form'] = $form;
13508 $this->ur
['signature'] = $signature;
13509 $this->ur
['ef'] = $ef;
13510 $this->ur
['formex'] = $formex;
13511 if (!$this->sign
) {
13512 $this->setSignature('', '', '', '', 0, array());
13517 * Enable document signature (requires the OpenSSL Library).
13518 * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
13519 * To create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13520 * To export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13521 * To convert pfx certificate to pem: openssl pkcs12 -in tcpdf.pfx -out tcpdf.crt -nodes
13522 * @param $signing_cert (mixed) signing certificate (string or filename prefixed with 'file://')
13523 * @param $private_key (mixed) private key (string or filename prefixed with 'file://')
13524 * @param $private_key_password (string) password
13525 * @param $extracerts (string) specifies the name of a file containing a bunch of extra certificates to include in the signature which can for example be used to help the recipient to verify the certificate that you used.
13526 * @param $cert_type (int) The access permissions granted for this document. Valid values shall be: 1 = No changes to the document shall be permitted; any change to the document shall invalidate the signature; 2 = Permitted changes shall be filling in forms, instantiating page templates, and signing; other changes shall invalidate the signature; 3 = Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature.
13527 * @param $info (array) array of option information: Name, Location, Reason, ContactInfo.
13528 * @param $approval (string) Enable approval signature eg. for PDF incremental update
13530 * @author Nicola Asuni
13531 * @since 4.6.005 (2009-04-24)
13533 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='') {
13534 // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13535 // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13536 // to convert pfx certificate to pem: openssl
13537 // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
13538 $this->sign
= true;
13540 $this->sig_obj_id
= $this->n
; // signature widget
13541 ++
$this->n
; // signature object ($this->sig_obj_id + 1)
13542 $this->signature_data
= array();
13543 if (strlen($signing_cert) == 0) {
13544 $this->Error('Please provide a certificate file and password!');
13546 if (strlen($private_key) == 0) {
13547 $private_key = $signing_cert;
13549 $this->signature_data
['signcert'] = $signing_cert;
13550 $this->signature_data
['privkey'] = $private_key;
13551 $this->signature_data
['password'] = $private_key_password;
13552 $this->signature_data
['extracerts'] = $extracerts;
13553 $this->signature_data
['cert_type'] = $cert_type;
13554 $this->signature_data
['info'] = $info;
13555 $this->signature_data
['approval'] = $approval;
13559 * Set the digital signature appearance (a cliccable rectangle area to get signature properties)
13560 * @param $x (float) Abscissa of the upper-left corner.
13561 * @param $y (float) Ordinate of the upper-left corner.
13562 * @param $w (float) Width of the signature area.
13563 * @param $h (float) Height of the signature area.
13564 * @param $page (int) option page number (if < 0 the current page is used).
13565 * @param $name (string) Name of the signature.
13567 * @author Nicola Asuni
13568 * @since 5.3.011 (2010-06-17)
13570 public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13571 $this->signature_appearance
= $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13575 * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties)
13576 * @param $x (float) Abscissa of the upper-left corner.
13577 * @param $y (float) Ordinate of the upper-left corner.
13578 * @param $w (float) Width of the signature area.
13579 * @param $h (float) Height of the signature area.
13580 * @param $page (int) option page number (if < 0 the current page is used).
13581 * @param $name (string) Name of the signature.
13583 * @author Nicola Asuni
13584 * @since 5.9.101 (2011-07-06)
13586 public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13588 $this->empty_signature_appearance
[] = array('objid' => $this->n
) +
$this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13592 * Get the array that defines the signature appearance (page and rectangle coordinates).
13593 * @param $x (float) Abscissa of the upper-left corner.
13594 * @param $y (float) Ordinate of the upper-left corner.
13595 * @param $w (float) Width of the signature area.
13596 * @param $h (float) Height of the signature area.
13597 * @param $page (int) option page number (if < 0 the current page is used).
13598 * @param $name (string) Name of the signature.
13599 * @return (array) Array defining page and rectangle coordinates of signature appearance.
13601 * @author Nicola Asuni
13602 * @since 5.9.101 (2011-07-06)
13604 protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13606 if (($page < 1) OR ($page > $this->numpages
)) {
13607 $sigapp['page'] = $this->page
;
13609 $sigapp['page'] = intval($page);
13611 if (empty($name)) {
13612 $sigapp['name'] = 'Signature';
13614 $sigapp['name'] = $name;
13616 $a = $x * $this->k
;
13617 $b = $this->pagedim
[($sigapp['page'])]['h'] - (($y +
$h) * $this->k
);
13618 $c = $w * $this->k
;
13619 $d = $h * $this->k
;
13620 $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a +
$c), ($b +
$d));
13625 * Enable document timestamping (requires the OpenSSL Library).
13626 * The trusted timestamping improve document security that means that no one should be able to change the document once it has been recorded.
13627 * Use with digital signature only!
13628 * @param $tsa_host (string) Time Stamping Authority (TSA) server (prefixed with 'https://')
13629 * @param $tsa_username (string) Specifies the username for TSA authorization (optional) OR specifies the TSA authorization PEM file (see: example_66.php, optional)
13630 * @param $tsa_password (string) Specifies the password for TSA authorization (optional)
13631 * @param $tsa_cert (string) Specifies the location of TSA certificate for authorization (optional for cURL)
13633 * @author Richard Stockinger
13634 * @since 6.0.090 (2014-06-16)
13636 public function setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='') {
13637 $this->tsa_data
= array();
13638 if (!function_exists('curl_init')) {
13639 $this->Error('Please enable cURL PHP extension!');
13641 if (strlen($tsa_host) == 0) {
13642 $this->Error('Please specify the host of Time Stamping Authority (TSA)!');
13644 $this->tsa_data
['tsa_host'] = $tsa_host;
13645 if (is_file($tsa_username)) {
13646 $this->tsa_data
['tsa_auth'] = $tsa_username;
13648 $this->tsa_data
['tsa_username'] = $tsa_username;
13650 $this->tsa_data
['tsa_password'] = $tsa_password;
13651 $this->tsa_data
['tsa_cert'] = $tsa_cert;
13652 $this->tsa_timestamp
= true;
13656 * NOT YET IMPLEMENTED
13657 * Request TSA for a timestamp
13658 * @param $signature (string) Digital signature as binary string
13659 * @return (string) Timestamped digital signature
13661 * @author Richard Stockinger
13662 * @since 6.0.090 (2014-06-16)
13664 protected function applyTSA($signature) {
13665 if (!$this->tsa_timestamp
) {
13668 //@TODO: implement this feature
13673 * Create a new page group.
13674 * NOTE: call this function before calling AddPage()
13675 * @param $page (int) starting group page (leave empty for next page).
13677 * @since 3.0.000 (2008-03-27)
13679 public function startPageGroup($page='') {
13680 if (empty($page)) {
13681 $page = $this->page +
1;
13683 $this->newpagegroup
[$page] = sizeof($this->newpagegroup
) +
1;
13687 * Set the starting page number.
13688 * @param $num (int) Starting page number.
13689 * @since 5.9.093 (2011-06-16)
13692 public function setStartingPageNumber($num=1) {
13693 $this->starting_page_number
= max(0, intval($num));
13697 * Returns the string alias used right align page numbers.
13698 * If the current font is unicode type, the returned string wil contain an additional open curly brace.
13700 * @since 5.9.099 (2011-06-27)
13703 public function getAliasRightShift() {
13704 // calculate aproximatively the ratio between widths of aliases and replacements.
13705 $ref = '{'.TCPDF_STATIC
::$alias_right_shift.'}{'.TCPDF_STATIC
::$alias_tot_pages.'}{'.TCPDF_STATIC
::$alias_num_page.'}';
13706 $rep = str_repeat(' ', $this->GetNumChars($ref));
13707 $wrep = $this->GetStringWidth($rep);
13709 $wdiff = max(1, ($this->GetStringWidth($ref) / $wrep));
13713 $sdiff = sprintf('%F', $wdiff);
13714 $alias = TCPDF_STATIC
::$alias_right_shift.$sdiff.'}';
13715 if ($this->isUnicodeFont()) {
13716 $alias = '{'.$alias;
13722 * Returns the string alias used for the total number of pages.
13723 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13724 * This alias will be replaced by the total number of pages in the document.
13726 * @since 4.0.018 (2008-08-08)
13729 public function getAliasNbPages() {
13730 if ($this->isUnicodeFont()) {
13731 return '{'.TCPDF_STATIC
::$alias_tot_pages.'}';
13733 return TCPDF_STATIC
::$alias_tot_pages;
13737 * Returns the string alias used for the page number.
13738 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13739 * This alias will be replaced by the page number.
13741 * @since 4.5.000 (2009-01-02)
13744 public function getAliasNumPage() {
13745 if ($this->isUnicodeFont()) {
13746 return '{'.TCPDF_STATIC
::$alias_num_page.'}';
13748 return TCPDF_STATIC
::$alias_num_page;
13752 * Return the alias for the total number of pages in the current page group.
13753 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13754 * This alias will be replaced by the total number of pages in this group.
13755 * @return alias of the current page group
13757 * @since 3.0.000 (2008-03-27)
13759 public function getPageGroupAlias() {
13760 if ($this->isUnicodeFont()) {
13761 return '{'.TCPDF_STATIC
::$alias_group_tot_pages.'}';
13763 return TCPDF_STATIC
::$alias_group_tot_pages;
13767 * Return the alias for the page number on the current page group.
13768 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13769 * This alias will be replaced by the page number (relative to the belonging group).
13770 * @return alias of the current page group
13772 * @since 4.5.000 (2009-01-02)
13774 public function getPageNumGroupAlias() {
13775 if ($this->isUnicodeFont()) {
13776 return '{'.TCPDF_STATIC
::$alias_group_num_page.'}';
13778 return TCPDF_STATIC
::$alias_group_num_page;
13782 * Return the current page in the group.
13783 * @return current page in the group
13785 * @since 3.0.000 (2008-03-27)
13787 public function getGroupPageNo() {
13788 return $this->pagegroups
[$this->currpagegroup
];
13792 * Returns the current group page number formatted as a string.
13794 * @since 4.3.003 (2008-11-18)
13795 * @see PaneNo(), formatPageNumber()
13797 public function getGroupPageNoFormatted() {
13798 return TCPDF_STATIC
::formatPageNumber($this->getGroupPageNo());
13802 * Returns the current page number formatted as a string.
13804 * @since 4.2.005 (2008-11-06)
13805 * @see PaneNo(), formatPageNumber()
13807 public function PageNoFormatted() {
13808 return TCPDF_STATIC
::formatPageNumber($this->PageNo());
13814 * @since 3.0.000 (2008-03-27)
13816 protected function _putocg() {
13817 if (empty($this->pdflayers
)) {
13820 foreach ($this->pdflayers
as $key => $layer) {
13821 $this->pdflayers
[$key]['objid'] = $this->_newobj();
13822 $out = '<< /Type /OCG';
13823 $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers
[$key]['objid']);
13824 $out .= ' /Usage <<';
13825 if (isset($layer['print']) AND ($layer['print'] !== NULL)) {
13826 $out .= ' /Print <</PrintState /'.($layer['print']?
'ON':'OFF').'>>';
13828 $out .= ' /View <</ViewState /'.($layer['view']?
'ON':'OFF').'>>';
13830 $out .= "\n".'endobj';
13836 * Start a new pdf layer.
13837 * @param $name (string) Layer name (only a-z letters and numbers). Leave empty for automatic name.
13838 * @param $print (boolean|null) Set to TRUE to print this layer, FALSE to not print and NULL to not set this option
13839 * @param $view (boolean) Set to true to view this layer.
13840 * @param $lock (boolean) If true lock the layer
13842 * @since 5.9.102 (2011-07-13)
13844 public function startLayer($name='', $print=true, $view=true, $lock=true) {
13845 if ($this->state
!= 2) {
13848 $layer = sprintf('LYR%03d', (count($this->pdflayers
) +
1));
13849 if (empty($name)) {
13852 $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
13854 $this->pdflayers
[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view, 'lock' => $lock);
13855 $this->openMarkedContent
= true;
13856 $this->_out('/OC /'.$layer.' BDC');
13860 * End the current PDF layer.
13862 * @since 5.9.102 (2011-07-13)
13864 public function endLayer() {
13865 if ($this->state
!= 2) {
13868 if ($this->openMarkedContent
) {
13869 // close existing open marked-content layer
13870 $this->_out('EMC');
13871 $this->openMarkedContent
= false;
13876 * Set the visibility of the successive elements.
13877 * This can be useful, for instance, to put a background
13878 * image or color that will show on screen but won't print.
13879 * @param $v (string) visibility mode. Legal values are: all, print, screen or view.
13881 * @since 3.0.000 (2008-03-27)
13883 public function setVisibility($v) {
13884 if ($this->state
!= 2) {
13890 $this->startLayer('Print', true, false);
13895 $this->startLayer('View', false, true);
13903 $this->Error('Incorrect visibility: '.$v);
13910 * Add transparency parameters to the current extgstate
13911 * @param $parms (array) parameters
13912 * @return the number of extgstates
13914 * @since 3.0.000 (2008-03-27)
13916 protected function addExtGState($parms) {
13917 if ($this->pdfa_mode
) {
13918 // transparencies are not allowed in PDF/A mode
13921 // check if this ExtGState already exist
13922 foreach ($this->extgstates
as $i => $ext) {
13923 if ($ext['parms'] == $parms) {
13924 if ($this->inxobj
) {
13925 // we are inside an XObject template
13926 $this->xobjects
[$this->xobjid
]['extgstates'][$i] = $ext;
13928 // return reference to existing ExtGState
13932 $n = (count($this->extgstates
) +
1);
13933 $this->extgstates
[$n] = array('parms' => $parms);
13934 if ($this->inxobj
) {
13935 // we are inside an XObject template
13936 $this->xobjects
[$this->xobjid
]['extgstates'][$n] = $this->extgstates
[$n];
13943 * @param $gs (array) extgstate
13945 * @since 3.0.000 (2008-03-27)
13947 protected function setExtGState($gs) {
13948 if ($this->pdfa_mode
OR ($this->state
!= 2)) {
13949 // transparency is not allowed in PDF/A mode
13952 $this->_out(sprintf('/GS%d gs', $gs));
13956 * Put extgstates for object transparency
13958 * @since 3.0.000 (2008-03-27)
13960 protected function _putextgstates() {
13961 foreach ($this->extgstates
as $i => $ext) {
13962 $this->extgstates
[$i]['n'] = $this->_newobj();
13963 $out = '<< /Type /ExtGState';
13964 foreach ($ext['parms'] as $k => $v) {
13965 if (is_float($v)) {
13966 $v = sprintf('%F', $v);
13967 } elseif ($v === true) {
13969 } elseif ($v === false) {
13972 $out .= ' /'.$k.' '.$v;
13975 $out .= "\n".'endobj';
13981 * Set overprint mode for stroking (OP) and non-stroking (op) painting operations.
13982 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
13983 * @param $stroking (boolean) If true apply overprint for stroking operations.
13984 * @param $nonstroking (boolean) If true apply overprint for painting operations other than stroking.
13985 * @param $mode (integer) Overprint mode: (0 = each source colour component value replaces the value previously painted for the corresponding device colorant; 1 = a tint value of 0.0 for a source colour component shall leave the corresponding component of the previously painted colour unchanged).
13987 * @since 5.9.152 (2012-03-23)
13989 public function setOverprint($stroking=true, $nonstroking='', $mode=0) {
13990 if ($this->state
!= 2) {
13993 $stroking = $stroking ?
true : false;
13994 if (TCPDF_STATIC
::empty_string($nonstroking)) {
13995 // default value if not set
13996 $nonstroking = $stroking;
13998 $nonstroking = $nonstroking ?
true : false;
14000 if (($mode != 0) AND ($mode != 1)) {
14003 $this->overprint
= array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode);
14004 $gs = $this->addExtGState($this->overprint
);
14005 $this->setExtGState($gs);
14009 * Get the overprint mode array (OP, op, OPM).
14010 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
14013 * @since 5.9.152 (2012-03-23)
14015 public function getOverprint() {
14016 return $this->overprint
;
14020 * Set alpha for stroking (CA) and non-stroking (ca) operations.
14021 * @param $stroking (float) Alpha value for stroking operations: real value from 0 (transparent) to 1 (opaque).
14022 * @param $bm (string) blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity
14023 * @param $nonstroking (float) Alpha value for non-stroking operations: real value from 0 (transparent) to 1 (opaque).
14024 * @param $ais (boolean)
14026 * @since 3.0.000 (2008-03-27)
14028 public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) {
14029 if ($this->pdfa_mode
) {
14030 // transparency is not allowed in PDF/A mode
14033 $stroking = floatval($stroking);
14034 if (TCPDF_STATIC
::empty_string($nonstroking)) {
14035 // default value if not set
14036 $nonstroking = $stroking;
14038 $nonstroking = floatval($nonstroking);
14040 if ($bm[0] == '/') {
14041 // remove trailing slash
14042 $bm = substr($bm, 1);
14044 if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
14047 $ais = $ais ?
true : false;
14048 $this->alpha
= array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais);
14049 $gs = $this->addExtGState($this->alpha
);
14050 $this->setExtGState($gs);
14054 * Get the alpha mode array (CA, ca, BM, AIS).
14055 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
14058 * @since 5.9.152 (2012-03-23)
14060 public function getAlpha() {
14061 return $this->alpha
;
14065 * Set the default JPEG compression quality (1-100)
14066 * @param $quality (int) JPEG quality, integer between 1 and 100
14068 * @since 3.0.000 (2008-03-27)
14070 public function setJPEGQuality($quality) {
14071 if (($quality < 1) OR ($quality > 100)) {
14074 $this->jpeg_quality
= intval($quality);
14078 * Set the default number of columns in a row for HTML tables.
14079 * @param $cols (int) number of columns
14081 * @since 3.0.014 (2008-06-04)
14083 public function setDefaultTableColumns($cols=4) {
14084 $this->default_table_columns
= intval($cols);
14088 * Set the height of the cell (line height) respect the font height.
14089 * @param $h (int) cell proportion respect font height (typical value = 1.25).
14091 * @since 3.0.014 (2008-06-04)
14093 public function setCellHeightRatio($h) {
14094 $this->cell_height_ratio
= $h;
14098 * return the height of cell repect font height.
14100 * @since 4.0.012 (2008-07-24)
14102 public function getCellHeightRatio() {
14103 return $this->cell_height_ratio
;
14107 * Set the PDF version (check PDF reference for valid values).
14108 * @param $version (string) PDF document version.
14110 * @since 3.1.000 (2008-06-09)
14112 public function setPDFVersion($version='1.7') {
14113 if ($this->pdfa_mode
) {
14115 $this->PDFVersion
= '1.4';
14117 $this->PDFVersion
= $version;
14122 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
14123 * (see Section 8.1 of PDF reference, "Viewer Preferences").
14124 * <ul><li>HideToolbar boolean (Optional) A flag specifying whether to hide the viewer application's tool bars when the document is active. Default value: false.</li><li>HideMenubar boolean (Optional) A flag specifying whether to hide the viewer application's menu bar when the document is active. Default value: false.</li><li>HideWindowUI boolean (Optional) A flag specifying whether to hide user interface elements in the document's window (such as scroll bars and navigation controls), leaving only the document's contents displayed. Default value: false.</li><li>FitWindow boolean (Optional) A flag specifying whether to resize the document's window to fit the size of the first displayed page. Default value: false.</li><li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li><li>DisplayDocTitle boolean (Optional; PDF 1.4) A flag specifying whether the window's title bar should display the document title taken from the Title entry of the document information dictionary (see Section 10.2.1, "Document Information Dictionary"). If false, the title bar should instead display the name of the PDF file containing the document. Default value: false.</li><li>NonFullScreenPageMode name (Optional) The document's page mode, specifying how to display the document on exiting full-screen mode:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>UseOC Optional content group panel visible</li></ul>This entry is meaningful only if the value of the PageMode entry in the catalog dictionary (see Section 3.6.1, "Document Catalog") is FullScreen; it is ignored otherwise. Default value: UseNone.</li><li>ViewArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be displayed when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>ViewClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be rendered when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li><li>PrintScaling name (Optional; PDF 1.6) The page scaling option to be selected when a print dialog is displayed for this document. Valid values are: <ul><li>None, which indicates that the print dialog should reflect no page scaling</li><li>AppDefault (default), which indicates that applications should use the current print scaling</li></ul></li><li>Duplex name (Optional; PDF 1.7) The paper handling option to use when printing the file from the print dialog. The following values are valid:<ul><li>Simplex - Print single-sided</li><li>DuplexFlipShortEdge - Duplex and flip on the short edge of the sheet</li><li>DuplexFlipLongEdge - Duplex and flip on the long edge of the sheet</li></ul>Default value: none</li><li>PickTrayByPDFSize boolean (Optional; PDF 1.7) A flag specifying whether the PDF page size is used to select the input paper tray. This setting influences only the preset values used to populate the print dialog presented by a PDF viewer application. If PickTrayByPDFSize is true, the check box in the print dialog associated with input paper tray is checked. Note: This setting has no effect on Mac OS systems, which do not provide the ability to pick the input tray by size.</li><li>PrintPageRange array (Optional; PDF 1.7) The page numbers used to initialize the print dialog box when the file is printed. The first page of the PDF file is denoted by 1. Each pair consists of the first and last pages in the sub-range. An odd number of integers causes this entry to be ignored. Negative numbers cause the entire array to be ignored. Default value: as defined by PDF viewer application</li><li>NumCopies integer (Optional; PDF 1.7) The number of copies to be printed when the print dialog is opened for this file. Supported values are the integers 2 through 5. Values outside this range are ignored. Default value: as defined by PDF viewer application, but typically 1</li></ul>
14125 * @param $preferences (array) array of options.
14126 * @author Nicola Asuni
14128 * @since 3.1.000 (2008-06-09)
14130 public function setViewerPreferences($preferences) {
14131 $this->viewer_preferences
= $preferences;
14135 * Paints color transition registration bars
14136 * @param $x (float) abscissa of the top left corner of the rectangle.
14137 * @param $y (float) ordinate of the top left corner of the rectangle.
14138 * @param $w (float) width of the rectangle.
14139 * @param $h (float) height of the rectangle.
14140 * @param $transition (boolean) if true prints tcolor transitions to white.
14141 * @param $vertical (boolean) if true prints bar vertically.
14142 * @param $colors (string) colors to print separated by comma. Valid values are: A,W,R,G,B,C,M,Y,K,RGB,CMYK,ALL,ALLSPOT,<SPOT_COLOR_NAME>. Where: A = grayscale black, W = grayscale white, R = RGB red, G RGB green, B RGB blue, C = CMYK cyan, M = CMYK magenta, Y = CMYK yellow, K = CMYK key/black, RGB = RGB registration color, CMYK = CMYK registration color, ALL = Spot registration color, ALLSPOT = print all defined spot colors, <SPOT_COLOR_NAME> = name of the spot color to print.
14143 * @author Nicola Asuni
14144 * @since 4.9.000 (2010-03-26)
14147 public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
14148 if (strpos($colors, 'ALLSPOT') !== false) {
14149 // expand spot colors
14151 foreach ($this->spot_colors
as $spot_color_name => $v) {
14152 $spot_colors .= ','.$spot_color_name;
14154 if (!empty($spot_colors)) {
14155 $spot_colors = substr($spot_colors, 1);
14156 $colors = str_replace('ALLSPOT', $spot_colors, $colors);
14158 $colors = str_replace('ALLSPOT', 'NONE', $colors);
14161 $bars = explode(',', $colors);
14162 $numbars = count($bars); // number of bars to print
14163 if ($numbars <= 0) {
14166 // set bar measures
14168 $coords = array(0, 0, 0, 1);
14169 $wb = $w / $numbars; // bar width
14170 $hb = $h; // bar height
14171 $xd = $wb; // delta x
14172 $yd = 0; // delta y
14174 $coords = array(1, 0, 0, 0);
14175 $wb = $w; // bar width
14176 $hb = $h / $numbars; // bar height
14177 $xd = 0; // delta x
14178 $yd = $hb; // delta y
14182 foreach ($bars as $col) {
14184 // set transition colors
14185 case 'A': { // BLACK (GRAYSCALE)
14186 $col_a = array(255);
14190 case 'W': { // WHITE (GRAYSCALE)
14192 $col_b = array(255);
14195 case 'R': { // RED (RGB)
14196 $col_a = array(255,255,255);
14197 $col_b = array(255,0,0);
14200 case 'G': { // GREEN (RGB)
14201 $col_a = array(255,255,255);
14202 $col_b = array(0,255,0);
14205 case 'B': { // BLUE (RGB)
14206 $col_a = array(255,255,255);
14207 $col_b = array(0,0,255);
14210 case 'C': { // CYAN (CMYK)
14211 $col_a = array(0,0,0,0);
14212 $col_b = array(100,0,0,0);
14215 case 'M': { // MAGENTA (CMYK)
14216 $col_a = array(0,0,0,0);
14217 $col_b = array(0,100,0,0);
14220 case 'Y': { // YELLOW (CMYK)
14221 $col_a = array(0,0,0,0);
14222 $col_b = array(0,0,100,0);
14225 case 'K': { // KEY - BLACK (CMYK)
14226 $col_a = array(0,0,0,0);
14227 $col_b = array(0,0,0,100);
14230 case 'RGB': { // BLACK REGISTRATION (RGB)
14231 $col_a = array(255,255,255);
14232 $col_b = array(0,0,0);
14235 case 'CMYK': { // BLACK REGISTRATION (CMYK)
14236 $col_a = array(0,0,0,0);
14237 $col_b = array(100,100,100,100);
14240 case 'ALL': { // SPOT COLOR REGISTRATION
14241 $col_a = array(0,0,0,0,'None');
14242 $col_b = array(100,100,100,100,'All');
14245 case 'NONE': { // SKIP THIS COLOR
14246 $col_a = array(0,0,0,0,'None');
14247 $col_b = array(0,0,0,0,'None');
14250 default: { // SPECIFIC SPOT COLOR NAME
14251 $col_a = array(0,0,0,0,'None');
14252 $col_b = TCPDF_COLORS
::getSpotColor($col, $this->spot_colors
);
14253 if ($col_b === false) {
14254 // in case of error defaults to the registration color
14255 $col_b = array(100,100,100,100,'All');
14260 if ($col != 'NONE') {
14263 $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
14265 $this->SetFillColorArray($col_b);
14266 // colored rectangle
14267 $this->Rect($xb, $yb, $wb, $hb, 'F', array());
14276 * Paints crop marks.
14277 * @param $x (float) abscissa of the crop mark center.
14278 * @param $y (float) ordinate of the crop mark center.
14279 * @param $w (float) width of the crop mark.
14280 * @param $h (float) height of the crop mark.
14281 * @param $type (string) type of crop mark, one symbol per type separated by comma: T = TOP, F = BOTTOM, L = LEFT, R = RIGHT, TL = A = TOP-LEFT, TR = B = TOP-RIGHT, BL = C = BOTTOM-LEFT, BR = D = BOTTOM-RIGHT.
14282 * @param $color (array) crop mark color (default spot registration color).
14283 * @author Nicola Asuni
14284 * @since 4.9.000 (2010-03-26)
14287 public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(100,100,100,100,'All')) {
14288 $this->SetLineStyle(array('width' => (0.5 / $this->k
), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
14289 $type = strtoupper($type);
14290 $type = preg_replace('/[^A-Z\-\,]*/', '', $type);
14291 // split type in single components
14292 $type = str_replace('-', ',', $type);
14293 $type = str_replace('TL', 'T,L', $type);
14294 $type = str_replace('TR', 'T,R', $type);
14295 $type = str_replace('BL', 'F,L', $type);
14296 $type = str_replace('BR', 'F,R', $type);
14297 $type = str_replace('A', 'T,L', $type);
14298 $type = str_replace('B', 'T,R', $type);
14299 $type = str_replace('T,RO', 'BO', $type);
14300 $type = str_replace('C', 'F,L', $type);
14301 $type = str_replace('D', 'F,R', $type);
14302 $crops = explode(',', strtoupper($type));
14303 // remove duplicates
14304 $crops = array_unique($crops);
14305 $dw = ($w / 4); // horizontal space to leave before the intersection point
14306 $dh = ($h / 4); // vertical space to leave before the intersection point
14307 foreach ($crops as $crop) {
14342 $this->Line($x1, $y1, $x2, $y2);
14347 * Paints a registration mark
14348 * @param $x (float) abscissa of the registration mark center.
14349 * @param $y (float) ordinate of the registration mark center.
14350 * @param $r (float) radius of the crop mark.
14351 * @param $double (boolean) if true print two concentric crop marks.
14352 * @param $cola (array) crop mark color (default spot registration color 'All').
14353 * @param $colb (array) second crop mark color (default spot registration color 'None').
14354 * @author Nicola Asuni
14355 * @since 4.9.000 (2010-03-26)
14358 public function registrationMark($x, $y, $r, $double=false, $cola=array(100,100,100,100,'All'), $colb=array(0,0,0,0,'None')) {
14359 $line_style = array('width' => max((0.5 / $this->k
),($r / 30)), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
14360 $this->SetFillColorArray($cola);
14361 $this->PieSector($x, $y, $r, 90, 180, 'F');
14362 $this->PieSector($x, $y, $r, 270, 360, 'F');
14363 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14366 $this->SetFillColorArray($colb);
14367 $this->PieSector($x, $y, $ri, 90, 180, 'F');
14368 $this->PieSector($x, $y, $ri, 270, 360, 'F');
14369 $this->SetFillColorArray($cola);
14370 $this->PieSector($x, $y, $ri, 0, 90, 'F');
14371 $this->PieSector($x, $y, $ri, 180, 270, 'F');
14372 $this->Circle($x, $y, $ri, 0, 360, 'C', $line_style, array(), 8);
14377 * Paints a CMYK registration mark
14378 * @param $x (float) abscissa of the registration mark center.
14379 * @param $y (float) ordinate of the registration mark center.
14380 * @param $r (float) radius of the crop mark.
14381 * @author Nicola Asuni
14382 * @since 6.0.038 (2013-09-30)
14385 public function registrationMarkCMYK($x, $y, $r) {
14387 $lw = max((0.5 / $this->k
),($r / 8));
14393 $this->SetFillColorArray(array(100,0,0,0));
14394 $this->PieSector($x, $y, $ri, 270, 360, 'F');
14396 $this->SetFillColorArray(array(0,100,0,0));
14397 $this->PieSector($x, $y, $ri, 0, 90, 'F');
14399 $this->SetFillColorArray(array(0,0,100,0));
14400 $this->PieSector($x, $y, $ri, 90, 180, 'F');
14402 $this->SetFillColorArray(array(0,0,0,100));
14403 $this->PieSector($x, $y, $ri, 180, 270, 'F');
14404 // registration color
14405 $line_style = array('width' => $lw, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(100,100,100,100,'All'));
14406 $this->SetFillColorArray(array(100,100,100,100,'All'));
14408 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14410 $this->Line($x, ($y - $re), $x, ($y - $ri));
14411 $this->Line($x, ($y +
$ri), $x, ($y +
$re));
14412 $this->Line(($x - $re), $y, ($x - $ri), $y);
14413 $this->Line(($x +
$ri), $y, ($x +
$re), $y);
14417 * Paints a linear colour gradient.
14418 * @param $x (float) abscissa of the top left corner of the rectangle.
14419 * @param $y (float) ordinate of the top left corner of the rectangle.
14420 * @param $w (float) width of the rectangle.
14421 * @param $h (float) height of the rectangle.
14422 * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
14423 * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
14424 * @param $coords (array) array of the form (x1, y1, x2, y2) which defines the gradient vector (see linear_gradient_coords.jpg). The default value is from left to right (x1=0, y1=0, x2=1, y2=0).
14425 * @author Andreas W\FCrmser, Nicola Asuni
14426 * @since 3.1.000 (2008-06-09)
14429 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
14430 $this->Clip($x, $y, $w, $h);
14431 $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14435 * Paints a radial colour gradient.
14436 * @param $x (float) abscissa of the top left corner of the rectangle.
14437 * @param $y (float) ordinate of the top left corner of the rectangle.
14438 * @param $w (float) width of the rectangle.
14439 * @param $h (float) height of the rectangle.
14440 * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
14441 * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
14442 * @param $coords (array) array of the form (fx, fy, cx, cy, r) where (fx, fy) is the starting point of the gradient with color1, (cx, cy) is the center of the circle with color2, and r is the radius of the circle (see radial_gradient_coords.jpg). (fx, fy) should be inside the circle, otherwise some areas will not be defined.
14443 * @author Andreas W\FCrmser, Nicola Asuni
14444 * @since 3.1.000 (2008-06-09)
14447 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
14448 $this->Clip($x, $y, $w, $h);
14449 $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14453 * Paints a coons patch mesh.
14454 * @param $x (float) abscissa of the top left corner of the rectangle.
14455 * @param $y (float) ordinate of the top left corner of the rectangle.
14456 * @param $w (float) width of the rectangle.
14457 * @param $h (float) height of the rectangle.
14458 * @param $col1 (array) first color (lower left corner) (RGB components).
14459 * @param $col2 (array) second color (lower right corner) (RGB components).
14460 * @param $col3 (array) third color (upper right corner) (RGB components).
14461 * @param $col4 (array) fourth color (upper left corner) (RGB components).
14462 * @param $coords (array) <ul><li>for one patch mesh: array(float x1, float y1, .... float x12, float y12): 12 pairs of coordinates (normally from 0 to 1) which specify the Bezier control points that define the patch. First pair is the lower left edge point, next is its right control point (control point 2). Then the other points are defined in the order: control point 1, edge point, control point 2 going counter-clockwise around the patch. Last (x12, y12) is the first edge point's left control point (control point 1).</li><li>for two or more patch meshes: array[number of patches]: arrays with the following keys for each patch: f: where to put that patch (0 = first patch, 1, 2, 3 = right, top and left of precedent patch - I didn't figure this out completely - just try and error ;-) points: 12 pairs of coordinates of the Bezier control points as above for the first patch, 8 pairs of coordinates for the following patches, ignoring the coordinates already defined by the precedent patch (I also didn't figure out the order of these - also: try and see what's happening) colors: must be 4 colors for the first patch, 2 colors for the following patches</li></ul>
14463 * @param $coords_min (array) minimum value used by the coordinates. If a coordinate's value is smaller than this it will be cut to coords_min. default: 0
14464 * @param $coords_max (array) maximum value used by the coordinates. If a coordinate's value is greater than this it will be cut to coords_max. default: 1
14465 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
14466 * @author Andreas W\FCrmser, Nicola Asuni
14467 * @since 3.1.000 (2008-06-09)
14470 public function CoonsPatchMesh($x, $y, $w, $h, $col1=array(), $col2=array(), $col3=array(), $col4=array(), $coords=array(0.00,0.0,0.33,0.00,0.67,0.00,1.00,0.00,1.00,0.33,1.00,0.67,1.00,1.00,0.67,1.00,0.33,1.00,0.00,1.00,0.00,0.67,0.00,0.33), $coords_min=0, $coords_max=1, $antialias=false) {
14471 if ($this->pdfa_mode
OR ($this->state
!= 2)) {
14474 $this->Clip($x, $y, $w, $h);
14475 $n = count($this->gradients
) +
1;
14476 $this->gradients
[$n] = array();
14477 $this->gradients
[$n]['type'] = 6; //coons patch mesh
14478 $this->gradients
[$n]['coords'] = array();
14479 $this->gradients
[$n]['antialias'] = $antialias;
14480 $this->gradients
[$n]['colors'] = array();
14481 $this->gradients
[$n]['transparency'] = false;
14482 //check the coords array if it is the simple array or the multi patch array
14483 if (!isset($coords[0]['f'])) {
14484 //simple array -> convert to multi patch array
14485 if (!isset($col1[1])) {
14486 $col1[1] = $col1[2] = $col1[0];
14488 if (!isset($col2[1])) {
14489 $col2[1] = $col2[2] = $col2[0];
14491 if (!isset($col3[1])) {
14492 $col3[1] = $col3[2] = $col3[0];
14494 if (!isset($col4[1])) {
14495 $col4[1] = $col4[2] = $col4[0];
14497 $patch_array[0]['f'] = 0;
14498 $patch_array[0]['points'] = $coords;
14499 $patch_array[0]['colors'][0]['r'] = $col1[0];
14500 $patch_array[0]['colors'][0]['g'] = $col1[1];
14501 $patch_array[0]['colors'][0]['b'] = $col1[2];
14502 $patch_array[0]['colors'][1]['r'] = $col2[0];
14503 $patch_array[0]['colors'][1]['g'] = $col2[1];
14504 $patch_array[0]['colors'][1]['b'] = $col2[2];
14505 $patch_array[0]['colors'][2]['r'] = $col3[0];
14506 $patch_array[0]['colors'][2]['g'] = $col3[1];
14507 $patch_array[0]['colors'][2]['b'] = $col3[2];
14508 $patch_array[0]['colors'][3]['r'] = $col4[0];
14509 $patch_array[0]['colors'][3]['g'] = $col4[1];
14510 $patch_array[0]['colors'][3]['b'] = $col4[2];
14512 //multi patch array
14513 $patch_array = $coords;
14515 $bpcd = 65535; //16 bits per coordinate
14516 //build the data stream
14517 $this->gradients
[$n]['stream'] = '';
14518 $count_patch = count($patch_array);
14519 for ($i=0; $i < $count_patch; ++
$i) {
14520 $this->gradients
[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
14521 $count_points = count($patch_array[$i]['points']);
14522 for ($j=0; $j < $count_points; ++
$j) {
14523 //each point as 16 bit
14524 $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
14525 if ($patch_array[$i]['points'][$j] < 0) {
14526 $patch_array[$i]['points'][$j] = 0;
14528 if ($patch_array[$i]['points'][$j] > $bpcd) {
14529 $patch_array[$i]['points'][$j] = $bpcd;
14531 $this->gradients
[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
14532 $this->gradients
[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] %
256));
14534 $count_cols = count($patch_array[$i]['colors']);
14535 for ($j=0; $j < $count_cols; ++
$j) {
14536 //each color component as 8 bit
14537 $this->gradients
[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
14538 $this->gradients
[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
14539 $this->gradients
[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
14542 //paint the gradient
14543 $this->_out('/Sh'.$n.' sh');
14544 //restore previous Graphic State
14545 $this->_outRestoreGraphicsState();
14546 if ($this->inxobj
) {
14547 // we are inside an XObject template
14548 $this->xobjects
[$this->xobjid
]['gradients'][$n] = $this->gradients
[$n];
14553 * Set a rectangular clipping area.
14554 * @param $x (float) abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
14555 * @param $y (float) ordinate of the top left corner of the rectangle.
14556 * @param $w (float) width of the rectangle.
14557 * @param $h (float) height of the rectangle.
14558 * @author Andreas W\FCrmser, Nicola Asuni
14559 * @since 3.1.000 (2008-06-09)
14562 protected function Clip($x, $y, $w, $h) {
14563 if ($this->state
!= 2) {
14567 $x = $this->w
- $x - $w;
14569 //save current Graphic State
14571 //set clipping area
14572 $s .= sprintf(' %F %F %F %F re W n', $x*$this->k
, ($this->h
-$y)*$this->k
, $w*$this->k
, -$h*$this->k
);
14573 //set up transformation matrix for gradient
14574 $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k
, $h*$this->k
, $x*$this->k
, ($this->h
-($y+
$h))*$this->k
);
14580 * @param $type (int) type of gradient (1 Function-based shading; 2 Axial shading; 3 Radial shading; 4 Free-form Gouraud-shaded triangle mesh; 5 Lattice-form Gouraud-shaded triangle mesh; 6 Coons patch mesh; 7 Tensor-product patch mesh). (Not all types are currently supported)
14581 * @param $coords (array) array of coordinates.
14582 * @param $stops (array) array gradient color components: color = array of GRAY, RGB or CMYK color components; offset = (0 to 1) represents a location along the gradient vector; exponent = exponent of the exponential interpolation function (default = 1).
14583 * @param $background (array) An array of colour components appropriate to the colour space, specifying a single background colour value.
14584 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
14585 * @author Nicola Asuni
14586 * @since 3.1.000 (2008-06-09)
14589 public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
14590 if ($this->pdfa_mode
OR ($this->state
!= 2)) {
14593 $n = count($this->gradients
) +
1;
14594 $this->gradients
[$n] = array();
14595 $this->gradients
[$n]['type'] = $type;
14596 $this->gradients
[$n]['coords'] = $coords;
14597 $this->gradients
[$n]['antialias'] = $antialias;
14598 $this->gradients
[$n]['colors'] = array();
14599 $this->gradients
[$n]['transparency'] = false;
14601 $numcolspace = count($stops[0]['color']);
14602 $bcolor = array_values($background);
14603 switch($numcolspace) {
14606 $this->gradients
[$n]['colspace'] = 'DeviceCMYK';
14607 if (!empty($background)) {
14608 $this->gradients
[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
14613 $this->gradients
[$n]['colspace'] = 'DeviceRGB';
14614 if (!empty($background)) {
14615 $this->gradients
[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
14619 case 1: { // GRAY SCALE
14620 $this->gradients
[$n]['colspace'] = 'DeviceGray';
14621 if (!empty($background)) {
14622 $this->gradients
[$n]['background'] = sprintf('%F', $bcolor[0]/255);
14627 $num_stops = count($stops);
14628 $last_stop_id = $num_stops - 1;
14629 foreach ($stops as $key => $stop) {
14630 $this->gradients
[$n]['colors'][$key] = array();
14631 // offset represents a location along the gradient vector
14632 if (isset($stop['offset'])) {
14633 $this->gradients
[$n]['colors'][$key]['offset'] = $stop['offset'];
14636 $this->gradients
[$n]['colors'][$key]['offset'] = 0;
14637 } elseif ($key == $last_stop_id) {
14638 $this->gradients
[$n]['colors'][$key]['offset'] = 1;
14640 $offsetstep = (1 - $this->gradients
[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
14641 $this->gradients
[$n]['colors'][$key]['offset'] = $this->gradients
[$n]['colors'][($key - 1)]['offset'] +
$offsetstep;
14644 if (isset($stop['opacity'])) {
14645 $this->gradients
[$n]['colors'][$key]['opacity'] = $stop['opacity'];
14646 if ((!$this->pdfa_mode
) AND ($stop['opacity'] < 1)) {
14647 $this->gradients
[$n]['transparency'] = true;
14650 $this->gradients
[$n]['colors'][$key]['opacity'] = 1;
14652 // exponent for the exponential interpolation function
14653 if (isset($stop['exponent'])) {
14654 $this->gradients
[$n]['colors'][$key]['exponent'] = $stop['exponent'];
14656 $this->gradients
[$n]['colors'][$key]['exponent'] = 1;
14659 $color = array_values($stop['color']);
14660 switch($numcolspace) {
14663 $this->gradients
[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
14667 $this->gradients
[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255);
14670 case 1: { // GRAY SCALE
14671 $this->gradients
[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255);
14676 if ($this->gradients
[$n]['transparency']) {
14677 // paint luminosity gradient
14678 $this->_out('/TGS'.$n.' gs');
14680 //paint the gradient
14681 $this->_out('/Sh'.$n.' sh');
14682 //restore previous Graphic State
14683 $this->_outRestoreGraphicsState();
14684 if ($this->inxobj
) {
14685 // we are inside an XObject template
14686 $this->xobjects
[$this->xobjid
]['gradients'][$n] = $this->gradients
[$n];
14691 * Output gradient shaders.
14692 * @author Nicola Asuni
14693 * @since 3.1.000 (2008-06-09)
14696 function _putshaders() {
14697 if ($this->pdfa_mode
) {
14700 $idt = count($this->gradients
); //index for transparency gradients
14701 foreach ($this->gradients
as $id => $grad) {
14702 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
14703 $fc = $this->_newobj();
14705 $out .= ' /FunctionType 3';
14706 $out .= ' /Domain [0 1]';
14711 $num_cols = count($grad['colors']);
14712 $lastcols = $num_cols - 1;
14713 for ($i = 1; $i < $num_cols; ++
$i) {
14714 $functions .= ($fc +
$i).' 0 R ';
14715 if ($i < $lastcols) {
14716 $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']);
14720 $out .= ' /Functions ['.trim($functions).']';
14721 $out .= ' /Bounds ['.trim($bounds).']';
14722 $out .= ' /Encode ['.trim($encode).']';
14724 $out .= "\n".'endobj';
14726 for ($i = 1; $i < $num_cols; ++
$i) {
14729 $out .= ' /FunctionType 2';
14730 $out .= ' /Domain [0 1]';
14731 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
14732 $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
14733 $out .= ' /N '.$grad['colors'][$i]['exponent'];
14735 $out .= "\n".'endobj';
14738 // set transparency fuctions
14739 if ($grad['transparency']) {
14740 $ft = $this->_newobj();
14742 $out .= ' /FunctionType 3';
14743 $out .= ' /Domain [0 1]';
14746 $num_cols = count($grad['colors']);
14747 for ($i = 1; $i < $num_cols; ++
$i) {
14748 $functions .= ($ft +
$i).' 0 R ';
14750 $out .= ' /Functions ['.trim($functions).']';
14751 $out .= ' /Bounds ['.trim($bounds).']';
14752 $out .= ' /Encode ['.trim($encode).']';
14754 $out .= "\n".'endobj';
14756 for ($i = 1; $i < $num_cols; ++
$i) {
14759 $out .= ' /FunctionType 2';
14760 $out .= ' /Domain [0 1]';
14761 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
14762 $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
14763 $out .= ' /N '.$grad['colors'][$i]['exponent'];
14765 $out .= "\n".'endobj';
14770 // set shading object
14772 $out = '<< /ShadingType '.$grad['type'];
14773 if (isset($grad['colspace'])) {
14774 $out .= ' /ColorSpace /'.$grad['colspace'];
14776 $out .= ' /ColorSpace /DeviceRGB';
14778 if (isset($grad['background']) AND !empty($grad['background'])) {
14779 $out .= ' /Background ['.$grad['background'].']';
14781 if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
14782 $out .= ' /AntiAlias true';
14784 if ($grad['type'] == 2) {
14785 $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
14786 $out .= ' /Domain [0 1]';
14787 $out .= ' /Function '.$fc.' 0 R';
14788 $out .= ' /Extend [true true]';
14790 } elseif ($grad['type'] == 3) {
14791 //x0, y0, r0, x1, y1, r1
14792 //at this this time radius of inner circle is 0
14793 $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
14794 $out .= ' /Domain [0 1]';
14795 $out .= ' /Function '.$fc.' 0 R';
14796 $out .= ' /Extend [true true]';
14798 } elseif ($grad['type'] == 6) {
14799 $out .= ' /BitsPerCoordinate 16';
14800 $out .= ' /BitsPerComponent 8';
14801 $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
14802 $out .= ' /BitsPerFlag 8';
14803 $stream = $this->_getrawstream($grad['stream']);
14804 $out .= ' /Length '.strlen($stream);
14806 $out .= ' stream'."\n".$stream."\n".'endstream';
14808 $out .= "\n".'endobj';
14810 if ($grad['transparency']) {
14811 $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
14812 $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
14814 $this->gradients
[$id]['id'] = $this->n
;
14815 // set pattern object
14817 $out = '<< /Type /Pattern /PatternType 2';
14818 $out .= ' /Shading '.$this->gradients
[$id]['id'].' 0 R';
14820 $out .= "\n".'endobj';
14822 $this->gradients
[$id]['pattern'] = $this->n
;
14823 // set shading and pattern for transparency mask
14824 if ($grad['transparency']) {
14825 // luminosity pattern
14826 $idgs = $id +
$idt;
14828 $this->_out($shading_transparency);
14829 $this->gradients
[$idgs]['id'] = $this->n
;
14831 $out = '<< /Type /Pattern /PatternType 2';
14832 $out .= ' /Shading '.$this->gradients
[$idgs]['id'].' 0 R';
14834 $out .= "\n".'endobj';
14836 $this->gradients
[$idgs]['pattern'] = $this->n
;
14837 // luminosity XObject
14838 $oid = $this->_newobj();
14839 $this->xobjects
['LX'.$oid] = array('n' => $oid);
14841 $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt
.' '.$this->hPt
.' re f Q';
14842 if ($this->compress
) {
14843 $filter = ' /Filter /FlateDecode';
14844 $stream = gzcompress($stream);
14846 $stream = $this->_getrawstream($stream);
14847 $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
14848 $out .= ' /Length '.strlen($stream);
14849 $rect = sprintf('%F %F', $this->wPt
, $this->hPt
);
14850 $out .= ' /BBox [0 0 '.$rect.']';
14851 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
14852 $out .= ' /Resources <<';
14853 $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
14854 $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients
[$idgs]['pattern'].' 0 R >>';
14857 $out .= ' stream'."\n".$stream."\n".'endstream';
14858 $out .= "\n".'endobj';
14862 $out = '<< /Type /Mask /S /Luminosity /G '.($this->n
- 1).' 0 R >>'."\n".'endobj';
14866 $out = '<< /Type /ExtGState /SMask '.($this->n
- 1).' 0 R /AIS false >>'."\n".'endobj';
14868 $this->extgstates
[] = array('n' => $this->n
, 'name' => 'TGS'.$id);
14874 * Draw the sector of a circle.
14875 * It can be used for instance to render pie charts.
14876 * @param $xc (float) abscissa of the center.
14877 * @param $yc (float) ordinate of the center.
14878 * @param $r (float) radius.
14879 * @param $a (float) start angle (in degrees).
14880 * @param $b (float) end angle (in degrees).
14881 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
14882 * @param $cw: (float) indicates whether to go clockwise (default: true).
14883 * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). Default: 90.
14884 * @author Maxime Delorme, Nicola Asuni
14885 * @since 3.1.000 (2008-06-09)
14888 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
14889 $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
14893 * Draw the sector of an ellipse.
14894 * It can be used for instance to render pie charts.
14895 * @param $xc (float) abscissa of the center.
14896 * @param $yc (float) ordinate of the center.
14897 * @param $rx (float) the x-axis radius.
14898 * @param $ry (float) the y-axis radius.
14899 * @param $a (float) start angle (in degrees).
14900 * @param $b (float) end angle (in degrees).
14901 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
14902 * @param $cw: (float) indicates whether to go clockwise.
14903 * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock).
14904 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of arc.
14905 * @author Maxime Delorme, Nicola Asuni
14906 * @since 3.1.000 (2008-06-09)
14909 public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
14910 if ($this->state
!= 2) {
14914 $xc = ($this->w
- $xc);
14916 $op = TCPDF_STATIC
::getPathPaintOperator($style);
14918 $line_style = array();
14922 $b = (360 - $a +
$o);
14923 $a = (360 - $d +
$o);
14928 $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
14933 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
14934 * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
14935 * Only vector drawing is supported, not text or bitmap.
14936 * Although the script was successfully tested with various AI format versions, best results are probably achieved with files that were exported in the AI3 format (tested with Illustrator CS2, Freehand MX and Photoshop CS2).
14937 * @param $file (string) Name of the file containing the image or a '@' character followed by the EPS/AI data string.
14938 * @param $x (float) Abscissa of the upper-left corner.
14939 * @param $y (float) Ordinate of the upper-left corner.
14940 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
14941 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
14942 * @param $link (mixed) URL or identifier returned by AddLink().
14943 * @param $useBoundingBox (boolean) specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
14944 * @param $align (string) Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
14945 * @param $palign (string) Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
14946 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
14947 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
14948 * @param $fixoutvals (boolean) if true remove values outside the bounding box.
14949 * @author Valentin Schmidt, Nicola Asuni
14950 * @since 3.1.000 (2008-06-09)
14953 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
14954 if ($this->state
!= 2) {
14957 if ($this->rasterize_vector_images
AND ($w > 0) AND ($h > 0)) {
14958 // convert EPS to raster image using GD or ImageMagick libraries
14959 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14967 // check page for no-write regions and adapt page margins if necessary
14968 list($x, $y) = $this->checkPageRegions($h, $x, $y);
14970 if ($file[0] === '@') { // image from string
14971 $data = substr($file, 1);
14972 } else { // EPS/AI file
14973 $data = TCPDF_STATIC
::fileGetContents($file);
14975 if ($data === FALSE) {
14976 $this->Error('EPS file not found: '.$file);
14979 // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
14980 preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
14981 if (count($regs) > 1) {
14982 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
14983 if (strpos($version_str, 'Adobe Illustrator') !== false) {
14984 $versexp = explode(' ', $version_str);
14985 $version = (float)array_pop($versexp);
14986 if ($version >= 9) {
14987 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
14991 // strip binary bytes in front of PS-header
14992 $start = strpos($data, '%!PS-Adobe');
14994 $data = substr($data, $start);
14996 // find BoundingBox params
14997 preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
14998 if (count($regs) > 1) {
14999 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
15001 $this->Error('No BoundingBox found in EPS/AI file: '.$file);
15003 $start = strpos($data, '%%EndSetup');
15004 if ($start === false) {
15005 $start = strpos($data, '%%EndProlog');
15007 if ($start === false) {
15008 $start = strpos($data, '%%BoundingBox');
15010 $data = substr($data, $start);
15011 $end = strpos($data, '%%PageTrailer');
15012 if ($end===false) {
15013 $end = strpos($data, 'showpage');
15016 $data = substr($data, 0, $end);
15018 // calculate image width and height on document
15019 if (($w <= 0) AND ($h <= 0)) {
15020 $w = ($x2 - $x1) / $k;
15021 $h = ($y2 - $y1) / $k;
15022 } elseif ($w <= 0) {
15023 $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
15024 } elseif ($h <= 0) {
15025 $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
15027 // fit the image on available space
15028 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
15029 if ($this->rasterize_vector_images
) {
15030 // convert EPS to raster image using GD or ImageMagick libraries
15031 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
15033 // set scaling factors
15034 $scale_x = $w / (($x2 - $x1) / $k);
15035 $scale_y = $h / (($y2 - $y1) / $k);
15037 $this->img_rb_y
= $y +
$h;
15040 if ($palign == 'L') {
15041 $ximg = $this->lMargin
;
15042 } elseif ($palign == 'C') {
15043 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15044 } elseif ($palign == 'R') {
15045 $ximg = $this->w
- $this->rMargin
- $w;
15049 $this->img_rb_x
= $ximg;
15051 if ($palign == 'L') {
15052 $ximg = $this->lMargin
;
15053 } elseif ($palign == 'C') {
15054 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15055 } elseif ($palign == 'R') {
15056 $ximg = $this->w
- $this->rMargin
- $w;
15060 $this->img_rb_x
= $ximg +
$w;
15062 if ($useBoundingBox) {
15063 $dx = $ximg * $k - $x1;
15064 $dy = $y * $k - $y1;
15069 // save the current graphic state
15070 $this->_out('q'.$this->epsmarker
);
15072 $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy +
($this->hPt
- (2 * $y * $k) - ($y2 - $y1))));
15074 if (isset($scale_x)) {
15075 $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
15077 // handle pc/unix/mac line endings
15078 $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY
);
15080 $cnt = count($lines);
15081 for ($i=0; $i < $cnt; ++
$i) {
15082 $line = $lines[$i];
15083 if (($line == '') OR ($line[0] == '%')) {
15086 $len = strlen($line);
15087 // check for spot color names
15089 if (strcasecmp('x', substr(trim($line), -1)) == 0) {
15090 if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) {
15091 // extract spot color name
15092 $color_name = $matches[0];
15093 // remove color name from string
15094 $line = str_replace(' '.$color_name, '', $line);
15095 // remove pharentesis from color name
15096 $color_name = substr($color_name, 1, -1);
15099 $chunks = explode(' ', $line);
15100 $cmd = trim(array_pop($chunks));
15102 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
15103 $b = array_pop($chunks);
15104 $g = array_pop($chunks);
15105 $r = array_pop($chunks);
15106 $this->_out(''.$r.' '.$g.' '.$b.' '.($cmd=='Xa'?
'rg':'RG')); //substr($line, 0, -2).'rg' -> in EPS (AI8): c m y k r g b rg!
15111 // check for values outside the bounding box
15116 // skip values outside bounding box
15117 foreach ($chunks as $key => $val) {
15118 if ((($key %
2) == 0) AND (($val < $x1) OR ($val > $x2))) {
15120 } elseif ((($key %
2) != 0) AND (($val < $y1) OR ($val > $y2))) {
15148 $this->_out($line);
15151 case 'x': {// custom fill color
15152 if (empty($color_name)) {
15154 list($col_c, $col_m, $col_y, $col_k) = $chunks;
15155 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
15157 // Spot Color (CMYK + tint)
15158 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15159 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15160 $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors
[$color_name]['i'], (1 - $col_t));
15161 $this->_out($color_cmd);
15165 case 'X': { // custom stroke color
15166 if (empty($color_name)) {
15168 list($col_c, $col_m, $col_y, $col_k) = $chunks;
15169 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
15171 // Spot Color (CMYK + tint)
15172 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15173 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15174 $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors
[$color_name]['i'], (1 - $col_t));
15175 $this->_out($color_cmd);
15187 $line[($len - 1)] = strtolower($cmd);
15188 $this->_out($line);
15193 $this->_out($cmd . '*');
15200 $max = min(($i +
5), $cnt);
15201 for ($j = ($i +
1); $j < $max; ++
$j) {
15202 $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
15222 // restore previous graphic state
15223 $this->_out($this->epsmarker
.'Q');
15224 if (!empty($border)) {
15232 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
15237 $this->Link($ximg, $y, $w, $h, $link, 0);
15239 // set pointer to align the next text/objects
15243 $this->x
= $this->img_rb_x
;
15247 $this->y
= $y +
round($h/2);
15248 $this->x
= $this->img_rb_x
;
15252 $this->y
= $this->img_rb_y
;
15253 $this->x
= $this->img_rb_x
;
15257 $this->SetY($this->img_rb_y
);
15264 $this->endlinex
= $this->img_rb_x
;
15268 * Set document barcode.
15269 * @param $bc (string) barcode
15272 public function setBarcode($bc='') {
15273 $this->barcode
= $bc;
15277 * Get current barcode.
15280 * @since 4.0.012 (2008-07-24)
15282 public function getBarcode() {
15283 return $this->barcode
;
15287 * Print a Linear Barcode.
15288 * @param $code (string) code to print
15289 * @param $type (string) type of barcode (see tcpdf_barcodes_1d.php for supported formats).
15290 * @param $x (int) x position in user units (empty string = current x position)
15291 * @param $y (int) y position in user units (empty string = current y position)
15292 * @param $w (int) width in user units (empty string = remaining page width)
15293 * @param $h (int) height in user units (empty string = remaining page height)
15294 * @param $xres (float) width of the smallest bar in user units (empty string = default value = 0.4mm)
15295 * @param $style (array) array of options:<ul>
15296 * <li>boolean $style['border'] if true prints a border</li>
15297 * <li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li>
15298 * <li>int $style['hpadding'] horizontal padding in user units (set to 'auto' for automatic padding)</li>
15299 * <li>int $style['vpadding'] vertical padding in user units (set to 'auto' for automatic padding)</li>
15300 * <li>array $style['fgcolor'] color array for bars and text</li>
15301 * <li>mixed $style['bgcolor'] color array for background (set to false for transparent)</li>
15302 * <li>boolean $style['text'] if true prints text below the barcode</li>
15303 * <li>string $style['label'] override default label</li>
15304 * <li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li>
15305 * <li>int $style['stretchtext']: 0 = disabled; 1 = horizontal scaling only if necessary; 2 = forced horizontal scaling; 3 = character spacing only if necessary; 4 = forced character spacing.</li>
15306 * <li>string $style['position'] horizontal position of the containing barcode cell on the page: L = left margin; C = center; R = right margin.</li>
15307 * <li>string $style['align'] horizontal position of the barcode on the containing rectangle: L = left; C = center; R = right.</li>
15308 * <li>string $style['stretch'] if true stretch the barcode to best fit the available width, otherwise uses $xres resolution for a single bar.</li>
15309 * <li>string $style['fitwidth'] if true reduce the width to fit the barcode width + padding. When this option is enabled the 'stretch' option is automatically disabled.</li>
15310 * <li>string $style['cellfitalign'] this option works only when 'fitwidth' is true and 'position' is unset or empty. Set the horizontal position of the containing barcode cell inside the specified rectangle: L = left; C = center; R = right.</li></ul>
15311 * @param $align (string) Indicates the alignment of the pointer next to barcode insertion relative to barcode height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
15312 * @author Nicola Asuni
15313 * @since 3.1.000 (2008-06-09)
15316 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
15317 if (TCPDF_STATIC
::empty_string(trim($code))) {
15320 require_once(dirname(__FILE__
).'/tcpdf_barcodes_1d.php');
15321 // save current graphic settings
15322 $gvars = $this->getGraphicVars();
15323 // create new barcode object
15324 $barcodeobj = new TCPDFBarcode($code, $type);
15325 $arrcode = $barcodeobj->getBarcodeArray();
15326 if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] <= 0)) {
15327 $this->Error('Error in 1D barcode string');
15329 if ($arrcode['maxh'] <= 0) {
15330 $arrcode['maxh'] = 1;
15332 // set default values
15333 if (!isset($style['position'])) {
15334 $style['position'] = '';
15335 } elseif ($style['position'] == 'S') {
15336 // keep this for backward compatibility
15337 $style['position'] = '';
15338 $style['stretch'] = true;
15340 if (!isset($style['fitwidth'])) {
15341 if (!isset($style['stretch'])) {
15342 $style['fitwidth'] = true;
15344 $style['fitwidth'] = false;
15347 if ($style['fitwidth']) {
15349 $style['stretch'] = false;
15351 if (!isset($style['stretch'])) {
15352 if (($w === '') OR ($w <= 0)) {
15353 $style['stretch'] = false;
15355 $style['stretch'] = true;
15358 if (!isset($style['fgcolor'])) {
15359 $style['fgcolor'] = array(0,0,0); // default black
15361 if (!isset($style['bgcolor'])) {
15362 $style['bgcolor'] = false; // default transparent
15364 if (!isset($style['border'])) {
15365 $style['border'] = false;
15368 if (!isset($style['text'])) {
15369 $style['text'] = false;
15371 if ($style['text'] AND isset($style['font'])) {
15372 if (isset($style['fontsize'])) {
15373 $fontsize = $style['fontsize'];
15375 $this->SetFont($style['font'], '', $fontsize);
15377 if (!isset($style['stretchtext'])) {
15378 $style['stretchtext'] = 4;
15386 // check page for no-write regions and adapt page margins if necessary
15387 list($x, $y) = $this->checkPageRegions($h, $x, $y);
15388 if (($w === '') OR ($w <= 0)) {
15390 $w = $x - $this->lMargin
;
15392 $w = $this->w
- $this->rMargin
- $x;
15396 if (!isset($style['padding'])) {
15398 } elseif ($style['padding'] === 'auto') {
15399 $padding = 10 * ($w / ($arrcode['maxw'] +
20));
15401 $padding = floatval($style['padding']);
15403 // horizontal padding
15404 if (!isset($style['hpadding'])) {
15405 $hpadding = $padding;
15406 } elseif ($style['hpadding'] === 'auto') {
15407 $hpadding = 10 * ($w / ($arrcode['maxw'] +
20));
15409 $hpadding = floatval($style['hpadding']);
15411 // vertical padding
15412 if (!isset($style['vpadding'])) {
15413 $vpadding = $padding;
15414 } elseif ($style['vpadding'] === 'auto') {
15415 $vpadding = ($hpadding / 2);
15417 $vpadding = floatval($style['vpadding']);
15419 // calculate xres (single bar width)
15420 $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
15421 if ($style['stretch']) {
15424 if (TCPDF_STATIC
::empty_string($xres)) {
15425 $xres = (0.141 * $this->k
); // default bar width = 0.4 mm
15427 if ($xres > $max_xres) {
15428 // correct xres to fit on $w
15431 if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
15432 OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
15433 $hpadding = 10 * $xres;
15434 if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
15435 $vpadding = ($hpadding / 2);
15439 if ($style['fitwidth']) {
15441 $w = (($arrcode['maxw'] * $xres) +
(2 * $hpadding));
15442 if (isset($style['cellfitalign'])) {
15443 switch ($style['cellfitalign']) {
15446 $x -= ($wold - $w);
15452 $x +
= ($wold - $w);
15458 $x -= (($wold - $w) / 2);
15460 $x +
= (($wold - $w) / 2);
15470 $text_height = $this->getCellHeight($fontsize / $this->k
);
15472 if (($h === '') OR ($h <= 0)) {
15473 // set default height
15474 $h = (($arrcode['maxw'] * $xres) / 3) +
(2 * $vpadding) +
$text_height;
15476 $barh = $h - $text_height - (2 * $vpadding);
15478 // try to reduce font or padding to fit barcode on available height
15479 if ($text_height > $h) {
15480 $fontsize = (($h * $this->k
) / (4 * $this->cell_height_ratio
));
15481 $text_height = $this->getCellHeight($fontsize / $this->k
);
15482 $this->SetFont($style['font'], '', $fontsize);
15484 if ($vpadding > 0) {
15485 $vpadding = (($h - $text_height) / 4);
15487 $barh = $h - $text_height - (2 * $vpadding);
15489 // fit the barcode on available space
15490 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15492 $this->img_rb_y
= $y +
$h;
15495 if ($style['position'] == 'L') {
15496 $xpos = $this->lMargin
;
15497 } elseif ($style['position'] == 'C') {
15498 $xpos = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15499 } elseif ($style['position'] == 'R') {
15500 $xpos = $this->w
- $this->rMargin
- $w;
15504 $this->img_rb_x
= $xpos;
15506 if ($style['position'] == 'L') {
15507 $xpos = $this->lMargin
;
15508 } elseif ($style['position'] == 'C') {
15509 $xpos = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15510 } elseif ($style['position'] == 'R') {
15511 $xpos = $this->w
- $this->rMargin
- $w;
15515 $this->img_rb_x
= $xpos +
$w;
15517 $xpos_rect = $xpos;
15518 if (!isset($style['align'])) {
15519 $style['align'] = 'C';
15521 switch ($style['align']) {
15523 $xpos = $xpos_rect +
$hpadding;
15527 $xpos = $xpos_rect +
($w - ($arrcode['maxw'] * $xres)) - $hpadding;
15532 $xpos = $xpos_rect +
(($w - ($arrcode['maxw'] * $xres)) / 2);
15536 $xpos_text = $xpos;
15537 // barcode is always printed in LTR direction
15538 $tempRTL = $this->rtl
;
15539 $this->rtl
= false;
15540 // print background color
15541 if ($style['bgcolor']) {
15542 $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ?
'DF' : 'F', '', $style['bgcolor']);
15543 } elseif ($style['border']) {
15544 $this->Rect($xpos_rect, $y, $w, $h, 'D');
15546 // set foreground color
15547 $this->SetDrawColorArray($style['fgcolor']);
15548 $this->SetTextColorArray($style['fgcolor']);
15550 foreach ($arrcode['bcode'] as $k => $v) {
15551 $bw = ($v['w'] * $xres);
15553 // draw a vertical bar
15554 $ypos = $y +
$vpadding +
($v['p'] * $barh / $arrcode['maxh']);
15555 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
15560 if ($style['text']) {
15561 if (isset($style['label']) AND !TCPDF_STATIC
::empty_string($style['label'])) {
15562 $label = $style['label'];
15566 $txtwidth = ($arrcode['maxw'] * $xres);
15567 if ($this->GetStringWidth($label) > $txtwidth) {
15568 $style['stretchtext'] = 2;
15571 $this->x
= $xpos_text;
15572 $this->y
= $y +
$vpadding +
$barh;
15573 $cellpadding = $this->cell_padding
;
15574 $this->SetCellPadding(0);
15575 $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
15576 $this->cell_padding
= $cellpadding;
15578 // restore original direction
15579 $this->rtl
= $tempRTL;
15580 // restore previous settings
15581 $this->setGraphicVars($gvars);
15582 // set pointer to align the next text/objects
15586 $this->x
= $this->img_rb_x
;
15590 $this->y
= $y +
round($h / 2);
15591 $this->x
= $this->img_rb_x
;
15595 $this->y
= $this->img_rb_y
;
15596 $this->x
= $this->img_rb_x
;
15600 $this->SetY($this->img_rb_y
);
15607 $this->endlinex
= $this->img_rb_x
;
15611 * Print 2D Barcode.
15612 * @param $code (string) code to print
15613 * @param $type (string) type of barcode (see tcpdf_barcodes_2d.php for supported formats).
15614 * @param $x (int) x position in user units
15615 * @param $y (int) y position in user units
15616 * @param $w (int) width in user units
15617 * @param $h (int) height in user units
15618 * @param $style (array) array of options:<ul>
15619 * <li>boolean $style['border'] if true prints a border around the barcode</li>
15620 * <li>int $style['padding'] padding to leave around the barcode in barcode units (set to 'auto' for automatic padding)</li>
15621 * <li>int $style['hpadding'] horizontal padding in barcode units (set to 'auto' for automatic padding)</li>
15622 * <li>int $style['vpadding'] vertical padding in barcode units (set to 'auto' for automatic padding)</li>
15623 * <li>int $style['module_width'] width of a single module in points</li>
15624 * <li>int $style['module_height'] height of a single module in points</li>
15625 * <li>array $style['fgcolor'] color array for bars and text</li>
15626 * <li>mixed $style['bgcolor'] color array for background or false for transparent</li>
15627 * <li>string $style['position'] barcode position on the page: L = left margin; C = center; R = right margin; S = stretch</li><li>$style['module_width'] width of a single module in points</li>
15628 * <li>$style['module_height'] height of a single module in points</li></ul>
15629 * @param $align (string) Indicates the alignment of the pointer next to barcode insertion relative to barcode height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
15630 * @param $distort (boolean) if true distort the barcode to fit width and height, otherwise preserve aspect ratio
15631 * @author Nicola Asuni
15632 * @since 4.5.037 (2009-04-07)
15635 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) {
15636 if (TCPDF_STATIC
::empty_string(trim($code))) {
15639 require_once(dirname(__FILE__
).'/tcpdf_barcodes_2d.php');
15640 // save current graphic settings
15641 $gvars = $this->getGraphicVars();
15642 // create new barcode object
15643 $barcodeobj = new TCPDF2DBarcode($code, $type);
15644 $arrcode = $barcodeobj->getBarcodeArray();
15645 if (($arrcode === false) OR empty($arrcode) OR !isset($arrcode['num_rows']) OR ($arrcode['num_rows'] == 0) OR !isset($arrcode['num_cols']) OR ($arrcode['num_cols'] == 0)) {
15646 $this->Error('Error in 2D barcode string');
15648 // set default values
15649 if (!isset($style['position'])) {
15650 $style['position'] = '';
15652 if (!isset($style['fgcolor'])) {
15653 $style['fgcolor'] = array(0,0,0); // default black
15655 if (!isset($style['bgcolor'])) {
15656 $style['bgcolor'] = false; // default transparent
15658 if (!isset($style['border'])) {
15659 $style['border'] = false;
15662 if (!isset($style['padding'])) {
15663 $style['padding'] = 0;
15664 } elseif ($style['padding'] === 'auto') {
15665 $style['padding'] = 4;
15667 if (!isset($style['hpadding'])) {
15668 $style['hpadding'] = $style['padding'];
15669 } elseif ($style['hpadding'] === 'auto') {
15670 $style['hpadding'] = 4;
15672 if (!isset($style['vpadding'])) {
15673 $style['vpadding'] = $style['padding'];
15674 } elseif ($style['vpadding'] === 'auto') {
15675 $style['vpadding'] = 4;
15677 $hpad = (2 * $style['hpadding']);
15678 $vpad = (2 * $style['vpadding']);
15679 // cell (module) dimension
15680 if (!isset($style['module_width'])) {
15681 $style['module_width'] = 1; // width of a single module in points
15683 if (!isset($style['module_height'])) {
15684 $style['module_height'] = 1; // height of a single module in points
15692 // check page for no-write regions and adapt page margins if necessary
15693 list($x, $y) = $this->checkPageRegions($h, $x, $y);
15694 // number of barcode columns and rows
15695 $rows = $arrcode['num_rows'];
15696 $cols = $arrcode['num_cols'];
15697 if (($rows <= 0) ||
($cols <= 0)){
15698 $this->Error('Error in 2D barcode string');
15700 // module width and height
15701 $mw = $style['module_width'];
15702 $mh = $style['module_height'];
15703 if (($mw <= 0) OR ($mh <= 0)) {
15704 $this->Error('Error in 2D barcode string');
15706 // get max dimensions
15708 $maxw = $x - $this->lMargin
;
15710 $maxw = $this->w
- $this->rMargin
- $x;
15712 $maxh = ($this->h
- $this->tMargin
- $this->bMargin
);
15713 $ratioHW = ((($rows * $mh) +
$hpad) / (($cols * $mw) +
$vpad));
15714 $ratioWH = ((($cols * $mw) +
$vpad) / (($rows * $mh) +
$hpad));
15716 if (($maxw * $ratioHW) > $maxh) {
15717 $maxw = $maxh * $ratioWH;
15719 if (($maxh * $ratioWH) > $maxw) {
15720 $maxh = $maxw * $ratioHW;
15723 // set maximum dimesions
15731 if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
15732 $w = ($cols +
$hpad) * ($mw / $this->k
);
15733 $h = ($rows +
$vpad) * ($mh / $this->k
);
15734 } elseif (($w === '') OR ($w <= 0)) {
15735 $w = $h * $ratioWH;
15736 } elseif (($h === '') OR ($h <= 0)) {
15737 $h = $w * $ratioHW;
15739 // barcode size (excluding padding)
15740 $bw = ($w * $cols) / ($cols +
$hpad);
15741 $bh = ($h * $rows) / ($rows +
$vpad);
15742 // dimension of single barcode cell unit
15746 if (($cw / $ch) > ($mw / $mh)) {
15747 // correct horizontal distortion
15748 $cw = $ch * $mw / $mh;
15750 $style['hpadding'] = ($w - $bw) / (2 * $cw);
15752 // correct vertical distortion
15753 $ch = $cw * $mh / $mw;
15755 $style['vpadding'] = ($h - $bh) / (2 * $ch);
15758 // fit the barcode on available space
15759 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15761 $this->img_rb_y
= $y +
$h;
15764 if ($style['position'] == 'L') {
15765 $xpos = $this->lMargin
;
15766 } elseif ($style['position'] == 'C') {
15767 $xpos = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15768 } elseif ($style['position'] == 'R') {
15769 $xpos = $this->w
- $this->rMargin
- $w;
15773 $this->img_rb_x
= $xpos;
15775 if ($style['position'] == 'L') {
15776 $xpos = $this->lMargin
;
15777 } elseif ($style['position'] == 'C') {
15778 $xpos = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15779 } elseif ($style['position'] == 'R') {
15780 $xpos = $this->w
- $this->rMargin
- $w;
15784 $this->img_rb_x
= $xpos +
$w;
15786 $xstart = $xpos +
($style['hpadding'] * $cw);
15787 $ystart = $y +
($style['vpadding'] * $ch);
15788 // barcode is always printed in LTR direction
15789 $tempRTL = $this->rtl
;
15790 $this->rtl
= false;
15791 // print background color
15792 if ($style['bgcolor']) {
15793 $this->Rect($xpos, $y, $w, $h, $style['border'] ?
'DF' : 'F', '', $style['bgcolor']);
15794 } elseif ($style['border']) {
15795 $this->Rect($xpos, $y, $w, $h, 'D');
15797 // set foreground color
15798 $this->SetDrawColorArray($style['fgcolor']);
15799 // print barcode cells
15801 for ($r = 0; $r < $rows; ++
$r) {
15804 for ($c = 0; $c < $cols; ++
$c) {
15805 if ($arrcode['bcode'][$r][$c] == 1) {
15806 // draw a single barcode cell
15807 $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
15813 // restore original direction
15814 $this->rtl
= $tempRTL;
15815 // restore previous settings
15816 $this->setGraphicVars($gvars);
15817 // set pointer to align the next text/objects
15821 $this->x
= $this->img_rb_x
;
15825 $this->y
= $y +
round($h/2);
15826 $this->x
= $this->img_rb_x
;
15830 $this->y
= $this->img_rb_y
;
15831 $this->x
= $this->img_rb_x
;
15835 $this->SetY($this->img_rb_y
);
15842 $this->endlinex
= $this->img_rb_x
;
15846 * Returns an array containing current margins:
15848 <li>$ret['left'] = left margin</li>
15849 <li>$ret['right'] = right margin</li>
15850 <li>$ret['top'] = top margin</li>
15851 <li>$ret['bottom'] = bottom margin</li>
15852 <li>$ret['header'] = header margin</li>
15853 <li>$ret['footer'] = footer margin</li>
15854 <li>$ret['cell'] = cell padding array</li>
15855 <li>$ret['padding_left'] = cell left padding</li>
15856 <li>$ret['padding_top'] = cell top padding</li>
15857 <li>$ret['padding_right'] = cell right padding</li>
15858 <li>$ret['padding_bottom'] = cell bottom padding</li>
15860 * @return array containing all margins measures
15862 * @since 3.2.000 (2008-06-23)
15864 public function getMargins() {
15866 'left' => $this->lMargin
,
15867 'right' => $this->rMargin
,
15868 'top' => $this->tMargin
,
15869 'bottom' => $this->bMargin
,
15870 'header' => $this->header_margin
,
15871 'footer' => $this->footer_margin
,
15872 'cell' => $this->cell_padding
,
15873 'padding_left' => $this->cell_padding
['L'],
15874 'padding_top' => $this->cell_padding
['T'],
15875 'padding_right' => $this->cell_padding
['R'],
15876 'padding_bottom' => $this->cell_padding
['B']
15882 * Returns an array containing original margins:
15884 <li>$ret['left'] = left margin</li>
15885 <li>$ret['right'] = right margin</li>
15887 * @return array containing all margins measures
15889 * @since 4.0.012 (2008-07-24)
15891 public function getOriginalMargins() {
15893 'left' => $this->original_lMargin
,
15894 'right' => $this->original_rMargin
15900 * Returns the current font size.
15901 * @return current font size
15903 * @since 3.2.000 (2008-06-23)
15905 public function getFontSize() {
15906 return $this->FontSize
;
15910 * Returns the current font size in points unit.
15911 * @return current font size in points unit
15913 * @since 3.2.000 (2008-06-23)
15915 public function getFontSizePt() {
15916 return $this->FontSizePt
;
15920 * Returns the current font family name.
15921 * @return string current font family name
15923 * @since 4.3.008 (2008-12-05)
15925 public function getFontFamily() {
15926 return $this->FontFamily
;
15930 * Returns the current font style.
15931 * @return string current font style
15933 * @since 4.3.008 (2008-12-05)
15935 public function getFontStyle() {
15936 return $this->FontStyle
;
15940 * Cleanup HTML code (requires HTML Tidy library).
15941 * @param $html (string) htmlcode to fix
15942 * @param $default_css (string) CSS commands to add
15943 * @param $tagvs (array) parameters for setHtmlVSpace method
15944 * @param $tidy_options (array) options for tidy_parse_string function
15945 * @return string XHTML code cleaned up
15946 * @author Nicola Asuni
15948 * @since 5.9.017 (2010-11-16)
15949 * @see setHtmlVSpace()
15951 public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
15952 return TCPDF_STATIC
::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces
);
15956 * Returns the border width from CSS property
15957 * @param $width (string) border width
15958 * @return int with in user units
15960 * @since 5.7.000 (2010-08-02)
15962 protected function getCSSBorderWidth($width) {
15963 if ($width == 'thin') {
15964 $width = (2 / $this->k
);
15965 } elseif ($width == 'medium') {
15966 $width = (4 / $this->k
);
15967 } elseif ($width == 'thick') {
15968 $width = (6 / $this->k
);
15970 $width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
15976 * Returns the border dash style from CSS property
15977 * @param $style (string) border style to convert
15978 * @return int sash style (return -1 in case of none or hidden border)
15980 * @since 5.7.000 (2010-08-02)
15982 protected function getCSSBorderDashStyle($style) {
15983 switch (strtolower($style)) {
16012 * Returns the border style array from CSS border properties
16013 * @param $cssborder (string) border properties
16014 * @return array containing border properties
16016 * @since 5.7.000 (2010-08-02)
16018 protected function getCSSBorderStyle($cssborder) {
16019 $bprop = preg_split('/[\s]+/', trim($cssborder));
16020 $border = array(); // value to be returned
16021 switch (count($bprop)) {
16023 $width = $bprop[0];
16024 $style = $bprop[1];
16025 $color = $bprop[2];
16030 $style = $bprop[0];
16031 $color = $bprop[1];
16036 $style = $bprop[0];
16047 if ($style == 'none') {
16050 $border['cap'] = 'square';
16051 $border['join'] = 'miter';
16052 $border['dash'] = $this->getCSSBorderDashStyle($style);
16053 if ($border['dash'] < 0) {
16056 $border['width'] = $this->getCSSBorderWidth($width);
16057 $border['color'] = TCPDF_COLORS
::convertHTMLColorToDec($color, $this->spot_colors
);
16062 * Get the internal Cell padding from CSS attribute.
16063 * @param $csspadding (string) padding properties
16064 * @param $width (float) width of the containing element
16065 * @return array of cell paddings
16067 * @since 5.9.000 (2010-10-04)
16069 public function getCSSPadding($csspadding, $width=0) {
16070 $padding = preg_split('/[\s]+/', trim($csspadding));
16071 $cell_padding = array(); // value to be returned
16072 switch (count($padding)) {
16074 $cell_padding['T'] = $padding[0];
16075 $cell_padding['R'] = $padding[1];
16076 $cell_padding['B'] = $padding[2];
16077 $cell_padding['L'] = $padding[3];
16081 $cell_padding['T'] = $padding[0];
16082 $cell_padding['R'] = $padding[1];
16083 $cell_padding['B'] = $padding[2];
16084 $cell_padding['L'] = $padding[1];
16088 $cell_padding['T'] = $padding[0];
16089 $cell_padding['R'] = $padding[1];
16090 $cell_padding['B'] = $padding[0];
16091 $cell_padding['L'] = $padding[1];
16095 $cell_padding['T'] = $padding[0];
16096 $cell_padding['R'] = $padding[0];
16097 $cell_padding['B'] = $padding[0];
16098 $cell_padding['L'] = $padding[0];
16102 return $this->cell_padding
;
16106 $width = $this->w
- $this->lMargin
- $this->rMargin
;
16108 $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
16109 $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
16110 $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
16111 $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
16112 return $cell_padding;
16116 * Get the internal Cell margin from CSS attribute.
16117 * @param $cssmargin (string) margin properties
16118 * @param $width (float) width of the containing element
16119 * @return array of cell margins
16121 * @since 5.9.000 (2010-10-04)
16123 public function getCSSMargin($cssmargin, $width=0) {
16124 $margin = preg_split('/[\s]+/', trim($cssmargin));
16125 $cell_margin = array(); // value to be returned
16126 switch (count($margin)) {
16128 $cell_margin['T'] = $margin[0];
16129 $cell_margin['R'] = $margin[1];
16130 $cell_margin['B'] = $margin[2];
16131 $cell_margin['L'] = $margin[3];
16135 $cell_margin['T'] = $margin[0];
16136 $cell_margin['R'] = $margin[1];
16137 $cell_margin['B'] = $margin[2];
16138 $cell_margin['L'] = $margin[1];
16142 $cell_margin['T'] = $margin[0];
16143 $cell_margin['R'] = $margin[1];
16144 $cell_margin['B'] = $margin[0];
16145 $cell_margin['L'] = $margin[1];
16149 $cell_margin['T'] = $margin[0];
16150 $cell_margin['R'] = $margin[0];
16151 $cell_margin['B'] = $margin[0];
16152 $cell_margin['L'] = $margin[0];
16156 return $this->cell_margin
;
16160 $width = $this->w
- $this->lMargin
- $this->rMargin
;
16162 $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
16163 $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
16164 $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
16165 $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
16166 return $cell_margin;
16170 * Get the border-spacing from CSS attribute.
16171 * @param $cssbspace (string) border-spacing CSS properties
16172 * @param $width (float) width of the containing element
16173 * @return array of border spacings
16175 * @since 5.9.010 (2010-10-27)
16177 public function getCSSBorderMargin($cssbspace, $width=0) {
16178 $space = preg_split('/[\s]+/', trim($cssbspace));
16179 $border_spacing = array(); // value to be returned
16180 switch (count($space)) {
16182 $border_spacing['H'] = $space[0];
16183 $border_spacing['V'] = $space[1];
16187 $border_spacing['H'] = $space[0];
16188 $border_spacing['V'] = $space[0];
16192 return array('H' => 0, 'V' => 0);
16196 $width = $this->w
- $this->lMargin
- $this->rMargin
;
16198 $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
16199 $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
16200 return $border_spacing;
16204 * Returns the letter-spacing value from CSS value
16205 * @param $spacing (string) letter-spacing value
16206 * @param $parent (float) font spacing (tracking) value of the parent element
16207 * @return float quantity to increases or decreases the space between characters in a text.
16209 * @since 5.9.000 (2010-10-02)
16211 protected function getCSSFontSpacing($spacing, $parent=0) {
16212 $val = 0; // value to be returned
16213 $spacing = trim($spacing);
16214 switch ($spacing) {
16220 if ($parent == 'normal') {
16228 $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
16235 * Returns the percentage of font stretching from CSS value
16236 * @param $stretch (string) stretch mode
16237 * @param $parent (float) stretch value of the parent element
16238 * @return float font stretching percentage
16240 * @since 5.9.000 (2010-10-02)
16242 protected function getCSSFontStretching($stretch, $parent=100) {
16243 $val = 100; // value to be returned
16244 $stretch = trim($stretch);
16245 switch ($stretch) {
16246 case 'ultra-condensed': {
16250 case 'extra-condensed': {
16254 case 'condensed': {
16258 case 'semi-condensed': {
16266 case 'semi-expanded': {
16274 case 'extra-expanded': {
16278 case 'ultra-expanded': {
16283 $val = ($parent +
10);
16287 $val = ($parent - 10);
16291 if ($parent == 'normal') {
16299 $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
16306 * Convert HTML string containing font size value to points
16307 * @param $val (string) String containing font size value and unit.
16308 * @param $refsize (float) Reference font size in points.
16309 * @param $parent_size (float) Parent font size in points.
16310 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
16311 * @return float value in points
16314 public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') {
16315 $refsize = TCPDF_FONTS
::getFontRefSize($refsize);
16316 $parent_size = TCPDF_FONTS
::getFontRefSize($parent_size, $refsize);
16319 $size = ($refsize - 4);
16323 $size = ($refsize - 3);
16327 $size = ($refsize - 2);
16335 $size = ($refsize +
2);
16339 $size = ($refsize +
4);
16343 $size = ($refsize +
6);
16347 $size = ($parent_size - 3);
16351 $size = ($parent_size +
3);
16355 $size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true);
16362 * Returns the HTML DOM array.
16363 * @param $html (string) html code
16366 * @since 3.2.000 (2008-06-20)
16368 protected function getHtmlDomArray($html) {
16369 // array of CSS styles ( selector => properties).
16371 // get CSS array defined at previous call
16372 $matches = array();
16373 if (preg_match_all('/<cssarray>([^\<]*)<\/cssarray>/isU', $html, $matches) > 0) {
16374 if (isset($matches[1][0])) {
16375 $css = array_merge($css, json_decode($this->unhtmlentities($matches[1][0]), true));
16377 $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
16379 // extract external CSS files
16380 $matches = array();
16381 if (preg_match_all('/<link([^\>]*)>/isU', $html, $matches) > 0) {
16382 foreach ($matches[1] as $key => $link) {
16384 if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
16386 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
16387 // get 'all' and 'print' media, other media types are discarded
16388 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16389 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16391 if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
16392 // read CSS data file
16393 $cssdata = TCPDF_STATIC
::fileGetContents(trim($type[1]));
16394 if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) {
16395 $css = array_merge($css, TCPDF_STATIC
::extractCSSproperties($cssdata));
16402 // extract style tags
16403 $matches = array();
16404 if (preg_match_all('/<style([^\>]*)>([^\<]*)<\/style>/isU', $html, $matches) > 0) {
16405 foreach ($matches[1] as $key => $media) {
16407 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
16408 // get 'all' and 'print' media, other media types are discarded
16409 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16410 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16411 $cssdata = $matches[2][$key];
16412 $css = array_merge($css, TCPDF_STATIC
::extractCSSproperties($cssdata));
16416 // create a special tag to contain the CSS array (used for table content)
16417 $csstagarray = '<cssarray>'.htmlentities(json_encode($css)).'</cssarray>';
16418 // remove head and style blocks
16419 $html = preg_replace('/<head([^\>]*)>(.*?)<\/head>/siU', '', $html);
16420 $html = preg_replace('/<style([^\>]*)>([^\<]*)<\/style>/isU', '', $html);
16421 // define block tags
16422 $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
16423 // define self-closing tags
16424 $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
16425 // remove all unsupported tags (the line below lists all supported tags)
16426 $html = strip_tags($html, '<marker/><a><b><blockquote><body><br><br/><dd><del><div><dl><dt><em><font><form><h1><h2><h3><h4><h5><h6><hr><hr/><i><img><input><label><li><ol><option><p><pre><s><select><small><span><strike><strong><sub><sup><table><tablehead><tcpdf><td><textarea><th><thead><tr><tt><u><ul>');
16427 //replace some blank characters
16428 $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
16429 $html = preg_replace('/<(table|tr|td|th|tcpdf|blockquote|dd|div|dl|dt|form|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^\>]*)>[\n\r\t]+/', '<\\1\\2>', $html);
16430 $html = preg_replace('@(\r\n|\r)@', "\n", $html);
16431 $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
16432 $html = strtr($html, $repTable);
16434 while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
16435 $html_a = substr($html, 0, $offset);
16436 $html_b = substr($html, $offset, ($pos - $offset +
6));
16437 while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
16438 // preserve newlines on <pre> tag
16439 $html_b = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
16441 while (preg_match("'<xre([^\>]*)>(.*?)".$this->re_space
['p']."(.*?)</pre>'".$this->re_space
['m'], $html_b)) {
16442 // preserve spaces on <pre> tag
16443 $html_b = preg_replace("'<xre([^\>]*)>(.*?)".$this->re_space
['p']."(.*?)</pre>'".$this->re_space
['m'], "<xre\\1>\\2 \\3</pre>", $html_b);
16445 $html = $html_a.$html_b.substr($html, $pos +
6);
16446 $offset = strlen($html_a.$html_b);
16449 while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
16450 $html_a = substr($html, 0, $offset);
16451 $html_b = substr($html, $offset, ($pos - $offset +
11));
16452 while (preg_match("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
16453 // preserve newlines on <textarea> tag
16454 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
16455 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
16457 $html = $html_a.$html_b.substr($html, $pos +
11);
16458 $offset = strlen($html_a.$html_b);
16460 $html = preg_replace('/([\s]*)<option/si', '<option', $html);
16461 $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
16463 while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
16464 $html_a = substr($html, 0, $offset);
16465 $html_b = substr($html, $offset, ($pos - $offset +
9));
16466 while (preg_match("'<option([^\>]*)>(.*?)</option>'si", $html_b)) {
16467 $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
16468 $html_b = preg_replace("'<option([^\>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
16470 $html = $html_a.$html_b.substr($html, $pos +
9);
16471 $offset = strlen($html_a.$html_b);
16473 if (preg_match("'</select'si", $html)) {
16474 $html = preg_replace("'<select([^\>]*)>'si", "<select\\1 opt=\"", $html);
16475 $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
16477 $html = str_replace("\n", ' ', $html);
16478 // restore textarea newlines
16479 $html = str_replace('<TBR>', "\n", $html);
16480 // remove extra spaces from code
16481 $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
16482 $html = preg_replace('/'.$this->re_space
['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space
['m'], '</\\1>', $html);
16483 $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
16484 $html = preg_replace('/'.$this->re_space
['p'].'+<(ul|ol|dl|br)/'.$this->re_space
['m'], '<\\1', $html);
16485 $html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|dt|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html);
16486 $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
16487 $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
16488 $html = preg_replace('/'.$this->re_space
['p'].'+<img/'.$this->re_space
['m'], chr(32).'<img', $html);
16489 $html = preg_replace('/<img([^\>]*)>[\s]+([^\<])/xi', '<img\\1> \\2', $html);
16490 $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
16491 $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
16492 $html = preg_replace('/<textarea([^\>]*)>([^\<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
16493 $html = preg_replace('/<li([^\>]*)><\/li>/', '<li\\1> </li>', $html);
16494 $html = preg_replace('/<li([^\>]*)>'.$this->re_space
['p'].'*<img/'.$this->re_space
['m'], '<li\\1><font size="1"> </font><img', $html);
16495 $html = preg_replace('/<([^\>\/]*)>[\s]/', '<\\1> ', $html); // preserve some spaces
16496 $html = preg_replace('/[\s]<\/([^\>]*)>/', ' </\\1>', $html); // preserve some spaces
16497 $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment
16498 $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment
16499 $html = preg_replace('/'.$this->re_space
['p'].'+/'.$this->re_space
['m'], chr(32), $html); // replace multiple spaces with a single space
16501 $html = $this->stringTrim($html);
16502 // fix br tag after li
16503 $html = preg_replace('/<li><br([^\>]*)>/', '<li> <br\\1>', $html);
16504 // fix first image tag alignment
16505 $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
16506 // pattern for generic tag
16507 $tagpattern = '/(<[^>]+>)/';
16508 // explodes the string
16509 $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
);
16511 $maxel = count($a);
16514 // create an array of elements
16516 $dom[$key] = array();
16517 // set inheritable properties fot the first void element
16518 // possible inheritable properties are: azimuth, border-collapse, border-spacing, caption-side, color, cursor, direction, empty-cells, font, font-family, font-stretch, font-size, font-size-adjust, font-style, font-variant, font-weight, letter-spacing, line-height, list-style, list-style-image, list-style-position, list-style-type, orphans, page, page-break-inside, quotes, speak, speak-header, text-align, text-indent, text-transform, volume, white-space, widows, word-spacing
16519 $dom[$key]['tag'] = false;
16520 $dom[$key]['block'] = false;
16521 $dom[$key]['value'] = '';
16522 $dom[$key]['parent'] = 0;
16523 $dom[$key]['hide'] = false;
16524 $dom[$key]['fontname'] = $this->FontFamily
;
16525 $dom[$key]['fontstyle'] = $this->FontStyle
;
16526 $dom[$key]['fontsize'] = $this->FontSizePt
;
16527 $dom[$key]['font-stretch'] = $this->font_stretching
;
16528 $dom[$key]['letter-spacing'] = $this->font_spacing
;
16529 $dom[$key]['stroke'] = $this->textstrokewidth
;
16530 $dom[$key]['fill'] = (($this->textrendermode %
2) == 0);
16531 $dom[$key]['clip'] = ($this->textrendermode
> 3);
16532 $dom[$key]['line-height'] = $this->cell_height_ratio
;
16533 $dom[$key]['bgcolor'] = false;
16534 $dom[$key]['fgcolor'] = $this->fgcolor
; // color
16535 $dom[$key]['strokecolor'] = $this->strokecolor
;
16536 $dom[$key]['align'] = '';
16537 $dom[$key]['listtype'] = '';
16538 $dom[$key]['text-indent'] = 0;
16539 $dom[$key]['text-transform'] = '';
16540 $dom[$key]['border'] = array();
16541 $dom[$key]['dir'] = $this->rtl?
'rtl':'ltr';
16542 $thead = false; // true when we are inside the THEAD tag
16545 array_push($level, 0); // root
16546 while ($elkey < $maxel) {
16547 $dom[$key] = array();
16548 $element = $a[$elkey];
16549 $dom[$key]['elkey'] = $elkey;
16550 if (preg_match($tagpattern, $element)) {
16552 $element = substr($element, 1, -1);
16554 preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
16555 $tagname = strtolower($tag[1]);
16556 // check if we are inside a table header
16557 if ($tagname == 'thead') {
16558 if ($element[0] == '/') {
16566 $dom[$key]['tag'] = true;
16567 $dom[$key]['value'] = $tagname;
16568 if (in_array($dom[$key]['value'], $blocktags)) {
16569 $dom[$key]['block'] = true;
16571 $dom[$key]['block'] = false;
16573 if ($element[0] == '/') {
16574 // *** closing html tag
16575 $dom[$key]['opening'] = false;
16576 $dom[$key]['parent'] = end($level);
16578 $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
16579 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
16580 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
16581 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
16582 $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
16583 $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
16584 $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
16585 $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
16586 $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
16587 $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
16588 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
16589 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
16590 $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
16591 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
16592 $dom[$key]['text-transform'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['text-transform'];
16593 $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
16594 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
16595 $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
16597 // set the number of columns in table tag
16598 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
16599 $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
16601 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
16602 $dom[($dom[$key]['parent'])]['content'] = $csstagarray;
16603 for ($i = ($dom[$key]['parent'] +
1); $i < $key; ++
$i) {
16604 $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
16607 // mark nested tables
16608 $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
16609 // remove thead sections from nested tables
16610 $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
16611 $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
16613 // store header rows on a new table
16614 if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
16615 if (TCPDF_STATIC
::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
16616 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
16618 for ($i = $dom[$key]['parent']; $i <= $key; ++
$i) {
16619 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
16621 if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
16622 $dom[($dom[$key]['parent'])]['attribute'] = array();
16624 // header elements must be always contained in a single page
16625 $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
16627 if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC
::empty_string($dom[($dom[$key]['parent'])]['thead']))) {
16628 // remove the nobr attributes from the table header
16629 $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
16630 $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
16633 // *** opening or self-closing html tag
16634 $dom[$key]['opening'] = true;
16635 $dom[$key]['parent'] = end($level);
16636 if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
16637 // self-closing tag
16638 $dom[$key]['self'] = true;
16641 array_push($level, $key);
16642 $dom[$key]['self'] = false;
16644 // copy some values from parent
16647 $parentkey = $dom[$key]['parent'];
16648 $dom[$key]['hide'] = $dom[$parentkey]['hide'];
16649 $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
16650 $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
16651 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
16652 $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
16653 $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
16654 $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
16655 $dom[$key]['fill'] = $dom[$parentkey]['fill'];
16656 $dom[$key]['clip'] = $dom[$parentkey]['clip'];
16657 $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16658 $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
16659 $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
16660 $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
16661 $dom[$key]['align'] = $dom[$parentkey]['align'];
16662 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16663 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16664 $dom[$key]['text-transform'] = $dom[$parentkey]['text-transform'];
16665 $dom[$key]['border'] = array();
16666 $dom[$key]['dir'] = $dom[$parentkey]['dir'];
16669 preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER
);
16670 $dom[$key]['attribute'] = array(); // reset attribute array
16671 while (list($id, $name) = each($attr_array[1])) {
16672 $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
16674 if (!empty($css)) {
16675 // merge CSS style to current style
16676 list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC
::getCSSdataArray($dom, $key, $css);
16677 $dom[$key]['attribute']['style'] = TCPDF_STATIC
::getTagStyleFromCSSarray($dom[$key]['cssdata']);
16679 // split style attributes
16680 if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
16681 // get style attributes
16682 preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER
);
16683 $dom[$key]['style'] = array(); // reset style attribute array
16684 while (list($id, $name) = each($style_array[1])) {
16685 // in case of duplicate attribute the last replace the previous
16686 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
16688 // --- get some style attributes ---
16690 if (isset($dom[$key]['style']['direction'])) {
16691 $dom[$key]['dir'] = $dom[$key]['style']['direction'];
16694 if (isset($dom[$key]['style']['display'])) {
16695 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
16698 if (isset($dom[$key]['style']['font-family'])) {
16699 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
16702 if (isset($dom[$key]['style']['list-style-type'])) {
16703 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
16704 if ($dom[$key]['listtype'] == 'inherit') {
16705 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16709 if (isset($dom[$key]['style']['text-indent'])) {
16710 $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
16711 if ($dom[$key]['text-indent'] == 'inherit') {
16712 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16716 if (isset($dom[$key]['style']['text-transform'])) {
16717 $dom[$key]['text-transform'] = $dom[$key]['style']['text-transform'];
16720 if (isset($dom[$key]['style']['font-size'])) {
16721 $fsize = trim($dom[$key]['style']['font-size']);
16722 $dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt');
16725 if (isset($dom[$key]['style']['font-stretch'])) {
16726 $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
16729 if (isset($dom[$key]['style']['letter-spacing'])) {
16730 $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
16732 // line-height (internally is the cell height ratio)
16733 if (isset($dom[$key]['style']['line-height'])) {
16734 $lineheight = trim($dom[$key]['style']['line-height']);
16735 switch ($lineheight) {
16736 // A normal line height. This is default
16738 $dom[$key]['line-height'] = $dom[0]['line-height'];
16742 $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16745 if (is_numeric($lineheight)) {
16746 // convert to percentage of font height
16747 $lineheight = ($lineheight * 100).'%';
16749 $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
16750 if (substr($lineheight, -1) !== '%') {
16751 if ($dom[$key]['fontsize'] <= 0) {
16752 $dom[$key]['line-height'] = 1;
16754 $dom[$key]['line-height'] = (($dom[$key]['line-height'] - $this->cell_padding
['T'] - $this->cell_padding
['B']) / $dom[$key]['fontsize']);
16761 if (isset($dom[$key]['style']['font-weight'])) {
16762 if (strtolower($dom[$key]['style']['font-weight'][0]) == 'n') {
16763 if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
16764 $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
16766 } elseif (strtolower($dom[$key]['style']['font-weight'][0]) == 'b') {
16767 $dom[$key]['fontstyle'] .= 'B';
16770 if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style'][0]) == 'i')) {
16771 $dom[$key]['fontstyle'] .= 'I';
16774 if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['style']['color']))) {
16775 $dom[$key]['fgcolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors
);
16776 } elseif ($dom[$key]['value'] == 'a') {
16777 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray
;
16779 // background color
16780 if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['style']['background-color']))) {
16781 $dom[$key]['bgcolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors
);
16784 if (isset($dom[$key]['style']['text-decoration'])) {
16785 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
16786 foreach ($decors as $dec) {
16788 if (!TCPDF_STATIC
::empty_string($dec)) {
16789 if ($dec[0] == 'u') {
16791 $dom[$key]['fontstyle'] .= 'U';
16792 } elseif ($dec[0] == 'l') {
16794 $dom[$key]['fontstyle'] .= 'D';
16795 } elseif ($dec[0] == 'o') {
16797 $dom[$key]['fontstyle'] .= 'O';
16801 } elseif ($dom[$key]['value'] == 'a') {
16802 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle
;
16804 // check for width attribute
16805 if (isset($dom[$key]['style']['width'])) {
16806 $dom[$key]['width'] = $dom[$key]['style']['width'];
16808 // check for height attribute
16809 if (isset($dom[$key]['style']['height'])) {
16810 $dom[$key]['height'] = $dom[$key]['style']['height'];
16812 // check for text alignment
16813 if (isset($dom[$key]['style']['text-align'])) {
16814 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align'][0]);
16816 // check for CSS border properties
16817 if (isset($dom[$key]['style']['border'])) {
16818 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
16819 if (!empty($borderstyle)) {
16820 $dom[$key]['border']['LTRB'] = $borderstyle;
16823 if (isset($dom[$key]['style']['border-color'])) {
16824 $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
16825 if (isset($brd_colors[3])) {
16826 $dom[$key]['border']['L']['color'] = TCPDF_COLORS
::convertHTMLColorToDec($brd_colors[3], $this->spot_colors
);
16828 if (isset($brd_colors[1])) {
16829 $dom[$key]['border']['R']['color'] = TCPDF_COLORS
::convertHTMLColorToDec($brd_colors[1], $this->spot_colors
);
16831 if (isset($brd_colors[0])) {
16832 $dom[$key]['border']['T']['color'] = TCPDF_COLORS
::convertHTMLColorToDec($brd_colors[0], $this->spot_colors
);
16834 if (isset($brd_colors[2])) {
16835 $dom[$key]['border']['B']['color'] = TCPDF_COLORS
::convertHTMLColorToDec($brd_colors[2], $this->spot_colors
);
16838 if (isset($dom[$key]['style']['border-width'])) {
16839 $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
16840 if (isset($brd_widths[3])) {
16841 $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
16843 if (isset($brd_widths[1])) {
16844 $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
16846 if (isset($brd_widths[0])) {
16847 $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
16849 if (isset($brd_widths[2])) {
16850 $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
16853 if (isset($dom[$key]['style']['border-style'])) {
16854 $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
16855 if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) {
16856 $dom[$key]['border']['L']['cap'] = 'square';
16857 $dom[$key]['border']['L']['join'] = 'miter';
16858 $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
16859 if ($dom[$key]['border']['L']['dash'] < 0) {
16860 $dom[$key]['border']['L'] = array();
16863 if (isset($brd_styles[1])) {
16864 $dom[$key]['border']['R']['cap'] = 'square';
16865 $dom[$key]['border']['R']['join'] = 'miter';
16866 $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
16867 if ($dom[$key]['border']['R']['dash'] < 0) {
16868 $dom[$key]['border']['R'] = array();
16871 if (isset($brd_styles[0])) {
16872 $dom[$key]['border']['T']['cap'] = 'square';
16873 $dom[$key]['border']['T']['join'] = 'miter';
16874 $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
16875 if ($dom[$key]['border']['T']['dash'] < 0) {
16876 $dom[$key]['border']['T'] = array();
16879 if (isset($brd_styles[2])) {
16880 $dom[$key]['border']['B']['cap'] = 'square';
16881 $dom[$key]['border']['B']['join'] = 'miter';
16882 $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
16883 if ($dom[$key]['border']['B']['dash'] < 0) {
16884 $dom[$key]['border']['B'] = array();
16888 $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
16889 foreach ($cellside as $bsk => $bsv) {
16890 if (isset($dom[$key]['style']['border-'.$bsv])) {
16891 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
16892 if (!empty($borderstyle)) {
16893 $dom[$key]['border'][$bsk] = $borderstyle;
16896 if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
16897 $dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors
);
16899 if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
16900 $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
16902 if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
16903 $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
16904 if ($dom[$key]['border'][$bsk]['dash'] < 0) {
16905 $dom[$key]['border'][$bsk] = array();
16909 // check for CSS padding properties
16910 if (isset($dom[$key]['style']['padding'])) {
16911 $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
16913 $dom[$key]['padding'] = $this->cell_padding
;
16915 foreach ($cellside as $psk => $psv) {
16916 if (isset($dom[$key]['style']['padding-'.$psv])) {
16917 $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
16920 // check for CSS margin properties
16921 if (isset($dom[$key]['style']['margin'])) {
16922 $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
16924 $dom[$key]['margin'] = $this->cell_margin
;
16926 foreach ($cellside as $psk => $psv) {
16927 if (isset($dom[$key]['style']['margin-'.$psv])) {
16928 $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
16931 // check for CSS border-spacing properties
16932 if (isset($dom[$key]['style']['border-spacing'])) {
16933 $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
16935 // page-break-inside
16936 if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
16937 $dom[$key]['attribute']['nobr'] = 'true';
16939 // page-break-before
16940 if (isset($dom[$key]['style']['page-break-before'])) {
16941 if ($dom[$key]['style']['page-break-before'] == 'always') {
16942 $dom[$key]['attribute']['pagebreak'] = 'true';
16943 } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
16944 $dom[$key]['attribute']['pagebreak'] = 'left';
16945 } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
16946 $dom[$key]['attribute']['pagebreak'] = 'right';
16949 // page-break-after
16950 if (isset($dom[$key]['style']['page-break-after'])) {
16951 if ($dom[$key]['style']['page-break-after'] == 'always') {
16952 $dom[$key]['attribute']['pagebreakafter'] = 'true';
16953 } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
16954 $dom[$key]['attribute']['pagebreakafter'] = 'left';
16955 } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
16956 $dom[$key]['attribute']['pagebreakafter'] = 'right';
16960 if (isset($dom[$key]['attribute']['display'])) {
16961 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
16963 if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
16964 $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
16965 if (!empty($borderstyle)) {
16966 $dom[$key]['border']['LTRB'] = $borderstyle;
16969 // check for font tag
16970 if ($dom[$key]['value'] == 'font') {
16972 if (isset($dom[$key]['attribute']['face'])) {
16973 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
16976 if (isset($dom[$key]['attribute']['size'])) {
16978 if ($dom[$key]['attribute']['size'][0] == '+') {
16979 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] +
intval(substr($dom[$key]['attribute']['size'], 1));
16980 } elseif ($dom[$key]['attribute']['size'][0] == '-') {
16981 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
16983 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16986 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16990 // force natural alignment for lists
16991 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
16992 AND (!isset($dom[$key]['align']) OR TCPDF_STATIC
::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
16994 $dom[$key]['align'] = 'R';
16996 $dom[$key]['align'] = 'L';
16999 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
17000 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
17001 $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO
;
17004 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
17005 $dom[$key]['fontstyle'] .= 'B';
17007 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
17008 $dom[$key]['fontstyle'] .= 'I';
17010 if ($dom[$key]['value'] == 'u') {
17011 $dom[$key]['fontstyle'] .= 'U';
17013 if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
17014 $dom[$key]['fontstyle'] .= 'D';
17016 if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
17017 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle
;
17019 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
17020 $dom[$key]['fontname'] = $this->default_monospaced_font
;
17022 if (!empty($dom[$key]['value']) AND ($dom[$key]['value'][0] == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
17023 // headings h1, h2, h3, h4, h5, h6
17024 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
17025 $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
17026 $dom[$key]['fontsize'] = $dom[0]['fontsize'] +
$headsize;
17028 if (!isset($dom[$key]['style']['font-weight'])) {
17029 $dom[$key]['fontstyle'] .= 'B';
17032 if (($dom[$key]['value'] == 'table')) {
17033 $dom[$key]['rows'] = 0; // number of rows
17034 $dom[$key]['trids'] = array(); // IDs of TR elements
17035 $dom[$key]['thead'] = ''; // table header rows
17037 if (($dom[$key]['value'] == 'tr')) {
17038 $dom[$key]['cols'] = 0;
17040 $dom[$key]['thead'] = true;
17041 // rows on thead block are printed as a separate table
17043 $dom[$key]['thead'] = false;
17044 // store the number of rows on table element
17045 ++
$dom[($dom[$key]['parent'])]['rows'];
17046 // store the TR elements IDs on table element
17047 array_push($dom[($dom[$key]['parent'])]['trids'], $key);
17050 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
17051 if (isset($dom[$key]['attribute']['colspan'])) {
17052 $colspan = intval($dom[$key]['attribute']['colspan']);
17056 $dom[$key]['attribute']['colspan'] = $colspan;
17057 $dom[($dom[$key]['parent'])]['cols'] +
= $colspan;
17060 if (isset($dom[$key]['attribute']['dir'])) {
17061 $dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
17063 // set foreground color attribute
17064 if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['attribute']['color']))) {
17065 $dom[$key]['fgcolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors
);
17066 } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
17067 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray
;
17069 // set background color attribute
17070 if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['attribute']['bgcolor']))) {
17071 $dom[$key]['bgcolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors
);
17073 // set stroke color attribute
17074 if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['attribute']['strokecolor']))) {
17075 $dom[$key]['strokecolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors
);
17077 // check for width attribute
17078 if (isset($dom[$key]['attribute']['width'])) {
17079 $dom[$key]['width'] = $dom[$key]['attribute']['width'];
17081 // check for height attribute
17082 if (isset($dom[$key]['attribute']['height'])) {
17083 $dom[$key]['height'] = $dom[$key]['attribute']['height'];
17085 // check for text alignment
17086 if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
17087 $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align'][0]);
17089 // check for text rendering mode (the following attributes do not exist in HTML)
17090 if (isset($dom[$key]['attribute']['stroke'])) {
17091 // font stroke width
17092 $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
17094 if (isset($dom[$key]['attribute']['fill'])) {
17096 if ($dom[$key]['attribute']['fill'] == 'true') {
17097 $dom[$key]['fill'] = true;
17099 $dom[$key]['fill'] = false;
17102 if (isset($dom[$key]['attribute']['clip'])) {
17104 if ($dom[$key]['attribute']['clip'] == 'true') {
17105 $dom[$key]['clip'] = true;
17107 $dom[$key]['clip'] = false;
17110 } // end opening tag
17113 $dom[$key]['tag'] = false;
17114 $dom[$key]['block'] = false;
17115 $dom[$key]['parent'] = end($level);
17116 $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
17117 if (!empty($dom[$dom[$key]['parent']]['text-transform'])) {
17118 // text-transform for unicode requires mb_convert_case (Multibyte String Functions)
17119 if (function_exists('mb_convert_case')) {
17120 $ttm = array('capitalize' => MB_CASE_TITLE
, 'uppercase' => MB_CASE_UPPER
, 'lowercase' => MB_CASE_LOWER
);
17121 if (isset($ttm[$dom[$dom[$key]['parent']]['text-transform']])) {
17122 $element = mb_convert_case($element, $ttm[$dom[$dom[$key]['parent']]['text-transform']], $this->encoding
);
17124 } elseif (!$this->isunicode
) {
17125 switch ($dom[$dom[$key]['parent']]['text-transform']) {
17126 case 'capitalize': {
17127 $element = ucwords(strtolower($element));
17130 case 'uppercase': {
17131 $element = strtoupper($element);
17134 case 'lowercase': {
17135 $element = strtolower($element);
17141 $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
17150 * Returns the string used to find spaces
17153 * @author Nicola Asuni
17154 * @since 4.8.024 (2010-01-15)
17156 protected function getSpaceString() {
17157 $spacestr = chr(32);
17158 if ($this->isUnicodeFont()) {
17159 $spacestr = chr(0).chr(32);
17165 * Serialize an array of parameters to be used with TCPDF tag in HTML code.
17166 * @param $pararray (array) parameters array
17167 * @return sting containing serialized data
17168 * @since 4.9.006 (2010-04-02)
17172 public function serializeTCPDFtagParameters($pararray) {
17173 return TCPDF_STATIC
::serializeTCPDFtagParameters($pararray);
17177 * Prints a cell (rectangular area) with optional borders, background color and html text string.
17178 * The upper-left corner of the cell corresponds to the current position. After the call, the current position moves to the right or to the next line.<br />
17179 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
17180 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
17181 * Supported tags are: a, b, blockquote, br, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, li, ol, p, pre, small, span, strong, sub, sup, table, tcpdf, td, th, thead, tr, tt, u, ul
17182 * NOTE: all the HTML attributes must be enclosed in double-quote.
17183 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
17184 * @param $h (float) Cell minimum height. The cell extends automatically if needed.
17185 * @param $x (float) upper-left corner X coordinate
17186 * @param $y (float) upper-left corner Y coordinate
17187 * @param $html (string) html text to print. Default value: empty string.
17188 * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul> or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
17189 * @param $ln (int) Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL language)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>
17190 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
17191 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
17192 * @param $reseth (boolean) if true reset the last cell height (default true).
17193 * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
17194 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
17195 * @see Multicell(), writeHTML()
17198 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
17199 return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
17203 * Allows to preserve some HTML formatting (limited support).<br />
17204 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
17205 * Supported tags are: a, b, blockquote, br, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, li, ol, p, pre, small, span, strong, sub, sup, table, tcpdf, td, th, thead, tr, tt, u, ul
17206 * NOTE: all the HTML attributes must be enclosed in double-quote.
17207 * @param $html (string) text to display
17208 * @param $ln (boolean) if true add a new line after text (default = true)
17209 * @param $fill (boolean) Indicates if the background must be painted (true) or transparent (false).
17210 * @param $reseth (boolean) if true reset the last cell height (default false).
17211 * @param $cell (boolean) if true add the current left (or right for RTL) padding to each Write (default false).
17212 * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
17215 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
17216 $gvars = $this->getGraphicVars();
17217 // store current values
17218 $prev_cell_margin = $this->cell_margin
;
17219 $prev_cell_padding = $this->cell_padding
;
17220 $prevPage = $this->page
;
17221 $prevlMargin = $this->lMargin
;
17222 $prevrMargin = $this->rMargin
;
17223 $curfontname = $this->FontFamily
;
17224 $curfontstyle = $this->FontStyle
;
17225 $curfontsize = $this->FontSizePt
;
17226 $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
17227 $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
17228 $curfontstretcing = $this->font_stretching
;
17229 $curfonttracking = $this->font_spacing
;
17230 $this->newline
= true;
17232 $startlinepage = $this->page
;
17233 $minstartliney = $this->y
;
17234 $maxbottomliney = 0;
17235 $startlinex = $this->x
;
17236 $startliney = $this->y
;
17240 $this_method_vars = array();
17242 $fontaligned = false;
17243 $reverse_dir = false; // true when the text direction is reversed
17244 $this->premode
= false;
17245 if ($this->inxobj
) {
17246 // we are inside an XObject template
17247 $pask = count($this->xobjects
[$this->xobjid
]['annotations']);
17248 } elseif (isset($this->PageAnnots
[$this->page
])) {
17249 $pask = count($this->PageAnnots
[$this->page
]);
17253 if ($this->inxobj
) {
17254 // we are inside an XObject template
17255 $startlinepos = strlen($this->xobjects
[$this->xobjid
]['outdata']);
17256 } elseif (!$this->InFooter
) {
17257 if (isset($this->footerlen
[$this->page
])) {
17258 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
] - $this->footerlen
[$this->page
];
17260 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
];
17262 $startlinepos = $this->footerpos
[$this->page
];
17264 // we are inside the footer
17265 $startlinepos = $this->pagelen
[$this->page
];
17270 $w = $this->x
- $this->lMargin
;
17272 $w = $this->w
- $this->rMargin
- $this->x
;
17274 $w -= ($this->cell_padding
['L'] +
$this->cell_padding
['R']);
17277 $this->x
-= $this->cell_padding
['R'];
17278 $this->lMargin +
= $this->cell_padding
['R'];
17280 $this->x +
= $this->cell_padding
['L'];
17281 $this->rMargin +
= $this->cell_padding
['L'];
17284 if ($this->customlistindent
>= 0) {
17285 $this->listindent
= $this->customlistindent
;
17287 $this->listindent
= $this->GetStringWidth('000000');
17289 $this->listindentlevel
= 0;
17290 // save previous states
17291 $prev_cell_height_ratio = $this->cell_height_ratio
;
17292 $prev_listnum = $this->listnum
;
17293 $prev_listordered = $this->listordered
;
17294 $prev_listcount = $this->listcount
;
17295 $prev_lispacer = $this->lispacer
;
17296 $this->listnum
= 0;
17297 $this->listordered
= array();
17298 $this->listcount
= array();
17299 $this->lispacer
= '';
17300 if ((TCPDF_STATIC
::empty_string($this->lasth
)) OR ($reseth)) {
17301 // reset row height
17302 $this->resetLastH();
17304 $dom = $this->getHtmlDomArray($html);
17305 $maxel = count($dom);
17307 while ($key < $maxel) {
17308 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND $dom[$key]['hide']) {
17309 // store the node key
17310 $hidden_node_key = $key;
17311 if ($dom[$key]['self']) {
17312 // skip just this self-closing tag
17315 // skip this and all children tags
17316 while (($key < $maxel) AND (!$dom[$key]['tag'] OR $dom[$key]['opening'] OR ($dom[$key]['parent'] != $hidden_node_key))) {
17317 // skip hidden objects
17323 if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
17324 // check for pagebreak
17325 if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
17326 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17327 $this->checkPageBreak($this->PageBreakTrigger +
1);
17328 $this->htmlvspace
= ($this->PageBreakTrigger +
1);
17330 if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl
) AND (($this->page %
2) == 0)) OR (($this->rtl
) AND (($this->page %
2) != 0))))
17331 OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl
) AND (($this->page %
2) != 0)) OR (($this->rtl
) AND (($this->page %
2) == 0))))) {
17332 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17333 $this->checkPageBreak($this->PageBreakTrigger +
1);
17334 $this->htmlvspace
= ($this->PageBreakTrigger +
1);
17337 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
17338 if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
17339 $dom[$key]['attribute']['nobr'] = false;
17341 // store current object
17342 $this->startTransaction();
17343 // save this method vars
17344 $this_method_vars['html'] = $html;
17345 $this_method_vars['ln'] = $ln;
17346 $this_method_vars['fill'] = $fill;
17347 $this_method_vars['reseth'] = $reseth;
17348 $this_method_vars['cell'] = $cell;
17349 $this_method_vars['align'] = $align;
17350 $this_method_vars['gvars'] = $gvars;
17351 $this_method_vars['prevPage'] = $prevPage;
17352 $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
17353 $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
17354 $this_method_vars['prevlMargin'] = $prevlMargin;
17355 $this_method_vars['prevrMargin'] = $prevrMargin;
17356 $this_method_vars['curfontname'] = $curfontname;
17357 $this_method_vars['curfontstyle'] = $curfontstyle;
17358 $this_method_vars['curfontsize'] = $curfontsize;
17359 $this_method_vars['curfontascent'] = $curfontascent;
17360 $this_method_vars['curfontdescent'] = $curfontdescent;
17361 $this_method_vars['curfontstretcing'] = $curfontstretcing;
17362 $this_method_vars['curfonttracking'] = $curfonttracking;
17363 $this_method_vars['minstartliney'] = $minstartliney;
17364 $this_method_vars['maxbottomliney'] = $maxbottomliney;
17365 $this_method_vars['yshift'] = $yshift;
17366 $this_method_vars['startlinepage'] = $startlinepage;
17367 $this_method_vars['startlinepos'] = $startlinepos;
17368 $this_method_vars['startlinex'] = $startlinex;
17369 $this_method_vars['startliney'] = $startliney;
17370 $this_method_vars['newline'] = $newline;
17371 $this_method_vars['loop'] = $loop;
17372 $this_method_vars['curpos'] = $curpos;
17373 $this_method_vars['pask'] = $pask;
17374 $this_method_vars['lalign'] = $lalign;
17375 $this_method_vars['plalign'] = $plalign;
17376 $this_method_vars['w'] = $w;
17377 $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
17378 $this_method_vars['prev_listnum'] = $prev_listnum;
17379 $this_method_vars['prev_listordered'] = $prev_listordered;
17380 $this_method_vars['prev_listcount'] = $prev_listcount;
17381 $this_method_vars['prev_lispacer'] = $prev_lispacer;
17382 $this_method_vars['fontaligned'] = $fontaligned;
17383 $this_method_vars['key'] = $key;
17384 $this_method_vars['dom'] = $dom;
17387 // print THEAD block
17388 if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
17389 if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC
::empty_string($dom[$dom[$key]['parent']]['thead'])) {
17390 $this->inthead
= true;
17391 // print table header (thead)
17392 $this->writeHTML($this->thead
, false, false, false, false, '');
17393 // check if we are on a new page or on a new column
17394 if (($this->y
< $this->start_transaction_y
) OR ($this->checkPageBreak($this->lasth
, '', false))) {
17395 // we are on a new page or on a new column and the total object height is less than the available vertical space.
17396 // restore previous object
17397 $this->rollbackTransaction(true);
17398 // restore previous values
17399 foreach ($this_method_vars as $vkey => $vval) {
17402 // disable table header
17403 $tmp_thead = $this->thead
;
17405 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17407 if ((!$this->checkPageBreak($this->PageBreakTrigger +
1)) AND ($this->y
< $pre_y)) {
17408 // fix for multicolumn mode
17409 $startliney = $this->y
;
17411 $this->start_transaction_page
= $this->page
;
17412 $this->start_transaction_y
= $this->y
;
17413 // restore table header
17414 $this->thead
= $tmp_thead;
17415 // fix table border properties
17416 if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
17417 $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
17418 } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
17419 $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
17421 $tmp_cellspacing = 0;
17423 $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page
;
17424 $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column
;
17425 $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y +
$tmp_cellspacing;
17426 $xoffset = ($this->x
- $dom[$dom[$key]['parent']]['borderposition']['x']);
17427 $dom[$dom[$key]['parent']]['borderposition']['x'] +
= $xoffset;
17428 $dom[$dom[$key]['parent']]['borderposition']['xmax'] +
= $xoffset;
17429 // print table header (thead)
17430 $this->writeHTML($this->thead
, false, false, false, false, '');
17433 // move $key index forward to skip THEAD block
17434 while ( ($key < $maxel) AND (!(
17435 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
17436 OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
17440 if ($dom[$key]['tag'] OR ($key == 0)) {
17441 if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
17442 $dom[$key]['align'] = ($this->rtl
) ?
'R' : 'L';
17444 // vertically align image in line
17445 if ((!$this->newline
) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
17446 // get image height
17447 $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], ($dom[$key]['fontsize'] / $this->k
), 'px');
17448 $autolinebreak = false;
17449 if (!empty($dom[$key]['width'])) {
17450 $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], ($dom[$key]['fontsize'] / $this->k
), 'px', false);
17451 if (($imgw <= ($this->w
- $this->lMargin
- $this->rMargin
- $this->cell_padding
['L'] - $this->cell_padding
['R']))
17452 AND ((($this->rtl
) AND (($this->x
- $imgw) < ($this->lMargin +
$this->cell_padding
['L'])))
17453 OR ((!$this->rtl
) AND (($this->x +
$imgw) > ($this->w
- $this->rMargin
- $this->cell_padding
['R']))))) {
17454 // add automatic line break
17455 $autolinebreak = true;
17456 $this->Ln('', $cell);
17457 if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
17458 // go back to evaluate this line break
17463 if (!$autolinebreak) {
17464 if ($this->inPageBody()) {
17466 // check for page break
17467 if ((!$this->checkPageBreak($imgh)) AND ($this->y
< $pre_y)) {
17468 // fix for multicolumn mode
17469 $startliney = $this->y
;
17472 if ($this->page
> $startlinepage) {
17473 // fix line splitted over two pages
17474 if (isset($this->footerlen
[$startlinepage])) {
17475 $curpos = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
17477 // line to be moved one page forward
17478 $pagebuff = $this->getPageBuffer($startlinepage);
17479 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17480 $tstart = substr($pagebuff, 0, $startlinepos);
17481 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17482 // remove line from previous page
17483 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17484 $pagebuff = $this->getPageBuffer($this->page
);
17485 $tstart = substr($pagebuff, 0, $this->cntmrk
[$this->page
]);
17486 $tend = substr($pagebuff, $this->cntmrk
[$this->page
]);
17487 // add line start to current page
17488 $yshift = ($minstartliney - $this->y
);
17489 if ($fontaligned) {
17490 $yshift +
= ($curfontsize / $this->k
);
17492 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k
));
17493 $this->setPageBuffer($this->page
, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17494 // shift the annotations and links
17495 if (isset($this->PageAnnots
[$this->page
])) {
17496 $next_pask = count($this->PageAnnots
[$this->page
]);
17500 if (isset($this->PageAnnots
[$startlinepage])) {
17501 foreach ($this->PageAnnots
[$startlinepage] as $pak => $pac) {
17502 if ($pak >= $pask) {
17503 $this->PageAnnots
[$this->page
][] = $pac;
17504 unset($this->PageAnnots
[$startlinepage][$pak]);
17505 $npak = count($this->PageAnnots
[$this->page
]) - 1;
17506 $this->PageAnnots
[$this->page
][$npak]['y'] -= $yshift;
17510 $pask = $next_pask;
17511 $startlinepos = $this->cntmrk
[$this->page
];
17512 $startlinepage = $this->page
;
17513 $startliney = $this->y
;
17514 $this->newline
= false;
17516 $this->y +
= ($this->getCellHeight($curfontsize / $this->k
) - ($curfontdescent * $this->cell_height_ratio
) - $imgh);
17517 $minstartliney = min($this->y
, $minstartliney);
17518 $maxbottomliney = ($startliney +
$this->getCellHeight($curfontsize / $this->k
));
17520 } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
17521 // account for different font size
17522 $pfontname = $curfontname;
17523 $pfontstyle = $curfontstyle;
17524 $pfontsize = $curfontsize;
17525 $fontname = (isset($dom[$key]['fontname']) ?
$dom[$key]['fontname'] : $curfontname);
17526 $fontstyle = (isset($dom[$key]['fontstyle']) ?
$dom[$key]['fontstyle'] : $curfontstyle);
17527 $fontsize = (isset($dom[$key]['fontsize']) ?
$dom[$key]['fontsize'] : $curfontsize);
17528 $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
17529 $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
17530 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
17531 OR ($this->cell_height_ratio
!= $dom[$key]['line-height'])
17532 OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
17533 if (($key < ($maxel - 1)) AND (
17534 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
17535 OR ($this->cell_height_ratio
!= $dom[$key]['line-height'])
17536 OR (!$this->newline
AND is_numeric($fontsize) AND is_numeric($curfontsize)
17537 AND ($fontsize >= 0) AND ($curfontsize >= 0)
17538 AND (($fontsize != $curfontsize) OR ($fontstyle != $curfontstyle) OR ($fontname != $curfontname)))
17540 if ($this->page
> $startlinepage) {
17541 // fix lines splitted over two pages
17542 if (isset($this->footerlen
[$startlinepage])) {
17543 $curpos = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
17545 // line to be moved one page forward
17546 $pagebuff = $this->getPageBuffer($startlinepage);
17547 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17548 $tstart = substr($pagebuff, 0, $startlinepos);
17549 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17550 // remove line start from previous page
17551 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17552 $pagebuff = $this->getPageBuffer($this->page
);
17553 $tstart = substr($pagebuff, 0, $this->cntmrk
[$this->page
]);
17554 $tend = substr($pagebuff, $this->cntmrk
[$this->page
]);
17555 // add line start to current page
17556 $yshift = ($minstartliney - $this->y
);
17557 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k
));
17558 $this->setPageBuffer($this->page
, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17559 // shift the annotations and links
17560 if (isset($this->PageAnnots
[$this->page
])) {
17561 $next_pask = count($this->PageAnnots
[$this->page
]);
17565 if (isset($this->PageAnnots
[$startlinepage])) {
17566 foreach ($this->PageAnnots
[$startlinepage] as $pak => $pac) {
17567 if ($pak >= $pask) {
17568 $this->PageAnnots
[$this->page
][] = $pac;
17569 unset($this->PageAnnots
[$startlinepage][$pak]);
17570 $npak = count($this->PageAnnots
[$this->page
]) - 1;
17571 $this->PageAnnots
[$this->page
][$npak]['y'] -= $yshift;
17575 $pask = $next_pask;
17576 $startlinepos = $this->cntmrk
[$this->page
];
17577 $startlinepage = $this->page
;
17578 $startliney = $this->y
;
17580 if (!isset($dom[$key]['line-height'])) {
17581 $dom[$key]['line-height'] = $this->cell_height_ratio
;
17583 if (!$dom[$key]['block']) {
17584 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']))) {
17585 $this->y +
= (((($curfontsize * $this->cell_height_ratio
) - ($fontsize * $dom[$key]['line-height'])) / $this->k
) +
$curfontascent - $fontascent - $curfontdescent +
$fontdescent) / 2;
17587 if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
17588 $current_line_align_data = array($key, $minstartliney, $maxbottomliney);
17589 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)))) {
17590 $minstartliney = min($this->y
, $line_align_data[1]);
17591 $maxbottomliney = max(($this->y +
$this->getCellHeight($fontsize / $this->k
)), $line_align_data[2]);
17593 $minstartliney = min($this->y
, $minstartliney);
17594 $maxbottomliney = max(($this->y +
$this->getCellHeight($fontsize / $this->k
)), $maxbottomliney);
17596 $line_align_data = $current_line_align_data;
17599 $this->cell_height_ratio
= $dom[$key]['line-height'];
17600 $fontaligned = true;
17602 $this->SetFont($fontname, $fontstyle, $fontsize);
17603 // reset row height
17604 $this->resetLastH();
17605 $curfontname = $fontname;
17606 $curfontstyle = $fontstyle;
17607 $curfontsize = $fontsize;
17608 $curfontascent = $fontascent;
17609 $curfontdescent = $fontdescent;
17612 // set text rendering mode
17613 $textstroke = isset($dom[$key]['stroke']) ?
$dom[$key]['stroke'] : $this->textstrokewidth
;
17614 $textfill = isset($dom[$key]['fill']) ?
$dom[$key]['fill'] : (($this->textrendermode %
2) == 0);
17615 $textclip = isset($dom[$key]['clip']) ?
$dom[$key]['clip'] : ($this->textrendermode
> 3);
17616 $this->setTextRenderingMode($textstroke, $textfill, $textclip);
17617 if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
17618 $this->setFontStretching($dom[$key]['font-stretch']);
17620 if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
17621 $this->setFontSpacing($dom[$key]['letter-spacing']);
17623 if (($plalign == 'J') AND $dom[$key]['block']) {
17626 // get current position on page buffer
17627 $curpos = $this->pagelen
[$startlinepage];
17628 if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
17629 $this->SetFillColorArray($dom[$key]['bgcolor']);
17632 $wfill = $fill |
false;
17634 if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
17635 $this->SetTextColorArray($dom[$key]['fgcolor']);
17637 if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
17638 $this->SetDrawColorArray($dom[$key]['strokecolor']);
17640 if (isset($dom[$key]['align'])) {
17641 $lalign = $dom[$key]['align'];
17643 if (TCPDF_STATIC
::empty_string($lalign)) {
17648 if ($this->newline
AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
17650 $fontaligned = false;
17651 // we are at the beginning of a new line
17652 if (isset($startlinex)) {
17653 $yshift = ($minstartliney - $startliney);
17654 if (($yshift > 0) OR ($this->page
> $startlinepage)) {
17658 // the last line must be shifted to be aligned as requested
17659 $linew = abs($this->endlinex
- $startlinex);
17660 if ($this->inxobj
) {
17661 // we are inside an XObject template
17662 $pstart = substr($this->xobjects
[$this->xobjid
]['outdata'], 0, $startlinepos);
17663 if (isset($opentagpos)) {
17664 $midpos = $opentagpos;
17669 $pmid = substr($this->xobjects
[$this->xobjid
]['outdata'], $startlinepos, ($midpos - $startlinepos));
17670 $pend = substr($this->xobjects
[$this->xobjid
]['outdata'], $midpos);
17672 $pmid = substr($this->xobjects
[$this->xobjid
]['outdata'], $startlinepos);
17676 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
17677 if (isset($opentagpos) AND isset($this->footerlen
[$startlinepage]) AND (!$this->InFooter
)) {
17678 $this->footerpos
[$startlinepage] = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
17679 $midpos = min($opentagpos, $this->footerpos
[$startlinepage]);
17680 } elseif (isset($opentagpos)) {
17681 $midpos = $opentagpos;
17682 } elseif (isset($this->footerlen
[$startlinepage]) AND (!$this->InFooter
)) {
17683 $this->footerpos
[$startlinepage] = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
17684 $midpos = $this->footerpos
[$startlinepage];
17689 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
17690 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
17692 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
17696 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl
)) OR (($plalign == 'L') AND ($this->rtl
)))))) {
17697 // calculate shifting amount
17699 if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns
> 1)) {
17700 $tw +
= $this->cell_padding
['R'];
17702 if ($this->lMargin
!= $prevlMargin) {
17703 $tw +
= ($prevlMargin - $this->lMargin
);
17705 if ($this->rMargin
!= $prevrMargin) {
17706 $tw +
= ($prevrMargin - $this->rMargin
);
17708 $one_space_width = $this->GetStringWidth(chr(32));
17709 $no = 0; // number of spaces on a line contained on a single block
17710 if ($this->isRTLTextDir()) { // RTL
17711 // remove left space if exist
17712 $pos1 = TCPDF_STATIC
::revstrpos($pmid, '[(');
17714 $pos1 = intval($pos1);
17715 if ($this->isUnicodeFont()) {
17716 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, '[('.chr(0).chr(32)));
17719 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, '[('.chr(32)));
17722 if ($pos1 == $pos2) {
17723 $pmid = substr($pmid, 0, ($pos1 +
2)).substr($pmid, ($pos1 +
2 +
$spacelen));
17724 if (substr($pmid, $pos1, 4) == '[()]') {
17725 $linew -= $one_space_width;
17726 } elseif ($pos1 == strpos($pmid, '[(')) {
17732 // remove right space if exist
17733 $pos1 = TCPDF_STATIC
::revstrpos($pmid, ')]');
17735 $pos1 = intval($pos1);
17736 if ($this->isUnicodeFont()) {
17737 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, chr(0).chr(32).')]')) +
2;
17740 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, chr(32).')]')) +
1;
17743 if ($pos1 == $pos2) {
17744 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
17745 $linew -= $one_space_width;
17749 $mdiff = ($tw - $linew);
17750 if ($plalign == 'C') {
17752 $t_x = -($mdiff / 2);
17754 $t_x = ($mdiff / 2);
17756 } elseif ($plalign == 'R') {
17757 // right alignment on LTR document
17759 } elseif ($plalign == 'L') {
17760 // left alignment on RTL document
17762 } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
17764 if ($this->isRTLTextDir()) {
17765 // align text on the left
17768 $ns = 0; // number of spaces
17770 // escape special characters
17771 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
17772 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
17774 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER
)) {
17775 $spacestr = $this->getSpaceString();
17776 $maxkk = count($lnstring[1]) - 1;
17777 for ($kk=0; $kk <= $maxkk; ++
$kk) {
17778 // restore special characters
17779 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
17780 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
17781 // store number of spaces on the strings
17782 $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
17783 // count total spaces on line
17784 $ns +
= $lnstring[2][$kk];
17785 $lnstring[3][$kk] = $ns;
17790 // calculate additional space to add to each existing space
17791 $spacewidth = ($mdiff / ($ns - $no)) * $this->k
;
17792 if ($this->FontSize
<= 0) {
17793 $this->FontSize
= 1;
17795 $spacewidthu = -1000 * ($mdiff +
(($ns +
$no) * $one_space_width)) / $ns / $this->FontSize
;
17796 if ($this->font_spacing
!= 0) {
17797 // fixed spacing mode
17798 $osw = -1000 * $this->font_spacing
/ $this->FontSize
;
17799 $spacewidthu +
= $osw;
17806 $prev_epsposbeg = 0;
17808 if ($this->isRTLTextDir()) {
17809 $textpos = $this->wPt
;
17811 while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE
, $offset) == 1) {
17812 // check if we are inside a string section '[( ... )]'
17813 $stroffset = strpos($pmid, '[(', $offset);
17814 if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
17815 // set offset to the end of string section
17816 $offset = strpos($pmid, ')]', $stroffset);
17817 while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
17818 $offset = strpos($pmid, ')]', ($offset +
1));
17820 if ($offset === false) {
17821 $this->Error('HTML Justification: malformed PDF code.');
17825 if ($this->isRTLTextDir()) {
17826 $spacew = ($spacewidth * ($nsmax - $ns));
17828 $spacew = ($spacewidth * $ns);
17830 $offset = $strpiece[2][1] +
strlen($strpiece[2][0]);
17831 $epsposend = strpos($pmid, $this->epsmarker
.'Q', $offset);
17832 if ($epsposend !== null) {
17833 $epsposend +
= strlen($this->epsmarker
.'Q');
17834 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker
, $offset);
17835 if ($epsposbeg === null) {
17836 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker
, ($prev_epsposbeg - 6));
17837 $prev_epsposbeg = $epsposbeg;
17839 if (($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) {
17840 // shift EPS images
17841 $trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
17842 $pmid_b = substr($pmid, 0, $epsposbeg);
17843 $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
17844 $pmid_e = substr($pmid, $epsposend);
17845 $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
17846 $offset = $epsposend;
17851 // shift blocks of code
17852 switch ($strpiece[2][0]) {
17857 // get current X position
17858 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
17859 if (!isset($xmatches[1])) {
17862 $currentxpos = $xmatches[1];
17863 $textpos = $currentxpos;
17864 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
17865 $ns = $lnstring[3][$strcount];
17866 if ($this->isRTLTextDir()) {
17867 $spacew = ($spacewidth * ($nsmax - $ns));
17872 if (preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $pmatch) == 1) {
17873 $newpmid = sprintf('%F',(floatval($pmatch[1]) +
$spacew)).' '.$pmatch[2].' x*#!#*x'.$pmatch[3].$pmatch[4];
17874 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17875 unset($pmatch, $newpmid);
17881 if (!TCPDF_STATIC
::empty_string($this->lispacer
)) {
17882 $this->lispacer
= '';
17885 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
17886 if (!isset($xmatches[1])) {
17889 $currentxpos = $xmatches[1];
17892 if ($this->isRTLTextDir()) { // RTL
17893 if ($currentxpos < $textpos) {
17894 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
17895 $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17897 if ($strcount > 0) {
17898 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
17899 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17903 if ($currentxpos > $textpos) {
17904 if ($strcount > 0) {
17905 $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
17907 $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17909 if ($strcount > 1) {
17910 $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
17912 if ($strcount > 0) {
17913 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17917 if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $pmatch) == 1) {
17918 $newx = sprintf('%F',(floatval($pmatch[1]) +
$x_diff));
17919 $neww = sprintf('%F',(floatval($pmatch[3]) +
$w_diff));
17920 $newpmid = $newx.' '.$pmatch[2].' '.$neww.' '.$pmatch[4].' x*#!#*x'.$pmatch[5].$pmatch[6];
17921 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17922 unset($pmatch, $newpmid, $newx, $neww);
17927 // get current X position
17928 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);
17929 if (!isset($xmatches[1])) {
17932 $currentxpos = $xmatches[1];
17934 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) {
17935 $newx1 = sprintf('%F',(floatval($pmatch[1]) +
$spacew));
17936 $newx2 = sprintf('%F',(floatval($pmatch[3]) +
$spacew));
17937 $newx3 = sprintf('%F',(floatval($pmatch[5]) +
$spacew));
17938 $newpmid = $newx1.' '.$pmatch[2].' '.$newx2.' '.$pmatch[4].' '.$newx3.' '.$pmatch[6].' x*#!#*x'.$pmatch[7].$pmatch[8];
17939 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17940 unset($pmatch, $newpmid, $newx1, $newx2, $newx3);
17945 // shift the annotations and links
17946 $cxpos = ($currentxpos / $this->k
);
17947 $lmpos = ($this->lMargin +
$this->cell_padding
['L'] +
$this->feps
);
17948 if ($this->inxobj
) {
17949 // we are inside an XObject template
17950 foreach ($this->xobjects
[$this->xobjid
]['annotations'] as $pak => $pac) {
17951 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k
) >= ($currentxpos - $this->feps
)) AND (($pac['x'] * $this->k
) <= ($currentxpos +
$this->feps
))) {
17952 if ($cxpos > $lmpos) {
17953 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['x'] +
= ($spacew / $this->k
);
17954 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['w'] +
= (($spacewidth * $pac['numspaces']) / $this->k
);
17956 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['w'] +
= (($spacewidth * $pac['numspaces']) / $this->k
);
17961 } elseif (isset($this->PageAnnots
[$this->page
])) {
17962 foreach ($this->PageAnnots
[$this->page
] as $pak => $pac) {
17963 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k
) >= ($currentxpos - $this->feps
)) AND (($pac['x'] * $this->k
) <= ($currentxpos +
$this->feps
))) {
17964 if ($cxpos > $lmpos) {
17965 $this->PageAnnots
[$this->page
][$pak]['x'] +
= ($spacew / $this->k
);
17966 $this->PageAnnots
[$this->page
][$pak]['w'] +
= (($spacewidth * $pac['numspaces']) / $this->k
);
17968 $this->PageAnnots
[$this->page
][$pak]['w'] +
= (($spacewidth * $pac['numspaces']) / $this->k
);
17976 $pmid = str_replace('x*#!#*x', '', $pmid);
17977 if ($this->isUnicodeFont()) {
17978 // multibyte characters
17979 $spacew = $spacewidthu;
17980 if ($this->font_stretching
!= 100) {
17981 // word spacing is affected by stretching
17982 $spacew /= ($this->font_stretching
/ 100);
17984 // escape special characters
17986 $pmid = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmid);
17987 $pmid = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmid);
17988 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmid, $pamatch) > 0) {
17989 foreach($pamatch[0] as $pk => $pmatch) {
17990 $replace = $pamatch[1][$pk];
17991 $replace = str_replace('#!#OP#!#', '(', $replace);
17992 $replace = str_replace('#!#CP#!#', ')', $replace);
17993 $newpmid = '[('.str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacew).' (', $replace).')]';
17994 $pos = strpos($pmid, $pmatch, $pos);
17995 if ($pos !== FALSE) {
17996 $pmid = substr_replace($pmid, $newpmid, $pos, strlen($pmatch));
18002 if ($this->inxobj
) {
18003 // we are inside an XObject template
18004 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart."\n".$pmid."\n".$pend;
18006 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
18008 $endlinepos = strlen($pstart."\n".$pmid."\n");
18010 // non-unicode (single-byte characters)
18011 if ($this->font_stretching
!= 100) {
18012 // word spacing (Tw) is affected by stretching
18013 $spacewidth /= ($this->font_stretching
/ 100);
18015 $rs = sprintf('%F Tw', $spacewidth);
18016 $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
18017 if ($this->inxobj
) {
18018 // we are inside an XObject template
18019 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
18021 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
18023 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
18027 } // end if $startlinex
18028 if (($t_x != 0) OR ($yshift < 0)) {
18030 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k
), ($yshift * $this->k
));
18031 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18032 $endlinepos = strlen($pstart);
18033 if ($this->inxobj
) {
18034 // we are inside an XObject template
18035 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$pend;
18036 foreach ($this->xobjects
[$this->xobjid
]['annotations'] as $pak => $pac) {
18037 if ($pak >= $pask) {
18038 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['x'] +
= $t_x;
18039 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['y'] -= $yshift;
18043 $this->setPageBuffer($startlinepage, $pstart.$pend);
18044 // shift the annotations and links
18045 if (isset($this->PageAnnots
[$this->page
])) {
18046 foreach ($this->PageAnnots
[$this->page
] as $pak => $pac) {
18047 if ($pak >= $pask) {
18048 $this->PageAnnots
[$this->page
][$pak]['x'] +
= $t_x;
18049 $this->PageAnnots
[$this->page
][$pak]['y'] -= $yshift;
18054 $this->y
-= $yshift;
18057 $pbrk = $this->checkPageBreak($this->lasth
);
18058 $this->newline
= false;
18059 $startlinex = $this->x
;
18060 $startliney = $this->y
;
18061 if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
18062 $startliney -= ((0.3 * $this->FontSizePt
) / $this->k
);
18063 } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
18064 $startliney -= (($this->FontSizePt
/ 0.7) / $this->k
);
18066 $minstartliney = $startliney;
18067 $maxbottomliney = ($this->y +
$this->getCellHeight($fontsize / $this->k
));
18069 $startlinepage = $this->page
;
18070 if (isset($endlinepos) AND (!$pbrk)) {
18071 $startlinepos = $endlinepos;
18073 if ($this->inxobj
) {
18074 // we are inside an XObject template
18075 $startlinepos = strlen($this->xobjects
[$this->xobjid
]['outdata']);
18076 } elseif (!$this->InFooter
) {
18077 if (isset($this->footerlen
[$this->page
])) {
18078 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
] - $this->footerlen
[$this->page
];
18080 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
];
18082 $startlinepos = $this->footerpos
[$this->page
];
18084 $startlinepos = $this->pagelen
[$this->page
];
18087 unset($endlinepos);
18088 $plalign = $lalign;
18089 if (isset($this->PageAnnots
[$this->page
])) {
18090 $pask = count($this->PageAnnots
[$this->page
]);
18094 if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
18095 AND (isset($this->emptypagemrk
[$this->page
]))
18096 AND ($this->emptypagemrk
[$this->page
] == $this->pagelen
[$this->page
]))) {
18097 $this->SetFont($fontname, $fontstyle, $fontsize);
18099 $this->SetFillColorArray($this->bgcolor
);
18103 if (isset($opentagpos)) {
18104 unset($opentagpos);
18106 if ($dom[$key]['tag']) {
18107 if ($dom[$key]['opening']) {
18108 // get text indentation (if any)
18109 if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
18110 $this->textindent
= $dom[$key]['text-indent'];
18111 $this->newline
= true;
18114 if (($dom[$key]['value'] == 'table') AND isset($dom[$key]['cols']) AND ($dom[$key]['cols'] > 0)) {
18115 // available page width
18117 $wtmp = $this->x
- $this->lMargin
;
18119 $wtmp = $this->w
- $this->rMargin
- $this->x
;
18121 // get cell spacing
18122 if (isset($dom[$key]['attribute']['cellspacing'])) {
18123 $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
18124 $cellspacing = array('H' => $clsp, 'V' => $clsp);
18125 } elseif (isset($dom[$key]['border-spacing'])) {
18126 $cellspacing = $dom[$key]['border-spacing'];
18128 $cellspacing = array('H' => 0, 'V' => 0);
18131 if (isset($dom[$key]['width'])) {
18132 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
18134 $table_width = $wtmp;
18136 $table_width -= (2 * $cellspacing['H']);
18137 if (!$this->inthead
) {
18138 $this->y +
= $cellspacing['V'];
18141 $cellspacingx = -$cellspacing['H'];
18143 $cellspacingx = $cellspacing['H'];
18145 // total table width without cellspaces
18146 $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
18147 // minimum column width
18148 $table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
18149 // array of custom column widths
18150 $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
18153 if ($dom[$key]['value'] == 'tr') {
18154 // reset column counter
18158 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
18159 $trid = $dom[$key]['parent'];
18160 $table_el = $dom[$trid]['parent'];
18161 if (!isset($dom[$table_el]['cols'])) {
18162 $dom[$table_el]['cols'] = $dom[$trid]['cols'];
18164 // store border info
18166 if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
18167 $tdborder = $dom[$key]['border'];
18169 $colspan = intval($dom[$key]['attribute']['colspan']);
18170 if ($colspan <= 0) {
18173 $old_cell_padding = $this->cell_padding
;
18174 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
18175 $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
18176 $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
18177 } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
18178 $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
18180 $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
18182 $this->cell_padding
= $current_cell_padding;
18183 if (isset($dom[$key]['height'])) {
18184 // minimum cell height
18185 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
18189 if (isset($dom[$key]['content'])) {
18190 $cell_content = stripslashes($dom[$key]['content']);
18192 $cell_content = ' ';
18194 $tagtype = $dom[$key]['value'];
18196 while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
18197 // move $key index forward
18200 if (!isset($dom[$trid]['startpage'])) {
18201 $dom[$trid]['startpage'] = $this->page
;
18203 $this->setPage($dom[$trid]['startpage']);
18205 if (!isset($dom[$trid]['startcolumn'])) {
18206 $dom[$trid]['startcolumn'] = $this->current_column
;
18207 } elseif ($this->current_column
!= $dom[$trid]['startcolumn']) {
18209 $this->selectColumn($dom[$trid]['startcolumn']);
18212 if (!isset($dom[$trid]['starty'])) {
18213 $dom[$trid]['starty'] = $this->y
;
18215 $this->y
= $dom[$trid]['starty'];
18217 if (!isset($dom[$trid]['startx'])) {
18218 $dom[$trid]['startx'] = $this->x
;
18219 $this->x +
= $cellspacingx;
18221 $this->x +
= ($cellspacingx / 2);
18223 if (isset($dom[$parentid]['attribute']['rowspan'])) {
18224 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
18228 // skip row-spanned cells started on the previous rows
18229 if (isset($dom[$table_el]['rowspans'])) {
18231 $rskmax = count($dom[$table_el]['rowspans']);
18232 while ($rsk < $rskmax) {
18233 $trwsp = $dom[$table_el]['rowspans'][$rsk];
18234 $rsstartx = $trwsp['startx'];
18235 $rsendx = $trwsp['endx'];
18236 // account for margin changes
18237 if ($trwsp['startpage'] < $this->page
) {
18238 if (($this->rtl
) AND ($this->pagedim
[$this->page
]['orm'] != $this->pagedim
[$trwsp['startpage']]['orm'])) {
18239 $dl = ($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$trwsp['startpage']]['orm']);
18242 } elseif ((!$this->rtl
) AND ($this->pagedim
[$this->page
]['olm'] != $this->pagedim
[$trwsp['startpage']]['olm'])) {
18243 $dl = ($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$trwsp['startpage']]['olm']);
18248 if (($trwsp['rowspan'] > 0)
18249 AND ($rsstartx > ($this->x
- $cellspacing['H'] - $current_cell_padding['L'] - $this->feps
))
18250 AND ($rsstartx < ($this->x +
$cellspacing['H'] +
$current_cell_padding['R'] +
$this->feps
))
18251 AND (($trwsp['starty'] < ($this->y
- $this->feps
)) OR ($trwsp['startpage'] < $this->page
) OR ($trwsp['startcolumn'] < $this->current_column
))) {
18252 // set the starting X position of the current cell
18253 $this->x
= $rsendx +
$cellspacingx;
18254 // increment column indicator
18255 $colid +
= $trwsp['colspan'];
18256 if (($trwsp['rowspan'] == 1)
18257 AND (isset($dom[$trid]['endy']))
18258 AND (isset($dom[$trid]['endpage']))
18259 AND (isset($dom[$trid]['endcolumn']))
18260 AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
18261 AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18262 // set ending Y position for row
18263 $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18264 $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
18272 if (isset($dom[$parentid]['width'])) {
18273 // user specified width
18274 $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
18275 $tmpcw = ($cellw / $colspan);
18276 for ($i = 0; $i < $colspan; ++
$i) {
18277 $table_colwidths[($colid +
$i)] = $tmpcw;
18280 // inherit column width
18282 for ($i = 0; $i < $colspan; ++
$i) {
18283 $cellw +
= $table_colwidths[($colid +
$i)];
18286 $cellw +
= (($colspan - 1) * $cellspacing['H']);
18287 // increment column indicator
18288 $colid +
= $colspan;
18289 // add rowspan information to table element
18290 if ($rowspan > 1) {
18291 $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
));
18293 $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x
));
18294 if ($rowspan > 1) {
18295 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
18297 // push background colors
18298 if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
18299 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
18301 // store border info
18302 if (isset($tdborder) AND !empty($tdborder)) {
18303 $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
18305 $prevLastH = $this->lasth
;
18306 // store some info for multicolumn mode
18308 $this->colxshift
['x'] = $this->w
- $this->x
- $this->rMargin
;
18310 $this->colxshift
['x'] = $this->x
- $this->lMargin
;
18312 $this->colxshift
['s'] = $cellspacing;
18313 $this->colxshift
['p'] = $current_cell_padding;
18314 // ****** write the cell content ******
18315 $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
18316 // restore some values
18317 $this->colxshift
= array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
18318 $this->lasth
= $prevLastH;
18319 $this->cell_padding
= $old_cell_padding;
18320 $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x
;
18321 // update the end of row position
18322 if ($rowspan <= 1) {
18323 if (isset($dom[$trid]['endy'])) {
18324 if (($this->page
== $dom[$trid]['endpage']) AND ($this->current_column
== $dom[$trid]['endcolumn'])) {
18325 $dom[$trid]['endy'] = max($this->y
, $dom[$trid]['endy']);
18326 } elseif (($this->page
> $dom[$trid]['endpage']) OR ($this->current_column
> $dom[$trid]['endcolumn'])) {
18327 $dom[$trid]['endy'] = $this->y
;
18330 $dom[$trid]['endy'] = $this->y
;
18332 if (isset($dom[$trid]['endpage'])) {
18333 $dom[$trid]['endpage'] = max($this->page
, $dom[$trid]['endpage']);
18335 $dom[$trid]['endpage'] = $this->page
;
18337 if (isset($dom[$trid]['endcolumn'])) {
18338 $dom[$trid]['endcolumn'] = max($this->current_column
, $dom[$trid]['endcolumn']);
18340 $dom[$trid]['endcolumn'] = $this->current_column
;
18343 // account for row-spanned cells
18344 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x
;
18345 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y
;
18346 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page
;
18347 $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column
;
18349 if (isset($dom[$table_el]['rowspans'])) {
18350 // update endy and endpage on rowspanned cells
18351 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
18352 if ($trwsp['rowspan'] > 0) {
18353 if (isset($dom[$trid]['endpage'])) {
18354 if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18355 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18356 } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
18357 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
18358 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
18359 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
18361 $dom[$trid]['endy'] = $this->pagedim
[$dom[$trid]['endpage']]['hk'] - $this->pagedim
[$dom[$trid]['endpage']]['bm'];
18367 $this->x +
= ($cellspacingx / 2);
18369 // opening tag (or self-closing tag)
18370 if (!isset($opentagpos)) {
18371 if ($this->inxobj
) {
18372 // we are inside an XObject template
18373 $opentagpos = strlen($this->xobjects
[$this->xobjid
]['outdata']);
18374 } elseif (!$this->InFooter
) {
18375 if (isset($this->footerlen
[$this->page
])) {
18376 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
] - $this->footerlen
[$this->page
];
18378 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
];
18380 $opentagpos = $this->footerpos
[$this->page
];
18383 $dom = $this->openHTMLTagHandler($dom, $key, $cell);
18385 } else { // closing tag
18386 $prev_numpages = $this->numpages
;
18387 $old_bordermrk = $this->bordermrk
[$this->page
];
18388 $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
18389 if ($this->bordermrk
[$this->page
] > $old_bordermrk) {
18390 $startlinepos +
= ($this->bordermrk
[$this->page
] - $old_bordermrk);
18392 if ($prev_numpages > $this->numpages
) {
18393 $startlinepage = $this->page
;
18396 } elseif (strlen($dom[$key]['value']) > 0) {
18398 if (!TCPDF_STATIC
::empty_string($this->lispacer
) AND ($this->lispacer
!= '^')) {
18399 $this->SetFont($pfontname, $pfontstyle, $pfontsize);
18400 $this->resetLastH();
18401 $minstartliney = $this->y
;
18402 $maxbottomliney = ($startliney +
$this->getCellHeight($this->FontSize
));
18403 if (is_numeric($pfontsize) AND ($pfontsize > 0)) {
18404 $this->putHtmlListBullet($this->listnum
, $this->lispacer
, $pfontsize);
18406 $this->SetFont($curfontname, $curfontstyle, $curfontsize);
18407 $this->resetLastH();
18408 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
18409 $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
18410 $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
18411 $this->y +
= ($this->getCellHeight(($pfontsize - $curfontsize) / $this->k
) +
$pfontascent - $curfontascent - $pfontdescent +
$curfontdescent) / 2;
18412 $minstartliney = min($this->y
, $minstartliney);
18413 $maxbottomliney = max(($this->y +
$this->getCellHeight($pfontsize / $this->k
)), $maxbottomliney);
18417 $this->htmlvspace
= 0;
18418 if ((!$this->premode
) AND $this->isRTLTextDir()) {
18419 // reverse spaces order
18420 $lsp = ''; // left spaces
18421 $rsp = ''; // right spaces
18422 if (preg_match('/^('.$this->re_space
['p'].'+)/'.$this->re_space
['m'], $dom[$key]['value'], $matches)) {
18423 $lsp = $matches[1];
18425 if (preg_match('/('.$this->re_space
['p'].'+)$/'.$this->re_space
['m'], $dom[$key]['value'], $matches)) {
18426 $rsp = $matches[1];
18428 $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
18431 if (!$this->premode
) {
18432 $prelen = strlen($dom[$key]['value']);
18433 if ($this->isRTLTextDir()) {
18434 // right trim except non-breaking space
18435 $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
18437 // left trim except non-breaking space
18438 $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
18440 $postlen = strlen($dom[$key]['value']);
18441 if (($postlen == 0) AND ($prelen > 0)) {
18442 $dom[$key]['trimmed_space'] = true;
18446 $firstblock = true;
18448 $firstblock = false;
18449 // replace empty multiple spaces string with a single space
18450 $dom[$key]['value'] = preg_replace('/^'.$this->re_space
['p'].'+$/'.$this->re_space
['m'], chr(32), $dom[$key]['value']);
18454 $this->x
-= $this->textindent
;
18456 $this->x +
= $this->textindent
;
18458 if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
18459 $strlinelen = $this->GetStringWidth($dom[$key]['value']);
18460 if (!empty($this->HREF
) AND (isset($this->HREF
['url']))) {
18463 if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
18464 $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
18467 if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
18468 $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
18470 $strrest = $this->addHtmlLink($this->HREF
['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
18472 $wadj = 0; // space to leave for block continuity
18474 $cwa = ($this->x
- $this->lMargin
);
18476 $cwa = ($this->w
- $this->rMargin
- $this->x
);
18478 if (($strlinelen < $cwa) AND (isset($dom[($key +
1)])) AND ($dom[($key +
1)]['tag']) AND (!$dom[($key +
1)]['block'])) {
18479 // check the next text blocks for continuity
18480 $nkey = ($key +
1);
18481 $write_block = true;
18482 $same_textdir = true;
18483 $tmp_fontname = $this->FontFamily
;
18484 $tmp_fontstyle = $this->FontStyle
;
18485 $tmp_fontsize = $this->FontSizePt
;
18486 while ($write_block AND isset($dom[$nkey])) {
18487 if ($dom[$nkey]['tag']) {
18488 if ($dom[$nkey]['block']) {
18490 $write_block = false;
18492 $tmp_fontname = isset($dom[$nkey]['fontname']) ?
$dom[$nkey]['fontname'] : $this->FontFamily
;
18493 $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ?
$dom[$nkey]['fontstyle'] : $this->FontStyle
;
18494 $tmp_fontsize = isset($dom[$nkey]['fontsize']) ?
$dom[$nkey]['fontsize'] : $this->FontSizePt
;
18495 $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
18497 $nextstr = TCPDF_STATIC
::pregSplit('/'.$this->re_space
['p'].'+/', $this->re_space
['m'], $dom[$nkey]['value']);
18498 if (isset($nextstr[0]) AND $same_textdir) {
18499 $wadj +
= $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
18500 if (isset($nextstr[1])) {
18501 $write_block = false;
18508 if (($wadj > 0) AND (($strlinelen +
$wadj) >= $cwa)) {
18510 $nextstr = TCPDF_STATIC
::pregSplit('/'.$this->re_space
['p'].'/', $this->re_space
['m'], $dom[$key]['value']);
18511 $numblks = count($nextstr);
18512 if ($numblks > 1) {
18513 // try to split on blank spaces
18514 $wadj = ($cwa - $strlinelen +
$this->GetStringWidth($nextstr[($numblks - 1)]));
18516 // set the entire block on new line
18517 $wadj = $this->GetStringWidth($nextstr[0]);
18520 // check for reversed text direction
18521 if (($wadj > 0) AND (($this->rtl
AND ($this->tmprtl
=== 'L')) OR (!$this->rtl
AND ($this->tmprtl
=== 'R')))) {
18522 // LTR text on RTL direction or RTL text on LTR direction
18523 $reverse_dir = true;
18524 $this->rtl
= !$this->rtl
;
18525 $revshift = ($strlinelen +
$wadj +
0.000001); // add little quantity for rounding problems
18527 $this->x +
= $revshift;
18529 $this->x
-= $revshift;
18533 // ****** write only until the end of the line and get the rest ******
18534 $strrest = $this->Write($this->lasth
, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
18535 // restore default direction
18536 if ($reverse_dir AND ($wadj == 0)) {
18538 $this->rtl
= !$this->rtl
;
18539 $reverse_dir = false;
18543 $this->textindent
= 0;
18544 if (strlen($strrest) > 0) {
18545 // store the remaining string on the previous $key position
18546 $this->newline
= true;
18547 if ($strrest == $dom[$key]['value']) {
18548 // used to avoid infinite loop
18553 $dom[$key]['value'] = $strrest;
18556 $this->x
-= $this->cell_padding
['R'];
18558 $this->x +
= $this->cell_padding
['L'];
18566 // add the positive font spacing of the last character (if any)
18567 if ($this->font_spacing
> 0) {
18569 $this->x
-= $this->font_spacing
;
18571 $this->x +
= $this->font_spacing
;
18577 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')) {
18578 // check if we are on a new page or on a new column
18579 if ((!$undo) AND (($this->y
< $this->start_transaction_y
) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y
)))) {
18580 // we are on a new page or on a new column and the total object height is less than the available vertical space.
18581 // restore previous object
18582 $this->rollbackTransaction(true);
18583 // restore previous values
18584 foreach ($this_method_vars as $vkey => $vval) {
18587 if (!empty($dom[$key]['thead'])) {
18588 $this->inthead
= true;
18590 // add a page (or trig AcceptPageBreak() for multicolumn mode)
18592 if ((!$this->checkPageBreak($this->PageBreakTrigger +
1)) AND ($this->y
< $pre_y)) {
18593 $startliney = $this->y
;
18595 $undo = true; // avoid infinite loop
18600 } // end for each $key
18601 // align the last line
18602 if (isset($startlinex)) {
18603 $yshift = ($minstartliney - $startliney);
18604 if (($yshift > 0) OR ($this->page
> $startlinepage)) {
18608 // the last line must be shifted to be aligned as requested
18609 $linew = abs($this->endlinex
- $startlinex);
18610 if ($this->inxobj
) {
18611 // we are inside an XObject template
18612 $pstart = substr($this->xobjects
[$this->xobjid
]['outdata'], 0, $startlinepos);
18613 if (isset($opentagpos)) {
18614 $midpos = $opentagpos;
18619 $pmid = substr($this->xobjects
[$this->xobjid
]['outdata'], $startlinepos, ($midpos - $startlinepos));
18620 $pend = substr($this->xobjects
[$this->xobjid
]['outdata'], $midpos);
18622 $pmid = substr($this->xobjects
[$this->xobjid
]['outdata'], $startlinepos);
18626 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
18627 if (isset($opentagpos) AND isset($this->footerlen
[$startlinepage]) AND (!$this->InFooter
)) {
18628 $this->footerpos
[$startlinepage] = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
18629 $midpos = min($opentagpos, $this->footerpos
[$startlinepage]);
18630 } elseif (isset($opentagpos)) {
18631 $midpos = $opentagpos;
18632 } elseif (isset($this->footerlen
[$startlinepage]) AND (!$this->InFooter
)) {
18633 $this->footerpos
[$startlinepage] = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
18634 $midpos = $this->footerpos
[$startlinepage];
18639 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
18640 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
18642 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
18646 if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl
)) OR (($plalign == 'L') AND ($this->rtl
)))))) {
18647 // calculate shifting amount
18649 if ($this->lMargin
!= $prevlMargin) {
18650 $tw +
= ($prevlMargin - $this->lMargin
);
18652 if ($this->rMargin
!= $prevrMargin) {
18653 $tw +
= ($prevrMargin - $this->rMargin
);
18655 $one_space_width = $this->GetStringWidth(chr(32));
18656 $no = 0; // number of spaces on a line contained on a single block
18657 if ($this->isRTLTextDir()) { // RTL
18658 // remove left space if exist
18659 $pos1 = TCPDF_STATIC
::revstrpos($pmid, '[(');
18661 $pos1 = intval($pos1);
18662 if ($this->isUnicodeFont()) {
18663 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, '[('.chr(0).chr(32)));
18666 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, '[('.chr(32)));
18669 if ($pos1 == $pos2) {
18670 $pmid = substr($pmid, 0, ($pos1 +
2)).substr($pmid, ($pos1 +
2 +
$spacelen));
18671 if (substr($pmid, $pos1, 4) == '[()]') {
18672 $linew -= $one_space_width;
18673 } elseif ($pos1 == strpos($pmid, '[(')) {
18679 // remove right space if exist
18680 $pos1 = TCPDF_STATIC
::revstrpos($pmid, ')]');
18682 $pos1 = intval($pos1);
18683 if ($this->isUnicodeFont()) {
18684 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, chr(0).chr(32).')]')) +
2;
18687 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, chr(32).')]')) +
1;
18690 if ($pos1 == $pos2) {
18691 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
18692 $linew -= $one_space_width;
18696 $mdiff = ($tw - $linew);
18697 if ($plalign == 'C') {
18699 $t_x = -($mdiff / 2);
18701 $t_x = ($mdiff / 2);
18703 } elseif ($plalign == 'R') {
18704 // right alignment on LTR document
18706 } elseif ($plalign == 'L') {
18707 // left alignment on RTL document
18710 } // end if startlinex
18711 if (($t_x != 0) OR ($yshift < 0)) {
18713 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k
), ($yshift * $this->k
));
18714 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18715 $endlinepos = strlen($pstart);
18716 if ($this->inxobj
) {
18717 // we are inside an XObject template
18718 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$pend;
18719 foreach ($this->xobjects
[$this->xobjid
]['annotations'] as $pak => $pac) {
18720 if ($pak >= $pask) {
18721 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['x'] +
= $t_x;
18722 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['y'] -= $yshift;
18726 $this->setPageBuffer($startlinepage, $pstart.$pend);
18727 // shift the annotations and links
18728 if (isset($this->PageAnnots
[$this->page
])) {
18729 foreach ($this->PageAnnots
[$this->page
] as $pak => $pac) {
18730 if ($pak >= $pask) {
18731 $this->PageAnnots
[$this->page
][$pak]['x'] +
= $t_x;
18732 $this->PageAnnots
[$this->page
][$pak]['y'] -= $yshift;
18737 $this->y
-= $yshift;
18741 // restore previous values
18742 $this->setGraphicVars($gvars);
18743 if ($this->num_columns
> 1) {
18744 $this->selectColumn();
18745 } elseif ($this->page
> $prevPage) {
18746 $this->lMargin
= $this->pagedim
[$this->page
]['olm'];
18747 $this->rMargin
= $this->pagedim
[$this->page
]['orm'];
18749 // restore previous list state
18750 $this->cell_height_ratio
= $prev_cell_height_ratio;
18751 $this->listnum
= $prev_listnum;
18752 $this->listordered
= $prev_listordered;
18753 $this->listcount
= $prev_listcount;
18754 $this->lispacer
= $prev_lispacer;
18755 if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
18756 $this->Ln($this->lasth
);
18757 if ($this->y
< $maxbottomliney) {
18758 $this->y
= $maxbottomliney;
18765 * Process opening tags.
18766 * @param $dom (array) html dom array
18767 * @param $key (int) current element id
18768 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
18769 * @return $dom array
18772 protected function openHTMLTagHandler($dom, $key, $cell) {
18774 $parent = $dom[($dom[$key]['parent'])];
18775 $firsttag = ($key == 1);
18776 // check for text direction attribute
18777 if (isset($tag['dir'])) {
18778 $this->setTempRTL($tag['dir']);
18780 $this->tmprtl
= false;
18782 if ($tag['block']) {
18783 $hbz = 0; // distance from y to line bottom
18784 $hb = 0; // vertical space between block tags
18785 // calculate vertical space for block tags
18786 if (isset($this->tagvspaces
[$tag['value']][0]['h']) AND ($this->tagvspaces
[$tag['value']][0]['h'] >= 0)) {
18787 $cur_h = $this->tagvspaces
[$tag['value']][0]['h'];
18788 } elseif (isset($tag['fontsize'])) {
18789 $cur_h = $this->getCellHeight($tag['fontsize'] / $this->k
);
18791 $cur_h = $this->getCellHeight($this->FontSize
);
18793 if (isset($this->tagvspaces
[$tag['value']][0]['n'])) {
18794 $on = $this->tagvspaces
[$tag['value']][0]['n'];
18795 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18800 if ((!isset($this->tagvspaces
[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br', 'hr')))) {
18803 $hb = ($on * $cur_h);
18805 if (($this->htmlvspace
<= 0) AND ($on > 0)) {
18806 if (isset($parent['fontsize'])) {
18807 $hbz = (($parent['fontsize'] / $this->k
) * $this->cell_height_ratio
);
18809 $hbz = $this->getCellHeight($this->FontSize
);
18812 if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) {
18813 // fix vertical space after table
18816 // closing vertical space
18818 if (isset($this->tagvspaces
[$tag['value']][1]['h']) AND ($this->tagvspaces
[$tag['value']][1]['h'] >= 0)) {
18819 $pre_h = $this->tagvspaces
[$tag['value']][1]['h'];
18820 } elseif (isset($parent['fontsize'])) {
18821 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k
);
18823 $pre_h = $this->getCellHeight($this->FontSize
);
18825 if (isset($this->tagvspaces
[$tag['value']][1]['n'])) {
18826 $cn = $this->tagvspaces
[$tag['value']][1]['n'];
18827 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18832 if (isset($this->tagvspaces
[$tag['value']][1])) {
18833 $hbc = ($cn * $pre_h);
18837 switch($tag['value']) {
18841 $dom[$key]['rowspans'] = array();
18842 if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
18843 $this->htmlvspace
= 0;
18844 // set table header
18845 if (!TCPDF_STATIC
::empty_string($dom[$key]['thead'])) {
18846 // set table header
18847 $this->thead
= $dom[$key]['thead'];
18848 if (!isset($this->theadMargins
) OR (empty($this->theadMargins
))) {
18849 $this->theadMargins
= array();
18850 $this->theadMargins
['cell_padding'] = $this->cell_padding
;
18851 $this->theadMargins
['lmargin'] = $this->lMargin
;
18852 $this->theadMargins
['rmargin'] = $this->rMargin
;
18853 $this->theadMargins
['page'] = $this->page
;
18854 $this->theadMargins
['cell'] = $cell;
18855 $this->theadMargins
['gvars'] = $this->getGraphicVars();
18859 // store current margins and page
18860 $dom[$key]['old_cell_padding'] = $this->cell_padding
;
18861 if (isset($tag['attribute']['cellpadding'])) {
18862 $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
18863 $this->SetCellPadding($pad);
18864 } elseif (isset($tag['padding'])) {
18865 $this->cell_padding
= $tag['padding'];
18867 if (isset($tag['attribute']['cellspacing'])) {
18868 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
18869 } elseif (isset($tag['border-spacing'])) {
18870 $cs = $tag['border-spacing']['V'];
18872 $prev_y = $this->y
;
18873 if ($this->checkPageBreak(((2 * $cp) +
(2 * $cs) +
$this->lasth
), '', false) OR ($this->y
< $prev_y)) {
18874 $this->inthead
= true;
18875 // add a page (or trig AcceptPageBreak() for multicolumn mode)
18876 $this->checkPageBreak($this->PageBreakTrigger +
1);
18881 // array of columns positions
18882 $dom[$key]['cellpos'] = array();
18886 if ((isset($tag['height'])) AND ($tag['height'] != '')) {
18887 $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
18889 $hrHeight = $this->GetLineWidth();
18891 $this->addHTMLVertSpace($hbz, max($hb, ($hrHeight / 2)), $cell, $firsttag);
18892 $x = $this->GetX();
18893 $y = $this->GetY();
18894 $wtmp = $this->w
- $this->lMargin
- $this->rMargin
;
18896 $wtmp -= ($this->cell_padding
['L'] +
$this->cell_padding
['R']);
18898 if ((isset($tag['width'])) AND ($tag['width'] != '')) {
18899 $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
18903 $prevlinewidth = $this->GetLineWidth();
18904 $this->SetLineWidth($hrHeight);
18905 $this->Line($x, $y, $x +
$hrWidth, $y);
18906 $this->SetLineWidth($prevlinewidth);
18907 $this->addHTMLVertSpace(max($hbc, ($hrHeight / 2)), 0, $cell, !isset($dom[($key +
1)]));
18911 if (array_key_exists('href', $tag['attribute'])) {
18912 $this->HREF
['url'] = $tag['attribute']['href'];
18917 if (!empty($tag['attribute']['src'])) {
18918 if ($tag['attribute']['src'][0] === '@') {
18920 $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1));
18924 $type = TCPDF_IMAGES
::getImageFileType($tag['attribute']['src']);
18926 if (!isset($tag['width'])) {
18929 if (!isset($tag['height'])) {
18930 $tag['height'] = 0;
18932 //if (!isset($tag['attribute']['align'])) {
18933 // the only alignment supported is "bottom"
18934 // further development is required for other modes.
18935 $tag['attribute']['align'] = 'bottom';
18937 switch($tag['attribute']['align']) {
18958 if (isset($this->HREF
['url']) AND !TCPDF_STATIC
::empty_string($this->HREF
['url'])) {
18959 $imglink = $this->HREF
['url'];
18960 if ($imglink[0] == '#') {
18961 // convert url to internal link
18962 $lnkdata = explode(',', $imglink);
18963 if (isset($lnkdata[0])) {
18964 $page = intval(substr($lnkdata[0], 1));
18965 if (empty($page) OR ($page <= 0)) {
18966 $page = $this->page
;
18968 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
18969 $lnky = floatval($lnkdata[1]);
18973 $imglink = $this->AddLink();
18974 $this->SetLink($imglink, $lnky, $page);
18979 if (isset($tag['border']) AND !empty($tag['border'])) {
18980 // currently only support 1 (frame) or a combination of 'LTRB'
18981 $border = $tag['border'];
18984 if (isset($tag['width'])) {
18985 $iw = $this->getHTMLUnitToUnits($tag['width'], ($tag['fontsize'] / $this->k
), 'px', false);
18988 if (isset($tag['height'])) {
18989 $ih = $this->getHTMLUnitToUnits($tag['height'], ($tag['fontsize'] / $this->k
), 'px', false);
18991 if (($type == 'eps') OR ($type == 'ai')) {
18992 $this->ImageEps($tag['attribute']['src'], $xpos, $this->y
, $iw, $ih, $imglink, true, $align, '', $border, true);
18993 } elseif ($type == 'svg') {
18994 $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y
, $iw, $ih, $imglink, $align, '', $border, true);
18996 $this->Image($tag['attribute']['src'], $xpos, $this->y
, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
19004 $this->y
= (($this->img_rb_y +
$prevy - ($this->getCellHeight($tag['fontsize'] / $this->k
))) / 2);
19008 $this->y
= $this->img_rb_y
- ($this->getCellHeight($tag['fontsize'] / $this->k
) - ($this->getFontDescent($tag['fontname'], $tag['fontstyle'], $tag['fontsize']) * $this->cell_height_ratio
));
19017 if ($this->listnum
== 1) {
19018 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19020 $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
19025 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19030 $this->rMargin +
= $this->listindent
;
19032 $this->lMargin +
= $this->listindent
;
19034 ++
$this->listindentlevel
;
19035 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19041 if ($tag['value'] == 'ol') {
19042 $this->listordered
[$this->listnum
] = true;
19044 $this->listordered
[$this->listnum
] = false;
19046 if (isset($tag['attribute']['start'])) {
19047 $this->listcount
[$this->listnum
] = intval($tag['attribute']['start']) - 1;
19049 $this->listcount
[$this->listnum
] = 0;
19052 $this->rMargin +
= $this->listindent
;
19053 $this->x
-= $this->listindent
;
19055 $this->lMargin +
= $this->listindent
;
19056 $this->x +
= $this->listindent
;
19058 ++
$this->listindentlevel
;
19059 if ($this->listnum
== 1) {
19061 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19064 $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
19070 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19072 if ($this->listordered
[$this->listnum
]) {
19074 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC
::empty_string($parent['attribute']['type'])) {
19075 $this->lispacer
= $parent['attribute']['type'];
19076 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC
::empty_string($parent['listtype'])) {
19077 $this->lispacer
= $parent['listtype'];
19078 } elseif (isset($this->lisymbol
) AND !TCPDF_STATIC
::empty_string($this->lisymbol
)) {
19079 $this->lispacer
= $this->lisymbol
;
19081 $this->lispacer
= '#';
19083 ++
$this->listcount
[$this->listnum
];
19084 if (isset($tag['attribute']['value'])) {
19085 $this->listcount
[$this->listnum
] = intval($tag['attribute']['value']);
19089 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC
::empty_string($parent['attribute']['type'])) {
19090 $this->lispacer
= $parent['attribute']['type'];
19091 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC
::empty_string($parent['listtype'])) {
19092 $this->lispacer
= $parent['listtype'];
19093 } elseif (isset($this->lisymbol
) AND !TCPDF_STATIC
::empty_string($this->lisymbol
)) {
19094 $this->lispacer
= $this->lisymbol
;
19096 $this->lispacer
= '!';
19101 case 'blockquote': {
19103 $this->rMargin +
= $this->listindent
;
19105 $this->lMargin +
= $this->listindent
;
19107 ++
$this->listindentlevel
;
19108 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19112 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19116 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19120 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19124 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19125 $this->premode
= true;
19129 $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt
) / $this->k
));
19133 $this->SetXY($this->GetX(), $this->GetY() +
((0.3 * $this->FontSizePt
) / $this->k
));
19142 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19145 // Form fields (since 4.8.000 - 2009-09-07)
19147 if (isset($tag['attribute']['action'])) {
19148 $this->form_action
= $tag['attribute']['action'];
19150 $this->Error('Please explicitly set action attribute path!');
19152 if (isset($tag['attribute']['enctype'])) {
19153 $this->form_enctype
= $tag['attribute']['enctype'];
19155 $this->form_enctype
= 'application/x-www-form-urlencoded';
19157 if (isset($tag['attribute']['method'])) {
19158 $this->form_mode
= $tag['attribute']['method'];
19160 $this->form_mode
= 'post';
19165 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['name'])) {
19166 $name = $tag['attribute']['name'];
19172 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['readonly'])) {
19173 $prop['readonly'] = true;
19175 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['value'])) {
19176 $value = $tag['attribute']['value'];
19178 if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['maxlength'])) {
19179 $opt['maxlen'] = intval($tag['attribute']['maxlength']);
19181 $h = $this->getCellHeight($this->FontSize
);
19182 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['size'])) {
19183 $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
19187 if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
19192 if (isset($tag['align'])) {
19193 switch ($tag['align']) {
19208 switch ($tag['attribute']['type']) {
19210 if (isset($value)) {
19211 $opt['v'] = $value;
19213 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19217 if (isset($value)) {
19218 $opt['v'] = $value;
19220 $prop['password'] = 'true';
19221 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19225 if (!isset($value)) {
19228 $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
19232 if (!isset($value)) {
19235 $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
19239 if (!isset($value)) {
19242 $w = $this->GetStringWidth($value) * 1.5;
19244 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19246 $action['S'] = 'SubmitForm';
19247 $action['F'] = $this->form_action
;
19248 if ($this->form_enctype
!= 'FDF') {
19249 $action['Flags'] = array('ExportFormat');
19251 if ($this->form_mode
== 'get') {
19252 $action['Flags'] = array('GetMethod');
19254 $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
19258 if (!isset($value)) {
19261 $w = $this->GetStringWidth($value) * 1.5;
19263 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19264 $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
19268 $prop['fileSelect'] = 'true';
19269 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19270 if (!isset($value)) {
19273 $w = $this->GetStringWidth($value) * 2;
19275 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19276 $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
19277 $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19281 if (isset($value)) {
19282 $opt['v'] = $value;
19284 $opt['f'] = array('invisible', 'hidden');
19285 $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
19289 // THIS TYPE MUST BE FIXED
19290 if (isset($tag['attribute']['src']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['src'])) {
19291 $img = $tag['attribute']['src'];
19296 //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
19297 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19298 $jsaction = $tag['attribute']['onclick'];
19302 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19306 if (!isset($value)) {
19309 $w = $this->GetStringWidth($value) * 1.5;
19311 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19312 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19313 $jsaction = $tag['attribute']['onclick'];
19317 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19326 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['readonly'])) {
19327 $prop['readonly'] = true;
19329 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['name'])) {
19330 $name = $tag['attribute']['name'];
19334 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['value'])) {
19335 $opt['v'] = $tag['attribute']['value'];
19337 if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['cols'])) {
19338 $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
19342 if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['rows'])) {
19343 $h = intval($tag['attribute']['rows']) * $this->getCellHeight($this->FontSize
);
19347 $prop['multiline'] = 'true';
19348 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19352 $h = $this->getCellHeight($this->FontSize
);
19353 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['size'])) {
19354 $h *= ($tag['attribute']['size'] +
1);
19358 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['name'])) {
19359 $name = $tag['attribute']['name'];
19364 if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['opt'])) {
19365 $options = explode('#!NwL!#', $tag['attribute']['opt']);
19367 foreach ($options as $val) {
19368 if (strpos($val, '#!TaB!#') !== false) {
19369 $opts = explode('#!TaB!#', $val);
19371 $w = max($w, $this->GetStringWidth($opts[1]));
19374 $w = max($w, $this->GetStringWidth($val));
19381 if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
19382 $prop['multipleSelection'] = 'true';
19383 $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19385 $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19390 if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML
=== true)) {
19391 // Special tag used to call TCPDF methods
19392 if (isset($tag['attribute']['method'])) {
19393 $tcpdf_method = $tag['attribute']['method'];
19394 if (method_exists($this, $tcpdf_method)) {
19395 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
19396 $params = TCPDF_STATIC
::unserializeTCPDFtagParameters($tag['attribute']['params']);
19397 call_user_func_array(array($this, $tcpdf_method), $params);
19399 $this->$tcpdf_method();
19401 $this->newline
= true;
19411 // define tags that support borders and background colors
19412 $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
19413 if (in_array($tag['value'], $bordertags)) {
19415 $dom[$key]['borderposition'] = $this->getBorderStartPosition();
19417 if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
19418 $pba = $dom[$key]['attribute']['pagebreakafter'];
19419 // check for pagebreak
19420 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19421 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19422 $this->checkPageBreak($this->PageBreakTrigger +
1);
19424 if ((($pba == 'left') AND (((!$this->rtl
) AND (($this->page %
2) == 0)) OR (($this->rtl
) AND (($this->page %
2) != 0))))
19425 OR (($pba == 'right') AND (((!$this->rtl
) AND (($this->page %
2) != 0)) OR (($this->rtl
) AND (($this->page %
2) == 0))))) {
19426 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19427 $this->checkPageBreak($this->PageBreakTrigger +
1);
19434 * Process closing tags.
19435 * @param $dom (array) html dom array
19436 * @param $key (int) current element id
19437 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
19438 * @param $maxbottomliney (int) maximum y value of current line
19439 * @return $dom array
19442 protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
19444 $parent = $dom[($dom[$key]['parent'])];
19445 $lasttag = ((!isset($dom[($key +
1)])) OR ((!isset($dom[($key +
2)])) AND ($dom[($key +
1)]['value'] == 'marker')));
19446 $in_table_head = false;
19447 // maximum x position (used to draw borders)
19453 if ($tag['block']) {
19454 $hbz = 0; // distance from y to line bottom
19455 $hb = 0; // vertical space between block tags
19456 // calculate vertical space for block tags
19457 if (isset($this->tagvspaces
[$tag['value']][1]['h']) AND ($this->tagvspaces
[$tag['value']][1]['h'] >= 0)) {
19458 $pre_h = $this->tagvspaces
[$tag['value']][1]['h'];
19459 } elseif (isset($parent['fontsize'])) {
19460 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k
);
19462 $pre_h = $this->getCellHeight($this->FontSize
);
19464 if (isset($this->tagvspaces
[$tag['value']][1]['n'])) {
19465 $cn = $this->tagvspaces
[$tag['value']][1]['n'];
19466 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
19471 if ((!isset($this->tagvspaces
[$tag['value']])) AND ($tag['value'] == 'div')) {
19474 $hb = ($cn * $pre_h);
19476 if ($maxbottomliney > $this->PageBreakTrigger
) {
19477 $hbz = $this->getCellHeight($this->FontSize
);
19478 } elseif ($this->y
< $maxbottomliney) {
19479 $hbz = ($maxbottomliney - $this->y
);
19483 switch($tag['value']) {
19485 $table_el = $dom[($dom[$key]['parent'])]['parent'];
19486 if (!isset($parent['endy'])) {
19487 $dom[($dom[$key]['parent'])]['endy'] = $this->y
;
19488 $parent['endy'] = $this->y
;
19490 if (!isset($parent['endpage'])) {
19491 $dom[($dom[$key]['parent'])]['endpage'] = $this->page
;
19492 $parent['endpage'] = $this->page
;
19494 if (!isset($parent['endcolumn'])) {
19495 $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column
;
19496 $parent['endcolumn'] = $this->current_column
;
19498 // update row-spanned cells
19499 if (isset($dom[$table_el]['rowspans'])) {
19500 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19501 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
19502 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19503 if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
19504 $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
19505 } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
19506 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19507 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19508 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19512 // report new endy and endpage to the rowspanned cells
19513 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19514 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19515 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
19516 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19517 $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
19518 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19519 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
19520 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19523 // update remaining rowspanned cells
19524 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19525 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19526 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
19527 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
19528 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
19532 $prev_page = $this->page
;
19533 $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
19534 if ($this->num_columns
> 1) {
19535 if ((($this->current_column
== 0) AND ($dom[($dom[$key]['parent'])]['endcolumn'] == ($this->num_columns
- 1)))
19536 OR (($this->current_column
== $dom[($dom[$key]['parent'])]['endcolumn']) AND ($prev_page < $this->page
))) {
19538 $this->selectColumn(0);
19539 $dom[($dom[$key]['parent'])]['endcolumn'] = 0;
19540 $dom[($dom[$key]['parent'])]['endy'] = $this->y
;
19542 $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
19543 $this->y
= $dom[($dom[$key]['parent'])]['endy'];
19546 $this->y
= $dom[($dom[$key]['parent'])]['endy'];
19548 if (isset($dom[$table_el]['attribute']['cellspacing'])) {
19549 $this->y +
= $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
19550 } elseif (isset($dom[$table_el]['border-spacing'])) {
19551 $this->y +
= $dom[$table_el]['border-spacing']['V'];
19553 $this->Ln(0, $cell);
19554 if ($this->current_column
== $parent['startcolumn']) {
19555 $this->x
= $parent['startx'];
19557 // account for booklet mode
19558 if ($this->page
> $parent['startpage']) {
19559 if (($this->rtl
) AND ($this->pagedim
[$this->page
]['orm'] != $this->pagedim
[$parent['startpage']]['orm'])) {
19560 $this->x
-= ($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$parent['startpage']]['orm']);
19561 } elseif ((!$this->rtl
) AND ($this->pagedim
[$this->page
]['olm'] != $this->pagedim
[$parent['startpage']]['olm'])) {
19562 $this->x +
= ($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$parent['startpage']]['olm']);
19568 // closing tag used for the thead part
19569 $in_table_head = true;
19570 $this->inthead
= false;
19572 $table_el = $parent;
19573 // set default border
19574 if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
19575 // set default border
19576 $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
19580 $default_border = $border;
19581 // fix bottom line alignment of last line before page break
19582 foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
19583 // update row-spanned cells
19584 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19585 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19586 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) {
19587 $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
19589 if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) {
19590 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
19594 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
19595 $pgendy = $this->pagedim
[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim
[$dom[$prevtrkey]['endpage']]['bm'];
19596 $dom[$prevtrkey]['endy'] = $pgendy;
19597 // update row-spanned cells
19598 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19599 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19600 if (($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
19601 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
19602 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
19607 $prevtrkey = $trkey;
19608 $table_el = $dom[($dom[$key]['parent'])];
19611 if (count($table_el['trids']) > 0) {
19614 foreach ($table_el['trids'] as $j => $trkey) {
19615 $parent = $dom[$trkey];
19616 if (!isset($xmax)) {
19617 $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
19619 // for each cell on the row
19620 foreach ($parent['cellpos'] as $k => $cellpos) {
19621 if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
19622 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
19623 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
19624 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
19625 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
19626 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
19627 $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
19628 $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
19630 $endy = $parent['endy'];
19631 $startpage = $parent['startpage'];
19632 $endpage = $parent['endpage'];
19633 $startcolumn = $parent['startcolumn'];
19634 $endcolumn = $parent['endcolumn'];
19636 if ($this->num_columns
== 0) {
19637 $this->num_columns
= 1;
19639 if (isset($cellpos['border'])) {
19640 $border = $cellpos['border'];
19642 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
19643 $this->SetFillColorArray($cellpos['bgcolor']);
19648 $x = $cellpos['startx'];
19649 $y = $parent['starty'];
19651 $w = abs($cellpos['endx'] - $cellpos['startx']);
19652 // get border modes
19653 $border_start = TCPDF_STATIC
::getBorderMode($border, $position='start', $this->opencell
);
19654 $border_end = TCPDF_STATIC
::getBorderMode($border, $position='end', $this->opencell
);
19655 $border_middle = TCPDF_STATIC
::getBorderMode($border, $position='middle', $this->opencell
);
19656 // design borders around HTML cells.
19657 for ($page = $startpage; $page <= $endpage; ++
$page) { // for each page
19659 $this->setPage($page);
19660 if ($this->num_columns
< 2) {
19661 // single-column mode
19663 $this->y
= $this->tMargin
;
19665 // account for margin changes
19666 if ($page > $startpage) {
19667 if (($this->rtl
) AND ($this->pagedim
[$page]['orm'] != $this->pagedim
[$startpage]['orm'])) {
19668 $this->x
-= ($this->pagedim
[$page]['orm'] - $this->pagedim
[$startpage]['orm']);
19669 } elseif ((!$this->rtl
) AND ($this->pagedim
[$page]['olm'] != $this->pagedim
[$startpage]['olm'])) {
19670 $this->x +
= ($this->pagedim
[$page]['olm'] - $this->pagedim
[$startpage]['olm']);
19673 if ($startpage == $endpage) { // single page
19676 for ($column = $startcolumn; $column <= $endcolumn; ++
$column) { // for each column
19677 $this->selectColumn($column);
19678 if ($startcolumn == $endcolumn) { // single column
19679 $cborder = $border;
19680 $h = $endy - $parent['starty'];
19683 } elseif ($column == $startcolumn) { // first column
19684 $cborder = $border_start;
19685 $this->y
= $starty;
19687 $h = $this->h
- $this->y
- $this->bMargin
;
19689 $deltacol = $this->x +
$this->rMargin
- $this->w
;
19691 $deltacol = $this->x
- $this->lMargin
;
19693 } elseif ($column == $endcolumn) { // end column
19694 $cborder = $border_end;
19695 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19696 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19698 $this->x +
= $deltacol;
19699 $h = $endy - $this->y
;
19700 } else { // middle column
19701 $cborder = $border_middle;
19702 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19703 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19705 $this->x +
= $deltacol;
19706 $h = $this->h
- $this->y
- $this->bMargin
;
19708 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19709 } // end for each column
19710 } elseif ($page == $startpage) { // first page
19713 for ($column = $startcolumn; $column < $this->num_columns
; ++
$column) { // for each column
19714 $this->selectColumn($column);
19715 if ($column == $startcolumn) { // first column
19716 $cborder = $border_start;
19717 $this->y
= $starty;
19719 $h = $this->h
- $this->y
- $this->bMargin
;
19721 $deltacol = $this->x +
$this->rMargin
- $this->w
;
19723 $deltacol = $this->x
- $this->lMargin
;
19725 } else { // middle column
19726 $cborder = $border_middle;
19727 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19728 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19730 $this->x +
= $deltacol;
19731 $h = $this->h
- $this->y
- $this->bMargin
;
19733 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19734 } // end for each column
19735 } elseif ($page == $endpage) { // last page
19738 for ($column = 0; $column <= $endcolumn; ++
$column) { // for each column
19739 $this->selectColumn($column);
19740 if ($column == $endcolumn) { // end column
19741 $cborder = $border_end;
19742 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19743 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19745 $this->x +
= $deltacol;
19746 $h = $endy - $this->y
;
19747 } else { // middle column
19748 $cborder = $border_middle;
19749 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19750 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19752 $this->x +
= $deltacol;
19753 $h = $this->h
- $this->y
- $this->bMargin
;
19755 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19756 } // end for each column
19757 } else { // middle page
19760 for ($column = 0; $column < $this->num_columns
; ++
$column) { // for each column
19761 $this->selectColumn($column);
19762 $cborder = $border_middle;
19763 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19764 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19766 $this->x +
= $deltacol;
19767 $h = $this->h
- $this->y
- $this->bMargin
;
19768 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19769 } // end for each column
19771 if ($cborder OR $fill) {
19772 $offsetlen = strlen($ccode);
19773 // draw border and fill
19774 if ($this->inxobj
) {
19775 // we are inside an XObject template
19776 if (end($this->xobjects
[$this->xobjid
]['transfmrk']) !== false) {
19777 $pagemarkkey = key($this->xobjects
[$this->xobjid
]['transfmrk']);
19778 $pagemark = $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey];
19779 $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey] +
= $offsetlen;
19781 $pagemark = $this->xobjects
[$this->xobjid
]['intmrk'];
19782 $this->xobjects
[$this->xobjid
]['intmrk'] +
= $offsetlen;
19784 $pagebuff = $this->xobjects
[$this->xobjid
]['outdata'];
19785 $pstart = substr($pagebuff, 0, $pagemark);
19786 $pend = substr($pagebuff, $pagemark);
19787 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$ccode.$pend;
19789 // draw border and fill
19790 if (end($this->transfmrk
[$this->page
]) !== false) {
19791 $pagemarkkey = key($this->transfmrk
[$this->page
]);
19792 $pagemark = $this->transfmrk
[$this->page
][$pagemarkkey];
19793 } elseif ($this->InFooter
) {
19794 $pagemark = $this->footerpos
[$this->page
];
19796 $pagemark = $this->intmrk
[$this->page
];
19798 $pagebuff = $this->getPageBuffer($this->page
);
19799 $pstart = substr($pagebuff, 0, $pagemark);
19800 $pend = substr($pagebuff, $pagemark);
19801 $this->setPageBuffer($this->page
, $pstart.$ccode.$pend);
19804 } // end for each page
19805 // restore default border
19806 $border = $default_border;
19807 } // end for each cell on the row
19808 if (isset($table_el['attribute']['cellspacing'])) {
19809 $this->y +
= $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
19810 } elseif (isset($table_el['border-spacing'])) {
19811 $this->y +
= $table_el['border-spacing']['V'];
19813 $this->Ln(0, $cell);
19814 $this->x
= $parent['startx'];
19815 if ($endpage > $startpage) {
19816 if (($this->rtl
) AND ($this->pagedim
[$endpage]['orm'] != $this->pagedim
[$startpage]['orm'])) {
19817 $this->x +
= ($this->pagedim
[$endpage]['orm'] - $this->pagedim
[$startpage]['orm']);
19818 } elseif ((!$this->rtl
) AND ($this->pagedim
[$endpage]['olm'] != $this->pagedim
[$startpage]['olm'])) {
19819 $this->x +
= ($this->pagedim
[$endpage]['olm'] - $this->pagedim
[$startpage]['olm']);
19823 if (!$in_table_head) { // we are not inside a thead section
19824 $this->cell_padding
= $table_el['old_cell_padding'];
19825 // reset row height
19826 $this->resetLastH();
19827 if (($this->page
== ($this->numpages
- 1)) AND ($this->pageopen
[$this->numpages
])) {
19828 $plendiff = ($this->pagelen
[$this->numpages
] - $this->emptypagemrk
[$this->numpages
]);
19829 if (($plendiff > 0) AND ($plendiff < 60)) {
19830 $pagediff = substr($this->getPageBuffer($this->numpages
), $this->emptypagemrk
[$this->numpages
], $plendiff);
19831 if (substr($pagediff, 0, 5) == 'BT /F') {
19832 // the difference is only a font setting
19836 if ($plendiff == 0) {
19837 // remove last blank page
19838 $this->deletePage($this->numpages
);
19841 if (isset($this->theadMargins
['top'])) {
19842 // restore top margin
19843 $this->tMargin
= $this->theadMargins
['top'];
19845 if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
19846 // reset main table header
19848 $this->theadMargins
= array();
19849 $this->pagedim
[$this->page
]['tm'] = $this->tMargin
;
19852 $parent = $table_el;
19860 $this->SetXY($this->GetX(), $this->GetY() +
((0.7 * $parent['fontsize']) / $this->k
));
19864 $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k
));
19868 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19871 case 'blockquote': {
19873 $this->rMargin
-= $this->listindent
;
19875 $this->lMargin
-= $this->listindent
;
19877 --$this->listindentlevel
;
19878 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19882 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19886 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19887 $this->premode
= false;
19892 if ($this->listnum
<= 0) {
19893 $this->listnum
= 0;
19894 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19896 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19898 $this->resetLastH();
19902 $this->lispacer
= '';
19903 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19907 $this->lispacer
= '';
19909 $this->rMargin
-= $this->listindent
;
19911 $this->lMargin
-= $this->listindent
;
19913 --$this->listindentlevel
;
19914 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19920 $this->lispacer
= '';
19922 $this->rMargin
-= $this->listindent
;
19924 $this->lMargin
-= $this->listindent
;
19926 --$this->listindentlevel
;
19927 if ($this->listnum
<= 0) {
19928 $this->listnum
= 0;
19929 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19931 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19933 $this->resetLastH();
19937 $this->lispacer
= '';
19938 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19947 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19950 // Form fields (since 4.8.000 - 2009-09-07)
19952 $this->form_action
= '';
19953 $this->form_enctype
= 'application/x-www-form-urlencoded';
19960 // draw border and background (if any)
19961 $this->drawHTMLTagBorder($parent, $xmax);
19962 if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
19963 $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
19964 // check for pagebreak
19965 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19966 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19967 $this->checkPageBreak($this->PageBreakTrigger +
1);
19969 if ((($pba == 'left') AND (((!$this->rtl
) AND (($this->page %
2) == 0)) OR (($this->rtl
) AND (($this->page %
2) != 0))))
19970 OR (($pba == 'right') AND (((!$this->rtl
) AND (($this->page %
2) != 0)) OR (($this->rtl
) AND (($this->page %
2) == 0))))) {
19971 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19972 $this->checkPageBreak($this->PageBreakTrigger +
1);
19975 $this->tmprtl
= false;
19980 * Add vertical spaces if needed.
19981 * @param $hbz (string) Distance between current y and line bottom.
19982 * @param $hb (string) The height of the break.
19983 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
19984 * @param $firsttag (boolean) set to true when the tag is the first.
19985 * @param $lasttag (boolean) set to true when the tag is the last.
19988 protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
19990 $this->Ln(0, $cell);
19991 $this->htmlvspace
= 0;
19995 $this->Ln($hbz, $cell);
19996 $this->htmlvspace
= 0;
19999 if ($hb < $this->htmlvspace
) {
20002 $hd = $hb - $this->htmlvspace
;
20003 $this->htmlvspace
= $hb;
20005 $this->Ln(($hbz +
$hd), $cell);
20009 * Return the starting coordinates to draw an html border
20010 * @return array containing top-left border coordinates
20012 * @since 5.7.000 (2010-08-03)
20014 protected function getBorderStartPosition() {
20016 $xmax = $this->lMargin
;
20018 $xmax = $this->w
- $this->rMargin
;
20020 return array('page' => $this->page
, 'column' => $this->current_column
, 'x' => $this->x
, 'y' => $this->y
, 'xmax' => $xmax);
20024 * Draw an HTML block border and fill
20025 * @param $tag (array) array of tag properties.
20026 * @param $xmax (int) end X coordinate for border.
20028 * @since 5.7.000 (2010-08-03)
20030 protected function drawHTMLTagBorder($tag, $xmax) {
20031 if (!isset($tag['borderposition'])) {
20035 $prev_x = $this->x
;
20036 $prev_y = $this->y
;
20037 $prev_lasth = $this->lasth
;
20041 if (isset($tag['border']) AND !empty($tag['border'])) {
20042 // get border style
20043 $border = $tag['border'];
20044 if (!TCPDF_STATIC
::empty_string($this->thead
) AND (!$this->inthead
)) {
20045 // border for table header
20046 $border = TCPDF_STATIC
::getBorderMode($border, $position='middle', $this->opencell
);
20049 if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
20050 // get background color
20051 $old_bgcolor = $this->bgcolor
;
20052 $this->SetFillColorArray($tag['bgcolor']);
20055 if (!$border AND !$fill) {
20059 if (isset($tag['attribute']['cellspacing'])) {
20060 $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
20061 $cellspacing = array('H' => $clsp, 'V' => $clsp);
20062 } elseif (isset($tag['border-spacing'])) {
20063 $cellspacing = $tag['border-spacing'];
20065 $cellspacing = array('H' => 0, 'V' => 0);
20067 if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
20068 // draw the border externally respect the sqare edge.
20069 $border['mode'] = 'ext';
20072 if ($xmax >= $tag['borderposition']['x']) {
20073 $xmax = $tag['borderposition']['xmax'];
20075 $w = ($tag['borderposition']['x'] - $xmax);
20077 if ($xmax <= $tag['borderposition']['x']) {
20078 $xmax = $tag['borderposition']['xmax'];
20080 $w = ($xmax - $tag['borderposition']['x']);
20085 $w +
= $cellspacing['H'];
20086 $startpage = $tag['borderposition']['page'];
20087 $startcolumn = $tag['borderposition']['column'];
20088 $x = $tag['borderposition']['x'];
20089 $y = $tag['borderposition']['y'];
20090 $endpage = $this->page
;
20091 $starty = $tag['borderposition']['y'] - $cellspacing['V'];
20092 $currentY = $this->y
;
20094 // get latest column
20095 $endcolumn = $this->current_column
;
20096 if ($this->num_columns
== 0) {
20097 $this->num_columns
= 1;
20099 // get border modes
20100 $border_start = TCPDF_STATIC
::getBorderMode($border, $position='start', $this->opencell
);
20101 $border_end = TCPDF_STATIC
::getBorderMode($border, $position='end', $this->opencell
);
20102 $border_middle = TCPDF_STATIC
::getBorderMode($border, $position='middle', $this->opencell
);
20103 // temporary disable page regions
20104 $temp_page_regions = $this->page_regions
;
20105 $this->page_regions
= array();
20106 // design borders around HTML cells.
20107 for ($page = $startpage; $page <= $endpage; ++
$page) { // for each page
20109 $this->setPage($page);
20110 if ($this->num_columns
< 2) {
20111 // single-column mode
20113 $this->y
= $this->tMargin
;
20115 // account for margin changes
20116 if ($page > $startpage) {
20117 if (($this->rtl
) AND ($this->pagedim
[$page]['orm'] != $this->pagedim
[$startpage]['orm'])) {
20118 $this->x
-= ($this->pagedim
[$page]['orm'] - $this->pagedim
[$startpage]['orm']);
20119 } elseif ((!$this->rtl
) AND ($this->pagedim
[$page]['olm'] != $this->pagedim
[$startpage]['olm'])) {
20120 $this->x +
= ($this->pagedim
[$page]['olm'] - $this->pagedim
[$startpage]['olm']);
20123 if ($startpage == $endpage) {
20125 for ($column = $startcolumn; $column <= $endcolumn; ++
$column) { // for each column
20126 $this->selectColumn($column);
20127 if ($startcolumn == $endcolumn) { // single column
20128 $cborder = $border;
20129 $h = ($currentY - $y) +
$cellspacing['V'];
20130 $this->y
= $starty;
20131 } elseif ($column == $startcolumn) { // first column
20132 $cborder = $border_start;
20133 $this->y
= $starty;
20134 $h = $this->h
- $this->y
- $this->bMargin
;
20135 } elseif ($column == $endcolumn) { // end column
20136 $cborder = $border_end;
20137 $h = $currentY - $this->y
;
20138 } else { // middle column
20139 $cborder = $border_middle;
20140 $h = $this->h
- $this->y
- $this->bMargin
;
20142 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20143 } // end for each column
20144 } elseif ($page == $startpage) { // first page
20145 for ($column = $startcolumn; $column < $this->num_columns
; ++
$column) { // for each column
20146 $this->selectColumn($column);
20147 if ($column == $startcolumn) { // first column
20148 $cborder = $border_start;
20149 $this->y
= $starty;
20150 $h = $this->h
- $this->y
- $this->bMargin
;
20151 } else { // middle column
20152 $cborder = $border_middle;
20153 $h = $this->h
- $this->y
- $this->bMargin
;
20155 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20156 } // end for each column
20157 } elseif ($page == $endpage) { // last page
20158 for ($column = 0; $column <= $endcolumn; ++
$column) { // for each column
20159 $this->selectColumn($column);
20160 if ($column == $endcolumn) {
20162 $cborder = $border_end;
20163 $h = $currentY - $this->y
;
20166 $cborder = $border_middle;
20167 $h = $this->h
- $this->y
- $this->bMargin
;
20169 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20170 } // end for each column
20171 } else { // middle page
20172 for ($column = 0; $column < $this->num_columns
; ++
$column) { // for each column
20173 $this->selectColumn($column);
20174 $cborder = $border_middle;
20175 $h = $this->h
- $this->y
- $this->bMargin
;
20176 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20177 } // end for each column
20179 if ($cborder OR $fill) {
20180 $offsetlen = strlen($ccode);
20181 // draw border and fill
20182 if ($this->inxobj
) {
20183 // we are inside an XObject template
20184 if (end($this->xobjects
[$this->xobjid
]['transfmrk']) !== false) {
20185 $pagemarkkey = key($this->xobjects
[$this->xobjid
]['transfmrk']);
20186 $pagemark = $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey];
20187 $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey] +
= $offsetlen;
20189 $pagemark = $this->xobjects
[$this->xobjid
]['intmrk'];
20190 $this->xobjects
[$this->xobjid
]['intmrk'] +
= $offsetlen;
20192 $pagebuff = $this->xobjects
[$this->xobjid
]['outdata'];
20193 $pstart = substr($pagebuff, 0, $pagemark);
20194 $pend = substr($pagebuff, $pagemark);
20195 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$ccode.$pend;
20197 if (end($this->transfmrk
[$this->page
]) !== false) {
20198 $pagemarkkey = key($this->transfmrk
[$this->page
]);
20199 $pagemark = $this->transfmrk
[$this->page
][$pagemarkkey];
20200 } elseif ($this->InFooter
) {
20201 $pagemark = $this->footerpos
[$this->page
];
20203 $pagemark = $this->intmrk
[$this->page
];
20205 $pagebuff = $this->getPageBuffer($this->page
);
20206 $pstart = substr($pagebuff, 0, $pagemark);
20207 $pend = substr($pagebuff, $pagemark);
20208 $this->setPageBuffer($this->page
, $pstart.$ccode.$pend);
20209 $this->bordermrk
[$this->page
] +
= $offsetlen;
20210 $this->cntmrk
[$this->page
] +
= $offsetlen;
20213 } // end for each page
20214 // restore page regions
20215 $this->page_regions
= $temp_page_regions;
20216 if (isset($old_bgcolor)) {
20217 // restore background color
20218 $this->SetFillColorArray($old_bgcolor);
20220 // restore pointer position
20221 $this->x
= $prev_x;
20222 $this->y
= $prev_y;
20223 $this->lasth
= $prev_lasth;
20227 * Set the default bullet to be used as LI bullet symbol
20228 * @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')
20230 * @since 4.0.028 (2008-09-26)
20232 public function setLIsymbol($symbol='!') {
20233 // check for custom image symbol
20234 if (substr($symbol, 0, 4) == 'img|') {
20235 $this->lisymbol
= $symbol;
20238 $symbol = strtolower($symbol);
20239 $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');
20240 if (in_array($symbol, $valid_symbols)) {
20241 $this->lisymbol
= $symbol;
20243 $this->lisymbol
= '';
20248 * Set the booklet mode for double-sided pages.
20249 * @param $booklet (boolean) true set the booklet mode on, false otherwise.
20250 * @param $inner (float) Inner page margin.
20251 * @param $outer (float) Outer page margin.
20253 * @since 4.2.000 (2008-10-29)
20255 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
20256 $this->booklet
= $booklet;
20258 $this->lMargin
= $inner;
20261 $this->rMargin
= $outer;
20266 * Swap the left and right margins.
20267 * @param $reverse (boolean) if true swap left and right margins.
20269 * @since 4.2.000 (2008-10-29)
20271 protected function swapMargins($reverse=true) {
20273 // swap left and right margins
20274 $mtemp = $this->original_lMargin
;
20275 $this->original_lMargin
= $this->original_rMargin
;
20276 $this->original_rMargin
= $mtemp;
20277 $deltam = $this->original_lMargin
- $this->original_rMargin
;
20278 $this->lMargin +
= $deltam;
20279 $this->rMargin
-= $deltam;
20284 * Set the vertical spaces for HTML tags.
20285 * The array must have the following structure (example):
20286 * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1)));
20287 * The first array level contains the tag names,
20288 * the second level contains 0 for opening tags or 1 for closing tags,
20289 * the third level contains the vertical space unit (h) and the number spaces to add (n).
20290 * If the h parameter is not specified, default values are used.
20291 * @param $tagvs (array) array of tags and relative vertical spaces.
20293 * @since 4.2.001 (2008-10-30)
20295 public function setHtmlVSpace($tagvs) {
20296 $this->tagvspaces
= $tagvs;
20300 * Set custom width for list indentation.
20301 * @param $width (float) width of the indentation. Use negative value to disable it.
20303 * @since 4.2.007 (2008-11-12)
20305 public function setListIndentWidth($width) {
20306 return $this->customlistindent
= floatval($width);
20310 * Set the top/bottom cell sides to be open or closed when the cell cross the page.
20311 * @param $isopen (boolean) if true keeps the top/bottom border open for the cell sides that cross the page.
20313 * @since 4.2.010 (2008-11-14)
20315 public function setOpenCell($isopen) {
20316 $this->opencell
= $isopen;
20320 * Set the color and font style for HTML links.
20321 * @param $color (array) RGB array of colors
20322 * @param $fontstyle (string) additional font styles to add
20324 * @since 4.4.003 (2008-12-09)
20326 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
20327 $this->htmlLinkColorArray
= $color;
20328 $this->htmlLinkFontStyle
= $fontstyle;
20332 * Convert HTML string containing value and unit of measure to user's units or points.
20333 * @param $htmlval (string) String containing values and unit.
20334 * @param $refsize (string) Reference value in points.
20335 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
20336 * @param $points (boolean) If true returns points, otherwise returns value in user's units.
20337 * @return float value in user's unit or point if $points=true
20339 * @since 4.4.004 (2008-12-10)
20341 public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
20342 $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
20351 if (in_array($defaultunit, $supportedunits)) {
20352 $unit = $defaultunit;
20354 if (is_numeric($htmlval)) {
20355 $value = floatval($htmlval);
20356 } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
20357 $value = floatval($mnum[1]);
20358 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
20359 if (in_array($munit[1], $supportedunits)) {
20367 $retval = (($value * $refsize) / 100);
20372 $retval = ($value * $refsize);
20375 // height of lower case 'x' (about half the font-size)
20377 $retval = ($value * ($refsize / 2));
20382 $retval = (($value * $this->dpi
) / $k);
20387 $retval = (($value / 2.54 * $this->dpi
) / $k);
20392 $retval = (($value / 25.4 * $this->dpi
) / $k);
20395 // one pica is 12 points
20397 $retval = (($value * 12) / $k);
20402 $retval = ($value / $k);
20407 $retval = $this->pixelsToUnits($value);
20409 $retval *= $this->k
;
20418 * Output an HTML list bullet or ordered item symbol
20419 * @param $listdepth (int) list nesting level
20420 * @param $listtype (string) type of list
20421 * @param $size (float) current font size
20423 * @since 4.4.004 (2008-12-10)
20425 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
20426 if ($this->state
!= 2) {
20431 $bgcolor = $this->bgcolor
;
20432 $color = $this->fgcolor
;
20433 $strokecolor = $this->strokecolor
;
20437 $lspace = $this->GetStringWidth(' ');
20438 if ($listtype == '^') {
20439 // special symbol used for avoid justification of rect bullet
20440 $this->lispacer
= '';
20442 } elseif ($listtype == '!') {
20443 // set default list type for unordered list
20444 $deftypes = array('disc', 'circle', 'square');
20445 $listtype = $deftypes[($listdepth - 1) %
3];
20446 } elseif ($listtype == '#') {
20447 // set default list type for ordered list
20448 $listtype = 'decimal';
20449 } elseif (substr($listtype, 0, 4) == 'img|') {
20450 // custom image type ('img|type|width|height|image.ext')
20451 $img = explode('|', $listtype);
20454 switch ($listtype) {
20461 $lspace +
= (2 * $r);
20463 $this->x +
= $lspace;
20465 $this->x
-= $lspace;
20467 $this->Circle(($this->x +
$r), ($this->y +
($this->lasth
/ 2)), $r, 0, 360, 'F', array(), $color, 8);
20472 $lspace +
= (2 * $r);
20474 $this->x +
= $lspace;
20476 $this->x
-= $lspace;
20478 $prev_line_style = $this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
;
20479 $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
20480 $this->Circle(($this->x +
$r), ($this->y +
($this->lasth
/ 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
20481 $this->_out($prev_line_style); // restore line settings
20488 $this->x +
= $lspace;
20490 $this->x
-= $lspace;
20492 $this->Rect($this->x
, ($this->y +
(($this->lasth
- $l) / 2)), $l, $l, 'F', array(), $color);
20496 // 1=>type, 2=>width, 3=>height, 4=>image.ext
20497 $lspace +
= $img[2];
20499 $this->x +
= $lspace;
20501 $this->x
-= $lspace;
20503 $imgtype = strtolower($img[1]);
20504 $prev_y = $this->y
;
20505 switch ($imgtype) {
20507 $this->ImageSVG($img[4], $this->x
, ($this->y +
(($this->lasth
- $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
20512 $this->ImageEps($img[4], $this->x
, ($this->y +
(($this->lasth
- $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
20516 $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);
20520 $this->y
= $prev_y;
20524 // $this->listcount[$this->listnum];
20528 $textitem = $this->listcount
[$this->listnum
];
20531 case 'decimal-leading-zero': {
20532 $textitem = sprintf('%02d', $this->listcount
[$this->listnum
]);
20536 case 'lower-roman': {
20537 $textitem = strtolower(TCPDF_STATIC
::intToRoman($this->listcount
[$this->listnum
]));
20541 case 'upper-roman': {
20542 $textitem = TCPDF_STATIC
::intToRoman($this->listcount
[$this->listnum
]);
20546 case 'lower-alpha':
20547 case 'lower-latin': {
20548 $textitem = chr(97 +
$this->listcount
[$this->listnum
] - 1);
20552 case 'upper-alpha':
20553 case 'upper-latin': {
20554 $textitem = chr(65 +
$this->listcount
[$this->listnum
] - 1);
20557 case 'lower-greek': {
20558 $textitem = TCPDF_FONTS
::unichr((945 +
$this->listcount
[$this->listnum
] - 1), $this->isunicode
);
20562 // Types to be implemented (special handling)
20572 case 'cjk-ideographic': {
20581 case 'hiragana-iroha': {
20584 case 'katakana-iroha': {
20589 $textitem = $this->listcount
[$this->listnum
];
20592 if (!TCPDF_STATIC
::empty_string($textitem)) {
20593 // Check whether we need a new page or new column
20594 $prev_y = $this->y
;
20595 $h = $this->getCellHeight($this->FontSize
);
20596 if ($this->checkPageBreak($h) OR ($this->y
< $prev_y)) {
20599 // print ordered item
20601 $textitem = '.'.$textitem;
20603 $textitem = $textitem.'.';
20605 $lspace +
= $this->GetStringWidth($textitem);
20607 $this->x +
= $lspace;
20609 $this->x
-= $lspace;
20611 $this->Write($this->lasth
, $textitem, '', false, '', false, 0, false);
20614 $this->lispacer
= '^';
20616 $this->SetFillColorArray($bgcolor);
20617 $this->SetDrawColorArray($strokecolor);
20618 $this->SettextColorArray($color);
20622 * Returns current graphic variables as array.
20623 * @return array of graphic variables
20625 * @since 4.2.010 (2008-11-14)
20627 protected function getGraphicVars() {
20629 'FontFamily' => $this->FontFamily
,
20630 'FontStyle' => $this->FontStyle
,
20631 'FontSizePt' => $this->FontSizePt
,
20632 'rMargin' => $this->rMargin
,
20633 'lMargin' => $this->lMargin
,
20634 'cell_padding' => $this->cell_padding
,
20635 'cell_margin' => $this->cell_margin
,
20636 'LineWidth' => $this->LineWidth
,
20637 'linestyleWidth' => $this->linestyleWidth
,
20638 'linestyleCap' => $this->linestyleCap
,
20639 'linestyleJoin' => $this->linestyleJoin
,
20640 'linestyleDash' => $this->linestyleDash
,
20641 'textrendermode' => $this->textrendermode
,
20642 'textstrokewidth' => $this->textstrokewidth
,
20643 'DrawColor' => $this->DrawColor
,
20644 'FillColor' => $this->FillColor
,
20645 'TextColor' => $this->TextColor
,
20646 'ColorFlag' => $this->ColorFlag
,
20647 'bgcolor' => $this->bgcolor
,
20648 'fgcolor' => $this->fgcolor
,
20649 'htmlvspace' => $this->htmlvspace
,
20650 'listindent' => $this->listindent
,
20651 'listindentlevel' => $this->listindentlevel
,
20652 'listnum' => $this->listnum
,
20653 'listordered' => $this->listordered
,
20654 'listcount' => $this->listcount
,
20655 'lispacer' => $this->lispacer
,
20656 'cell_height_ratio' => $this->cell_height_ratio
,
20657 'font_stretching' => $this->font_stretching
,
20658 'font_spacing' => $this->font_spacing
,
20659 'alpha' => $this->alpha
,
20661 'lasth' => $this->lasth
,
20662 'tMargin' => $this->tMargin
,
20663 'bMargin' => $this->bMargin
,
20664 'AutoPageBreak' => $this->AutoPageBreak
,
20665 'PageBreakTrigger' => $this->PageBreakTrigger
,
20670 'wPt' => $this->wPt
,
20671 'hPt' => $this->hPt
,
20672 'fwPt' => $this->fwPt
,
20673 'fhPt' => $this->fhPt
,
20674 'page' => $this->page
,
20675 'current_column' => $this->current_column
,
20676 'num_columns' => $this->num_columns
20682 * Set graphic variables.
20683 * @param $gvars (array) array of graphic variablesto restore
20684 * @param $extended (boolean) if true restore extended graphic variables
20686 * @since 4.2.010 (2008-11-14)
20688 protected function setGraphicVars($gvars, $extended=false) {
20689 if ($this->state
!= 2) {
20692 $this->FontFamily
= $gvars['FontFamily'];
20693 $this->FontStyle
= $gvars['FontStyle'];
20694 $this->FontSizePt
= $gvars['FontSizePt'];
20695 $this->rMargin
= $gvars['rMargin'];
20696 $this->lMargin
= $gvars['lMargin'];
20697 $this->cell_padding
= $gvars['cell_padding'];
20698 $this->cell_margin
= $gvars['cell_margin'];
20699 $this->LineWidth
= $gvars['LineWidth'];
20700 $this->linestyleWidth
= $gvars['linestyleWidth'];
20701 $this->linestyleCap
= $gvars['linestyleCap'];
20702 $this->linestyleJoin
= $gvars['linestyleJoin'];
20703 $this->linestyleDash
= $gvars['linestyleDash'];
20704 $this->textrendermode
= $gvars['textrendermode'];
20705 $this->textstrokewidth
= $gvars['textstrokewidth'];
20706 $this->DrawColor
= $gvars['DrawColor'];
20707 $this->FillColor
= $gvars['FillColor'];
20708 $this->TextColor
= $gvars['TextColor'];
20709 $this->ColorFlag
= $gvars['ColorFlag'];
20710 $this->bgcolor
= $gvars['bgcolor'];
20711 $this->fgcolor
= $gvars['fgcolor'];
20712 $this->htmlvspace
= $gvars['htmlvspace'];
20713 $this->listindent
= $gvars['listindent'];
20714 $this->listindentlevel
= $gvars['listindentlevel'];
20715 $this->listnum
= $gvars['listnum'];
20716 $this->listordered
= $gvars['listordered'];
20717 $this->listcount
= $gvars['listcount'];
20718 $this->lispacer
= $gvars['lispacer'];
20719 $this->cell_height_ratio
= $gvars['cell_height_ratio'];
20720 $this->font_stretching
= $gvars['font_stretching'];
20721 $this->font_spacing
= $gvars['font_spacing'];
20722 $this->alpha
= $gvars['alpha'];
20724 // restore extended values
20725 $this->lasth
= $gvars['lasth'];
20726 $this->tMargin
= $gvars['tMargin'];
20727 $this->bMargin
= $gvars['bMargin'];
20728 $this->AutoPageBreak
= $gvars['AutoPageBreak'];
20729 $this->PageBreakTrigger
= $gvars['PageBreakTrigger'];
20730 $this->x
= $gvars['x'];
20731 $this->y
= $gvars['y'];
20732 $this->w
= $gvars['w'];
20733 $this->h
= $gvars['h'];
20734 $this->wPt
= $gvars['wPt'];
20735 $this->hPt
= $gvars['hPt'];
20736 $this->fwPt
= $gvars['fwPt'];
20737 $this->fhPt
= $gvars['fhPt'];
20738 $this->page
= $gvars['page'];
20739 $this->current_column
= $gvars['current_column'];
20740 $this->num_columns
= $gvars['num_columns'];
20742 $this->_out(''.$this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
.' '.$this->FillColor
.'');
20743 if (!TCPDF_STATIC
::empty_string($this->FontFamily
)) {
20744 $this->SetFont($this->FontFamily
, $this->FontStyle
, $this->FontSizePt
);
20749 * Outputs the "save graphics state" operator 'q'
20752 protected function _outSaveGraphicsState() {
20757 * Outputs the "restore graphics state" operator 'Q'
20760 protected function _outRestoreGraphicsState() {
20765 * Writes data to a temporary file on filesystem.
20766 * @param $filename (string) file name
20767 * @param $data (mixed) data to write on file
20768 * @param $append (boolean) if true append data, false replace.
20769 * @param $serialize (boolean) if true serialize data.
20770 * @since 4.5.000 (2008-12-31)
20773 protected function writeDiskCache($filename, $data, $append=false, $serialize=false) {
20779 $f = @fopen
($filename, $fmode);
20781 $this->Error('Unable to write cache file: '.$filename);
20784 $data = $this->file_id
.serialize($data);
20788 // update file length (needed for transactions)
20789 if (!isset($this->cache_file_length
['_'.$filename])) {
20790 $this->cache_file_length
['_'.$filename] = strlen($data);
20792 $this->cache_file_length
['_'.$filename] +
= strlen($data);
20797 * Read data from a temporary file on filesystem.
20798 * @param $filename (string) file name
20799 * @param $unserialize (boolean) if true unserialize data.
20800 * @return mixed retrieved data
20801 * @since 4.5.000 (2008-12-31)
20804 protected function readDiskCache($filename, $unserialize=false) {
20805 $data = file_get_contents($filename);
20806 if ($data === FALSE) {
20807 $this->Error('Unable to read the file: '.$filename);
20809 if ($unserialize) {
20810 if (substr($data, 0, 32) != $this->file_id
) {
20811 $this->Error('Invalid cache file: '.$filename);
20813 $data = unserialize(substr($data, 32));
20819 * Set buffer content (always append data).
20820 * @param $data (string) data
20822 * @since 4.5.000 (2009-01-02)
20824 protected function setBuffer($data) {
20825 $this->bufferlen +
= strlen($data);
20826 if ($this->diskcache
) {
20827 if (!isset($this->buffer
) OR TCPDF_STATIC
::empty_string($this->buffer
)) {
20828 $this->buffer
= TCPDF_STATIC
::getObjFilename('buf');
20830 $this->writeDiskCache($this->buffer
, $data, true, false);
20832 $this->buffer
.= $data;
20837 * Replace the buffer content
20838 * @param $data (string) data
20840 * @since 5.5.000 (2010-06-22)
20842 protected function replaceBuffer($data) {
20843 $this->bufferlen
= strlen($data);
20844 if ($this->diskcache
) {
20845 if (!isset($this->buffer
) OR TCPDF_STATIC
::empty_string($this->buffer
)) {
20846 $this->buffer
= TCPDF_STATIC
::getObjFilename('buf');
20848 $this->writeDiskCache($this->buffer
, $data, false, false);
20850 $this->buffer
= $data;
20855 * Get buffer content.
20856 * @return string buffer content
20858 * @since 4.5.000 (2009-01-02)
20860 protected function getBuffer() {
20861 if ($this->diskcache
) {
20862 return $this->readDiskCache($this->buffer
, false);
20864 return $this->buffer
;
20869 * Set page buffer content.
20870 * @param $page (int) page number
20871 * @param $data (string) page data
20872 * @param $append (boolean) if true append data, false replace.
20874 * @since 4.5.000 (2008-12-31)
20876 protected function setPageBuffer($page, $data, $append=false) {
20877 if ($this->diskcache
) {
20878 if (!isset($this->pages
[$page])) {
20879 $this->pages
[$page] = TCPDF_STATIC
::getObjFilename('page');
20881 $this->writeDiskCache($this->pages
[$page], $data, $append, false);
20884 $this->pages
[$page] .= $data;
20886 $this->pages
[$page] = $data;
20889 if ($append AND isset($this->pagelen
[$page])) {
20890 $this->pagelen
[$page] +
= strlen($data);
20892 $this->pagelen
[$page] = strlen($data);
20897 * Get page buffer content.
20898 * @param $page (int) page number
20899 * @return string page buffer content or false in case of error
20901 * @since 4.5.000 (2008-12-31)
20903 protected function getPageBuffer($page) {
20904 if ($this->diskcache
) {
20905 return $this->readDiskCache($this->pages
[$page], false);
20906 } elseif (isset($this->pages
[$page])) {
20907 return $this->pages
[$page];
20913 * Set image buffer content.
20914 * @param $image (string) image key
20915 * @param $data (array) image data
20916 * @return int image index number
20918 * @since 4.5.000 (2008-12-31)
20920 protected function setImageBuffer($image, $data) {
20921 if (($data['i'] = array_search($image, $this->imagekeys
)) === FALSE) {
20922 $this->imagekeys
[$this->numimages
] = $image;
20923 $data['i'] = $this->numimages
;
20924 ++
$this->numimages
;
20926 if ($this->diskcache
) {
20927 if (!isset($this->images
[$image])) {
20928 $this->images
[$image] = TCPDF_STATIC
::getObjFilename('img');
20930 $this->writeDiskCache($this->images
[$image], $data, false, true);
20932 $this->images
[$image] = $data;
20938 * Set image buffer content for a specified sub-key.
20939 * @param $image (string) image key
20940 * @param $key (string) image sub-key
20941 * @param $data (array) image data
20943 * @since 4.5.000 (2008-12-31)
20945 protected function setImageSubBuffer($image, $key, $data) {
20946 if (!isset($this->images
[$image])) {
20947 $this->setImageBuffer($image, array());
20949 if ($this->diskcache
) {
20950 $tmpimg = $this->getImageBuffer($image);
20951 $tmpimg[$key] = $data;
20952 $this->writeDiskCache($this->images
[$image], $tmpimg, false, true);
20954 $this->images
[$image][$key] = $data;
20959 * Get image buffer content.
20960 * @param $image (string) image key
20961 * @return string image buffer content or false in case of error
20963 * @since 4.5.000 (2008-12-31)
20965 protected function getImageBuffer($image) {
20966 if ($this->diskcache
AND isset($this->images
[$image])) {
20967 return $this->readDiskCache($this->images
[$image], true);
20968 } elseif (isset($this->images
[$image])) {
20969 return $this->images
[$image];
20975 * Set font buffer content.
20976 * @param $font (string) font key
20977 * @param $data (array) font data
20979 * @since 4.5.000 (2009-01-02)
20981 protected function setFontBuffer($font, $data) {
20982 if ($this->diskcache
) {
20983 if (!isset($this->fonts
[$font])) {
20984 $this->fonts
[$font] = TCPDF_STATIC
::getObjFilename('font');
20986 $this->writeDiskCache($this->fonts
[$font], $data, false, true);
20988 $this->fonts
[$font] = $data;
20990 if (!in_array($font, $this->fontkeys
)) {
20991 $this->fontkeys
[] = $font;
20992 // store object ID for current font
20994 $this->font_obj_ids
[$font] = $this->n
;
20995 $this->setFontSubBuffer($font, 'n', $this->n
);
21000 * Set font buffer content.
21001 * @param $font (string) font key
21002 * @param $key (string) font sub-key
21003 * @param $data (array) font data
21005 * @since 4.5.000 (2009-01-02)
21007 protected function setFontSubBuffer($font, $key, $data) {
21008 if (!isset($this->fonts
[$font])) {
21009 $this->setFontBuffer($font, array());
21011 if ($this->diskcache
) {
21012 $tmpfont = $this->getFontBuffer($font);
21013 $tmpfont[$key] = $data;
21014 $this->writeDiskCache($this->fonts
[$font], $tmpfont, false, true);
21016 $this->fonts
[$font][$key] = $data;
21021 * Get font buffer content.
21022 * @param $font (string) font key
21023 * @return string font buffer content or false in case of error
21025 * @since 4.5.000 (2009-01-02)
21027 protected function getFontBuffer($font) {
21028 if ($this->diskcache
AND isset($this->fonts
[$font])) {
21029 return $this->readDiskCache($this->fonts
[$font], true);
21030 } elseif (isset($this->fonts
[$font])) {
21031 return $this->fonts
[$font];
21037 * Move a page to a previous position.
21038 * @param $frompage (int) number of the source page
21039 * @param $topage (int) number of the destination page (must be less than $frompage)
21040 * @return true in case of success, false in case of error.
21042 * @since 4.5.000 (2009-01-02)
21044 public function movePage($frompage, $topage) {
21045 if (($frompage > $this->numpages
) OR ($frompage <= $topage)) {
21048 if ($frompage == $this->page
) {
21049 // close the page before moving it
21052 // move all page-related states
21053 $tmppage = $this->getPageBuffer($frompage);
21054 $tmppagedim = $this->pagedim
[$frompage];
21055 $tmppagelen = $this->pagelen
[$frompage];
21056 $tmpintmrk = $this->intmrk
[$frompage];
21057 $tmpbordermrk = $this->bordermrk
[$frompage];
21058 $tmpcntmrk = $this->cntmrk
[$frompage];
21059 $tmppageobjects = $this->pageobjects
[$frompage];
21060 if (isset($this->footerpos
[$frompage])) {
21061 $tmpfooterpos = $this->footerpos
[$frompage];
21063 if (isset($this->footerlen
[$frompage])) {
21064 $tmpfooterlen = $this->footerlen
[$frompage];
21066 if (isset($this->transfmrk
[$frompage])) {
21067 $tmptransfmrk = $this->transfmrk
[$frompage];
21069 if (isset($this->PageAnnots
[$frompage])) {
21070 $tmpannots = $this->PageAnnots
[$frompage];
21072 if (isset($this->newpagegroup
) AND !empty($this->newpagegroup
)) {
21073 for ($i = $frompage; $i > $topage; --$i) {
21074 if (isset($this->newpagegroup
[$i]) AND (($i +
$this->pagegroups
[$this->newpagegroup
[$i]]) > $frompage)) {
21075 --$this->pagegroups
[$this->newpagegroup
[$i]];
21079 for ($i = $topage; $i > 0; --$i) {
21080 if (isset($this->newpagegroup
[$i]) AND (($i +
$this->pagegroups
[$this->newpagegroup
[$i]]) > $topage)) {
21081 ++
$this->pagegroups
[$this->newpagegroup
[$i]];
21086 for ($i = $frompage; $i > $topage; --$i) {
21088 // shift pages down
21089 $this->setPageBuffer($i, $this->getPageBuffer($j));
21090 $this->pagedim
[$i] = $this->pagedim
[$j];
21091 $this->pagelen
[$i] = $this->pagelen
[$j];
21092 $this->intmrk
[$i] = $this->intmrk
[$j];
21093 $this->bordermrk
[$i] = $this->bordermrk
[$j];
21094 $this->cntmrk
[$i] = $this->cntmrk
[$j];
21095 $this->pageobjects
[$i] = $this->pageobjects
[$j];
21096 if (isset($this->footerpos
[$j])) {
21097 $this->footerpos
[$i] = $this->footerpos
[$j];
21098 } elseif (isset($this->footerpos
[$i])) {
21099 unset($this->footerpos
[$i]);
21101 if (isset($this->footerlen
[$j])) {
21102 $this->footerlen
[$i] = $this->footerlen
[$j];
21103 } elseif (isset($this->footerlen
[$i])) {
21104 unset($this->footerlen
[$i]);
21106 if (isset($this->transfmrk
[$j])) {
21107 $this->transfmrk
[$i] = $this->transfmrk
[$j];
21108 } elseif (isset($this->transfmrk
[$i])) {
21109 unset($this->transfmrk
[$i]);
21111 if (isset($this->PageAnnots
[$j])) {
21112 $this->PageAnnots
[$i] = $this->PageAnnots
[$j];
21113 } elseif (isset($this->PageAnnots
[$i])) {
21114 unset($this->PageAnnots
[$i]);
21116 if (isset($this->newpagegroup
[$j])) {
21117 $this->newpagegroup
[$i] = $this->newpagegroup
[$j];
21118 unset($this->newpagegroup
[$j]);
21120 if ($this->currpagegroup
== $j) {
21121 $this->currpagegroup
= $i;
21124 $this->setPageBuffer($topage, $tmppage);
21125 $this->pagedim
[$topage] = $tmppagedim;
21126 $this->pagelen
[$topage] = $tmppagelen;
21127 $this->intmrk
[$topage] = $tmpintmrk;
21128 $this->bordermrk
[$topage] = $tmpbordermrk;
21129 $this->cntmrk
[$topage] = $tmpcntmrk;
21130 $this->pageobjects
[$topage] = $tmppageobjects;
21131 if (isset($tmpfooterpos)) {
21132 $this->footerpos
[$topage] = $tmpfooterpos;
21133 } elseif (isset($this->footerpos
[$topage])) {
21134 unset($this->footerpos
[$topage]);
21136 if (isset($tmpfooterlen)) {
21137 $this->footerlen
[$topage] = $tmpfooterlen;
21138 } elseif (isset($this->footerlen
[$topage])) {
21139 unset($this->footerlen
[$topage]);
21141 if (isset($tmptransfmrk)) {
21142 $this->transfmrk
[$topage] = $tmptransfmrk;
21143 } elseif (isset($this->transfmrk
[$topage])) {
21144 unset($this->transfmrk
[$topage]);
21146 if (isset($tmpannots)) {
21147 $this->PageAnnots
[$topage] = $tmpannots;
21148 } elseif (isset($this->PageAnnots
[$topage])) {
21149 unset($this->PageAnnots
[$topage]);
21152 $tmpoutlines = $this->outlines
;
21153 foreach ($tmpoutlines as $key => $outline) {
21154 if (!$outline['f']) {
21155 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
21156 $this->outlines
[$key]['p'] = ($outline['p'] +
1);
21157 } elseif ($outline['p'] == $frompage) {
21158 $this->outlines
[$key]['p'] = $topage;
21163 $tmpdests = $this->dests
;
21164 foreach ($tmpdests as $key => $dest) {
21166 if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
21167 $this->dests
[$key]['p'] = ($dest['p'] +
1);
21168 } elseif ($dest['p'] == $frompage) {
21169 $this->dests
[$key]['p'] = $topage;
21174 $tmplinks = $this->links
;
21175 foreach ($tmplinks as $key => $link) {
21177 if (($link['p'] >= $topage) AND ($link['p'] < $frompage)) {
21178 $this->links
[$key]['p'] = ($link['p'] +
1);
21179 } elseif ($link['p'] == $frompage) {
21180 $this->links
[$key]['p'] = $topage;
21184 // adjust javascript
21185 $jfrompage = $frompage;
21186 $jtopage = $topage;
21187 if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript
, $pamatch) > 0) {
21188 foreach($pamatch[0] as $pk => $pmatch) {
21189 $pagenum = intval($pamatch[3][$pk]) +
1;
21190 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
21191 $newpage = ($pagenum +
1);
21192 } elseif ($pagenum == $jfrompage) {
21193 $newpage = $jtopage;
21195 $newpage = $pagenum;
21198 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21199 $this->javascript
= str_replace($pmatch, $newjs, $this->javascript
);
21203 // return to last page
21204 $this->lastPage(true);
21209 * Remove the specified page.
21210 * @param $page (int) page to remove
21211 * @return true in case of success, false in case of error.
21213 * @since 4.6.004 (2009-04-23)
21215 public function deletePage($page) {
21216 if (($page < 1) OR ($page > $this->numpages
)) {
21219 // delete current page
21220 unset($this->pages
[$page]);
21221 unset($this->pagedim
[$page]);
21222 unset($this->pagelen
[$page]);
21223 unset($this->intmrk
[$page]);
21224 unset($this->bordermrk
[$page]);
21225 unset($this->cntmrk
[$page]);
21226 foreach ($this->pageobjects
[$page] as $oid) {
21227 if (isset($this->offsets
[$oid])){
21228 unset($this->offsets
[$oid]);
21231 unset($this->pageobjects
[$page]);
21232 if (isset($this->footerpos
[$page])) {
21233 unset($this->footerpos
[$page]);
21235 if (isset($this->footerlen
[$page])) {
21236 unset($this->footerlen
[$page]);
21238 if (isset($this->transfmrk
[$page])) {
21239 unset($this->transfmrk
[$page]);
21241 if (isset($this->PageAnnots
[$page])) {
21242 unset($this->PageAnnots
[$page]);
21244 if (isset($this->newpagegroup
) AND !empty($this->newpagegroup
)) {
21245 for ($i = $page; $i > 0; --$i) {
21246 if (isset($this->newpagegroup
[$i]) AND (($i +
$this->pagegroups
[$this->newpagegroup
[$i]]) > $page)) {
21247 --$this->pagegroups
[$this->newpagegroup
[$i]];
21252 if (isset($this->pageopen
[$page])) {
21253 unset($this->pageopen
[$page]);
21255 if ($page < $this->numpages
) {
21256 // update remaining pages
21257 for ($i = $page; $i < $this->numpages
; ++
$i) {
21260 $this->setPageBuffer($i, $this->getPageBuffer($j));
21261 $this->pagedim
[$i] = $this->pagedim
[$j];
21262 $this->pagelen
[$i] = $this->pagelen
[$j];
21263 $this->intmrk
[$i] = $this->intmrk
[$j];
21264 $this->bordermrk
[$i] = $this->bordermrk
[$j];
21265 $this->cntmrk
[$i] = $this->cntmrk
[$j];
21266 $this->pageobjects
[$i] = $this->pageobjects
[$j];
21267 if (isset($this->footerpos
[$j])) {
21268 $this->footerpos
[$i] = $this->footerpos
[$j];
21269 } elseif (isset($this->footerpos
[$i])) {
21270 unset($this->footerpos
[$i]);
21272 if (isset($this->footerlen
[$j])) {
21273 $this->footerlen
[$i] = $this->footerlen
[$j];
21274 } elseif (isset($this->footerlen
[$i])) {
21275 unset($this->footerlen
[$i]);
21277 if (isset($this->transfmrk
[$j])) {
21278 $this->transfmrk
[$i] = $this->transfmrk
[$j];
21279 } elseif (isset($this->transfmrk
[$i])) {
21280 unset($this->transfmrk
[$i]);
21282 if (isset($this->PageAnnots
[$j])) {
21283 $this->PageAnnots
[$i] = $this->PageAnnots
[$j];
21284 } elseif (isset($this->PageAnnots
[$i])) {
21285 unset($this->PageAnnots
[$i]);
21287 if (isset($this->newpagegroup
[$j])) {
21288 $this->newpagegroup
[$i] = $this->newpagegroup
[$j];
21289 unset($this->newpagegroup
[$j]);
21291 if ($this->currpagegroup
== $j) {
21292 $this->currpagegroup
= $i;
21294 if (isset($this->pageopen
[$j])) {
21295 $this->pageopen
[$i] = $this->pageopen
[$j];
21296 } elseif (isset($this->pageopen
[$i])) {
21297 unset($this->pageopen
[$i]);
21300 // remove last page
21301 unset($this->pages
[$this->numpages
]);
21302 unset($this->pagedim
[$this->numpages
]);
21303 unset($this->pagelen
[$this->numpages
]);
21304 unset($this->intmrk
[$this->numpages
]);
21305 unset($this->bordermrk
[$this->numpages
]);
21306 unset($this->cntmrk
[$this->numpages
]);
21307 foreach ($this->pageobjects
[$this->numpages
] as $oid) {
21308 if (isset($this->offsets
[$oid])){
21309 unset($this->offsets
[$oid]);
21312 unset($this->pageobjects
[$this->numpages
]);
21313 if (isset($this->footerpos
[$this->numpages
])) {
21314 unset($this->footerpos
[$this->numpages
]);
21316 if (isset($this->footerlen
[$this->numpages
])) {
21317 unset($this->footerlen
[$this->numpages
]);
21319 if (isset($this->transfmrk
[$this->numpages
])) {
21320 unset($this->transfmrk
[$this->numpages
]);
21322 if (isset($this->PageAnnots
[$this->numpages
])) {
21323 unset($this->PageAnnots
[$this->numpages
]);
21325 if (isset($this->newpagegroup
[$this->numpages
])) {
21326 unset($this->newpagegroup
[$this->numpages
]);
21328 if ($this->currpagegroup
== $this->numpages
) {
21329 $this->currpagegroup
= ($this->numpages
- 1);
21331 if (isset($this->pagegroups
[$this->numpages
])) {
21332 unset($this->pagegroups
[$this->numpages
]);
21334 if (isset($this->pageopen
[$this->numpages
])) {
21335 unset($this->pageopen
[$this->numpages
]);
21339 $this->page
= $this->numpages
;
21341 $tmpoutlines = $this->outlines
;
21342 foreach ($tmpoutlines as $key => $outline) {
21343 if (!$outline['f']) {
21344 if ($outline['p'] > $page) {
21345 $this->outlines
[$key]['p'] = $outline['p'] - 1;
21346 } elseif ($outline['p'] == $page) {
21347 unset($this->outlines
[$key]);
21352 $tmpdests = $this->dests
;
21353 foreach ($tmpdests as $key => $dest) {
21355 if ($dest['p'] > $page) {
21356 $this->dests
[$key]['p'] = $dest['p'] - 1;
21357 } elseif ($dest['p'] == $page) {
21358 unset($this->dests
[$key]);
21363 $tmplinks = $this->links
;
21364 foreach ($tmplinks as $key => $link) {
21366 if ($link['p'] > $page) {
21367 $this->links
[$key]['p'] = $link['p'] - 1;
21368 } elseif ($link['p'] == $page) {
21369 unset($this->links
[$key]);
21373 // adjust javascript
21375 if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript
, $pamatch) > 0) {
21376 foreach($pamatch[0] as $pk => $pmatch) {
21377 $pagenum = intval($pamatch[3][$pk]) +
1;
21378 if ($pagenum >= $jpage) {
21379 $newpage = ($pagenum - 1);
21380 } elseif ($pagenum == $jpage) {
21383 $newpage = $pagenum;
21386 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21387 $this->javascript
= str_replace($pmatch, $newjs, $this->javascript
);
21391 // return to last page
21392 if ($this->numpages
> 0) {
21393 $this->lastPage(true);
21399 * Clone the specified page to a new page.
21400 * @param $page (int) number of page to copy (0 = current page)
21401 * @return true in case of success, false in case of error.
21403 * @since 4.9.015 (2010-04-20)
21405 public function copyPage($page=0) {
21408 $page = $this->page
;
21410 if (($page < 1) OR ($page > $this->numpages
)) {
21413 // close the last page
21415 // copy all page-related states
21417 $this->page
= $this->numpages
;
21418 $this->setPageBuffer($this->page
, $this->getPageBuffer($page));
21419 $this->pagedim
[$this->page
] = $this->pagedim
[$page];
21420 $this->pagelen
[$this->page
] = $this->pagelen
[$page];
21421 $this->intmrk
[$this->page
] = $this->intmrk
[$page];
21422 $this->bordermrk
[$this->page
] = $this->bordermrk
[$page];
21423 $this->cntmrk
[$this->page
] = $this->cntmrk
[$page];
21424 $this->pageobjects
[$this->page
] = $this->pageobjects
[$page];
21425 $this->pageopen
[$this->page
] = false;
21426 if (isset($this->footerpos
[$page])) {
21427 $this->footerpos
[$this->page
] = $this->footerpos
[$page];
21429 if (isset($this->footerlen
[$page])) {
21430 $this->footerlen
[$this->page
] = $this->footerlen
[$page];
21432 if (isset($this->transfmrk
[$page])) {
21433 $this->transfmrk
[$this->page
] = $this->transfmrk
[$page];
21435 if (isset($this->PageAnnots
[$page])) {
21436 $this->PageAnnots
[$this->page
] = $this->PageAnnots
[$page];
21438 if (isset($this->newpagegroup
[$page])) {
21439 // start a new group
21440 $this->newpagegroup
[$this->page
] = sizeof($this->newpagegroup
) +
1;
21441 $this->currpagegroup
= $this->newpagegroup
[$this->page
];
21442 $this->pagegroups
[$this->currpagegroup
] = 1;
21443 } elseif (isset($this->currpagegroup
) AND ($this->currpagegroup
> 0)) {
21444 ++
$this->pagegroups
[$this->currpagegroup
];
21447 $tmpoutlines = $this->outlines
;
21448 foreach ($tmpoutlines as $key => $outline) {
21449 if ($outline['p'] == $page) {
21450 $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']);
21454 $tmplinks = $this->links
;
21455 foreach ($tmplinks as $key => $link) {
21456 if ($link['p'] == $page) {
21457 $this->links
[] = array('p' => $this->page
, 'y' => $link['y'], 'f' => $link['f']);
21460 // return to last page
21461 $this->lastPage(true);
21466 * Output a Table of Content Index (TOC).
21467 * This method must be called after all Bookmarks were set.
21468 * Before calling this method you have to open the page using the addTOCPage() method.
21469 * After calling this method you have to call endTOCPage() to close the TOC page.
21470 * You can override this method to achieve different styles.
21471 * @param $page (int) page number where this TOC should be inserted (leave empty for current page).
21472 * @param $numbersfont (string) set the font for page numbers (please use monospaced font for better alignment).
21473 * @param $filler (string) string used to fill the space between text and page number.
21474 * @param $toc_name (string) name to use for TOC bookmark.
21475 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
21476 * @param $color (array) RGB color array for bookmark title (values from 0 to 255).
21478 * @author Nicola Asuni
21479 * @since 4.5.000 (2009-01-02)
21480 * @see addTOCPage(), endTOCPage(), addHTMLTOC()
21482 public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
21483 $fontsize = $this->FontSizePt
;
21484 $fontfamily = $this->FontFamily
;
21485 $fontstyle = $this->FontStyle
;
21486 $w = $this->w
- $this->lMargin
- $this->rMargin
;
21487 $spacer = $this->GetStringWidth(chr(32)) * 4;
21488 $lmargin = $this->lMargin
;
21489 $rmargin = $this->rMargin
;
21490 $x_start = $this->GetX();
21491 $page_first = $this->page
;
21492 $current_page = $this->page
;
21493 $page_fill_start = false;
21494 $page_fill_end = false;
21495 $current_column = $this->current_column
;
21496 if (TCPDF_STATIC
::empty_string($numbersfont)) {
21497 $numbersfont = $this->default_monospaced_font
;
21499 if (TCPDF_STATIC
::empty_string($filler)) {
21502 if (TCPDF_STATIC
::empty_string($page)) {
21510 $this->SetFont($numbersfont, $fontstyle, $fontsize);
21511 $numwidth = $this->GetStringWidth('00000');
21512 $maxpage = 0; //used for pages on attached documents
21513 foreach ($this->outlines
as $key => $outline) {
21514 // check for extra pages (used for attachments)
21515 if (($this->page
> $page_first) AND ($outline['p'] >= $this->numpages
)) {
21516 $outline['p'] +
= ($this->page
- $page_first);
21525 if ($outline['l'] == 0) {
21526 $this->SetFont($fontfamily, $outline['s'].'B', $fontsize);
21528 $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']);
21530 $this->SetTextColorArray($outline['c']);
21531 // check for page break
21532 $this->checkPageBreak(2 * $this->getCellHeight($this->FontSize
));
21533 // set margins and X position
21534 if (($this->page
== $current_page) AND ($this->current_column
== $current_column)) {
21535 $this->lMargin
= $lmargin;
21536 $this->rMargin
= $rmargin;
21538 if ($this->current_column
!= $current_column) {
21540 $x_start = $this->w
- $this->columns
[$this->current_column
]['x'];
21542 $x_start = $this->columns
[$this->current_column
]['x'];
21545 $lmargin = $this->lMargin
;
21546 $rmargin = $this->rMargin
;
21547 $current_page = $this->page
;
21548 $current_column = $this->current_column
;
21550 $this->SetX($x_start);
21551 $indent = ($spacer * $outline['l']);
21553 $this->x
-= $indent;
21554 $this->rMargin
= $this->w
- $this->x
;
21556 $this->x +
= $indent;
21557 $this->lMargin
= $this->x
;
21559 $link = $this->AddLink();
21560 $this->SetLink($link, $outline['y'], $outline['p']);
21563 $txt = ' '.$outline['t'];
21565 $txt = $outline['t'].' ';
21567 $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
21569 $tw = $this->x
- $this->lMargin
;
21571 $tw = $this->w
- $this->rMargin
- $this->x
;
21573 $this->SetFont($numbersfont, $fontstyle, $fontsize);
21574 if (TCPDF_STATIC
::empty_string($page)) {
21575 $pagenum = $outline['p'];
21577 // placemark to be replaced with the correct number
21578 $pagenum = '{#'.($outline['p']).'}';
21579 if ($this->isUnicodeFont()) {
21580 $pagenum = '{'.$pagenum.'}';
21582 $maxpage = max($maxpage, $outline['p']);
21584 $fw = ($tw - $this->GetStringWidth($pagenum.$filler));
21585 $wfiller = $this->GetStringWidth($filler);
21586 if ($wfiller > 0) {
21587 $numfills = floor($fw / $wfiller);
21591 if ($numfills > 0) {
21592 $rowfill = str_repeat($filler, $numfills);
21597 $pagenum = $pagenum.$gap.$rowfill;
21599 $pagenum = $rowfill.$gap.$pagenum;
21601 // write the number
21602 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
21604 $page_last = $this->getPage();
21605 $numpages = ($page_last - $page_first +
1);
21606 // account for booklet mode
21607 if ($this->booklet
) {
21608 // check if a blank page is required before TOC
21609 $page_fill_start = ((($page_first %
2) == 0) XOR (($page %
2) == 0));
21610 $page_fill_end = (!((($numpages %
2) == 0) XOR ($page_fill_start)));
21611 if ($page_fill_start) {
21612 // add a page at the end (to be moved before TOC)
21617 if ($page_fill_end) {
21618 // add a page at the end
21624 $maxpage = max($maxpage, $page_last);
21625 if (!TCPDF_STATIC
::empty_string($page)) {
21626 for ($p = $page_first; $p <= $page_last; ++
$p) {
21628 $temppage = $this->getPageBuffer($p);
21629 for ($n = 1; $n <= $maxpage; ++
$n) {
21630 // update page numbers
21632 // get page number aliases
21633 $pnalias = $this->getInternalPageNumberAliases($a);
21634 // calculate replacement number
21635 if (($n >= $page) AND ($n <= $this->numpages
)) {
21636 $np = $n +
$numpages;
21640 $na = TCPDF_STATIC
::formatTOCPageNumber(($this->starting_page_number +
$np - 1));
21641 $nu = TCPDF_FONTS
::UTF8ToUTF16BE($na, false, $this->isunicode
, $this->CurrentFont
);
21642 // replace aliases with numbers
21643 foreach ($pnalias['u'] as $u) {
21644 $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
21646 $nr = $nu.TCPDF_FONTS
::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode
, $this->CurrentFont
);
21648 $nr = TCPDF_FONTS
::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode
, $this->CurrentFont
).$nu;
21650 $temppage = str_replace($u, $nr, $temppage);
21652 foreach ($pnalias['a'] as $a) {
21653 $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
21655 $nr = $na.' '.$sfill;
21657 $nr = $sfill.' '.$na;
21659 $temppage = str_replace($a, $nr, $temppage);
21663 $this->setPageBuffer($p, $temppage);
21666 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21667 if ($page_fill_start) {
21668 $this->movePage($page_last, $page_first);
21670 for ($i = 0; $i < $numpages; ++
$i) {
21671 $this->movePage($page_last, $page);
21677 * Output a Table Of Content Index (TOC) using HTML templates.
21678 * This method must be called after all Bookmarks were set.
21679 * Before calling this method you have to open the page using the addTOCPage() method.
21680 * After calling this method you have to call endTOCPage() to close the TOC page.
21681 * @param $page (int) page number where this TOC should be inserted (leave empty for current page).
21682 * @param $toc_name (string) name to use for TOC bookmark.
21683 * @param $templates (array) array of html templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number.
21684 * @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)
21685 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
21686 * @param $color (array) RGB color array for title (values from 0 to 255).
21688 * @author Nicola Asuni
21689 * @since 5.0.001 (2010-05-06)
21690 * @see addTOCPage(), endTOCPage(), addTOC()
21692 public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
21694 $prev_htmlLinkColorArray = $this->htmlLinkColorArray
;
21695 $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle
;
21696 // set new style for link
21697 $this->htmlLinkColorArray
= array();
21698 $this->htmlLinkFontStyle
= '';
21699 $page_first = $this->getPage();
21700 $page_fill_start = false;
21701 $page_fill_end = false;
21702 // get the font type used for numbers in each template
21703 $current_font = $this->FontFamily
;
21704 foreach ($templates as $level => $html) {
21705 $dom = $this->getHtmlDomArray($html);
21706 foreach ($dom as $key => $value) {
21707 if ($value['value'] == '#TOC_PAGE_NUMBER#') {
21708 $this->SetFont($dom[($key - 1)]['fontname']);
21709 $templates['F'.$level] = $this->isUnicodeFont();
21713 $this->SetFont($current_font);
21714 $maxpage = 0; //used for pages on attached documents
21715 foreach ($this->outlines
as $key => $outline) {
21716 // get HTML template
21717 $row = $templates[$outline['l']];
21718 if (TCPDF_STATIC
::empty_string($page)) {
21719 $pagenum = $outline['p'];
21721 // placemark to be replaced with the correct number
21722 $pagenum = '{#'.($outline['p']).'}';
21723 if ($templates['F'.$outline['l']]) {
21724 $pagenum = '{'.$pagenum.'}';
21726 $maxpage = max($maxpage, $outline['p']);
21728 // replace templates with current values
21729 $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
21730 $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
21731 // add link to page
21732 $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
21733 // write bookmark entry
21734 $this->writeHTML($row, false, false, true, false, '');
21736 // restore link styles
21737 $this->htmlLinkColorArray
= $prev_htmlLinkColorArray;
21738 $this->htmlLinkFontStyle
= $prev_htmlLinkFontStyle;
21739 // move TOC page and replace numbers
21740 $page_last = $this->getPage();
21741 $numpages = ($page_last - $page_first +
1);
21742 // account for booklet mode
21743 if ($this->booklet
) {
21744 // check if a blank page is required before TOC
21745 $page_fill_start = ((($page_first %
2) == 0) XOR (($page %
2) == 0));
21746 $page_fill_end = (!((($numpages %
2) == 0) XOR ($page_fill_start)));
21747 if ($page_fill_start) {
21748 // add a page at the end (to be moved before TOC)
21753 if ($page_fill_end) {
21754 // add a page at the end
21760 $maxpage = max($maxpage, $page_last);
21761 if (!TCPDF_STATIC
::empty_string($page)) {
21762 for ($p = $page_first; $p <= $page_last; ++
$p) {
21764 $temppage = $this->getPageBuffer($p);
21765 for ($n = 1; $n <= $maxpage; ++
$n) {
21766 // update page numbers
21768 // get page number aliases
21769 $pnalias = $this->getInternalPageNumberAliases($a);
21770 // calculate replacement number
21772 $np = $n +
$numpages;
21776 $na = TCPDF_STATIC
::formatTOCPageNumber(($this->starting_page_number +
$np - 1));
21777 $nu = TCPDF_FONTS
::UTF8ToUTF16BE($na, false, $this->isunicode
, $this->CurrentFont
);
21778 // replace aliases with numbers
21779 foreach ($pnalias['u'] as $u) {
21780 if ($correct_align) {
21781 $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
21783 $nr = $nu.TCPDF_FONTS
::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode
, $this->CurrentFont
);
21785 $nr = TCPDF_FONTS
::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode
, $this->CurrentFont
).$nu;
21790 $temppage = str_replace($u, $nr, $temppage);
21792 foreach ($pnalias['a'] as $a) {
21793 if ($correct_align) {
21794 $sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
21796 $nr = $na.' '.$sfill;
21798 $nr = $sfill.' '.$na;
21803 $temppage = str_replace($a, $nr, $temppage);
21807 $this->setPageBuffer($p, $temppage);
21810 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21811 if ($page_fill_start) {
21812 $this->movePage($page_last, $page_first);
21814 for ($i = 0; $i < $numpages; ++
$i) {
21815 $this->movePage($page_last, $page);
21821 * Stores a copy of the current TCPDF object used for undo operation.
21823 * @since 4.5.029 (2009-03-19)
21825 public function startTransaction() {
21826 if (isset($this->objcopy
)) {
21827 // remove previous copy
21828 $this->commitTransaction();
21830 // record current page number and Y position
21831 $this->start_transaction_page
= $this->page
;
21832 $this->start_transaction_y
= $this->y
;
21833 // clone current object
21834 $this->objcopy
= TCPDF_STATIC
::objclone($this);
21838 * Delete the copy of the current TCPDF object used for undo operation.
21840 * @since 4.5.029 (2009-03-19)
21842 public function commitTransaction() {
21843 if (isset($this->objcopy
)) {
21844 $this->objcopy
->_destroy(true, true);
21845 unset($this->objcopy
);
21850 * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction().
21851 * @param $self (boolean) if true restores current class object to previous state without the need of reassignment via the returned value.
21852 * @return TCPDF object.
21854 * @since 4.5.029 (2009-03-19)
21856 public function rollbackTransaction($self=false) {
21857 if (isset($this->objcopy
)) {
21858 if (isset($this->objcopy
->diskcache
) AND $this->objcopy
->diskcache
) {
21859 // truncate files to previous values
21860 foreach ($this->objcopy
->cache_file_length
as $file => $length) {
21861 $file = substr($file, 1);
21862 $handle = fopen($file, 'r+');
21863 ftruncate($handle, $length);
21866 $this->_destroy(true, true);
21868 $objvars = get_object_vars($this->objcopy
);
21869 foreach ($objvars as $key => $value) {
21870 $this->$key = $value;
21873 return $this->objcopy
;
21878 // --- MULTI COLUMNS METHODS -----------------------
21881 * Set multiple columns of the same size
21882 * @param $numcols (int) number of columns (set to zero to disable columns mode)
21883 * @param $width (int) column width
21884 * @param $y (int) column starting Y position (leave empty for current Y position)
21886 * @since 4.9.001 (2010-03-28)
21888 public function setEqualColumns($numcols=0, $width=0, $y='') {
21889 $this->columns
= array();
21890 if ($numcols < 2) {
21892 $this->columns
= array();
21894 // maximum column width
21895 $maxwidth = ($this->w
- $this->original_lMargin
- $this->original_rMargin
) / $numcols;
21896 if (($width == 0) OR ($width > $maxwidth)) {
21897 $width = $maxwidth;
21899 if (TCPDF_STATIC
::empty_string($y)) {
21902 // space between columns
21903 $space = (($this->w
- $this->original_lMargin
- $this->original_rMargin
- ($numcols * $width)) / ($numcols - 1));
21904 // fill the columns array (with, space, starting Y position)
21905 for ($i = 0; $i < $numcols; ++
$i) {
21906 $this->columns
[$i] = array('w' => $width, 's' => $space, 'y' => $y);
21909 $this->num_columns
= $numcols;
21910 $this->current_column
= 0;
21911 $this->column_start_page
= $this->page
;
21912 $this->selectColumn(0);
21916 * Remove columns and reset page margins.
21918 * @since 5.9.072 (2011-04-26)
21920 public function resetColumns() {
21921 $this->lMargin
= $this->original_lMargin
;
21922 $this->rMargin
= $this->original_rMargin
;
21923 $this->setEqualColumns();
21927 * Set columns array.
21928 * Each column is represented by an array of arrays with the following keys: (w = width, s = space between columns, y = column top position).
21929 * @param $columns (array)
21931 * @since 4.9.001 (2010-03-28)
21933 public function setColumnsArray($columns) {
21934 $this->columns
= $columns;
21935 $this->num_columns
= count($columns);
21936 $this->current_column
= 0;
21937 $this->column_start_page
= $this->page
;
21938 $this->selectColumn(0);
21942 * Set position at a given column
21943 * @param $col (int) column number (from 0 to getNumberOfColumns()-1); empty string = current column.
21945 * @since 4.9.001 (2010-03-28)
21947 public function selectColumn($col='') {
21948 if (is_string($col)) {
21949 $col = $this->current_column
;
21950 } elseif ($col >= $this->num_columns
) {
21953 $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
21954 $enable_thead = false;
21955 if ($this->num_columns
> 1) {
21956 if ($col != $this->current_column
) {
21957 // move Y pointer at the top of the column
21958 if ($this->column_start_page
== $this->page
) {
21959 $this->y
= $this->columns
[$col]['y'];
21961 $this->y
= $this->tMargin
;
21963 // Avoid to write table headers more than once
21964 if (($this->page
> $this->maxselcol
['page']) OR (($this->page
== $this->maxselcol
['page']) AND ($col > $this->maxselcol
['column']))) {
21965 $enable_thead = true;
21966 $this->maxselcol
['page'] = $this->page
;
21967 $this->maxselcol
['column'] = $col;
21970 $xshift = $this->colxshift
;
21971 // set X position of the current column by case
21972 $listindent = ($this->listindentlevel
* $this->listindent
);
21973 // calculate column X position
21975 for ($i = 0; $i < $col; ++
$i) {
21976 $colpos +
= ($this->columns
[$i]['w'] +
$this->columns
[$i]['s']);
21979 $x = $this->w
- $this->original_rMargin
- $colpos;
21980 $this->rMargin
= ($this->w
- $x +
$listindent);
21981 $this->lMargin
= ($x - $this->columns
[$col]['w']);
21982 $this->x
= $x - $listindent;
21984 $x = $this->original_lMargin +
$colpos;
21985 $this->lMargin
= ($x +
$listindent);
21986 $this->rMargin
= ($this->w
- $x - $this->columns
[$col]['w']);
21987 $this->x
= $x +
$listindent;
21989 $this->columns
[$col]['x'] = $x;
21991 $this->current_column
= $col;
21992 // fix for HTML mode
21993 $this->newline
= true;
21994 // print HTML table header (if any)
21995 if ((!TCPDF_STATIC
::empty_string($this->thead
)) AND (!$this->inthead
)) {
21996 if ($enable_thead) {
21997 // print table header
21998 $this->writeHTML($this->thead
, false, false, false, false, '');
21999 $this->y +
= $xshift['s']['V'];
22000 // store end of header position
22001 if (!isset($this->columns
[$col]['th'])) {
22002 $this->columns
[$col]['th'] = array();
22004 $this->columns
[$col]['th']['\''.$this->page
.'\''] = $this->y
;
22006 } elseif (isset($this->columns
[$col]['th']['\''.$this->page
.'\''])) {
22007 $this->y
= $this->columns
[$col]['th']['\''.$this->page
.'\''];
22010 // account for an html table cell over multiple columns
22012 $this->rMargin +
= $xshift['x'];
22013 $this->x
-= ($xshift['x'] +
$xshift['p']['R']);
22015 $this->lMargin +
= $xshift['x'];
22016 $this->x +
= $xshift['x'] +
$xshift['p']['L'];
22021 * Return the current column number
22022 * @return int current column number
22024 * @since 5.5.011 (2010-07-08)
22026 public function getColumn() {
22027 return $this->current_column
;
22031 * Return the current number of columns.
22032 * @return int number of columns
22034 * @since 5.8.018 (2010-08-25)
22036 public function getNumberOfColumns() {
22037 return $this->num_columns
;
22041 * Set Text rendering mode.
22042 * @param $stroke (int) outline size in user units (0 = disable).
22043 * @param $fill (boolean) if true fills the text (default).
22044 * @param $clip (boolean) if true activate clipping mode
22046 * @since 4.9.008 (2009-04-02)
22048 public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
22049 // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
22050 // convert text rendering parameters
22054 if ($fill === true) {
22056 if ($clip === true) {
22057 // Fill, then stroke text and add to path for clipping
22058 $textrendermode = 6;
22060 // Fill, then stroke text
22061 $textrendermode = 2;
22063 $textstrokewidth = $stroke;
22065 if ($clip === true) {
22066 // Fill text and add to path for clipping
22067 $textrendermode = 4;
22070 $textrendermode = 0;
22075 if ($clip === true) {
22076 // Stroke text and add to path for clipping
22077 $textrendermode = 5;
22080 $textrendermode = 1;
22082 $textstrokewidth = $stroke;
22084 if ($clip === true) {
22085 // Add text to path for clipping
22086 $textrendermode = 7;
22088 // Neither fill nor stroke text (invisible)
22089 $textrendermode = 3;
22093 $this->textrendermode
= $textrendermode;
22094 $this->textstrokewidth
= $stroke;
22098 * Set parameters for drop shadow effect for text.
22099 * @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.
22100 * @since 5.9.174 (2012-07-25)
22103 public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) {
22104 if (isset($params['enabled'])) {
22105 $this->txtshadow
['enabled'] = $params['enabled']?
true:false;
22107 $this->txtshadow
['enabled'] = false;
22109 if (isset($params['depth_w'])) {
22110 $this->txtshadow
['depth_w'] = floatval($params['depth_w']);
22112 $this->txtshadow
['depth_w'] = 0;
22114 if (isset($params['depth_h'])) {
22115 $this->txtshadow
['depth_h'] = floatval($params['depth_h']);
22117 $this->txtshadow
['depth_h'] = 0;
22119 if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) {
22120 $this->txtshadow
['color'] = $params['color'];
22122 $this->txtshadow
['color'] = $this->strokecolor
;
22124 if (isset($params['opacity'])) {
22125 $this->txtshadow
['opacity'] = min(1, max(0, floatval($params['opacity'])));
22127 $this->txtshadow
['opacity'] = 1;
22129 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'))) {
22130 $this->txtshadow
['blend_mode'] = $params['blend_mode'];
22132 $this->txtshadow
['blend_mode'] = 'Normal';
22134 if ((($this->txtshadow
['depth_w'] == 0) AND ($this->txtshadow
['depth_h'] == 0)) OR ($this->txtshadow
['opacity'] == 0)) {
22135 $this->txtshadow
['enabled'] = false;
22140 * Return the text shadow parameters array.
22141 * @return Array of parameters.
22142 * @since 5.9.174 (2012-07-25)
22145 public function getTextShadow() {
22146 return $this->txtshadow
;
22150 * Returns an array of chars containing soft hyphens.
22151 * @param $word (array) array of chars
22152 * @param $patterns (array) Array of hypenation patterns.
22153 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm.
22154 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
22155 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
22156 * @param $charmin (int) Minimum word length to apply the hyphenation algoritm.
22157 * @param $charmax (int) Maximum length of broken piece of word.
22158 * @return array text with soft hyphens
22159 * @author Nicola Asuni
22160 * @since 4.9.012 (2010-04-12)
22163 protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22164 $hyphenword = array(); // hyphens positions
22165 $numchars = count($word);
22166 if ($numchars <= $charmin) {
22169 $word_string = TCPDF_FONTS
::UTF8ArrSubString($word, '', '', $this->isunicode
);
22170 // some words will be returned as-is
22171 $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})(\]?)$/';
22172 if (preg_match($pattern, $word_string) > 0) {
22176 $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})(\]?)$/';
22177 if (preg_match($pattern, $word_string) > 0) {
22181 if (isset($dictionary[$word_string])) {
22182 return TCPDF_FONTS
::UTF8StringToArray($dictionary[$word_string], $this->isunicode
, $this->CurrentFont
);
22184 // surround word with '_' characters
22185 $tmpword = array_merge(array(46), $word, array(46));
22186 $tmpnumchars = $numchars +
2;
22187 $maxpos = $tmpnumchars - $charmin;
22188 for ($pos = 0; $pos < $maxpos; ++
$pos) {
22189 $imax = min(($tmpnumchars - $pos), $charmax);
22190 for ($i = $charmin; $i <= $imax; ++
$i) {
22191 $subword = strtolower(TCPDF_FONTS
::UTF8ArrSubString($tmpword, $pos, ($pos +
$i), $this->isunicode
));
22192 if (isset($patterns[$subword])) {
22193 $pattern = TCPDF_FONTS
::UTF8StringToArray($patterns[$subword], $this->isunicode
, $this->CurrentFont
);
22194 $pattern_length = count($pattern);
22196 for ($j = 0; $j < $pattern_length; ++
$j) {
22197 // check if $pattern[$j] is a number = hyphenation level (only numbers from 1 to 5 are valid)
22198 if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
22202 $zero = $pos +
$j - $digits;
22204 // get hyphenation level
22205 $level = ($pattern[$j] - 48);
22206 // if two levels from two different patterns match at the same point, the higher one is selected.
22207 if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] < $level)) {
22208 $hyphenword[$zero] = $level;
22217 $maxpos = $numchars - $rightmin;
22218 for ($i = $leftmin; $i <= $maxpos; ++
$i) {
22219 // only odd levels indicate allowed hyphenation points
22220 if (isset($hyphenword[$i]) AND (($hyphenword[$i] %
2) != 0)) {
22221 // 173 = soft hyphen character
22222 array_splice($word, $i +
$inserted, 0, 173);
22230 * Returns text with soft hyphens.
22231 * @param $text (string) text to process
22232 * @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/
22233 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm.
22234 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
22235 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
22236 * @param $charmin (int) Minimum word length to apply the hyphenation algoritm.
22237 * @param $charmax (int) Maximum length of broken piece of word.
22238 * @return array text with soft hyphens
22239 * @author Nicola Asuni
22240 * @since 4.9.012 (2010-04-12)
22243 public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22244 $text = $this->unhtmlentities($text);
22245 $word = array(); // last word
22246 $txtarr = array(); // text to be returned
22247 $intag = false; // true if we are inside an HTML tag
22248 $skip = false; // true to skip hyphenation
22249 if (!is_array($patterns)) {
22250 $patterns = TCPDF_STATIC
::getHyphenPatternsFromTEX($patterns);
22252 // get array of characters
22253 $unichars = TCPDF_FONTS
::UTF8StringToArray($text, $this->isunicode
, $this->CurrentFont
);
22255 foreach ($unichars as $char) {
22256 if ((!$intag) AND (!$skip) AND TCPDF_FONT_DATA
::$uni_type[$char] == 'L') {
22257 // letter character
22260 // other type of character
22261 if (!TCPDF_STATIC
::empty_string($word)) {
22262 // hypenate the word
22263 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22267 if (chr($char) == '<') {
22268 // we are inside an HTML tag
22270 } elseif ($intag AND (chr($char) == '>')) {
22273 // check for style tag
22274 $expected = array(115, 116, 121, 108, 101); // = 'style'
22275 $current = array_slice($txtarr, -6, 5); // last 5 chars
22276 $compare = array_diff($expected, $current);
22277 if (empty($compare)) {
22278 // check if it is a closing tag
22279 $expected = array(47); // = '/'
22280 $current = array_slice($txtarr, -7, 1);
22281 $compare = array_diff($expected, $current);
22282 if (empty($compare)) {
22283 // closing style tag
22286 // opening style tag
22293 if (!TCPDF_STATIC
::empty_string($word)) {
22294 // hypenate the word
22295 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22297 // convert char array to string and return
22298 return TCPDF_FONTS
::UTF8ArrSubString($txtarr, '', '', $this->isunicode
);
22302 * Enable/disable rasterization of vector images using ImageMagick library.
22303 * @param $mode (boolean) if true enable rasterization, false otherwise.
22305 * @since 5.0.000 (2010-04-27)
22307 public function setRasterizeVectorImages($mode) {
22308 $this->rasterize_vector_images
= $mode;
22312 * Enable or disable default option for font subsetting.
22313 * @param $enable (boolean) if true enable font subsetting by default.
22314 * @author Nicola Asuni
22316 * @since 5.3.002 (2010-06-07)
22318 public function setFontSubsetting($enable=true) {
22319 if ($this->pdfa_mode
) {
22320 $this->font_subsetting
= false;
22322 $this->font_subsetting
= $enable ?
true : false;
22327 * Return the default option for font subsetting.
22328 * @return boolean default font subsetting state.
22329 * @author Nicola Asuni
22331 * @since 5.3.002 (2010-06-07)
22333 public function getFontSubsetting() {
22334 return $this->font_subsetting
;
22338 * Left trim the input string
22339 * @param $str (string) string to trim
22340 * @param $replace (string) string that replace spaces.
22341 * @return left trimmed string
22342 * @author Nicola Asuni
22344 * @since 5.8.000 (2010-08-11)
22346 public function stringLeftTrim($str, $replace='') {
22347 return preg_replace('/^'.$this->re_space
['p'].'+/'.$this->re_space
['m'], $replace, $str);
22351 * Right trim the input string
22352 * @param $str (string) string to trim
22353 * @param $replace (string) string that replace spaces.
22354 * @return right trimmed string
22355 * @author Nicola Asuni
22357 * @since 5.8.000 (2010-08-11)
22359 public function stringRightTrim($str, $replace='') {
22360 return preg_replace('/'.$this->re_space
['p'].'+$/'.$this->re_space
['m'], $replace, $str);
22364 * Trim the input string
22365 * @param $str (string) string to trim
22366 * @param $replace (string) string that replace spaces.
22367 * @return trimmed string
22368 * @author Nicola Asuni
22370 * @since 5.8.000 (2010-08-11)
22372 public function stringTrim($str, $replace='') {
22373 $str = $this->stringLeftTrim($str, $replace);
22374 $str = $this->stringRightTrim($str, $replace);
22379 * Return true if the current font is unicode type.
22380 * @return true for unicode font, false otherwise.
22381 * @author Nicola Asuni
22383 * @since 5.8.002 (2010-08-14)
22385 public function isUnicodeFont() {
22386 return (($this->CurrentFont
['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont
['type'] == 'cidfont0'));
22390 * Return normalized font name
22391 * @param $fontfamily (string) property string containing font family names
22392 * @return string normalized font name
22393 * @author Nicola Asuni
22395 * @since 5.8.004 (2010-08-17)
22397 public function getFontFamilyName($fontfamily) {
22398 // remove spaces and symbols
22399 $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily));
22400 // extract all font names
22401 $fontslist = preg_split('/[,]/', $fontfamily);
22402 // find first valid font name
22403 foreach ($fontslist as $font) {
22404 // replace font variations
22405 $font = preg_replace('/regular$/', '', $font);
22406 $font = preg_replace('/italic$/', 'I', $font);
22407 $font = preg_replace('/oblique$/', 'I', $font);
22408 $font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
22409 // replace common family names and core fonts
22410 $pattern = array();
22411 $replacement = array();
22412 $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
22413 $replacement[] = 'times';
22414 $pattern[] = '/^sansserif/';
22415 $replacement[] = 'helvetica';
22416 $pattern[] = '/^monospace/';
22417 $replacement[] = 'courier';
22418 $font = preg_replace($pattern, $replacement, $font);
22419 if (in_array(strtolower($font), $this->fontlist
) OR in_array($font, $this->fontkeys
)) {
22423 // return current font as default
22424 return $this->CurrentFont
['fontkey'];
22428 * Start a new XObject Template.
22429 * 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).
22430 * 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.
22431 * Note: X,Y coordinates will be reset to 0,0.
22432 * @param $w (int) Template width in user units (empty string or zero = page width less margins).
22433 * @param $h (int) Template height in user units (empty string or zero = page height less margins).
22434 * @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).
22435 * @return int the XObject Template ID in case of success or false in case of error.
22436 * @author Nicola Asuni
22438 * @since 5.8.017 (2010-08-24)
22439 * @see endTemplate(), printTemplate()
22441 public function startTemplate($w=0, $h=0, $group=false) {
22442 if ($this->inxobj
) {
22443 // we are already inside an XObject template
22446 $this->inxobj
= true;
22449 $this->xobjid
= 'XT'.$this->n
;
22451 $this->xobjects
[$this->xobjid
] = array('n' => $this->n
);
22452 // store current graphic state
22453 $this->xobjects
[$this->xobjid
]['gvars'] = $this->getGraphicVars();
22455 $this->xobjects
[$this->xobjid
]['intmrk'] = 0;
22456 $this->xobjects
[$this->xobjid
]['transfmrk'] = array();
22457 $this->xobjects
[$this->xobjid
]['outdata'] = '';
22458 $this->xobjects
[$this->xobjid
]['xobjects'] = array();
22459 $this->xobjects
[$this->xobjid
]['images'] = array();
22460 $this->xobjects
[$this->xobjid
]['fonts'] = array();
22461 $this->xobjects
[$this->xobjid
]['annotations'] = array();
22462 $this->xobjects
[$this->xobjid
]['extgstates'] = array();
22463 $this->xobjects
[$this->xobjid
]['gradients'] = array();
22464 $this->xobjects
[$this->xobjid
]['spot_colors'] = array();
22465 // set new environment
22466 $this->num_columns
= 1;
22467 $this->current_column
= 0;
22468 $this->SetAutoPageBreak(false);
22469 if (($w === '') OR ($w <= 0)) {
22470 $w = $this->w
- $this->lMargin
- $this->rMargin
;
22472 if (($h === '') OR ($h <= 0)) {
22473 $h = $this->h
- $this->tMargin
- $this->bMargin
;
22475 $this->xobjects
[$this->xobjid
]['x'] = 0;
22476 $this->xobjects
[$this->xobjid
]['y'] = 0;
22477 $this->xobjects
[$this->xobjid
]['w'] = $w;
22478 $this->xobjects
[$this->xobjid
]['h'] = $h;
22481 $this->wPt
= $this->w
* $this->k
;
22482 $this->hPt
= $this->h
* $this->k
;
22483 $this->fwPt
= $this->wPt
;
22484 $this->fhPt
= $this->hPt
;
22487 $this->lMargin
= 0;
22488 $this->rMargin
= 0;
22489 $this->tMargin
= 0;
22490 $this->bMargin
= 0;
22492 $this->xobjects
[$this->xobjid
]['group'] = $group;
22493 return $this->xobjid
;
22497 * End the current XObject Template started with startTemplate() and restore the previous graphic state.
22498 * 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).
22499 * 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.
22500 * @return int the XObject Template ID in case of success or false in case of error.
22501 * @author Nicola Asuni
22503 * @since 5.8.017 (2010-08-24)
22504 * @see startTemplate(), printTemplate()
22506 public function endTemplate() {
22507 if (!$this->inxobj
) {
22508 // we are not inside a template
22511 $this->inxobj
= false;
22512 // restore previous graphic state
22513 $this->setGraphicVars($this->xobjects
[$this->xobjid
]['gvars'], true);
22514 return $this->xobjid
;
22518 * Print an XObject Template.
22519 * You can print an XObject Template inside the currently opened Template.
22520 * 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).
22521 * 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.
22522 * @param $id (string) The ID of XObject Template to print.
22523 * @param $x (int) X position in user units (empty string = current x position)
22524 * @param $y (int) Y position in user units (empty string = current y position)
22525 * @param $w (int) Width in user units (zero = remaining page width)
22526 * @param $h (int) Height in user units (zero = remaining page height)
22527 * @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>
22528 * @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>
22529 * @param $fitonpage (boolean) If true the template is resized to not exceed page dimensions.
22530 * @author Nicola Asuni
22532 * @since 5.8.017 (2010-08-24)
22533 * @see startTemplate(), endTemplate()
22535 public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
22536 if ($this->state
!= 2) {
22539 if (!isset($this->xobjects
[$id])) {
22540 $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
22542 if ($this->inxobj
) {
22543 if ($id == $this->xobjid
) {
22544 // close current template
22545 $this->endTemplate();
22547 // use the template as resource for the template currently opened
22548 $this->xobjects
[$this->xobjid
]['xobjects'][$id] = $this->xobjects
[$id];
22551 // set default values
22558 // check page for no-write regions and adapt page margins if necessary
22559 list($x, $y) = $this->checkPageRegions($h, $x, $y);
22560 $ow = $this->xobjects
[$id]['w'];
22564 $oh = $this->xobjects
[$id]['h'];
22568 // calculate template width and height on document
22569 if (($w <= 0) AND ($h <= 0)) {
22572 } elseif ($w <= 0) {
22573 $w = $h * $ow / $oh;
22574 } elseif ($h <= 0) {
22575 $h = $w * $oh / $ow;
22577 // fit the template on available space
22578 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22579 // set page alignment
22583 if ($palign == 'L') {
22584 $xt = $this->lMargin
;
22585 } elseif ($palign == 'C') {
22586 $xt = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
22587 } elseif ($palign == 'R') {
22588 $xt = $this->w
- $this->rMargin
- $w;
22594 if ($palign == 'L') {
22595 $xt = $this->lMargin
;
22596 } elseif ($palign == 'C') {
22597 $xt = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
22598 } elseif ($palign == 'R') {
22599 $xt = $this->w
- $this->rMargin
- $w;
22605 // print XObject Template + Transformation matrix
22606 $this->StartTransform();
22607 // translate and scale
22615 $tm[4] = $xt * $this->k
;
22616 $tm[5] = ($this->h
- $h - $y) * $this->k
;
22617 $this->Transform($tm);
22619 $this->_out('/'.$id.' Do');
22620 $this->StopTransform();
22622 if (!empty($this->xobjects
[$id]['annotations'])) {
22623 foreach ($this->xobjects
[$id]['annotations'] as $annot) {
22624 // transform original coordinates
22625 $coordlt = TCPDF_STATIC
::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k
), (-$annot['y'] * $this->k
)));
22626 $ax = ($coordlt[4] / $this->k
);
22627 $ay = ($this->h
- $h - ($coordlt[5] / $this->k
));
22628 $coordrb = TCPDF_STATIC
::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] +
$annot['w']) * $this->k
), ((-$annot['y'] - $annot['h']) * $this->k
)));
22629 $aw = ($coordrb[4] / $this->k
) - $ax;
22630 $ah = ($this->h
- $h - ($coordrb[5] / $this->k
)) - $ay;
22631 $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
22634 // set pointer to align the next text/objects
22642 $this->y
= $y +
round($h/2);
22652 $this->SetY($rb_y);
22662 * Set the percentage of character stretching.
22663 * @param $perc (int) percentage of stretching (100 = no stretching)
22664 * @author Nicola Asuni
22666 * @since 5.9.000 (2010-09-29)
22668 public function setFontStretching($perc=100) {
22669 $this->font_stretching
= $perc;
22673 * Get the percentage of character stretching.
22674 * @return float stretching value
22675 * @author Nicola Asuni
22677 * @since 5.9.000 (2010-09-29)
22679 public function getFontStretching() {
22680 return $this->font_stretching
;
22684 * Set the amount to increase or decrease the space between characters in a text.
22685 * @param $spacing (float) amount to increase or decrease the space between characters in a text (0 = default spacing)
22686 * @author Nicola Asuni
22688 * @since 5.9.000 (2010-09-29)
22690 public function setFontSpacing($spacing=0) {
22691 $this->font_spacing
= $spacing;
22695 * Get the amount to increase or decrease the space between characters in a text.
22696 * @return int font spacing (tracking) value
22697 * @author Nicola Asuni
22699 * @since 5.9.000 (2010-09-29)
22701 public function getFontSpacing() {
22702 return $this->font_spacing
;
22706 * Return an array of no-write page regions
22707 * @return array of no-write page regions
22708 * @author Nicola Asuni
22710 * @since 5.9.003 (2010-10-13)
22711 * @see setPageRegions(), addPageRegion()
22713 public function getPageRegions() {
22714 return $this->page_regions
;
22718 * Set no-write regions on page.
22719 * 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.
22720 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22721 * You can set multiple regions for the same page.
22722 * @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.
22723 * @author Nicola Asuni
22725 * @since 5.9.003 (2010-10-13)
22726 * @see addPageRegion(), getPageRegions()
22728 public function setPageRegions($regions=array()) {
22729 // empty current regions array
22730 $this->page_regions
= array();
22732 foreach ($regions as $data) {
22733 $this->addPageRegion($data);
22738 * Add a single no-write region on selected page.
22739 * 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.
22740 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22741 * You can set multiple regions for the same page.
22742 * @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).
22743 * @author Nicola Asuni
22745 * @since 5.9.003 (2010-10-13)
22746 * @see setPageRegions(), getPageRegions()
22748 public function addPageRegion($region) {
22749 if (!isset($region['page']) OR empty($region['page'])) {
22750 $region['page'] = $this->page
;
22752 if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
22753 AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
22754 AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
22755 $this->page_regions
[] = $region;
22760 * Remove a single no-write region.
22761 * @param $key (int) region key
22762 * @author Nicola Asuni
22764 * @since 5.9.003 (2010-10-13)
22765 * @see setPageRegions(), getPageRegions()
22767 public function removePageRegion($key) {
22768 if (isset($this->page_regions
[$key])) {
22769 unset($this->page_regions
[$key]);
22774 * Check page for no-write regions and adapt current coordinates and page margins if necessary.
22775 * 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.
22776 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22777 * @param $h (float) height of the text/image/object to print in user units
22778 * @param $x (float) current X coordinate in user units
22779 * @param $y (float) current Y coordinate in user units
22780 * @return array($x, $y)
22781 * @author Nicola Asuni
22783 * @since 5.9.003 (2010-10-13)
22785 protected function checkPageRegions($h, $x, $y) {
22786 // set default values
22793 if (!$this->check_page_regions
OR empty($this->page_regions
)) {
22794 // no page regions defined
22795 return array($x, $y);
22798 $h = $this->getCellHeight($this->FontSize
);
22800 // check for page break
22801 if ($this->checkPageBreak($h, $y)) {
22802 // the content will be printed on a new page
22806 if ($this->num_columns
> 1) {
22808 $this->lMargin
= ($this->columns
[$this->current_column
]['x'] - $this->columns
[$this->current_column
]['w']);
22810 $this->rMargin
= ($this->w
- $this->columns
[$this->current_column
]['x'] - $this->columns
[$this->current_column
]['w']);
22814 $this->lMargin
= max($this->clMargin
, $this->original_lMargin
);
22816 $this->rMargin
= max($this->crMargin
, $this->original_rMargin
);
22819 // adjust coordinates and page margins
22820 foreach ($this->page_regions
as $regid => $regdata) {
22821 if ($regdata['page'] == $this->page
) {
22822 // check region boundaries
22823 if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
22824 // Y is inside the region
22825 $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
22826 $yt = max($y, $regdata['yt']);
22827 $yb = min(($yt +
$h), $regdata['yb']);
22828 $xt = (($yt - $regdata['yt']) * $minv) +
$regdata['xt'];
22829 $xb = (($yb - $regdata['yt']) * $minv) +
$regdata['xt'];
22830 if ($regdata['side'] == 'L') { // left side
22831 $new_margin = max($xt, $xb);
22832 if ($this->lMargin
< $new_margin) {
22834 // adjust left page margin
22835 $this->lMargin
= max(0, $new_margin);
22837 if ($x < $new_margin) {
22838 // adjust x position
22840 if ($new_margin > ($this->w
- $this->rMargin
)) {
22841 // adjust y position
22842 $y = $regdata['yb'] - $h;
22846 } elseif ($regdata['side'] == 'R') { // right side
22847 $new_margin = min($xt, $xb);
22848 if (($this->w
- $this->rMargin
) > $new_margin) {
22850 // adjust right page margin
22851 $this->rMargin
= max(0, ($this->w
- $new_margin));
22853 if ($x > $new_margin) {
22854 // adjust x position
22856 if ($new_margin > $this->lMargin
) {
22857 // adjust y position
22858 $y = $regdata['yb'] - $h;
22866 return array($x, $y);
22869 // --- SVG METHODS ---------------------------------------------------------
22872 * Embedd a Scalable Vector Graphics (SVG) image.
22873 * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
22874 * @param $file (string) Name of the SVG file or a '@' character followed by the SVG data string.
22875 * @param $x (float) Abscissa of the upper-left corner.
22876 * @param $y (float) Ordinate of the upper-left corner.
22877 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
22878 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
22879 * @param $link (mixed) URL or identifier returned by AddLink().
22880 * @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.
22881 * @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>
22882 * @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)))
22883 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
22884 * @author Nicola Asuni
22885 * @since 5.0.000 (2010-05-02)
22888 public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
22889 if ($this->state
!= 2) {
22893 $this->svggradients
= array();
22894 $this->svggradientid
= 0;
22895 $this->svgdefsmode
= false;
22896 $this->svgdefs
= array();
22897 $this->svgclipmode
= false;
22898 $this->svgclippaths
= array();
22899 $this->svgcliptm
= array();
22900 $this->svgclipid
= 0;
22901 $this->svgtext
= '';
22902 $this->svgtextmode
= array();
22903 if ($this->rasterize_vector_images
AND ($w > 0) AND ($h > 0)) {
22904 // convert SVG to raster image using GD or ImageMagick libraries
22905 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22907 if ($file[0] === '@') { // image from string
22908 $this->svgdir
= '';
22909 $svgdata = substr($file, 1);
22910 } else { // SVG file
22911 $this->svgdir
= dirname($file);
22912 $svgdata = TCPDF_STATIC
::fileGetContents($file);
22914 if ($svgdata === FALSE) {
22915 $this->Error('SVG file not found: '.$file);
22923 // check page for no-write regions and adapt page margins if necessary
22924 list($x, $y) = $this->checkPageRegions($h, $x, $y);
22930 $aspect_ratio_align = 'xMidYMid';
22931 $aspect_ratio_ms = 'meet';
22933 // get original image width and height
22934 preg_match('/<svg([^\>]*)>/si', $svgdata, $regs);
22935 if (isset($regs[1]) AND !empty($regs[1])) {
22937 if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22938 $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit
, false);
22941 if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22942 $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit
, false);
22945 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22946 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit
, false);
22949 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22950 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit
, false);
22953 $view_box = array();
22954 if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
22955 if (count($tmp) == 5) {
22957 foreach ($tmp as $key => $val) {
22958 $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit
, false);
22960 $ox = $view_box[0];
22961 $oy = $view_box[1];
22963 // get aspect ratio
22965 if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22966 $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
22967 switch (count($aspect_ratio)) {
22969 $aspect_ratio_align = $aspect_ratio[1];
22970 $aspect_ratio_ms = $aspect_ratio[2];
22974 $aspect_ratio_align = $aspect_ratio[0];
22975 $aspect_ratio_ms = $aspect_ratio[1];
22979 $aspect_ratio_align = $aspect_ratio[0];
22980 $aspect_ratio_ms = 'meet';
22993 // calculate image width and height on document
22994 if (($w <= 0) AND ($h <= 0)) {
22995 // convert image size to document unit
22998 } elseif ($w <= 0) {
22999 $w = $h * $ow / $oh;
23000 } elseif ($h <= 0) {
23001 $h = $w * $oh / $ow;
23003 // fit the image on available space
23004 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
23005 if ($this->rasterize_vector_images
) {
23006 // convert SVG to raster image using GD or ImageMagick libraries
23007 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
23010 $this->img_rb_y
= $y +
$h;
23013 if ($palign == 'L') {
23014 $ximg = $this->lMargin
;
23015 } elseif ($palign == 'C') {
23016 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
23017 } elseif ($palign == 'R') {
23018 $ximg = $this->w
- $this->rMargin
- $w;
23022 $this->img_rb_x
= $ximg;
23024 if ($palign == 'L') {
23025 $ximg = $this->lMargin
;
23026 } elseif ($palign == 'C') {
23027 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
23028 } elseif ($palign == 'R') {
23029 $ximg = $this->w
- $this->rMargin
- $w;
23033 $this->img_rb_x
= $ximg +
$w;
23035 // store current graphic vars
23036 $gvars = $this->getGraphicVars();
23037 // store SVG position and scale factors
23038 $svgoffset_x = ($ximg - $ox) * $this->k
;
23039 $svgoffset_y = -($y - $oy) * $this->k
;
23040 if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
23041 $ow = $view_box[2];
23042 $oh = $view_box[3];
23051 $svgscale_x = $w / $ow;
23052 $svgscale_y = $h / $oh;
23053 // scaling and alignment
23054 if ($aspect_ratio_align != 'none') {
23055 // store current scaling values
23056 $svgscale_old_x = $svgscale_x;
23057 $svgscale_old_y = $svgscale_y;
23058 // force uniform scaling
23059 if ($aspect_ratio_ms == 'slice') {
23060 // the entire viewport is covered by the viewBox
23061 if ($svgscale_x > $svgscale_y) {
23062 $svgscale_y = $svgscale_x;
23063 } elseif ($svgscale_x < $svgscale_y) {
23064 $svgscale_x = $svgscale_y;
23067 // the entire viewBox is visible within the viewport
23068 if ($svgscale_x < $svgscale_y) {
23069 $svgscale_y = $svgscale_x;
23070 } elseif ($svgscale_x > $svgscale_y) {
23071 $svgscale_x = $svgscale_y;
23074 // correct X alignment
23075 switch (substr($aspect_ratio_align, 1, 3)) {
23081 $svgoffset_x +
= (($w * $this->k
) - ($ow * $this->k
* $svgscale_x));
23086 $svgoffset_x +
= ((($w * $this->k
) - ($ow * $this->k
* $svgscale_x)) / 2);
23090 // correct Y alignment
23091 switch (substr($aspect_ratio_align, 5)) {
23097 $svgoffset_y -= (($h * $this->k
) - ($oh * $this->k
* $svgscale_y));
23102 $svgoffset_y -= ((($h * $this->k
) - ($oh * $this->k
* $svgscale_y)) / 2);
23107 // store current page break mode
23108 $page_break_mode = $this->AutoPageBreak
;
23109 $page_break_margin = $this->getBreakMargin();
23110 $cell_padding = $this->cell_padding
;
23111 $this->SetCellPadding(0);
23112 $this->SetAutoPageBreak(false);
23113 // save the current graphic state
23114 $this->_out('q'.$this->epsmarker
);
23115 // set initial clipping mask
23116 $this->Rect($ximg, $y, $w, $h, 'CNZ', array(), array());
23117 // scale and translate
23118 $e = $ox * $this->k
* (1 - $svgscale_x);
23119 $f = ($this->h
- $oy) * $this->k
* (1 - $svgscale_y);
23120 $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e +
$svgoffset_x), ($f +
$svgoffset_y)));
23121 // creates a new XML parser to be used by the other XML functions
23122 $this->parser
= xml_parser_create('UTF-8');
23123 // the following function allows to use parser inside object
23124 xml_set_object($this->parser
, $this);
23125 // disable case-folding for this XML parser
23126 xml_parser_set_option($this->parser
, XML_OPTION_CASE_FOLDING
, 0);
23127 // sets the element handler functions for the XML parser
23128 xml_set_element_handler($this->parser
, 'startSVGElementHandler', 'endSVGElementHandler');
23129 // sets the character data handler function for the XML parser
23130 xml_set_character_data_handler($this->parser
, 'segSVGContentHandler');
23131 // start parsing an XML document
23132 if (!xml_parse($this->parser
, $svgdata)) {
23133 $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
));
23134 $this->Error($error_message);
23136 // free this XML parser
23137 xml_parser_free($this->parser
);
23138 // restore previous graphic state
23139 $this->_out($this->epsmarker
.'Q');
23140 // restore graphic vars
23141 $this->setGraphicVars($gvars);
23142 $this->lasth
= $gvars['lasth'];
23143 if (!empty($border)) {
23151 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
23156 $this->Link($ximg, $y, $w, $h, $link, 0);
23158 // set pointer to align the next text/objects
23162 $this->x
= $this->img_rb_x
;
23166 $this->y
= $y +
round($h/2);
23167 $this->x
= $this->img_rb_x
;
23171 $this->y
= $this->img_rb_y
;
23172 $this->x
= $this->img_rb_x
;
23176 $this->SetY($this->img_rb_y
);
23180 // restore pointer to starting position
23181 $this->x
= $gvars['x'];
23182 $this->y
= $gvars['y'];
23183 $this->page
= $gvars['page'];
23184 $this->current_column
= $gvars['current_column'];
23185 $this->tMargin
= $gvars['tMargin'];
23186 $this->bMargin
= $gvars['bMargin'];
23187 $this->w
= $gvars['w'];
23188 $this->h
= $gvars['h'];
23189 $this->wPt
= $gvars['wPt'];
23190 $this->hPt
= $gvars['hPt'];
23191 $this->fwPt
= $gvars['fwPt'];
23192 $this->fhPt
= $gvars['fhPt'];
23196 $this->endlinex
= $this->img_rb_x
;
23197 // restore page break
23198 $this->SetAutoPageBreak($page_break_mode, $page_break_margin);
23199 $this->cell_padding
= $cell_padding;
23203 * Convert SVG transformation matrix to PDF.
23204 * @param $tm (array) original SVG transformation matrix
23205 * @return array transformation matrix
23207 * @since 5.0.000 (2010-05-02)
23209 protected function convertSVGtMatrix($tm) {
23214 $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit
, false) * $this->k
;
23215 $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit
, false) * $this->k
;
23217 $y = $this->h
* $this->k
;
23218 $e = ($x * (1 - $a)) - ($y * $c) +
$e;
23219 $f = ($y * (1 - $d)) - ($x * $b) +
$f;
23220 return array($a, $b, $c, $d, $e, $f);
23224 * Apply SVG graphic transformation matrix.
23225 * @param $tm (array) original SVG transformation matrix
23227 * @since 5.0.000 (2010-05-02)
23229 protected function SVGTransform($tm) {
23230 $this->Transform($this->convertSVGtMatrix($tm));
23234 * Apply the requested SVG styles (*** TO BE COMPLETED ***)
23235 * @param $svgstyle (array) array of SVG styles to apply
23236 * @param $prevsvgstyle (array) array of previous SVG style
23237 * @param $x (int) X origin of the bounding box
23238 * @param $y (int) Y origin of the bounding box
23239 * @param $w (int) width of the bounding box
23240 * @param $h (int) height of the bounding box
23241 * @param $clip_function (string) clip function
23242 * @param $clip_params (array) array of parameters for clipping function
23243 * @return object style
23244 * @author Nicola Asuni
23245 * @since 5.0.000 (2010-05-02)
23248 protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
23249 if ($this->state
!= 2) {
23253 $minlen = (0.01 / $this->k
); // minimum acceptable length
23254 if (!isset($svgstyle['opacity'])) {
23259 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
23260 $clip_path = $this->svgclippaths
[$regs[1]];
23261 foreach ($clip_path as $cp) {
23262 $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
23266 if ($svgstyle['opacity'] != 1) {
23267 $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false);
23270 $fill_color = TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors
);
23271 $this->SetFillColorArray($fill_color);
23273 $text_color = TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors
);
23274 $this->SetTextColorArray($text_color);
23276 if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
23277 $top = (isset($regs[1])?
$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit
, false):0);
23278 $right = (isset($regs[2])?
$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit
, false):0);
23279 $bottom = (isset($regs[3])?
$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit
, false):0);
23280 $left = (isset($regs[4])?
$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit
, false):0);
23283 $cw = $w - $left - $right;
23284 $ch = $h - $top - $bottom;
23285 if ($svgstyle['clip-rule'] == 'evenodd') {
23286 $clip_rule = 'CNZ';
23288 $clip_rule = 'CEO';
23290 $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
23294 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
23296 $gradient = $this->svggradients
[$regs[1]];
23297 if (isset($gradient['xref'])) {
23298 // reference to another gradient definition
23299 $newgradient = $this->svggradients
[$gradient['xref']];
23300 $newgradient['coords'] = $gradient['coords'];
23301 $newgradient['mode'] = $gradient['mode'];
23302 $newgradient['type'] = $gradient['type'];
23303 $newgradient['gradientUnits'] = $gradient['gradientUnits'];
23304 if (isset($gradient['gradientTransform'])) {
23305 $newgradient['gradientTransform'] = $gradient['gradientTransform'];
23307 $gradient = $newgradient;
23309 //save current Graphic State
23310 $this->_outSaveGraphicsState();
23311 //set clipping area
23312 if (!empty($clip_function) AND method_exists($this, $clip_function)) {
23313 $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
23314 if ((!isset($gradient['type']) OR ($gradient['type'] != 3)) AND is_array($bbox) AND (count($bbox) == 4)) {
23315 list($x, $y, $w, $h) = $bbox;
23318 if ($gradient['mode'] == 'measure') {
23319 if (!isset($gradient['coords'][4])) {
23320 $gradient['coords'][4] = 0.5;
23322 if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
23323 $gtm = $gradient['gradientTransform'];
23324 // apply transformation matrix
23325 $xa = ($gtm[0] * $gradient['coords'][0]) +
($gtm[2] * $gradient['coords'][1]) +
$gtm[4];
23326 $ya = ($gtm[1] * $gradient['coords'][0]) +
($gtm[3] * $gradient['coords'][1]) +
$gtm[5];
23327 $xb = ($gtm[0] * $gradient['coords'][2]) +
($gtm[2] * $gradient['coords'][3]) +
$gtm[4];
23328 $yb = ($gtm[1] * $gradient['coords'][2]) +
($gtm[3] * $gradient['coords'][3]) +
$gtm[5];
23329 $r = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) +
pow(($gtm[1] * $gradient['coords'][4]), 2));
23330 $gradient['coords'][0] = $xa;
23331 $gradient['coords'][1] = $ya;
23332 $gradient['coords'][2] = $xb;
23333 $gradient['coords'][3] = $yb;
23334 $gradient['coords'][4] = $r;
23336 // convert SVG coordinates to user units
23337 $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit
, false);
23338 $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit
, false);
23339 $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit
, false);
23340 $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit
, false);
23341 $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit
, false);
23342 if ($w <= $minlen) {
23345 if ($h <= $minlen) {
23349 if ($gradient['gradientUnits'] == 'objectBoundingBox') {
23350 // convert to SVG coordinate system
23351 $gradient['coords'][0] +
= $x;
23352 $gradient['coords'][1] +
= $y;
23353 $gradient['coords'][2] +
= $x;
23354 $gradient['coords'][3] +
= $y;
23356 // calculate percentages
23357 $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w);
23358 $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h);
23359 $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w);
23360 $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h);
23361 $gradient['coords'][4] /= $w;
23362 } elseif ($gradient['mode'] == 'percentage') {
23363 foreach($gradient['coords'] as $key => $val) {
23364 $gradient['coords'][$key] = (intval($val) / 100);
23366 $gradient['coords'][$key] = 0;
23367 } elseif ($val > 1) {
23368 $gradient['coords'][$key] = 1;
23372 if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
23373 // single color (no shading)
23374 $gradient['coords'][0] = 1;
23375 $gradient['coords'][1] = 0;
23376 $gradient['coords'][2] = 0.999;
23377 $gradient['coords'][3] = 0;
23379 // swap Y coordinates
23380 $tmp = $gradient['coords'][1];
23381 $gradient['coords'][1] = $gradient['coords'][3];
23382 $gradient['coords'][3] = $tmp;
23383 // set transformation map for gradient
23384 $cy = ($this->h
- $y);
23385 if ($gradient['type'] == 3) {
23386 // circular gradient
23387 $cy -= ($gradient['coords'][1] * ($w +
$h));
23391 $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k
), ($h * $this->k
), ($x * $this->k
), ($cy * $this->k
)));
23392 if (count($gradient['stops']) > 1) {
23393 $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
23395 } elseif ($svgstyle['fill'] != 'none') {
23396 $fill_color = TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors
);
23397 if ($svgstyle['fill-opacity'] != 1) {
23398 $this->setAlpha($this->alpha
['CA'], 'Normal', $svgstyle['fill-opacity'], false);
23400 $this->SetFillColorArray($fill_color);
23401 if ($svgstyle['fill-rule'] == 'evenodd') {
23408 if ($svgstyle['stroke'] != 'none') {
23409 if ($svgstyle['stroke-opacity'] != 1) {
23410 $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha
['ca'], false);
23411 } elseif (preg_match('/rgba\(\d+%?,\s*\d+%?,\s*\d+%?,\s*(\d+(?:\.\d+)?)\)/i', $svgstyle['stroke'], $rgba_matches)) {
23412 $this->setAlpha($rgba_matches[1], 'Normal', $this->alpha
['ca'], false);
23414 $stroke_style = array(
23415 'color' => TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors
),
23416 'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit
, false),
23417 'cap' => $svgstyle['stroke-linecap'],
23418 'join' => $svgstyle['stroke-linejoin']
23420 if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
23421 $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
23423 $this->SetLineStyle($stroke_style);
23428 if (!empty($svgstyle['font'])) {
23429 if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
23430 $font_family = $this->getFontFamilyName($regs[1]);
23432 $font_family = $svgstyle['font-family'];
23434 if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23435 $font_size = trim($regs[1]);
23437 $font_size = $svgstyle['font-size'];
23439 if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23440 $font_style = trim($regs[1]);
23442 $font_style = $svgstyle['font-style'];
23444 if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23445 $font_weight = trim($regs[1]);
23447 $font_weight = $svgstyle['font-weight'];
23449 if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23450 $font_stretch = trim($regs[1]);
23452 $font_stretch = $svgstyle['font-stretch'];
23454 if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23455 $font_spacing = trim($regs[1]);
23457 $font_spacing = $svgstyle['letter-spacing'];
23460 $font_family = $this->getFontFamilyName($svgstyle['font-family']);
23461 $font_size = $svgstyle['font-size'];
23462 $font_style = $svgstyle['font-style'];
23463 $font_weight = $svgstyle['font-weight'];
23464 $font_stretch = $svgstyle['font-stretch'];
23465 $font_spacing = $svgstyle['letter-spacing'];
23467 $font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles
[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit
);
23468 $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
23469 $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
23470 switch ($font_style) {
23485 switch ($font_weight) {
23488 $font_style .= 'B';
23492 switch ($svgstyle['text-decoration']) {
23493 case 'underline': {
23494 $font_style .= 'U';
23498 $font_style .= 'O';
23501 case 'line-through': {
23502 $font_style .= 'D';
23510 $this->SetFont($font_family, $font_style, $font_size);
23511 $this->setFontStretching($font_stretch);
23512 $this->setFontSpacing($font_spacing);
23517 * Draws an SVG path
23518 * @param $d (string) attribute d of the path SVG element
23519 * @param $style (string) Style of rendering. Possible values are:
23521 * <li>D or empty string: Draw (default).</li>
23522 * <li>F: Fill.</li>
23523 * <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
23524 * <li>DF or FD: Draw and fill.</li>
23525 * <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
23526 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
23527 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
23529 * @return array of container box measures (x, y, w, h)
23530 * @author Nicola Asuni
23531 * @since 5.0.000 (2010-05-02)
23534 protected function SVGPath($d, $style='') {
23535 if ($this->state
!= 2) {
23538 // set fill/stroke style
23539 $op = TCPDF_STATIC
::getPathPaintOperator($style, '');
23544 $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
23545 preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER
);
23552 $xmin = 2147483647;
23554 $ymin = 2147483647;
23557 $minlen = (0.01 / $this->k
); // minimum acceptable length (3 point)
23558 $firstcmd = true; // used to print first point
23559 // draw curve pieces
23560 foreach ($paths as $key => $val) {
23562 $cmd = trim($val[1]);
23563 if (strtolower($cmd) == $cmd) {
23564 // use relative coordinated instead of absolute
23574 if (isset($val[2])) {
23575 // get curve parameters
23576 $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
23578 foreach ($rawparams as $ck => $cp) {
23579 $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit
, false);
23580 if (abs($params[$ck]) < $minlen) {
23581 // aproximate little values to zero
23586 // store current origin point
23589 switch (strtoupper($cmd)) {
23590 case 'M': { // moveto
23591 foreach ($params as $ck => $cp) {
23592 if (($ck %
2) == 0) {
23593 $x = $cp +
$xoffset;
23595 $y = $cp +
$yoffset;
23596 if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23598 $this->_outPoint($x, $y);
23601 $this->_outLine($x, $y);
23606 $xmin = min($xmin, $x);
23607 $ymin = min($ymin, $y);
23608 $xmax = max($xmax, $x);
23609 $ymax = max($ymax, $y);
23618 case 'L': { // lineto
23619 foreach ($params as $ck => $cp) {
23620 if (($ck %
2) == 0) {
23621 $x = $cp +
$xoffset;
23623 $y = $cp +
$yoffset;
23624 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23625 $this->_outLine($x, $y);
23629 $xmin = min($xmin, $x);
23630 $ymin = min($ymin, $y);
23631 $xmax = max($xmax, $x);
23632 $ymax = max($ymax, $y);
23641 case 'H': { // horizontal lineto
23642 foreach ($params as $ck => $cp) {
23643 $x = $cp +
$xoffset;
23644 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23645 $this->_outLine($x, $y);
23649 $xmin = min($xmin, $x);
23650 $xmax = max($xmax, $x);
23657 case 'V': { // vertical lineto
23658 foreach ($params as $ck => $cp) {
23659 $y = $cp +
$yoffset;
23660 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23661 $this->_outLine($x, $y);
23665 $ymin = min($ymin, $y);
23666 $ymax = max($ymax, $y);
23673 case 'C': { // curveto
23674 foreach ($params as $ck => $cp) {
23675 $params[$ck] = $cp;
23676 if ((($ck +
1) %
6) == 0) {
23677 $x1 = $params[($ck - 5)] +
$xoffset;
23678 $y1 = $params[($ck - 4)] +
$yoffset;
23679 $x2 = $params[($ck - 3)] +
$xoffset;
23680 $y2 = $params[($ck - 2)] +
$yoffset;
23681 $x = $params[($ck - 1)] +
$xoffset;
23682 $y = $params[($ck)] +
$yoffset;
23683 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23684 $xmin = min($xmin, $x, $x1, $x2);
23685 $ymin = min($ymin, $y, $y1, $y2);
23686 $xmax = max($xmax, $x, $x1, $x2);
23687 $ymax = max($ymax, $y, $y1, $y2);
23696 case 'S': { // shorthand/smooth curveto
23697 foreach ($params as $ck => $cp) {
23698 $params[$ck] = $cp;
23699 if ((($ck +
1) %
4) == 0) {
23700 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
23701 $x1 = (2 * $x) - $x2;
23702 $y1 = (2 * $y) - $y2;
23707 $x2 = $params[($ck - 3)] +
$xoffset;
23708 $y2 = $params[($ck - 2)] +
$yoffset;
23709 $x = $params[($ck - 1)] +
$xoffset;
23710 $y = $params[($ck)] +
$yoffset;
23711 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23712 $xmin = min($xmin, $x, $x1, $x2);
23713 $ymin = min($ymin, $y, $y1, $y2);
23714 $xmax = max($xmax, $x, $x1, $x2);
23715 $ymax = max($ymax, $y, $y1, $y2);
23724 case 'Q': { // quadratic Bezier curveto
23725 foreach ($params as $ck => $cp) {
23726 $params[$ck] = $cp;
23727 if ((($ck +
1) %
4) == 0) {
23728 // convert quadratic points to cubic points
23729 $x1 = $params[($ck - 3)] +
$xoffset;
23730 $y1 = $params[($ck - 2)] +
$yoffset;
23731 $xa = ($x +
(2 * $x1)) / 3;
23732 $ya = ($y +
(2 * $y1)) / 3;
23733 $x = $params[($ck - 1)] +
$xoffset;
23734 $y = $params[($ck)] +
$yoffset;
23735 $xb = ($x +
(2 * $x1)) / 3;
23736 $yb = ($y +
(2 * $y1)) / 3;
23737 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23738 $xmin = min($xmin, $x, $xa, $xb);
23739 $ymin = min($ymin, $y, $ya, $yb);
23740 $xmax = max($xmax, $x, $xa, $xb);
23741 $ymax = max($ymax, $y, $ya, $yb);
23750 case 'T': { // shorthand/smooth quadratic Bezier curveto
23751 foreach ($params as $ck => $cp) {
23752 $params[$ck] = $cp;
23753 if (($ck %
2) != 0) {
23754 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
23755 $x1 = (2 * $x) - $x1;
23756 $y1 = (2 * $y) - $y1;
23761 // convert quadratic points to cubic points
23762 $xa = ($x +
(2 * $x1)) / 3;
23763 $ya = ($y +
(2 * $y1)) / 3;
23764 $x = $params[($ck - 1)] +
$xoffset;
23765 $y = $params[($ck)] +
$yoffset;
23766 $xb = ($x +
(2 * $x1)) / 3;
23767 $yb = ($y +
(2 * $y1)) / 3;
23768 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23769 $xmin = min($xmin, $x, $xa, $xb);
23770 $ymin = min($ymin, $y, $ya, $yb);
23771 $xmax = max($xmax, $x, $xa, $xb);
23772 $ymax = max($ymax, $y, $ya, $yb);
23781 case 'A': { // elliptical arc
23782 foreach ($params as $ck => $cp) {
23783 $params[$ck] = $cp;
23784 if ((($ck +
1) %
7) == 0) {
23787 $rx = abs($params[($ck - 6)]);
23788 $ry = abs($params[($ck - 5)]);
23789 $ang = -$rawparams[($ck - 4)];
23790 $angle = deg2rad($ang);
23791 $fa = $rawparams[($ck - 3)]; // large-arc-flag
23792 $fs = $rawparams[($ck - 2)]; // sweep-flag
23793 $x = $params[($ck - 1)] +
$xoffset;
23794 $y = $params[$ck] +
$yoffset;
23795 if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
23796 // endpoints are almost identical
23797 $xmin = min($xmin, $x);
23798 $ymin = min($ymin, $y);
23799 $xmax = max($xmax, $x);
23800 $ymax = max($ymax, $y);
23802 $cos_ang = cos($angle);
23803 $sin_ang = sin($angle);
23804 $a = (($x0 - $x) / 2);
23805 $b = (($y0 - $y) / 2);
23806 $xa = ($a * $cos_ang) - ($b * $sin_ang);
23807 $ya = ($a * $sin_ang) +
($b * $cos_ang);
23812 $delta = ($xa2 / $rx2) +
($ya2 / $ry2);
23814 $rx *= sqrt($delta);
23815 $ry *= sqrt($delta);
23819 $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
23820 if ($numerator < 0) {
23823 $root = sqrt($numerator / (($rx2 * $ya2) +
($ry2 * $xa2)));
23828 $cax = $root * (($rx * $ya) / $ry);
23829 $cay = -$root * (($ry * $xa) / $rx);
23830 // coordinates of ellipse center
23831 $cx = ($cax * $cos_ang) - ($cay * $sin_ang) +
(($x0 +
$x) / 2);
23832 $cy = ($cax * $sin_ang) +
($cay * $cos_ang) +
(($y0 +
$y) / 2);
23834 $angs = TCPDF_STATIC
::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
23835 $dang = TCPDF_STATIC
::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
23836 if (($fs == 0) AND ($dang > 0)) {
23837 $dang -= (2 * M_PI
);
23838 } elseif (($fs == 1) AND ($dang < 0)) {
23839 $dang +
= (2 * M_PI
);
23841 $angf = $angs - $dang;
23842 if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
23848 $angs = round(rad2deg($angs), 6);
23849 $angf = round(rad2deg($angf), 6);
23850 // covent angles to positive values
23851 if (($angs < 0) AND ($angf < 0)) {
23856 if (($key == 0) AND (isset($paths[($key +
1)][1])) AND (trim($paths[($key +
1)][1]) == 'z')) {
23859 list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
23860 $xmin = min($xmin, $x, $axmin);
23861 $ymin = min($ymin, $y, $aymin);
23862 $xmax = max($xmax, $x, $axmax);
23863 $ymax = max($ymax, $y, $aymax);
23883 return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
23887 * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***)
23888 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
23889 * @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.
23890 * @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.
23891 * @param $ctm (array) tranformation matrix for clipping mode (starting transformation matrix).
23892 * @author Nicola Asuni
23893 * @since 5.0.000 (2010-05-02)
23896 protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
23897 // check if we are in clip mode
23898 if ($this->svgclipmode
) {
23899 $this->svgclippaths
[$this->svgclipid
][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm
[$this->svgclipid
]);
23902 if ($this->svgdefsmode
AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
23903 if (isset($attribs['id'])) {
23904 $attribs['child_elements'] = array();
23905 $this->svgdefs
[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23908 if (end($this->svgdefs
) !== FALSE) {
23909 $last_svgdefs_id = key($this->svgdefs
);
23910 if (isset($this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'])) {
23911 $attribs['id'] = 'DF_'.(count($this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements']) +
1);
23912 $this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23919 if ($parser == 'clip-path') {
23920 // set clipping mode
23923 // get styling properties
23924 $prev_svgstyle = $this->svgstyles
[max(0,(count($this->svgstyles
) - 1))]; // previous style
23925 $svgstyle = $this->svgstyles
[0]; // set default style
23926 if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) {
23927 // default fill attribute for clipping
23928 $attribs['fill'] = 'none';
23930 if (isset($attribs['style']) AND !TCPDF_STATIC
::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) {
23931 // fix style for regular expression
23932 $attribs['style'] = ';'.$attribs['style'];
23934 foreach ($prev_svgstyle as $key => $val) {
23935 if (in_array($key, TCPDF_IMAGES
::$svginheritprop)) {
23936 // inherit previous value
23937 $svgstyle[$key] = $val;
23939 if (isset($attribs[$key]) AND !TCPDF_STATIC
::empty_string($attribs[$key])) {
23940 // specific attribute settings
23941 if ($attribs[$key] == 'inherit') {
23942 $svgstyle[$key] = $val;
23944 $svgstyle[$key] = $attribs[$key];
23946 } elseif (isset($attribs['style']) AND !TCPDF_STATIC
::empty_string($attribs['style'])) {
23947 // CSS style syntax
23948 $attrval = array();
23949 if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
23950 if ($attrval[1] == 'inherit') {
23951 $svgstyle[$key] = $val;
23953 $svgstyle[$key] = $attrval[1];
23958 // transformation matrix
23959 if (!empty($ctm)) {
23962 $tm = array(1,0,0,1,0,0);
23964 if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
23965 $tm = TCPDF_STATIC
::getTransformationMatrixProduct($tm, TCPDF_STATIC
::getSVGTransformMatrix($attribs['transform']));
23967 $svgstyle['transfmatrix'] = $tm;
23968 $invisible = false;
23969 if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
23970 // the current graphics element is invisible (nothing is painted)
23976 $this->svgdefsmode
= true;
23984 $this->svgclipmode
= true;
23985 if (!isset($attribs['id'])) {
23986 $attribs['id'] = 'CP_'.(count($this->svgcliptm
) +
1);
23988 $this->svgclipid
= $attribs['id'];
23989 $this->svgclippaths
[$this->svgclipid
] = array();
23990 $this->svgcliptm
[$this->svgclipid
] = $tm;
23994 // start of SVG object
23998 // group together related graphics elements
23999 array_push($this->svgstyles
, $svgstyle);
24000 $this->StartTransform();
24001 $x = (isset($attribs['x'])?
$attribs['x']:0);
24002 $y = (isset($attribs['y'])?
$attribs['y']:0);
24003 $w = 1;//(isset($attribs['width'])?$attribs['width']:1);
24004 $h = 1;//(isset($attribs['height'])?$attribs['height']:1);
24005 $tm = TCPDF_STATIC
::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
24006 $this->SVGTransform($tm);
24007 $this->setSVGStyles($svgstyle, $prev_svgstyle);
24010 case 'linearGradient': {
24011 if ($this->pdfa_mode
) {
24014 if (!isset($attribs['id'])) {
24015 $attribs['id'] = 'GR_'.(count($this->svggradients
) +
1);
24017 $this->svggradientid
= $attribs['id'];
24018 $this->svggradients
[$this->svggradientid
] = array();
24019 $this->svggradients
[$this->svggradientid
]['type'] = 2;
24020 $this->svggradients
[$this->svggradientid
]['stops'] = array();
24021 if (isset($attribs['gradientUnits'])) {
24022 $this->svggradients
[$this->svggradientid
]['gradientUnits'] = $attribs['gradientUnits'];
24024 $this->svggradients
[$this->svggradientid
]['gradientUnits'] = 'objectBoundingBox';
24026 //$attribs['spreadMethod']
24027 if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2'])))
24028 OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%'))
24029 OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%'))
24030 OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%'))
24031 OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) {
24032 $this->svggradients
[$this->svggradientid
]['mode'] = 'percentage';
24034 $this->svggradients
[$this->svggradientid
]['mode'] = 'measure';
24036 $x1 = (isset($attribs['x1'])?
$attribs['x1']:'0');
24037 $y1 = (isset($attribs['y1'])?
$attribs['y1']:'0');
24038 $x2 = (isset($attribs['x2'])?
$attribs['x2']:'100');
24039 $y2 = (isset($attribs['y2'])?
$attribs['y2']:'0');
24040 if (isset($attribs['gradientTransform'])) {
24041 $this->svggradients
[$this->svggradientid
]['gradientTransform'] = TCPDF_STATIC
::getSVGTransformMatrix($attribs['gradientTransform']);
24043 $this->svggradients
[$this->svggradientid
]['coords'] = array($x1, $y1, $x2, $y2);
24044 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24045 // gradient is defined on another place
24046 $this->svggradients
[$this->svggradientid
]['xref'] = substr($attribs['xlink:href'], 1);
24050 case 'radialGradient': {
24051 if ($this->pdfa_mode
) {
24054 if (!isset($attribs['id'])) {
24055 $attribs['id'] = 'GR_'.(count($this->svggradients
) +
1);
24057 $this->svggradientid
= $attribs['id'];
24058 $this->svggradients
[$this->svggradientid
] = array();
24059 $this->svggradients
[$this->svggradientid
]['type'] = 3;
24060 $this->svggradients
[$this->svggradientid
]['stops'] = array();
24061 if (isset($attribs['gradientUnits'])) {
24062 $this->svggradients
[$this->svggradientid
]['gradientUnits'] = $attribs['gradientUnits'];
24064 $this->svggradients
[$this->svggradientid
]['gradientUnits'] = 'objectBoundingBox';
24066 //$attribs['spreadMethod']
24067 if (((!isset($attribs['cx'])) AND (!isset($attribs['cy'])))
24068 OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%'))
24069 OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')))) {
24070 $this->svggradients
[$this->svggradientid
]['mode'] = 'percentage';
24071 } elseif (isset($attribs['r']) AND is_numeric($attribs['r']) AND ($attribs['r']) <= 1) {
24072 $this->svggradients
[$this->svggradientid
]['mode'] = 'ratio';
24074 $this->svggradients
[$this->svggradientid
]['mode'] = 'measure';
24076 $cx = (isset($attribs['cx']) ?
$attribs['cx'] : 0.5);
24077 $cy = (isset($attribs['cy']) ?
$attribs['cy'] : 0.5);
24078 $fx = (isset($attribs['fx']) ?
$attribs['fx'] : $cx);
24079 $fy = (isset($attribs['fy']) ?
$attribs['fy'] : $cy);
24080 $r = (isset($attribs['r']) ?
$attribs['r'] : 0.5);
24081 if (isset($attribs['gradientTransform'])) {
24082 $this->svggradients
[$this->svggradientid
]['gradientTransform'] = TCPDF_STATIC
::getSVGTransformMatrix($attribs['gradientTransform']);
24084 $this->svggradients
[$this->svggradientid
]['coords'] = array($cx, $cy, $fx, $fy, $r);
24085 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24086 // gradient is defined on another place
24087 $this->svggradients
[$this->svggradientid
]['xref'] = substr($attribs['xlink:href'], 1);
24093 if (substr($attribs['offset'], -1) == '%') {
24094 $offset = floatval(substr($attribs['offset'], -1)) / 100;
24096 $offset = floatval($attribs['offset']);
24101 $stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors
):'black';
24102 $opacity = isset($svgstyle['stop-opacity'])?
$svgstyle['stop-opacity']:1;
24103 $this->svggradients
[$this->svggradientid
]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
24111 if (isset($attribs['d'])) {
24112 $d = trim($attribs['d']);
24114 $x = (isset($attribs['x'])?
$attribs['x']:0);
24115 $y = (isset($attribs['y'])?
$attribs['y']:0);
24116 $w = (isset($attribs['width'])?
$attribs['width']:1);
24117 $h = (isset($attribs['height'])?
$attribs['height']:1);
24118 $tm = TCPDF_STATIC
::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
24120 $this->SVGTransform($tm);
24121 $this->SVGPath($d, 'CNZ');
24123 $this->StartTransform();
24124 $this->SVGTransform($tm);
24125 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ'));
24126 if (!empty($obstyle)) {
24127 $this->SVGPath($d, $obstyle);
24129 $this->StopTransform();
24140 $x = (isset($attribs['x'])?
$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false):0);
24141 $y = (isset($attribs['y'])?
$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false):0);
24142 $w = (isset($attribs['width'])?
$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit
, false):0);
24143 $h = (isset($attribs['height'])?
$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit
, false):0);
24144 $rx = (isset($attribs['rx'])?
$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit
, false):0);
24145 $ry = (isset($attribs['ry'])?
$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit
, false):$rx);
24147 $this->SVGTransform($tm);
24148 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
24150 $this->StartTransform();
24151 $this->SVGTransform($tm);
24152 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
24153 if (!empty($obstyle)) {
24154 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
24156 $this->StopTransform();
24164 $r = (isset($attribs['r']) ?
$this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit
, false) : 0);
24165 $cx = (isset($attribs['cx']) ?
$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit
, false) : (isset($attribs['x']) ?
$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false) : 0));
24166 $cy = (isset($attribs['cy']) ?
$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit
, false) : (isset($attribs['y']) ?
$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false) : 0));
24172 $this->SVGTransform($tm);
24173 $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
24175 $this->StartTransform();
24176 $this->SVGTransform($tm);
24177 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
24178 if (!empty($obstyle)) {
24179 $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
24181 $this->StopTransform();
24189 $rx = (isset($attribs['rx']) ?
$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit
, false) : 0);
24190 $ry = (isset($attribs['ry']) ?
$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit
, false) : 0);
24191 $cx = (isset($attribs['cx']) ?
$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit
, false) : (isset($attribs['x']) ?
$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false) : 0));
24192 $cy = (isset($attribs['cy']) ?
$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit
, false) : (isset($attribs['y']) ?
$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false) : 0));
24198 $this->SVGTransform($tm);
24199 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
24201 $this->StartTransform();
24202 $this->SVGTransform($tm);
24203 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
24204 if (!empty($obstyle)) {
24205 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
24207 $this->StopTransform();
24215 $x1 = (isset($attribs['x1'])?
$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit
, false):0);
24216 $y1 = (isset($attribs['y1'])?
$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit
, false):0);
24217 $x2 = (isset($attribs['x2'])?
$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit
, false):0);
24218 $y2 = (isset($attribs['y2'])?
$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit
, false):0);
24221 $w = abs($x2 - $x1);
24222 $h = abs($y2 - $y1);
24224 $this->StartTransform();
24225 $this->SVGTransform($tm);
24226 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
24227 $this->Line($x1, $y1, $x2, $y2);
24228 $this->StopTransform();
24237 $points = (isset($attribs['points'])?
$attribs['points']:'0 0');
24238 $points = trim($points);
24239 // note that point may use a complex syntax not covered here
24240 $points = preg_split('/[\,\s]+/si', $points);
24241 if (count($points) < 4) {
24245 $xmin = 2147483647;
24247 $ymin = 2147483647;
24249 foreach ($points as $key => $val) {
24250 $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit
, false);
24251 if (($key %
2) == 0) {
24253 $xmin = min($xmin, $p[$key]);
24254 $xmax = max($xmax, $p[$key]);
24257 $ymin = min($ymin, $p[$key]);
24258 $ymax = max($ymax, $p[$key]);
24263 $w = ($xmax - $xmin);
24264 $h = ($ymax - $ymin);
24265 if ($name == 'polyline') {
24266 $this->StartTransform();
24267 $this->SVGTransform($tm);
24268 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
24269 if (!empty($obstyle)) {
24270 $this->PolyLine($p, $obstyle, array(), array());
24272 $this->StopTransform();
24273 } else { // polygon
24275 $this->SVGTransform($tm);
24276 $this->Polygon($p, 'CNZ', array(), array(), true);
24278 $this->StartTransform();
24279 $this->SVGTransform($tm);
24280 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
24281 if (!empty($obstyle)) {
24282 $this->Polygon($p, $obstyle, array(), array(), true);
24284 $this->StopTransform();
24294 if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
24297 $x = (isset($attribs['x'])?
$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false):0);
24298 $y = (isset($attribs['y'])?
$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false):0);
24299 $w = (isset($attribs['width'])?
$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit
, false):0);
24300 $h = (isset($attribs['height'])?
$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit
, false):0);
24301 $img = $attribs['xlink:href'];
24303 $this->StartTransform();
24304 $this->SVGTransform($tm);
24305 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
24306 if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
24307 // embedded image encoded as base64
24308 $img = '@'.base64_decode(substr($img, strlen($m[0])));
24311 if (!TCPDF_STATIC
::empty_string($this->svgdir
) AND (($img[0] == '.') OR (basename($img) == $img))) {
24312 // replace relative path with full server path
24313 $img = $this->svgdir
.'/'.$img;
24315 if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
24316 $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
24317 if (($findroot === false) OR ($findroot > 1)) {
24318 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
24319 $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
24321 $img = $_SERVER['DOCUMENT_ROOT'].$img;
24325 $img = urldecode($img);
24326 $testscrtype = @parse_url
($img);
24327 if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
24328 // convert URL to server path
24329 $img = str_replace(K_PATH_URL
, K_PATH_MAIN
, $img);
24333 $imgtype = TCPDF_IMAGES
::getImageFileType($img);
24334 if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
24335 $this->ImageEps($img, $x, $y, $w, $h);
24336 } elseif ($imgtype == 'svg') {
24337 $this->ImageSVG($img, $x, $y, $w, $h);
24339 $this->Image($img, $x, $y, $w, $h);
24341 $this->StopTransform();
24348 // only basic support - advanced features must be implemented
24349 $this->svgtextmode
['invisible'] = $invisible;
24353 array_push($this->svgstyles
, $svgstyle);
24354 if (isset($attribs['x'])) {
24355 $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false);
24356 } elseif ($name == 'tspan') {
24361 if (isset($attribs['dx'])) {
24362 $x +
= $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit
, false);
24364 if (isset($attribs['y'])) {
24365 $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false);
24366 } elseif ($name == 'tspan') {
24371 if (isset($attribs['dy'])) {
24372 $y +
= $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit
, false);
24374 $svgstyle['text-color'] = $svgstyle['fill'];
24375 $this->svgtext
= '';
24376 if (isset($svgstyle['text-anchor'])) {
24377 $this->svgtextmode
['text-anchor'] = $svgstyle['text-anchor'];
24379 $this->svgtextmode
['text-anchor'] = 'start';
24381 if (isset($svgstyle['direction'])) {
24382 if ($svgstyle['direction'] == 'rtl') {
24383 $this->svgtextmode
['rtl'] = true;
24385 $this->svgtextmode
['rtl'] = false;
24388 $this->svgtextmode
['rtl'] = false;
24390 if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
24391 $this->svgtextmode
['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit
, false);
24393 $this->svgtextmode
['stroke'] = false;
24395 $this->StartTransform();
24396 $this->SVGTransform($tm);
24397 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
24404 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24405 $svgdefid = substr($attribs['xlink:href'], 1);
24406 if (isset($this->svgdefs
[$svgdefid])) {
24407 $use = $this->svgdefs
[$svgdefid];
24408 if (isset($attribs['xlink:href'])) {
24409 unset($attribs['xlink:href']);
24411 if (isset($attribs['id'])) {
24412 unset($attribs['id']);
24414 if (isset($use['attribs']['x']) AND isset($attribs['x'])) {
24415 $attribs['x'] +
= $use['attribs']['x'];
24417 if (isset($use['attribs']['y']) AND isset($attribs['y'])) {
24418 $attribs['y'] +
= $use['attribs']['y'];
24420 if (empty($attribs['style'])) {
24421 $attribs['style'] = '';
24423 if (!empty($use['attribs']['style'])) {
24425 $attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']);
24427 $attribs = array_merge($use['attribs'], $attribs);
24428 $this->startSVGElementHandler($parser, $use['name'], $attribs);
24438 // process child elements
24439 if (!empty($attribs['child_elements'])) {
24440 $child_elements = $attribs['child_elements'];
24441 unset($attribs['child_elements']);
24442 foreach($child_elements as $child_element) {
24443 if (empty($child_element['attribs']['closing_tag'])) {
24444 $this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']);
24446 if (isset($child_element['attribs']['content'])) {
24447 $this->svgtext
= $child_element['attribs']['content'];
24449 $this->endSVGElementHandler('child-tag', $child_element['name']);
24456 * Sets the closing SVG element handler function for the XML parser.
24457 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
24458 * @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.
24459 * @author Nicola Asuni
24460 * @since 5.0.000 (2010-05-02)
24463 protected function endSVGElementHandler($parser, $name) {
24464 if ($this->svgdefsmode
AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {;
24465 if (end($this->svgdefs
) !== FALSE) {
24466 $last_svgdefs_id = key($this->svgdefs
);
24467 if (isset($this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'])) {
24468 foreach($this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) {
24469 if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) {
24470 $this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext
));
24474 if ($this->svgdefs
[$last_svgdefs_id]['name'] == $name) {
24475 $this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext
));
24484 $this->svgdefsmode
= false;
24489 $this->svgclipmode
= false;
24493 // ungroup: remove last style from array
24494 array_pop($this->svgstyles
);
24495 $this->StopTransform();
24500 if ($this->svgtextmode
['invisible']) {
24501 // This implementation must be fixed to following the rule:
24502 // 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.
24506 $text = $this->svgtext
;
24507 //$text = $this->stringTrim($text);
24508 $textlen = $this->GetStringWidth($text);
24509 if ($this->svgtextmode
['text-anchor'] != 'start') {
24510 // check if string is RTL text
24511 if ($this->svgtextmode
['text-anchor'] == 'end') {
24512 if ($this->svgtextmode
['rtl']) {
24513 $this->x +
= $textlen;
24515 $this->x
-= $textlen;
24517 } elseif ($this->svgtextmode
['text-anchor'] == 'middle') {
24518 if ($this->svgtextmode
['rtl']) {
24519 $this->x +
= ($textlen / 2);
24521 $this->x
-= ($textlen / 2);
24525 $textrendermode = $this->textrendermode
;
24526 $textstrokewidth = $this->textstrokewidth
;
24527 $this->setTextRenderingMode($this->svgtextmode
['stroke'], true, false);
24528 if ($name == 'text') {
24529 // store current coordinates
24533 $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
24534 if ($name == 'text') {
24535 // restore coordinates
24539 // restore previous rendering mode
24540 $this->textrendermode
= $textrendermode;
24541 $this->textstrokewidth
= $textstrokewidth;
24542 $this->svgtext
= '';
24543 $this->StopTransform();
24544 if (!$this->svgdefsmode
) {
24545 array_pop($this->svgstyles
);
24556 * Sets the character data handler function for the XML parser.
24557 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
24558 * @param $data (string) The second parameter, data, contains the character data as a string.
24559 * @author Nicola Asuni
24560 * @since 5.0.000 (2010-05-02)
24563 protected function segSVGContentHandler($parser, $data) {
24564 $this->svgtext
.= $data;
24567 // --- END SVG METHODS -----------------------------------------------------
24569 } // END OF TCPDF CLASS
24571 //============================================================+
24573 //============================================================+