2 //============================================================+
3 // File name : tcpdf.php
6 // Last Update : 2014-05-06
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 * Regular expression used to find blank characters (required for word-wrapping).
1294 * @since 4.6.006 (2009-04-28)
1296 protected $re_spaces = '/[^\S\xa0]/';
1299 * Array of $re_spaces parts.
1301 * @since 5.5.011 (2010-07-09)
1303 protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
1306 * Digital signature object ID.
1308 * @since 4.6.022 (2009-06-23)
1310 protected $sig_obj_id = 0;
1313 * ID of page objects.
1315 * @since 4.7.000 (2009-08-29)
1317 protected $page_obj_id = array();
1320 * List of form annotations IDs.
1322 * @since 4.8.000 (2009-09-07)
1324 protected $form_obj_id = array();
1327 * 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.
1329 * @since 4.8.000 (2009-09-07)
1331 protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1334 * Javascript objects array.
1336 * @since 4.8.000 (2009-09-07)
1338 protected $js_objects = array();
1341 * Current form action (used during XHTML rendering).
1343 * @since 4.8.000 (2009-09-07)
1345 protected $form_action = '';
1348 * Current form encryption type (used during XHTML rendering).
1350 * @since 4.8.000 (2009-09-07)
1352 protected $form_enctype = 'application/x-www-form-urlencoded';
1355 * Current method to submit forms.
1357 * @since 4.8.000 (2009-09-07)
1359 protected $form_mode = 'post';
1362 * List of fonts used on form fields (fontname => fontkey).
1364 * @since 4.8.001 (2009-09-09)
1366 protected $annotation_fonts = array();
1369 * List of radio buttons parent objects.
1371 * @since 4.8.001 (2009-09-09)
1373 protected $radiobutton_groups = array();
1376 * List of radio group objects IDs.
1378 * @since 4.8.001 (2009-09-09)
1380 protected $radio_groups = array();
1383 * Text indentation value (used for text-indent CSS attribute).
1385 * @since 4.8.006 (2009-09-23)
1387 protected $textindent = 0;
1390 * Store page number when startTransaction() is called.
1392 * @since 4.8.006 (2009-09-23)
1394 protected $start_transaction_page = 0;
1397 * Store Y position when startTransaction() is called.
1399 * @since 4.9.001 (2010-03-28)
1401 protected $start_transaction_y = 0;
1404 * True when we are printing the thead section on a new page.
1406 * @since 4.8.027 (2010-01-25)
1408 protected $inthead = false;
1411 * Array of column measures (width, space, starting Y position).
1413 * @since 4.9.001 (2010-03-28)
1415 protected $columns = array();
1420 * @since 4.9.001 (2010-03-28)
1422 protected $num_columns = 1;
1425 * Current column number.
1427 * @since 4.9.001 (2010-03-28)
1429 protected $current_column = 0;
1432 * Starting page for columns.
1434 * @since 4.9.001 (2010-03-28)
1436 protected $column_start_page = 0;
1439 * Maximum page and column selected.
1441 * @since 5.8.000 (2010-08-11)
1443 protected $maxselcol = array('page' => 0, 'column' => 0);
1446 * Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding.
1448 * @since 5.8.000 (2010-08-11)
1450 protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
1453 * 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.
1455 * @since 4.9.008 (2010-04-03)
1457 protected $textrendermode = 0;
1460 * Text stroke width in doc units.
1462 * @since 4.9.008 (2010-04-03)
1464 protected $textstrokewidth = 0;
1467 * Current stroke color.
1469 * @since 4.9.008 (2010-04-03)
1471 protected $strokecolor;
1474 * Default unit of measure for document.
1476 * @since 5.0.000 (2010-04-22)
1478 protected $pdfunit = 'mm';
1481 * Boolean flag true when we are on TOC (Table Of Content) page.
1484 protected $tocpage = false;
1487 * Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library.
1489 * @since 5.0.000 (2010-04-26)
1491 protected $rasterize_vector_images = false;
1494 * Boolean flag: if true enables font subsetting by default.
1496 * @since 5.3.002 (2010-06-07)
1498 protected $font_subsetting = true;
1501 * Array of default graphic settings.
1503 * @since 5.5.008 (2010-07-02)
1505 protected $default_graphic_vars = array();
1508 * Array of XObjects.
1510 * @since 5.8.014 (2010-08-23)
1512 protected $xobjects = array();
1515 * Boolean value true when we are inside an XObject.
1517 * @since 5.8.017 (2010-08-24)
1519 protected $inxobj = false;
1522 * Current XObject ID.
1524 * @since 5.8.017 (2010-08-24)
1526 protected $xobjid = '';
1529 * Percentage of character stretching.
1531 * @since 5.9.000 (2010-09-29)
1533 protected $font_stretching = 100;
1536 * Increases or decreases the space between characters in a text by the specified amount (tracking).
1538 * @since 5.9.000 (2010-09-29)
1540 protected $font_spacing = 0;
1543 * Array of no-write regions.
1544 * ('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)
1546 * @since 5.9.003 (2010-10-14)
1548 protected $page_regions = array();
1551 * Boolean value true when page region check is active.
1554 protected $check_page_regions = true;
1557 * Array of PDF layers data.
1559 * @since 5.9.102 (2011-07-13)
1561 protected $pdflayers = array();
1564 * A dictionary of names and corresponding destinations (Dests key on document Catalog).
1566 * @since 5.9.097 (2011-06-23)
1568 protected $dests = array();
1571 * Object ID for Named Destinations
1573 * @since 5.9.097 (2011-06-23)
1578 * Embedded Files Names
1580 * @since 5.9.204 (2013-01-23)
1582 protected $efnames = array();
1585 * Directory used for the last SVG image.
1587 * @since 5.0.000 (2010-05-05)
1589 protected $svgdir = '';
1592 * Deafult unit of measure for SVG.
1594 * @since 5.0.000 (2010-05-02)
1596 protected $svgunit = 'px';
1599 * Array of SVG gradients.
1601 * @since 5.0.000 (2010-05-02)
1603 protected $svggradients = array();
1606 * ID of last SVG gradient.
1608 * @since 5.0.000 (2010-05-02)
1610 protected $svggradientid = 0;
1613 * Boolean value true when in SVG defs group.
1615 * @since 5.0.000 (2010-05-02)
1617 protected $svgdefsmode = false;
1620 * Array of SVG defs.
1622 * @since 5.0.000 (2010-05-02)
1624 protected $svgdefs = array();
1627 * Boolean value true when in SVG clipPath tag.
1629 * @since 5.0.000 (2010-04-26)
1631 protected $svgclipmode = false;
1634 * Array of SVG clipPath commands.
1636 * @since 5.0.000 (2010-05-02)
1638 protected $svgclippaths = array();
1641 * Array of SVG clipPath tranformation matrix.
1643 * @since 5.8.022 (2010-08-31)
1645 protected $svgcliptm = array();
1648 * ID of last SVG clipPath.
1650 * @since 5.0.000 (2010-05-02)
1652 protected $svgclipid = 0;
1657 * @since 5.0.000 (2010-05-02)
1659 protected $svgtext = '';
1662 * SVG text properties.
1664 * @since 5.8.013 (2010-08-23)
1666 protected $svgtextmode = array();
1669 * Array of SVG properties.
1671 * @since 5.0.000 (2010-05-02)
1673 protected $svgstyles = array(array(
1674 'alignment-baseline' => 'auto',
1675 'baseline-shift' => 'baseline',
1677 'clip-path' => 'none',
1678 'clip-rule' => 'nonzero',
1680 'color-interpolation' => 'sRGB',
1681 'color-interpolation-filters' => 'linearRGB',
1682 'color-profile' => 'auto',
1683 'color-rendering' => 'auto',
1685 'direction' => 'ltr',
1686 'display' => 'inline',
1687 'dominant-baseline' => 'auto',
1688 'enable-background' => 'accumulate',
1690 'fill-opacity' => 1,
1691 'fill-rule' => 'nonzero',
1693 'flood-color' => 'black',
1694 'flood-opacity' => 1,
1696 'font-family' => 'helvetica',
1697 'font-size' => 'medium',
1698 'font-size-adjust' => 'none',
1699 'font-stretch' => 'normal',
1700 'font-style' => 'normal',
1701 'font-variant' => 'normal',
1702 'font-weight' => 'normal',
1703 'glyph-orientation-horizontal' => '0deg',
1704 'glyph-orientation-vertical' => 'auto',
1705 'image-rendering' => 'auto',
1706 'kerning' => 'auto',
1707 'letter-spacing' => 'normal',
1708 'lighting-color' => 'white',
1710 'marker-end' => 'none',
1711 'marker-mid' => 'none',
1712 'marker-start' => 'none',
1715 'overflow' => 'auto',
1716 'pointer-events' => 'visiblePainted',
1717 'shape-rendering' => 'auto',
1718 'stop-color' => 'black',
1719 'stop-opacity' => 1,
1721 'stroke-dasharray' => 'none',
1722 'stroke-dashoffset' => 0,
1723 'stroke-linecap' => 'butt',
1724 'stroke-linejoin' => 'miter',
1725 'stroke-miterlimit' => 4,
1726 'stroke-opacity' => 1,
1727 'stroke-width' => 1,
1728 'text-anchor' => 'start',
1729 'text-decoration' => 'none',
1730 'text-rendering' => 'auto',
1731 'unicode-bidi' => 'normal',
1732 'visibility' => 'visible',
1733 'word-spacing' => 'normal',
1734 'writing-mode' => 'lr-tb',
1735 'text-color' => 'black',
1736 'transfmatrix' => array(1, 0, 0, 1, 0, 0)
1740 * If true force sRGB color profile for all document.
1742 * @since 5.9.121 (2011-09-28)
1744 protected $force_srgb = false;
1747 * If true set the document to PDF/A mode.
1749 * @since 5.9.121 (2011-09-27)
1751 protected $pdfa_mode = false;
1754 * Document creation date-time
1756 * @since 5.9.152 (2012-03-22)
1758 protected $doc_creation_timestamp;
1761 * Document modification date-time
1763 * @since 5.9.152 (2012-03-22)
1765 protected $doc_modification_timestamp;
1770 * @since 5.9.128 (2011-10-06)
1772 protected $custom_xmp = '';
1775 * Overprint mode array.
1776 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
1778 * @since 5.9.152 (2012-03-23)
1780 protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0);
1784 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
1786 * @since 5.9.152 (2012-03-23)
1788 protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false);
1791 * Define the page boundaries boxes to be set on document.
1793 * @since 5.9.152 (2012-03-23)
1795 protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
1798 * If true print TCPDF meta link.
1800 * @since 5.9.152 (2012-03-23)
1802 protected $tcpdflink = true;
1805 * Cache array for computed GD gamma values.
1807 * @since 5.9.1632 (2012-06-05)
1809 protected $gdgammacache = array();
1811 //------------------------------------------------------------
1813 //------------------------------------------------------------
1816 * This is the class constructor.
1817 * It allows to set up the page format, the orientation and the measure unit used in all the methods (except for the font sizes).
1819 * 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.
1821 * @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>
1822 * @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.
1823 * @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().
1824 * @param $unicode (boolean) TRUE means that the input text is unicode (default = true)
1825 * @param $encoding (string) Charset encoding (used only when converting back html entities); default is UTF-8.
1826 * @param $diskcache (boolean) If TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower).
1827 * @param $pdfa (boolean) If TRUE set the document to PDF/A mode.
1829 * @see getPageSizeFromFormat(), setPageFormat()
1831 public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) {
1832 /* Set internal character encoding to ASCII */
1833 if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1834 $this->internal_encoding
= mb_internal_encoding();
1835 mb_internal_encoding('ASCII');
1837 $this->font_obj_ids
= array();
1838 $this->page_obj_id
= array();
1839 $this->form_obj_id
= array();
1841 $this->pdfa_mode
= $pdfa;
1842 $this->force_srgb
= false;
1844 $this->diskcache
= $diskcache ?
true : false;
1845 // set language direction
1847 $this->tmprtl
= false;
1850 // initialization of properties
1851 $this->isunicode
= $unicode;
1853 $this->transfmrk
[0] = array();
1854 $this->pagedim
= array();
1857 $this->pages
= array();
1859 $this->fonts
= array();
1860 $this->FontFiles
= array();
1861 $this->diffs
= array();
1862 $this->images
= array();
1863 $this->links
= array();
1864 $this->gradients
= array();
1865 $this->InFooter
= false;
1867 $this->FontFamily
= defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN
:'helvetica';
1868 $this->FontStyle
= '';
1869 $this->FontSizePt
= 12;
1870 $this->underline
= false;
1871 $this->overline
= false;
1872 $this->linethrough
= false;
1873 $this->DrawColor
= '0 G';
1874 $this->FillColor
= '0 g';
1875 $this->TextColor
= '0 g';
1876 $this->ColorFlag
= false;
1877 $this->pdflayers
= array();
1878 // encryption values
1879 $this->encrypted
= false;
1880 $this->last_enc_key
= '';
1881 // standard Unicode fonts
1882 $this->CoreFonts
= array(
1883 'courier'=>'Courier',
1884 'courierB'=>'Courier-Bold',
1885 'courierI'=>'Courier-Oblique',
1886 'courierBI'=>'Courier-BoldOblique',
1887 'helvetica'=>'Helvetica',
1888 'helveticaB'=>'Helvetica-Bold',
1889 'helveticaI'=>'Helvetica-Oblique',
1890 'helveticaBI'=>'Helvetica-BoldOblique',
1891 'times'=>'Times-Roman',
1892 'timesB'=>'Times-Bold',
1893 'timesI'=>'Times-Italic',
1894 'timesBI'=>'Times-BoldItalic',
1896 'zapfdingbats'=>'ZapfDingbats'
1899 $this->setPageUnit($unit);
1900 // set page format and orientation
1901 $this->setPageFormat($format, $orientation);
1902 // page margins (1 cm)
1903 $margin = 28.35 / $this->k
;
1904 $this->SetMargins($margin, $margin);
1905 $this->clMargin
= $this->lMargin
;
1906 $this->crMargin
= $this->rMargin
;
1907 // internal cell padding
1908 $cpadding = $margin / 10;
1909 $this->setCellPaddings($cpadding, 0, $cpadding, 0);
1911 $this->setCellMargins(0, 0, 0, 0);
1912 // line width (0.2 mm)
1913 $this->LineWidth
= 0.57 / $this->k
;
1914 $this->linestyleWidth
= sprintf('%F w', ($this->LineWidth
* $this->k
));
1915 $this->linestyleCap
= '0 J';
1916 $this->linestyleJoin
= '0 j';
1917 $this->linestyleDash
= '[] 0 d';
1918 // automatic page break
1919 $this->SetAutoPageBreak(true, (2 * $margin));
1920 // full width display mode
1921 $this->SetDisplayMode('fullwidth');
1923 $this->SetCompression();
1924 // set default PDF version number
1925 $this->setPDFVersion();
1926 $this->tcpdflink
= true;
1927 $this->encoding
= $encoding;
1928 $this->HREF
= array();
1929 $this->getFontsList();
1930 $this->fgcolor
= array('R' => 0, 'G' => 0, 'B' => 0);
1931 $this->strokecolor
= array('R' => 0, 'G' => 0, 'B' => 0);
1932 $this->bgcolor
= array('R' => 255, 'G' => 255, 'B' => 255);
1933 $this->extgstates
= array();
1934 $this->setTextShadow();
1936 $this->sign
= false;
1937 $this->ur
['enabled'] = false;
1938 $this->ur
['document'] = '/FullSave';
1939 $this->ur
['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
1940 $this->ur
['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1941 $this->ur
['signature'] = '/Modify';
1942 $this->ur
['ef'] = '/Create/Delete/Modify/Import';
1943 $this->ur
['formex'] = '';
1944 $this->signature_appearance
= array('page' => 1, 'rect' => '0 0 0 0', 'name' => 'Signature');
1945 $this->empty_signature_appearance
= array();
1946 // set default JPEG quality
1947 $this->jpeg_quality
= 75;
1948 // initialize some settings
1949 TCPDF_FONTS
::utf8Bidi(array(''), '', false, $this->isunicode
, $this->CurrentFont
);
1951 $this->SetFont($this->FontFamily
, $this->FontStyle
, $this->FontSizePt
);
1952 $this->setHeaderFont(array($this->FontFamily
, $this->FontStyle
, $this->FontSizePt
));
1953 $this->setFooterFont(array($this->FontFamily
, $this->FontStyle
, $this->FontSizePt
));
1954 // check if PCRE Unicode support is enabled
1955 if ($this->isunicode
AND (@preg_match
('/\pL/u', 'a') == 1)) {
1956 // PCRE unicode support is turned ON
1957 // \s : any whitespace character
1958 // \p{Z} : any separator
1959 // \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words.
1960 // \xa0 : Unicode Character 'NO-BREAK SPACE' (U+00A0)
1961 //$this->setSpacesRE('/(?!\xa0)[\s\p{Z}\p{Lo}]/u');
1962 $this->setSpacesRE('/(?!\xa0)[\s\p{Z}]/u');
1964 // PCRE unicode support is turned OFF
1965 $this->setSpacesRE('/[^\S\xa0]/');
1967 $this->default_form_prop
= array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1968 // set file ID for trailer
1969 $serformat = (is_array($format) ?
serialize($format) : $format);
1970 $this->file_id
= md5(TCPDF_STATIC
::getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding));
1971 // set document creation and modification timestamp
1972 $this->doc_creation_timestamp
= time();
1973 $this->doc_modification_timestamp
= $this->doc_creation_timestamp
;
1974 // get default graphic vars
1975 $this->default_graphic_vars
= $this->getGraphicVars();
1976 $this->header_xobj_autoreset
= false;
1977 $this->custom_xmp
= '';
1981 * Default destructor.
1983 * @since 1.53.0.TC016
1985 public function __destruct() {
1986 // restore internal encoding
1987 if (isset($this->internal_encoding
) AND !empty($this->internal_encoding
)) {
1988 mb_internal_encoding($this->internal_encoding
);
1990 // unset all class variables
1991 $this->_destroy(true);
1995 * Set the units of measure for the document.
1996 * @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.
1998 * @since 3.0.015 (2008-06-06)
2000 public function setPageUnit($unit) {
2001 $unit = strtolower($unit);
2012 $this->k
= $this->dpi
/ 25.4;
2017 $this->k
= $this->dpi
/ 2.54;
2022 $this->k
= $this->dpi
;
2027 $this->Error('Incorrect unit: '.$unit);
2031 $this->pdfunit
= $unit;
2032 if (isset($this->CurOrientation
)) {
2033 $this->setPageOrientation($this->CurOrientation
);
2038 * Change the format of the current page
2039 * @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 numners (width, height) or an array containing the following measures and options:<ul>
2040 * <li>['format'] = page format name (one of the above);</li>
2041 * <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>
2042 * <li>['PZ'] : The page's preferred zoom (magnification) factor.</li>
2043 * <li>['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed:</li>
2044 * <li>['MediaBox']['llx'] : lower-left x coordinate in points</li>
2045 * <li>['MediaBox']['lly'] : lower-left y coordinate in points</li>
2046 * <li>['MediaBox']['urx'] : upper-right x coordinate in points</li>
2047 * <li>['MediaBox']['ury'] : upper-right y coordinate in points</li>
2048 * <li>['CropBox'] : the visible region of default user space:</li>
2049 * <li>['CropBox']['llx'] : lower-left x coordinate in points</li>
2050 * <li>['CropBox']['lly'] : lower-left y coordinate in points</li>
2051 * <li>['CropBox']['urx'] : upper-right x coordinate in points</li>
2052 * <li>['CropBox']['ury'] : upper-right y coordinate in points</li>
2053 * <li>['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment:</li>
2054 * <li>['BleedBox']['llx'] : lower-left x coordinate in points</li>
2055 * <li>['BleedBox']['lly'] : lower-left y coordinate in points</li>
2056 * <li>['BleedBox']['urx'] : upper-right x coordinate in points</li>
2057 * <li>['BleedBox']['ury'] : upper-right y coordinate in points</li>
2058 * <li>['TrimBox'] : the intended dimensions of the finished page after trimming:</li>
2059 * <li>['TrimBox']['llx'] : lower-left x coordinate in points</li>
2060 * <li>['TrimBox']['lly'] : lower-left y coordinate in points</li>
2061 * <li>['TrimBox']['urx'] : upper-right x coordinate in points</li>
2062 * <li>['TrimBox']['ury'] : upper-right y coordinate in points</li>
2063 * <li>['ArtBox'] : the extent of the page's meaningful content:</li>
2064 * <li>['ArtBox']['llx'] : lower-left x coordinate in points</li>
2065 * <li>['ArtBox']['lly'] : lower-left y coordinate in points</li>
2066 * <li>['ArtBox']['urx'] : upper-right x coordinate in points</li>
2067 * <li>['ArtBox']['ury'] : upper-right y coordinate in points</li>
2068 * <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>
2069 * <li>['BoxColorInfo'][BOXTYPE]['C'] : an array of three numbers in the range 0-255, representing the components in the DeviceRGB colour space.</li>
2070 * <li>['BoxColorInfo'][BOXTYPE]['W'] : the guideline width in default user units</li>
2071 * <li>['BoxColorInfo'][BOXTYPE]['S'] : the guideline style: S = Solid; D = Dashed</li>
2072 * <li>['BoxColorInfo'][BOXTYPE]['D'] : dash array defining a pattern of dashes and gaps to be used in drawing dashed guidelines</li>
2073 * <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>
2074 * <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>
2075 * <li>['trans']['S'] : transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li>
2076 * <li>['trans']['D'] : The duration of the transition effect, in seconds.</li>
2077 * <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>
2078 * <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>
2079 * <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>
2080 * <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>
2081 * <li>['trans']['B'] : (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li>
2083 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul>
2084 * <li>P or Portrait (default)</li>
2085 * <li>L or Landscape</li>
2086 * <li>'' (empty string) for automatic orientation</li>
2089 * @since 3.0.015 (2008-06-06)
2090 * @see getPageSizeFromFormat()
2092 protected function setPageFormat($format, $orientation='P') {
2093 if (!empty($format) AND isset($this->pagedim
[$this->page
])) {
2094 // remove inherited values
2095 unset($this->pagedim
[$this->page
]);
2097 if (is_string($format)) {
2098 // get page measures from format name
2099 $pf = TCPDF_STATIC
::getPageSizeFromFormat($format);
2100 $this->fwPt
= $pf[0];
2101 $this->fhPt
= $pf[1];
2103 // the boundaries of the physical medium on which the page shall be displayed or printed
2104 if (isset($format['MediaBox'])) {
2105 $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
);
2106 $this->fwPt
= (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k
);
2107 $this->fhPt
= (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k
);
2109 if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) {
2110 $pf = array(($format[0] * $this->k
), ($format[1] * $this->k
));
2112 if (!isset($format['format'])) {
2114 $format['format'] = 'A4';
2116 $pf = TCPDF_STATIC
::getPageSizeFromFormat($format['format']);
2118 $this->fwPt
= $pf[0];
2119 $this->fhPt
= $pf[1];
2120 $this->pagedim
= TCPDF_STATIC
::setPageBoxes($this->page
, 'MediaBox', 0, 0, $this->fwPt
, $this->fhPt
, true, $this->k
, $this->pagedim
);
2122 // the visible region of default user space
2123 if (isset($format['CropBox'])) {
2124 $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
);
2126 // the region to which the contents of the page shall be clipped when output in a production environment
2127 if (isset($format['BleedBox'])) {
2128 $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
);
2130 // the intended dimensions of the finished page after trimming
2131 if (isset($format['TrimBox'])) {
2132 $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
);
2134 // the page's meaningful content (including potential white space)
2135 if (isset($format['ArtBox'])) {
2136 $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
);
2138 // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
2139 if (isset($format['BoxColorInfo'])) {
2140 $this->pagedim
[$this->page
]['BoxColorInfo'] = $format['BoxColorInfo'];
2142 if (isset($format['Rotate']) AND (($format['Rotate'] %
90) == 0)) {
2143 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2144 $this->pagedim
[$this->page
]['Rotate'] = intval($format['Rotate']);
2146 if (isset($format['PZ'])) {
2147 // The page's preferred zoom (magnification) factor
2148 $this->pagedim
[$this->page
]['PZ'] = floatval($format['PZ']);
2150 if (isset($format['trans'])) {
2151 // The style and duration of the visual transition to use when moving from another page to the given page during a presentation
2152 if (isset($format['trans']['Dur'])) {
2153 // The page's display duration
2154 $this->pagedim
[$this->page
]['trans']['Dur'] = floatval($format['trans']['Dur']);
2156 $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
2157 if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
2158 // The transition style that shall be used when moving to this page from another during a presentation
2159 $this->pagedim
[$this->page
]['trans']['S'] = $format['trans']['S'];
2160 $valid_effect = array('Split', 'Blinds');
2161 $valid_vals = array('H', 'V');
2162 if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
2163 $this->pagedim
[$this->page
]['trans']['Dm'] = $format['trans']['Dm'];
2165 $valid_effect = array('Split', 'Box', 'Fly');
2166 $valid_vals = array('I', 'O');
2167 if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
2168 $this->pagedim
[$this->page
]['trans']['M'] = $format['trans']['M'];
2170 $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
2171 if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
2172 if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
2173 OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
2174 OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
2175 $this->pagedim
[$this->page
]['trans']['Di'] = intval($format['trans']['Di']);
2178 if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
2179 $this->pagedim
[$this->page
]['trans']['SS'] = floatval($format['trans']['SS']);
2181 if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
2182 $this->pagedim
[$this->page
]['trans']['B'] = 'true';
2185 $this->pagedim
[$this->page
]['trans']['S'] = 'R';
2187 if (isset($format['trans']['D'])) {
2188 // The duration of the transition effect, in seconds
2189 $this->pagedim
[$this->page
]['trans']['D'] = floatval($format['trans']['D']);
2191 $this->pagedim
[$this->page
]['trans']['D'] = 1;
2195 $this->setPageOrientation($orientation);
2199 * Set page orientation.
2200 * @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>
2201 * @param $autopagebreak (boolean) Boolean indicating if auto-page-break mode should be on or off.
2202 * @param $bottommargin (float) bottom margin of the page.
2204 * @since 3.0.015 (2008-06-06)
2206 public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
2207 if (!isset($this->pagedim
[$this->page
]['MediaBox'])) {
2208 // the boundaries of the physical medium on which the page shall be displayed or printed
2209 $this->pagedim
= TCPDF_STATIC
::setPageBoxes($this->page
, 'MediaBox', 0, 0, $this->fwPt
, $this->fhPt
, true, $this->k
, $this->pagedim
);
2211 if (!isset($this->pagedim
[$this->page
]['CropBox'])) {
2212 // the visible region of default user space
2213 $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
);
2215 if (!isset($this->pagedim
[$this->page
]['BleedBox'])) {
2216 // the region to which the contents of the page shall be clipped when output in a production environment
2217 $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
);
2219 if (!isset($this->pagedim
[$this->page
]['TrimBox'])) {
2220 // the intended dimensions of the finished page after trimming
2221 $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
);
2223 if (!isset($this->pagedim
[$this->page
]['ArtBox'])) {
2224 // the page's meaningful content (including potential white space)
2225 $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
);
2227 if (!isset($this->pagedim
[$this->page
]['Rotate'])) {
2228 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2229 $this->pagedim
[$this->page
]['Rotate'] = 0;
2231 if (!isset($this->pagedim
[$this->page
]['PZ'])) {
2232 // The page's preferred zoom (magnification) factor
2233 $this->pagedim
[$this->page
]['PZ'] = 1;
2235 if ($this->fwPt
> $this->fhPt
) {
2237 $default_orientation = 'L';
2240 $default_orientation = 'P';
2242 $valid_orientations = array('P', 'L');
2243 if (empty($orientation)) {
2244 $orientation = $default_orientation;
2246 $orientation = strtoupper($orientation[0]);
2248 if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
2249 $this->CurOrientation
= $orientation;
2250 $this->wPt
= $this->fhPt
;
2251 $this->hPt
= $this->fwPt
;
2253 $this->CurOrientation
= $default_orientation;
2254 $this->wPt
= $this->fwPt
;
2255 $this->hPt
= $this->fhPt
;
2257 if ((abs($this->pagedim
[$this->page
]['MediaBox']['urx'] - $this->hPt
) < $this->feps
) AND (abs($this->pagedim
[$this->page
]['MediaBox']['ury'] - $this->wPt
) < $this->feps
)){
2258 // swap X and Y coordinates (change page orientation)
2259 $this->pagedim
= TCPDF_STATIC
::swapPageBoxCoordinates($this->page
, $this->pagedim
);
2261 $this->w
= ($this->wPt
/ $this->k
);
2262 $this->h
= ($this->hPt
/ $this->k
);
2263 if (TCPDF_STATIC
::empty_string($autopagebreak)) {
2264 if (isset($this->AutoPageBreak
)) {
2265 $autopagebreak = $this->AutoPageBreak
;
2267 $autopagebreak = true;
2270 if (TCPDF_STATIC
::empty_string($bottommargin)) {
2271 if (isset($this->bMargin
)) {
2272 $bottommargin = $this->bMargin
;
2274 // default value = 2 cm
2275 $bottommargin = 2 * 28.35 / $this->k
;
2278 $this->SetAutoPageBreak($autopagebreak, $bottommargin);
2279 // store page dimensions
2280 $this->pagedim
[$this->page
]['w'] = $this->wPt
;
2281 $this->pagedim
[$this->page
]['h'] = $this->hPt
;
2282 $this->pagedim
[$this->page
]['wk'] = $this->w
;
2283 $this->pagedim
[$this->page
]['hk'] = $this->h
;
2284 $this->pagedim
[$this->page
]['tm'] = $this->tMargin
;
2285 $this->pagedim
[$this->page
]['bm'] = $bottommargin;
2286 $this->pagedim
[$this->page
]['lm'] = $this->lMargin
;
2287 $this->pagedim
[$this->page
]['rm'] = $this->rMargin
;
2288 $this->pagedim
[$this->page
]['pb'] = $autopagebreak;
2289 $this->pagedim
[$this->page
]['or'] = $this->CurOrientation
;
2290 $this->pagedim
[$this->page
]['olm'] = $this->original_lMargin
;
2291 $this->pagedim
[$this->page
]['orm'] = $this->original_rMargin
;
2295 * Set regular expression to detect withespaces or word separators.
2296 * The pattern delimiter must be the forward-slash character "/".
2297 * Some example patterns are:
2299 * Non-Unicode or missing PCRE unicode support: "/[^\S\xa0]/"
2300 * Unicode and PCRE unicode support: "/(?!\xa0)[\s\p{Z}]/u"
2301 * Unicode and PCRE unicode support in Chinese mode: "/(?!\xa0)[\s\p{Z}\p{Lo}]/u"
2302 * if PCRE unicode support is turned ON ("\P" is the negate class of "\p"):
2303 * \s : any whitespace character
2304 * \p{Z} : any separator
2305 * \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words.
2306 * \xa0 : Unicode Character 'NO-BREAK SPACE' (U+00A0)
2308 * @param $re (string) regular expression (leave empty for default).
2310 * @since 4.6.016 (2009-06-15)
2312 public function setSpacesRE($re='/[^\S\xa0]/') {
2313 $this->re_spaces
= $re;
2314 $re_parts = explode('/', $re);
2315 // get pattern parts
2316 $this->re_space
= array();
2317 if (isset($re_parts[1]) AND !empty($re_parts[1])) {
2318 $this->re_space
['p'] = $re_parts[1];
2320 $this->re_space
['p'] = '[\s]';
2322 // set pattern modifiers
2323 if (isset($re_parts[2]) AND !empty($re_parts[2])) {
2324 $this->re_space
['m'] = $re_parts[2];
2326 $this->re_space
['m'] = '';
2331 * Enable or disable Right-To-Left language mode
2332 * @param $enable (Boolean) if true enable Right-To-Left language mode.
2333 * @param $resetx (Boolean) if true reset the X position on direction change.
2335 * @since 2.0.000 (2008-01-03)
2337 public function setRTL($enable, $resetx=true) {
2338 $enable = $enable ?
true : false;
2339 $resetx = ($resetx AND ($enable != $this->rtl
));
2340 $this->rtl
= $enable;
2341 $this->tmprtl
= false;
2348 * Return the RTL status
2351 * @since 4.0.012 (2008-07-24)
2353 public function getRTL() {
2358 * Force temporary RTL language direction
2359 * @param $mode (mixed) can be false, 'L' for LTR or 'R' for RTL
2361 * @since 2.1.000 (2008-01-09)
2363 public function setTempRTL($mode) {
2365 switch (strtoupper($mode)) {
2386 $this->tmprtl
= $newmode;
2390 * Return the current temporary RTL status
2393 * @since 4.8.014 (2009-11-04)
2395 public function isRTLTextDir() {
2396 return ($this->rtl
OR ($this->tmprtl
== 'R'));
2400 * Set the last cell height.
2401 * @param $h (float) cell height.
2402 * @author Nicola Asuni
2404 * @since 1.53.0.TC034
2406 public function setLastH($h) {
2411 * Return the cell height
2412 * @param $fontsize (int) Font size in internal units
2413 * @param $padding (boolean) If true add cell padding
2416 public function getCellHeight($fontsize, $padding=TRUE) {
2417 $height = ($fontsize * $this->cell_height_ratio
);
2419 $height +
= ($this->cell_padding
['T'] +
$this->cell_padding
['B']);
2421 return round($height, 6);
2425 * Reset the last cell height.
2427 * @since 5.9.000 (2010-10-03)
2429 public function resetLastH() {
2430 $this->lasth
= $this->getCellHeight($this->FontSize
);
2434 * Get the last cell height.
2435 * @return last cell height
2437 * @since 4.0.017 (2008-08-05)
2439 public function getLastH() {
2440 return $this->lasth
;
2444 * Set the adjusting factor to convert pixels to user units.
2445 * @param $scale (float) adjusting factor to convert pixels to user units.
2446 * @author Nicola Asuni
2450 public function setImageScale($scale) {
2451 $this->imgscale
= $scale;
2455 * Returns the adjusting factor to convert pixels to user units.
2456 * @return float adjusting factor to convert pixels to user units.
2457 * @author Nicola Asuni
2461 public function getImageScale() {
2462 return $this->imgscale
;
2466 * Returns an array of page dimensions:
2467 * <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>
2468 * @param $pagenum (int) page number (empty = current page)
2469 * @return array of page dimensions.
2470 * @author Nicola Asuni
2472 * @since 4.5.027 (2009-03-16)
2474 public function getPageDimensions($pagenum='') {
2475 if (empty($pagenum)) {
2476 $pagenum = $this->page
;
2478 return $this->pagedim
[$pagenum];
2482 * Returns the page width in units.
2483 * @param $pagenum (int) page number (empty = current page)
2484 * @return int page width.
2485 * @author Nicola Asuni
2488 * @see getPageDimensions()
2490 public function getPageWidth($pagenum='') {
2491 if (empty($pagenum)) {
2494 return $this->pagedim
[$pagenum]['w'];
2498 * Returns the page height in units.
2499 * @param $pagenum (int) page number (empty = current page)
2500 * @return int page height.
2501 * @author Nicola Asuni
2504 * @see getPageDimensions()
2506 public function getPageHeight($pagenum='') {
2507 if (empty($pagenum)) {
2510 return $this->pagedim
[$pagenum]['h'];
2514 * Returns the page break margin.
2515 * @param $pagenum (int) page number (empty = current page)
2516 * @return int page break margin.
2517 * @author Nicola Asuni
2520 * @see getPageDimensions()
2522 public function getBreakMargin($pagenum='') {
2523 if (empty($pagenum)) {
2524 return $this->bMargin
;
2526 return $this->pagedim
[$pagenum]['bm'];
2530 * Returns the scale factor (number of points in user unit).
2531 * @return int scale factor.
2532 * @author Nicola Asuni
2536 public function getScaleFactor() {
2541 * Defines the left, top and right margins.
2542 * @param $left (float) Left margin.
2543 * @param $top (float) Top margin.
2544 * @param $right (float) Right margin. Default value is the left one.
2545 * @param $keepmargins (boolean) if true overwrites the default page margins
2548 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
2550 public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
2551 //Set left, top and right margins
2552 $this->lMargin
= $left;
2553 $this->tMargin
= $top;
2557 $this->rMargin
= $right;
2559 // overwrite original values
2560 $this->original_lMargin
= $this->lMargin
;
2561 $this->original_rMargin
= $this->rMargin
;
2566 * 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.
2567 * @param $margin (float) The margin.
2570 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
2572 public function SetLeftMargin($margin) {
2574 $this->lMargin
= $margin;
2575 if (($this->page
> 0) AND ($this->x
< $margin)) {
2581 * Defines the top margin. The method can be called before creating the first page.
2582 * @param $margin (float) The margin.
2585 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
2587 public function SetTopMargin($margin) {
2589 $this->tMargin
= $margin;
2590 if (($this->page
> 0) AND ($this->y
< $margin)) {
2596 * Defines the right margin. The method can be called before creating the first page.
2597 * @param $margin (float) The margin.
2600 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
2602 public function SetRightMargin($margin) {
2603 $this->rMargin
= $margin;
2604 if (($this->page
> 0) AND ($this->x
> ($this->w
- $margin))) {
2605 $this->x
= $this->w
- $margin;
2610 * Set the same internal Cell padding for top, right, bottom, left-
2611 * @param $pad (float) internal padding.
2613 * @since 2.1.000 (2008-01-09)
2614 * @see getCellPaddings(), setCellPaddings()
2616 public function SetCellPadding($pad) {
2618 $this->cell_padding
['L'] = $pad;
2619 $this->cell_padding
['T'] = $pad;
2620 $this->cell_padding
['R'] = $pad;
2621 $this->cell_padding
['B'] = $pad;
2626 * Set the internal Cell paddings.
2627 * @param $left (float) left padding
2628 * @param $top (float) top padding
2629 * @param $right (float) right padding
2630 * @param $bottom (float) bottom padding
2632 * @since 5.9.000 (2010-10-03)
2633 * @see getCellPaddings(), SetCellPadding()
2635 public function setCellPaddings($left='', $top='', $right='', $bottom='') {
2636 if (($left !== '') AND ($left >= 0)) {
2637 $this->cell_padding
['L'] = $left;
2639 if (($top !== '') AND ($top >= 0)) {
2640 $this->cell_padding
['T'] = $top;
2642 if (($right !== '') AND ($right >= 0)) {
2643 $this->cell_padding
['R'] = $right;
2645 if (($bottom !== '') AND ($bottom >= 0)) {
2646 $this->cell_padding
['B'] = $bottom;
2651 * Get the internal Cell padding array.
2652 * @return array of padding values
2654 * @since 5.9.000 (2010-10-03)
2655 * @see setCellPaddings(), SetCellPadding()
2657 public function getCellPaddings() {
2658 return $this->cell_padding
;
2662 * Set the internal Cell margins.
2663 * @param $left (float) left margin
2664 * @param $top (float) top margin
2665 * @param $right (float) right margin
2666 * @param $bottom (float) bottom margin
2668 * @since 5.9.000 (2010-10-03)
2669 * @see getCellMargins()
2671 public function setCellMargins($left='', $top='', $right='', $bottom='') {
2672 if (($left !== '') AND ($left >= 0)) {
2673 $this->cell_margin
['L'] = $left;
2675 if (($top !== '') AND ($top >= 0)) {
2676 $this->cell_margin
['T'] = $top;
2678 if (($right !== '') AND ($right >= 0)) {
2679 $this->cell_margin
['R'] = $right;
2681 if (($bottom !== '') AND ($bottom >= 0)) {
2682 $this->cell_margin
['B'] = $bottom;
2687 * Get the internal Cell margin array.
2688 * @return array of margin values
2690 * @since 5.9.000 (2010-10-03)
2691 * @see setCellMargins()
2693 public function getCellMargins() {
2694 return $this->cell_margin
;
2698 * Adjust the internal Cell padding array to take account of the line width.
2699 * @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)))
2700 * @return array of adjustments
2702 * @since 5.9.000 (2010-10-03)
2704 protected function adjustCellPadding($brd=0) {
2708 if (is_string($brd)) {
2709 // convert string to array
2710 $slen = strlen($brd);
2712 for ($i = 0; $i < $slen; ++
$i) {
2713 $newbrd[$brd[$i]] = true;
2716 } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) {
2717 $brd = array('LRTB' => true);
2719 if (!is_array($brd)) {
2722 // store current cell padding
2723 $cp = $this->cell_padding
;
2724 // select border mode
2725 if (isset($brd['mode'])) {
2726 $mode = $brd['mode'];
2727 unset($brd['mode']);
2732 foreach ($brd as $border => $style) {
2733 $line_width = $this->LineWidth
;
2734 if (is_array($style) AND isset($style['width'])) {
2736 $line_width = $style['width'];
2738 $adj = 0; // line width inside the cell
2750 $adj = ($line_width / 2);
2754 // correct internal cell padding if required to avoid overlap between text and lines
2755 if ((strpos($border,'T') !== false) AND ($this->cell_padding
['T'] < $adj)) {
2756 $this->cell_padding
['T'] = $adj;
2758 if ((strpos($border,'R') !== false) AND ($this->cell_padding
['R'] < $adj)) {
2759 $this->cell_padding
['R'] = $adj;
2761 if ((strpos($border,'B') !== false) AND ($this->cell_padding
['B'] < $adj)) {
2762 $this->cell_padding
['B'] = $adj;
2764 if ((strpos($border,'L') !== false) AND ($this->cell_padding
['L'] < $adj)) {
2765 $this->cell_padding
['L'] = $adj;
2768 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']));
2772 * 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.
2773 * @param $auto (boolean) Boolean indicating if mode should be on or off.
2774 * @param $margin (float) Distance from the bottom of the page.
2777 * @see Cell(), MultiCell(), AcceptPageBreak()
2779 public function SetAutoPageBreak($auto, $margin=0) {
2780 $this->AutoPageBreak
= $auto ?
true : false;
2781 $this->bMargin
= $margin;
2782 $this->PageBreakTrigger
= $this->h
- $margin;
2786 * Return the auto-page-break mode (true or false).
2787 * @return boolean auto-page-break mode
2791 public function getAutoPageBreak() {
2792 return $this->AutoPageBreak
;
2796 * Defines the way the document is to be displayed by the viewer.
2797 * @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>
2798 * @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>
2799 * @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>
2803 public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
2804 if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
2805 $this->ZoomMode
= $zoom;
2807 $this->Error('Incorrect zoom display mode: '.$zoom);
2809 $this->LayoutMode
= TCPDF_STATIC
::getPageLayoutMode($layout);
2810 $this->PageMode
= TCPDF_STATIC
::getPageMode($mode);
2814 * 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.
2815 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
2816 * @param $compress (boolean) Boolean indicating if compression must be enabled.
2820 public function SetCompression($compress=true) {
2821 if (function_exists('gzcompress')) {
2822 $this->compress
= $compress ?
true : false;
2824 $this->compress
= false;
2829 * Set flag to force sRGB_IEC61966-2.1 black scaled ICC color profile for the whole document.
2830 * @param $mode (boolean) If true force sRGB output intent.
2832 * @since 5.9.121 (2011-09-28)
2834 public function setSRGBmode($mode=false) {
2835 $this->force_srgb
= $mode ?
true : false;
2839 * Turn on/off Unicode mode for document information dictionary (meta tags).
2840 * This has effect only when unicode mode is set to false.
2841 * @param $unicode (boolean) if true set the meta information in Unicode
2842 * @since 5.9.027 (2010-12-01)
2845 public function SetDocInfoUnicode($unicode=true) {
2846 $this->docinfounicode
= $unicode ?
true : false;
2850 * Defines the title of the document.
2851 * @param $title (string) The title.
2854 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
2856 public function SetTitle($title) {
2857 $this->title
= $title;
2861 * Defines the subject of the document.
2862 * @param $subject (string) The subject.
2865 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
2867 public function SetSubject($subject) {
2868 $this->subject
= $subject;
2872 * Defines the author of the document.
2873 * @param $author (string) The name of the author.
2876 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
2878 public function SetAuthor($author) {
2879 $this->author
= $author;
2883 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
2884 * @param $keywords (string) The list of keywords.
2887 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
2889 public function SetKeywords($keywords) {
2890 $this->keywords
= $keywords;
2894 * Defines the creator of the document. This is typically the name of the application that generates the PDF.
2895 * @param $creator (string) The name of the creator.
2898 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
2900 public function SetCreator($creator) {
2901 $this->creator
= $creator;
2905 * Throw an exception or print an error message and die if the K_TCPDF_PARSER_THROW_EXCEPTION_ERROR constant is set to true.
2906 * @param $msg (string) The error message
2910 public function Error($msg) {
2911 // unset all class variables
2912 $this->_destroy(true);
2913 if (defined('K_TCPDF_THROW_EXCEPTION_ERROR') AND !K_TCPDF_THROW_EXCEPTION_ERROR
) {
2914 die('<strong>TCPDF ERROR: </strong>'.$msg);
2916 throw new Exception('TCPDF ERROR: '.$msg);
2921 * This method begins the generation of the PDF document.
2922 * It is not necessary to call it explicitly because AddPage() does it automatically.
2923 * Note: no page is created by this method
2926 * @see AddPage(), Close()
2928 public function Open() {
2933 * Terminates the PDF document.
2934 * It is not necessary to call this method explicitly because Output() does it automatically.
2935 * If the document contains no page, AddPage() is called to prevent from getting an invalid document.
2938 * @see Open(), Output()
2940 public function Close() {
2941 if ($this->state
== 3) {
2944 if ($this->page
== 0) {
2948 if ($this->tcpdflink
) {
2949 // save current graphic settings
2950 $gvars = $this->getGraphicVars();
2951 $this->setEqualColumns();
2952 $this->lastpage(true);
2953 $this->SetAutoPageBreak(false);
2955 $this->y
= $this->h
- (1 / $this->k
);
2957 $this->_outSaveGraphicsState();
2958 $font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN
:'helvetica';
2959 $this->SetFont($font, '', 1);
2960 $this->setTextRenderingMode(0, false, false);
2961 $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";
2962 $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
2963 $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
2964 $this->_outRestoreGraphicsState();
2965 // restore graphic settings
2966 $this->setGraphicVars($gvars);
2972 // unset all class variables (except critical ones)
2973 $this->_destroy(false);
2977 * Move pointer at the specified document page and update page dimensions.
2978 * @param $pnum (int) page number (1 ... numpages)
2979 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
2981 * @since 2.1.000 (2008-01-07)
2982 * @see getPage(), lastpage(), getNumPages()
2984 public function setPage($pnum, $resetmargins=false) {
2985 if (($pnum == $this->page
) AND ($this->state
== 2)) {
2988 if (($pnum > 0) AND ($pnum <= $this->numpages
)) {
2990 // save current graphic settings
2991 //$gvars = $this->getGraphicVars();
2992 $oldpage = $this->page
;
2993 $this->page
= $pnum;
2994 $this->wPt
= $this->pagedim
[$this->page
]['w'];
2995 $this->hPt
= $this->pagedim
[$this->page
]['h'];
2996 $this->w
= $this->pagedim
[$this->page
]['wk'];
2997 $this->h
= $this->pagedim
[$this->page
]['hk'];
2998 $this->tMargin
= $this->pagedim
[$this->page
]['tm'];
2999 $this->bMargin
= $this->pagedim
[$this->page
]['bm'];
3000 $this->original_lMargin
= $this->pagedim
[$this->page
]['olm'];
3001 $this->original_rMargin
= $this->pagedim
[$this->page
]['orm'];
3002 $this->AutoPageBreak
= $this->pagedim
[$this->page
]['pb'];
3003 $this->CurOrientation
= $this->pagedim
[$this->page
]['or'];
3004 $this->SetAutoPageBreak($this->AutoPageBreak
, $this->bMargin
);
3005 // restore graphic settings
3006 //$this->setGraphicVars($gvars);
3007 if ($resetmargins) {
3008 $this->lMargin
= $this->pagedim
[$this->page
]['olm'];
3009 $this->rMargin
= $this->pagedim
[$this->page
]['orm'];
3010 $this->SetY($this->tMargin
);
3012 // account for booklet mode
3013 if ($this->pagedim
[$this->page
]['olm'] != $this->pagedim
[$oldpage]['olm']) {
3014 $deltam = $this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$this->page
]['orm'];
3015 $this->lMargin +
= $deltam;
3016 $this->rMargin
-= $deltam;
3020 $this->Error('Wrong page number on setPage() function: '.$pnum);
3025 * Reset pointer to the last document page.
3026 * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
3028 * @since 2.0.000 (2008-01-04)
3029 * @see setPage(), getPage(), getNumPages()
3031 public function lastPage($resetmargins=false) {
3032 $this->setPage($this->getNumPages(), $resetmargins);
3036 * Get current document page number.
3037 * @return int page number
3039 * @since 2.1.000 (2008-01-07)
3040 * @see setPage(), lastpage(), getNumPages()
3042 public function getPage() {
3047 * Get the total number of insered pages.
3048 * @return int number of pages
3050 * @since 2.1.000 (2008-01-07)
3051 * @see setPage(), getPage(), lastpage()
3053 public function getNumPages() {
3054 return $this->numpages
;
3058 * Adds a new TOC (Table Of Content) page to the document.
3059 * @param $orientation (string) page orientation.
3060 * @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().
3061 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
3063 * @since 5.0.001 (2010-05-06)
3064 * @see AddPage(), startPage(), endPage(), endTOCPage()
3066 public function addTOCPage($orientation='', $format='', $keepmargins=false) {
3067 $this->AddPage($orientation, $format, $keepmargins, true);
3071 * Terminate the current TOC (Table Of Content) page
3073 * @since 5.0.001 (2010-05-06)
3074 * @see AddPage(), startPage(), endPage(), addTOCPage()
3076 public function endTOCPage() {
3077 $this->endPage(true);
3081 * 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).
3082 * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.
3083 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3084 * @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().
3085 * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
3086 * @param $tocpage (boolean) if true set the tocpage state to true (the added page will be used to display Table Of Content).
3089 * @see startPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3091 public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
3092 if ($this->inxobj
) {
3093 // we are inside an XObject template
3096 if (!isset($this->original_lMargin
) OR $keepmargins) {
3097 $this->original_lMargin
= $this->lMargin
;
3099 if (!isset($this->original_rMargin
) OR $keepmargins) {
3100 $this->original_rMargin
= $this->rMargin
;
3102 // terminate previous page
3105 $this->startPage($orientation, $format, $tocpage);
3109 * Terminate the current page
3110 * @param $tocpage (boolean) if true set the tocpage state to false (end the page used to display Table Of Content).
3112 * @since 4.2.010 (2008-11-14)
3113 * @see AddPage(), startPage(), addTOCPage(), endTOCPage()
3115 public function endPage($tocpage=false) {
3116 // check if page is already closed
3117 if (($this->page
== 0) OR ($this->numpages
> $this->page
) OR (!$this->pageopen
[$this->page
])) {
3120 // print page footer
3124 // mark page as closed
3125 $this->pageopen
[$this->page
] = false;
3127 $this->tocpage
= false;
3132 * Starts a new page to the document. The page must be closed using the endPage() function.
3133 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
3134 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3135 * @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().
3136 * @param $tocpage (boolean) if true the page is designated to contain the Table-Of-Content.
3137 * @since 4.2.010 (2008-11-14)
3138 * @see AddPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3141 public function startPage($orientation='', $format='', $tocpage=false) {
3143 $this->tocpage
= true;
3145 // move page numbers of documents to be attached
3146 if ($this->tocpage
) {
3147 // move reference to unexistent pages (used for page attachments)
3149 $tmpoutlines = $this->outlines
;
3150 foreach ($tmpoutlines as $key => $outline) {
3151 if (!$outline['f'] AND ($outline['p'] > $this->numpages
)) {
3152 $this->outlines
[$key]['p'] = ($outline['p'] +
1);
3156 $tmpdests = $this->dests
;
3157 foreach ($tmpdests as $key => $dest) {
3158 if (!$dest['f'] AND ($dest['p'] > $this->numpages
)) {
3159 $this->dests
[$key]['p'] = ($dest['p'] +
1);
3163 $tmplinks = $this->links
;
3164 foreach ($tmplinks as $key => $link) {
3165 if (!$link['f'] AND ($link['p'] > $this->numpages
)) {
3166 $this->links
[$key]['p'] = ($link['p'] +
1);
3170 if ($this->numpages
> $this->page
) {
3171 // this page has been already added
3172 $this->setPage($this->page +
1);
3173 $this->SetY($this->tMargin
);
3177 if ($this->state
== 0) {
3181 $this->swapMargins($this->booklet
);
3182 // save current graphic settings
3183 $gvars = $this->getGraphicVars();
3185 $this->_beginpage($orientation, $format);
3186 // mark page as open
3187 $this->pageopen
[$this->page
] = true;
3188 // restore graphic settings
3189 $this->setGraphicVars($gvars);
3191 $this->setPageMark();
3192 // print page header
3194 // restore graphic settings
3195 $this->setGraphicVars($gvars);
3197 $this->setPageMark();
3198 // print table header (if any)
3199 $this->setTableHeader();
3200 // set mark for empty page check
3201 $this->emptypagemrk
[$this->page
]= $this->pagelen
[$this->page
];
3205 * Set start-writing mark on current page stream used to put borders and fills.
3206 * Borders and fills are always created after content and inserted on the position marked by this method.
3207 * This function must be called after calling Image() function for a background image.
3208 * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
3210 * @since 4.0.016 (2008-07-30)
3212 public function setPageMark() {
3213 $this->intmrk
[$this->page
] = $this->pagelen
[$this->page
];
3214 $this->bordermrk
[$this->page
] = $this->intmrk
[$this->page
];
3215 $this->setContentMark();
3219 * Set start-writing mark on selected page.
3220 * Borders and fills are always created after content and inserted on the position marked by this method.
3221 * @param $page (int) page number (default is the current page)
3223 * @since 4.6.021 (2009-07-20)
3225 protected function setContentMark($page=0) {
3227 $page = $this->page
;
3229 if (isset($this->footerlen
[$page])) {
3230 $this->cntmrk
[$page] = $this->pagelen
[$page] - $this->footerlen
[$page];
3232 $this->cntmrk
[$page] = $this->pagelen
[$page];
3238 * @param $ln (string) header image logo
3239 * @param $lw (string) header image logo width in mm
3240 * @param $ht (string) string to print as title on document header
3241 * @param $hs (string) string to print on document header
3242 * @param $tc (array) RGB array color for text.
3243 * @param $lc (array) RGB array color for line.
3246 public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) {
3247 $this->header_logo
= $ln;
3248 $this->header_logo_width
= $lw;
3249 $this->header_title
= $ht;
3250 $this->header_string
= $hs;
3251 $this->header_text_color
= $tc;
3252 $this->header_line_color
= $lc;
3257 * @param $tc (array) RGB array color for text.
3258 * @param $lc (array) RGB array color for line.
3261 public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) {
3262 $this->footer_text_color
= $tc;
3263 $this->footer_line_color
= $lc;
3267 * Returns header data:
3268 * <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>
3271 * @since 4.0.012 (2008-07-24)
3273 public function getHeaderData() {
3275 $ret['logo'] = $this->header_logo
;
3276 $ret['logo_width'] = $this->header_logo_width
;
3277 $ret['title'] = $this->header_title
;
3278 $ret['string'] = $this->header_string
;
3279 $ret['text_color'] = $this->header_text_color
;
3280 $ret['line_color'] = $this->header_line_color
;
3285 * Set header margin.
3286 * (minimum distance between header and top page margin)
3287 * @param $hm (int) distance in user units
3290 public function setHeaderMargin($hm=10) {
3291 $this->header_margin
= $hm;
3295 * Returns header margin in user units.
3297 * @since 4.0.012 (2008-07-24)
3300 public function getHeaderMargin() {
3301 return $this->header_margin
;
3305 * Set footer margin.
3306 * (minimum distance between footer and bottom page margin)
3307 * @param $fm (int) distance in user units
3310 public function setFooterMargin($fm=10) {
3311 $this->footer_margin
= $fm;
3315 * Returns footer margin in user units.
3317 * @since 4.0.012 (2008-07-24)
3320 public function getFooterMargin() {
3321 return $this->footer_margin
;
3324 * Set a flag to print page header.
3325 * @param $val (boolean) set to true to print the page header (default), false otherwise.
3328 public function setPrintHeader($val=true) {
3329 $this->print_header
= $val ?
true : false;
3333 * Set a flag to print page footer.
3334 * @param $val (boolean) set to true to print the page footer (default), false otherwise.
3337 public function setPrintFooter($val=true) {
3338 $this->print_footer
= $val ?
true : false;
3342 * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
3346 public function getImageRBX() {
3347 return $this->img_rb_x
;
3351 * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
3355 public function getImageRBY() {
3356 return $this->img_rb_y
;
3360 * Reset the xobject template used by Header() method.
3363 public function resetHeaderTemplate() {
3364 $this->header_xobjid
= false;
3368 * Set a flag to automatically reset the xobject template used by Header() method at each page.
3369 * @param $val (boolean) set to true to reset Header xobject template at each page, false otherwise.
3372 public function setHeaderTemplateAutoreset($val=true) {
3373 $this->header_xobj_autoreset
= $val ?
true : false;
3377 * This method is used to render the page header.
3378 * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3381 public function Header() {
3382 if ($this->header_xobjid
=== false) {
3383 // start a new XObject Template
3384 $this->header_xobjid
= $this->startTemplate($this->w
, $this->tMargin
);
3385 $headerfont = $this->getHeaderFont();
3386 $headerdata = $this->getHeaderData();
3387 $this->y
= $this->header_margin
;
3389 $this->x
= $this->w
- $this->original_rMargin
;
3391 $this->x
= $this->original_lMargin
;
3393 if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE
)) {
3394 $imgtype = TCPDF_IMAGES
::getImageFileType(K_PATH_IMAGES
.$headerdata['logo']);
3395 if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
3396 $this->ImageEps(K_PATH_IMAGES
.$headerdata['logo'], '', '', $headerdata['logo_width']);
3397 } elseif ($imgtype == 'svg') {
3398 $this->ImageSVG(K_PATH_IMAGES
.$headerdata['logo'], '', '', $headerdata['logo_width']);
3400 $this->Image(K_PATH_IMAGES
.$headerdata['logo'], '', '', $headerdata['logo_width']);
3402 $imgy = $this->getImageRBY();
3406 $cell_height = $this->getCellHeight($headerfont[2] / $this->k
);
3407 // set starting margin for text data cell
3408 if ($this->getRTL()) {
3409 $header_x = $this->original_rMargin +
($headerdata['logo_width'] * 1.1);
3411 $header_x = $this->original_lMargin +
($headerdata['logo_width'] * 1.1);
3413 $cw = $this->w
- $this->original_lMargin
- $this->original_rMargin
- ($headerdata['logo_width'] * 1.1);
3414 $this->SetTextColorArray($this->header_text_color
);
3416 $this->SetFont($headerfont[0], 'B', $headerfont[2] +
1);
3417 $this->SetX($header_x);
3418 $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
3420 $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
3421 $this->SetX($header_x);
3422 $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false);
3423 // print an ending header line
3424 $this->SetLineStyle(array('width' => 0.85 / $this->k
, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color']));
3425 $this->SetY((2.835 / $this->k
) +
max($imgy, $this->y
));
3427 $this->SetX($this->original_rMargin
);
3429 $this->SetX($this->original_lMargin
);
3431 $this->Cell(($this->w
- $this->original_lMargin
- $this->original_rMargin
), 0, '', 'T', 0, 'C');
3432 $this->endTemplate();
3434 // print header template
3437 if (!$this->header_xobj_autoreset
AND $this->booklet
AND (($this->page %
2) == 0)) {
3438 // adjust margins for booklet mode
3439 $dx = ($this->original_lMargin
- $this->original_rMargin
);
3442 $x = $this->w +
$dx;
3446 $this->printTemplate($this->header_xobjid
, $x, 0, 0, 0, '', '', false);
3447 if ($this->header_xobj_autoreset
) {
3448 // reset header xobject template at each page
3449 $this->header_xobjid
= false;
3454 * This method is used to render the page footer.
3455 * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3458 public function Footer() {
3460 $this->SetTextColorArray($this->footer_text_color
);
3461 //set style for cell border
3462 $line_width = (0.85 / $this->k
);
3463 $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color
));
3464 //print document barcode
3465 $barcode = $this->getBarcode();
3466 if (!empty($barcode)) {
3467 $this->Ln($line_width);
3468 $barcode_width = round(($this->w
- $this->original_lMargin
- $this->original_rMargin
) / 3);
3470 'position' => $this->rtl?
'R':'L',
3471 'align' => $this->rtl?
'R':'L',
3474 'cellfitalign' => '',
3477 'fgcolor' => array(0,0,0),
3481 $this->write1DBarcode($barcode, 'C128', '', $cur_y +
$line_width, '', (($this->footer_margin
/ 3) - $line_width), 0.3, $style, '');
3483 $w_page = isset($this->l
['w_page']) ?
$this->l
['w_page'].' ' : '';
3484 if (empty($this->pagegroups
)) {
3485 $pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
3487 $pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
3489 $this->SetY($cur_y);
3491 if ($this->getRTL()) {
3492 $this->SetX($this->original_rMargin
);
3493 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
3495 $this->SetX($this->original_lMargin
);
3496 $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R');
3501 * This method is used to render the page header.
3503 * @since 4.0.012 (2008-07-24)
3505 protected function setHeader() {
3506 if (!$this->print_header
OR ($this->state
!= 2)) {
3509 $this->InHeader
= true;
3510 $this->setGraphicVars($this->default_graphic_vars
);
3511 $temp_thead = $this->thead
;
3512 $temp_theadMargins = $this->theadMargins
;
3513 $lasth = $this->lasth
;
3514 $newline = $this->newline
;
3515 $this->_outSaveGraphicsState();
3516 $this->rMargin
= $this->original_rMargin
;
3517 $this->lMargin
= $this->original_lMargin
;
3518 $this->SetCellPadding(0);
3519 //set current position
3521 $this->SetXY($this->original_rMargin
, $this->header_margin
);
3523 $this->SetXY($this->original_lMargin
, $this->header_margin
);
3525 $this->SetFont($this->header_font
[0], $this->header_font
[1], $this->header_font
[2]);
3529 $this->SetXY($this->original_rMargin
, $this->tMargin
);
3531 $this->SetXY($this->original_lMargin
, $this->tMargin
);
3533 $this->_outRestoreGraphicsState();
3534 $this->lasth
= $lasth;
3535 $this->thead
= $temp_thead;
3536 $this->theadMargins
= $temp_theadMargins;
3537 $this->newline
= $newline;
3538 $this->InHeader
= false;
3542 * This method is used to render the page footer.
3544 * @since 4.0.012 (2008-07-24)
3546 protected function setFooter() {
3547 if ($this->state
!= 2) {
3550 $this->InFooter
= true;
3551 // save current graphic settings
3552 $gvars = $this->getGraphicVars();
3554 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
];
3556 if ($this->print_footer
) {
3557 $this->setGraphicVars($this->default_graphic_vars
);
3558 $this->current_column
= 0;
3559 $this->num_columns
= 1;
3560 $temp_thead = $this->thead
;
3561 $temp_theadMargins = $this->theadMargins
;
3562 $lasth = $this->lasth
;
3563 $this->_outSaveGraphicsState();
3564 $this->rMargin
= $this->original_rMargin
;
3565 $this->lMargin
= $this->original_lMargin
;
3566 $this->SetCellPadding(0);
3567 //set current position
3568 $footer_y = $this->h
- $this->footer_margin
;
3570 $this->SetXY($this->original_rMargin
, $footer_y);
3572 $this->SetXY($this->original_lMargin
, $footer_y);
3574 $this->SetFont($this->footer_font
[0], $this->footer_font
[1], $this->footer_font
[2]);
3578 $this->SetXY($this->original_rMargin
, $this->tMargin
);
3580 $this->SetXY($this->original_lMargin
, $this->tMargin
);
3582 $this->_outRestoreGraphicsState();
3583 $this->lasth
= $lasth;
3584 $this->thead
= $temp_thead;
3585 $this->theadMargins
= $temp_theadMargins;
3587 // restore graphic settings
3588 $this->setGraphicVars($gvars);
3589 $this->current_column
= $gvars['current_column'];
3590 $this->num_columns
= $gvars['num_columns'];
3591 // calculate footer length
3592 $this->footerlen
[$this->page
] = $this->pagelen
[$this->page
] - $this->footerpos
[$this->page
] +
1;
3593 $this->InFooter
= false;
3597 * Check if we are on the page body (excluding page header and footer).
3598 * @return true if we are not in page header nor in page footer, false otherwise.
3600 * @since 5.9.091 (2011-06-15)
3602 protected function inPageBody() {
3603 return (($this->InHeader
=== false) AND ($this->InFooter
=== false));
3607 * This method is used to render the table header on new page (if any).
3609 * @since 4.5.030 (2009-03-25)
3611 protected function setTableHeader() {
3612 if ($this->num_columns
> 1) {
3613 // multi column mode
3616 if (isset($this->theadMargins
['top'])) {
3617 // restore the original top-margin
3618 $this->tMargin
= $this->theadMargins
['top'];
3619 $this->pagedim
[$this->page
]['tm'] = $this->tMargin
;
3620 $this->y
= $this->tMargin
;
3622 if (!TCPDF_STATIC
::empty_string($this->thead
) AND (!$this->inthead
)) {
3624 $prev_lMargin = $this->lMargin
;
3625 $prev_rMargin = $this->rMargin
;
3626 $prev_cell_padding = $this->cell_padding
;
3627 $this->lMargin
= $this->theadMargins
['lmargin'] +
($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$this->theadMargins
['page']]['olm']);
3628 $this->rMargin
= $this->theadMargins
['rmargin'] +
($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$this->theadMargins
['page']]['orm']);
3629 $this->cell_padding
= $this->theadMargins
['cell_padding'];
3631 $this->x
= $this->w
- $this->rMargin
;
3633 $this->x
= $this->lMargin
;
3635 // account for special "cell" mode
3636 if ($this->theadMargins
['cell']) {
3638 $this->x
-= $this->cell_padding
['R'];
3640 $this->x +
= $this->cell_padding
['L'];
3643 $gvars = $this->getGraphicVars();
3644 if (!empty($this->theadMargins
['gvars'])) {
3645 // set the correct graphic style
3646 $this->setGraphicVars($this->theadMargins
['gvars']);
3647 $this->rMargin
= $gvars['rMargin'];
3648 $this->lMargin
= $gvars['lMargin'];
3650 // print table header
3651 $this->writeHTML($this->thead
, false, false, false, false, '');
3652 $this->setGraphicVars($gvars);
3653 // set new top margin to skip the table headers
3654 if (!isset($this->theadMargins
['top'])) {
3655 $this->theadMargins
['top'] = $this->tMargin
;
3657 // store end of header position
3658 if (!isset($this->columns
[0]['th'])) {
3659 $this->columns
[0]['th'] = array();
3661 $this->columns
[0]['th']['\''.$this->page
.'\''] = $this->y
;
3662 $this->tMargin
= $this->y
;
3663 $this->pagedim
[$this->page
]['tm'] = $this->tMargin
;
3665 $this->lMargin
= $prev_lMargin;
3666 $this->rMargin
= $prev_rMargin;
3667 $this->cell_padding
= $prev_cell_padding;
3672 * Returns the current page number.
3673 * @return int page number
3676 * @see getAliasNbPages()
3678 public function PageNo() {
3683 * Returns the array of spot colors.
3684 * @return (array) Spot colors array.
3686 * @since 6.0.038 (2013-09-30)
3688 public function getAllSpotColors() {
3689 return $this->spot_colors
;
3693 * Defines a new spot color.
3694 * It can be expressed in RGB components or gray scale.
3695 * The method can be called before the first page is created and the value is retained from page to page.
3696 * @param $name (string) Full name of the spot color.
3697 * @param $c (float) Cyan color for CMYK. Value between 0 and 100.
3698 * @param $m (float) Magenta color for CMYK. Value between 0 and 100.
3699 * @param $y (float) Yellow color for CMYK. Value between 0 and 100.
3700 * @param $k (float) Key (Black) color for CMYK. Value between 0 and 100.
3702 * @since 4.0.024 (2008-09-12)
3703 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3705 public function AddSpotColor($name, $c, $m, $y, $k) {
3706 if (!isset($this->spot_colors
[$name])) {
3707 $i = (1 +
count($this->spot_colors
));
3708 $this->spot_colors
[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i);
3713 * Set the spot color for the specified type ('draw', 'fill', 'text').
3714 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3715 * @param $name (string) Name of the spot color.
3716 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3717 * @return (string) PDF color command.
3719 * @since 5.9.125 (2011-10-03)
3721 public function setSpotColor($type, $name, $tint=100) {
3722 $spotcolor = TCPDF_COLORS
::getSpotColor($name, $this->spot_colors
);
3723 if ($spotcolor === false) {
3724 $this->Error('Undefined spot color: '.$name.', you must add it using the AddSpotColor() method.');
3726 $tint = (max(0, min(100, $tint)) / 100);
3727 $pdfcolor = sprintf('/CS%d ', $this->spot_colors
[$name]['i']);
3730 $pdfcolor .= sprintf('CS %F SCN', $tint);
3731 $this->DrawColor
= $pdfcolor;
3732 $this->strokecolor
= $spotcolor;
3736 $pdfcolor .= sprintf('cs %F scn', $tint);
3737 $this->FillColor
= $pdfcolor;
3738 $this->bgcolor
= $spotcolor;
3742 $pdfcolor .= sprintf('cs %F scn', $tint);
3743 $this->TextColor
= $pdfcolor;
3744 $this->fgcolor
= $spotcolor;
3748 $this->ColorFlag
= ($this->FillColor
!= $this->TextColor
);
3749 if ($this->state
== 2) {
3750 $this->_out($pdfcolor);
3752 if ($this->inxobj
) {
3753 // we are inside an XObject template
3754 $this->xobjects
[$this->xobjid
]['spot_colors'][$name] = $this->spot_colors
[$name];
3760 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
3761 * @param $name (string) Name of the spot color.
3762 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3764 * @since 4.0.024 (2008-09-12)
3765 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3767 public function SetDrawSpotColor($name, $tint=100) {
3768 $this->setSpotColor('draw', $name, $tint);
3772 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
3773 * @param $name (string) Name of the spot color.
3774 * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3776 * @since 4.0.024 (2008-09-12)
3777 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
3779 public function SetFillSpotColor($name, $tint=100) {
3780 $this->setSpotColor('fill', $name, $tint);
3784 * Defines the spot color used for text.
3785 * @param $name (string) Name of the spot color.
3786 * @param $tint (int) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3788 * @since 4.0.024 (2008-09-12)
3789 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
3791 public function SetTextSpotColor($name, $tint=100) {
3792 $this->setSpotColor('text', $name, $tint);
3796 * Set the color array for the specified type ('draw', 'fill', 'text').
3797 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3798 * The method can be called before the first page is created and the value is retained from page to page.
3799 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3800 * @param $color (array) Array of colors (1=gray, 3=RGB, 4=CMYK or 5=spotcolor=CMYK+name values).
3801 * @param $ret (boolean) If true do not send the PDF command.
3802 * @return (string) The PDF command or empty string.
3804 * @since 3.1.000 (2008-06-11)
3806 public function setColorArray($type, $color, $ret=false) {
3807 if (is_array($color)) {
3808 $color = array_values($color);
3809 // component: grey, RGB red or CMYK cyan
3810 $c = isset($color[0]) ?
$color[0] : -1;
3811 // component: RGB green or CMYK magenta
3812 $m = isset($color[1]) ?
$color[1] : -1;
3813 // component: RGB blue or CMYK yellow
3814 $y = isset($color[2]) ?
$color[2] : -1;
3815 // component: CMYK black
3816 $k = isset($color[3]) ?
$color[3] : -1;
3818 $name = isset($color[4]) ?
$color[4] : '';
3820 return $this->setColor($type, $c, $m, $y, $k, $ret, $name);
3827 * Defines the color used for all drawing operations (lines, rectangles and cell borders).
3828 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3829 * The method can be called before the first page is created and the value is retained from page to page.
3830 * @param $color (array) Array of colors (1, 3 or 4 values).
3831 * @param $ret (boolean) If true do not send the PDF command.
3832 * @return string the PDF command
3834 * @since 3.1.000 (2008-06-11)
3835 * @see SetDrawColor()
3837 public function SetDrawColorArray($color, $ret=false) {
3838 return $this->setColorArray('draw', $color, $ret);
3842 * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
3843 * It can be expressed in RGB, CMYK or GRAY SCALE components.
3844 * The method can be called before the first page is created and the value is retained from page to page.
3845 * @param $color (array) Array of colors (1, 3 or 4 values).
3846 * @param $ret (boolean) If true do not send the PDF command.
3848 * @since 3.1.000 (2008-6-11)
3849 * @see SetFillColor()
3851 public function SetFillColorArray($color, $ret=false) {
3852 return $this->setColorArray('fill', $color, $ret);
3856 * Defines the color used for text. It can be expressed in RGB components or gray scale.
3857 * The method can be called before the first page is created and the value is retained from page to page.
3858 * @param $color (array) Array of colors (1, 3 or 4 values).
3859 * @param $ret (boolean) If true do not send the PDF command.
3861 * @since 3.1.000 (2008-6-11)
3862 * @see SetFillColor()
3864 public function SetTextColorArray($color, $ret=false) {
3865 return $this->setColorArray('text', $color, $ret);
3869 * Defines the color used by the specified type ('draw', 'fill', 'text').
3870 * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3871 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3872 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3873 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3874 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3875 * @param $ret (boolean) If true do not send the command.
3876 * @param $name (string) spot color name (if any)
3877 * @return (string) The PDF command or empty string.
3879 * @since 5.9.125 (2011-10-03)
3881 public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3882 // set default values
3883 if (!is_numeric($col1)) {
3886 if (!is_numeric($col2)) {
3889 if (!is_numeric($col3)) {
3892 if (!is_numeric($col4)) {
3895 // set color by case
3897 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
3899 $col1 = max(0, min(255, $col1));
3900 $intcolor = array('G' => $col1);
3901 $pdfcolor = sprintf('%F ', ($col1 / 255));
3903 } elseif ($col4 == -1) {
3905 $col1 = max(0, min(255, $col1));
3906 $col2 = max(0, min(255, $col2));
3907 $col3 = max(0, min(255, $col3));
3908 $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
3909 $pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255));
3912 $col1 = max(0, min(100, $col1));
3913 $col2 = max(0, min(100, $col2));
3914 $col3 = max(0, min(100, $col3));
3915 $col4 = max(0, min(100, $col4));
3918 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
3919 $pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
3923 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
3924 $this->AddSpotColor($name, $col1, $col2, $col3, $col4);
3925 $pdfcolor = $this->setSpotColor($type, $name, 100);
3930 $pdfcolor .= strtoupper($suffix);
3931 $this->DrawColor
= $pdfcolor;
3932 $this->strokecolor
= $intcolor;
3936 $pdfcolor .= $suffix;
3937 $this->FillColor
= $pdfcolor;
3938 $this->bgcolor
= $intcolor;
3942 $pdfcolor .= $suffix;
3943 $this->TextColor
= $pdfcolor;
3944 $this->fgcolor
= $intcolor;
3948 $this->ColorFlag
= ($this->FillColor
!= $this->TextColor
);
3949 if (($type != 'text') AND ($this->state
== 2)) {
3951 $this->_out($pdfcolor);
3959 * 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.
3960 * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3961 * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3962 * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3963 * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3964 * @param $ret (boolean) If true do not send the command.
3965 * @param $name (string) spot color name (if any)
3966 * @return string the PDF command
3969 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
3971 public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3972 return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name);
3976 * 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.
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 SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
3988 public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3989 return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name);
3993 * 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.
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) Empty string.
4003 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
4005 public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4006 return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name);
4010 * Returns the length of a string in user unit. A font must be selected.<br>
4011 * @param $s (string) The string whose length is to be computed
4012 * @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.
4013 * @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.
4014 * @param $fontsize (float) Font size in points. The default value is the current size.
4015 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
4016 * @return mixed int total string length or array of characted widths
4017 * @author Nicola Asuni
4021 public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4022 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);
4026 * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br>
4027 * @param $sa (string) The array of chars whose total length is to be computed
4028 * @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.
4029 * @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.
4030 * @param $fontsize (float) Font size in points. The default value is the current size.
4031 * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
4032 * @return mixed int total string length or array of characted widths
4033 * @author Nicola Asuni
4035 * @since 2.4.000 (2008-03-06)
4037 public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4038 // store current values
4039 if (!TCPDF_STATIC
::empty_string($fontname)) {
4040 $prev_FontFamily = $this->FontFamily
;
4041 $prev_FontStyle = $this->FontStyle
;
4042 $prev_FontSizePt = $this->FontSizePt
;
4043 $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false);
4045 // convert UTF-8 array to Latin1 if required
4046 if ($this->isunicode
AND (!$this->isUnicodeFont())) {
4047 $sa = TCPDF_FONTS
::UTF8ArrToLatin1Arr($sa);
4049 $w = 0; // total width
4050 $wa = array(); // array of characters widths
4051 foreach ($sa as $ck => $char) {
4053 $cw = $this->GetCharWidth($char, isset($sa[($ck +
1)]));
4057 // restore previous values
4058 if (!TCPDF_STATIC
::empty_string($fontname)) {
4059 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
4068 * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking).
4069 * @param $char (int) The char code whose length is to be returned
4070 * @param $notlast (boolean) If false ignore the font-spacing.
4071 * @return float char width
4072 * @author Nicola Asuni
4074 * @since 2.4.000 (2008-03-06)
4076 public function GetCharWidth($char, $notlast=true) {
4078 $chw = $this->getRawCharWidth($char);
4079 if (($this->font_spacing
< 0) OR (($this->font_spacing
> 0) AND $notlast)) {
4080 // increase/decrease font spacing
4081 $chw +
= $this->font_spacing
;
4083 if ($this->font_stretching
!= 100) {
4084 // fixed stretching mode
4085 $chw *= ($this->font_stretching
/ 100);
4091 * Returns the length of the char in user unit for the current font.
4092 * @param $char (int) The char code whose length is to be returned
4093 * @return float char width
4094 * @author Nicola Asuni
4096 * @since 5.9.000 (2010-09-28)
4098 public function getRawCharWidth($char) {
4100 // SHY character will not be printed
4103 if (isset($this->CurrentFont
['cw'][$char])) {
4104 $w = $this->CurrentFont
['cw'][$char];
4105 } elseif (isset($this->CurrentFont
['dw'])) {
4107 $w = $this->CurrentFont
['dw'];
4108 } elseif (isset($this->CurrentFont
['cw'][32])) {
4110 $w = $this->CurrentFont
['cw'][32];
4114 return $this->getAbsFontMeasure($w);
4118 * Returns the numbero of characters in a string.
4119 * @param $s (string) The input string.
4120 * @return int number of characters
4122 * @since 2.0.0001 (2008-01-07)
4124 public function GetNumChars($s) {
4125 if ($this->isUnicodeFont()) {
4126 return count(TCPDF_FONTS
::UTF8StringToArray($s, $this->isunicode
, $this->CurrentFont
));
4132 * Fill the list of available fonts ($this->fontlist).
4134 * @since 4.0.013 (2008-07-28)
4136 protected function getFontsList() {
4137 if (($fontsdir = opendir(TCPDF_FONTS
::_getfontpath())) !== false) {
4138 while (($file = readdir($fontsdir)) !== false) {
4139 if (substr($file, -4) == '.php') {
4140 array_push($this->fontlist
, strtolower(basename($file, '.php')));
4143 closedir($fontsdir);
4148 * Returns the unicode caracter specified by the value
4149 * @param $c (int) UTF-8 value
4150 * @return Returns the specified character.
4151 * @since 2.3.000 (2008-03-05)
4155 public function unichr($c) {
4156 return TCPDF_FONTS
::unichr($c, $this->isunicode
);
4160 * Convert and add the selected TrueType or Type1 font to the fonts folder (that must be writeable).
4161 * @param $fontfile (string) Font file (full path).
4162 * @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.
4163 * @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.
4164 * @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.
4165 * @param $outpath (string) Output path for generated font files (must be writeable by the web server). Leave empty for default font folder.
4166 * @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).
4167 * @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.
4168 * @param $addcbbox (boolean) If true includes the character bounding box information on the php font file.
4169 * @return (string) TCPDF font name.
4170 * @author Nicola Asuni
4171 * @since 5.9.123 (2010-09-30)
4175 public function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false) {
4176 return TCPDF_FONTS
::addTTFfont($fontfile, $fonttype, $enc, $flags, $outpath, $platid, $encid, $addcbbox);
4180 * Imports a TrueType, Type1, core, or CID0 font and makes it available.
4181 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
4182 * 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.
4183 * @param $family (string) Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
4184 * @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>
4185 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4186 * @return array containing the font data, or false in case of error.
4187 * @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.
4190 * @see SetFont(), setFontSubsetting()
4192 public function AddFont($family, $style='', $fontfile='', $subset='default') {
4193 if ($subset === 'default') {
4194 $subset = $this->font_subsetting
;
4196 if ($this->pdfa_mode
) {
4199 if (TCPDF_STATIC
::empty_string($family)) {
4200 if (!TCPDF_STATIC
::empty_string($this->FontFamily
)) {
4201 $family = $this->FontFamily
;
4203 $this->Error('Empty font family');
4206 // move embedded styles on $style
4207 if (substr($family, -1) == 'I') {
4209 $family = substr($family, 0, -1);
4211 if (substr($family, -1) == 'B') {
4213 $family = substr($family, 0, -1);
4215 // normalize family name
4216 $family = strtolower($family);
4217 if ((!$this->isunicode
) AND ($family == 'arial')) {
4218 $family = 'helvetica';
4220 if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
4223 if ($this->pdfa_mode
AND (isset($this->CoreFonts
[$family]))) {
4224 // all fonts must be embedded
4225 $family = 'pdfa'.$family;
4227 $tempstyle = strtoupper($style);
4230 if (strpos($tempstyle, 'U') !== false) {
4231 $this->underline
= true;
4233 $this->underline
= false;
4235 // line-through (deleted)
4236 if (strpos($tempstyle, 'D') !== false) {
4237 $this->linethrough
= true;
4239 $this->linethrough
= false;
4242 if (strpos($tempstyle, 'O') !== false) {
4243 $this->overline
= true;
4245 $this->overline
= false;
4248 if (strpos($tempstyle, 'B') !== false) {
4252 if (strpos($tempstyle, 'I') !== false) {
4256 $fontkey = $family.$style;
4257 $font_style = $style.($this->underline ?
'U' : '').($this->linethrough ?
'D' : '').($this->overline ?
'O' : '');
4258 $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
4259 // check if the font has been already added
4260 $fb = $this->getFontBuffer($fontkey);
4261 if ($fb !== false) {
4262 if ($this->inxobj
) {
4263 // we are inside an XObject template
4264 $this->xobjects
[$this->xobjid
]['fonts'][$fontkey] = $fb['i'];
4268 // get specified font directory (if any)
4270 if (!TCPDF_STATIC
::empty_string($fontfile)) {
4271 $fontdir = dirname($fontfile);
4272 if (TCPDF_STATIC
::empty_string($fontdir) OR ($fontdir == '.')) {
4278 // true when the font style variation is missing
4279 $missing_style = false;
4280 // search and include font file
4281 if (TCPDF_STATIC
::empty_string($fontfile) OR (!@file_exists
($fontfile))) {
4282 // build a standard filenames for specified font
4283 $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
4284 $fontfile = TCPDF_FONTS
::getFontFullPath($tmp_fontfile, $fontdir);
4285 if (TCPDF_STATIC
::empty_string($fontfile)) {
4286 $missing_style = true;
4287 // try to remove the style part
4288 $tmp_fontfile = str_replace(' ', '', $family).'.php';
4289 $fontfile = TCPDF_FONTS
::getFontFullPath($tmp_fontfile, $fontdir);
4292 // include font file
4293 if (!TCPDF_STATIC
::empty_string($fontfile) AND (@file_exists
($fontfile))) {
4296 $this->Error('Could not include font definition file: '.$family.'');
4298 // check font parameters
4299 if ((!isset($type)) OR (!isset($cw))) {
4300 $this->Error('The font definition file has a bad format: '.$fontfile.'');
4302 // SET default parameters
4303 if (!isset($file) OR TCPDF_STATIC
::empty_string($file)) {
4306 if (!isset($enc) OR TCPDF_STATIC
::empty_string($enc)) {
4309 if (!isset($cidinfo) OR TCPDF_STATIC
::empty_string($cidinfo)) {
4310 $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0);
4311 $cidinfo['uni2cid'] = array();
4313 if (!isset($ctg) OR TCPDF_STATIC
::empty_string($ctg)) {
4316 if (!isset($desc) OR TCPDF_STATIC
::empty_string($desc)) {
4319 if (!isset($up) OR TCPDF_STATIC
::empty_string($up)) {
4322 if (!isset($ut) OR TCPDF_STATIC
::empty_string($ut)) {
4325 if (!isset($cw) OR TCPDF_STATIC
::empty_string($cw)) {
4328 if (!isset($dw) OR TCPDF_STATIC
::empty_string($dw)) {
4329 // set default width
4330 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
4331 $dw = $desc['MissingWidth'];
4332 } elseif (isset($cw[32])) {
4339 if ($type == 'core') {
4340 $name = $this->CoreFonts
[$fontkey];
4342 } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
4344 } elseif ($type == 'TrueTypeUnicode') {
4345 $enc = 'Identity-H';
4346 } elseif ($type == 'cidfont0') {
4347 if ($this->pdfa_mode
) {
4348 $this->Error('All fonts must be embedded in PDF/A mode!');
4351 $this->Error('Unknow font type: '.$type.'');
4353 // set name if unset
4354 if (!isset($name) OR empty($name)) {
4357 // create artificial font style variations if missing (only works with non-embedded fonts)
4358 if (($type != 'core') AND $missing_style) {
4360 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
4361 $name .= $styles[$bistyle];
4363 if (strpos($bistyle, 'B') !== false) {
4364 if (isset($desc['StemV'])) {
4365 // from normal to bold
4366 $desc['StemV'] = round($desc['StemV'] * 1.75);
4369 $desc['StemV'] = 123;
4372 // artificial italic
4373 if (strpos($bistyle, 'I') !== false) {
4374 if (isset($desc['ItalicAngle'])) {
4375 $desc['ItalicAngle'] -= 11;
4377 $desc['ItalicAngle'] = -11;
4379 if (isset($desc['Flags'])) {
4380 $desc['Flags'] |
= 64; //bit 7
4382 $desc['Flags'] = 64;
4386 // check if the array of characters bounding boxes is defined
4387 if (!isset($cbbox)) {
4390 // initialize subsetchars
4391 $subsetchars = array_fill(0, 255, true);
4392 $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));
4393 if ($this->inxobj
) {
4394 // we are inside an XObject template
4395 $this->xobjects
[$this->xobjid
]['fonts'][$fontkey] = $this->numfonts
;
4397 if (isset($diff) AND (!empty($diff))) {
4398 //Search existing encodings
4400 $nb = count($this->diffs
);
4401 for ($i=1; $i <= $nb; ++
$i) {
4402 if ($this->diffs
[$i] == $diff) {
4409 $this->diffs
[$d] = $diff;
4411 $this->setFontSubBuffer($fontkey, 'diff', $d);
4413 if (!TCPDF_STATIC
::empty_string($file)) {
4414 if (!isset($this->FontFiles
[$file])) {
4415 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
4416 $this->FontFiles
[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4417 } elseif ($type != 'core') {
4418 $this->FontFiles
[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4421 // update fontkeys that are sharing this font file
4422 $this->FontFiles
[$file]['subset'] = ($this->FontFiles
[$file]['subset'] AND $subset);
4423 if (!in_array($fontkey, $this->FontFiles
[$file]['fontkeys'])) {
4424 $this->FontFiles
[$file]['fontkeys'][] = $fontkey;
4432 * Sets the font used to print character strings.
4433 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
4434 * The method can be called before the first page is created and the font is retained from page to page.
4435 * If you just wish to change the current font size, it is simpler to call SetFontSize().
4436 * 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 />
4437 * @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.
4438 * @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.
4439 * @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
4440 * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4441 * @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.
4442 * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
4443 * @author Nicola Asuni
4446 * @see AddFont(), SetFontSize()
4448 public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) {
4449 //Select a font; size given in points
4450 if ($size === null) {
4451 $size = $this->FontSizePt
;
4456 // try to add font (if not already added)
4457 $fontdata = $this->AddFont($family, $style, $fontfile, $subset);
4458 $this->FontFamily
= $fontdata['family'];
4459 $this->FontStyle
= $fontdata['style'];
4460 if (isset($this->CurrentFont
['fontkey']) AND isset($this->CurrentFont
['subsetchars'])) {
4461 // save subset chars of the previous font
4462 $this->setFontSubBuffer($this->CurrentFont
['fontkey'], 'subsetchars', $this->CurrentFont
['subsetchars']);
4464 $this->CurrentFont
= $this->getFontBuffer($fontdata['fontkey']);
4465 $this->SetFontSize($size, $out);
4469 * Defines the size of the current font.
4470 * @param $size (float) The font size in points.
4471 * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
4476 public function SetFontSize($size, $out=true) {
4477 // font size in points
4478 $this->FontSizePt
= $size;
4479 // font size in user units
4480 $this->FontSize
= $size / $this->k
;
4481 // calculate some font metrics
4482 if (isset($this->CurrentFont
['desc']['FontBBox'])) {
4483 $bbox = explode(' ', substr($this->CurrentFont
['desc']['FontBBox'], 1, -1));
4484 $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
4486 $font_height = $size * 1.219;
4488 if (isset($this->CurrentFont
['desc']['Ascent']) AND ($this->CurrentFont
['desc']['Ascent'] > 0)) {
4489 $font_ascent = ($this->CurrentFont
['desc']['Ascent'] * $size / 1000);
4491 if (isset($this->CurrentFont
['desc']['Descent']) AND ($this->CurrentFont
['desc']['Descent'] <= 0)) {
4492 $font_descent = (- $this->CurrentFont
['desc']['Descent'] * $size / 1000);
4494 if (!isset($font_ascent) AND !isset($font_descent)) {
4496 $font_ascent = 0.76 * $font_height;
4497 $font_descent = $font_height - $font_ascent;
4498 } elseif (!isset($font_descent)) {
4499 $font_descent = $font_height - $font_ascent;
4500 } elseif (!isset($font_ascent)) {
4501 $font_ascent = $font_height - $font_descent;
4503 $this->FontAscent
= ($font_ascent / $this->k
);
4504 $this->FontDescent
= ($font_descent / $this->k
);
4505 if ($out AND ($this->page
> 0) AND (isset($this->CurrentFont
['i'])) AND ($this->state
== 2)) {
4506 $this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont
['i'], $this->FontSizePt
));
4511 * Returns the bounding box of the current font in user units.
4514 * @since 5.9.152 (2012-03-23)
4516 public function getFontBBox() {
4518 if (isset($this->CurrentFont
['desc']['FontBBox'])) {
4519 $tmpbbox = explode(' ', substr($this->CurrentFont
['desc']['FontBBox'], 1, -1));
4520 $fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox);
4523 if (isset($this->CurrentFont
['desc']['MaxWidth'])) {
4524 $maxw = $this->getAbsFontMeasure(intval($this->CurrentFont
['desc']['MaxWidth']));
4527 if (isset($this->CurrentFont
['desc']['MissingWidth'])) {
4528 $maxw = max($maxw, $this->CurrentFont
['desc']['MissingWidth']);
4530 if (isset($this->CurrentFont
['desc']['AvgWidth'])) {
4531 $maxw = max($maxw, $this->CurrentFont
['desc']['AvgWidth']);
4533 if (isset($this->CurrentFont
['dw'])) {
4534 $maxw = max($maxw, $this->CurrentFont
['dw']);
4536 foreach ($this->CurrentFont
['cw'] as $char => $w) {
4537 $maxw = max($maxw, $w);
4542 $maxw = $this->getAbsFontMeasure($maxw);
4544 $fbbox = array(0, (0 - $this->FontDescent
), $maxw, $this->FontAscent
);
4550 * Convert a relative font measure into absolute value.
4551 * @param $s (int) Font measure.
4552 * @return float Absolute measure.
4553 * @since 5.9.186 (2012-09-13)
4555 public function getAbsFontMeasure($s) {
4556 return ($s * $this->FontSize
/ 1000);
4560 * Returns the glyph bounding box of the specified character in the current font in user units.
4561 * @param $char (int) Input character code.
4562 * @return mixed array(xMin, yMin, xMax, yMax) or FALSE if not defined.
4563 * @since 5.9.186 (2012-09-13)
4565 public function getCharBBox($char) {
4566 if (isset($this->CurrentFont
['cbbox'][$char])) {
4567 return array_map(array($this,'getAbsFontMeasure'), $this->CurrentFont
['cbbox'][intval($char)]);
4573 * Return the font descent value
4574 * @param $font (string) font name
4575 * @param $style (string) font style
4576 * @param $size (float) The size (in points)
4577 * @return int font descent
4579 * @author Nicola Asuni
4580 * @since 4.9.003 (2010-03-30)
4582 public function getFontDescent($font, $style='', $size=0) {
4583 $fontdata = $this->AddFont($font, $style);
4584 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4585 if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
4586 $descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
4588 $descent = (1.219 * 0.24 * $size);
4590 return ($descent / $this->k
);
4594 * Return the font ascent value.
4595 * @param $font (string) font name
4596 * @param $style (string) font style
4597 * @param $size (float) The size (in points)
4598 * @return int font ascent
4600 * @author Nicola Asuni
4601 * @since 4.9.003 (2010-03-30)
4603 public function getFontAscent($font, $style='', $size=0) {
4604 $fontdata = $this->AddFont($font, $style);
4605 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4606 if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
4607 $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
4609 $ascent = 1.219 * 0.76 * $size;
4611 return ($ascent / $this->k
);
4615 * Return true in the character is present in the specified font.
4616 * @param $char (mixed) Character to check (integer value or string)
4617 * @param $font (string) Font name (family name).
4618 * @param $style (string) Font style.
4619 * @return (boolean) true if the char is defined, false otherwise.
4621 * @since 5.9.153 (2012-03-28)
4623 public function isCharDefined($char, $font='', $style='') {
4624 if (is_string($char)) {
4625 // get character code
4626 $char = TCPDF_FONTS
::UTF8StringToArray($char, $this->isunicode
, $this->CurrentFont
);
4629 if (TCPDF_STATIC
::empty_string($font)) {
4630 if (TCPDF_STATIC
::empty_string($style)) {
4631 return (isset($this->CurrentFont
['cw'][intval($char)]));
4633 $font = $this->FontFamily
;
4635 $fontdata = $this->AddFont($font, $style);
4636 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4637 return (isset($fontinfo['cw'][intval($char)]));
4641 * Replace missing font characters on selected font with specified substitutions.
4642 * @param $text (string) Text to process.
4643 * @param $font (string) Font name (family name).
4644 * @param $style (string) Font style.
4645 * @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.
4646 * @return (string) Processed text.
4648 * @since 5.9.153 (2012-03-28)
4650 public function replaceMissingChars($text, $font='', $style='', $subs=array()) {
4654 if (TCPDF_STATIC
::empty_string($font)) {
4655 $font = $this->FontFamily
;
4657 $fontdata = $this->AddFont($font, $style);
4658 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4659 $uniarr = TCPDF_FONTS
::UTF8StringToArray($text, $this->isunicode
, $this->CurrentFont
);
4660 foreach ($uniarr as $k => $chr) {
4661 if (!isset($fontinfo['cw'][$chr])) {
4662 // this character is missing on the selected font
4663 if (isset($subs[$chr])) {
4664 // we have available substitutions
4665 if (is_array($subs[$chr])) {
4666 foreach($subs[$chr] as $s) {
4667 if (isset($fontinfo['cw'][$s])) {
4672 } elseif (isset($fontinfo['cw'][$subs[$chr]])) {
4673 $uniarr[$k] = $subs[$chr];
4678 return TCPDF_FONTS
::UniArrSubString(TCPDF_FONTS
::UTF8ArrayToUniArray($uniarr, $this->isunicode
));
4682 * Defines the default monospaced font.
4683 * @param $font (string) Font name.
4687 public function SetDefaultMonospacedFont($font) {
4688 $this->default_monospaced_font
= $font;
4692 * 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 />
4693 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
4696 * @see Cell(), Write(), Image(), Link(), SetLink()
4698 public function AddLink() {
4699 // create a new internal link
4700 $n = count($this->links
) +
1;
4701 $this->links
[$n] = array('p' => 0, 'y' => 0, 'f' => false);
4706 * Defines the page and position a link points to.
4707 * @param $link (int) The link identifier returned by AddLink()
4708 * @param $y (float) Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
4709 * @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.
4714 public function SetLink($link, $y=0, $page=-1) {
4716 if (!empty($page) AND ($page[0] == '*')) {
4717 $page = intval(substr($page, 1));
4718 // this page number will not be changed when moving/add/deleting pages
4722 $page = $this->page
;
4727 $this->links
[$link] = array('p' => $page, 'y' => $y, 'f' => $fixed);
4731 * Puts a link on a rectangular area of the page.
4732 * 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.
4733 * @param $x (float) Abscissa of the upper-left corner of the rectangle
4734 * @param $y (float) Ordinate of the upper-left corner of the rectangle
4735 * @param $w (float) Width of the rectangle
4736 * @param $h (float) Height of the rectangle
4737 * @param $link (mixed) URL or identifier returned by AddLink()
4738 * @param $spaces (int) number of spaces on the text to link
4741 * @see AddLink(), Annotation(), Cell(), Write(), Image()
4743 public function Link($x, $y, $w, $h, $link, $spaces=0) {
4744 $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
4748 * Puts a markup annotation on a rectangular area of the page.
4749 * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!
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 $text (string) annotation text or alternate content
4755 * @param $opt (array) array of options (see section 8.4 of PDF reference 1.7).
4756 * @param $spaces (int) number of spaces on the text to link
4758 * @since 4.0.018 (2008-08-06)
4760 public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
4761 if ($this->inxobj
) {
4762 // store parameters for later use on template
4763 $this->xobjects
[$this->xobjid
]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
4772 // check page for no-write regions and adapt page margins if necessary
4773 list($x, $y) = $this->checkPageRegions($h, $x, $y);
4774 // recalculate coordinates to account for graphic transformations
4775 if (isset($this->transfmatrix
) AND !empty($this->transfmatrix
)) {
4776 for ($i=$this->transfmatrix_key
; $i > 0; --$i) {
4777 $maxid = count($this->transfmatrix
[$i]) - 1;
4778 for ($j=$maxid; $j >= 0; --$j) {
4779 $ctm = $this->transfmatrix
[$i][$j];
4780 if (isset($ctm['a'])) {
4782 $y = ($this->h
- $y) * $this->k
;
4788 $x1 = ($ctm['a'] * $xt) +
($ctm['c'] * $yt) +
$ctm['e'];
4789 $y1 = ($ctm['b'] * $xt) +
($ctm['d'] * $yt) +
$ctm['f'];
4793 $x2 = ($ctm['a'] * $xt) +
($ctm['c'] * $yt) +
$ctm['e'];
4794 $y2 = ($ctm['b'] * $xt) +
($ctm['d'] * $yt) +
$ctm['f'];
4798 $x3 = ($ctm['a'] * $xt) +
($ctm['c'] * $yt) +
$ctm['e'];
4799 $y3 = ($ctm['b'] * $xt) +
($ctm['d'] * $yt) +
$ctm['f'];
4803 $x4 = ($ctm['a'] * $xt) +
($ctm['c'] * $yt) +
$ctm['e'];
4804 $y4 = ($ctm['b'] * $xt) +
($ctm['d'] * $yt) +
$ctm['f'];
4805 // new coordinates (rectangle area)
4806 $x = min($x1, $x2, $x3, $x4);
4807 $y = max($y1, $y2, $y3, $y4);
4808 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k
;
4809 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k
;
4811 $y = $this->h
- ($y / $this->k
);
4816 if ($this->page
<= 0) {
4819 $page = $this->page
;
4821 if (!isset($this->PageAnnots
[$page])) {
4822 $this->PageAnnots
[$page] = array();
4824 $this->PageAnnots
[$page][] = array('n' => ++
$this->n
, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
4825 if (!$this->pdfa_mode
) {
4826 if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!TCPDF_STATIC
::empty_string($opt['FS']))
4827 AND (@file_exists
($opt['FS']) OR TCPDF_STATIC
::isValidURL($opt['FS']))
4828 AND (!isset($this->embeddedfiles
[basename($opt['FS'])]))) {
4829 $this->embeddedfiles
[basename($opt['FS'])] = array('f' => ++
$this->n
, 'n' => ++
$this->n
, 'file' => $opt['FS']);
4832 // Add widgets annotation's icons
4833 if (isset($opt['mk']['i']) AND @file_exists
($opt['mk']['i'])) {
4834 $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
4836 if (isset($opt['mk']['ri']) AND @file_exists
($opt['mk']['ri'])) {
4837 $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4839 if (isset($opt['mk']['ix']) AND @file_exists
($opt['mk']['ix'])) {
4840 $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4845 * Embedd the attached files.
4846 * @since 4.4.000 (2008-12-07)
4850 protected function _putEmbeddedFiles() {
4851 if ($this->pdfa_mode
) {
4852 // embedded files are not allowed in PDF/A mode
4855 reset($this->embeddedfiles
);
4856 foreach ($this->embeddedfiles
as $filename => $filedata) {
4857 $data = TCPDF_STATIC
::fileGetContents($filedata['file']);
4858 if ($data !== FALSE) {
4859 $rawsize = strlen($data);
4862 $this->efnames
[$filename] = $filedata['f'].' 0 R';
4863 // embedded file specification object
4864 $out = $this->_getobj($filedata['f'])."\n";
4865 $out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']).' /EF <</F '.$filedata['n'].' 0 R>> >>';
4866 $out .= "\n".'endobj';
4868 // embedded file object
4870 if ($this->compress
) {
4871 $data = gzcompress($data);
4872 $filter = ' /Filter /FlateDecode';
4874 $stream = $this->_getrawstream($data, $filedata['n']);
4875 $out = $this->_getobj($filedata['n'])."\n";
4876 $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>';
4877 $out .= ' stream'."\n".$stream."\n".'endstream';
4878 $out .= "\n".'endobj';
4886 * Prints a text cell at the specified position.
4887 * This method allows to place a string precisely on the page.
4888 * @param $x (float) Abscissa of the cell origin
4889 * @param $y (float) Ordinate of the cell origin
4890 * @param $txt (string) String to print
4891 * @param $fstroke (int) outline size in user units (false = disable)
4892 * @param $fclip (boolean) if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
4893 * @param $ffill (boolean) if true fills the text
4894 * @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)))
4895 * @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.
4896 * @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>
4897 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
4898 * @param $link (mixed) URL or identifier returned by AddLink().
4899 * @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.
4900 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
4901 * @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>
4902 * @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>
4903 * @param $rtloff (boolean) if true uses the page top-left corner as origin of axis for $x and $y initial position.
4906 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()
4908 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) {
4909 $textrendermode = $this->textrendermode
;
4910 $textstrokewidth = $this->textstrokewidth
;
4911 $this->setTextRenderingMode($fstroke, $ffill, $fclip);
4912 $this->SetXY($x, $y, $rtloff);
4913 $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
4914 // restore previous rendering mode
4915 $this->textrendermode
= $textrendermode;
4916 $this->textstrokewidth
= $textstrokewidth;
4920 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
4921 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
4922 * This method is called automatically and should not be called directly by the application.
4926 * @see SetAutoPageBreak()
4928 public function AcceptPageBreak() {
4929 if ($this->num_columns
> 1) {
4930 // multi column mode
4931 if ($this->current_column
< ($this->num_columns
- 1)) {
4932 // go to next column
4933 $this->selectColumn($this->current_column +
1);
4934 } elseif ($this->AutoPageBreak
) {
4938 $this->selectColumn(0);
4940 // avoid page breaking from checkPageBreak()
4943 return $this->AutoPageBreak
;
4947 * Add page if needed.
4948 * @param $h (float) Cell height. Default value: 0.
4949 * @param $y (mixed) starting y position, leave empty for current position.
4950 * @param $addpage (boolean) if true add a page, otherwise only return the true/false state
4951 * @return boolean true in case of page break, false otherwise.
4952 * @since 3.2.000 (2008-07-01)
4955 protected function checkPageBreak($h=0, $y='', $addpage=true) {
4956 if (TCPDF_STATIC
::empty_string($y)) {
4959 $current_page = $this->page
;
4960 if ((($y +
$h) > $this->PageBreakTrigger
) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) {
4962 //Automatic page break
4964 $this->AddPage($this->CurOrientation
);
4965 $this->y
= $this->tMargin
;
4966 $oldpage = $this->page
- 1;
4968 if ($this->pagedim
[$this->page
]['orm'] != $this->pagedim
[$oldpage]['orm']) {
4969 $this->x
= $x - ($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$oldpage]['orm']);
4974 if ($this->pagedim
[$this->page
]['olm'] != $this->pagedim
[$oldpage]['olm']) {
4975 $this->x
= $x +
($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$oldpage]['olm']);
4983 if ($current_page != $this->page
) {
4984 // account for columns mode
4991 * 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 />
4992 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
4993 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
4994 * @param $h (float) Cell height. Default value: 0.
4995 * @param $txt (string) String to print. Default value: empty string.
4996 * @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)))
4997 * @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.
4998 * @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>
4999 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5000 * @param $link (mixed) URL or identifier returned by AddLink().
5001 * @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.
5002 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5003 * @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>
5004 * @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>
5007 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
5009 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') {
5010 $prev_cell_margin = $this->cell_margin
;
5011 $prev_cell_padding = $this->cell_padding
;
5012 $this->adjustCellPadding($border);
5013 if (!$ignore_min_height) {
5014 $min_cell_height = $this->getCellHeight($this->FontSize
);
5015 if ($h < $min_cell_height) {
5016 $h = $min_cell_height;
5019 $this->checkPageBreak($h +
$this->cell_margin
['T'] +
$this->cell_margin
['B']);
5020 // apply text shadow if enabled
5021 if ($this->txtshadow
['enabled']) {
5025 $bc = $this->bgcolor
;
5026 $fc = $this->fgcolor
;
5027 $sc = $this->strokecolor
;
5028 $alpha = $this->alpha
;
5030 $this->x +
= $this->txtshadow
['depth_w'];
5031 $this->y +
= $this->txtshadow
['depth_h'];
5032 $this->SetFillColorArray($this->txtshadow
['color']);
5033 $this->SetTextColorArray($this->txtshadow
['color']);
5034 $this->SetDrawColorArray($this->txtshadow
['color']);
5035 if ($this->txtshadow
['opacity'] != $alpha['CA']) {
5036 $this->setAlpha($this->txtshadow
['opacity'], $this->txtshadow
['blend_mode']);
5038 if ($this->state
== 2) {
5039 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5044 $this->SetFillColorArray($bc);
5045 $this->SetTextColorArray($fc);
5046 $this->SetDrawColorArray($sc);
5047 if ($this->txtshadow
['opacity'] != $alpha['CA']) {
5048 $this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']);
5051 if ($this->state
== 2) {
5052 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5054 $this->cell_padding
= $prev_cell_padding;
5055 $this->cell_margin
= $prev_cell_margin;
5059 * 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 />
5060 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
5061 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
5062 * @param $h (float) Cell height. Default value: 0.
5063 * @param $txt (string) String to print. Default value: empty string.
5064 * @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)))
5065 * @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.
5066 * @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>
5067 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5068 * @param $link (mixed) URL or identifier returned by AddLink().
5069 * @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.
5070 * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5071 * @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>
5072 * @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>
5073 * @return string containing cell code
5078 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') {
5079 // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space
5080 $txt = str_replace(TCPDF_FONTS
::unichr(160, $this->isunicode
), ' ', $txt);
5081 $prev_cell_margin = $this->cell_margin
;
5082 $prev_cell_padding = $this->cell_padding
;
5083 $txt = TCPDF_STATIC
::removeSHY($txt, $this->isunicode
);
5084 $rs = ''; //string to be returned
5085 $this->adjustCellPadding($border);
5086 if (!$ignore_min_height) {
5087 $min_cell_height = $this->getCellHeight($this->FontSize
);
5088 if ($h < $min_cell_height) {
5089 $h = $min_cell_height;
5093 // check page for no-write regions and adapt page margins if necessary
5094 list($this->x
, $this->y
) = $this->checkPageRegions($h, $this->x
, $this->y
);
5096 $x = $this->x
- $this->cell_margin
['R'];
5098 $x = $this->x +
$this->cell_margin
['L'];
5100 $y = $this->y +
$this->cell_margin
['T'];
5101 $prev_font_stretching = $this->font_stretching
;
5102 $prev_font_spacing = $this->font_spacing
;
5103 // cell vertical alignment
5110 $y -= $this->cell_padding
['T'];
5115 $y -= ($h - $this->cell_padding
['B'] - $this->FontAscent
- $this->FontDescent
);
5122 $y -= (($h - $this->FontAscent
- $this->FontDescent
) / 2);
5133 $y -= ($this->cell_padding
['T'] +
$this->FontAscent
);
5138 $y -= ($h - $this->cell_padding
['B'] - $this->FontDescent
);
5145 $y -= (($h +
$this->FontAscent
- $this->FontDescent
) / 2);
5156 $y -= ($this->cell_padding
['T'] +
$this->FontAscent +
$this->FontDescent
);
5161 $y -= ($h - $this->cell_padding
['B']);
5168 $y -= (($h +
$this->FontAscent +
$this->FontDescent
) / 2);
5191 // text vertical alignment
5195 $yt = $y +
$this->cell_padding
['T'];
5200 $yt = $y +
$h - $this->cell_padding
['B'] - $this->FontAscent
- $this->FontDescent
;
5207 $yt = $y +
(($h - $this->FontAscent
- $this->FontDescent
) / 2);
5211 $basefonty = $yt +
$this->FontAscent
;
5212 if (TCPDF_STATIC
::empty_string($w) OR ($w <= 0)) {
5214 $w = $x - $this->lMargin
;
5216 $w = $this->w
- $this->rMargin
- $x;
5221 if (is_string($border) AND (strlen($border) == 4)) {
5225 if ($fill OR ($border == 1)) {
5227 $op = ($border == 1) ?
'B' : 'f';
5232 $xk = (($x - $w) * $k);
5236 $s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h
- $y) * $k), ($w * $k), (-$h * $k), $op);
5239 $s .= $this->getCellBorder($x, $y, $w, $h, $border);
5242 if ($this->isunicode
) {
5243 if (($this->CurrentFont
['type'] == 'core') OR ($this->CurrentFont
['type'] == 'TrueType') OR ($this->CurrentFont
['type'] == 'Type1')) {
5244 $txt2 = TCPDF_FONTS
::UTF8ToLatin1($txt2, $this->isunicode
, $this->CurrentFont
);
5246 $unicode = TCPDF_FONTS
::UTF8StringToArray($txt, $this->isunicode
, $this->CurrentFont
); // array of UTF-8 unicode values
5247 $unicode = TCPDF_FONTS
::utf8Bidi($unicode, '', $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
5248 // replace thai chars (if any)
5249 if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS
== true)) {
5251 $numchars = count($unicode);
5252 // po pla, for far, for fan
5253 $longtail = array(0x0e1b, 0x0e1d, 0x0e1f);
5254 // do chada, to patak
5255 $lowtail = array(0x0e0e, 0x0e0f);
5256 // mai hun arkad, sara i, sara ii, sara ue, sara uee
5257 $upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37);
5258 // mai ek, mai tho, mai tri, mai chattawa, karan
5259 $tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c);
5260 // sara u, sara uu, pinthu
5261 $lowvowel = array(0x0e38, 0x0e39, 0x0e3a);
5263 for ($i = 0; $i < $numchars; $i++
) {
5264 if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) {
5265 $ch0 = $unicode[$i];
5266 $ch1 = ($i > 0) ?
$unicode[($i - 1)] : 0;
5267 $ch2 = ($i > 1) ?
$unicode[($i - 2)] : 0;
5268 $chn = ($i < ($numchars - 1)) ?
$unicode[($i +
1)] : 0;
5269 if (in_array($ch0, $tonemark)) {
5270 if ($chn == 0x0e33) {
5272 if (in_array($ch1, $longtail)) {
5273 // tonemark at upper left
5274 $output[] = $this->replaceChar($ch0, (0xf713 +
$ch0 - 0x0e48));
5276 // tonemark at upper right (normal position)
5279 } elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) {
5280 // tonemark at lower left
5281 $output[] = $this->replaceChar($ch0, (0xf705 +
$ch0 - 0x0e48));
5282 } elseif (in_array($ch1, $upvowel)) {
5283 if (in_array($ch2, $longtail)) {
5284 // tonemark at upper left
5285 $output[] = $this->replaceChar($ch0, (0xf713 +
$ch0 - 0x0e48));
5287 // tonemark at upper right (normal position)
5291 // tonemark at lower right
5292 $output[] = $this->replaceChar($ch0, (0xf70a +
$ch0 - 0x0e48));
5294 } elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) {
5295 // add lower left nikhahit and sara aa
5296 if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) {
5298 $this->CurrentFont
['subsetchars'][0xf711] = true;
5300 $this->CurrentFont
['subsetchars'][0x0e32] = true;
5304 } elseif (in_array($ch1, $longtail)) {
5305 if ($ch0 == 0x0e31) {
5306 // lower left mai hun arkad
5307 $output[] = $this->replaceChar($ch0, 0xf710);
5308 } elseif (in_array($ch0, $upvowel)) {
5310 $output[] = $this->replaceChar($ch0, (0xf701 +
$ch0 - 0x0e34));
5311 } elseif ($ch0 == 0x0e47) {
5312 // lower left mai tai koo
5313 $output[] = $this->replaceChar($ch0, 0xf712);
5318 } elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) {
5320 $output[] = $this->replaceChar($ch0, (0xf718 +
$ch0 - 0x0e38));
5321 } elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) {
5322 // yo ying without lower part
5323 $output[] = $this->replaceChar($ch0, 0xf70f);
5324 } elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) {
5325 // tho santan without lower part
5326 $output[] = $this->replaceChar($ch0, 0xf700);
5331 // non-thai character
5332 $output[] = $unicode[$i];
5336 // update font subsetchars
5337 $this->setFontSubBuffer($this->CurrentFont
['fontkey'], 'subsetchars', $this->CurrentFont
['subsetchars']);
5338 } // end of K_THAI_TOPCHARS
5339 $txt2 = TCPDF_FONTS
::arrUTF8ToUTF16BE($unicode, false);
5342 $txt2 = TCPDF_STATIC
::_escape($txt2);
5343 // get current text width (considering general font stretching and spacing)
5344 $txwidth = $this->GetStringWidth($txt);
5346 // check for stretch mode
5348 // calculate ratio between cell width and text width
5352 $ratio = (($w - $this->cell_padding
['L'] - $this->cell_padding
['R']) / $width);
5354 // check if stretching is required
5355 if (($ratio < 1) OR (($ratio > 1) AND (($stretch %
2) == 0))) {
5356 // the text will be stretched to fit cell width
5358 // set new character spacing
5359 $this->font_spacing +
= ($w - $this->cell_padding
['L'] - $this->cell_padding
['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching
/ 100));
5361 // set new horizontal stretching
5362 $this->font_stretching
*= $ratio;
5364 // recalculate text width (the text fills the entire cell)
5365 $width = $w - $this->cell_padding
['L'] - $this->cell_padding
['R'];
5370 if ($this->font_stretching
!= 100) {
5371 // apply font stretching
5372 $rs .= sprintf('BT %F Tz ET ', $this->font_stretching
);
5374 if ($this->font_spacing
!= 0) {
5375 // increase/decrease font spacing
5376 $rs .= sprintf('BT %F Tc ET ', ($this->font_spacing
* $this->k
));
5378 if ($this->ColorFlag
AND ($this->textrendermode
< 4)) {
5379 $s .= 'q '.$this->TextColor
.' ';
5382 $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode
, ($this->textstrokewidth
* $this->k
));
5383 // count number of spaces
5384 $ns = substr_count($txt, chr(32));
5387 if (($align == 'J') AND ($ns > 0)) {
5388 if ($this->isUnicodeFont()) {
5389 // get string width without spaces
5390 $width = $this->GetStringWidth(str_replace(' ', '', $txt));
5391 // calculate average space width
5392 $spacewidth = -1000 * ($w - $width - $this->cell_padding
['L'] - $this->cell_padding
['R']) / ($ns?
$ns:1) / ($this->FontSize?
$this->FontSize
:1);
5393 if ($this->font_stretching
!= 100) {
5394 // word spacing is affected by stretching
5395 $spacewidth /= ($this->font_stretching
/ 100);
5397 // set word position to be used with TJ operator
5398 $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2);
5399 $unicode_justification = true;
5404 $spacewidth = (($w - $width - $this->cell_padding
['L'] - $this->cell_padding
['R']) / ($ns?
$ns:1)) * $this->k
;
5405 if ($this->font_stretching
!= 100) {
5406 // word spacing (Tw) is affected by stretching
5407 $spacewidth /= ($this->font_stretching
/ 100);
5410 $rs .= sprintf('BT %F Tw ET ', $spacewidth);
5412 $width = $w - $this->cell_padding
['L'] - $this->cell_padding
['R'];
5414 // replace carriage return characters
5415 $txt2 = str_replace("\r", ' ', $txt2);
5418 $dx = ($w - $width) / 2;
5423 $dx = $this->cell_padding
['R'];
5425 $dx = $w - $width - $this->cell_padding
['R'];
5431 $dx = $w - $width - $this->cell_padding
['L'];
5433 $dx = $this->cell_padding
['L'];
5440 $dx = $this->cell_padding
['R'];
5442 $dx = $this->cell_padding
['L'];
5448 $xdx = $x - $dx - $width;
5454 $s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h
- $basefonty) * $k), $txt2);
5455 if (isset($uniblock)) {
5456 // print overlapping characters as separate string
5457 $xshift = 0; // horizontal shift
5458 $ty = (($this->h
- $basefonty +
(0.2 * $this->FontSize
)) * $k);
5459 $spw = (($w - $txwidth - $this->cell_padding
['L'] - $this->cell_padding
['R']) / ($ns?
$ns:1));
5460 foreach ($uniblock as $uk => $uniarr) {
5461 if (($uk %
2) == 0) {
5463 if ($spacewidth != 0) {
5464 // justification shift
5465 $xshift +
= (count(array_keys($uniarr, 32)) * $spw);
5467 $xshift +
= $this->GetArrStringWidth($uniarr); // + shift justification
5469 // character to print
5470 $topchr = TCPDF_FONTS
::arrUTF8ToUTF16BE($uniarr, false);
5471 $topchr = TCPDF_STATIC
::_escape($topchr);
5472 $s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk +
($xshift * $k)), $ty, $topchr);
5476 if ($this->underline
) {
5477 $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
5479 if ($this->linethrough
) {
5480 $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
5482 if ($this->overline
) {
5483 $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
5485 if ($this->ColorFlag
AND ($this->textrendermode
< 4)) {
5489 $this->Link($xdx, $yt, $width, ($this->FontAscent +
$this->FontDescent
), $link, $ns);
5496 if ($this->font_spacing
!= 0) {
5497 // reset font spacing mode
5498 $rs .= ' BT 0 Tc ET';
5500 if ($this->font_stretching
!= 100) {
5501 // reset font stretching mode
5502 $rs .= ' BT 100 Tz ET';
5505 // reset word spacing
5506 if (!$this->isUnicodeFont() AND ($align == 'J')) {
5507 $rs .= ' BT 0 Tw ET';
5509 // reset stretching and spacing
5510 $this->font_stretching
= $prev_font_stretching;
5511 $this->font_spacing
= $prev_font_spacing;
5514 //Go to the beginning of the next line
5515 $this->y
= $y +
$h +
$this->cell_margin
['B'];
5518 $this->x
= $this->w
- $this->rMargin
;
5520 $this->x
= $this->lMargin
;
5524 // go left or right by case
5526 $this->x
= $x - $w - $this->cell_margin
['L'];
5528 $this->x
= $x +
$w +
$this->cell_margin
['R'];
5531 $gstyles = ''.$this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
.' '.$this->FillColor
."\n";
5533 $this->cell_padding
= $prev_cell_padding;
5534 $this->cell_margin
= $prev_cell_margin;
5539 * Replace a char if is defined on the current font.
5540 * @param $oldchar (int) Integer code (unicode) of the character to replace.
5541 * @param $newchar (int) Integer code (unicode) of the new character.
5542 * @return int the replaced char or the old char in case the new char i not defined
5544 * @since 5.9.167 (2012-06-22)
5546 protected function replaceChar($oldchar, $newchar) {
5547 if ($this->isCharDefined($newchar)) {
5548 // add the new char on the subset list
5549 $this->CurrentFont
['subsetchars'][$newchar] = true;
5550 // return the new character
5553 // return the old char
5558 * Returns the code to draw the cell border
5559 * @param $x (float) X coordinate.
5560 * @param $y (float) Y coordinate.
5561 * @param $w (float) Cell width.
5562 * @param $h (float) Cell height.
5563 * @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)))
5564 * @return string containing cell border code
5566 * @see SetLineStyle()
5567 * @since 5.7.000 (2010-08-02)
5569 protected function getCellBorder($x, $y, $w, $h, $brd) {
5570 $s = ''; // string to be returned
5575 $brd = array('LRTB' => true);
5577 // calculate coordinates for border
5580 $xeL = ($x - $w) * $k;
5584 $xeR = ($x +
$w) * $k;
5586 $yeL = (($this->h
- ($y +
$h)) * $k);
5587 $yeT = (($this->h
- $y) * $k);
5592 if (is_string($brd)) {
5593 // convert string to array
5594 $slen = strlen($brd);
5596 for ($i = 0; $i < $slen; ++
$i) {
5597 $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
5601 if (isset($brd['mode'])) {
5602 $mode = $brd['mode'];
5603 unset($brd['mode']);
5607 foreach ($brd as $border => $style) {
5608 if (is_array($style) AND !empty($style)) {
5609 // apply border style
5610 $prev_style = $this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
.' ';
5611 $s .= $this->SetLineStyle($style, true)."\n";
5615 $off = (($this->LineWidth
/ 2) * $k);
5624 $w +
= $this->LineWidth
;
5625 $h +
= $this->LineWidth
;
5629 $off = ($this->LineWidth
/ 2) * $k;
5638 $w -= $this->LineWidth
;
5639 $h -= $this->LineWidth
;
5655 // draw borders by case
5656 if (strlen($border) == 4) {
5657 $s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k));
5658 } elseif (strlen($border) == 3) {
5659 if (strpos($border,'B') === false) { // LTR
5660 $s .= sprintf('%F %F m ', $xL, $yL);
5661 $s .= sprintf('%F %F l ', $xT, $yT);
5662 $s .= sprintf('%F %F l ', $xR, $yR);
5663 $s .= sprintf('%F %F l ', $xB, $yB);
5665 } elseif (strpos($border,'L') === false) { // TRB
5666 $s .= sprintf('%F %F m ', $xT, $yT);
5667 $s .= sprintf('%F %F l ', $xR, $yR);
5668 $s .= sprintf('%F %F l ', $xB, $yB);
5669 $s .= sprintf('%F %F l ', $xL, $yL);
5671 } elseif (strpos($border,'T') === false) { // RBL
5672 $s .= sprintf('%F %F m ', $xR, $yR);
5673 $s .= sprintf('%F %F l ', $xB, $yB);
5674 $s .= sprintf('%F %F l ', $xL, $yL);
5675 $s .= sprintf('%F %F l ', $xT, $yT);
5677 } elseif (strpos($border,'R') === false) { // BLT
5678 $s .= sprintf('%F %F m ', $xB, $yB);
5679 $s .= sprintf('%F %F l ', $xL, $yL);
5680 $s .= sprintf('%F %F l ', $xT, $yT);
5681 $s .= sprintf('%F %F l ', $xR, $yR);
5684 } elseif (strlen($border) == 2) {
5685 if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
5686 $s .= sprintf('%F %F m ', $xL, $yL);
5687 $s .= sprintf('%F %F l ', $xT, $yT);
5688 $s .= sprintf('%F %F l ', $xR, $yR);
5690 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
5691 $s .= sprintf('%F %F m ', $xT, $yT);
5692 $s .= sprintf('%F %F l ', $xR, $yR);
5693 $s .= sprintf('%F %F l ', $xB, $yB);
5695 } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
5696 $s .= sprintf('%F %F m ', $xR, $yR);
5697 $s .= sprintf('%F %F l ', $xB, $yB);
5698 $s .= sprintf('%F %F l ', $xL, $yL);
5700 } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
5701 $s .= sprintf('%F %F m ', $xB, $yB);
5702 $s .= sprintf('%F %F l ', $xL, $yL);
5703 $s .= sprintf('%F %F l ', $xT, $yT);
5705 } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
5706 $s .= sprintf('%F %F m ', $xL, $yL);
5707 $s .= sprintf('%F %F l ', $xT, $yT);
5709 $s .= sprintf('%F %F m ', $xR, $yR);
5710 $s .= sprintf('%F %F l ', $xB, $yB);
5712 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
5713 $s .= sprintf('%F %F m ', $xT, $yT);
5714 $s .= sprintf('%F %F l ', $xR, $yR);
5716 $s .= sprintf('%F %F m ', $xB, $yB);
5717 $s .= sprintf('%F %F l ', $xL, $yL);
5720 } else { // strlen($border) == 1
5721 if (strpos($border,'L') !== false) { // L
5722 $s .= sprintf('%F %F m ', $xL, $yL);
5723 $s .= sprintf('%F %F l ', $xT, $yT);
5725 } elseif (strpos($border,'T') !== false) { // T
5726 $s .= sprintf('%F %F m ', $xT, $yT);
5727 $s .= sprintf('%F %F l ', $xR, $yR);
5729 } elseif (strpos($border,'R') !== false) { // R
5730 $s .= sprintf('%F %F m ', $xR, $yR);
5731 $s .= sprintf('%F %F l ', $xB, $yB);
5733 } elseif (strpos($border,'B') !== false) { // B
5734 $s .= sprintf('%F %F m ', $xB, $yB);
5735 $s .= sprintf('%F %F l ', $xL, $yL);
5739 if (is_array($style) AND !empty($style)) {
5740 // reset border style to previous value
5741 $s .= "\n".$this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
."\n";
5748 * This method allows printing text with line breaks.
5749 * 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 />
5750 * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
5751 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
5752 * @param $h (float) Cell minimum height. The cell extends automatically if needed.
5753 * @param $txt (string) String to print
5754 * @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)))
5755 * @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>
5756 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5757 * @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>
5758 * @param $x (float) x position in user units
5759 * @param $y (float) y position in user units
5760 * @param $reseth (boolean) if true reset the last cell height (default true).
5761 * @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.
5762 * @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.
5763 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
5764 * @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.
5765 * @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.
5766 * @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.
5767 * @return int Return the number of cells or 1 for html mode.
5770 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
5772 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) {
5773 $prev_cell_margin = $this->cell_margin
;
5774 $prev_cell_padding = $this->cell_padding
;
5775 // adjust internal padding
5776 $this->adjustCellPadding($border);
5777 $mc_padding = $this->cell_padding
;
5778 $mc_margin = $this->cell_margin
;
5779 $this->cell_padding
['T'] = 0;
5780 $this->cell_padding
['B'] = 0;
5781 $this->setCellMargins(0, 0, 0, 0);
5782 if (TCPDF_STATIC
::empty_string($this->lasth
) OR $reseth) {
5784 $this->resetLastH();
5786 if (!TCPDF_STATIC
::empty_string($y)) {
5792 if (($h > 0) AND $this->inPageBody() AND (($y +
$h +
$mc_margin['T'] +
$mc_margin['B']) > $this->PageBreakTrigger
)) {
5793 // spit cell in more pages/columns
5794 $newh = ($this->PageBreakTrigger
- $y);
5795 $resth = ($h - $newh); // cell to be printed on the next page/column
5798 // get current page number
5799 $startpage = $this->page
;
5800 // get current column
5801 $startcolumn = $this->current_column
;
5802 if (!TCPDF_STATIC
::empty_string($x)) {
5807 // check page for no-write regions and adapt page margins if necessary
5808 list($x, $y) = $this->checkPageRegions(0, $x, $y);
5810 $oy = $y +
$mc_margin['T'];
5812 $ox = ($this->w
- $x - $mc_margin['R']);
5814 $ox = ($x +
$mc_margin['L']);
5819 if (TCPDF_STATIC
::empty_string($w) OR ($w <= 0)) {
5821 $w = ($this->x
- $this->lMargin
- $mc_margin['L']);
5823 $w = ($this->w
- $this->x
- $this->rMargin
- $mc_margin['R']);
5826 // store original margin values
5827 $lMargin = $this->lMargin
;
5828 $rMargin = $this->rMargin
;
5830 $this->rMargin
= ($this->w
- $this->x
);
5831 $this->lMargin
= ($this->x
- $w);
5833 $this->lMargin
= ($this->x
);
5834 $this->rMargin
= ($this->w
- $this->x
- $w);
5836 $this->clMargin
= $this->lMargin
;
5837 $this->crMargin
= $this->rMargin
;
5840 $this->y +
= $mc_padding['T'];
5842 if ($ishtml) { // ******* Write HTML text
5843 $this->writeHTML($txt, true, false, $reseth, true, $align);
5845 } else { // ******* Write simple text
5846 $prev_FontSizePt = $this->FontSizePt
;
5848 // ajust height values
5849 $tobottom = ($this->h
- $this->y
- $this->bMargin
- $this->cell_padding
['T'] - $this->cell_padding
['B']);
5850 $h = $maxh = max(min($h, $tobottom), min($maxh, $tobottom));
5852 // vertical alignment
5855 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5857 // try to reduce font size to fit text on cell (use a quick search algorithm)
5859 $fmax = $this->FontSizePt
;
5860 $diff_epsilon = (1 / $this->k
); // one point (min resolution)
5861 $maxit = ($fmax - $fmin); // max number of iterations
5862 while ($maxit > 0) {
5863 $fmid = (($fmax +
$fmin) / 2);
5864 $this->SetFontSize($fmid, false);
5865 $this->resetLastH();
5866 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5867 $diff = ($maxh - $text_height);
5869 if ($diff < $diff_epsilon) {
5879 $this->SetFontSize($this->FontSizePt
);
5881 if ($text_height < $maxh) {
5882 if ($valign == 'M') {
5883 // text vertically centered
5884 $this->y +
= (($maxh - $text_height) / 2);
5885 } elseif ($valign == 'B') {
5886 // text vertically aligned on bottom
5887 $this->y +
= ($maxh - $text_height);
5891 $nl = $this->Write($this->lasth
, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
5893 // restore font size
5894 $this->SetFontSize($prev_FontSizePt);
5898 // add bottom padding
5899 $this->y +
= $mc_padding['B'];
5901 // Get end-of-text Y position
5902 $currentY = $this->y
;
5903 // get latest page number
5904 $endpage = $this->page
;
5906 $skip = ($endpage - $startpage);
5908 while ($tmpresth > 0) {
5910 // add a page (or trig AcceptPageBreak() for multicolumn mode)
5911 $this->checkPageBreak($this->PageBreakTrigger +
1);
5913 if ($this->num_columns
> 1) {
5914 $tmpresth -= ($this->h
- $this->y
- $this->bMargin
);
5916 $tmpresth -= ($this->h
- $this->tMargin
- $this->bMargin
);
5920 $currentY = $this->y
;
5921 $endpage = $this->page
;
5923 // get latest column
5924 $endcolumn = $this->current_column
;
5925 if ($this->num_columns
== 0) {
5926 $this->num_columns
= 1;
5928 // disable page regions check
5929 $check_page_regions = $this->check_page_regions
;
5930 $this->check_page_regions
= false;
5932 $border_start = TCPDF_STATIC
::getBorderMode($border, $position='start', $this->opencell
);
5933 $border_end = TCPDF_STATIC
::getBorderMode($border, $position='end', $this->opencell
);
5934 $border_middle = TCPDF_STATIC
::getBorderMode($border, $position='middle', $this->opencell
);
5935 // design borders around HTML cells.
5936 for ($page = $startpage; $page <= $endpage; ++
$page) { // for each page
5938 $this->setPage($page);
5939 if ($this->num_columns
< 2) {
5940 // single-column mode
5942 $this->y
= $this->tMargin
;
5944 // account for margin changes
5945 if ($page > $startpage) {
5946 if (($this->rtl
) AND ($this->pagedim
[$page]['orm'] != $this->pagedim
[$startpage]['orm'])) {
5947 $this->x
-= ($this->pagedim
[$page]['orm'] - $this->pagedim
[$startpage]['orm']);
5948 } elseif ((!$this->rtl
) AND ($this->pagedim
[$page]['olm'] != $this->pagedim
[$startpage]['olm'])) {
5949 $this->x +
= ($this->pagedim
[$page]['olm'] - $this->pagedim
[$startpage]['olm']);
5952 if ($startpage == $endpage) {
5954 for ($column = $startcolumn; $column <= $endcolumn; ++
$column) { // for each column
5955 $this->selectColumn($column);
5957 $this->x
-= $mc_margin['R'];
5959 $this->x +
= $mc_margin['L'];
5961 if ($startcolumn == $endcolumn) { // single column
5963 $h = max($h, ($currentY - $oy));
5965 } elseif ($column == $startcolumn) { // first column
5966 $cborder = $border_start;
5968 $h = $this->h
- $this->y
- $this->bMargin
;
5969 } elseif ($column == $endcolumn) { // end column
5970 $cborder = $border_end;
5971 $h = $currentY - $this->y
;
5975 } else { // middle column
5976 $cborder = $border_middle;
5977 $h = $this->h
- $this->y
- $this->bMargin
;
5980 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
5981 } // end for each column
5982 } elseif ($page == $startpage) { // first page
5983 for ($column = $startcolumn; $column < $this->num_columns
; ++
$column) { // for each column
5984 $this->selectColumn($column);
5986 $this->x
-= $mc_margin['R'];
5988 $this->x +
= $mc_margin['L'];
5990 if ($column == $startcolumn) { // first column
5991 $cborder = $border_start;
5993 $h = $this->h
- $this->y
- $this->bMargin
;
5994 } else { // middle column
5995 $cborder = $border_middle;
5996 $h = $this->h
- $this->y
- $this->bMargin
;
5999 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6000 } // end for each column
6001 } elseif ($page == $endpage) { // last page
6002 for ($column = 0; $column <= $endcolumn; ++
$column) { // for each column
6003 $this->selectColumn($column);
6005 $this->x
-= $mc_margin['R'];
6007 $this->x +
= $mc_margin['L'];
6009 if ($column == $endcolumn) {
6011 $cborder = $border_end;
6012 $h = $currentY - $this->y
;
6018 $cborder = $border_middle;
6019 $h = $this->h
- $this->y
- $this->bMargin
;
6022 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6023 } // end for each column
6024 } else { // middle page
6025 for ($column = 0; $column < $this->num_columns
; ++
$column) { // for each column
6026 $this->selectColumn($column);
6028 $this->x
-= $mc_margin['R'];
6030 $this->x +
= $mc_margin['L'];
6032 $cborder = $border_middle;
6033 $h = $this->h
- $this->y
- $this->bMargin
;
6035 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6036 } // end for each column
6038 if ($cborder OR $fill) {
6039 $offsetlen = strlen($ccode);
6040 // draw border and fill
6041 if ($this->inxobj
) {
6042 // we are inside an XObject template
6043 if (end($this->xobjects
[$this->xobjid
]['transfmrk']) !== false) {
6044 $pagemarkkey = key($this->xobjects
[$this->xobjid
]['transfmrk']);
6045 $pagemark = $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey];
6046 $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey] +
= $offsetlen;
6048 $pagemark = $this->xobjects
[$this->xobjid
]['intmrk'];
6049 $this->xobjects
[$this->xobjid
]['intmrk'] +
= $offsetlen;
6051 $pagebuff = $this->xobjects
[$this->xobjid
]['outdata'];
6052 $pstart = substr($pagebuff, 0, $pagemark);
6053 $pend = substr($pagebuff, $pagemark);
6054 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$ccode.$pend;
6056 if (end($this->transfmrk
[$this->page
]) !== false) {
6057 $pagemarkkey = key($this->transfmrk
[$this->page
]);
6058 $pagemark = $this->transfmrk
[$this->page
][$pagemarkkey];
6059 $this->transfmrk
[$this->page
][$pagemarkkey] +
= $offsetlen;
6060 } elseif ($this->InFooter
) {
6061 $pagemark = $this->footerpos
[$this->page
];
6062 $this->footerpos
[$this->page
] +
= $offsetlen;
6064 $pagemark = $this->intmrk
[$this->page
];
6065 $this->intmrk
[$this->page
] +
= $offsetlen;
6067 $pagebuff = $this->getPageBuffer($this->page
);
6068 $pstart = substr($pagebuff, 0, $pagemark);
6069 $pend = substr($pagebuff, $pagemark);
6070 $this->setPageBuffer($this->page
, $pstart.$ccode.$pend);
6073 } // end for each page
6074 // restore page regions check
6075 $this->check_page_regions
= $check_page_regions;
6076 // Get end-of-cell Y position
6077 $currentY = $this->GetY();
6078 // restore previous values
6079 if ($this->num_columns
> 1) {
6080 $this->selectColumn();
6082 // restore original margins
6083 $this->lMargin
= $lMargin;
6084 $this->rMargin
= $rMargin;
6085 if ($this->page
> $startpage) {
6086 // check for margin variations between pages (i.e. booklet mode)
6087 $dl = ($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$startpage]['olm']);
6088 $dr = ($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$startpage]['orm']);
6089 if (($dl != 0) OR ($dr != 0)) {
6090 $this->lMargin +
= $dl;
6091 $this->rMargin +
= $dr;
6096 //Go to the beginning of the next line
6097 $this->SetY($currentY +
$mc_margin['B']);
6099 $this->SetX($x +
$w +
$mc_margin['L'] +
$mc_margin['R']);
6102 // go left or right by case
6103 $this->setPage($startpage);
6105 $this->SetX($x +
$w +
$mc_margin['L'] +
$mc_margin['R']);
6107 $this->setContentMark();
6108 $this->cell_padding
= $prev_cell_padding;
6109 $this->cell_margin
= $prev_cell_margin;
6110 $this->clMargin
= $this->lMargin
;
6111 $this->crMargin
= $this->rMargin
;
6116 * This method return the estimated number of lines for print a simple text string using Multicell() method.
6117 * @param $txt (string) String for calculating his height
6118 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
6119 * @param $reseth (boolean) if true reset the last cell height (default false).
6120 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
6121 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
6122 * @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)))
6123 * @return float Return the minimal height needed for multicell method for printing the $txt param.
6124 * @author Alexander Escalona Fern\E1ndez, Nicola Asuni
6128 public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6129 if ($txt === NULL) {
6136 // adjust internal padding
6137 $prev_cell_padding = $this->cell_padding
;
6138 $prev_lasth = $this->lasth
;
6139 if (is_array($cellpadding)) {
6140 $this->cell_padding
= $cellpadding;
6142 $this->adjustCellPadding($border);
6143 if (TCPDF_STATIC
::empty_string($w) OR ($w <= 0)) {
6145 $w = $this->x
- $this->lMargin
;
6147 $w = $this->w
- $this->rMargin
- $this->x
;
6150 $wmax = $w - $this->cell_padding
['L'] - $this->cell_padding
['R'];
6153 $this->resetLastH();
6157 $chars = TCPDF_FONTS
::utf8Bidi(TCPDF_FONTS
::UTF8StringToArray($txt, $this->isunicode
, $this->CurrentFont
), $txt, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6158 $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
6159 $length = count($chars);
6160 $lastSeparator = -1;
6161 for ($i = 0; $i < $length; ++
$i) {
6163 $charWidth = $charsWidth[$i];
6166 OR preg_match($this->re_spaces
, TCPDF_FONTS
::unichr($c, $this->isunicode
))
6168 AND ($i > 0) AND ($i < ($length - 1))
6169 AND @preg_match
('/[\p{L}]/'.$this->re_space
['m'], TCPDF_FONTS
::unichr($chars[($i - 1)], $this->isunicode
))
6170 AND @preg_match
('/[\p{L}]/'.$this->re_space
['m'], TCPDF_FONTS
::unichr($chars[($i +
1)], $this->isunicode
))
6174 $lastSeparator = $i;
6176 if ((($sum +
$charWidth) > $wmax) OR ($c == 10)) {
6179 $lastSeparator = -1;
6181 } elseif ($lastSeparator != -1) {
6182 $i = $lastSeparator;
6183 $lastSeparator = -1;
6192 if ($chars[($length - 1)] == 10) {
6195 $this->cell_padding
= $prev_cell_padding;
6196 $this->lasth
= $prev_lasth;
6201 * This method return the estimated height needed for printing a simple text string using the Multicell() method.
6202 * Generally, if you want to know the exact height for a block of content you can use the following alternative technique:
6204 * // store current object
6205 * $pdf->startTransaction();
6206 * // store starting values
6207 * $start_y = $pdf->GetY();
6208 * $start_page = $pdf->getPage();
6209 * // call your printing functions with your parameters
6210 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6211 * $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);
6212 * // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6214 * $end_y = $pdf->GetY();
6215 * $end_page = $pdf->getPage();
6216 * // calculate height
6218 * if ($end_page == $start_page) {
6219 * $height = $end_y - $start_y;
6221 * for ($page=$start_page; $page <= $end_page; ++$page) {
6222 * $this->setPage($page);
6223 * if ($page == $start_page) {
6225 * $height = $this->h - $start_y - $this->bMargin;
6226 * } elseif ($page == $end_page) {
6228 * $height = $end_y - $this->tMargin;
6230 * $height = $this->h - $this->tMargin - $this->bMargin;
6234 * // restore previous object
6235 * $pdf = $pdf->rollbackTransaction();
6237 * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
6238 * @param $txt (string) String for calculating his height
6239 * @param $reseth (boolean) if true reset the last cell height (default false).
6240 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
6241 * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
6242 * @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)))
6243 * @return float Return the minimal height needed for multicell method for printing the $txt param.
6244 * @author Nicola Asuni, Alexander Escalona Fern\E1ndez
6247 public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6248 // adjust internal padding
6249 $prev_cell_padding = $this->cell_padding
;
6250 $prev_lasth = $this->lasth
;
6251 if (is_array($cellpadding)) {
6252 $this->cell_padding
= $cellpadding;
6254 $this->adjustCellPadding($border);
6255 $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
6256 $height = $this->getCellHeight(($lines * $this->FontSize
), $autopadding);
6257 $this->cell_padding
= $prev_cell_padding;
6258 $this->lasth
= $prev_lasth;
6263 * This method prints text from the current position.<br />
6264 * @param $h (float) Line height
6265 * @param $txt (string) String to print
6266 * @param $link (mixed) URL or identifier returned by AddLink()
6267 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
6268 * @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>
6269 * @param $ln (boolean) if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
6270 * @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.
6271 * @param $firstline (boolean) if true prints only the first line and return the remaining string.
6272 * @param $firstblock (boolean) if true the string is the starting of a line.
6273 * @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.
6274 * @param $wadj (float) first line width will be reduced by this amount (used in HTML mode).
6275 * @param $margin (array) margin array of the parent container
6276 * @return mixed Return the number of cells or the remaining string if $firstline = true.
6280 public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
6281 // check page for no-write regions and adapt page margins if necessary
6282 list($this->x
, $this->y
) = $this->checkPageRegions($h, $this->x
, $this->y
);
6283 if (strlen($txt) == 0) {
6287 if ($margin === '') {
6288 // set default margins
6289 $margin = $this->cell_margin
;
6291 // remove carriage returns
6292 $s = str_replace("\r", '', $txt);
6293 // check if string contains arabic text
6294 if (preg_match(TCPDF_FONT_DATA
::$uni_RE_PATTERN_ARABIC, $s)) {
6299 // check if string contains RTL text
6300 if ($arabic OR ($this->tmprtl
== 'R') OR preg_match(TCPDF_FONT_DATA
::$uni_RE_PATTERN_RTL, $s)) {
6306 $chrwidth = $this->GetCharWidth(46); // dot character
6307 // get array of unicode values
6308 $chars = TCPDF_FONTS
::UTF8StringToArray($s, $this->isunicode
, $this->CurrentFont
);
6309 // calculate maximum width for a single character on string
6310 $chrw = $this->GetArrStringWidth($chars, '', '', 0, true);
6311 array_walk($chrw, array($this, 'getRawCharWidth'));
6312 $maxchwidth = max($chrw);
6313 // get array of chars
6314 $uchars = TCPDF_FONTS
::UTF8ArrayToUniArray($chars, $this->isunicode
);
6315 // get the number of characters
6316 $nb = count($chars);
6317 // replacement for SHY character (minus symbol)
6318 $shy_replacement = 45;
6319 $shy_replacement_char = TCPDF_FONTS
::unichr($shy_replacement, $this->isunicode
);
6320 // widht for SHY replacement
6321 $shy_replacement_width = $this->GetCharWidth($shy_replacement);
6323 $maxy = $this->y +
$maxh - $h - $this->cell_padding
['T'] - $this->cell_padding
['B'];
6325 $pw = $w = $this->w
- $this->lMargin
- $this->rMargin
;
6326 // calculate remaining line width ($w)
6328 $w = $this->x
- $this->lMargin
;
6330 $w = $this->w
- $this->rMargin
- $this->x
;
6333 $wmax = ($w - $wadj);
6335 $wmax -= ($this->cell_padding
['L'] +
$this->cell_padding
['R']);
6337 if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) {
6338 // the maximum width character do not fit on column
6341 // minimum row height
6342 $row_height = max($h, $this->getCellHeight($this->FontSize
));
6343 $start_page = $this->page
;
6344 $i = 0; // character position
6345 $j = 0; // current starting position
6346 $sep = -1; // position of the last blank space
6347 $shy = false; // true if the last blank is a soft hypen (SHY)
6348 $l = 0; // current string length
6349 $nl = 0; //number of lines
6351 $pc = 0; // previous character
6352 // for each character
6354 if (($maxh > 0) AND ($this->y
>= $maxy) ) {
6357 //Get the current character
6359 if ($c == 10) { // 10 = "\n" = new line
6360 //Explicit line break
6361 if ($align == 'J') {
6370 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, $i);
6373 $tmparr = array_slice($chars, $j, ($i - $j));
6375 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6377 $linew = $this->GetArrStringWidth($tmparr);
6380 $this->endlinex
= $startx - $linew;
6382 $this->endlinex
= $startx +
$linew;
6385 $tmpcellpadding = $this->cell_padding
;
6387 $this->SetCellPadding(0);
6390 if ($firstblock AND $this->isRTLTextDir()) {
6391 $tmpstr = $this->stringRightTrim($tmpstr);
6393 // Skip newlines at the begining of a page or column
6394 if (!empty($tmpstr) OR ($this->y
< ($this->PageBreakTrigger
- $row_height))) {
6395 $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
6399 $this->cell_padding
= $tmpcellpadding;
6400 return (TCPDF_FONTS
::UniArrSubString($uchars, $i));
6407 // account for margin changes
6408 if ((($this->y +
$this->lasth
) > $this->PageBreakTrigger
) AND ($this->inPageBody())) {
6409 $this->AcceptPageBreak();
6411 $this->x
-= $margin['R'];
6413 $this->x +
= $margin['L'];
6415 $this->lMargin +
= $margin['L'];
6416 $this->rMargin +
= $margin['R'];
6418 $w = $this->getRemainingWidth();
6419 $wmax = ($w - $this->cell_padding
['L'] - $this->cell_padding
['R']);
6421 // 160 is the non-breaking space.
6422 // 173 is SHY (Soft Hypen).
6423 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
6424 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
6425 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
6428 OR preg_match($this->re_spaces
, TCPDF_FONTS
::unichr($c, $this->isunicode
))
6430 AND ($i < ($nb - 1))
6431 AND @preg_match
('/[\p{L}]/'.$this->re_space
['m'], TCPDF_FONTS
::unichr($pc, $this->isunicode
))
6432 AND @preg_match
('/[\p{L}]/'.$this->re_space
['m'], TCPDF_FONTS
::unichr($chars[($i +
1)], $this->isunicode
))
6436 // update last blank space position
6438 // check if is a SHY
6439 if (($c == 173) OR ($c == 45)) {
6442 $tmp_shy_replacement_width = 0;
6443 $tmp_shy_replacement_char = '';
6445 $tmp_shy_replacement_width = $shy_replacement_width;
6446 $tmp_shy_replacement_char = $shy_replacement_char;
6452 // update string length
6453 if ($this->isUnicodeFont() AND ($arabic)) {
6454 // with bidirectional algorithm some chars may be changed affecting the line length
6455 // *** very slow ***
6456 $l = $this->GetArrStringWidth(TCPDF_FONTS
::utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl
, $this->isunicode
, $this->CurrentFont
));
6458 $l +
= $this->GetCharWidth($c);
6460 if (($l > $wmax) OR (($c == 173) AND (($l +
$tmp_shy_replacement_width) > $wmax)) ) {
6461 // we have reached the end of column
6463 // check if the line was already started
6464 if (($this->rtl
AND ($this->x
<= ($this->w
- $this->rMargin
- $this->cell_padding
['R'] - $margin['R'] - $chrwidth)))
6465 OR ((!$this->rtl
) AND ($this->x
>= ($this->lMargin +
$this->cell_padding
['L'] +
$margin['L'] +
$chrwidth)))) {
6466 // print a void cell and go to next line
6467 $this->Cell($w, $h, '', 0, 1);
6470 return (TCPDF_FONTS
::UniArrSubString($uchars, $j));
6473 // truncate the word because do not fit on column
6474 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, $i);
6477 $tmparr = array_slice($chars, $j, ($i - $j));
6479 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6481 $linew = $this->GetArrStringWidth($tmparr);
6484 $this->endlinex
= $startx - $linew;
6486 $this->endlinex
= $startx +
$linew;
6489 $tmpcellpadding = $this->cell_padding
;
6491 $this->SetCellPadding(0);
6494 if ($firstblock AND $this->isRTLTextDir()) {
6495 $tmpstr = $this->stringRightTrim($tmpstr);
6497 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6500 $this->cell_padding
= $tmpcellpadding;
6501 return (TCPDF_FONTS
::UniArrSubString($uchars, $i));
6508 if ($this->rtl
AND (!$firstblock) AND ($sep < $i)) {
6513 // check the length of the next string
6514 $strrest = TCPDF_FONTS
::UniArrSubString($uchars, ($sep +
$endspace));
6515 $nextstr = TCPDF_STATIC
::pregSplit('/'.$this->re_space
['p'].'/', $this->re_space
['m'], $this->stringTrim($strrest));
6516 if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
6517 // truncate the word because do not fit on a full page width
6518 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, $i);
6521 $tmparr = array_slice($chars, $j, ($i - $j));
6523 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6525 $linew = $this->GetArrStringWidth($tmparr);
6528 $this->endlinex
= ($startx - $linew);
6530 $this->endlinex
= ($startx +
$linew);
6533 $tmpcellpadding = $this->cell_padding
;
6535 $this->SetCellPadding(0);
6538 if ($firstblock AND $this->isRTLTextDir()) {
6539 $tmpstr = $this->stringRightTrim($tmpstr);
6541 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6544 $this->cell_padding
= $tmpcellpadding;
6545 return (TCPDF_FONTS
::UniArrSubString($uchars, $i));
6552 // add hypen (minus symbol) at the end of the line
6553 $shy_width = $tmp_shy_replacement_width;
6555 $shy_char_left = $tmp_shy_replacement_char;
6556 $shy_char_right = '';
6558 $shy_char_left = '';
6559 $shy_char_right = $tmp_shy_replacement_char;
6563 $shy_char_left = '';
6564 $shy_char_right = '';
6566 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, ($sep +
$endspace));
6569 $tmparr = array_slice($chars, $j, (($sep +
$endspace) - $j));
6571 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6573 $linew = $this->GetArrStringWidth($tmparr);
6576 $this->endlinex
= $startx - $linew - $shy_width;
6578 $this->endlinex
= $startx +
$linew +
$shy_width;
6581 $tmpcellpadding = $this->cell_padding
;
6583 $this->SetCellPadding(0);
6587 if ($firstblock AND $this->isRTLTextDir()) {
6588 $tmpstr = $this->stringRightTrim($tmpstr);
6590 $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
6593 if ($chars[$sep] == 45) {
6596 // return the remaining text
6597 $this->cell_padding
= $tmpcellpadding;
6598 return (TCPDF_FONTS
::UniArrSubString($uchars, ($sep +
$endspace)));
6606 // account for margin changes
6607 if ((($this->y +
$this->lasth
) > $this->PageBreakTrigger
) AND ($this->inPageBody())) {
6608 $this->AcceptPageBreak();
6610 $this->x
-= $margin['R'];
6612 $this->x +
= $margin['L'];
6614 $this->lMargin +
= $margin['L'];
6615 $this->rMargin +
= $margin['R'];
6617 $w = $this->getRemainingWidth();
6618 $wmax = $w - $this->cell_padding
['L'] - $this->cell_padding
['R'];
6627 // save last character
6630 } // end while i < nb
6631 // print last substring (if any)
6660 $tmpstr = TCPDF_FONTS
::UniArrSubString($uchars, $j, $nb);
6663 $tmparr = array_slice($chars, $j, ($nb - $j));
6665 $tmparr = TCPDF_FONTS
::utf8Bidi($tmparr, $tmpstr, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
6667 $linew = $this->GetArrStringWidth($tmparr);
6670 $this->endlinex
= $startx - $linew;
6672 $this->endlinex
= $startx +
$linew;
6675 $tmpcellpadding = $this->cell_padding
;
6677 $this->SetCellPadding(0);
6680 if ($firstblock AND $this->isRTLTextDir()) {
6681 $tmpstr = $this->stringRightTrim($tmpstr);
6683 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
6686 $this->cell_padding
= $tmpcellpadding;
6687 return (TCPDF_FONTS
::UniArrSubString($uchars, $nb));
6698 * Returns the remaining width between the current position and margins.
6699 * @return int Return the remaining width
6702 protected function getRemainingWidth() {
6703 list($this->x
, $this->y
) = $this->checkPageRegions(0, $this->x
, $this->y
);
6705 return ($this->x
- $this->lMargin
);
6707 return ($this->w
- $this->rMargin
- $this->x
);
6712 * Set the block dimensions accounting for page breaks and page/column fitting
6713 * @param $w (float) width
6714 * @param $h (float) height
6715 * @param $x (float) X coordinate
6716 * @param $y (float) Y coodiante
6717 * @param $fitonpage (boolean) if true the block is resized to not exceed page dimensions.
6718 * @return array($w, $h, $x, $y)
6720 * @since 5.5.009 (2010-07-05)
6722 protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
6724 // set maximum width
6725 $w = ($this->w
- $this->lMargin
- $this->rMargin
);
6731 // set maximum height
6732 $h = ($this->PageBreakTrigger
- $this->tMargin
);
6737 // resize the block to be vertically contained on a single page or single column
6738 if ($fitonpage OR $this->AutoPageBreak
) {
6739 $ratio_wh = ($w / $h);
6740 if ($h > ($this->PageBreakTrigger
- $this->tMargin
)) {
6741 $h = $this->PageBreakTrigger
- $this->tMargin
;
6742 $w = ($h * $ratio_wh);
6744 // resize the block to be horizontally contained on a single page or single column
6746 $maxw = ($this->w
- $this->lMargin
- $this->rMargin
);
6749 $h = ($w / $ratio_wh);
6753 // Check whether we need a new page or new column first as this does not fit
6756 if ($this->checkPageBreak($h, $y) OR ($this->y
< $prev_y)) {
6759 $x +
= ($prev_x - $this->x
);
6761 $x +
= ($this->x
- $prev_x);
6763 $this->newline
= true;
6765 // resize the block to be contained on the remaining available page or column space
6767 $ratio_wh = ($w / $h);
6768 if (($y +
$h) > $this->PageBreakTrigger
) {
6769 $h = $this->PageBreakTrigger
- $y;
6770 $w = ($h * $ratio_wh);
6772 if ((!$this->rtl
) AND (($x +
$w) > ($this->w
- $this->rMargin
))) {
6773 $w = $this->w
- $this->rMargin
- $x;
6774 $h = ($w / $ratio_wh);
6775 } elseif (($this->rtl
) AND (($x - $w) < ($this->lMargin
))) {
6776 $w = $x - $this->lMargin
;
6777 $h = ($w / $ratio_wh);
6780 return array($w, $h, $x, $y);
6784 * Puts an image in the page.
6785 * The upper-left corner must be given.
6786 * The dimensions can be specified in different ways:<ul>
6787 * <li>explicit width and height (expressed in user unit)</li>
6788 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
6789 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
6790 * 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;
6791 * The format can be specified explicitly or inferred from the file extension.<br />
6792 * It is possible to put a link on the image.<br />
6793 * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
6794 * @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').
6795 * @param $x (float) Abscissa of the upper-left corner (LTR) or upper-right corner (RTL).
6796 * @param $y (float) Ordinate of the upper-left corner (LTR) or upper-right corner (RTL).
6797 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
6798 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
6799 * @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.
6800 * @param $link (mixed) URL or identifier returned by AddLink().
6801 * @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>
6802 * @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).
6803 * @param $dpi (int) dot-per-inch resolution used on resize
6804 * @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>
6805 * @param $ismask (boolean) true if this image is a mask, false otherwise
6806 * @param $imgmask (mixed) image object returned by this function or false
6807 * @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)))
6808 * @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).
6809 * @param $hidden (boolean) If true do not display the image.
6810 * @param $fitonpage (boolean) If true the image is resized to not exceed page dimensions.
6811 * @param $alt (boolean) If true the image will be added as alternative and not directly printed (the ID of the image will be returned).
6812 * @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.
6813 * @return image information
6817 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()) {
6818 if ($this->state
!= 2) {
6827 // check page for no-write regions and adapt page margins if necessary
6828 list($x, $y) = $this->checkPageRegions($h, $x, $y);
6829 $exurl = ''; // external streams
6831 // check if we are passing an image as file or string
6832 if ($file[0] === '@') {
6833 // image from string
6834 $imgdata = substr($file, 1);
6835 } else { // image file
6836 if ($file[0] === '*') {
6837 // image as external stream
6838 $file = substr($file, 1);
6841 // check if is a local file
6842 if (!@file_exists
($file)) {
6843 // try to encode spaces on filename
6844 $tfile = str_replace(' ', '%20', $file);
6845 if (@file_exists
($tfile)) {
6849 if (($imsize = @getimagesize
($file)) === FALSE) {
6850 if (in_array($file, $this->imagekeys
)) {
6851 // get existing image data
6852 $info = $this->getImageBuffer($file);
6853 $imsize = array($info['w'], $info['h']);
6854 } elseif (strpos($file, '__tcpdf_img') === FALSE) {
6855 $imgdata = TCPDF_STATIC
::fileGetContents($file);
6859 if (!empty($imgdata)) {
6860 // copy image to cache
6861 $original_file = $file;
6862 $file = TCPDF_STATIC
::getObjFilename('img');
6863 $fp = fopen($file, 'w');
6864 fwrite($fp, $imgdata);
6867 $imsize = @getimagesize
($file);
6868 if ($imsize === FALSE) {
6870 $file = $original_file;
6872 $this->cached_files
[] = $file;
6875 if ($imsize === FALSE) {
6876 if (($w > 0) AND ($h > 0)) {
6877 // get measures from specified data
6878 $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit
, true) * $this->imgscale
* $this->k
;
6879 $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit
, true) * $this->imgscale
* $this->k
;
6880 $imsize = array($pw, $ph);
6882 $this->Error('[Image] Unable to get the size of the image: '.$file);
6886 $filehash = md5($this->file_id
.$file);
6887 // get original image width and height in pixels
6888 list($pixw, $pixh) = $imsize;
6889 // calculate image width and height on document
6890 if (($w <= 0) AND ($h <= 0)) {
6891 // convert image size to document unit
6892 $w = $this->pixelsToUnits($pixw);
6893 $h = $this->pixelsToUnits($pixh);
6894 } elseif ($w <= 0) {
6895 $w = $h * $pixw / $pixh;
6896 } elseif ($h <= 0) {
6897 $h = $w * $pixh / $pixw;
6898 } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
6899 if (strlen($fitbox) !== 2) {
6900 // set default alignment
6903 // scale image dimensions proportionally to fit within the ($w, $h) box
6904 if ((($w * $pixh) / ($h * $pixw)) < 1) {
6905 // store current height
6907 // calculate new height
6908 $h = $w * $pixh / $pixw;
6909 // height difference
6910 $hdiff = ($oldh - $h);
6911 // vertical alignment
6912 switch (strtoupper($fitbox[1])) {
6926 // store current width
6928 // calculate new width
6929 $w = $h * $pixw / $pixh;
6931 $wdiff = ($oldw - $w);
6932 // horizontal alignment
6933 switch (strtoupper($fitbox[0])) {
6957 // fit the image on available space
6958 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
6959 // calculate new minimum dimensions in pixels
6960 $neww = round($w * $this->k
* $dpi / $this->dpi
);
6961 $newh = round($h * $this->k
* $dpi / $this->dpi
);
6962 // check if resize is necessary (resize is used only to reduce the image)
6963 $newsize = ($neww * $newh);
6964 $pixsize = ($pixw * $pixh);
6965 if (intval($resize) == 2) {
6967 } elseif ($newsize >= $pixsize) {
6970 // check if image has been already added on document
6972 if (in_array($file, $this->imagekeys
)) {
6974 // get existing image data
6975 $info = $this->getImageBuffer($file);
6976 if (strpos($file, '__tcpdf_imgmask_') === FALSE) {
6977 // check if the newer image is larger
6978 $oldsize = ($info['w'] * $info['h']);
6979 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
6983 } elseif (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_imgmask_') === FALSE)) {
6984 // create temp image file (without alpha channel)
6985 $tempfile_plain = K_PATH_CACHE
.'__tcpdf_imgmask_plain_'.$filehash;
6986 // create temp alpha file
6987 $tempfile_alpha = K_PATH_CACHE
.'__tcpdf_imgmask_alpha_'.$filehash;
6988 // check for cached images
6989 if (in_array($tempfile_plain, $this->imagekeys
)) {
6990 // get existing image data
6991 $info = $this->getImageBuffer($tempfile_plain);
6992 // check if the newer image is larger
6993 $oldsize = ($info['w'] * $info['h']);
6994 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
6999 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7000 // embed image, masked with previously embedded mask
7001 return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7006 //First use of image, get info
7007 $type = strtolower($type);
7009 $type = TCPDF_IMAGES
::getImageFileType($file, $imsize);
7010 } elseif ($type == 'jpg') {
7013 $mqr = TCPDF_STATIC
::get_mqr();
7014 TCPDF_STATIC
::set_mqr(false);
7015 // Specific image handlers (defined on TCPDF_IMAGES CLASS)
7016 $mtd = '_parse'.$type;
7017 // GD image handler function
7018 $gdfunction = 'imagecreatefrom'.$type;
7020 if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
7021 // TCPDF image functions
7022 $info = TCPDF_IMAGES
::$mtd($file);
7023 if (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_imgmask_') === FALSE)
7024 AND (($info === 'pngalpha') OR (isset($info['trns']) AND !empty($info['trns'])))) {
7025 return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
7028 if (($info === false) AND function_exists($gdfunction)) {
7031 $img = $gdfunction($file);
7032 if ($img !== false) {
7034 $imgr = imagecreatetruecolor($neww, $newh);
7035 if (($type == 'gif') OR ($type == 'png')) {
7036 $imgr = TCPDF_IMAGES
::setGDImageTransparency($imgr, $img);
7038 imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
7041 if (($type == 'gif') OR ($type == 'png')) {
7042 $info = TCPDF_IMAGES
::_toPNG($img);
7044 $info = TCPDF_IMAGES
::_toJPEG($img, $this->jpeg_quality
);
7047 } catch(Exception
$e) {
7051 if (($info === false) AND extension_loaded('imagick')) {
7053 // ImageMagick library
7054 $img = new Imagick();
7055 if ($type == 'SVG') {
7056 // get SVG file content
7057 $svgimg = TCPDF_STATIC
::fileGetContents($file);
7058 if ($svgimg !== FALSE) {
7059 // get width and height
7061 if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) {
7064 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7065 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit
, false);
7066 $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit
;
7067 $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
7072 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7073 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit
, false);
7074 $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit
;
7075 $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
7080 if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
7081 $vbw = ($ow * $this->imgscale
* $this->k
);
7082 $vbh = ($oh * $this->imgscale
* $this->k
);
7083 $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh);
7084 $svgtag = $vbox.$svgtag;
7086 $svgimg = preg_replace('/<svg([^\>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
7088 $img->readImageBlob($svgimg);
7091 $img->readImage($file);
7094 $img->resizeImage($neww, $newh, 10, 1, false);
7096 $img->setCompressionQuality($this->jpeg_quality
);
7097 $img->setImageFormat('jpeg');
7098 $tempname = TCPDF_STATIC
::getObjFilename('img');
7099 $img->writeImage($tempname);
7100 $info = TCPDF_IMAGES
::_parsejpeg($tempname);
7103 } catch(Exception
$e) {
7107 if ($info === false) {
7108 // unable to process image
7111 TCPDF_STATIC
::set_mqr($mqr);
7114 $info['cs'] = 'DeviceGray';
7116 if ($imgmask !== false) {
7117 $info['masked'] = $imgmask;
7119 if (!empty($exurl)) {
7120 $info['exurl'] = $exurl;
7122 // array of alternative images
7123 $info['altimgs'] = $altimgs;
7124 // add image to document
7125 $info['i'] = $this->setImageBuffer($file, $info);
7128 $this->img_rb_y
= $y +
$h;
7131 if ($palign == 'L') {
7132 $ximg = $this->lMargin
;
7133 } elseif ($palign == 'C') {
7134 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
7135 } elseif ($palign == 'R') {
7136 $ximg = $this->w
- $this->rMargin
- $w;
7140 $this->img_rb_x
= $ximg;
7142 if ($palign == 'L') {
7143 $ximg = $this->lMargin
;
7144 } elseif ($palign == 'C') {
7145 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
7146 } elseif ($palign == 'R') {
7147 $ximg = $this->w
- $this->rMargin
- $w;
7151 $this->img_rb_x
= $ximg +
$w;
7153 if ($ismask OR $hidden) {
7154 // image is not displayed
7157 $xkimg = $ximg * $this->k
;
7159 // only non-alternative immages will be set
7160 $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']));
7162 if (!empty($border)) {
7170 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
7175 $this->Link($ximg, $y, $w, $h, $link, 0);
7177 // set pointer to align the next text/objects
7181 $this->x
= $this->img_rb_x
;
7185 $this->y
= $y +
round($h/2);
7186 $this->x
= $this->img_rb_x
;
7190 $this->y
= $this->img_rb_y
;
7191 $this->x
= $this->img_rb_x
;
7195 $this->SetY($this->img_rb_y
);
7202 $this->endlinex
= $this->img_rb_x
;
7203 if ($this->inxobj
) {
7204 // we are inside an XObject template
7205 $this->xobjects
[$this->xobjid
]['images'][] = $info['i'];
7211 * Extract info from a PNG image with alpha channel using the Imagick or GD library.
7212 * @param $file (string) Name of the file containing the image.
7213 * @param $x (float) Abscissa of the upper-left corner.
7214 * @param $y (float) Ordinate of the upper-left corner.
7215 * @param $wpx (float) Original width of the image in pixels.
7216 * @param $hpx (float) original height of the image in pixels.
7217 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
7218 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
7219 * @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.
7220 * @param $link (mixed) URL or identifier returned by AddLink().
7221 * @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>
7222 * @param $resize (boolean) If true resize (reduce) the image to fit $w and $h (requires GD library).
7223 * @param $dpi (int) dot-per-inch resolution used on resize
7224 * @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>
7225 * @param $filehash (string) File hash used to build unique file names.
7226 * @author Nicola Asuni
7228 * @since 4.3.007 (2008-12-04)
7231 protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
7232 // create temp images
7233 if (empty($filehash)) {
7234 $filehash = md5($this->file_id
.$file);
7236 // create temp image file (without alpha channel)
7237 $tempfile_plain = K_PATH_CACHE
.'__tcpdf_imgmask_plain_'.$filehash;
7238 // create temp alpha file
7239 $tempfile_alpha = K_PATH_CACHE
.'__tcpdf_imgmask_alpha_'.$filehash;
7242 // ImageMagick extension
7243 if (($parsed === false) AND extension_loaded('imagick')) {
7245 // ImageMagick library
7246 $img = new Imagick();
7247 $img->readImage($file);
7248 // clone image object
7249 $imga = TCPDF_STATIC
::objclone($img);
7250 // extract alpha channel
7251 if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) {
7252 $img->setImageAlphaChannel(Imagick
::ALPHACHANNEL_EXTRACT
);
7254 $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
7255 $img->negateImage(true);
7257 $img->setImageFormat('png');
7258 $img->writeImage($tempfile_alpha);
7259 // remove alpha channel
7260 if (method_exists($imga, 'setImageMatte')) {
7261 $imga->setImageMatte(false);
7263 $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
7265 $imga->setImageFormat('png');
7266 $imga->writeImage($tempfile_plain);
7268 } catch (Exception
$e) {
7269 // Imagemagick fails, try with GD
7270 $parse_error = 'Imagick library error: '.$e->getMessage();
7274 if (($parsed === false) AND function_exists('imagecreatefrompng')) {
7277 $img = imagecreatefrompng($file);
7278 $imgalpha = imagecreate($wpx, $hpx);
7279 // generate gray scale palette (0 -> 255)
7280 for ($c = 0; $c < 256; ++
$c) {
7281 ImageColorAllocate($imgalpha, $c, $c, $c);
7283 // extract alpha channel
7284 for ($xpx = 0; $xpx < $wpx; ++
$xpx) {
7285 for ($ypx = 0; $ypx < $hpx; ++
$ypx) {
7286 $color = imagecolorat($img, $xpx, $ypx);
7287 // get and correct gamma color
7288 $alpha = $this->getGDgamma($img, $color);
7289 imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
7292 imagepng($imgalpha, $tempfile_alpha);
7293 imagedestroy($imgalpha);
7294 // extract image without alpha channel
7295 $imgplain = imagecreatetruecolor($wpx, $hpx);
7296 imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
7297 imagepng($imgplain, $tempfile_plain);
7298 imagedestroy($imgplain);
7300 } catch (Exception
$e) {
7302 $parse_error = 'GD library error: '.$e->getMessage();
7305 if ($parsed === false) {
7306 if (empty($parse_error)) {
7307 $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
7309 $this->Error($parse_error);
7313 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7314 // embed image, masked with previously embedded mask
7315 $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7316 // remove temp files
7317 unlink($tempfile_alpha);
7318 unlink($tempfile_plain);
7322 * Get the GD-corrected PNG gamma value from alpha color
7323 * @param $img (int) GD image Resource ID.
7324 * @param $c (int) alpha color
7326 * @since 4.3.007 (2008-12-04)
7328 protected function getGDgamma($img, $c) {
7329 if (!isset($this->gdgammacache
['#'.$c])) {
7330 $colors = imagecolorsforindex($img, $c);
7331 // GD alpha is only 7 bit (0 -> 127)
7332 $this->gdgammacache
['#'.$c] = (((127 - $colors['alpha']) / 127) * 255);
7334 $this->gdgammacache
['#'.$c] = (pow(($this->gdgammacache
['#'.$c] / 255), 2.2) * 255);
7335 // store the latest values on cache to improve performances
7336 if (count($this->gdgammacache
) > 8) {
7337 // remove one element from the cache array
7338 array_shift($this->gdgammacache
);
7341 return $this->gdgammacache
['#'.$c];
7345 * Performs a line break.
7346 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
7347 * @param $h (float) The height of the break. By default, the value equals the height of the last printed cell.
7348 * @param $cell (boolean) if true add the current left (or right o for RTL) padding to the X coordinate
7353 public function Ln($h='', $cell=false) {
7354 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'])) {
7355 // revove vertical space from the top of the column
7360 $cellpadding = $this->cell_padding
['R'];
7362 $cellpadding = $this->cell_padding
['L'];
7368 $this->x
= $this->w
- $this->rMargin
- $cellpadding;
7370 $this->x
= $this->lMargin +
$cellpadding;
7372 if (is_string($h)) {
7373 $this->y +
= $this->lasth
;
7377 $this->newline
= true;
7381 * Returns the relative X value of current position.
7382 * The value is relative to the left border for LTR languages and to the right border for RTL languages.
7386 * @see SetX(), GetY(), SetY()
7388 public function GetX() {
7391 return ($this->w
- $this->x
);
7398 * Returns the absolute X value of current position.
7402 * @see SetX(), GetY(), SetY()
7404 public function GetAbsX() {
7409 * Returns the ordinate of the current position.
7413 * @see SetY(), GetX(), SetX()
7415 public function GetY() {
7420 * Defines the abscissa of the current position.
7421 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
7422 * @param $x (float) The value of the abscissa in user units.
7423 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7426 * @see GetX(), GetY(), SetY(), SetXY()
7428 public function SetX($x, $rtloff=false) {
7430 if (!$rtloff AND $this->rtl
) {
7432 $this->x
= $this->w
- $x;
7440 $this->x
= $this->w +
$x;
7446 if ($this->x
> $this->w
) {
7447 $this->x
= $this->w
;
7452 * Moves the current abscissa back to the left margin and sets the ordinate.
7453 * If the passed value is negative, it is relative to the bottom of the page.
7454 * @param $y (float) The value of the ordinate in user units.
7455 * @param $resetx (bool) if true (default) reset the X position.
7456 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7459 * @see GetX(), GetY(), SetY(), SetXY()
7461 public function SetY($y, $resetx=true, $rtloff=false) {
7465 if (!$rtloff AND $this->rtl
) {
7466 $this->x
= $this->w
- $this->rMargin
;
7468 $this->x
= $this->lMargin
;
7474 $this->y
= $this->h +
$y;
7479 if ($this->y
> $this->h
) {
7480 $this->y
= $this->h
;
7485 * Defines the abscissa and ordinate of the current position.
7486 * If the passed values are negative, they are relative respectively to the right and bottom of the page.
7487 * @param $x (float) The value of the abscissa.
7488 * @param $y (float) The value of the ordinate.
7489 * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7492 * @see SetX(), SetY()
7494 public function SetXY($x, $y, $rtloff=false) {
7495 $this->SetY($y, false, $rtloff);
7496 $this->SetX($x, $rtloff);
7500 * Set the absolute X coordinate of the current pointer.
7501 * @param $x (float) The value of the abscissa in user units.
7503 * @since 5.9.186 (2012-09-13)
7504 * @see setAbsX(), setAbsY(), SetAbsXY()
7506 public function SetAbsX($x) {
7507 $this->x
= floatval($x);
7511 * Set the absolute Y coordinate of the current pointer.
7512 * @param $y (float) (float) The value of the ordinate in user units.
7514 * @since 5.9.186 (2012-09-13)
7515 * @see setAbsX(), setAbsY(), SetAbsXY()
7517 public function SetAbsY($y) {
7518 $this->y
= floatval($y);
7522 * Set the absolute X and Y coordinates of the current pointer.
7523 * @param $x (float) The value of the abscissa in user units.
7524 * @param $y (float) (float) The value of the ordinate in user units.
7526 * @since 5.9.186 (2012-09-13)
7527 * @see setAbsX(), setAbsY(), SetAbsXY()
7529 public function SetAbsXY($x, $y) {
7535 * Send the document to a given destination: string, local file or browser.
7536 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
7537 * The method first calls Close() if necessary to terminate the document.
7538 * @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.
7539 * @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>
7544 public function Output($name='doc.pdf', $dest='I') {
7545 //Output PDF to some destination
7546 //Finish document if necessary
7547 if ($this->state
< 3) {
7550 //Normalize parameters
7551 if (is_bool($dest)) {
7552 $dest = $dest ?
'D' : 'F';
7554 $dest = strtoupper($dest);
7555 if ($dest[0] != 'F') {
7556 $name = preg_replace('/[\s]+/', '_', $name);
7557 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
7560 // *** apply digital signature to the document ***
7561 // get the document content
7562 $pdfdoc = $this->getBuffer();
7563 // remove last newline
7564 $pdfdoc = substr($pdfdoc, 0, -1);
7565 // Remove the original buffer
7566 if (isset($this->diskcache
) AND $this->diskcache
) {
7567 // remove buffer file from cache
7568 unlink($this->buffer
);
7570 unset($this->buffer
);
7571 // remove filler space
7572 $byterange_string_len = strlen(TCPDF_STATIC
::$byterange_string);
7573 // define the ByteRange
7574 $byte_range = array();
7576 $byte_range[1] = strpos($pdfdoc, TCPDF_STATIC
::$byterange_string) +
$byterange_string_len +
10;
7577 $byte_range[2] = $byte_range[1] +
$this->signature_max_length +
2;
7578 $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
7579 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
7580 // replace the ByteRange
7581 $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
7582 $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
7583 $pdfdoc = str_replace(TCPDF_STATIC
::$byterange_string, $byterange, $pdfdoc);
7584 // write the document to a temporary folder
7585 $tempdoc = TCPDF_STATIC
::getObjFilename('doc');
7586 $f = fopen($tempdoc, 'wb');
7588 $this->Error('Unable to create temporary file: '.$tempdoc);
7590 $pdfdoc_length = strlen($pdfdoc);
7591 fwrite($f, $pdfdoc, $pdfdoc_length);
7593 // get digital signature via openssl library
7594 $tempsign = TCPDF_STATIC
::getObjFilename('sig');
7595 if (empty($this->signature_data
['extracerts'])) {
7596 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data
['signcert'], array($this->signature_data
['privkey'], $this->signature_data
['password']), array(), PKCS7_BINARY | PKCS7_DETACHED
);
7598 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']);
7602 $signature = file_get_contents($tempsign);
7604 // extract signature
7605 $signature = substr($signature, $pdfdoc_length);
7606 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") +
13));
7607 $tmparr = explode("\n\n", $signature);
7608 $signature = $tmparr[1];
7611 $signature = base64_decode(trim($signature));
7612 // convert signature to hex
7613 $signature = current(unpack('H*', $signature));
7614 $signature = str_pad($signature, $this->signature_max_length
, '0');
7615 // disable disk caching
7616 $this->diskcache
= false;
7617 // Add signature to the document
7618 $this->buffer
= substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
7619 $this->bufferlen
= strlen($this->buffer
);
7623 // Send PDF to the standard output
7624 if (ob_get_contents()) {
7625 $this->Error('Some data has already been output, can\'t send PDF file');
7627 if (php_sapi_name() != 'cli') {
7628 // send output to a browser
7629 header('Content-Type: application/pdf');
7630 if (headers_sent()) {
7631 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7633 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7634 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7635 header('Pragma: public');
7636 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7637 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7638 header('Content-Disposition: inline; filename="'.basename($name).'"');
7639 TCPDF_STATIC
::sendOutputData($this->getBuffer(), $this->bufferlen
);
7641 echo $this->getBuffer();
7646 // download PDF as file
7647 if (ob_get_contents()) {
7648 $this->Error('Some data has already been output, can\'t send PDF file');
7650 header('Content-Description: File Transfer');
7651 if (headers_sent()) {
7652 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7654 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7655 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7656 header('Pragma: public');
7657 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7658 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7659 // force download dialog
7660 if (strpos(php_sapi_name(), 'cgi') === false) {
7661 header('Content-Type: application/force-download');
7662 header('Content-Type: application/octet-stream', false);
7663 header('Content-Type: application/download', false);
7664 header('Content-Type: application/pdf', false);
7666 header('Content-Type: application/pdf');
7668 // use the Content-Disposition header to supply a recommended filename
7669 header('Content-Disposition: attachment; filename="'.basename($name).'"');
7670 header('Content-Transfer-Encoding: binary');
7671 TCPDF_STATIC
::sendOutputData($this->getBuffer(), $this->bufferlen
);
7677 // save PDF to a local file
7678 if ($this->diskcache
) {
7679 copy($this->buffer
, $name);
7681 $f = fopen($name, 'wb');
7683 $this->Error('Unable to create output file: '.$name);
7685 fwrite($f, $this->getBuffer(), $this->bufferlen
);
7688 if ($dest == 'FI') {
7689 // send headers to browser
7690 header('Content-Type: application/pdf');
7691 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7692 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7693 header('Pragma: public');
7694 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7695 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7696 header('Content-Disposition: inline; filename="'.basename($name).'"');
7697 TCPDF_STATIC
::sendOutputData(file_get_contents($name), filesize($name));
7698 } elseif ($dest == 'FD') {
7699 // send headers to browser
7700 if (ob_get_contents()) {
7701 $this->Error('Some data has already been output, can\'t send PDF file');
7703 header('Content-Description: File Transfer');
7704 if (headers_sent()) {
7705 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7707 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7708 header('Pragma: public');
7709 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7710 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7711 // force download dialog
7712 if (strpos(php_sapi_name(), 'cgi') === false) {
7713 header('Content-Type: application/force-download');
7714 header('Content-Type: application/octet-stream', false);
7715 header('Content-Type: application/download', false);
7716 header('Content-Type: application/pdf', false);
7718 header('Content-Type: application/pdf');
7720 // use the Content-Disposition header to supply a recommended filename
7721 header('Content-Disposition: attachment; filename="'.basename($name).'"');
7722 header('Content-Transfer-Encoding: binary');
7723 TCPDF_STATIC
::sendOutputData(file_get_contents($name), filesize($name));
7728 // return PDF as base64 mime multi-part email attachment (RFC 2045)
7729 $retval = 'Content-Type: application/pdf;'."\r\n";
7730 $retval .= ' name="'.$name.'"'."\r\n";
7731 $retval .= 'Content-Transfer-Encoding: base64'."\r\n";
7732 $retval .= 'Content-Disposition: attachment;'."\r\n";
7733 $retval .= ' filename="'.$name.'"'."\r\n\r\n";
7734 $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
7738 // returns PDF as a string
7739 return $this->getBuffer();
7742 $this->Error('Incorrect output destination: '.$dest);
7749 * Unset all class variables except the following critical variables.
7750 * @param $destroyall (boolean) if true destroys all class variables, otherwise preserves critical variables.
7751 * @param $preserve_objcopy (boolean) if true preserves the objcopy variable
7753 * @since 4.5.016 (2009-02-24)
7755 public function _destroy($destroyall=false, $preserve_objcopy=false) {
7756 if ($destroyall AND isset($this->diskcache
) AND $this->diskcache
AND (!$preserve_objcopy) AND (!TCPDF_STATIC
::empty_string($this->buffer
))) {
7757 // remove buffer file from cache
7758 unlink($this->buffer
);
7760 if ($destroyall AND !empty($this->cached_files
)) {
7761 // remove cached files
7762 foreach ($this->cached_files
as $cachefile) {
7763 if (is_file($cachefile)) {
7767 unset($this->cached_files
);
7769 foreach (array_keys(get_object_vars($this)) as $val) {
7770 if ($destroyall OR (
7771 ($val != 'internal_encoding')
7772 AND ($val != 'state')
7773 AND ($val != 'bufferlen')
7774 AND ($val != 'buffer')
7775 AND ($val != 'diskcache')
7776 AND ($val != 'cached_files')
7777 AND ($val != 'sign')
7778 AND ($val != 'signature_data')
7779 AND ($val != 'signature_max_length')
7780 AND ($val != 'byterange_string')
7782 if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) {
7790 * Check for locale-related bug
7793 protected function _dochecks() {
7794 //Check for locale-related bug
7796 $this->Error('Don\'t alter the locale before including class file');
7798 //Check for decimal separator
7799 if (sprintf('%.1F', 1.0) != '1.0') {
7800 setlocale(LC_NUMERIC
, 'C');
7805 * Return an array containing variations for the basic page number alias.
7806 * @param $a (string) Base alias.
7807 * @return array of page number aliases
7810 protected function getInternalPageNumberAliases($a= '') {
7812 // build array of Unicode + ASCII variants (the order is important)
7813 $alias = array('u' => array(), 'a' => array());
7815 $alias['u'][] = TCPDF_STATIC
::_escape($u);
7816 if ($this->isunicode
) {
7817 $alias['u'][] = TCPDF_STATIC
::_escape(TCPDF_FONTS
::UTF8ToLatin1($u, $this->isunicode
, $this->CurrentFont
));
7818 $alias['u'][] = TCPDF_STATIC
::_escape(TCPDF_FONTS
::utf8StrRev($u, false, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
));
7819 $alias['a'][] = TCPDF_STATIC
::_escape(TCPDF_FONTS
::UTF8ToLatin1($a, $this->isunicode
, $this->CurrentFont
));
7820 $alias['a'][] = TCPDF_STATIC
::_escape(TCPDF_FONTS
::utf8StrRev($a, false, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
));
7822 $alias['a'][] = TCPDF_STATIC
::_escape($a);
7827 * Return an array containing all internal page aliases.
7828 * @return array of page number aliases
7831 protected function getAllInternalPageNumberAliases() {
7832 $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);
7834 foreach($basic_alias as $k => $a) {
7835 $pnalias[$k] = $this->getInternalPageNumberAliases($a);
7841 * Replace right shift page number aliases with spaces to correct right alignment.
7842 * This works perfectly only when using monospaced fonts.
7843 * @param $page (string) Page content.
7844 * @param $aliases (array) Array of page aliases.
7845 * @param $diff (int) initial difference to add.
7846 * @return replaced page content.
7849 protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
7850 foreach ($aliases as $type => $alias) {
7851 foreach ($alias as $a) {
7852 // find position of compensation factor
7853 $startnum = (strpos($a, ':') +
1);
7854 $a = substr($a, 0, $startnum);
7855 if (($pos = strpos($page, $a)) !== false) {
7857 $endnum = strpos($page, '}', $pos);
7858 // string to be replaced
7859 $aa = substr($page, $pos, ($endnum - $pos +
1));
7860 // get compensation factor
7861 $ratio = substr($page, ($pos +
$startnum), ($endnum - $pos - $startnum));
7862 $ratio = preg_replace('/[^0-9\.]/', '', $ratio);
7863 $ratio = floatval($ratio);
7865 $chrdiff = floor(($diff +
12) * $ratio);
7866 $shift = str_repeat(' ', $chrdiff);
7867 $shift = TCPDF_FONTS
::UTF8ToUTF16BE($shift, false, $this->isunicode
, $this->CurrentFont
);
7869 $chrdiff = floor(($diff +
11) * $ratio);
7870 $shift = str_repeat(' ', $chrdiff);
7872 $page = str_replace($aa, $shift, $page);
7880 * Set page boxes to be included on page descriptions.
7881 * @param $boxes (array) Array of page boxes to set on document: ('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox').
7884 protected function setPageBoxTypes($boxes) {
7885 $this->page_boxes
= array();
7886 foreach ($boxes as $box) {
7887 if (in_array($box, TCPDF_STATIC
::$pageboxes)) {
7888 $this->page_boxes
[] = $box;
7894 * Output pages (and replace page number aliases).
7897 protected function _putpages() {
7898 $filter = ($this->compress
) ?
'/Filter /FlateDecode ' : '';
7899 // get internal aliases for page numbers
7900 $pnalias = $this->getAllInternalPageNumberAliases();
7901 $num_pages = $this->numpages
;
7902 $ptpa = TCPDF_STATIC
::formatPageNumber(($this->starting_page_number +
$num_pages - 1));
7903 $ptpu = TCPDF_FONTS
::UTF8ToUTF16BE($ptpa, false, $this->isunicode
, $this->CurrentFont
);
7904 $ptp_num_chars = $this->GetNumChars($ptpa);
7910 for ($n = 1; $n <= $num_pages; ++
$n) {
7912 $temppage = $this->getPageBuffer($n);
7913 $pagelen = strlen($temppage);
7914 // set replacements for total pages number
7915 $pnpa = TCPDF_STATIC
::formatPageNumber(($this->starting_page_number +
$n - 1));
7916 $pnpu = TCPDF_FONTS
::UTF8ToUTF16BE($pnpa, false, $this->isunicode
, $this->CurrentFont
);
7917 $pnp_num_chars = $this->GetNumChars($pnpa);
7918 $pdiff = 0; // difference used for right shift alignment of page numbers
7919 $gdiff = 0; // difference used for right shift alignment of page group numbers
7920 if (!empty($this->pagegroups
)) {
7921 if (isset($this->newpagegroup
[$n])) {
7924 $ptga = TCPDF_STATIC
::formatPageNumber($this->pagegroups
[$groupnum]);
7925 $ptgu = TCPDF_FONTS
::UTF8ToUTF16BE($ptga, false, $this->isunicode
, $this->CurrentFont
);
7926 $ptg_num_chars = $this->GetNumChars($ptga);
7929 $pnga = TCPDF_STATIC
::formatPageNumber($pagegroupnum);
7930 $pngu = TCPDF_FONTS
::UTF8ToUTF16BE($pnga, false, $this->isunicode
, $this->CurrentFont
);
7931 $png_num_chars = $this->GetNumChars($pnga);
7932 // replace page numbers
7934 $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
7935 $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
7936 $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
7937 $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
7938 list($temppage, $gdiff) = TCPDF_STATIC
::replacePageNumAliases($temppage, $replace, $gdiff);
7940 // replace page numbers
7942 $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
7943 $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
7944 $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
7945 $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
7946 list($temppage, $pdiff) = TCPDF_STATIC
::replacePageNumAliases($temppage, $replace, $pdiff);
7947 // replace right shift alias
7948 $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
7949 // replace EPS marker
7950 $temppage = str_replace($this->epsmarker
, '', $temppage);
7952 $this->page_obj_id
[$n] = $this->_newobj();
7954 $out .= ' /Type /Page';
7955 $out .= ' /Parent 1 0 R';
7956 $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp
);
7957 $out .= ' /Resources 2 0 R';
7958 foreach ($this->page_boxes
as $box) {
7960 $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']);
7962 if (isset($this->pagedim
[$n]['BoxColorInfo']) AND !empty($this->pagedim
[$n]['BoxColorInfo'])) {
7963 $out .= ' /BoxColorInfo <<';
7964 foreach ($this->page_boxes
as $box) {
7965 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box])) {
7966 $out .= ' /'.$box.' <<';
7967 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box]['C'])) {
7968 $color = $this->pagedim
[$n]['BoxColorInfo'][$box]['C'];
7970 $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
7973 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box]['W'])) {
7974 $out .= ' /W '.($this->pagedim
[$n]['BoxColorInfo'][$box]['W'] * $this->k
);
7976 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box]['S'])) {
7977 $out .= ' /S /'.$this->pagedim
[$n]['BoxColorInfo'][$box]['S'];
7979 if (isset($this->pagedim
[$n]['BoxColorInfo'][$box]['D'])) {
7980 $dashes = $this->pagedim
[$n]['BoxColorInfo'][$box]['D'];
7982 foreach ($dashes as $dash) {
7983 $out .= sprintf(' %F', ($dash * $this->k
));
7992 $out .= ' /Contents '.($this->n +
1).' 0 R';
7993 $out .= ' /Rotate '.$this->pagedim
[$n]['Rotate'];
7994 if (!$this->pdfa_mode
) {
7995 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
7997 if (isset($this->pagedim
[$n]['trans']) AND !empty($this->pagedim
[$n]['trans'])) {
7999 if (isset($this->pagedim
[$n]['trans']['Dur'])) {
8000 $out .= ' /Dur '.$this->pagedim
[$n]['trans']['Dur'];
8002 $out .= ' /Trans <<';
8003 $out .= ' /Type /Trans';
8004 if (isset($this->pagedim
[$n]['trans']['S'])) {
8005 $out .= ' /S /'.$this->pagedim
[$n]['trans']['S'];
8007 if (isset($this->pagedim
[$n]['trans']['D'])) {
8008 $out .= ' /D '.$this->pagedim
[$n]['trans']['D'];
8010 if (isset($this->pagedim
[$n]['trans']['Dm'])) {
8011 $out .= ' /Dm /'.$this->pagedim
[$n]['trans']['Dm'];
8013 if (isset($this->pagedim
[$n]['trans']['M'])) {
8014 $out .= ' /M /'.$this->pagedim
[$n]['trans']['M'];
8016 if (isset($this->pagedim
[$n]['trans']['Di'])) {
8017 $out .= ' /Di '.$this->pagedim
[$n]['trans']['Di'];
8019 if (isset($this->pagedim
[$n]['trans']['SS'])) {
8020 $out .= ' /SS '.$this->pagedim
[$n]['trans']['SS'];
8022 if (isset($this->pagedim
[$n]['trans']['B'])) {
8023 $out .= ' /B '.$this->pagedim
[$n]['trans']['B'];
8027 $out .= $this->_getannotsrefs($n);
8028 $out .= ' /PZ '.$this->pagedim
[$n]['PZ'];
8030 $out .= "\n".'endobj';
8033 $p = ($this->compress
) ?
gzcompress($temppage) : $temppage;
8035 $p = $this->_getrawstream($p);
8036 $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
8037 if ($this->diskcache
) {
8038 // remove temporary files
8039 unlink($this->pages
[$n]);
8043 $out = $this->_getobj(1)."\n";
8044 $out .= '<< /Type /Pages /Kids [';
8045 foreach($this->page_obj_id
as $page_obj) {
8046 $out .= ' '.$page_obj.' 0 R';
8048 $out .= ' ] /Count '.$num_pages.' >>';
8049 $out .= "\n".'endobj';
8054 * Output references to page annotations
8055 * @param $n (int) page number
8057 * @author Nicola Asuni
8058 * @since 4.7.000 (2008-08-29)
8061 protected function _putannotsrefs($n) {
8062 $this->_out($this->_getannotsrefs($n));
8066 * Get references to page annotations.
8067 * @param $n (int) page number
8070 * @author Nicola Asuni
8071 * @since 5.0.010 (2010-05-17)
8073 protected function _getannotsrefs($n) {
8074 if (!(isset($this->PageAnnots
[$n]) OR ($this->sign
AND isset($this->signature_data
['cert_type'])))) {
8077 $out = ' /Annots [';
8078 if (isset($this->PageAnnots
[$n])) {
8079 foreach ($this->PageAnnots
[$n] as $key => $val) {
8080 if (!in_array($val['n'], $this->radio_groups
)) {
8081 $out .= ' '.$val['n'].' 0 R';
8084 // add radiobutton groups
8085 if (isset($this->radiobutton_groups
[$n])) {
8086 foreach ($this->radiobutton_groups
[$n] as $key => $data) {
8087 if (isset($data['n'])) {
8088 $out .= ' '.$data['n'].' 0 R';
8093 if ($this->sign
AND ($n == $this->signature_appearance
['page']) AND isset($this->signature_data
['cert_type'])) {
8094 // set reference for signature object
8095 $out .= ' '.$this->sig_obj_id
.' 0 R';
8097 if (!empty($this->empty_signature_appearance
)) {
8098 foreach ($this->empty_signature_appearance
as $esa) {
8099 if ($esa['page'] == $n) {
8100 // set reference for empty signature objects
8101 $out .= ' '.$esa['objid'].' 0 R';
8110 * Output annotations objects for all pages.
8111 * !!! THIS METHOD IS NOT YET COMPLETED !!!
8112 * See section 12.5 of PDF 32000_2008 reference.
8114 * @author Nicola Asuni
8115 * @since 4.0.018 (2008-08-06)
8117 protected function _putannotsobjs() {
8118 // reset object counter
8119 for ($n=1; $n <= $this->numpages
; ++
$n) {
8120 if (isset($this->PageAnnots
[$n])) {
8121 // set page annotations
8122 foreach ($this->PageAnnots
[$n] as $key => $pl) {
8123 $annot_obj_id = $this->PageAnnots
[$n][$key]['n'];
8124 // create annotation object for grouping radiobuttons
8125 if (isset($this->radiobutton_groups
[$n][$pl['txt']]) AND is_array($this->radiobutton_groups
[$n][$pl['txt']])) {
8126 $radio_button_obj_id = $this->radiobutton_groups
[$n][$pl['txt']]['n'];
8128 $annots .= ' /Type /Annot';
8129 $annots .= ' /Subtype /Widget';
8130 $annots .= ' /Rect [0 0 0 0]';
8131 if ($this->radiobutton_groups
[$n][$pl['txt']]['#readonly#']) {
8133 $annots .= ' /F 68';
8134 $annots .= ' /Ff 49153';
8136 $annots .= ' /F 4'; // default print for PDF/A
8137 $annots .= ' /Ff 49152';
8139 $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
8140 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8141 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id);
8143 $annots .= ' /FT /Btn';
8144 $annots .= ' /Kids [';
8146 foreach ($this->radiobutton_groups
[$n][$pl['txt']] as $key => $data) {
8147 if (isset($data['kid'])) {
8148 $annots .= ' '.$data['kid'].' 0 R';
8149 if ($data['def'] !== 'Off') {
8150 $defval = $data['def'];
8155 if (!empty($defval)) {
8156 $annots .= ' /V /'.$defval;
8159 $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
8160 $this->form_obj_id
[] = $radio_button_obj_id;
8161 // store object id to be used on Parent entry of Kids
8162 $this->radiobutton_groups
[$n][$pl['txt']] = $radio_button_obj_id;
8165 $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER
);
8166 $a = $pl['x'] * $this->k
;
8167 $b = $this->pagedim
[$n]['h'] - (($pl['y'] +
$pl['h']) * $this->k
);
8168 $c = $pl['w'] * $this->k
;
8169 $d = $pl['h'] * $this->k
;
8170 $rect = sprintf('%F %F %F %F', $a, $b, $a+
$c, $b+
$d);
8171 // create new annotation object
8172 $annots = '<</Type /Annot';
8173 $annots .= ' /Subtype /'.$pl['opt']['subtype'];
8174 $annots .= ' /Rect ['.$rect.']';
8175 $ft = array('Btn', 'Tx', 'Ch', 'Sig');
8176 if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
8177 $annots .= ' /FT /'.$pl['opt']['ft'];
8180 $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
8181 $annots .= ' /P '.$this->page_obj_id
[$n].' 0 R';
8182 $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
8183 $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp
);
8184 if (isset($pl['opt']['f'])) {
8186 if (is_array($pl['opt']['f'])) {
8187 foreach ($pl['opt']['f'] as $f) {
8188 switch (strtolower($f)) {
8221 case 'togglenoview': {
8225 case 'lockedcontents': {
8235 $fval = intval($pl['opt']['f']);
8240 if ($this->pdfa_mode
) {
8241 // force print flag for PDF/A mode
8244 $annots .= ' /F '.intval($fval);
8245 if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
8246 $annots .= ' /AS /'.$pl['opt']['as'];
8248 if (isset($pl['opt']['ap'])) {
8249 // appearance stream
8250 $annots .= ' /AP <<';
8251 if (is_array($pl['opt']['ap'])) {
8252 foreach ($pl['opt']['ap'] as $apmode => $apdef) {
8253 // $apmode can be: n = normal; r = rollover; d = down;
8254 $annots .= ' /'.strtoupper($apmode);
8255 if (is_array($apdef)) {
8257 foreach ($apdef as $apstate => $stream) {
8258 // reference to XObject that define the appearance for this mode-state
8259 $apsobjid = $this->_putAPXObject($c, $d, $stream);
8260 $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
8264 // reference to XObject that define the appearance for this mode
8265 $apsobjid = $this->_putAPXObject($c, $d, $apdef);
8266 $annots .= ' '.$apsobjid.' 0 R';
8270 $annots .= $pl['opt']['ap'];
8274 if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
8275 $annots .= ' /BS <<';
8276 $annots .= ' /Type /Border';
8277 if (isset($pl['opt']['bs']['w'])) {
8278 $annots .= ' /W '.intval($pl['opt']['bs']['w']);
8280 $bstyles = array('S', 'D', 'B', 'I', 'U');
8281 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
8282 $annots .= ' /S /'.$pl['opt']['bs']['s'];
8284 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
8286 foreach ($pl['opt']['bs']['d'] as $cord) {
8287 $annots .= ' '.intval($cord);
8293 $annots .= ' /Border [';
8294 if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
8295 $annots .= intval($pl['opt']['border'][0]).' ';
8296 $annots .= intval($pl['opt']['border'][1]).' ';
8297 $annots .= intval($pl['opt']['border'][2]);
8298 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
8300 foreach ($pl['opt']['border'][3] as $dash) {
8301 $annots .= intval($dash).' ';
8310 if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
8311 $annots .= ' /BE <<';
8312 $bstyles = array('S', 'C');
8313 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) {
8314 $annots .= ' /S /'.$pl['opt']['bs']['s'];
8316 $annots .= ' /S /S';
8318 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
8319 $annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']);
8323 if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
8324 $annots .= ' /C '.TCPDF_COLORS
::getColorStringFromArray($pl['opt']['c']);
8326 //$annots .= ' /StructParent ';
8327 //$annots .= ' /OC ';
8328 $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
8329 if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
8330 // this is a markup type
8331 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8332 $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
8334 //$annots .= ' /Popup ';
8335 if (isset($pl['opt']['ca'])) {
8336 $annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca']));
8338 if (isset($pl['opt']['rc'])) {
8339 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8341 $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp
);
8342 //$annots .= ' /IRT ';
8343 if (isset($pl['opt']['subj'])) {
8344 $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
8346 //$annots .= ' /RT ';
8347 //$annots .= ' /IT ';
8348 //$annots .= ' /ExData ';
8350 $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
8352 switch (strtolower($pl['opt']['subtype'])) {
8354 if (isset($pl['opt']['open'])) {
8355 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ?
'true' : 'false');
8357 $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
8358 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8359 $annots .= ' /Name /'.$pl['opt']['name'];
8361 $annots .= ' /Name /Note';
8363 $statemodels = array('Marked', 'Review');
8364 if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
8365 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8367 $pl['opt']['statemodel'] = 'Marked';
8368 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8370 if ($pl['opt']['statemodel'] == 'Marked') {
8371 $states = array('Accepted', 'Unmarked');
8373 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
8375 if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
8376 $annots .= ' /State /'.$pl['opt']['state'];
8378 if ($pl['opt']['statemodel'] == 'Marked') {
8379 $annots .= ' /State /Unmarked';
8381 $annots .= ' /State /None';
8387 if (is_string($pl['txt'])) {
8388 if ($pl['txt'][0] == '#') {
8389 // internal destination
8390 $annots .= ' /Dest /'.TCPDF_STATIC
::encodeNameObject(substr($pl['txt'], 1));
8391 } elseif ($pl['txt'][0] == '%') {
8392 // embedded PDF file
8393 $filename = basename(substr($pl['txt'], 1));
8394 $annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles
[$filename]['a'].' >> >>';
8395 } elseif ($pl['txt'][0] == '*') {
8396 // embedded generic file
8397 $filename = basename(substr($pl['txt'], 1));
8398 $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});';
8399 $annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>';
8401 // external URI link
8402 $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
8404 } elseif (isset($this->links
[$pl['txt']])) {
8406 $l = $this->links
[$pl['txt']];
8407 if (isset($this->page_obj_id
[($l['p'])])) {
8408 $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
)));
8411 $hmodes = array('N', 'I', 'O', 'P');
8412 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
8413 $annots .= ' /H /'.$pl['opt']['h'];
8415 $annots .= ' /H /I';
8417 //$annots .= ' /PA ';
8418 //$annots .= ' /Quadpoints ';
8422 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8423 $annots .= ' /DA ('.$pl['opt']['da'].')';
8425 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8426 $annots .= ' /Q '.intval($pl['opt']['q']);
8428 if (isset($pl['opt']['rc'])) {
8429 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8431 if (isset($pl['opt']['ds'])) {
8432 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
8434 if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
8435 $annots .= ' /CL [';
8436 foreach ($pl['opt']['cl'] as $cl) {
8437 $annots .= sprintf('%F ', $cl * $this->k
);
8441 $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
8442 if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
8443 $annots .= ' /IT /'.$pl['opt']['it'];
8445 if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
8446 $l = $pl['opt']['rd'][0] * $this->k
;
8447 $r = $pl['opt']['rd'][1] * $this->k
;
8448 $t = $pl['opt']['rd'][2] * $this->k
;
8449 $b = $pl['opt']['rd'][3] * $this->k
;
8450 $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']';
8452 if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
8453 $annots .= ' /LE /'.$pl['opt']['le'];
8496 case 'fileattachment': {
8497 if ($this->pdfa_mode
) {
8498 // embedded files are not allowed in PDF/A mode
8501 if (!isset($pl['opt']['fs'])) {
8504 $filename = basename($pl['opt']['fs']);
8505 if (isset($this->embeddedfiles
[$filename]['f'])) {
8506 $annots .= ' /FS '.$this->embeddedfiles
[$filename]['f'].' 0 R';
8507 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
8508 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8509 $annots .= ' /Name /'.$pl['opt']['name'];
8511 $annots .= ' /Name /PushPin';
8513 // index (zero-based) of the annotation in the Annots array of this page
8514 $this->embeddedfiles
[$filename]['a'] = $key;
8519 if (!isset($pl['opt']['fs'])) {
8522 $filename = basename($pl['opt']['fs']);
8523 if (isset($this->embeddedfiles
[$filename]['f'])) {
8524 // ... TO BE COMPLETED ...
8525 // /R /C /B /E /CO /CP
8526 $annots .= ' /Sound '.$this->embeddedfiles
[$filename]['f'].' 0 R';
8527 $iconsapp = array('Speaker', 'Mic');
8528 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8529 $annots .= ' /Name /'.$pl['opt']['name'];
8531 $annots .= ' /Name /Speaker';
8540 $hmode = array('N', 'I', 'O', 'P', 'T');
8541 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
8542 $annots .= ' /H /'.$pl['opt']['h'];
8544 if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
8545 $annots .= ' /MK <<';
8546 if (isset($pl['opt']['mk']['r'])) {
8547 $annots .= ' /R '.$pl['opt']['mk']['r'];
8549 if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
8550 $annots .= ' /BC '.TCPDF_COLORS
::getColorStringFromArray($pl['opt']['mk']['bc']);
8552 if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
8553 $annots .= ' /BG '.TCPDF_COLORS
::getColorStringFromArray($pl['opt']['mk']['bg']);
8555 if (isset($pl['opt']['mk']['ca'])) {
8556 $annots .= ' /CA '.$pl['opt']['mk']['ca'];
8558 if (isset($pl['opt']['mk']['rc'])) {
8559 $annots .= ' /RC '.$pl['opt']['mk']['rc'];
8561 if (isset($pl['opt']['mk']['ac'])) {
8562 $annots .= ' /AC '.$pl['opt']['mk']['ac'];
8564 if (isset($pl['opt']['mk']['i'])) {
8565 $info = $this->getImageBuffer($pl['opt']['mk']['i']);
8566 if ($info !== false) {
8567 $annots .= ' /I '.$info['n'].' 0 R';
8570 if (isset($pl['opt']['mk']['ri'])) {
8571 $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
8572 if ($info !== false) {
8573 $annots .= ' /RI '.$info['n'].' 0 R';
8576 if (isset($pl['opt']['mk']['ix'])) {
8577 $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
8578 if ($info !== false) {
8579 $annots .= ' /IX '.$info['n'].' 0 R';
8582 if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
8583 $annots .= ' /IF <<';
8584 $if_sw = array('A', 'B', 'S', 'N');
8585 if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
8586 $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
8588 $if_s = array('A', 'P');
8589 if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
8590 $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
8592 if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
8593 $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
8595 if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
8596 $annots .= ' /FB true';
8600 if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
8601 $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
8605 // --- Entries for field dictionaries ---
8606 if (isset($this->radiobutton_groups
[$n][$pl['txt']])) {
8608 $annots .= ' /Parent '.$this->radiobutton_groups
[$n][$pl['txt']].' 0 R';
8610 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8611 $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
8613 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8614 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
8616 if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
8617 $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
8619 if (isset($pl['opt']['ff'])) {
8620 if (is_array($pl['opt']['ff'])) {
8621 // array of bit settings
8623 foreach($pl['opt']['ff'] as $val) {
8624 $flag +
= 1 << ($val - 1);
8627 $flag = intval($pl['opt']['ff']);
8629 $annots .= ' /Ff '.$flag;
8631 if (isset($pl['opt']['maxlen'])) {
8632 $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
8634 if (isset($pl['opt']['v'])) {
8636 if (is_array($pl['opt']['v'])) {
8637 foreach ($pl['opt']['v'] AS $optval) {
8638 if (is_float($optval)) {
8639 $optval = sprintf('%F', $optval);
8641 $annots .= ' '.$optval;
8644 $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
8647 if (isset($pl['opt']['dv'])) {
8649 if (is_array($pl['opt']['dv'])) {
8650 foreach ($pl['opt']['dv'] AS $optval) {
8651 if (is_float($optval)) {
8652 $optval = sprintf('%F', $optval);
8654 $annots .= ' '.$optval;
8657 $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
8660 if (isset($pl['opt']['rv'])) {
8662 if (is_array($pl['opt']['rv'])) {
8663 foreach ($pl['opt']['rv'] AS $optval) {
8664 if (is_float($optval)) {
8665 $optval = sprintf('%F', $optval);
8667 $annots .= ' '.$optval;
8670 $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
8673 if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
8674 $annots .= ' /A << '.$pl['opt']['a'].' >>';
8676 if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
8677 $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
8679 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8680 $annots .= ' /DA ('.$pl['opt']['da'].')';
8682 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8683 $annots .= ' /Q '.intval($pl['opt']['q']);
8685 if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
8686 $annots .= ' /Opt [';
8687 foreach($pl['opt']['opt'] AS $copt) {
8688 if (is_array($copt)) {
8689 $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
8691 $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
8696 if (isset($pl['opt']['ti'])) {
8697 $annots .= ' /TI '.intval($pl['opt']['ti']);
8699 if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
8701 foreach($pl['opt']['i'] AS $copt) {
8702 $annots .= intval($copt).' ';
8711 case 'printermark': {
8728 // create new annotation object
8729 $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
8730 if ($formfield AND !isset($this->radiobutton_groups
[$n][$pl['txt']])) {
8731 // store reference of form object
8732 $this->form_obj_id
[] = $annot_obj_id;
8736 } // end for each page
8740 * Put appearance streams XObject used to define annotation's appearance states.
8741 * @param $w (int) annotation width
8742 * @param $h (int) annotation height
8743 * @param $stream (string) appearance stream
8744 * @return int object ID
8746 * @since 4.8.001 (2009-09-09)
8748 protected function _putAPXObject($w=0, $h=0, $stream='') {
8749 $stream = trim($stream);
8750 $out = $this->_getobj()."\n";
8751 $this->xobjects
['AX'.$this->n
] = array('n' => $this->n
);
8753 $out .= ' /Type /XObject';
8754 $out .= ' /Subtype /Form';
8755 $out .= ' /FormType 1';
8756 if ($this->compress
) {
8757 $stream = gzcompress($stream);
8758 $out .= ' /Filter /FlateDecode';
8760 $rect = sprintf('%F %F', $w, $h);
8761 $out .= ' /BBox [0 0 '.$rect.']';
8762 $out .= ' /Matrix [1 0 0 1 0 0]';
8763 $out .= ' /Resources 2 0 R';
8764 $stream = $this->_getrawstream($stream);
8765 $out .= ' /Length '.strlen($stream);
8767 $out .= ' stream'."\n".$stream."\n".'endstream';
8768 $out .= "\n".'endobj';
8775 * @author Nicola Asuni
8778 protected function _putfonts() {
8780 foreach ($this->diffs
as $diff) {
8783 $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
8785 $mqr = TCPDF_STATIC
::get_mqr();
8786 TCPDF_STATIC
::set_mqr(false);
8787 foreach ($this->FontFiles
as $file => $info) {
8788 // search and get font file to embedd
8789 $fontfile = TCPDF_FONTS
::getFontFullPath($file, $info['fontdir']);
8790 if (!TCPDF_STATIC
::empty_string($fontfile)) {
8791 $font = file_get_contents($fontfile);
8792 $compressed = (substr($file, -2) == '.z');
8793 if ((!$compressed) AND (isset($info['length2']))) {
8794 $header = (ord($font[0]) == 128);
8796 // strip first binary header
8797 $font = substr($font, 6);
8799 if ($header AND (ord($font[$info['length1']]) == 128)) {
8800 // strip second binary header
8801 $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] +
6));
8803 } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
8806 $font = gzuncompress($font);
8808 // merge subset characters
8809 $subsetchars = array(); // used chars
8810 foreach ($info['fontkeys'] as $fontkey) {
8811 $fontinfo = $this->getFontBuffer($fontkey);
8812 $subsetchars +
= $fontinfo['subsetchars'];
8814 // rebuild a font subset
8815 $font = TCPDF_FONTS
::_getTrueTypeFontSubset($font, $subsetchars);
8816 // calculate new font length
8817 $info['length1'] = strlen($font);
8820 $font = gzcompress($font);
8824 $this->FontFiles
[$file]['n'] = $this->n
;
8825 $stream = $this->_getrawstream($font);
8826 $out = '<< /Length '.strlen($stream);
8828 $out .= ' /Filter /FlateDecode';
8830 $out .= ' /Length1 '.$info['length1'];
8831 if (isset($info['length2'])) {
8832 $out .= ' /Length2 '.$info['length2'].' /Length3 0';
8835 $out .= ' stream'."\n".$stream."\n".'endstream';
8836 $out .= "\n".'endobj';
8840 TCPDF_STATIC
::set_mqr($mqr);
8841 foreach ($this->fontkeys
as $k) {
8843 $font = $this->getFontBuffer($k);
8844 $type = $font['type'];
8845 $name = $font['name'];
8846 if ($type == 'core') {
8847 // standard core font
8848 $out = $this->_getobj($this->font_obj_ids
[$k])."\n";
8849 $out .= '<</Type /Font';
8850 $out .= ' /Subtype /Type1';
8851 $out .= ' /BaseFont /'.$name;
8852 $out .= ' /Name /F'.$font['i'];
8853 if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
8854 $out .= ' /Encoding /WinAnsiEncoding';
8856 if ($k == 'helvetica') {
8857 // add default font for annotations
8858 $this->annotation_fonts
[$k] = $font['i'];
8861 $out .= "\n".'endobj';
8863 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
8864 // additional Type1 or TrueType font
8865 $out = $this->_getobj($this->font_obj_ids
[$k])."\n";
8866 $out .= '<</Type /Font';
8867 $out .= ' /Subtype /'.$type;
8868 $out .= ' /BaseFont /'.$name;
8869 $out .= ' /Name /F'.$font['i'];
8870 $out .= ' /FirstChar 32 /LastChar 255';
8871 $out .= ' /Widths '.($this->n +
1).' 0 R';
8872 $out .= ' /FontDescriptor '.($this->n +
2).' 0 R';
8874 if (isset($font['diff'])) {
8875 $out .= ' /Encoding '.($nf +
$font['diff']).' 0 R';
8877 $out .= ' /Encoding /WinAnsiEncoding';
8881 $out .= "\n".'endobj';
8886 for ($i = 32; $i < 256; ++
$i) {
8887 if (isset($font['cw'][$i])) {
8888 $s .= $font['cw'][$i].' ';
8890 $s .= $font['dw'].' ';
8894 $s .= "\n".'endobj';
8898 $s = '<</Type /FontDescriptor /FontName /'.$name;
8899 foreach ($font['desc'] as $fdk => $fdv) {
8900 if (is_float($fdv)) {
8901 $fdv = sprintf('%F', $fdv);
8903 $s .= ' /'.$fdk.' '.$fdv.'';
8905 if (!TCPDF_STATIC
::empty_string($font['file'])) {
8906 $s .= ' /FontFile'.($type == 'Type1' ?
'' : '2').' '.$this->FontFiles
[$font['file']]['n'].' 0 R';
8909 $s .= "\n".'endobj';
8913 $mtd = '_put'.strtolower($type);
8914 if (!method_exists($this, $mtd)) {
8915 $this->Error('Unsupported font type: '.$type);
8923 * Adds unicode fonts.<br>
8924 * Based on PDF Reference 1.3 (section 5)
8925 * @param $font (array) font data
8927 * @author Nicola Asuni
8928 * @since 1.52.0.TC005 (2005-01-05)
8930 protected function _puttruetypeunicode($font) {
8932 if ($font['subset']) {
8933 // change name for font subsetting
8934 $subtag = sprintf('%06u', $font['i']);
8935 $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
8936 $fontname .= $subtag.'+';
8938 $fontname .= $font['name'];
8940 // A composite font composed of other fonts, organized hierarchically
8941 $out = $this->_getobj($this->font_obj_ids
[$font['fontkey']])."\n";
8942 $out .= '<< /Type /Font';
8943 $out .= ' /Subtype /Type0';
8944 $out .= ' /BaseFont /'.$fontname;
8945 $out .= ' /Name /F'.$font['i'];
8946 $out .= ' /Encoding /'.$font['enc'];
8947 $out .= ' /ToUnicode '.($this->n +
1).' 0 R';
8948 $out .= ' /DescendantFonts ['.($this->n +
2).' 0 R]';
8950 $out .= "\n".'endobj';
8952 // ToUnicode map for Identity-H
8953 $stream = TCPDF_FONT_DATA
::$uni_identity_h;
8956 $stream = ($this->compress
) ?
gzcompress($stream) : $stream;
8957 $filter = ($this->compress
) ?
'/Filter /FlateDecode ' : '';
8958 $stream = $this->_getrawstream($stream);
8959 $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
8961 // A CIDFont whose glyph descriptions are based on TrueType font technology
8962 $oid = $this->_newobj();
8963 $out = '<< /Type /Font';
8964 $out .= ' /Subtype /CIDFontType2';
8965 $out .= ' /BaseFont /'.$fontname;
8966 // A dictionary containing entries that define the character collection of the CIDFont.
8967 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
8968 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
8969 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
8970 $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
8971 $out .= ' /FontDescriptor '.($this->n +
1).' 0 R';
8972 $out .= ' /DW '.$font['dw']; // default width
8973 $out .= "\n".TCPDF_FONTS
::_putfontwidths($font, 0);
8974 if (isset($font['ctg']) AND (!TCPDF_STATIC
::empty_string($font['ctg']))) {
8975 $out .= "\n".'/CIDToGIDMap '.($this->n +
2).' 0 R';
8978 $out .= "\n".'endobj';
8981 // A font descriptor describing the CIDFont default metrics other than its glyph widths
8983 $out = '<< /Type /FontDescriptor';
8984 $out .= ' /FontName /'.$fontname;
8985 foreach ($font['desc'] as $key => $value) {
8986 if (is_float($value)) {
8987 $value = sprintf('%F', $value);
8989 $out .= ' /'.$key.' '.$value;
8992 if (!TCPDF_STATIC
::empty_string($font['file'])) {
8993 // A stream containing a TrueType font
8994 $out .= ' /FontFile2 '.$this->FontFiles
[$font['file']]['n'].' 0 R';
8995 $fontdir = $this->FontFiles
[$font['file']]['fontdir'];
8998 $out .= "\n".'endobj';
9000 if (isset($font['ctg']) AND (!TCPDF_STATIC
::empty_string($font['ctg']))) {
9002 // Embed CIDToGIDMap
9003 // A specification of the mapping from CIDs to glyph indices
9004 // search and get CTG font file to embedd
9005 $ctgfile = strtolower($font['ctg']);
9006 // search and get ctg font file to embedd
9007 $fontfile = TCPDF_FONTS
::getFontFullPath($ctgfile, $fontdir);
9008 if (TCPDF_STATIC
::empty_string($fontfile)) {
9009 $this->Error('Font file not found: '.$ctgfile);
9011 $stream = $this->_getrawstream(file_get_contents($fontfile));
9012 $out = '<< /Length '.strlen($stream).'';
9013 if (substr($fontfile, -2) == '.z') { // check file extension
9014 // Decompresses data encoded using the public-domain
9015 // zlib/deflate compression method, reproducing the
9016 // original text or binary data
9017 $out .= ' /Filter /FlateDecode';
9020 $out .= ' stream'."\n".$stream."\n".'endstream';
9021 $out .= "\n".'endobj';
9027 * Output CID-0 fonts.
9028 * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format
9029 * @param $font (array) font data
9031 * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
9032 * @since 3.2.000 (2008-06-23)
9034 protected function _putcidfont0($font) {
9036 if (!isset($font['cw'][1])) {
9039 if (isset($font['cidinfo']['uni2cid'])) {
9040 // convert unicode to cid.
9041 $uni2cid = $font['cidinfo']['uni2cid'];
9043 foreach ($font['cw'] as $uni => $width) {
9044 if (isset($uni2cid[$uni])) {
9045 $cw[($uni2cid[$uni] +
$cidoffset)] = $width;
9046 } elseif ($uni < 256) {
9048 } // else unknown character
9050 $font = array_merge($font, array('cw' => $cw));
9052 $name = $font['name'];
9053 $enc = $font['enc'];
9055 $longname = $name.'-'.$enc;
9059 $out = $this->_getobj($this->font_obj_ids
[$font['fontkey']])."\n";
9060 $out .= '<</Type /Font';
9061 $out .= ' /Subtype /Type0';
9062 $out .= ' /BaseFont /'.$longname;
9063 $out .= ' /Name /F'.$font['i'];
9065 $out .= ' /Encoding /'.$enc;
9067 $out .= ' /DescendantFonts ['.($this->n +
1).' 0 R]';
9069 $out .= "\n".'endobj';
9071 $oid = $this->_newobj();
9072 $out = '<</Type /Font';
9073 $out .= ' /Subtype /CIDFontType0';
9074 $out .= ' /BaseFont /'.$name;
9075 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9076 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9077 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9078 $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
9079 $out .= ' /FontDescriptor '.($this->n +
1).' 0 R';
9080 $out .= ' /DW '.$font['dw'];
9081 $out .= "\n".TCPDF_FONTS
::_putfontwidths($font, $cidoffset);
9083 $out .= "\n".'endobj';
9086 $s = '<</Type /FontDescriptor /FontName /'.$name;
9087 foreach ($font['desc'] as $k => $v) {
9088 if ($k != 'Style') {
9090 $v = sprintf('%F', $v);
9092 $s .= ' /'.$k.' '.$v.'';
9096 $s .= "\n".'endobj';
9104 protected function _putimages() {
9105 $filter = ($this->compress
) ?
'/Filter /FlateDecode ' : '';
9106 foreach ($this->imagekeys
as $file) {
9107 $info = $this->getImageBuffer($file);
9108 // set object for alternate images array
9109 if ((!$this->pdfa_mode
) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
9110 $altoid = $this->_newobj();
9112 foreach ($info['altimgs'] as $altimage) {
9113 if (isset($this->xobjects
['I'.$altimage[0]]['n'])) {
9114 $out .= ' << /Image '.$this->xobjects
['I'.$altimage[0]]['n'].' 0 R';
9115 $out .= ' /DefaultForPrinting';
9116 if ($altimage[1] === true) {
9125 $out .= "\n".'endobj';
9129 $oid = $this->_newobj();
9130 $this->xobjects
['I'.$info['i']] = array('n' => $oid);
9131 $this->setImageSubBuffer($file, 'n', $this->n
);
9132 $out = '<</Type /XObject';
9133 $out .= ' /Subtype /Image';
9134 $out .= ' /Width '.$info['w'];
9135 $out .= ' /Height '.$info['h'];
9136 if (array_key_exists('masked', $info)) {
9137 $out .= ' /SMask '.($this->n
- 1).' 0 R';
9141 if (isset($info['icc']) AND ($info['icc'] !== false)) {
9144 $out .= ' /ColorSpace [/ICCBased '.($this->n +
1).' 0 R]';
9145 } elseif ($info['cs'] == 'Indexed') {
9146 // Indexed Colour Space
9147 $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n +
1).' 0 R]';
9149 // Device Colour Space
9150 $out .= ' /ColorSpace /'.$info['cs'];
9152 if ($info['cs'] == 'DeviceCMYK') {
9153 $out .= ' /Decode [1 0 1 0 1 0 1 0]';
9155 $out .= ' /BitsPerComponent '.$info['bpc'];
9156 if (isset($altoid) AND ($altoid > 0)) {
9157 // reference to alternate images dictionary
9158 $out .= ' /Alternates '.$altoid.' 0 R';
9160 if (isset($info['exurl']) AND !empty($info['exurl'])) {
9162 $out .= ' /Length 0';
9163 $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
9164 if (isset($info['f'])) {
9165 $out .= ' /FFilter /'.$info['f'];
9168 $out .= ' stream'."\n".'endstream';
9170 if (isset($info['f'])) {
9171 $out .= ' /Filter /'.$info['f'];
9173 if (isset($info['parms'])) {
9174 $out .= ' '.$info['parms'];
9176 if (isset($info['trns']) AND is_array($info['trns'])) {
9178 $count_info = count($info['trns']);
9179 for ($i=0; $i < $count_info; ++
$i) {
9180 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
9182 $out .= ' /Mask ['.$trns.']';
9184 $stream = $this->_getrawstream($info['data']);
9185 $out .= ' /Length '.strlen($stream).' >>';
9186 $out .= ' stream'."\n".$stream."\n".'endstream';
9188 $out .= "\n".'endobj';
9191 // ICC colour profile
9193 $icc = ($this->compress
) ?
gzcompress($info['icc']) : $info['icc'];
9194 $icc = $this->_getrawstream($icc);
9195 $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9196 } elseif ($info['cs'] == 'Indexed') {
9199 $pal = ($this->compress
) ?
gzcompress($info['pal']) : $info['pal'];
9200 $pal = $this->_getrawstream($pal);
9201 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
9207 * Output Form XObjects Templates.
9208 * @author Nicola Asuni
9209 * @since 5.8.017 (2010-08-24)
9211 * @see startTemplate(), endTemplate(), printTemplate()
9213 protected function _putxobjects() {
9214 foreach ($this->xobjects
as $key => $data) {
9215 if (isset($data['outdata'])) {
9216 $stream = str_replace($this->epsmarker
, '', trim($data['outdata']));
9217 $out = $this->_getobj($data['n'])."\n";
9219 $out .= ' /Type /XObject';
9220 $out .= ' /Subtype /Form';
9221 $out .= ' /FormType 1';
9222 if ($this->compress
) {
9223 $stream = gzcompress($stream);
9224 $out .= ' /Filter /FlateDecode';
9226 $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
));
9227 $out .= ' /Matrix [1 0 0 1 0 0]';
9228 $out .= ' /Resources <<';
9229 $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9230 if (!$this->pdfa_mode
) {
9232 if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
9233 $out .= ' /ExtGState <<';
9234 foreach ($data['extgstates'] as $k => $extgstate) {
9235 if (isset($this->extgstates
[$k]['name'])) {
9236 $out .= ' /'.$this->extgstates
[$k]['name'];
9240 $out .= ' '.$this->extgstates
[$k]['n'].' 0 R';
9244 if (isset($data['gradients']) AND !empty($data['gradients'])) {
9247 foreach ($data['gradients'] as $id => $grad) {
9248 // gradient patterns
9249 $gp .= ' /p'.$id.' '.$this->gradients
[$id]['pattern'].' 0 R';
9250 // gradient shadings
9251 $gs .= ' /Sh'.$id.' '.$this->gradients
[$id]['id'].' 0 R';
9253 $out .= ' /Pattern <<'.$gp.' >>';
9254 $out .= ' /Shading <<'.$gs.' >>';
9258 if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
9259 $out .= ' /ColorSpace <<';
9260 foreach ($data['spot_colors'] as $name => $color) {
9261 $out .= ' /CS'.$color['i'].' '.$this->spot_colors
[$name]['n'].' 0 R';
9266 if (!empty($data['fonts'])) {
9267 $out .= ' /Font <<';
9268 foreach ($data['fonts'] as $fontkey => $fontid) {
9269 $out .= ' /F'.$fontid.' '.$this->font_obj_ids
[$fontkey].' 0 R';
9273 // images or nested xobjects
9274 if (!empty($data['images']) OR !empty($data['xobjects'])) {
9275 $out .= ' /XObject <<';
9276 foreach ($data['images'] as $imgid) {
9277 $out .= ' /I'.$imgid.' '.$this->xobjects
['I'.$imgid]['n'].' 0 R';
9279 foreach ($data['xobjects'] as $sub_id => $sub_objid) {
9280 $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
9284 $out .= ' >>'; //end resources
9285 if (isset($data['group']) AND ($data['group'] !== false)) {
9286 // set transparency group
9287 $out .= ' /Group << /Type /Group /S /Transparency';
9288 if (is_array($data['group'])) {
9289 if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
9290 $out .= ' /CS /'.$data['group']['CS'];
9292 if (isset($data['group']['I'])) {
9293 $out .= ' /I /'.($data['group']['I']===true?
'true':'false');
9295 if (isset($data['group']['K'])) {
9296 $out .= ' /K /'.($data['group']['K']===true?
'true':'false');
9301 $stream = $this->_getrawstream($stream, $data['n']);
9302 $out .= ' /Length '.strlen($stream);
9304 $out .= ' stream'."\n".$stream."\n".'endstream';
9305 $out .= "\n".'endobj';
9312 * Output Spot Colors Resources.
9314 * @since 4.0.024 (2008-09-12)
9316 protected function _putspotcolors() {
9317 foreach ($this->spot_colors
as $name => $color) {
9319 $this->spot_colors
[$name]['n'] = $this->n
;
9320 $out = '[/Separation /'.str_replace(' ', '#20', $name);
9321 $out .= ' /DeviceCMYK <<';
9322 $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
9323 $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
9324 $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
9325 $out .= "\n".'endobj';
9331 * Return XObjects Dictionary.
9332 * @return string XObjects dictionary
9334 * @since 5.8.014 (2010-08-23)
9336 protected function _getxobjectdict() {
9338 foreach ($this->xobjects
as $id => $objid) {
9339 $out .= ' /'.$id.' '.$objid['n'].' 0 R';
9345 * Output Resources Dictionary.
9348 protected function _putresourcedict() {
9349 $out = $this->_getobj(2)."\n";
9350 $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9351 $out .= ' /Font <<';
9352 foreach ($this->fontkeys
as $fontkey) {
9353 $font = $this->getFontBuffer($fontkey);
9354 $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
9357 $out .= ' /XObject <<';
9358 $out .= $this->_getxobjectdict();
9361 if (!empty($this->pdflayers
)) {
9362 $out .= ' /Properties <<';
9363 foreach ($this->pdflayers
as $layer) {
9364 $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
9368 if (!$this->pdfa_mode
) {
9370 if (isset($this->extgstates
) AND !empty($this->extgstates
)) {
9371 $out .= ' /ExtGState <<';
9372 foreach ($this->extgstates
as $k => $extgstate) {
9373 if (isset($extgstate['name'])) {
9374 $out .= ' /'.$extgstate['name'];
9378 $out .= ' '.$extgstate['n'].' 0 R';
9382 if (isset($this->gradients
) AND !empty($this->gradients
)) {
9385 foreach ($this->gradients
as $id => $grad) {
9386 // gradient patterns
9387 $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
9388 // gradient shadings
9389 $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
9391 $out .= ' /Pattern <<'.$gp.' >>';
9392 $out .= ' /Shading <<'.$gs.' >>';
9396 if (isset($this->spot_colors
) AND !empty($this->spot_colors
)) {
9397 $out .= ' /ColorSpace <<';
9398 foreach ($this->spot_colors
as $color) {
9399 $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
9404 $out .= "\n".'endobj';
9412 protected function _putresources() {
9413 $this->_putextgstates();
9416 $this->_putimages();
9417 $this->_putspotcolors();
9418 $this->_putshaders();
9419 $this->_putxobjects();
9420 $this->_putresourcedict();
9422 $this->_putEmbeddedFiles();
9423 $this->_putannotsobjs();
9424 $this->_putjavascript();
9425 $this->_putbookmarks();
9426 $this->_putencryption();
9430 * Adds some Metadata information (Document Information Dictionary)
9431 * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference)
9432 * @return int object id
9435 protected function _putinfo() {
9436 $oid = $this->_newobj();
9438 // store current isunicode value
9439 $prev_isunicode = $this->isunicode
;
9440 if ($this->docinfounicode
) {
9441 $this->isunicode
= true;
9443 if (!TCPDF_STATIC
::empty_string($this->title
)) {
9444 // The document's title.
9445 $out .= ' /Title '.$this->_textstring($this->title
, $oid);
9447 if (!TCPDF_STATIC
::empty_string($this->author
)) {
9448 // The name of the person who created the document.
9449 $out .= ' /Author '.$this->_textstring($this->author
, $oid);
9451 if (!TCPDF_STATIC
::empty_string($this->subject
)) {
9452 // The subject of the document.
9453 $out .= ' /Subject '.$this->_textstring($this->subject
, $oid);
9455 if (!TCPDF_STATIC
::empty_string($this->keywords
)) {
9456 // Keywords associated with the document.
9457 $out .= ' /Keywords '.$this->_textstring($this->keywords
, $oid);
9459 if (!TCPDF_STATIC
::empty_string($this->creator
)) {
9460 // 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.
9461 $out .= ' /Creator '.$this->_textstring($this->creator
, $oid);
9463 // restore previous isunicode value
9464 $this->isunicode
= $prev_isunicode;
9466 $out .= ' /Producer '.$this->_textstring(TCPDF_STATIC
::getTCPDFProducer(), $oid);
9467 // The date and time the document was created, in human-readable form
9468 $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp
);
9469 // The date and time the document was most recently modified, in human-readable form
9470 $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp
);
9471 // A name object indicating whether the document has been modified to include trapping information
9472 $out .= ' /Trapped /False';
9474 $out .= "\n".'endobj';
9480 * Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag.
9481 * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method!
9482 * @param $xmp (string) Custom XMP data.
9483 * @since 5.9.128 (2011-10-06)
9486 public function setExtraXMP($xmp) {
9487 $this->custom_xmp
= $xmp;
9491 * Put XMP data object and return ID.
9492 * @return (int) The object ID.
9493 * @since 5.9.121 (2011-09-28)
9496 protected function _putXMP() {
9497 $oid = $this->_newobj();
9498 // store current isunicode value
9499 $prev_isunicode = $this->isunicode
;
9500 $this->isunicode
= true;
9501 $prev_encrypted = $this->encrypted
;
9502 $this->encrypted
= false;
9504 $xmp = '<?xpacket begin="'.TCPDF_FONTS
::unichr(0xfeff, $this->isunicode
).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
9505 $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";
9506 $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
9507 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
9508 $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
9509 $xmp .= "\t\t\t".'<dc:title>'."\n";
9510 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9511 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC
::_escapeXML($this->title
).'</rdf:li>'."\n";
9512 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9513 $xmp .= "\t\t\t".'</dc:title>'."\n";
9514 $xmp .= "\t\t\t".'<dc:creator>'."\n";
9515 $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
9516 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC
::_escapeXML($this->author
).'</rdf:li>'."\n";
9517 $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
9518 $xmp .= "\t\t\t".'</dc:creator>'."\n";
9519 $xmp .= "\t\t\t".'<dc:description>'."\n";
9520 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9521 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC
::_escapeXML($this->subject
).'</rdf:li>'."\n";
9522 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9523 $xmp .= "\t\t\t".'</dc:description>'."\n";
9524 $xmp .= "\t\t\t".'<dc:subject>'."\n";
9525 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9526 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC
::_escapeXML($this->keywords
).'</rdf:li>'."\n";
9527 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9528 $xmp .= "\t\t\t".'</dc:subject>'."\n";
9529 $xmp .= "\t\t".'</rdf:Description>'."\n";
9530 // convert doc creation date format
9531 $dcdate = TCPDF_STATIC
::getFormattedDate($this->doc_creation_timestamp
);
9532 $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2);
9533 $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2);
9534 $doccreationdate .= '+'.substr($dcdate, 15, 2).':'.substr($dcdate, 18, 2);
9535 $doccreationdate = TCPDF_STATIC
::_escapeXML($doccreationdate);
9536 // convert doc modification date format
9537 $dmdate = TCPDF_STATIC
::getFormattedDate($this->doc_modification_timestamp
);
9538 $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2);
9539 $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2);
9540 $docmoddate .= '+'.substr($dmdate, 15, 2).':'.substr($dmdate, 18, 2);
9541 $docmoddate = TCPDF_STATIC
::_escapeXML($docmoddate);
9542 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
9543 $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n";
9544 $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator
.'</xmp:CreatorTool>'."\n";
9545 $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n";
9546 $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n";
9547 $xmp .= "\t\t".'</rdf:Description>'."\n";
9548 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
9549 $xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC
::_escapeXML($this->keywords
).'</pdf:Keywords>'."\n";
9550 $xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC
::_escapeXML(TCPDF_STATIC
::getTCPDFProducer()).'</pdf:Producer>'."\n";
9551 $xmp .= "\t\t".'</rdf:Description>'."\n";
9552 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
9553 $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);
9554 $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
9555 $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
9556 $xmp .= "\t\t".'</rdf:Description>'."\n";
9557 if ($this->pdfa_mode
) {
9558 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
9559 $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n";
9560 $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
9561 $xmp .= "\t\t".'</rdf:Description>'."\n";
9563 // XMP extension schemas
9564 $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";
9565 $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
9566 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9567 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9568 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
9569 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
9570 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
9571 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9572 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9573 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
9574 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
9575 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
9576 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9577 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9578 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9579 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9580 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
9581 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9582 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9583 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9584 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9585 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9586 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9587 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9588 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
9589 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
9590 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
9591 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9592 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9593 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9594 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9595 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
9596 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
9597 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
9598 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9599 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9600 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9601 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
9602 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
9603 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9604 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9605 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9606 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9607 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
9608 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
9609 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9610 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9611 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9612 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9613 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9614 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9615 $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
9616 $xmp .= "\t\t".'</rdf:Description>'."\n";
9617 $xmp .= "\t".'</rdf:RDF>'."\n";
9618 $xmp .= $this->custom_xmp
;
9619 $xmp .= '</x:xmpmeta>'."\n";
9620 $xmp .= '<?xpacket end="w"?>';
9621 $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
9622 // restore previous isunicode value
9623 $this->isunicode
= $prev_isunicode;
9624 $this->encrypted
= $prev_encrypted;
9631 * @return int object id
9634 protected function _putcatalog() {
9636 $xmpobj = $this->_putXMP();
9637 // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile
9638 if ($this->pdfa_mode
OR $this->force_srgb
) {
9639 $iccobj = $this->_newobj();
9640 $icc = file_get_contents(dirname(__FILE__
).'/include/sRGB.icc');
9642 if ($this->compress
) {
9643 $filter = ' /Filter /FlateDecode';
9644 $icc = gzcompress($icc);
9646 $icc = $this->_getrawstream($icc);
9647 $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9650 $oid = $this->_newobj();
9651 $out = '<< /Type /Catalog';
9652 $out .= ' /Version /'.$this->PDFVersion
;
9653 //$out .= ' /Extensions <<>>';
9654 $out .= ' /Pages 1 0 R';
9655 //$out .= ' /PageLabels ' //...;
9656 $out .= ' /Names <<';
9657 if ((!$this->pdfa_mode
) AND !empty($this->n_js
)) {
9658 $out .= ' /JavaScript '.$this->n_js
;
9660 if (!empty($this->efnames
)) {
9661 $out .= ' /EmbeddedFiles <</Names [';
9662 foreach ($this->efnames
AS $fn => $fref) {
9663 $out .= ' '.$this->_datastring($fn).' '.$fref;
9668 if (!empty($this->dests
)) {
9669 $out .= ' /Dests '.($this->n_dests
).' 0 R';
9671 $out .= $this->_putviewerpreferences();
9672 if (isset($this->LayoutMode
) AND (!TCPDF_STATIC
::empty_string($this->LayoutMode
))) {
9673 $out .= ' /PageLayout /'.$this->LayoutMode
;
9675 if (isset($this->PageMode
) AND (!TCPDF_STATIC
::empty_string($this->PageMode
))) {
9676 $out .= ' /PageMode /'.$this->PageMode
;
9678 if (count($this->outlines
) > 0) {
9679 $out .= ' /Outlines '.$this->OutlineRoot
.' 0 R';
9680 $out .= ' /PageMode /UseOutlines';
9682 //$out .= ' /Threads []';
9683 if ($this->ZoomMode
== 'fullpage') {
9684 $out .= ' /OpenAction ['.$this->page_obj_id
[1].' 0 R /Fit]';
9685 } elseif ($this->ZoomMode
== 'fullwidth') {
9686 $out .= ' /OpenAction ['.$this->page_obj_id
[1].' 0 R /FitH null]';
9687 } elseif ($this->ZoomMode
== 'real') {
9688 $out .= ' /OpenAction ['.$this->page_obj_id
[1].' 0 R /XYZ null null 1]';
9689 } elseif (!is_string($this->ZoomMode
)) {
9690 $out .= sprintf(' /OpenAction ['.$this->page_obj_id
[1].' 0 R /XYZ null null %F]', ($this->ZoomMode
/ 100));
9692 //$out .= ' /AA <<>>';
9693 //$out .= ' /URI <<>>';
9694 $out .= ' /Metadata '.$xmpobj.' 0 R';
9695 //$out .= ' /StructTreeRoot <<>>';
9696 //$out .= ' /MarkInfo <<>>';
9697 if (isset($this->l
['a_meta_language'])) {
9698 $out .= ' /Lang '.$this->_textstring($this->l
['a_meta_language'], $oid);
9700 //$out .= ' /SpiderInfo <<>>';
9701 // set OutputIntent to sRGB IEC61966-2.1 if required
9702 if ($this->pdfa_mode
OR $this->force_srgb
) {
9703 $out .= ' /OutputIntents [<<';
9704 $out .= ' /Type /OutputIntent';
9705 $out .= ' /S /GTS_PDFA1';
9706 $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9707 $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9708 $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
9709 $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9710 $out .= ' /DestOutputProfile '.$iccobj.' 0 R';
9713 //$out .= ' /PieceInfo <<>>';
9714 if (!empty($this->pdflayers
)) {
9716 $lyrobjs_print = '';
9719 foreach ($this->pdflayers
as $layer) {
9720 $layer_obj_ref = ' '.$layer['objid'].' 0 R';
9721 $lyrobjs .= $layer_obj_ref;
9722 if ($layer['print']) {
9723 $lyrobjs_print .= $layer_obj_ref;
9725 if ($layer['view']) {
9726 $lyrobjs_view .= $layer_obj_ref;
9728 if ($layer['lock']) {
9729 $lyrobjs_lock .= $layer_obj_ref;
9732 $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
9734 $out .= ' /Name '.$this->_textstring('Layers', $oid);
9735 $out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
9736 $out .= ' /BaseState /ON';
9737 $out .= ' /ON ['.$lyrobjs_print.']';
9738 $out .= ' /OFF ['.$lyrobjs_view.']';
9739 $out .= ' /Locked ['.$lyrobjs_lock.']';
9740 $out .= ' /Intent /View';
9742 $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
9743 $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
9745 $out .= ' /Order ['.$lyrobjs.']';
9746 $out .= ' /ListMode /AllPages';
9747 //$out .= ' /RBGroups ['..']';
9748 //$out .= ' /Locked ['..']';
9753 if (!empty($this->form_obj_id
)
9754 OR ($this->sign
AND isset($this->signature_data
['cert_type']))
9755 OR !empty($this->empty_signature_appearance
)) {
9756 $out .= ' /AcroForm <<';
9758 if ($this->sign
AND isset($this->signature_data
['cert_type'])) {
9759 // set reference for signature object
9760 $objrefs .= $this->sig_obj_id
.' 0 R';
9762 if (!empty($this->empty_signature_appearance
)) {
9763 foreach ($this->empty_signature_appearance
as $esa) {
9764 // set reference for empty signature objects
9765 $objrefs .= ' '.$esa['objid'].' 0 R';
9768 if (!empty($this->form_obj_id
)) {
9769 foreach($this->form_obj_id
as $objid) {
9770 $objrefs .= ' '.$objid.' 0 R';
9773 $out .= ' /Fields ['.$objrefs.']';
9774 // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
9775 $out .= ' /NeedAppearances false';
9776 if ($this->sign
AND isset($this->signature_data
['cert_type'])) {
9777 if ($this->signature_data
['cert_type'] > 0) {
9778 $out .= ' /SigFlags 3';
9780 $out .= ' /SigFlags 1';
9784 if (isset($this->annotation_fonts
) AND !empty($this->annotation_fonts
)) {
9786 $out .= ' /Font <<';
9787 foreach ($this->annotation_fonts
as $fontkey => $fontid) {
9788 $out .= ' /F'.$fontid.' '.$this->font_obj_ids
[$fontkey].' 0 R';
9792 $font = $this->getFontBuffer('helvetica');
9793 $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
9794 $out .= ' /Q '.(($this->rtl
)?
'2':'0');
9798 if ($this->sign
AND isset($this->signature_data
['cert_type'])) {
9799 if ($this->signature_data
['cert_type'] > 0) {
9800 $out .= ' /Perms << /DocMDP '.($this->sig_obj_id +
1).' 0 R >>';
9802 $out .= ' /Perms << /UR3 '.($this->sig_obj_id +
1).' 0 R >>';
9806 //$out .= ' /Legal <<>>';
9807 //$out .= ' /Requirements []';
9808 //$out .= ' /Collection <<>>';
9809 //$out .= ' /NeedsRendering true';
9811 $out .= "\n".'endobj';
9817 * Output viewer preferences.
9818 * @return string for viewer preferences
9819 * @author Nicola asuni
9820 * @since 3.1.000 (2008-06-09)
9823 protected function _putviewerpreferences() {
9824 $vp = $this->viewer_preferences
;
9825 $out = ' /ViewerPreferences <<';
9827 $out .= ' /Direction /R2L';
9829 $out .= ' /Direction /L2R';
9831 if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) {
9832 $out .= ' /HideToolbar true';
9834 if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) {
9835 $out .= ' /HideMenubar true';
9837 if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) {
9838 $out .= ' /HideWindowUI true';
9840 if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) {
9841 $out .= ' /FitWindow true';
9843 if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) {
9844 $out .= ' /CenterWindow true';
9846 if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) {
9847 $out .= ' /DisplayDocTitle true';
9849 if (isset($vp['NonFullScreenPageMode'])) {
9850 $out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode'];
9852 if (isset($vp['ViewArea'])) {
9853 $out .= ' /ViewArea /'.$vp['ViewArea'];
9855 if (isset($vp['ViewClip'])) {
9856 $out .= ' /ViewClip /'.$vp['ViewClip'];
9858 if (isset($vp['PrintArea'])) {
9859 $out .= ' /PrintArea /'.$vp['PrintArea'];
9861 if (isset($vp['PrintClip'])) {
9862 $out .= ' /PrintClip /'.$vp['PrintClip'];
9864 if (isset($vp['PrintScaling'])) {
9865 $out .= ' /PrintScaling /'.$vp['PrintScaling'];
9867 if (isset($vp['Duplex']) AND (!TCPDF_STATIC
::empty_string($vp['Duplex']))) {
9868 $out .= ' /Duplex /'.$vp['Duplex'];
9870 if (isset($vp['PickTrayByPDFSize'])) {
9871 if ($vp['PickTrayByPDFSize']) {
9872 $out .= ' /PickTrayByPDFSize true';
9874 $out .= ' /PickTrayByPDFSize false';
9877 if (isset($vp['PrintPageRange'])) {
9878 $PrintPageRangeNum = '';
9879 foreach ($vp['PrintPageRange'] as $k => $v) {
9880 $PrintPageRangeNum .= ' '.($v - 1).'';
9882 $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
9884 if (isset($vp['NumCopies'])) {
9885 $out .= ' /NumCopies '.intval($vp['NumCopies']);
9892 * Output PDF File Header (7.5.2).
9895 protected function _putheader() {
9896 $this->_out('%PDF-'.$this->PDFVersion
);
9897 $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
9901 * Output end of document (EOF).
9904 protected function _enddoc() {
9905 if (isset($this->CurrentFont
['fontkey']) AND isset($this->CurrentFont
['subsetchars'])) {
9906 // save subset chars of the previous font
9907 $this->setFontSubBuffer($this->CurrentFont
['fontkey'], 'subsetchars', $this->CurrentFont
['subsetchars']);
9910 $this->_putheader();
9912 $this->_putresources();
9913 // empty signature fields
9914 if (!empty($this->empty_signature_appearance
)) {
9915 foreach ($this->empty_signature_appearance
as $key => $esa) {
9916 // widget annotation for empty signature
9917 $out = $this->_getobj($esa['objid'])."\n";
9918 $out .= '<< /Type /Annot';
9919 $out .= ' /Subtype /Widget';
9920 $out .= ' /Rect ['.$esa['rect'].']';
9921 $out .= ' /P '.$this->page_obj_id
[($esa['page'])].' 0 R'; // link to signature appearance page
9923 $out .= ' /FT /Sig';
9924 $signame = $esa['name'].sprintf(' [%03d]', ($key +
1));
9925 $out .= ' /T '.$this->_textstring($signame, $esa['objid']);
9928 $out .= "\n".'endobj';
9933 if ($this->sign
AND isset($this->signature_data
['cert_type'])) {
9934 // widget annotation for signature
9935 $out = $this->_getobj($this->sig_obj_id
)."\n";
9936 $out .= '<< /Type /Annot';
9937 $out .= ' /Subtype /Widget';
9938 $out .= ' /Rect ['.$this->signature_appearance
['rect'].']';
9939 $out .= ' /P '.$this->page_obj_id
[($this->signature_appearance
['page'])].' 0 R'; // link to signature appearance page
9941 $out .= ' /FT /Sig';
9942 $out .= ' /T '.$this->_textstring($this->signature_appearance
['name'], $this->sig_obj_id
);
9944 $out .= ' /V '.($this->sig_obj_id +
1).' 0 R';
9946 $out .= "\n".'endobj';
9949 $this->_putsignature();
9952 $objid_info = $this->_putinfo();
9954 $objid_catalog = $this->_putcatalog();
9956 $o = $this->bufferlen
;
9958 $this->_out('xref');
9959 $this->_out('0 '.($this->n +
1));
9960 $this->_out('0000000000 65535 f ');
9961 $freegen = ($this->n +
2);
9962 for ($i=1; $i <= $this->n
; ++
$i) {
9963 if (!isset($this->offsets
[$i]) AND ($i > 1)) {
9964 $this->_out(sprintf('0000000000 %05d f ', $freegen));
9967 $this->_out(sprintf('%010d 00000 n ', $this->offsets
[$i]));
9971 $out = 'trailer'."\n";
9973 $out .= ' /Size '.($this->n +
1);
9974 $out .= ' /Root '.$objid_catalog.' 0 R';
9975 $out .= ' /Info '.$objid_info.' 0 R';
9976 if ($this->encrypted
) {
9977 $out .= ' /Encrypt '.$this->encryptdata
['objid'].' 0 R';
9979 $out .= ' /ID [ <'.$this->file_id
.'> <'.$this->file_id
.'> ]';
9982 $this->_out('startxref');
9984 $this->_out('%%EOF');
9985 $this->state
= 3; // end-of-doc
9986 if ($this->diskcache
) {
9987 // remove temporary files used for images
9988 foreach ($this->imagekeys
as $key) {
9989 // remove temporary files
9990 unlink($this->images
[$key]);
9992 foreach ($this->fontkeys
as $key) {
9993 // remove temporary files
9994 unlink($this->fonts
[$key]);
10000 * Initialize a new page.
10001 * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
10002 * @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().
10004 * @see getPageSizeFromFormat(), setPageFormat()
10006 protected function _beginpage($orientation='', $format='') {
10008 $this->pageobjects
[$this->page
] = array();
10009 $this->setPageBuffer($this->page
, '');
10010 // initialize array for graphics tranformation positions inside a page buffer
10011 $this->transfmrk
[$this->page
] = array();
10013 if (TCPDF_STATIC
::empty_string($orientation)) {
10014 if (isset($this->CurOrientation
)) {
10015 $orientation = $this->CurOrientation
;
10016 } elseif ($this->fwPt
> $this->fhPt
) {
10018 $orientation = 'L';
10021 $orientation = 'P';
10024 if (TCPDF_STATIC
::empty_string($format)) {
10025 $this->pagedim
[$this->page
] = $this->pagedim
[($this->page
- 1)];
10026 $this->setPageOrientation($orientation);
10028 $this->setPageFormat($format, $orientation);
10031 $this->x
= $this->w
- $this->rMargin
;
10033 $this->x
= $this->lMargin
;
10035 $this->y
= $this->tMargin
;
10036 if (isset($this->newpagegroup
[$this->page
])) {
10037 // start a new group
10038 $this->currpagegroup
= $this->newpagegroup
[$this->page
];
10039 $this->pagegroups
[$this->currpagegroup
] = 1;
10040 } elseif (isset($this->currpagegroup
) AND ($this->currpagegroup
> 0)) {
10041 ++
$this->pagegroups
[$this->currpagegroup
];
10046 * Mark end of page.
10049 protected function _endpage() {
10050 $this->setVisibility('all');
10055 * Begin a new object and return the object number.
10056 * @return int object number
10059 protected function _newobj() {
10060 $this->_out($this->_getobj());
10065 * Return the starting object string for the selected object ID.
10066 * @param $objid (int) Object ID (leave empty to get a new ID).
10067 * @return string the starting object string
10069 * @since 5.8.009 (2010-08-20)
10071 protected function _getobj($objid='') {
10072 if ($objid === '') {
10076 $this->offsets
[$objid] = $this->bufferlen
;
10077 $this->pageobjects
[$this->page
][] = $objid;
10078 return $objid.' 0 obj';
10083 * @param $x (int) X coordinate
10084 * @param $y (int) Y coordinate
10085 * @param $txt (string) text to underline
10088 protected function _dounderline($x, $y, $txt) {
10089 $w = $this->GetStringWidth($txt);
10090 return $this->_dounderlinew($x, $y, $w);
10094 * Underline for rectangular text area.
10095 * @param $x (int) X coordinate
10096 * @param $y (int) Y coordinate
10097 * @param $w (int) width to underline
10099 * @since 4.8.008 (2009-09-29)
10101 protected function _dounderlinew($x, $y, $w) {
10102 $linew = - $this->CurrentFont
['ut'] / 1000 * $this->FontSizePt
;
10103 return sprintf('%F %F %F %F re f', $x * $this->k
, ((($this->h
- $y) * $this->k
) +
$linew), $w * $this->k
, $linew);
10107 * Line through text.
10108 * @param $x (int) X coordinate
10109 * @param $y (int) Y coordinate
10110 * @param $txt (string) text to linethrough
10113 protected function _dolinethrough($x, $y, $txt) {
10114 $w = $this->GetStringWidth($txt);
10115 return $this->_dolinethroughw($x, $y, $w);
10119 * Line through for rectangular text area.
10120 * @param $x (int) X coordinate
10121 * @param $y (int) Y coordinate
10122 * @param $w (int) line length (width)
10124 * @since 4.9.008 (2009-09-29)
10126 protected function _dolinethroughw($x, $y, $w) {
10127 $linew = - $this->CurrentFont
['ut'] / 1000 * $this->FontSizePt
;
10128 return sprintf('%F %F %F %F re f', $x * $this->k
, ((($this->h
- $y) * $this->k
) +
$linew +
($this->FontSizePt
/ 3)), $w * $this->k
, $linew);
10133 * @param $x (int) X coordinate
10134 * @param $y (int) Y coordinate
10135 * @param $txt (string) text to overline
10137 * @since 4.9.015 (2010-04-19)
10139 protected function _dooverline($x, $y, $txt) {
10140 $w = $this->GetStringWidth($txt);
10141 return $this->_dooverlinew($x, $y, $w);
10145 * Overline for rectangular text area.
10146 * @param $x (int) X coordinate
10147 * @param $y (int) Y coordinate
10148 * @param $w (int) width to overline
10150 * @since 4.9.015 (2010-04-19)
10152 protected function _dooverlinew($x, $y, $w) {
10153 $linew = - $this->CurrentFont
['ut'] / 1000 * $this->FontSizePt
;
10154 return sprintf('%F %F %F %F re f', $x * $this->k
, (($this->h
- $y +
$this->FontAscent
) * $this->k
) - $linew, $w * $this->k
, $linew);
10159 * Format a data string for meta information
10160 * @param $s (string) data string to escape.
10161 * @param $n (int) object ID
10162 * @return string escaped string.
10165 protected function _datastring($s, $n=0) {
10169 $s = $this->_encrypt_data($n, $s);
10170 return '('. TCPDF_STATIC
::_escape($s).')';
10174 * Set the document creation timestamp
10175 * @param $time (mixed) Document creation timestamp in seconds or date-time string.
10177 * @since 5.9.152 (2012-03-23)
10179 public function setDocCreationTimestamp($time) {
10180 if (is_string($time)) {
10181 $time = TCPDF_STATIC
::getTimestamp($time);
10183 $this->doc_creation_timestamp
= intval($time);
10187 * Set the document modification timestamp
10188 * @param $time (mixed) Document modification timestamp in seconds or date-time string.
10190 * @since 5.9.152 (2012-03-23)
10192 public function setDocModificationTimestamp($time) {
10193 if (is_string($time)) {
10194 $time = TCPDF_STATIC
::getTimestamp($time);
10196 $this->doc_modification_timestamp
= intval($time);
10200 * Returns document creation timestamp in seconds.
10201 * @return (int) Creation timestamp in seconds.
10203 * @since 5.9.152 (2012-03-23)
10205 public function getDocCreationTimestamp() {
10206 return $this->doc_creation_timestamp
;
10210 * Returns document modification timestamp in seconds.
10211 * @return (int) Modfication timestamp in seconds.
10213 * @since 5.9.152 (2012-03-23)
10215 public function getDocModificationTimestamp() {
10216 return $this->doc_modification_timestamp
;
10220 * Returns a formatted date for meta information
10221 * @param $n (int) Object ID.
10222 * @param $timestamp (int) Timestamp to convert.
10223 * @return string escaped date string.
10225 * @since 4.6.028 (2009-08-25)
10227 protected function _datestring($n=0, $timestamp=0) {
10228 if ((empty($timestamp)) OR ($timestamp < 0)) {
10229 $timestamp = $this->doc_creation_timestamp
;
10231 return $this->_datastring('D:'.TCPDF_STATIC
::getFormattedDate($timestamp), $n);
10235 * Format a text string for meta information
10236 * @param $s (string) string to escape.
10237 * @param $n (int) object ID
10238 * @return string escaped string.
10241 protected function _textstring($s, $n=0) {
10242 if ($this->isunicode
) {
10243 //Convert string to UTF-16BE
10244 $s = TCPDF_FONTS
::UTF8ToUTF16BE($s, true, $this->isunicode
, $this->CurrentFont
);
10246 return $this->_datastring($s, $n);
10250 * THIS METHOD IS DEPRECATED
10251 * Format a text string
10252 * @param $s (string) string to escape.
10253 * @return string escaped string.
10257 protected function _escapetext($s) {
10258 if ($this->isunicode
) {
10259 if (($this->CurrentFont
['type'] == 'core') OR ($this->CurrentFont
['type'] == 'TrueType') OR ($this->CurrentFont
['type'] == 'Type1')) {
10260 $s = TCPDF_FONTS
::UTF8ToLatin1($s, $this->isunicode
, $this->CurrentFont
);
10262 //Convert string to UTF-16BE and reverse RTL language
10263 $s = TCPDF_FONTS
::utf8StrRev($s, false, $this->tmprtl
, $this->isunicode
, $this->CurrentFont
);
10266 return TCPDF_STATIC
::_escape($s);
10270 * get raw output stream.
10271 * @param $s (string) string to output.
10272 * @param $n (int) object reference for encryption mode
10274 * @author Nicola Asuni
10275 * @since 5.5.000 (2010-06-22)
10277 protected function _getrawstream($s, $n=0) {
10279 // default to current object
10282 return $this->_encrypt_data($n, $s);
10286 * Format output stream (DEPRECATED).
10287 * @param $s (string) string to output.
10288 * @param $n (int) object reference for encryption mode
10292 protected function _getstream($s, $n=0) {
10293 return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream';
10297 * Output a stream (DEPRECATED).
10298 * @param $s (string) string to output.
10299 * @param $n (int) object reference for encryption mode
10303 protected function _putstream($s, $n=0) {
10304 $this->_out($this->_getstream($s, $n));
10308 * Output a string to the document.
10309 * @param $s (string) string to output.
10312 protected function _out($s) {
10313 if ($this->state
== 2) {
10314 if ($this->inxobj
) {
10315 // we are inside an XObject template
10316 $this->xobjects
[$this->xobjid
]['outdata'] .= $s."\n";
10317 } elseif ((!$this->InFooter
) AND isset($this->footerlen
[$this->page
]) AND ($this->footerlen
[$this->page
] > 0)) {
10318 // puts data before page footer
10319 $pagebuff = $this->getPageBuffer($this->page
);
10320 $page = substr($pagebuff, 0, -$this->footerlen
[$this->page
]);
10321 $footer = substr($pagebuff, -$this->footerlen
[$this->page
]);
10322 $this->setPageBuffer($this->page
, $page.$s."\n".$footer);
10323 // update footer position
10324 $this->footerpos
[$this->page
] +
= strlen($s."\n");
10327 $this->setPageBuffer($this->page
, $s."\n", true);
10329 } elseif ($this->state
> 0) {
10330 // set general data
10331 $this->setBuffer($s."\n");
10337 * @param $font (array) Array describing the basic font parameters: (family, style, size).
10341 public function setHeaderFont($font) {
10342 $this->header_font
= $font;
10347 * @return array() Array describing the basic font parameters: (family, style, size).
10349 * @since 4.0.012 (2008-07-24)
10351 public function getHeaderFont() {
10352 return $this->header_font
;
10357 * @param $font (array) Array describing the basic font parameters: (family, style, size).
10361 public function setFooterFont($font) {
10362 $this->footer_font
= $font;
10367 * @return array() Array describing the basic font parameters: (family, style, size).
10369 * @since 4.0.012 (2008-07-24)
10371 public function getFooterFont() {
10372 return $this->footer_font
;
10376 * Set language array.
10377 * @param $language (array)
10381 public function setLanguageArray($language) {
10382 $this->l
= $language;
10383 if (isset($this->l
['a_meta_dir'])) {
10384 $this->rtl
= $this->l
['a_meta_dir']=='rtl' ?
true : false;
10386 $this->rtl
= false;
10391 * Returns the PDF data.
10394 public function getPDFData() {
10395 if ($this->state
< 3) {
10398 return $this->buffer
;
10402 * Output anchor link.
10403 * @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>)
10404 * @param $name (string) link name
10405 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
10406 * @param $firstline (boolean) if true prints only the first line and return the remaining string.
10407 * @param $color (array) array of RGB text color
10408 * @param $style (string) font style (U, D, B, I)
10409 * @param $firstblock (boolean) if true the string is the starting of a line.
10410 * @return the number of cells used or the remaining text if $firstline = true;
10413 public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
10414 if (isset($url[1]) AND ($url[0] == '#')) {
10415 // convert url to internal link
10416 $lnkdata = explode(',', $url);
10417 if (isset($lnkdata[0]) ) {
10418 $page = substr($lnkdata[0], 1);
10419 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
10420 $lnky = floatval($lnkdata[1]);
10424 $url = $this->AddLink();
10425 $this->SetLink($url, $lnky, $page);
10428 // store current settings
10429 $prevcolor = $this->fgcolor
;
10430 $prevstyle = $this->FontStyle
;
10431 if (empty($color)) {
10432 $this->SetTextColorArray($this->htmlLinkColorArray
);
10434 $this->SetTextColorArray($color);
10436 if ($style == -1) {
10437 $this->SetFont('', $this->FontStyle
.$this->htmlLinkFontStyle
);
10439 $this->SetFont('', $this->FontStyle
.$style);
10441 $ret = $this->Write($this->lasth
, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
10442 // restore settings
10443 $this->SetFont('', $prevstyle);
10444 $this->SetTextColorArray($prevcolor);
10449 * Converts pixels to User's Units.
10450 * @param $px (int) pixels
10451 * @return float value in user's unit
10453 * @see setImageScale(), getImageScale()
10455 public function pixelsToUnits($px) {
10456 return ($px / ($this->imgscale
* $this->k
));
10460 * Reverse function for htmlentities.
10461 * Convert entities in UTF-8.
10462 * @param $text_to_convert (string) Text to convert.
10463 * @return string converted text string
10466 public function unhtmlentities($text_to_convert) {
10467 return @html_entity_decode
($text_to_convert, ENT_QUOTES
, $this->encoding
);
10470 // ENCRYPTION METHODS ----------------------------------
10473 * Compute encryption key depending on object number where the encrypted data is stored.
10474 * This is used for all strings and streams without crypt filter specifier.
10475 * @param $n (int) object number
10476 * @return int object key
10478 * @author Nicola Asuni
10479 * @since 2.0.000 (2008-01-02)
10481 protected function _objectkey($n) {
10482 $objkey = $this->encryptdata
['key'].pack('VXxx', $n);
10483 if ($this->encryptdata
['mode'] == 2) { // AES-128
10485 $objkey .= "\x73\x41\x6C\x54"; // sAlT
10487 $objkey = substr(TCPDF_STATIC
::_md5_16($objkey), 0, (($this->encryptdata
['Length'] / 8) +
5));
10488 $objkey = substr($objkey, 0, 16);
10493 * Encrypt the input string.
10494 * @param $n (int) object number
10495 * @param $s (string) data string to encrypt
10496 * @return encrypted string
10498 * @author Nicola Asuni
10499 * @since 5.0.005 (2010-05-11)
10501 protected function _encrypt_data($n, $s) {
10502 if (!$this->encrypted
) {
10505 switch ($this->encryptdata
['mode']) {
10507 case 1: { // RC4-128
10508 $s = TCPDF_STATIC
::_RC4($this->_objectkey($n), $s, $this->last_enc_key
, $this->last_enc_key_c
);
10511 case 2: { // AES-128
10512 $s = TCPDF_STATIC
::_AES($this->_objectkey($n), $s);
10515 case 3: { // AES-256
10516 $s = TCPDF_STATIC
::_AES($this->encryptdata
['key'], $s);
10524 * Put encryption on PDF document.
10526 * @author Nicola Asuni
10527 * @since 2.0.000 (2008-01-02)
10529 protected function _putencryption() {
10530 if (!$this->encrypted
) {
10533 $this->encryptdata
['objid'] = $this->_newobj();
10535 if (!isset($this->encryptdata
['Filter']) OR empty($this->encryptdata
['Filter'])) {
10536 $this->encryptdata
['Filter'] = 'Standard';
10538 $out .= ' /Filter /'.$this->encryptdata
['Filter'];
10539 if (isset($this->encryptdata
['SubFilter']) AND !empty($this->encryptdata
['SubFilter'])) {
10540 $out .= ' /SubFilter /'.$this->encryptdata
['SubFilter'];
10542 if (!isset($this->encryptdata
['V']) OR empty($this->encryptdata
['V'])) {
10543 $this->encryptdata
['V'] = 1;
10545 // V is a code specifying the algorithm to be used in encrypting and decrypting the document
10546 $out .= ' /V '.$this->encryptdata
['V'];
10547 if (isset($this->encryptdata
['Length']) AND !empty($this->encryptdata
['Length'])) {
10548 // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
10549 $out .= ' /Length '.$this->encryptdata
['Length'];
10551 $out .= ' /Length 40';
10553 if ($this->encryptdata
['V'] >= 4) {
10554 if (!isset($this->encryptdata
['StmF']) OR empty($this->encryptdata
['StmF'])) {
10555 $this->encryptdata
['StmF'] = 'Identity';
10557 if (!isset($this->encryptdata
['StrF']) OR empty($this->encryptdata
['StrF'])) {
10558 // The name of the crypt filter that shall be used when decrypting all strings in the document.
10559 $this->encryptdata
['StrF'] = 'Identity';
10561 // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
10562 if (isset($this->encryptdata
['CF']) AND !empty($this->encryptdata
['CF'])) {
10564 $out .= ' /'.$this->encryptdata
['StmF'].' <<';
10565 $out .= ' /Type /CryptFilter';
10566 if (isset($this->encryptdata
['CF']['CFM']) AND !empty($this->encryptdata
['CF']['CFM'])) {
10568 $out .= ' /CFM /'.$this->encryptdata
['CF']['CFM'];
10569 if ($this->encryptdata
['pubkey']) {
10570 $out .= ' /Recipients [';
10571 foreach ($this->encryptdata
['Recipients'] as $rec) {
10572 $out .= ' <'.$rec.'>';
10575 if (isset($this->encryptdata
['CF']['EncryptMetadata']) AND (!$this->encryptdata
['CF']['EncryptMetadata'])) {
10576 $out .= ' /EncryptMetadata false';
10578 $out .= ' /EncryptMetadata true';
10582 $out .= ' /CFM /None';
10584 if (isset($this->encryptdata
['CF']['AuthEvent']) AND !empty($this->encryptdata
['CF']['AuthEvent'])) {
10585 // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
10586 $out .= ' /AuthEvent /'.$this->encryptdata
['CF']['AuthEvent'];
10588 $out .= ' /AuthEvent /DocOpen';
10590 if (isset($this->encryptdata
['CF']['Length']) AND !empty($this->encryptdata
['CF']['Length'])) {
10591 // The bit length of the encryption key.
10592 $out .= ' /Length '.$this->encryptdata
['CF']['Length'];
10596 // The name of the crypt filter that shall be used by default when decrypting streams.
10597 $out .= ' /StmF /'.$this->encryptdata
['StmF'];
10598 // The name of the crypt filter that shall be used when decrypting all strings in the document.
10599 $out .= ' /StrF /'.$this->encryptdata
['StrF'];
10600 if (isset($this->encryptdata
['EFF']) AND !empty($this->encryptdata
['EFF'])) {
10601 // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
10602 $out .= ' /EFF /'.$this->encryptdata
[''];
10605 // Additional encryption dictionary entries for the standard security handler
10606 if ($this->encryptdata
['pubkey']) {
10607 if (($this->encryptdata
['V'] < 4) AND isset($this->encryptdata
['Recipients']) AND !empty($this->encryptdata
['Recipients'])) {
10608 $out .= ' /Recipients [';
10609 foreach ($this->encryptdata
['Recipients'] as $rec) {
10610 $out .= ' <'.$rec.'>';
10616 if ($this->encryptdata
['V'] == 5) { // AES-256
10618 $out .= ' /OE ('.TCPDF_STATIC
::_escape($this->encryptdata
['OE']).')';
10619 $out .= ' /UE ('.TCPDF_STATIC
::_escape($this->encryptdata
['UE']).')';
10620 $out .= ' /Perms ('.TCPDF_STATIC
::_escape($this->encryptdata
['perms']).')';
10621 } elseif ($this->encryptdata
['V'] == 4) { // AES-128
10623 } elseif ($this->encryptdata
['V'] < 2) { // RC-40
10628 $out .= ' /O ('.TCPDF_STATIC
::_escape($this->encryptdata
['O']).')';
10629 $out .= ' /U ('.TCPDF_STATIC
::_escape($this->encryptdata
['U']).')';
10630 $out .= ' /P '.$this->encryptdata
['P'];
10631 if (isset($this->encryptdata
['EncryptMetadata']) AND (!$this->encryptdata
['EncryptMetadata'])) {
10632 $out .= ' /EncryptMetadata false';
10634 $out .= ' /EncryptMetadata true';
10638 $out .= "\n".'endobj';
10643 * Compute U value (used for encryption)
10644 * @return string U value
10646 * @since 2.0.000 (2008-01-02)
10647 * @author Nicola Asuni
10649 protected function _Uvalue() {
10650 if ($this->encryptdata
['mode'] == 0) { // RC4-40
10651 return TCPDF_STATIC
::_RC4($this->encryptdata
['key'], TCPDF_STATIC
::$enc_padding, $this->last_enc_key
, $this->last_enc_key_c
);
10652 } elseif ($this->encryptdata
['mode'] < 3) { // RC4-128, AES-128
10653 $tmp = TCPDF_STATIC
::_md5_16(TCPDF_STATIC
::$enc_padding.$this->encryptdata
['fileid']);
10654 $enc = TCPDF_STATIC
::_RC4($this->encryptdata
['key'], $tmp, $this->last_enc_key
, $this->last_enc_key_c
);
10655 $len = strlen($tmp);
10656 for ($i = 1; $i <= 19; ++
$i) {
10658 for ($j = 0; $j < $len; ++
$j) {
10659 $ek .= chr(ord($this->encryptdata
['key'][$j]) ^
$i);
10661 $enc = TCPDF_STATIC
::_RC4($ek, $enc, $this->last_enc_key
, $this->last_enc_key_c
);
10663 $enc .= str_repeat("\x00", 16);
10664 return substr($enc, 0, 32);
10665 } elseif ($this->encryptdata
['mode'] == 3) { // AES-256
10666 $seed = TCPDF_STATIC
::_md5_16(TCPDF_STATIC
::getRandomSeed());
10667 // User Validation Salt
10668 $this->encryptdata
['UVS'] = substr($seed, 0, 8);
10670 $this->encryptdata
['UKS'] = substr($seed, 8, 16);
10671 return hash('sha256', $this->encryptdata
['user_password'].$this->encryptdata
['UVS'], true).$this->encryptdata
['UVS'].$this->encryptdata
['UKS'];
10676 * Compute UE value (used for encryption)
10677 * @return string UE value
10679 * @since 5.9.006 (2010-10-19)
10680 * @author Nicola Asuni
10682 protected function _UEvalue() {
10683 $hashkey = hash('sha256', $this->encryptdata
['user_password'].$this->encryptdata
['UKS'], true);
10684 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128
, MCRYPT_MODE_CBC
));
10685 return mcrypt_encrypt(MCRYPT_RIJNDAEL_128
, $hashkey, $this->encryptdata
['key'], MCRYPT_MODE_CBC
, $iv);
10689 * Compute O value (used for encryption)
10690 * @return string O value
10692 * @since 2.0.000 (2008-01-02)
10693 * @author Nicola Asuni
10695 protected function _Ovalue() {
10696 if ($this->encryptdata
['mode'] < 3) { // RC4-40, RC4-128, AES-128
10697 $tmp = TCPDF_STATIC
::_md5_16($this->encryptdata
['owner_password']);
10698 if ($this->encryptdata
['mode'] > 0) {
10699 for ($i = 0; $i < 50; ++
$i) {
10700 $tmp = TCPDF_STATIC
::_md5_16($tmp);
10703 $owner_key = substr($tmp, 0, ($this->encryptdata
['Length'] / 8));
10704 $enc = TCPDF_STATIC
::_RC4($owner_key, $this->encryptdata
['user_password'], $this->last_enc_key
, $this->last_enc_key_c
);
10705 if ($this->encryptdata
['mode'] > 0) {
10706 $len = strlen($owner_key);
10707 for ($i = 1; $i <= 19; ++
$i) {
10709 for ($j = 0; $j < $len; ++
$j) {
10710 $ek .= chr(ord($owner_key[$j]) ^
$i);
10712 $enc = TCPDF_STATIC
::_RC4($ek, $enc, $this->last_enc_key
, $this->last_enc_key_c
);
10716 } elseif ($this->encryptdata
['mode'] == 3) { // AES-256
10717 $seed = TCPDF_STATIC
::_md5_16(TCPDF_STATIC
::getRandomSeed());
10718 // Owner Validation Salt
10719 $this->encryptdata
['OVS'] = substr($seed, 0, 8);
10721 $this->encryptdata
['OKS'] = substr($seed, 8, 16);
10722 return hash('sha256', $this->encryptdata
['owner_password'].$this->encryptdata
['OVS'].$this->encryptdata
['U'], true).$this->encryptdata
['OVS'].$this->encryptdata
['OKS'];
10727 * Compute OE value (used for encryption)
10728 * @return string OE value
10730 * @since 5.9.006 (2010-10-19)
10731 * @author Nicola Asuni
10733 protected function _OEvalue() {
10734 $hashkey = hash('sha256', $this->encryptdata
['owner_password'].$this->encryptdata
['OKS'].$this->encryptdata
['U'], true);
10735 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128
, MCRYPT_MODE_CBC
));
10736 return mcrypt_encrypt(MCRYPT_RIJNDAEL_128
, $hashkey, $this->encryptdata
['key'], MCRYPT_MODE_CBC
, $iv);
10740 * Convert password for AES-256 encryption mode
10741 * @param $password (string) password
10742 * @return string password
10744 * @since 5.9.006 (2010-10-19)
10745 * @author Nicola Asuni
10747 protected function _fixAES256Password($password) {
10748 $psw = ''; // password to be returned
10749 $psw_array = TCPDF_FONTS
::utf8Bidi(TCPDF_FONTS
::UTF8StringToArray($password, $this->isunicode
, $this->CurrentFont
), $password, $this->rtl
, $this->isunicode
, $this->CurrentFont
);
10750 foreach ($psw_array as $c) {
10751 $psw .= TCPDF_FONTS
::unichr($c, $this->isunicode
);
10753 return substr($psw, 0, 127);
10757 * Compute encryption key
10759 * @since 2.0.000 (2008-01-02)
10760 * @author Nicola Asuni
10762 protected function _generateencryptionkey() {
10763 $keybytelen = ($this->encryptdata
['Length'] / 8);
10764 if (!$this->encryptdata
['pubkey']) { // standard mode
10765 if ($this->encryptdata
['mode'] == 3) { // AES-256
10766 // generate 256 bit random key
10767 $this->encryptdata
['key'] = substr(hash('sha256', TCPDF_STATIC
::getRandomSeed(), true), 0, $keybytelen);
10768 // truncate passwords
10769 $this->encryptdata
['user_password'] = $this->_fixAES256Password($this->encryptdata
['user_password']);
10770 $this->encryptdata
['owner_password'] = $this->_fixAES256Password($this->encryptdata
['owner_password']);
10772 $this->encryptdata
['U'] = $this->_Uvalue();
10773 // Compute UE value
10774 $this->encryptdata
['UE'] = $this->_UEvalue();
10776 $this->encryptdata
['O'] = $this->_Ovalue();
10777 // Compute OE value
10778 $this->encryptdata
['OE'] = $this->_OEvalue();
10780 $this->encryptdata
['P'] = $this->encryptdata
['protection'];
10781 // Computing the encryption dictionary's Perms (permissions) value
10782 $perms = TCPDF_STATIC
::getEncPermissionsString($this->encryptdata
['protection']); // bytes 0-3
10783 $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
10784 if (isset($this->encryptdata
['CF']['EncryptMetadata']) AND (!$this->encryptdata
['CF']['EncryptMetadata'])) { // byte 8
10789 $perms .= 'adb'; // bytes 9-11
10790 $perms .= 'nick'; // bytes 12-15
10791 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128
, MCRYPT_MODE_ECB
));
10792 $this->encryptdata
['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128
, $this->encryptdata
['key'], $perms, MCRYPT_MODE_ECB
, $iv);
10793 } else { // RC4-40, RC4-128, AES-128
10795 $this->encryptdata
['user_password'] = substr($this->encryptdata
['user_password'].TCPDF_STATIC
::$enc_padding, 0, 32);
10796 $this->encryptdata
['owner_password'] = substr($this->encryptdata
['owner_password'].TCPDF_STATIC
::$enc_padding, 0, 32);
10798 $this->encryptdata
['O'] = $this->_Ovalue();
10799 // get default permissions (reverse byte order)
10800 $permissions = TCPDF_STATIC
::getEncPermissionsString($this->encryptdata
['protection']);
10801 // Compute encryption key
10802 $tmp = TCPDF_STATIC
::_md5_16($this->encryptdata
['user_password'].$this->encryptdata
['O'].$permissions.$this->encryptdata
['fileid']);
10803 if ($this->encryptdata
['mode'] > 0) {
10804 for ($i = 0; $i < 50; ++
$i) {
10805 $tmp = TCPDF_STATIC
::_md5_16(substr($tmp, 0, $keybytelen));
10808 $this->encryptdata
['key'] = substr($tmp, 0, $keybytelen);
10810 $this->encryptdata
['U'] = $this->_Uvalue();
10812 $this->encryptdata
['P'] = $this->encryptdata
['protection'];
10814 } else { // Public-Key mode
10815 // random 20-byte seed
10816 $seed = sha1(TCPDF_STATIC
::getRandomSeed(), true);
10817 $recipient_bytes = '';
10818 foreach ($this->encryptdata
['pubkeys'] as $pubkey) {
10819 // for each public certificate
10820 if (isset($pubkey['p'])) {
10821 $pkprotection = TCPDF_STATIC
::getUserPermissionCode($pubkey['p'], $this->encryptdata
['mode']);
10823 $pkprotection = $this->encryptdata
['protection'];
10825 // get default permissions (reverse byte order)
10826 $pkpermissions = TCPDF_STATIC
::getEncPermissionsString($pkprotection);
10828 $envelope = $seed.$pkpermissions;
10829 // write the envelope data to a temporary file
10830 $tempkeyfile = TCPDF_STATIC
::getObjFilename('key');
10831 $f = fopen($tempkeyfile, 'wb');
10833 $this->Error('Unable to create temporary key file: '.$tempkeyfile);
10835 $envelope_length = strlen($envelope);
10836 fwrite($f, $envelope, $envelope_length);
10838 $tempencfile = TCPDF_STATIC
::getObjFilename('enc');
10839 if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED
)) {
10840 $this->Error('Unable to encrypt the file: '.$tempkeyfile);
10842 unlink($tempkeyfile);
10843 // read encryption signature
10844 $signature = file_get_contents($tempencfile, false, null, $envelope_length);
10845 unlink($tempencfile);
10846 // extract signature
10847 $signature = substr($signature, strpos($signature, 'Content-Disposition'));
10848 $tmparr = explode("\n\n", $signature);
10849 $signature = trim($tmparr[1]);
10851 // decode signature
10852 $signature = base64_decode($signature);
10853 // convert signature to hex
10854 $hexsignature = current(unpack('H*', $signature));
10855 // store signature on recipients array
10856 $this->encryptdata
['Recipients'][] = $hexsignature;
10857 // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
10858 $recipient_bytes .= $signature;
10860 // calculate encryption key
10861 if ($this->encryptdata
['mode'] == 3) { // AES-256
10862 $this->encryptdata
['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
10863 } else { // RC4-40, RC4-128, AES-128
10864 $this->encryptdata
['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
10870 * Set document protection
10871 * Remark: the protection against modification is for people who have the full Acrobat product.
10872 * 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.
10873 * 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.
10874 * @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>
10875 * @param $user_pass (String) user password. Empty by default.
10876 * @param $owner_pass (String) owner password. If not specified, a random value is used.
10877 * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
10878 * @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')))
10880 * @since 2.0.000 (2008-01-02)
10881 * @author Nicola Asuni
10883 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) {
10884 if ($this->pdfa_mode
) {
10885 // encryption is not allowed in PDF/A mode
10888 $this->encryptdata
['protection'] = TCPDF_STATIC
::getUserPermissionCode($permissions, $mode);
10889 if (($pubkeys !== null) AND (is_array($pubkeys))) {
10891 $this->encryptdata
['pubkeys'] = $pubkeys;
10893 // public-Key Security requires at least 128 bit
10896 if (!function_exists('openssl_pkcs7_encrypt')) {
10897 $this->Error('Public-Key Security requires openssl library.');
10899 // Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
10900 $this->encryptdata
['pubkey'] = true;
10901 $this->encryptdata
['Filter'] = 'Adobe.PubSec';
10902 $this->encryptdata
['StmF'] = 'DefaultCryptFilter';
10903 $this->encryptdata
['StrF'] = 'DefaultCryptFilter';
10905 // standard mode (password mode)
10906 $this->encryptdata
['pubkey'] = false;
10907 $this->encryptdata
['Filter'] = 'Standard';
10908 $this->encryptdata
['StmF'] = 'StdCF';
10909 $this->encryptdata
['StrF'] = 'StdCF';
10911 if ($mode > 1) { // AES
10912 if (!extension_loaded('mcrypt')) {
10913 $this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).');
10915 if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128
) === false) {
10916 $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
10918 if (($mode == 3) AND !function_exists('hash')) {
10919 // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
10920 $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
10923 if ($owner_pass === null) {
10924 $owner_pass = md5(TCPDF_STATIC
::getRandomSeed());
10926 $this->encryptdata
['user_password'] = $user_pass;
10927 $this->encryptdata
['owner_password'] = $owner_pass;
10928 $this->encryptdata
['mode'] = $mode;
10930 case 0: { // RC4 40 bit
10931 $this->encryptdata
['V'] = 1;
10932 $this->encryptdata
['Length'] = 40;
10933 $this->encryptdata
['CF']['CFM'] = 'V2';
10936 case 1: { // RC4 128 bit
10937 $this->encryptdata
['V'] = 2;
10938 $this->encryptdata
['Length'] = 128;
10939 $this->encryptdata
['CF']['CFM'] = 'V2';
10940 if ($this->encryptdata
['pubkey']) {
10941 $this->encryptdata
['SubFilter'] = 'adbe.pkcs7.s4';
10942 $this->encryptdata
['Recipients'] = array();
10946 case 2: { // AES 128 bit
10947 $this->encryptdata
['V'] = 4;
10948 $this->encryptdata
['Length'] = 128;
10949 $this->encryptdata
['CF']['CFM'] = 'AESV2';
10950 $this->encryptdata
['CF']['Length'] = 128;
10951 if ($this->encryptdata
['pubkey']) {
10952 $this->encryptdata
['SubFilter'] = 'adbe.pkcs7.s5';
10953 $this->encryptdata
['Recipients'] = array();
10957 case 3: { // AES 256 bit
10958 $this->encryptdata
['V'] = 5;
10959 $this->encryptdata
['Length'] = 256;
10960 $this->encryptdata
['CF']['CFM'] = 'AESV3';
10961 $this->encryptdata
['CF']['Length'] = 256;
10962 if ($this->encryptdata
['pubkey']) {
10963 $this->encryptdata
['SubFilter'] = 'adbe.pkcs7.s5';
10964 $this->encryptdata
['Recipients'] = array();
10969 $this->encrypted
= true;
10970 $this->encryptdata
['fileid'] = TCPDF_STATIC
::convertHexStringToString($this->file_id
);
10971 $this->_generateencryptionkey();
10974 // END OF ENCRYPTION FUNCTIONS -------------------------
10976 // START TRANSFORMATIONS SECTION -----------------------
10979 * Starts a 2D tranformation saving current graphic state.
10980 * This function must be called before scaling, mirroring, translation, rotation and skewing.
10981 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
10983 * @since 2.1.000 (2008-01-07)
10984 * @see StartTransform(), StopTransform()
10986 public function StartTransform() {
10987 if ($this->state
!= 2) {
10990 $this->_outSaveGraphicsState();
10991 if ($this->inxobj
) {
10992 // we are inside an XObject template
10993 $this->xobjects
[$this->xobjid
]['transfmrk'][] = strlen($this->xobjects
[$this->xobjid
]['outdata']);
10995 $this->transfmrk
[$this->page
][] = $this->pagelen
[$this->page
];
10997 ++
$this->transfmatrix_key
;
10998 $this->transfmatrix
[$this->transfmatrix_key
] = array();
11002 * Stops a 2D tranformation restoring previous graphic state.
11003 * This function must be called after scaling, mirroring, translation, rotation and skewing.
11004 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
11006 * @since 2.1.000 (2008-01-07)
11007 * @see StartTransform(), StopTransform()
11009 public function StopTransform() {
11010 if ($this->state
!= 2) {
11013 $this->_outRestoreGraphicsState();
11014 if (isset($this->transfmatrix
[$this->transfmatrix_key
])) {
11015 array_pop($this->transfmatrix
[$this->transfmatrix_key
]);
11016 --$this->transfmatrix_key
;
11018 if ($this->inxobj
) {
11019 // we are inside an XObject template
11020 array_pop($this->xobjects
[$this->xobjid
]['transfmrk']);
11022 array_pop($this->transfmrk
[$this->page
]);
11026 * Horizontal Scaling.
11027 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
11028 * @param $x (int) abscissa of the scaling center. Default is current x position
11029 * @param $y (int) ordinate of the scaling center. Default is current y position
11031 * @since 2.1.000 (2008-01-07)
11032 * @see StartTransform(), StopTransform()
11034 public function ScaleX($s_x, $x='', $y='') {
11035 $this->Scale($s_x, 100, $x, $y);
11039 * Vertical Scaling.
11040 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
11041 * @param $x (int) abscissa of the scaling center. Default is current x position
11042 * @param $y (int) ordinate of the scaling center. Default is current y position
11044 * @since 2.1.000 (2008-01-07)
11045 * @see StartTransform(), StopTransform()
11047 public function ScaleY($s_y, $x='', $y='') {
11048 $this->Scale(100, $s_y, $x, $y);
11052 * Vertical and horizontal proportional Scaling.
11053 * @param $s (float) scaling factor for width and height as percent. 0 is not allowed.
11054 * @param $x (int) abscissa of the scaling center. Default is current x position
11055 * @param $y (int) ordinate of the scaling center. Default is current y position
11057 * @since 2.1.000 (2008-01-07)
11058 * @see StartTransform(), StopTransform()
11060 public function ScaleXY($s, $x='', $y='') {
11061 $this->Scale($s, $s, $x, $y);
11065 * Vertical and horizontal non-proportional Scaling.
11066 * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
11067 * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
11068 * @param $x (int) abscissa of the scaling center. Default is current x position
11069 * @param $y (int) ordinate of the scaling center. Default is current y position
11071 * @since 2.1.000 (2008-01-07)
11072 * @see StartTransform(), StopTransform()
11074 public function Scale($s_x, $s_y, $x='', $y='') {
11081 if (($s_x == 0) OR ($s_y == 0)) {
11082 $this->Error('Please do not use values equal to zero for scaling');
11084 $y = ($this->h
- $y) * $this->k
;
11086 //calculate elements of transformation matrix
11094 $tm[4] = $x * (1 - $s_x);
11095 $tm[5] = $y * (1 - $s_y);
11096 //scale the coordinate system
11097 $this->Transform($tm);
11101 * Horizontal Mirroring.
11102 * @param $x (int) abscissa of the point. Default is current x position
11104 * @since 2.1.000 (2008-01-07)
11105 * @see StartTransform(), StopTransform()
11107 public function MirrorH($x='') {
11108 $this->Scale(-100, 100, $x);
11112 * Verical Mirroring.
11113 * @param $y (int) ordinate of the point. Default is current y position
11115 * @since 2.1.000 (2008-01-07)
11116 * @see StartTransform(), StopTransform()
11118 public function MirrorV($y='') {
11119 $this->Scale(100, -100, '', $y);
11123 * Point reflection mirroring.
11124 * @param $x (int) abscissa of the point. Default is current x position
11125 * @param $y (int) ordinate of the point. Default is current y position
11127 * @since 2.1.000 (2008-01-07)
11128 * @see StartTransform(), StopTransform()
11130 public function MirrorP($x='',$y='') {
11131 $this->Scale(-100, -100, $x, $y);
11135 * Reflection against a straight line through point (x, y) with the gradient angle (angle).
11136 * @param $angle (float) gradient angle of the straight line. Default is 0 (horizontal line).
11137 * @param $x (int) abscissa of the point. Default is current x position
11138 * @param $y (int) ordinate of the point. Default is current y position
11140 * @since 2.1.000 (2008-01-07)
11141 * @see StartTransform(), StopTransform()
11143 public function MirrorL($angle=0, $x='',$y='') {
11144 $this->Scale(-100, 100, $x, $y);
11145 $this->Rotate(-2*($angle-90), $x, $y);
11149 * Translate graphic object horizontally.
11150 * @param $t_x (int) movement to the right (or left for RTL)
11152 * @since 2.1.000 (2008-01-07)
11153 * @see StartTransform(), StopTransform()
11155 public function TranslateX($t_x) {
11156 $this->Translate($t_x, 0);
11160 * Translate graphic object vertically.
11161 * @param $t_y (int) movement to the bottom
11163 * @since 2.1.000 (2008-01-07)
11164 * @see StartTransform(), StopTransform()
11166 public function TranslateY($t_y) {
11167 $this->Translate(0, $t_y);
11171 * Translate graphic object horizontally and vertically.
11172 * @param $t_x (int) movement to the right
11173 * @param $t_y (int) movement to the bottom
11175 * @since 2.1.000 (2008-01-07)
11176 * @see StartTransform(), StopTransform()
11178 public function Translate($t_x, $t_y) {
11179 //calculate elements of transformation matrix
11185 $tm[4] = $t_x * $this->k
;
11186 $tm[5] = -$t_y * $this->k
;
11187 //translate the coordinate system
11188 $this->Transform($tm);
11193 * @param $angle (float) angle in degrees for counter-clockwise rotation
11194 * @param $x (int) abscissa of the rotation center. Default is current x position
11195 * @param $y (int) ordinate of the rotation center. Default is current y position
11197 * @since 2.1.000 (2008-01-07)
11198 * @see StartTransform(), StopTransform()
11200 public function Rotate($angle, $x='', $y='') {
11207 $y = ($this->h
- $y) * $this->k
;
11209 //calculate elements of transformation matrix
11211 $tm[0] = cos(deg2rad($angle));
11212 $tm[1] = sin(deg2rad($angle));
11215 $tm[4] = $x +
($tm[1] * $y) - ($tm[0] * $x);
11216 $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
11217 //rotate the coordinate system around ($x,$y)
11218 $this->Transform($tm);
11222 * Skew horizontally.
11223 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
11224 * @param $x (int) abscissa of the skewing center. default is current x position
11225 * @param $y (int) ordinate of the skewing center. default is current y position
11227 * @since 2.1.000 (2008-01-07)
11228 * @see StartTransform(), StopTransform()
11230 public function SkewX($angle_x, $x='', $y='') {
11231 $this->Skew($angle_x, 0, $x, $y);
11236 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
11237 * @param $x (int) abscissa of the skewing center. default is current x position
11238 * @param $y (int) ordinate of the skewing center. default is current y position
11240 * @since 2.1.000 (2008-01-07)
11241 * @see StartTransform(), StopTransform()
11243 public function SkewY($angle_y, $x='', $y='') {
11244 $this->Skew(0, $angle_y, $x, $y);
11249 * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
11250 * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
11251 * @param $x (int) abscissa of the skewing center. default is current x position
11252 * @param $y (int) ordinate of the skewing center. default is current y position
11254 * @since 2.1.000 (2008-01-07)
11255 * @see StartTransform(), StopTransform()
11257 public function Skew($angle_x, $angle_y, $x='', $y='') {
11264 if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
11265 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
11268 $y = ($this->h
- $y) * $this->k
;
11269 //calculate elements of transformation matrix
11272 $tm[1] = tan(deg2rad($angle_y));
11273 $tm[2] = tan(deg2rad($angle_x));
11275 $tm[4] = -$tm[2] * $y;
11276 $tm[5] = -$tm[1] * $x;
11277 //skew the coordinate system
11278 $this->Transform($tm);
11282 * Apply graphic transformations.
11283 * @param $tm (array) transformation matrix
11285 * @since 2.1.000 (2008-01-07)
11286 * @see StartTransform(), StopTransform()
11288 protected function Transform($tm) {
11289 if ($this->state
!= 2) {
11292 $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
11293 // add tranformation matrix
11294 $this->transfmatrix
[$this->transfmatrix_key
][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
11295 // update transformation mark
11296 if ($this->inxobj
) {
11297 // we are inside an XObject template
11298 if (end($this->xobjects
[$this->xobjid
]['transfmrk']) !== false) {
11299 $key = key($this->xobjects
[$this->xobjid
]['transfmrk']);
11300 $this->xobjects
[$this->xobjid
]['transfmrk'][$key] = strlen($this->xobjects
[$this->xobjid
]['outdata']);
11302 } elseif (end($this->transfmrk
[$this->page
]) !== false) {
11303 $key = key($this->transfmrk
[$this->page
]);
11304 $this->transfmrk
[$this->page
][$key] = $this->pagelen
[$this->page
];
11308 // END TRANSFORMATIONS SECTION -------------------------
11310 // START GRAPHIC FUNCTIONS SECTION ---------------------
11311 // The following section is based on the code provided by David Hernandez Sanz
11314 * 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.
11315 * @param $width (float) The width.
11318 * @see Line(), Rect(), Cell(), MultiCell()
11320 public function SetLineWidth($width) {
11322 $this->LineWidth
= $width;
11323 $this->linestyleWidth
= sprintf('%F w', ($width * $this->k
));
11324 if ($this->state
== 2) {
11325 $this->_out($this->linestyleWidth
);
11330 * Returns the current the line width.
11331 * @return int Line width
11333 * @since 2.1.000 (2008-01-07)
11334 * @see Line(), SetLineWidth()
11336 public function GetLineWidth() {
11337 return $this->LineWidth
;
11342 * @param $style (array) Line style. Array with keys among the following:
11344 * <li>width (float): Width of the line in user units.</li>
11345 * <li>cap (string): Type of cap to put on the line. Possible values are:
11346 * butt, round, square. The difference between "square" and "butt" is that
11347 * "square" projects a flat end past the end of the line.</li>
11348 * <li>join (string): Type of join. Possible values are: miter, round,
11350 * <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
11351 * series of length values, which are the lengths of the on and off dashes.
11352 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
11353 * 1 off, 2 on, 1 off, ...</li>
11354 * <li>phase (integer): Modifier on the dash pattern which is used to shift
11355 * the point at which the pattern starts.</li>
11356 * <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>
11358 * @param $ret (boolean) if true do not send the command.
11359 * @return string the PDF command
11361 * @since 2.1.000 (2008-01-08)
11363 public function SetLineStyle($style, $ret=false) {
11364 $s = ''; // string to be returned
11365 if (!is_array($style)) {
11368 if (isset($style['width'])) {
11369 $this->LineWidth
= $style['width'];
11370 $this->linestyleWidth
= sprintf('%F w', ($style['width'] * $this->k
));
11371 $s .= $this->linestyleWidth
.' ';
11373 if (isset($style['cap'])) {
11374 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
11375 if (isset($ca[$style['cap']])) {
11376 $this->linestyleCap
= $ca[$style['cap']].' J';
11377 $s .= $this->linestyleCap
.' ';
11380 if (isset($style['join'])) {
11381 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
11382 if (isset($ja[$style['join']])) {
11383 $this->linestyleJoin
= $ja[$style['join']].' j';
11384 $s .= $this->linestyleJoin
.' ';
11387 if (isset($style['dash'])) {
11389 if ($style['dash']) {
11390 if (preg_match('/^.+,/', $style['dash']) > 0) {
11391 $tab = explode(',', $style['dash']);
11393 $tab = array($style['dash']);
11396 foreach ($tab as $i => $v) {
11398 $dash_string .= ' ';
11400 $dash_string .= sprintf('%F', $v);
11403 if (!isset($style['phase']) OR !$style['dash']) {
11404 $style['phase'] = 0;
11406 $this->linestyleDash
= sprintf('[%s] %F d', $dash_string, $style['phase']);
11407 $s .= $this->linestyleDash
.' ';
11409 if (isset($style['color'])) {
11410 $s .= $this->SetDrawColorArray($style['color'], true).' ';
11412 if (!$ret AND ($this->state
== 2)) {
11419 * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment.
11420 * @param $x (float) Abscissa of point.
11421 * @param $y (float) Ordinate of point.
11423 * @since 2.1.000 (2008-01-08)
11425 protected function _outPoint($x, $y) {
11426 if ($this->state
== 2) {
11427 $this->_out(sprintf('%F %F m', ($x * $this->k
), (($this->h
- $y) * $this->k
)));
11432 * Append a straight line segment from the current point to the point (x, y).
11433 * The new current point shall be (x, y).
11434 * @param $x (float) Abscissa of end point.
11435 * @param $y (float) Ordinate of end point.
11437 * @since 2.1.000 (2008-01-08)
11439 protected function _outLine($x, $y) {
11440 if ($this->state
== 2) {
11441 $this->_out(sprintf('%F %F l', ($x * $this->k
), (($this->h
- $y) * $this->k
)));
11446 * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space.
11447 * @param $x (float) Abscissa of upper-left corner.
11448 * @param $y (float) Ordinate of upper-left corner.
11449 * @param $w (float) Width.
11450 * @param $h (float) Height.
11451 * @param $op (string) options
11453 * @since 2.1.000 (2008-01-08)
11455 protected function _outRect($x, $y, $w, $h, $op) {
11456 if ($this->state
== 2) {
11457 $this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k
), (($this->h
- $y) * $this->k
), ($w * $this->k
), (-$h * $this->k
), $op));
11462 * 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.
11463 * The new current point shall be (x3, y3).
11464 * @param $x1 (float) Abscissa of control point 1.
11465 * @param $y1 (float) Ordinate of control point 1.
11466 * @param $x2 (float) Abscissa of control point 2.
11467 * @param $y2 (float) Ordinate of control point 2.
11468 * @param $x3 (float) Abscissa of end point.
11469 * @param $y3 (float) Ordinate of end point.
11471 * @since 2.1.000 (2008-01-08)
11473 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
11474 if ($this->state
== 2) {
11475 $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
)));
11480 * 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.
11481 * The new current point shall be (x3, y3).
11482 * @param $x2 (float) Abscissa of control point 2.
11483 * @param $y2 (float) Ordinate of control point 2.
11484 * @param $x3 (float) Abscissa of end point.
11485 * @param $y3 (float) Ordinate of end point.
11487 * @since 4.9.019 (2010-04-26)
11489 protected function _outCurveV($x2, $y2, $x3, $y3) {
11490 if ($this->state
== 2) {
11491 $this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k
), (($this->h
- $y2) * $this->k
), ($x3 * $this->k
), (($this->h
- $y3) * $this->k
)));
11496 * 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.
11497 * The new current point shall be (x3, y3).
11498 * @param $x1 (float) Abscissa of control point 1.
11499 * @param $y1 (float) Ordinate of control point 1.
11500 * @param $x3 (float) Abscissa of end point.
11501 * @param $y3 (float) Ordinate of end point.
11503 * @since 2.1.000 (2008-01-08)
11505 protected function _outCurveY($x1, $y1, $x3, $y3) {
11506 if ($this->state
== 2) {
11507 $this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k
), (($this->h
- $y1) * $this->k
), ($x3 * $this->k
), (($this->h
- $y3) * $this->k
)));
11512 * Draws a line between two points.
11513 * @param $x1 (float) Abscissa of first point.
11514 * @param $y1 (float) Ordinate of first point.
11515 * @param $x2 (float) Abscissa of second point.
11516 * @param $y2 (float) Ordinate of second point.
11517 * @param $style (array) Line style. Array like for SetLineStyle(). Default value: default line style (empty array).
11520 * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
11522 public function Line($x1, $y1, $x2, $y2, $style=array()) {
11523 if ($this->state
!= 2) {
11526 if (is_array($style)) {
11527 $this->SetLineStyle($style);
11529 $this->_outPoint($x1, $y1);
11530 $this->_outLine($x2, $y2);
11535 * Draws a rectangle.
11536 * @param $x (float) Abscissa of upper-left corner.
11537 * @param $y (float) Ordinate of upper-left corner.
11538 * @param $w (float) Width.
11539 * @param $h (float) Height.
11540 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11541 * @param $border_style (array) Border style of rectangle. Array with keys among the following:
11543 * <li>all: Line style of all borders. Array like for SetLineStyle().</li>
11544 * <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for SetLineStyle().</li>
11546 * If a key is not present or is null, the correspondent border is not drawn. Default value: default line style (empty array).
11547 * @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).
11550 * @see SetLineStyle()
11552 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
11553 if ($this->state
!= 2) {
11556 if (empty($style)) {
11559 if (!(strpos($style, 'F') === false) AND !empty($fill_color)) {
11560 // set background color
11561 $this->SetFillColorArray($fill_color);
11563 if (!empty($border_style)) {
11564 if (isset($border_style['all']) AND !empty($border_style['all'])) {
11565 //set global style for border
11566 $this->SetLineStyle($border_style['all']);
11567 $border_style = array();
11569 // remove stroke operator from style
11570 $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*' );
11571 if (isset($opnostroke[$style])) {
11572 $style = $opnostroke[$style];
11576 if (!empty($style)) {
11577 $op = TCPDF_STATIC
::getPathPaintOperator($style);
11578 $this->_outRect($x, $y, $w, $h, $op);
11580 if (!empty($border_style)) {
11581 $border_style2 = array();
11582 foreach ($border_style as $line => $value) {
11583 $length = strlen($line);
11584 for ($i = 0; $i < $length; ++
$i) {
11585 $border_style2[$line[$i]] = $value;
11588 $border_style = $border_style2;
11589 if (isset($border_style['L']) AND $border_style['L']) {
11590 $this->Line($x, $y, $x, $y +
$h, $border_style['L']);
11592 if (isset($border_style['T']) AND $border_style['T']) {
11593 $this->Line($x, $y, $x +
$w, $y, $border_style['T']);
11595 if (isset($border_style['R']) AND $border_style['R']) {
11596 $this->Line($x +
$w, $y, $x +
$w, $y +
$h, $border_style['R']);
11598 if (isset($border_style['B']) AND $border_style['B']) {
11599 $this->Line($x, $y +
$h, $x +
$w, $y +
$h, $border_style['B']);
11605 * Draws a Bezier curve.
11606 * The Bezier curve is a tangent to the line between the control points at
11607 * either end of the curve.
11608 * @param $x0 (float) Abscissa of start point.
11609 * @param $y0 (float) Ordinate of start point.
11610 * @param $x1 (float) Abscissa of control point 1.
11611 * @param $y1 (float) Ordinate of control point 1.
11612 * @param $x2 (float) Abscissa of control point 2.
11613 * @param $y2 (float) Ordinate of control point 2.
11614 * @param $x3 (float) Abscissa of end point.
11615 * @param $y3 (float) Ordinate of end point.
11616 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11617 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
11618 * @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).
11620 * @see SetLineStyle()
11621 * @since 2.1.000 (2008-01-08)
11623 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
11624 if ($this->state
!= 2) {
11627 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11628 $this->SetFillColorArray($fill_color);
11630 $op = TCPDF_STATIC
::getPathPaintOperator($style);
11632 $this->SetLineStyle($line_style);
11634 $this->_outPoint($x0, $y0);
11635 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11640 * Draws a poly-Bezier curve.
11641 * Each Bezier curve segment is a tangent to the line between the control points at
11642 * either end of the curve.
11643 * @param $x0 (float) Abscissa of start point.
11644 * @param $y0 (float) Ordinate of start point.
11645 * @param $segments (float) An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
11646 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11647 * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
11648 * @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).
11650 * @see SetLineStyle()
11651 * @since 3.0008 (2008-05-12)
11653 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
11654 if ($this->state
!= 2) {
11657 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11658 $this->SetFillColorArray($fill_color);
11660 $op = TCPDF_STATIC
::getPathPaintOperator($style);
11662 $line_style = array();
11665 $this->SetLineStyle($line_style);
11667 $this->_outPoint($x0, $y0);
11668 foreach ($segments as $segment) {
11669 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
11670 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11676 * Draws an ellipse.
11677 * An ellipse is formed from n Bezier curves.
11678 * @param $x0 (float) Abscissa of center point.
11679 * @param $y0 (float) Ordinate of center point.
11680 * @param $rx (float) Horizontal radius.
11681 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
11682 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
11683 * @param $astart: (float) Angle start of draw line. Default value: 0.
11684 * @param $afinish: (float) Angle finish of draw line. Default value: 360.
11685 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11686 * @param $line_style (array) Line style of ellipse. Array like for SetLineStyle(). Default value: default line style (empty array).
11687 * @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).
11688 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
11689 * @author Nicola Asuni
11691 * @since 2.1.000 (2008-01-08)
11693 public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11694 if ($this->state
!= 2) {
11697 if (TCPDF_STATIC
::empty_string($ry) OR ($ry == 0)) {
11700 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11701 $this->SetFillColorArray($fill_color);
11703 $op = TCPDF_STATIC
::getPathPaintOperator($style);
11705 $line_style = array();
11708 $this->SetLineStyle($line_style);
11710 $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
11715 * Append an elliptical arc to the current path.
11716 * An ellipse is formed from n Bezier curves.
11717 * @param $xc (float) Abscissa of center point.
11718 * @param $yc (float) Ordinate of center point.
11719 * @param $rx (float) Horizontal radius.
11720 * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
11721 * @param $xang: (float) Angle between the X-axis and the major axis of the ellipse. Default value: 0.
11722 * @param $angs: (float) Angle start of draw line. Default value: 0.
11723 * @param $angf: (float) Angle finish of draw line. Default value: 360.
11724 * @param $pie (boolean) if true do not mark the border point (used to draw pie sectors).
11725 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
11726 * @param $startpoint (boolean) if true output a starting point.
11727 * @param $ccw (boolean) if true draws in counter-clockwise.
11728 * @param $svg (boolean) if true the angles are in svg mode (already calculated).
11729 * @return array bounding box coordinates (x min, y min, x max, y max)
11730 * @author Nicola Asuni
11732 * @since 4.9.019 (2010-04-26)
11734 protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
11735 if (($rx <= 0) OR ($ry < 0)) {
11742 $xmin = 2147483647;
11743 $ymin = 2147483647;
11747 // center of the arc
11748 $this->_outPoint($xc, $yc);
11750 $xang = deg2rad((float) $xang);
11751 $angs = deg2rad((float) $angs);
11752 $angf = deg2rad((float) $angf);
11757 $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
11758 $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
11766 if ($ccw AND ($as > $af)) {
11767 // reverse rotation
11769 } elseif (!$ccw AND ($as < $af)) {
11770 // reverse rotation
11773 $total_angle = ($af - $as);
11777 // total arcs to draw
11778 $nc *= (2 * abs($total_angle) / M_PI
);
11779 $nc = round($nc) +
1;
11780 // angle of each arc
11781 $arcang = ($total_angle / $nc);
11782 // center point in PDF coordinates
11784 $y0 = ($this->h
- $yc);
11787 $alpha = sin($arcang) * ((sqrt(4 +
(3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
11788 $cos_xang = cos($xang);
11789 $sin_xang = sin($xang);
11790 $cos_ang = cos($ang);
11791 $sin_ang = sin($ang);
11793 $px1 = $x0 +
($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11794 $py1 = $y0 +
($rx * $sin_xang * $cos_ang) +
($ry * $cos_xang * $sin_ang);
11795 // first Bezier control point
11796 $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11797 $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) +
($ry * $cos_xang * $cos_ang)));
11799 // line from center to arc starting point
11800 $this->_outLine($px1, $this->h
- $py1);
11801 } elseif ($startpoint) {
11802 // arc starting point
11803 $this->_outPoint($px1, $this->h
- $py1);
11806 for ($i = 1; $i <= $nc; ++
$i) {
11808 $ang = $as +
($i * $arcang);
11812 $cos_ang = cos($ang);
11813 $sin_ang = sin($ang);
11814 // second arc point
11815 $px2 = $x0 +
($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11816 $py2 = $y0 +
($rx * $sin_xang * $cos_ang) +
($ry * $cos_xang * $sin_ang);
11817 // second Bezier control point
11818 $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11819 $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) +
($ry * $cos_xang * $cos_ang)));
11821 $cx1 = ($px1 +
$qx1);
11822 $cy1 = ($this->h
- ($py1 +
$qy1));
11823 $cx2 = ($px2 - $qx2);
11824 $cy2 = ($this->h
- ($py2 - $qy2));
11826 $cy3 = ($this->h
- $py2);
11827 $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
11828 // get bounding box coordinates
11829 $xmin = min($xmin, $cx1, $cx2, $cx3);
11830 $ymin = min($ymin, $cy1, $cy2, $cy3);
11831 $xmax = max($xmax, $cx1, $cx2, $cx3);
11832 $ymax = max($ymax, $cy1, $cy2, $cy3);
11833 // move to next point
11840 $this->_outLine($xc, $yc);
11841 // get bounding box coordinates
11842 $xmin = min($xmin, $xc);
11843 $ymin = min($ymin, $yc);
11844 $xmax = max($xmax, $xc);
11845 $ymax = max($ymax, $yc);
11847 return array($xmin, $ymin, $xmax, $ymax);
11852 * A circle is formed from n Bezier curves.
11853 * @param $x0 (float) Abscissa of center point.
11854 * @param $y0 (float) Ordinate of center point.
11855 * @param $r (float) Radius.
11856 * @param $angstr: (float) Angle start of draw line. Default value: 0.
11857 * @param $angend: (float) Angle finish of draw line. Default value: 360.
11858 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11859 * @param $line_style (array) Line style of circle. Array like for SetLineStyle(). Default value: default line style (empty array).
11860 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
11861 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of circle.
11863 * @since 2.1.000 (2008-01-08)
11865 public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11866 $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
11870 * Draws a polygonal line
11871 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
11872 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11873 * @param $line_style (array) Line style of polygon. Array with keys among the following:
11875 * <li>all: Line style of all lines. Array like for SetLineStyle().</li>
11876 * <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
11878 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
11879 * @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).
11880 * @since 4.8.003 (2009-09-15)
11883 public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
11884 $this->Polygon($p, $style, $line_style, $fill_color, false);
11889 * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
11890 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11891 * @param $line_style (array) Line style of polygon. Array with keys among the following:
11893 * <li>all: Line style of all lines. Array like for SetLineStyle().</li>
11894 * <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
11896 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
11897 * @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).
11898 * @param $closed (boolean) if true the polygon is closes, otherwise will remain open
11900 * @since 2.1.000 (2008-01-08)
11902 public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
11903 if ($this->state
!= 2) {
11906 $nc = count($p); // number of coordinates
11907 $np = $nc / 2; // number of points
11909 // close polygon by adding the first 2 points at the end (one line)
11910 for ($i = 0; $i < 4; ++
$i) {
11911 $p[$nc +
$i] = $p[$i];
11913 // copy style for the last added line
11914 if (isset($line_style[0])) {
11915 $line_style[$np] = $line_style[0];
11919 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11920 $this->SetFillColorArray($fill_color);
11922 $op = TCPDF_STATIC
::getPathPaintOperator($style);
11924 $line_style = array();
11928 if (isset($line_style['all'])) {
11929 $this->SetLineStyle($line_style['all']);
11935 $this->_outPoint($p[0], $p[1]);
11936 for ($i = 2; $i < $nc; $i = $i +
2) {
11937 $this->_outLine($p[$i], $p[$i +
1]);
11942 $this->_outPoint($p[0], $p[1]);
11943 for ($i = 2; $i < $nc; $i = $i +
2) {
11944 $line_num = ($i / 2) - 1;
11945 if (isset($line_style[$line_num])) {
11946 if ($line_style[$line_num] != 0) {
11947 if (is_array($line_style[$line_num])) {
11949 $this->SetLineStyle($line_style[$line_num]);
11950 $this->_outPoint($p[$i - 2], $p[$i - 1]);
11951 $this->_outLine($p[$i], $p[$i +
1]);
11953 $this->_outPoint($p[$i], $p[$i +
1]);
11955 $this->_outLine($p[$i], $p[$i +
1]);
11959 $this->_outLine($p[$i], $p[$i +
1]);
11966 $this->_outPoint($p[0], $p[1]);
11967 for ($i = 2; $i < $nc; $i = $i +
2) {
11968 $this->_outLine($p[$i], $p[$i +
1]);
11975 * Draws a regular polygon.
11976 * @param $x0 (float) Abscissa of center point.
11977 * @param $y0 (float) Ordinate of center point.
11978 * @param $r: (float) Radius of inscribed circle.
11979 * @param $ns (integer) Number of sides.
11980 * @param $angle (float) Angle oriented (anti-clockwise). Default value: 0.
11981 * @param $draw_circle (boolean) Draw inscribed circle or not. Default value: false.
11982 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11983 * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
11985 * <li>all: Line style of all sides. Array like for SetLineStyle().</li>
11986 * <li>0 to ($ns - 1): Line style of each side. Array like for SetLineStyle().</li>
11988 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
11989 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
11990 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
11992 * <li>D or empty string: Draw (default).</li>
11993 * <li>F: Fill.</li>
11994 * <li>DF or FD: Draw and fill.</li>
11995 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
11996 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
11998 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
11999 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
12001 * @since 2.1.000 (2008-01-08)
12003 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()) {
12007 if ($draw_circle) {
12008 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12011 for ($i = 0; $i < $ns; ++
$i) {
12012 $a = $angle +
($i * 360 / $ns);
12013 $a_rad = deg2rad((float) $a);
12014 $p[] = $x0 +
($r * sin($a_rad));
12015 $p[] = $y0 +
($r * cos($a_rad));
12017 $this->Polygon($p, $style, $line_style, $fill_color);
12021 * Draws a star polygon
12022 * @param $x0 (float) Abscissa of center point.
12023 * @param $y0 (float) Ordinate of center point.
12024 * @param $r (float) Radius of inscribed circle.
12025 * @param $nv (integer) Number of vertices.
12026 * @param $ng (integer) Number of gap (if ($ng % $nv = 1) then is a regular polygon).
12027 * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
12028 * @param $draw_circle: (boolean) Draw inscribed circle or not. Default value is false.
12029 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12030 * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
12032 * <li>all: Line style of all sides. Array like for
12033 * SetLineStyle().</li>
12034 * <li>0 to (n - 1): Line style of each side. Array like for SetLineStyle().</li>
12036 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
12037 * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
12038 * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
12040 * <li>D or empty string: Draw (default).</li>
12041 * <li>F: Fill.</li>
12042 * <li>DF or FD: Draw and fill.</li>
12043 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
12044 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
12046 * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
12047 * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
12049 * @since 2.1.000 (2008-01-08)
12051 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()) {
12055 if ($draw_circle) {
12056 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12059 $visited = array();
12060 for ($i = 0; $i < $nv; ++
$i) {
12061 $a = $angle +
($i * 360 / $nv);
12062 $a_rad = deg2rad((float) $a);
12063 $p2[] = $x0 +
($r * sin($a_rad));
12064 $p2[] = $y0 +
($r * cos($a_rad));
12065 $visited[] = false;
12070 $p[] = $p2[$i * 2];
12071 $p[] = $p2[($i * 2) +
1];
12072 $visited[$i] = true;
12075 } while (!$visited[$i]);
12076 $this->Polygon($p, $style, $line_style, $fill_color);
12080 * Draws a rounded rectangle.
12081 * @param $x (float) Abscissa of upper-left corner.
12082 * @param $y (float) Ordinate of upper-left corner.
12083 * @param $w (float) Width.
12084 * @param $h (float) Height.
12085 * @param $r (float) the radius of the circle used to round off the corners of the rectangle.
12086 * @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").
12087 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12088 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12089 * @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).
12091 * @since 2.1.000 (2008-01-08)
12093 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12094 $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
12098 * Draws a rounded rectangle.
12099 * @param $x (float) Abscissa of upper-left corner.
12100 * @param $y (float) Ordinate of upper-left corner.
12101 * @param $w (float) Width.
12102 * @param $h (float) Height.
12103 * @param $rx (float) the x-axis radius of the ellipse used to round off the corners of the rectangle.
12104 * @param $ry (float) the y-axis radius of the ellipse used to round off the corners of the rectangle.
12105 * @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").
12106 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12107 * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12108 * @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).
12110 * @since 4.9.019 (2010-04-22)
12112 public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12113 if ($this->state
!= 2) {
12116 if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
12118 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
12122 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12123 $this->SetFillColorArray($fill_color);
12125 $op = TCPDF_STATIC
::getPathPaintOperator($style);
12127 $border_style = array();
12129 if ($border_style) {
12130 $this->SetLineStyle($border_style);
12132 $MyArc = 4 / 3 * (sqrt(2) - 1);
12133 $this->_outPoint($x +
$rx, $y);
12134 $xc = $x +
$w - $rx;
12136 $this->_outLine($xc, $y);
12137 if ($round_corner[0]) {
12138 $this->_outCurve($xc +
($rx * $MyArc), $yc - $ry, $xc +
$rx, $yc - ($ry * $MyArc), $xc +
$rx, $yc);
12140 $this->_outLine($x +
$w, $y);
12142 $xc = $x +
$w - $rx;
12143 $yc = $y +
$h - $ry;
12144 $this->_outLine($x +
$w, $yc);
12145 if ($round_corner[1]) {
12146 $this->_outCurve($xc +
$rx, $yc +
($ry * $MyArc), $xc +
($rx * $MyArc), $yc +
$ry, $xc, $yc +
$ry);
12148 $this->_outLine($x +
$w, $y +
$h);
12151 $yc = $y +
$h - $ry;
12152 $this->_outLine($xc, $y +
$h);
12153 if ($round_corner[2]) {
12154 $this->_outCurve($xc - ($rx * $MyArc), $yc +
$ry, $xc - $rx, $yc +
($ry * $MyArc), $xc - $rx, $yc);
12156 $this->_outLine($x, $y +
$h);
12160 $this->_outLine($x, $yc);
12161 if ($round_corner[3]) {
12162 $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
12164 $this->_outLine($x, $y);
12165 $this->_outLine($x +
$rx, $y);
12171 * Draws a grahic arrow.
12172 * @param $x0 (float) Abscissa of first point.
12173 * @param $y0 (float) Ordinate of first point.
12174 * @param $x1 (float) Abscissa of second point.
12175 * @param $y1 (float) Ordinate of second point.
12176 * @param $head_style (int) (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead)
12177 * @param $arm_size (float) length of arrowhead arms
12178 * @param $arm_angle (int) angle between an arm and the shaft
12179 * @author Piotr Galecki, Nicola Asuni, Andy Meier
12180 * @since 4.6.018 (2009-07-10)
12182 public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
12183 // getting arrow direction angle
12184 // 0 deg angle is when both arms go along X axis. angle grows clockwise.
12185 $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
12186 if ($dir_angle < 0) {
12187 $dir_angle +
= (2 * M_PI
);
12189 $arm_angle = deg2rad($arm_angle);
12192 if ($head_style > 0) {
12193 // calculate the stopping point for the arrow shaft
12194 $sx1 = $x1 +
(($arm_size - $this->LineWidth
) * cos($dir_angle));
12195 $sy1 = $y1 +
(($arm_size - $this->LineWidth
) * sin($dir_angle));
12197 // main arrow line / shaft
12198 $this->Line($x0, $y0, $sx1, $sy1);
12199 // left arrowhead arm tip
12200 $x2L = $x1 +
($arm_size * cos($dir_angle +
$arm_angle));
12201 $y2L = $y1 +
($arm_size * sin($dir_angle +
$arm_angle));
12202 // right arrowhead arm tip
12203 $x2R = $x1 +
($arm_size * cos($dir_angle - $arm_angle));
12204 $y2R = $y1 +
($arm_size * sin($dir_angle - $arm_angle));
12207 switch ($head_style) {
12209 // draw only arrowhead arms
12211 $style = array(1, 1, 0);
12215 // draw closed arrowhead, but no fill
12220 // closed and filled arrowhead
12225 // filled arrowhead
12230 $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
12233 // END GRAPHIC FUNCTIONS SECTION -----------------------
12236 * Add a Named Destination.
12237 * NOTE: destination names are unique, so only last entry will be saved.
12238 * @param $name (string) Destination name.
12239 * @param $y (float) Y position in user units of the destiantion on the selected page (default = -1 = current position; 0 = page start;).
12240 * @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.
12241 * @param $x (float) X position in user units of the destiantion on the selected page (default = -1 = current position;).
12242 * @return (string) Stripped named destination identifier or false in case of error.
12244 * @author Christian Deligant, Nicola Asuni
12245 * @since 5.9.097 (2011-06-23)
12247 public function setDestination($name, $y=-1, $page='', $x=-1) {
12248 // remove unsupported characters
12249 $name = TCPDF_STATIC
::encodeNameObject($name);
12250 if (TCPDF_STATIC
::empty_string($name)) {
12254 $y = $this->GetY();
12255 } elseif ($y < 0) {
12257 } elseif ($y > $this->h
) {
12261 $x = $this->GetX();
12262 } elseif ($x < 0) {
12264 } elseif ($x > $this->w
) {
12268 if (!empty($page) AND ($page[0] == '*')) {
12269 $page = intval(substr($page, 1));
12270 // this page number will not be changed when moving/add/deleting pages
12273 if (empty($page)) {
12274 $page = $this->PageNo();
12275 if (empty($page)) {
12279 $this->dests
[$name] = array('x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed);
12284 * Return the Named Destination array.
12285 * @return (array) Named Destination array.
12287 * @author Nicola Asuni
12288 * @since 5.9.097 (2011-06-23)
12290 public function getDestination() {
12291 return $this->dests
;
12295 * Insert Named Destinations.
12297 * @author Johannes G\FCntert, Nicola Asuni
12298 * @since 5.9.098 (2011-06-23)
12300 protected function _putdests() {
12301 if (empty($this->dests
)) {
12304 $this->n_dests
= $this->_newobj();
12306 foreach($this->dests
as $name => $o) {
12307 $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
)));
12310 $out .= "\n".'endobj';
12315 * Adds a bookmark - alias for Bookmark().
12316 * @param $txt (string) Bookmark description.
12317 * @param $level (int) Bookmark level (minimum value is 0).
12318 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
12319 * @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.
12320 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic.
12321 * @param $color (array) RGB color array (values from 0 to 255).
12322 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;).
12323 * @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).
12326 public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12327 $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link);
12332 * @param $txt (string) Bookmark description.
12333 * @param $level (int) Bookmark level (minimum value is 0).
12334 * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
12335 * @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.
12336 * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic.
12337 * @param $color (array) RGB color array (values from 0 to 255).
12338 * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;).
12339 * @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).
12341 * @since 2.1.002 (2008-02-12)
12343 public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12347 if (isset($this->outlines
[0])) {
12348 $lastoutline = end($this->outlines
);
12349 $maxlevel = $lastoutline['l'] +
1;
12353 if ($level > $maxlevel) {
12354 $level = $maxlevel;
12357 $y = $this->GetY();
12358 } elseif ($y < 0) {
12360 } elseif ($y > $this->h
) {
12364 $x = $this->GetX();
12365 } elseif ($x < 0) {
12367 } elseif ($x > $this->w
) {
12371 if (!empty($page) AND ($page[0] == '*')) {
12372 $page = intval(substr($page, 1));
12373 // this page number will not be changed when moving/add/deleting pages
12376 if (empty($page)) {
12377 $page = $this->PageNo();
12378 if (empty($page)) {
12382 $this->outlines
[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed, 's' => strtoupper($style), 'c' => $color, 'u' => $link);
12386 * Sort bookmarks for page and key.
12388 * @since 5.9.119 (2011-09-19)
12390 protected function sortBookmarks() {
12391 // get sorting columns
12392 $outline_p = array();
12393 $outline_y = array();
12394 foreach ($this->outlines
as $key => $row) {
12395 $outline_p[$key] = $row['p'];
12396 $outline_k[$key] = $key;
12398 // sort outlines by page and original position
12399 array_multisort($outline_p, SORT_NUMERIC
, SORT_ASC
, $outline_k, SORT_NUMERIC
, SORT_ASC
, $this->outlines
);
12403 * Create a bookmark PDF string.
12405 * @author Olivier Plathey, Nicola Asuni
12406 * @since 2.1.002 (2008-02-12)
12408 protected function _putbookmarks() {
12409 $nb = count($this->outlines
);
12414 $this->sortBookmarks();
12417 foreach ($this->outlines
as $i => $o) {
12419 $parent = $lru[($o['l'] - 1)];
12420 //Set parent and last pointers
12421 $this->outlines
[$i]['parent'] = $parent;
12422 $this->outlines
[$parent]['last'] = $i;
12423 if ($o['l'] > $level) {
12424 //Level increasing: set first pointer
12425 $this->outlines
[$parent]['first'] = $i;
12428 $this->outlines
[$i]['parent'] = $nb;
12430 if (($o['l'] <= $level) AND ($i > 0)) {
12431 //Set prev and next pointers
12432 $prev = $lru[$o['l']];
12433 $this->outlines
[$prev]['next'] = $i;
12434 $this->outlines
[$i]['prev'] = $prev;
12436 $lru[$o['l']] = $i;
12441 $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';
12442 foreach ($this->outlines
as $i => $o) {
12443 $oid = $this->_newobj();
12444 // covert HTML title to string
12445 $title = preg_replace($nltags, "\n", $o['t']);
12446 $title = preg_replace("/[\r]+/si", '', $title);
12447 $title = preg_replace("/[\n]+/si", "\n", $title);
12448 $title = strip_tags($title);
12449 $title = $this->stringTrim($title);
12450 $out = '<</Title '.$this->_textstring($title, $oid);
12451 $out .= ' /Parent '.($n +
$o['parent']).' 0 R';
12452 if (isset($o['prev'])) {
12453 $out .= ' /Prev '.($n +
$o['prev']).' 0 R';
12455 if (isset($o['next'])) {
12456 $out .= ' /Next '.($n +
$o['next']).' 0 R';
12458 if (isset($o['first'])) {
12459 $out .= ' /First '.($n +
$o['first']).' 0 R';
12461 if (isset($o['last'])) {
12462 $out .= ' /Last '.($n +
$o['last']).' 0 R';
12464 if (isset($o['u']) AND !empty($o['u'])) {
12466 if (is_string($o['u'])) {
12467 if ($o['u'][0] == '#') {
12468 // internal destination
12469 $out .= ' /Dest /'.TCPDF_STATIC
::encodeNameObject(substr($o['u'], 1));
12470 } elseif ($o['u'][0] == '%') {
12471 // embedded PDF file
12472 $filename = basename(substr($o['u'], 1));
12473 $out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles
[$filename]['a'].' >> >>';
12474 } elseif ($o['u'][0] == '*') {
12475 // embedded generic file
12476 $filename = basename(substr($o['u'], 1));
12477 $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});';
12478 $out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>';
12480 // external URI link
12481 $out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>';
12483 } elseif (isset($this->links
[$o['u']])) {
12484 // internal link ID
12485 $l = $this->links
[$o['u']];
12486 if (isset($this->page_obj_id
[($l['p'])])) {
12487 $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
)));
12490 } elseif (isset($this->page_obj_id
[($o['p'])])) {
12492 $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
)));
12496 if (!empty($o['s'])) {
12498 if (strpos($o['s'], 'B') !== false) {
12502 if (strpos($o['s'], 'I') !== false) {
12506 $out .= sprintf(' /F %d', $style);
12507 // set bookmark color
12508 if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
12509 $color = array_values($o['c']);
12510 $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
12513 $out .= ' /C [0.0 0.0 0.0]';
12515 $out .= ' /Count 0'; // normally closed item
12517 $out .= "\n".'endobj';
12521 $this->OutlineRoot
= $this->_newobj();
12522 $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n +
$lru[0]).' 0 R >>'."\n".'endobj');
12525 // --- JAVASCRIPT ------------------------------------------------------
12528 * Adds a javascript
12529 * @param $script (string) Javascript code
12531 * @author Johannes G\FCntert, Nicola Asuni
12532 * @since 2.1.002 (2008-02-12)
12534 public function IncludeJS($script) {
12535 $this->javascript
.= $script;
12539 * Adds a javascript object and return object ID
12540 * @param $script (string) Javascript code
12541 * @param $onload (boolean) if true executes this object when opening the document
12542 * @return int internal object ID
12544 * @author Nicola Asuni
12545 * @since 4.8.000 (2009-09-07)
12547 public function addJavascriptObject($script, $onload=false) {
12548 if ($this->pdfa_mode
) {
12549 // javascript is not allowed in PDF/A mode
12553 $this->js_objects
[$this->n
] = array('n' => $this->n
, 'js' => $script, 'onload' => $onload);
12558 * Create a javascript PDF string.
12560 * @author Johannes G\FCntert, Nicola Asuni
12561 * @since 2.1.002 (2008-02-12)
12563 protected function _putjavascript() {
12564 if ($this->pdfa_mode
OR (empty($this->javascript
) AND empty($this->js_objects
))) {
12567 if (strpos($this->javascript
, 'this.addField') > 0) {
12568 if (!$this->ur
['enabled']) {
12569 //$this->setUserRights();
12571 // the following two lines are used to avoid form fields duplication after saving
12572 // The addField method only works when releasing user rights (UR3)
12573 $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
12574 $jsb = "getField('tcpdfdocsaved').value='saved';";
12575 $this->javascript
= $jsa."\n".$this->javascript
."\n".$jsb;
12577 // name tree for javascript
12578 $this->n_js
= '<< /Names [';
12579 if (!empty($this->javascript
)) {
12580 $this->n_js
.= ' (EmbeddedJS) '.($this->n +
1).' 0 R';
12582 if (!empty($this->js_objects
)) {
12583 foreach ($this->js_objects
as $key => $val) {
12584 if ($val['onload']) {
12585 $this->n_js
.= ' (JS'.$key.') '.$key.' 0 R';
12589 $this->n_js
.= ' ] >>';
12590 // default Javascript object
12591 if (!empty($this->javascript
)) {
12592 $obj_id = $this->_newobj();
12593 $out = '<< /S /JavaScript';
12594 $out .= ' /JS '.$this->_textstring($this->javascript
, $obj_id);
12596 $out .= "\n".'endobj';
12599 // additional Javascript objects
12600 if (!empty($this->js_objects
)) {
12601 foreach ($this->js_objects
as $key => $val) {
12602 $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
12609 * Adds a javascript form field.
12610 * @param $type (string) field type
12611 * @param $name (string) field name
12612 * @param $x (int) horizontal position
12613 * @param $y (int) vertical position
12614 * @param $w (int) width
12615 * @param $h (int) height
12616 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12618 * @author Denis Van Nuffelen, Nicola Asuni
12619 * @since 2.1.002 (2008-02-12)
12621 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
12625 // the followind avoid fields duplication after saving the document
12626 $this->javascript
.= "if (getField('tcpdfdocsaved').value != 'saved') {";
12628 $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";
12629 $this->javascript
.= 'f'.$name.'.textSize='.$this->FontSizePt
.";\n";
12630 while (list($key, $val) = each($prop)) {
12631 if (strcmp(substr($key, -5), 'Color') == 0) {
12632 $val = TCPDF_COLORS
::_JScolor($val);
12634 $val = "'".$val."'";
12636 $this->javascript
.= 'f'.$name.'.'.$key.'='.$val.";\n";
12643 $this->javascript
.= '}';
12646 // --- FORM FIELDS -----------------------------------------------------
12651 * Set default properties for form fields.
12652 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12654 * @author Nicola Asuni
12655 * @since 4.8.000 (2009-09-06)
12657 public function setFormDefaultProp($prop=array()) {
12658 $this->default_form_prop
= $prop;
12662 * Return the default properties for form fields.
12663 * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12665 * @author Nicola Asuni
12666 * @since 4.8.000 (2009-09-06)
12668 public function getFormDefaultProp() {
12669 return $this->default_form_prop
;
12673 * Creates a text field
12674 * @param $name (string) field name
12675 * @param $w (float) Width of the rectangle
12676 * @param $h (float) Height of the rectangle
12677 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12678 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
12679 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12680 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12681 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12683 * @author Nicola Asuni
12684 * @since 4.8.000 (2009-09-07)
12686 public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12693 // check page for no-write regions and adapt page margins if necessary
12694 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12696 $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
12699 // get default style
12700 $prop = array_merge($this->getFormDefaultProp(), $prop);
12701 // get annotation data
12702 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
12703 // set default appearance stream
12704 $this->annotation_fonts
[$this->CurrentFont
['fontkey']] = $this->CurrentFont
['i'];
12705 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont
['i'], $this->FontSizePt
, $this->TextColor
);
12706 $popt['da'] = $fontstyle;
12707 // build appearance stream
12708 $popt['ap'] = array();
12709 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12711 if (isset($prop['value']) AND !empty($prop['value'])) {
12712 $text = $prop['value'];
12713 } elseif (isset($opt['v']) AND !empty($opt['v'])) {
12716 $tmpid = $this->startTemplate($w, $h, false);
12718 if (isset($popt['q'])) {
12719 switch ($popt['q']) {
12738 $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12739 $this->endTemplate();
12741 $popt['ap']['n'] .= $this->xobjects
[$tmpid]['outdata'];
12742 unset($this->xobjects
[$tmpid]);
12743 $popt['ap']['n'] .= 'Q EMC';
12745 $opt = array_merge($popt, $opt);
12746 // remove some conflicting options
12748 // set remaining annotation data
12749 $opt['Subtype'] = 'Widget';
12752 // Additional annotation's parameters (check _putannotsobj() method):
12762 //$opt['mk']['bc'];
12763 //$opt['mk']['bg'];
12764 unset($opt['mk']['ca']);
12765 unset($opt['mk']['rc']);
12766 unset($opt['mk']['ac']);
12767 unset($opt['mk']['i']);
12768 unset($opt['mk']['ri']);
12769 unset($opt['mk']['ix']);
12770 unset($opt['mk']['if']);
12771 //$opt['mk']['if']['sw'];
12772 //$opt['mk']['if']['s'];
12773 //$opt['mk']['if']['a'];
12774 //$opt['mk']['if']['fb'];
12775 unset($opt['mk']['tp']);
12784 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12793 * Creates a RadioButton field.
12794 * @param $name (string) Field name.
12795 * @param $w (int) Width of the radio button.
12796 * @param $prop (array) Javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12797 * @param $opt (array) Annotation parameters. Possible values are described on official PDF32000_2008 reference.
12798 * @param $onvalue (string) Value to be returned if selected.
12799 * @param $checked (boolean) Define the initial state.
12800 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12801 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12802 * @param $js (boolean) If true put the field using JavaScript (requires Acrobat Writer to be rendered).
12804 * @author Nicola Asuni
12805 * @since 4.8.000 (2009-09-07)
12807 public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
12814 // check page for no-write regions and adapt page margins if necessary
12815 list($x, $y) = $this->checkPageRegions($w, $x, $y);
12817 $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
12820 if (TCPDF_STATIC
::empty_string($onvalue)) {
12824 $defval = $onvalue;
12829 $font = 'zapfdingbats';
12830 if ($this->pdfa_mode
) {
12831 // all fonts must be embedded
12832 $font = 'pdfa'.$font;
12834 $this->AddFont($font);
12835 $tmpfont = $this->getFontBuffer($font);
12836 // set data for parent group
12837 if (!isset($this->radiobutton_groups
[$this->page
])) {
12838 $this->radiobutton_groups
[$this->page
] = array();
12840 if (!isset($this->radiobutton_groups
[$this->page
][$name])) {
12841 $this->radiobutton_groups
[$this->page
][$name] = array();
12843 $this->radiobutton_groups
[$this->page
][$name]['n'] = $this->n
;
12844 $this->radio_groups
[] = $this->n
;
12846 $kid = ($this->n +
1);
12847 // save object ID to be added on Kids entry on parent object
12848 $this->radiobutton_groups
[$this->page
][$name][] = array('kid' => $kid, 'def' => $defval);
12849 // get default style
12850 $prop = array_merge($this->getFormDefaultProp(), $prop);
12851 $prop['NoToggleToOff'] = 'true';
12852 $prop['Radio'] = 'true';
12853 $prop['borderStyle'] = 'inset';
12854 // get annotation data
12855 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
12856 // set additional default options
12857 $this->annotation_fonts
[$tmpfont['fontkey']] = $tmpfont['i'];
12858 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt
, $this->TextColor
);
12859 $popt['da'] = $fontstyle;
12860 // build appearance stream
12861 $popt['ap'] = array();
12862 $popt['ap']['n'] = array();
12863 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k
);
12864 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt
/ 1000) / $this->k
)) * $this->k
);
12865 $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);
12866 $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);
12867 if (!isset($popt['mk'])) {
12868 $popt['mk'] = array();
12870 $popt['mk']['ca'] = '(l)';
12872 $opt = array_merge($popt, $opt);
12873 // set remaining annotation data
12874 $opt['Subtype'] = 'Widget';
12875 $opt['ft'] = 'Btn';
12877 $opt['v'] = array('/'.$onvalue);
12878 $opt['as'] = $onvalue;
12880 $opt['as'] = 'Off';
12882 // store readonly flag
12883 if (!isset($this->radiobutton_groups
[$this->page
][$name]['#readonly#'])) {
12884 $this->radiobutton_groups
[$this->page
][$name]['#readonly#'] = false;
12886 $this->radiobutton_groups
[$this->page
][$name]['#readonly#'] |
= ($opt['f'] & 64);
12887 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
12896 * Creates a List-box field
12897 * @param $name (string) field name
12898 * @param $w (int) width
12899 * @param $h (int) height
12900 * @param $values (array) array containing the list of values.
12901 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12902 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
12903 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12904 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12905 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12907 * @author Nicola Asuni
12908 * @since 4.8.000 (2009-09-07)
12910 public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12917 // check page for no-write regions and adapt page margins if necessary
12918 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12920 $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
12922 foreach ($values as $value) {
12923 if (is_array($value)) {
12924 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12926 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12929 $this->javascript
.= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12932 // get default style
12933 $prop = array_merge($this->getFormDefaultProp(), $prop);
12934 // get annotation data
12935 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
12936 // set additional default values
12937 $this->annotation_fonts
[$this->CurrentFont
['fontkey']] = $this->CurrentFont
['i'];
12938 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont
['i'], $this->FontSizePt
, $this->TextColor
);
12939 $popt['da'] = $fontstyle;
12940 // build appearance stream
12941 $popt['ap'] = array();
12942 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12944 foreach($values as $item) {
12945 if (is_array($item)) {
12946 $text .= $item[1]."\n";
12948 $text .= $item."\n";
12951 $tmpid = $this->startTemplate($w, $h, false);
12952 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12953 $this->endTemplate();
12955 $popt['ap']['n'] .= $this->xobjects
[$tmpid]['outdata'];
12956 unset($this->xobjects
[$tmpid]);
12957 $popt['ap']['n'] .= 'Q EMC';
12959 $opt = array_merge($popt, $opt);
12960 // set remaining annotation data
12961 $opt['Subtype'] = 'Widget';
12964 $opt['opt'] = $values;
12965 unset($opt['mk']['ca']);
12966 unset($opt['mk']['rc']);
12967 unset($opt['mk']['ac']);
12968 unset($opt['mk']['i']);
12969 unset($opt['mk']['ri']);
12970 unset($opt['mk']['ix']);
12971 unset($opt['mk']['if']);
12972 unset($opt['mk']['tp']);
12973 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12982 * Creates a Combo-box field
12983 * @param $name (string) field name
12984 * @param $w (int) width
12985 * @param $h (int) height
12986 * @param $values (array) array containing the list of values.
12987 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12988 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
12989 * @param $x (float) Abscissa of the upper-left corner of the rectangle
12990 * @param $y (float) Ordinate of the upper-left corner of the rectangle
12991 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12993 * @author Nicola Asuni
12994 * @since 4.8.000 (2009-09-07)
12996 public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13003 // check page for no-write regions and adapt page margins if necessary
13004 list($x, $y) = $this->checkPageRegions($h, $x, $y);
13006 $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
13008 foreach ($values as $value) {
13009 if (is_array($value)) {
13010 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
13012 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
13015 $this->javascript
.= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
13018 // get default style
13019 $prop = array_merge($this->getFormDefaultProp(), $prop);
13020 $prop['Combo'] = true;
13021 // get annotation data
13022 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
13023 // set additional default options
13024 $this->annotation_fonts
[$this->CurrentFont
['fontkey']] = $this->CurrentFont
['i'];
13025 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont
['i'], $this->FontSizePt
, $this->TextColor
);
13026 $popt['da'] = $fontstyle;
13027 // build appearance stream
13028 $popt['ap'] = array();
13029 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13031 foreach($values as $item) {
13032 if (is_array($item)) {
13033 $text .= $item[1]."\n";
13035 $text .= $item."\n";
13038 $tmpid = $this->startTemplate($w, $h, false);
13039 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13040 $this->endTemplate();
13042 $popt['ap']['n'] .= $this->xobjects
[$tmpid]['outdata'];
13043 unset($this->xobjects
[$tmpid]);
13044 $popt['ap']['n'] .= 'Q EMC';
13046 $opt = array_merge($popt, $opt);
13047 // set remaining annotation data
13048 $opt['Subtype'] = 'Widget';
13051 $opt['opt'] = $values;
13052 unset($opt['mk']['ca']);
13053 unset($opt['mk']['rc']);
13054 unset($opt['mk']['ac']);
13055 unset($opt['mk']['i']);
13056 unset($opt['mk']['ri']);
13057 unset($opt['mk']['ix']);
13058 unset($opt['mk']['if']);
13059 unset($opt['mk']['tp']);
13060 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13069 * Creates a CheckBox field
13070 * @param $name (string) field name
13071 * @param $w (int) width
13072 * @param $checked (boolean) define the initial state.
13073 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13074 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13075 * @param $onvalue (string) value to be returned if selected.
13076 * @param $x (float) Abscissa of the upper-left corner of the rectangle
13077 * @param $y (float) Ordinate of the upper-left corner of the rectangle
13078 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13080 * @author Nicola Asuni
13081 * @since 4.8.000 (2009-09-07)
13083 public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
13090 // check page for no-write regions and adapt page margins if necessary
13091 list($x, $y) = $this->checkPageRegions($w, $x, $y);
13093 $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
13096 if (!isset($prop['value'])) {
13097 $prop['value'] = array('Yes');
13099 // get default style
13100 $prop = array_merge($this->getFormDefaultProp(), $prop);
13101 $prop['borderStyle'] = 'inset';
13102 // get annotation data
13103 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
13104 // set additional default options
13105 $font = 'zapfdingbats';
13106 if ($this->pdfa_mode
) {
13107 // all fonts must be embedded
13108 $font = 'pdfa'.$font;
13110 $this->AddFont($font);
13111 $tmpfont = $this->getFontBuffer($font);
13112 $this->annotation_fonts
[$tmpfont['fontkey']] = $tmpfont['i'];
13113 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt
, $this->TextColor
);
13114 $popt['da'] = $fontstyle;
13115 // build appearance stream
13116 $popt['ap'] = array();
13117 $popt['ap']['n'] = array();
13118 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k
);
13119 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt
/ 1000) / $this->k
)) * $this->k
);
13120 $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);
13121 $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);
13123 $opt = array_merge($popt, $opt);
13124 // set remaining annotation data
13125 $opt['Subtype'] = 'Widget';
13126 $opt['ft'] = 'Btn';
13128 if (TCPDF_STATIC
::empty_string($onvalue)) {
13131 $opt['opt'] = array($onvalue);
13133 $opt['v'] = array('/Yes');
13134 $opt['as'] = 'Yes';
13136 $opt['v'] = array('/Off');
13137 $opt['as'] = 'Off';
13139 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
13148 * Creates a button field
13149 * @param $name (string) field name
13150 * @param $w (int) width
13151 * @param $h (int) height
13152 * @param $caption (string) caption.
13153 * @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.
13154 * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13155 * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13156 * @param $x (float) Abscissa of the upper-left corner of the rectangle
13157 * @param $y (float) Ordinate of the upper-left corner of the rectangle
13158 * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13160 * @author Nicola Asuni
13161 * @since 4.8.000 (2009-09-07)
13163 public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13170 // check page for no-write regions and adapt page margins if necessary
13171 list($x, $y) = $this->checkPageRegions($h, $x, $y);
13173 $this->_addfield('button', $name, $this->x
, $this->y
, $w, $h, $prop);
13174 $this->javascript
.= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
13175 $this->javascript
.= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
13176 $this->javascript
.= 'f'.$name.".highlight='push';\n";
13177 $this->javascript
.= 'f'.$name.".print=false;\n";
13180 // get default style
13181 $prop = array_merge($this->getFormDefaultProp(), $prop);
13182 $prop['Pushbutton'] = 'true';
13183 $prop['highlight'] = 'push';
13184 $prop['display'] = 'display.noPrint';
13185 // get annotation data
13186 $popt = TCPDF_STATIC
::getAnnotOptFromJSProp($prop, $this->spot_colors
, $this->rtl
);
13187 $this->annotation_fonts
[$this->CurrentFont
['fontkey']] = $this->CurrentFont
['i'];
13188 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont
['i'], $this->FontSizePt
, $this->TextColor
);
13189 $popt['da'] = $fontstyle;
13190 // build appearance stream
13191 $popt['ap'] = array();
13192 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13193 $tmpid = $this->startTemplate($w, $h, false);
13194 $bw = (2 / $this->k
); // border width
13196 'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13197 'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
13198 'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13199 'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
13200 $this->SetFillColor(204);
13201 $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
13202 $this->endTemplate();
13204 $popt['ap']['n'] .= $this->xobjects
[$tmpid]['outdata'];
13205 unset($this->xobjects
[$tmpid]);
13206 $popt['ap']['n'] .= 'Q EMC';
13207 // set additional default options
13208 if (!isset($popt['mk'])) {
13209 $popt['mk'] = array();
13211 $ann_obj_id = ($this->n +
1);
13212 if (!empty($action) AND !is_array($action)) {
13213 $ann_obj_id = ($this->n +
2);
13215 $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
13216 $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
13217 $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
13219 $opt = array_merge($popt, $opt);
13220 // set remaining annotation data
13221 $opt['Subtype'] = 'Widget';
13222 $opt['ft'] = 'Btn';
13223 $opt['t'] = $caption;
13225 if (!empty($action)) {
13226 if (is_array($action)) {
13227 // form action options as on section 12.7.5 of PDF32000_2008.
13228 $opt['aa'] = '/D <<';
13229 $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
13230 foreach ($action AS $key => $val) {
13231 if (($key == 'S') AND in_array($val, $bmode)) {
13232 $opt['aa'] .= ' /S /'.$val;
13233 } elseif (($key == 'F') AND (!empty($val))) {
13234 $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
13235 } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
13236 $opt['aa'] .= ' /Fields [';
13237 foreach ($val AS $field) {
13238 $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
13241 } elseif (($key == 'Flags')) {
13243 if (is_array($val)) {
13244 foreach ($val AS $flag) {
13246 case 'Include/Exclude': {
13250 case 'IncludeNoValueFields': {
13254 case 'ExportFormat': {
13258 case 'GetMethod': {
13262 case 'SubmitCoordinates': {
13270 case 'IncludeAppendSaves': {
13274 case 'IncludeAnnotations': {
13278 case 'SubmitPDF': {
13282 case 'CanonicalFormat': {
13286 case 'ExclNonUserAnnots': {
13294 case 'EmbedForm': {
13301 $ff = intval($val);
13303 $opt['aa'] .= ' /Flags '.$ff;
13306 $opt['aa'] .= ' >>';
13308 // Javascript action or raw action command
13309 $js_obj_id = $this->addJavascriptObject($action);
13310 $opt['aa'] = '/D '.$js_obj_id.' 0 R';
13313 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13321 // --- END FORMS FIELDS ------------------------------------------------
13324 * Add certification signature (DocMDP or UR3)
13325 * You can set only one signature type
13327 * @author Nicola Asuni
13328 * @since 4.6.008 (2009-05-07)
13330 protected function _putsignature() {
13331 if ((!$this->sign
) OR (!isset($this->signature_data
['cert_type']))) {
13334 $sigobjid = ($this->sig_obj_id +
1);
13335 $out = $this->_getobj($sigobjid)."\n";
13336 $out .= '<< /Type /Sig';
13337 $out .= ' /Filter /Adobe.PPKLite';
13338 $out .= ' /SubFilter /adbe.pkcs7.detached';
13339 $out .= ' '.TCPDF_STATIC
::$byterange_string;
13340 $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length
).'>';
13341 $out .= ' /Reference ['; // array of signature reference dictionaries
13342 $out .= ' << /Type /SigRef';
13343 if ($this->signature_data
['cert_type'] > 0) {
13344 $out .= ' /TransformMethod /DocMDP';
13345 $out .= ' /TransformParams <<';
13346 $out .= ' /Type /TransformParams';
13347 $out .= ' /P '.$this->signature_data
['cert_type'];
13348 $out .= ' /V /1.2';
13350 $out .= ' /TransformMethod /UR3';
13351 $out .= ' /TransformParams <<';
13352 $out .= ' /Type /TransformParams';
13353 $out .= ' /V /2.2';
13354 if (!TCPDF_STATIC
::empty_string($this->ur
['document'])) {
13355 $out .= ' /Document['.$this->ur
['document'].']';
13357 if (!TCPDF_STATIC
::empty_string($this->ur
['form'])) {
13358 $out .= ' /Form['.$this->ur
['form'].']';
13360 if (!TCPDF_STATIC
::empty_string($this->ur
['signature'])) {
13361 $out .= ' /Signature['.$this->ur
['signature'].']';
13363 if (!TCPDF_STATIC
::empty_string($this->ur
['annots'])) {
13364 $out .= ' /Annots['.$this->ur
['annots'].']';
13366 if (!TCPDF_STATIC
::empty_string($this->ur
['ef'])) {
13367 $out .= ' /EF['.$this->ur
['ef'].']';
13369 if (!TCPDF_STATIC
::empty_string($this->ur
['formex'])) {
13370 $out .= ' /FormEX['.$this->ur
['formex'].']';
13373 $out .= ' >>'; // close TransformParams
13374 // optional digest data (values must be calculated and replaced later)
13375 //$out .= ' /Data ********** 0 R';
13376 //$out .= ' /DigestMethod/MD5';
13377 //$out .= ' /DigestLocation[********** 34]';
13378 //$out .= ' /DigestValue<********************************>';
13380 $out .= ' ]'; // end of reference
13381 if (isset($this->signature_data
['info']['Name']) AND !TCPDF_STATIC
::empty_string($this->signature_data
['info']['Name'])) {
13382 $out .= ' /Name '.$this->_textstring($this->signature_data
['info']['Name'], $sigobjid);
13384 if (isset($this->signature_data
['info']['Location']) AND !TCPDF_STATIC
::empty_string($this->signature_data
['info']['Location'])) {
13385 $out .= ' /Location '.$this->_textstring($this->signature_data
['info']['Location'], $sigobjid);
13387 if (isset($this->signature_data
['info']['Reason']) AND !TCPDF_STATIC
::empty_string($this->signature_data
['info']['Reason'])) {
13388 $out .= ' /Reason '.$this->_textstring($this->signature_data
['info']['Reason'], $sigobjid);
13390 if (isset($this->signature_data
['info']['ContactInfo']) AND !TCPDF_STATIC
::empty_string($this->signature_data
['info']['ContactInfo'])) {
13391 $out .= ' /ContactInfo '.$this->_textstring($this->signature_data
['info']['ContactInfo'], $sigobjid);
13393 $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp
);
13395 $out .= "\n".'endobj';
13400 * Set User's Rights for PDF Reader
13401 * WARNING: This is experimental and currently do not work.
13402 * Check the PDF Reference 8.7.1 Transform Methods,
13403 * Table 8.105 Entries in the UR transform parameters dictionary
13404 * @param $enable (boolean) if true enable user's rights on PDF reader
13405 * @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.
13406 * @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.
13407 * @param $form (string) Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
13408 * @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.
13409 * @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
13410 Names specifying additional embedded-files-related usage rights for the document.
13411 * @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.
13413 * @author Nicola Asuni
13414 * @since 2.9.000 (2008-03-26)
13416 public function setUserRights(
13418 $document='/FullSave',
13419 $annots='/Create/Delete/Modify/Copy/Import/Export',
13420 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
13421 $signature='/Modify',
13422 $ef='/Create/Delete/Modify/Import',
13424 $this->ur
['enabled'] = $enable;
13425 $this->ur
['document'] = $document;
13426 $this->ur
['annots'] = $annots;
13427 $this->ur
['form'] = $form;
13428 $this->ur
['signature'] = $signature;
13429 $this->ur
['ef'] = $ef;
13430 $this->ur
['formex'] = $formex;
13431 if (!$this->sign
) {
13432 $this->setSignature('', '', '', '', 0, array());
13437 * Enable document signature (requires the OpenSSL Library).
13438 * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
13439 * To create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13440 * To export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13441 * To convert pfx certificate to pem: openssl pkcs12 -in tcpdf.pfx -out tcpdf.crt -nodes
13442 * @param $signing_cert (mixed) signing certificate (string or filename prefixed with 'file://')
13443 * @param $private_key (mixed) private key (string or filename prefixed with 'file://')
13444 * @param $private_key_password (string) password
13445 * @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.
13446 * @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.
13447 * @param $info (array) array of option information: Name, Location, Reason, ContactInfo.
13449 * @author Nicola Asuni
13450 * @since 4.6.005 (2009-04-24)
13452 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
13453 // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13454 // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13455 // to convert pfx certificate to pem: openssl
13456 // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
13457 $this->sign
= true;
13459 $this->sig_obj_id
= $this->n
; // signature widget
13460 ++
$this->n
; // signature object ($this->sig_obj_id + 1)
13461 $this->signature_data
= array();
13462 if (strlen($signing_cert) == 0) {
13463 $this->Error('Please provide a certificate file and password!');
13465 if (strlen($private_key) == 0) {
13466 $private_key = $signing_cert;
13468 $this->signature_data
['signcert'] = $signing_cert;
13469 $this->signature_data
['privkey'] = $private_key;
13470 $this->signature_data
['password'] = $private_key_password;
13471 $this->signature_data
['extracerts'] = $extracerts;
13472 $this->signature_data
['cert_type'] = $cert_type;
13473 $this->signature_data
['info'] = $info;
13477 * Set the digital signature appearance (a cliccable rectangle area to get signature properties)
13478 * @param $x (float) Abscissa of the upper-left corner.
13479 * @param $y (float) Ordinate of the upper-left corner.
13480 * @param $w (float) Width of the signature area.
13481 * @param $h (float) Height of the signature area.
13482 * @param $page (int) option page number (if < 0 the current page is used).
13483 * @param $name (string) Name of the signature.
13485 * @author Nicola Asuni
13486 * @since 5.3.011 (2010-06-17)
13488 public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13489 $this->signature_appearance
= $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13493 * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties)
13494 * @param $x (float) Abscissa of the upper-left corner.
13495 * @param $y (float) Ordinate of the upper-left corner.
13496 * @param $w (float) Width of the signature area.
13497 * @param $h (float) Height of the signature area.
13498 * @param $page (int) option page number (if < 0 the current page is used).
13499 * @param $name (string) Name of the signature.
13501 * @author Nicola Asuni
13502 * @since 5.9.101 (2011-07-06)
13504 public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13506 $this->empty_signature_appearance
[] = array('objid' => $this->n
) +
$this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13510 * Get the array that defines the signature appearance (page and rectangle coordinates).
13511 * @param $x (float) Abscissa of the upper-left corner.
13512 * @param $y (float) Ordinate of the upper-left corner.
13513 * @param $w (float) Width of the signature area.
13514 * @param $h (float) Height of the signature area.
13515 * @param $page (int) option page number (if < 0 the current page is used).
13516 * @param $name (string) Name of the signature.
13517 * @return (array) Array defining page and rectangle coordinates of signature appearance.
13519 * @author Nicola Asuni
13520 * @since 5.9.101 (2011-07-06)
13522 protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13524 if (($page < 1) OR ($page > $this->numpages
)) {
13525 $sigapp['page'] = $this->page
;
13527 $sigapp['page'] = intval($page);
13529 if (empty($name)) {
13530 $sigapp['name'] = 'Signature';
13532 $sigapp['name'] = $name;
13534 $a = $x * $this->k
;
13535 $b = $this->pagedim
[($sigapp['page'])]['h'] - (($y +
$h) * $this->k
);
13536 $c = $w * $this->k
;
13537 $d = $h * $this->k
;
13538 $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a +
$c), ($b +
$d));
13543 * Create a new page group.
13544 * NOTE: call this function before calling AddPage()
13545 * @param $page (int) starting group page (leave empty for next page).
13547 * @since 3.0.000 (2008-03-27)
13549 public function startPageGroup($page='') {
13550 if (empty($page)) {
13551 $page = $this->page +
1;
13553 $this->newpagegroup
[$page] = sizeof($this->newpagegroup
) +
1;
13557 * Set the starting page number.
13558 * @param $num (int) Starting page number.
13559 * @since 5.9.093 (2011-06-16)
13562 public function setStartingPageNumber($num=1) {
13563 $this->starting_page_number
= max(0, intval($num));
13567 * Returns the string alias used right align page numbers.
13568 * If the current font is unicode type, the returned string wil contain an additional open curly brace.
13570 * @since 5.9.099 (2011-06-27)
13573 public function getAliasRightShift() {
13574 // calculate aproximatively the ratio between widths of aliases and replacements.
13575 $ref = '{'.TCPDF_STATIC
::$alias_right_shift.'}{'.TCPDF_STATIC
::$alias_tot_pages.'}{'.TCPDF_STATIC
::$alias_num_page.'}';
13576 $rep = str_repeat(' ', $this->GetNumChars($ref));
13577 $wrep = $this->GetStringWidth($rep);
13579 $wdiff = max(1, ($this->GetStringWidth($ref) / $wrep));
13583 $sdiff = sprintf('%F', $wdiff);
13584 $alias = TCPDF_STATIC
::$alias_right_shift.$sdiff.'}';
13585 if ($this->isUnicodeFont()) {
13586 $alias = '{'.$alias;
13592 * Returns the string alias used for the total number of pages.
13593 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13594 * This alias will be replaced by the total number of pages in the document.
13596 * @since 4.0.018 (2008-08-08)
13599 public function getAliasNbPages() {
13600 if ($this->isUnicodeFont()) {
13601 return '{'.TCPDF_STATIC
::$alias_tot_pages.'}';
13603 return TCPDF_STATIC
::$alias_tot_pages;
13607 * Returns the string alias used for the page number.
13608 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13609 * This alias will be replaced by the page number.
13611 * @since 4.5.000 (2009-01-02)
13614 public function getAliasNumPage() {
13615 if ($this->isUnicodeFont()) {
13616 return '{'.TCPDF_STATIC
::$alias_num_page.'}';
13618 return TCPDF_STATIC
::$alias_num_page;
13622 * Return the alias for the total number of pages in the current page group.
13623 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13624 * This alias will be replaced by the total number of pages in this group.
13625 * @return alias of the current page group
13627 * @since 3.0.000 (2008-03-27)
13629 public function getPageGroupAlias() {
13630 if ($this->isUnicodeFont()) {
13631 return '{'.TCPDF_STATIC
::$alias_group_tot_pages.'}';
13633 return TCPDF_STATIC
::$alias_group_tot_pages;
13637 * Return the alias for the page number on the current page group.
13638 * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13639 * This alias will be replaced by the page number (relative to the belonging group).
13640 * @return alias of the current page group
13642 * @since 4.5.000 (2009-01-02)
13644 public function getPageNumGroupAlias() {
13645 if ($this->isUnicodeFont()) {
13646 return '{'.TCPDF_STATIC
::$alias_group_num_page.'}';
13648 return TCPDF_STATIC
::$alias_group_num_page;
13652 * Return the current page in the group.
13653 * @return current page in the group
13655 * @since 3.0.000 (2008-03-27)
13657 public function getGroupPageNo() {
13658 return $this->pagegroups
[$this->currpagegroup
];
13662 * Returns the current group page number formatted as a string.
13664 * @since 4.3.003 (2008-11-18)
13665 * @see PaneNo(), formatPageNumber()
13667 public function getGroupPageNoFormatted() {
13668 return TCPDF_STATIC
::formatPageNumber($this->getGroupPageNo());
13672 * Returns the current page number formatted as a string.
13674 * @since 4.2.005 (2008-11-06)
13675 * @see PaneNo(), formatPageNumber()
13677 public function PageNoFormatted() {
13678 return TCPDF_STATIC
::formatPageNumber($this->PageNo());
13684 * @since 3.0.000 (2008-03-27)
13686 protected function _putocg() {
13687 if (empty($this->pdflayers
)) {
13690 foreach ($this->pdflayers
as $key => $layer) {
13691 $this->pdflayers
[$key]['objid'] = $this->_newobj();
13692 $out = '<< /Type /OCG';
13693 $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers
[$key]['objid']);
13694 $out .= ' /Usage <<';
13695 if (isset($layer['print']) AND ($layer['print'] !== NULL)) {
13696 $out .= ' /Print <</PrintState /'.($layer['print']?
'ON':'OFF').'>>';
13698 $out .= ' /View <</ViewState /'.($layer['view']?
'ON':'OFF').'>>';
13700 $out .= "\n".'endobj';
13706 * Start a new pdf layer.
13707 * @param $name (string) Layer name (only a-z letters and numbers). Leave empty for automatic name.
13708 * @param $print (boolean|null) Set to TRUE to print this layer, FALSE to not print and NULL to not set this option
13709 * @param $view (boolean) Set to true to view this layer.
13710 * @param $lock (boolean) If true lock the layer
13712 * @since 5.9.102 (2011-07-13)
13714 public function startLayer($name='', $print=true, $view=true, $lock=true) {
13715 if ($this->state
!= 2) {
13718 $layer = sprintf('LYR%03d', (count($this->pdflayers
) +
1));
13719 if (empty($name)) {
13722 $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
13724 $this->pdflayers
[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view, 'lock' => $lock);
13725 $this->openMarkedContent
= true;
13726 $this->_out('/OC /'.$layer.' BDC');
13730 * End the current PDF layer.
13732 * @since 5.9.102 (2011-07-13)
13734 public function endLayer() {
13735 if ($this->state
!= 2) {
13738 if ($this->openMarkedContent
) {
13739 // close existing open marked-content layer
13740 $this->_out('EMC');
13741 $this->openMarkedContent
= false;
13746 * Set the visibility of the successive elements.
13747 * This can be useful, for instance, to put a background
13748 * image or color that will show on screen but won't print.
13749 * @param $v (string) visibility mode. Legal values are: all, print, screen or view.
13751 * @since 3.0.000 (2008-03-27)
13753 public function setVisibility($v) {
13754 if ($this->state
!= 2) {
13760 $this->startLayer('Print', true, false);
13765 $this->startLayer('View', false, true);
13773 $this->Error('Incorrect visibility: '.$v);
13780 * Add transparency parameters to the current extgstate
13781 * @param $parms (array) parameters
13782 * @return the number of extgstates
13784 * @since 3.0.000 (2008-03-27)
13786 protected function addExtGState($parms) {
13787 if ($this->pdfa_mode
) {
13788 // transparencies are not allowed in PDF/A mode
13791 // check if this ExtGState already exist
13792 foreach ($this->extgstates
as $i => $ext) {
13793 if ($ext['parms'] == $parms) {
13794 if ($this->inxobj
) {
13795 // we are inside an XObject template
13796 $this->xobjects
[$this->xobjid
]['extgstates'][$i] = $ext;
13798 // return reference to existing ExtGState
13802 $n = (count($this->extgstates
) +
1);
13803 $this->extgstates
[$n] = array('parms' => $parms);
13804 if ($this->inxobj
) {
13805 // we are inside an XObject template
13806 $this->xobjects
[$this->xobjid
]['extgstates'][$n] = $this->extgstates
[$n];
13813 * @param $gs (array) extgstate
13815 * @since 3.0.000 (2008-03-27)
13817 protected function setExtGState($gs) {
13818 if ($this->pdfa_mode
OR ($this->state
!= 2)) {
13819 // transparency is not allowed in PDF/A mode
13822 $this->_out(sprintf('/GS%d gs', $gs));
13826 * Put extgstates for object transparency
13828 * @since 3.0.000 (2008-03-27)
13830 protected function _putextgstates() {
13831 foreach ($this->extgstates
as $i => $ext) {
13832 $this->extgstates
[$i]['n'] = $this->_newobj();
13833 $out = '<< /Type /ExtGState';
13834 foreach ($ext['parms'] as $k => $v) {
13835 if (is_float($v)) {
13836 $v = sprintf('%F', $v);
13837 } elseif ($v === true) {
13839 } elseif ($v === false) {
13842 $out .= ' /'.$k.' '.$v;
13845 $out .= "\n".'endobj';
13851 * Set overprint mode for stroking (OP) and non-stroking (op) painting operations.
13852 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
13853 * @param $stroking (boolean) If true apply overprint for stroking operations.
13854 * @param $nonstroking (boolean) If true apply overprint for painting operations other than stroking.
13855 * @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).
13857 * @since 5.9.152 (2012-03-23)
13859 public function setOverprint($stroking=true, $nonstroking='', $mode=0) {
13860 if ($this->state
!= 2) {
13863 $stroking = $stroking ?
true : false;
13864 if (TCPDF_STATIC
::empty_string($nonstroking)) {
13865 // default value if not set
13866 $nonstroking = $stroking;
13868 $nonstroking = $nonstroking ?
true : false;
13870 if (($mode != 0) AND ($mode != 1)) {
13873 $this->overprint
= array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode);
13874 $gs = $this->addExtGState($this->overprint
);
13875 $this->setExtGState($gs);
13879 * Get the overprint mode array (OP, op, OPM).
13880 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
13883 * @since 5.9.152 (2012-03-23)
13885 public function getOverprint() {
13886 return $this->overprint
;
13890 * Set alpha for stroking (CA) and non-stroking (ca) operations.
13891 * @param $stroking (float) Alpha value for stroking operations: real value from 0 (transparent) to 1 (opaque).
13892 * @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
13893 * @param $nonstroking (float) Alpha value for non-stroking operations: real value from 0 (transparent) to 1 (opaque).
13894 * @param $ais (boolean)
13896 * @since 3.0.000 (2008-03-27)
13898 public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) {
13899 if ($this->pdfa_mode
) {
13900 // transparency is not allowed in PDF/A mode
13903 $stroking = floatval($stroking);
13904 if (TCPDF_STATIC
::empty_string($nonstroking)) {
13905 // default value if not set
13906 $nonstroking = $stroking;
13908 $nonstroking = floatval($nonstroking);
13910 if ($bm[0] == '/') {
13911 // remove trailing slash
13912 $bm = substr($bm, 1);
13914 if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
13917 $ais = $ais ?
true : false;
13918 $this->alpha
= array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais);
13919 $gs = $this->addExtGState($this->alpha
);
13920 $this->setExtGState($gs);
13924 * Get the alpha mode array (CA, ca, BM, AIS).
13925 * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
13928 * @since 5.9.152 (2012-03-23)
13930 public function getAlpha() {
13931 return $this->alpha
;
13935 * Set the default JPEG compression quality (1-100)
13936 * @param $quality (int) JPEG quality, integer between 1 and 100
13938 * @since 3.0.000 (2008-03-27)
13940 public function setJPEGQuality($quality) {
13941 if (($quality < 1) OR ($quality > 100)) {
13944 $this->jpeg_quality
= intval($quality);
13948 * Set the default number of columns in a row for HTML tables.
13949 * @param $cols (int) number of columns
13951 * @since 3.0.014 (2008-06-04)
13953 public function setDefaultTableColumns($cols=4) {
13954 $this->default_table_columns
= intval($cols);
13958 * Set the height of the cell (line height) respect the font height.
13959 * @param $h (int) cell proportion respect font height (typical value = 1.25).
13961 * @since 3.0.014 (2008-06-04)
13963 public function setCellHeightRatio($h) {
13964 $this->cell_height_ratio
= $h;
13968 * return the height of cell repect font height.
13970 * @since 4.0.012 (2008-07-24)
13972 public function getCellHeightRatio() {
13973 return $this->cell_height_ratio
;
13977 * Set the PDF version (check PDF reference for valid values).
13978 * @param $version (string) PDF document version.
13980 * @since 3.1.000 (2008-06-09)
13982 public function setPDFVersion($version='1.7') {
13983 if ($this->pdfa_mode
) {
13985 $this->PDFVersion
= '1.4';
13987 $this->PDFVersion
= $version;
13992 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
13993 * (see Section 8.1 of PDF reference, "Viewer Preferences").
13994 * <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>
13995 * @param $preferences (array) array of options.
13996 * @author Nicola Asuni
13998 * @since 3.1.000 (2008-06-09)
14000 public function setViewerPreferences($preferences) {
14001 $this->viewer_preferences
= $preferences;
14005 * Paints color transition registration bars
14006 * @param $x (float) abscissa of the top left corner of the rectangle.
14007 * @param $y (float) ordinate of the top left corner of the rectangle.
14008 * @param $w (float) width of the rectangle.
14009 * @param $h (float) height of the rectangle.
14010 * @param $transition (boolean) if true prints tcolor transitions to white.
14011 * @param $vertical (boolean) if true prints bar vertically.
14012 * @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.
14013 * @author Nicola Asuni
14014 * @since 4.9.000 (2010-03-26)
14017 public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
14018 if (strpos($colors, 'ALLSPOT') !== false) {
14019 // expand spot colors
14021 foreach ($this->spot_colors
as $spot_color_name => $v) {
14022 $spot_colors .= ','.$spot_color_name;
14024 if (!empty($spot_colors)) {
14025 $spot_colors = substr($spot_colors, 1);
14026 $colors = str_replace('ALLSPOT', $spot_colors, $colors);
14028 $colors = str_replace('ALLSPOT', 'NONE', $colors);
14031 $bars = explode(',', $colors);
14032 $numbars = count($bars); // number of bars to print
14033 if ($numbars <= 0) {
14036 // set bar measures
14038 $coords = array(0, 0, 0, 1);
14039 $wb = $w / $numbars; // bar width
14040 $hb = $h; // bar height
14041 $xd = $wb; // delta x
14042 $yd = 0; // delta y
14044 $coords = array(1, 0, 0, 0);
14045 $wb = $w; // bar width
14046 $hb = $h / $numbars; // bar height
14047 $xd = 0; // delta x
14048 $yd = $hb; // delta y
14052 foreach ($bars as $col) {
14054 // set transition colors
14055 case 'A': { // BLACK (GRAYSCALE)
14056 $col_a = array(255);
14060 case 'W': { // WHITE (GRAYSCALE)
14062 $col_b = array(255);
14065 case 'R': { // RED (RGB)
14066 $col_a = array(255,255,255);
14067 $col_b = array(255,0,0);
14070 case 'G': { // GREEN (RGB)
14071 $col_a = array(255,255,255);
14072 $col_b = array(0,255,0);
14075 case 'B': { // BLUE (RGB)
14076 $col_a = array(255,255,255);
14077 $col_b = array(0,0,255);
14080 case 'C': { // CYAN (CMYK)
14081 $col_a = array(0,0,0,0);
14082 $col_b = array(100,0,0,0);
14085 case 'M': { // MAGENTA (CMYK)
14086 $col_a = array(0,0,0,0);
14087 $col_b = array(0,100,0,0);
14090 case 'Y': { // YELLOW (CMYK)
14091 $col_a = array(0,0,0,0);
14092 $col_b = array(0,0,100,0);
14095 case 'K': { // KEY - BLACK (CMYK)
14096 $col_a = array(0,0,0,0);
14097 $col_b = array(0,0,0,100);
14100 case 'RGB': { // BLACK REGISTRATION (RGB)
14101 $col_a = array(255,255,255);
14102 $col_b = array(0,0,0);
14105 case 'CMYK': { // BLACK REGISTRATION (CMYK)
14106 $col_a = array(0,0,0,0);
14107 $col_b = array(100,100,100,100);
14110 case 'ALL': { // SPOT COLOR REGISTRATION
14111 $col_a = array(0,0,0,0,'None');
14112 $col_b = array(100,100,100,100,'All');
14115 case 'NONE': { // SKIP THIS COLOR
14116 $col_a = array(0,0,0,0,'None');
14117 $col_b = array(0,0,0,0,'None');
14120 default: { // SPECIFIC SPOT COLOR NAME
14121 $col_a = array(0,0,0,0,'None');
14122 $col_b = TCPDF_COLORS
::getSpotColor($col, $this->spot_colors
);
14123 if ($col_b === false) {
14124 // in case of error defaults to the registration color
14125 $col_b = array(100,100,100,100,'All');
14130 if ($col != 'NONE') {
14133 $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
14135 $this->SetFillColorArray($col_b);
14136 // colored rectangle
14137 $this->Rect($xb, $yb, $wb, $hb, 'F', array());
14146 * Paints crop marks.
14147 * @param $x (float) abscissa of the crop mark center.
14148 * @param $y (float) ordinate of the crop mark center.
14149 * @param $w (float) width of the crop mark.
14150 * @param $h (float) height of the crop mark.
14151 * @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.
14152 * @param $color (array) crop mark color (default spot registration color).
14153 * @author Nicola Asuni
14154 * @since 4.9.000 (2010-03-26)
14157 public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(100,100,100,100,'All')) {
14158 $this->SetLineStyle(array('width' => (0.5 / $this->k
), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
14159 $type = strtoupper($type);
14160 $type = preg_replace('/[^A-Z\-\,]*/', '', $type);
14161 // split type in single components
14162 $type = str_replace('-', ',', $type);
14163 $type = str_replace('TL', 'T,L', $type);
14164 $type = str_replace('TR', 'T,R', $type);
14165 $type = str_replace('BL', 'F,L', $type);
14166 $type = str_replace('BR', 'F,R', $type);
14167 $type = str_replace('A', 'T,L', $type);
14168 $type = str_replace('B', 'T,R', $type);
14169 $type = str_replace('T,RO', 'BO', $type);
14170 $type = str_replace('C', 'F,L', $type);
14171 $type = str_replace('D', 'F,R', $type);
14172 $crops = explode(',', strtoupper($type));
14173 // remove duplicates
14174 $crops = array_unique($crops);
14175 $dw = ($w / 4); // horizontal space to leave before the intersection point
14176 $dh = ($h / 4); // vertical space to leave before the intersection point
14177 foreach ($crops as $crop) {
14212 $this->Line($x1, $y1, $x2, $y2);
14217 * Paints a registration mark
14218 * @param $x (float) abscissa of the registration mark center.
14219 * @param $y (float) ordinate of the registration mark center.
14220 * @param $r (float) radius of the crop mark.
14221 * @param $double (boolean) if true print two concentric crop marks.
14222 * @param $cola (array) crop mark color (default spot registration color 'All').
14223 * @param $colb (array) second crop mark color (default spot registration color 'None').
14224 * @author Nicola Asuni
14225 * @since 4.9.000 (2010-03-26)
14228 public function registrationMark($x, $y, $r, $double=false, $cola=array(100,100,100,100,'All'), $colb=array(0,0,0,0,'None')) {
14229 $line_style = array('width' => max((0.5 / $this->k
),($r / 30)), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
14230 $this->SetFillColorArray($cola);
14231 $this->PieSector($x, $y, $r, 90, 180, 'F');
14232 $this->PieSector($x, $y, $r, 270, 360, 'F');
14233 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14236 $this->SetFillColorArray($colb);
14237 $this->PieSector($x, $y, $ri, 90, 180, 'F');
14238 $this->PieSector($x, $y, $ri, 270, 360, 'F');
14239 $this->SetFillColorArray($cola);
14240 $this->PieSector($x, $y, $ri, 0, 90, 'F');
14241 $this->PieSector($x, $y, $ri, 180, 270, 'F');
14242 $this->Circle($x, $y, $ri, 0, 360, 'C', $line_style, array(), 8);
14247 * Paints a CMYK registration mark
14248 * @param $x (float) abscissa of the registration mark center.
14249 * @param $y (float) ordinate of the registration mark center.
14250 * @param $r (float) radius of the crop mark.
14251 * @author Nicola Asuni
14252 * @since 6.0.038 (2013-09-30)
14255 public function registrationMarkCMYK($x, $y, $r) {
14257 $lw = max((0.5 / $this->k
),($r / 8));
14263 $this->SetFillColorArray(array(100,0,0,0));
14264 $this->PieSector($x, $y, $ri, 270, 360, 'F');
14266 $this->SetFillColorArray(array(0,100,0,0));
14267 $this->PieSector($x, $y, $ri, 0, 90, 'F');
14269 $this->SetFillColorArray(array(0,0,100,0));
14270 $this->PieSector($x, $y, $ri, 90, 180, 'F');
14272 $this->SetFillColorArray(array(0,0,0,100));
14273 $this->PieSector($x, $y, $ri, 180, 270, 'F');
14274 // registration color
14275 $line_style = array('width' => $lw, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(100,100,100,100,'All'));
14276 $this->SetFillColorArray(array(100,100,100,100,'All'));
14278 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14280 $this->Line($x, ($y - $re), $x, ($y - $ri));
14281 $this->Line($x, ($y +
$ri), $x, ($y +
$re));
14282 $this->Line(($x - $re), $y, ($x - $ri), $y);
14283 $this->Line(($x +
$ri), $y, ($x +
$re), $y);
14287 * Paints a linear colour gradient.
14288 * @param $x (float) abscissa of the top left corner of the rectangle.
14289 * @param $y (float) ordinate of the top left corner of the rectangle.
14290 * @param $w (float) width of the rectangle.
14291 * @param $h (float) height of the rectangle.
14292 * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
14293 * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
14294 * @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).
14295 * @author Andreas W\FCrmser, Nicola Asuni
14296 * @since 3.1.000 (2008-06-09)
14299 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
14300 $this->Clip($x, $y, $w, $h);
14301 $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14305 * Paints a radial colour gradient.
14306 * @param $x (float) abscissa of the top left corner of the rectangle.
14307 * @param $y (float) ordinate of the top left corner of the rectangle.
14308 * @param $w (float) width of the rectangle.
14309 * @param $h (float) height of the rectangle.
14310 * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
14311 * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
14312 * @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.
14313 * @author Andreas W\FCrmser, Nicola Asuni
14314 * @since 3.1.000 (2008-06-09)
14317 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
14318 $this->Clip($x, $y, $w, $h);
14319 $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14323 * Paints a coons patch mesh.
14324 * @param $x (float) abscissa of the top left corner of the rectangle.
14325 * @param $y (float) ordinate of the top left corner of the rectangle.
14326 * @param $w (float) width of the rectangle.
14327 * @param $h (float) height of the rectangle.
14328 * @param $col1 (array) first color (lower left corner) (RGB components).
14329 * @param $col2 (array) second color (lower right corner) (RGB components).
14330 * @param $col3 (array) third color (upper right corner) (RGB components).
14331 * @param $col4 (array) fourth color (upper left corner) (RGB components).
14332 * @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>
14333 * @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
14334 * @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
14335 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
14336 * @author Andreas W\FCrmser, Nicola Asuni
14337 * @since 3.1.000 (2008-06-09)
14340 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) {
14341 if ($this->pdfa_mode
OR ($this->state
!= 2)) {
14344 $this->Clip($x, $y, $w, $h);
14345 $n = count($this->gradients
) +
1;
14346 $this->gradients
[$n] = array();
14347 $this->gradients
[$n]['type'] = 6; //coons patch mesh
14348 $this->gradients
[$n]['coords'] = array();
14349 $this->gradients
[$n]['antialias'] = $antialias;
14350 $this->gradients
[$n]['colors'] = array();
14351 $this->gradients
[$n]['transparency'] = false;
14352 //check the coords array if it is the simple array or the multi patch array
14353 if (!isset($coords[0]['f'])) {
14354 //simple array -> convert to multi patch array
14355 if (!isset($col1[1])) {
14356 $col1[1] = $col1[2] = $col1[0];
14358 if (!isset($col2[1])) {
14359 $col2[1] = $col2[2] = $col2[0];
14361 if (!isset($col3[1])) {
14362 $col3[1] = $col3[2] = $col3[0];
14364 if (!isset($col4[1])) {
14365 $col4[1] = $col4[2] = $col4[0];
14367 $patch_array[0]['f'] = 0;
14368 $patch_array[0]['points'] = $coords;
14369 $patch_array[0]['colors'][0]['r'] = $col1[0];
14370 $patch_array[0]['colors'][0]['g'] = $col1[1];
14371 $patch_array[0]['colors'][0]['b'] = $col1[2];
14372 $patch_array[0]['colors'][1]['r'] = $col2[0];
14373 $patch_array[0]['colors'][1]['g'] = $col2[1];
14374 $patch_array[0]['colors'][1]['b'] = $col2[2];
14375 $patch_array[0]['colors'][2]['r'] = $col3[0];
14376 $patch_array[0]['colors'][2]['g'] = $col3[1];
14377 $patch_array[0]['colors'][2]['b'] = $col3[2];
14378 $patch_array[0]['colors'][3]['r'] = $col4[0];
14379 $patch_array[0]['colors'][3]['g'] = $col4[1];
14380 $patch_array[0]['colors'][3]['b'] = $col4[2];
14382 //multi patch array
14383 $patch_array = $coords;
14385 $bpcd = 65535; //16 bits per coordinate
14386 //build the data stream
14387 $this->gradients
[$n]['stream'] = '';
14388 $count_patch = count($patch_array);
14389 for ($i=0; $i < $count_patch; ++
$i) {
14390 $this->gradients
[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
14391 $count_points = count($patch_array[$i]['points']);
14392 for ($j=0; $j < $count_points; ++
$j) {
14393 //each point as 16 bit
14394 $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
14395 if ($patch_array[$i]['points'][$j] < 0) {
14396 $patch_array[$i]['points'][$j] = 0;
14398 if ($patch_array[$i]['points'][$j] > $bpcd) {
14399 $patch_array[$i]['points'][$j] = $bpcd;
14401 $this->gradients
[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
14402 $this->gradients
[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] %
256));
14404 $count_cols = count($patch_array[$i]['colors']);
14405 for ($j=0; $j < $count_cols; ++
$j) {
14406 //each color component as 8 bit
14407 $this->gradients
[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
14408 $this->gradients
[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
14409 $this->gradients
[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
14412 //paint the gradient
14413 $this->_out('/Sh'.$n.' sh');
14414 //restore previous Graphic State
14415 $this->_outRestoreGraphicsState();
14416 if ($this->inxobj
) {
14417 // we are inside an XObject template
14418 $this->xobjects
[$this->xobjid
]['gradients'][$n] = $this->gradients
[$n];
14423 * Set a rectangular clipping area.
14424 * @param $x (float) abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
14425 * @param $y (float) ordinate of the top left corner of the rectangle.
14426 * @param $w (float) width of the rectangle.
14427 * @param $h (float) height of the rectangle.
14428 * @author Andreas W\FCrmser, Nicola Asuni
14429 * @since 3.1.000 (2008-06-09)
14432 protected function Clip($x, $y, $w, $h) {
14433 if ($this->state
!= 2) {
14437 $x = $this->w
- $x - $w;
14439 //save current Graphic State
14441 //set clipping area
14442 $s .= sprintf(' %F %F %F %F re W n', $x*$this->k
, ($this->h
-$y)*$this->k
, $w*$this->k
, -$h*$this->k
);
14443 //set up transformation matrix for gradient
14444 $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k
, $h*$this->k
, $x*$this->k
, ($this->h
-($y+
$h))*$this->k
);
14450 * @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)
14451 * @param $coords (array) array of coordinates.
14452 * @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).
14453 * @param $background (array) An array of colour components appropriate to the colour space, specifying a single background colour value.
14454 * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
14455 * @author Nicola Asuni
14456 * @since 3.1.000 (2008-06-09)
14459 public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
14460 if ($this->pdfa_mode
OR ($this->state
!= 2)) {
14463 $n = count($this->gradients
) +
1;
14464 $this->gradients
[$n] = array();
14465 $this->gradients
[$n]['type'] = $type;
14466 $this->gradients
[$n]['coords'] = $coords;
14467 $this->gradients
[$n]['antialias'] = $antialias;
14468 $this->gradients
[$n]['colors'] = array();
14469 $this->gradients
[$n]['transparency'] = false;
14471 $numcolspace = count($stops[0]['color']);
14472 $bcolor = array_values($background);
14473 switch($numcolspace) {
14476 $this->gradients
[$n]['colspace'] = 'DeviceCMYK';
14477 if (!empty($background)) {
14478 $this->gradients
[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
14483 $this->gradients
[$n]['colspace'] = 'DeviceRGB';
14484 if (!empty($background)) {
14485 $this->gradients
[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
14489 case 1: { // GRAY SCALE
14490 $this->gradients
[$n]['colspace'] = 'DeviceGray';
14491 if (!empty($background)) {
14492 $this->gradients
[$n]['background'] = sprintf('%F', $bcolor[0]/255);
14497 $num_stops = count($stops);
14498 $last_stop_id = $num_stops - 1;
14499 foreach ($stops as $key => $stop) {
14500 $this->gradients
[$n]['colors'][$key] = array();
14501 // offset represents a location along the gradient vector
14502 if (isset($stop['offset'])) {
14503 $this->gradients
[$n]['colors'][$key]['offset'] = $stop['offset'];
14506 $this->gradients
[$n]['colors'][$key]['offset'] = 0;
14507 } elseif ($key == $last_stop_id) {
14508 $this->gradients
[$n]['colors'][$key]['offset'] = 1;
14510 $offsetstep = (1 - $this->gradients
[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
14511 $this->gradients
[$n]['colors'][$key]['offset'] = $this->gradients
[$n]['colors'][($key - 1)]['offset'] +
$offsetstep;
14514 if (isset($stop['opacity'])) {
14515 $this->gradients
[$n]['colors'][$key]['opacity'] = $stop['opacity'];
14516 if ((!$this->pdfa_mode
) AND ($stop['opacity'] < 1)) {
14517 $this->gradients
[$n]['transparency'] = true;
14520 $this->gradients
[$n]['colors'][$key]['opacity'] = 1;
14522 // exponent for the exponential interpolation function
14523 if (isset($stop['exponent'])) {
14524 $this->gradients
[$n]['colors'][$key]['exponent'] = $stop['exponent'];
14526 $this->gradients
[$n]['colors'][$key]['exponent'] = 1;
14529 $color = array_values($stop['color']);
14530 switch($numcolspace) {
14533 $this->gradients
[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
14537 $this->gradients
[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255);
14540 case 1: { // GRAY SCALE
14541 $this->gradients
[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255);
14546 if ($this->gradients
[$n]['transparency']) {
14547 // paint luminosity gradient
14548 $this->_out('/TGS'.$n.' gs');
14550 //paint the gradient
14551 $this->_out('/Sh'.$n.' sh');
14552 //restore previous Graphic State
14553 $this->_outRestoreGraphicsState();
14554 if ($this->inxobj
) {
14555 // we are inside an XObject template
14556 $this->xobjects
[$this->xobjid
]['gradients'][$n] = $this->gradients
[$n];
14561 * Output gradient shaders.
14562 * @author Nicola Asuni
14563 * @since 3.1.000 (2008-06-09)
14566 function _putshaders() {
14567 if ($this->pdfa_mode
) {
14570 $idt = count($this->gradients
); //index for transparency gradients
14571 foreach ($this->gradients
as $id => $grad) {
14572 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
14573 $fc = $this->_newobj();
14575 $out .= ' /FunctionType 3';
14576 $out .= ' /Domain [0 1]';
14581 $num_cols = count($grad['colors']);
14582 $lastcols = $num_cols - 1;
14583 for ($i = 1; $i < $num_cols; ++
$i) {
14584 $functions .= ($fc +
$i).' 0 R ';
14585 if ($i < $lastcols) {
14586 $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']);
14590 $out .= ' /Functions ['.trim($functions).']';
14591 $out .= ' /Bounds ['.trim($bounds).']';
14592 $out .= ' /Encode ['.trim($encode).']';
14594 $out .= "\n".'endobj';
14596 for ($i = 1; $i < $num_cols; ++
$i) {
14599 $out .= ' /FunctionType 2';
14600 $out .= ' /Domain [0 1]';
14601 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
14602 $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
14603 $out .= ' /N '.$grad['colors'][$i]['exponent'];
14605 $out .= "\n".'endobj';
14608 // set transparency fuctions
14609 if ($grad['transparency']) {
14610 $ft = $this->_newobj();
14612 $out .= ' /FunctionType 3';
14613 $out .= ' /Domain [0 1]';
14616 $num_cols = count($grad['colors']);
14617 for ($i = 1; $i < $num_cols; ++
$i) {
14618 $functions .= ($ft +
$i).' 0 R ';
14620 $out .= ' /Functions ['.trim($functions).']';
14621 $out .= ' /Bounds ['.trim($bounds).']';
14622 $out .= ' /Encode ['.trim($encode).']';
14624 $out .= "\n".'endobj';
14626 for ($i = 1; $i < $num_cols; ++
$i) {
14629 $out .= ' /FunctionType 2';
14630 $out .= ' /Domain [0 1]';
14631 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
14632 $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
14633 $out .= ' /N '.$grad['colors'][$i]['exponent'];
14635 $out .= "\n".'endobj';
14640 // set shading object
14642 $out = '<< /ShadingType '.$grad['type'];
14643 if (isset($grad['colspace'])) {
14644 $out .= ' /ColorSpace /'.$grad['colspace'];
14646 $out .= ' /ColorSpace /DeviceRGB';
14648 if (isset($grad['background']) AND !empty($grad['background'])) {
14649 $out .= ' /Background ['.$grad['background'].']';
14651 if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
14652 $out .= ' /AntiAlias true';
14654 if ($grad['type'] == 2) {
14655 $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
14656 $out .= ' /Domain [0 1]';
14657 $out .= ' /Function '.$fc.' 0 R';
14658 $out .= ' /Extend [true true]';
14660 } elseif ($grad['type'] == 3) {
14661 //x0, y0, r0, x1, y1, r1
14662 //at this this time radius of inner circle is 0
14663 $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
14664 $out .= ' /Domain [0 1]';
14665 $out .= ' /Function '.$fc.' 0 R';
14666 $out .= ' /Extend [true true]';
14668 } elseif ($grad['type'] == 6) {
14669 $out .= ' /BitsPerCoordinate 16';
14670 $out .= ' /BitsPerComponent 8';
14671 $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
14672 $out .= ' /BitsPerFlag 8';
14673 $stream = $this->_getrawstream($grad['stream']);
14674 $out .= ' /Length '.strlen($stream);
14676 $out .= ' stream'."\n".$stream."\n".'endstream';
14678 $out .= "\n".'endobj';
14680 if ($grad['transparency']) {
14681 $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
14682 $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
14684 $this->gradients
[$id]['id'] = $this->n
;
14685 // set pattern object
14687 $out = '<< /Type /Pattern /PatternType 2';
14688 $out .= ' /Shading '.$this->gradients
[$id]['id'].' 0 R';
14690 $out .= "\n".'endobj';
14692 $this->gradients
[$id]['pattern'] = $this->n
;
14693 // set shading and pattern for transparency mask
14694 if ($grad['transparency']) {
14695 // luminosity pattern
14696 $idgs = $id +
$idt;
14698 $this->_out($shading_transparency);
14699 $this->gradients
[$idgs]['id'] = $this->n
;
14701 $out = '<< /Type /Pattern /PatternType 2';
14702 $out .= ' /Shading '.$this->gradients
[$idgs]['id'].' 0 R';
14704 $out .= "\n".'endobj';
14706 $this->gradients
[$idgs]['pattern'] = $this->n
;
14707 // luminosity XObject
14708 $oid = $this->_newobj();
14709 $this->xobjects
['LX'.$oid] = array('n' => $oid);
14711 $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt
.' '.$this->hPt
.' re f Q';
14712 if ($this->compress
) {
14713 $filter = ' /Filter /FlateDecode';
14714 $stream = gzcompress($stream);
14716 $stream = $this->_getrawstream($stream);
14717 $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
14718 $out .= ' /Length '.strlen($stream);
14719 $rect = sprintf('%F %F', $this->wPt
, $this->hPt
);
14720 $out .= ' /BBox [0 0 '.$rect.']';
14721 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
14722 $out .= ' /Resources <<';
14723 $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
14724 $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients
[$idgs]['pattern'].' 0 R >>';
14727 $out .= ' stream'."\n".$stream."\n".'endstream';
14728 $out .= "\n".'endobj';
14732 $out = '<< /Type /Mask /S /Luminosity /G '.($this->n
- 1).' 0 R >>'."\n".'endobj';
14736 $out = '<< /Type /ExtGState /SMask '.($this->n
- 1).' 0 R /AIS false >>'."\n".'endobj';
14738 $this->extgstates
[] = array('n' => $this->n
, 'name' => 'TGS'.$id);
14744 * Draw the sector of a circle.
14745 * It can be used for instance to render pie charts.
14746 * @param $xc (float) abscissa of the center.
14747 * @param $yc (float) ordinate of the center.
14748 * @param $r (float) radius.
14749 * @param $a (float) start angle (in degrees).
14750 * @param $b (float) end angle (in degrees).
14751 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
14752 * @param $cw: (float) indicates whether to go clockwise (default: true).
14753 * @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.
14754 * @author Maxime Delorme, Nicola Asuni
14755 * @since 3.1.000 (2008-06-09)
14758 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
14759 $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
14763 * Draw the sector of an ellipse.
14764 * It can be used for instance to render pie charts.
14765 * @param $xc (float) abscissa of the center.
14766 * @param $yc (float) ordinate of the center.
14767 * @param $rx (float) the x-axis radius.
14768 * @param $ry (float) the y-axis radius.
14769 * @param $a (float) start angle (in degrees).
14770 * @param $b (float) end angle (in degrees).
14771 * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
14772 * @param $cw: (float) indicates whether to go clockwise.
14773 * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock).
14774 * @param $nc (integer) Number of curves used to draw a 90 degrees portion of arc.
14775 * @author Maxime Delorme, Nicola Asuni
14776 * @since 3.1.000 (2008-06-09)
14779 public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
14780 if ($this->state
!= 2) {
14784 $xc = ($this->w
- $xc);
14786 $op = TCPDF_STATIC
::getPathPaintOperator($style);
14788 $line_style = array();
14792 $b = (360 - $a +
$o);
14793 $a = (360 - $d +
$o);
14798 $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
14803 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
14804 * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
14805 * Only vector drawing is supported, not text or bitmap.
14806 * 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).
14807 * @param $file (string) Name of the file containing the image or a '@' character followed by the EPS/AI data string.
14808 * @param $x (float) Abscissa of the upper-left corner.
14809 * @param $y (float) Ordinate of the upper-left corner.
14810 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
14811 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
14812 * @param $link (mixed) URL or identifier returned by AddLink().
14813 * @param $useBoundingBox (boolean) specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
14814 * @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>
14815 * @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>
14816 * @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)))
14817 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
14818 * @param $fixoutvals (boolean) if true remove values outside the bounding box.
14819 * @author Valentin Schmidt, Nicola Asuni
14820 * @since 3.1.000 (2008-06-09)
14823 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
14824 if ($this->state
!= 2) {
14827 if ($this->rasterize_vector_images
AND ($w > 0) AND ($h > 0)) {
14828 // convert EPS to raster image using GD or ImageMagick libraries
14829 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14837 // check page for no-write regions and adapt page margins if necessary
14838 list($x, $y) = $this->checkPageRegions($h, $x, $y);
14840 if ($file[0] === '@') { // image from string
14841 $data = substr($file, 1);
14842 } else { // EPS/AI file
14843 $data = TCPDF_STATIC
::fileGetContents($file);
14845 if ($data === FALSE) {
14846 $this->Error('EPS file not found: '.$file);
14849 // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
14850 preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
14851 if (count($regs) > 1) {
14852 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
14853 if (strpos($version_str, 'Adobe Illustrator') !== false) {
14854 $versexp = explode(' ', $version_str);
14855 $version = (float)array_pop($versexp);
14856 if ($version >= 9) {
14857 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
14861 // strip binary bytes in front of PS-header
14862 $start = strpos($data, '%!PS-Adobe');
14864 $data = substr($data, $start);
14866 // find BoundingBox params
14867 preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
14868 if (count($regs) > 1) {
14869 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
14871 $this->Error('No BoundingBox found in EPS/AI file: '.$file);
14873 $start = strpos($data, '%%EndSetup');
14874 if ($start === false) {
14875 $start = strpos($data, '%%EndProlog');
14877 if ($start === false) {
14878 $start = strpos($data, '%%BoundingBox');
14880 $data = substr($data, $start);
14881 $end = strpos($data, '%%PageTrailer');
14882 if ($end===false) {
14883 $end = strpos($data, 'showpage');
14886 $data = substr($data, 0, $end);
14888 // calculate image width and height on document
14889 if (($w <= 0) AND ($h <= 0)) {
14890 $w = ($x2 - $x1) / $k;
14891 $h = ($y2 - $y1) / $k;
14892 } elseif ($w <= 0) {
14893 $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
14894 } elseif ($h <= 0) {
14895 $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
14897 // fit the image on available space
14898 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
14899 if ($this->rasterize_vector_images
) {
14900 // convert EPS to raster image using GD or ImageMagick libraries
14901 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14903 // set scaling factors
14904 $scale_x = $w / (($x2 - $x1) / $k);
14905 $scale_y = $h / (($y2 - $y1) / $k);
14907 $this->img_rb_y
= $y +
$h;
14910 if ($palign == 'L') {
14911 $ximg = $this->lMargin
;
14912 } elseif ($palign == 'C') {
14913 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
14914 } elseif ($palign == 'R') {
14915 $ximg = $this->w
- $this->rMargin
- $w;
14919 $this->img_rb_x
= $ximg;
14921 if ($palign == 'L') {
14922 $ximg = $this->lMargin
;
14923 } elseif ($palign == 'C') {
14924 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
14925 } elseif ($palign == 'R') {
14926 $ximg = $this->w
- $this->rMargin
- $w;
14930 $this->img_rb_x
= $ximg +
$w;
14932 if ($useBoundingBox) {
14933 $dx = $ximg * $k - $x1;
14934 $dy = $y * $k - $y1;
14939 // save the current graphic state
14940 $this->_out('q'.$this->epsmarker
);
14942 $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy +
($this->hPt
- (2 * $y * $k) - ($y2 - $y1))));
14944 if (isset($scale_x)) {
14945 $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
14947 // handle pc/unix/mac line endings
14948 $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY
);
14950 $cnt = count($lines);
14951 for ($i=0; $i < $cnt; ++
$i) {
14952 $line = $lines[$i];
14953 if (($line == '') OR ($line[0] == '%')) {
14956 $len = strlen($line);
14957 // check for spot color names
14959 if (strcasecmp('x', substr(trim($line), -1)) == 0) {
14960 if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) {
14961 // extract spot color name
14962 $color_name = $matches[0];
14963 // remove color name from string
14964 $line = str_replace(' '.$color_name, '', $line);
14965 // remove pharentesis from color name
14966 $color_name = substr($color_name, 1, -1);
14969 $chunks = explode(' ', $line);
14970 $cmd = trim(array_pop($chunks));
14972 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
14973 $b = array_pop($chunks);
14974 $g = array_pop($chunks);
14975 $r = array_pop($chunks);
14976 $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!
14981 // check for values outside the bounding box
14986 // skip values outside bounding box
14987 foreach ($chunks as $key => $val) {
14988 if ((($key %
2) == 0) AND (($val < $x1) OR ($val > $x2))) {
14990 } elseif ((($key %
2) != 0) AND (($val < $y1) OR ($val > $y2))) {
15018 $this->_out($line);
15021 case 'x': {// custom fill color
15022 if (empty($color_name)) {
15024 list($col_c, $col_m, $col_y, $col_k) = $chunks;
15025 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
15027 // Spot Color (CMYK + tint)
15028 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15029 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15030 $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors
[$color_name]['i'], (1 - $col_t));
15031 $this->_out($color_cmd);
15035 case 'X': { // custom stroke color
15036 if (empty($color_name)) {
15038 list($col_c, $col_m, $col_y, $col_k) = $chunks;
15039 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
15041 // Spot Color (CMYK + tint)
15042 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15043 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15044 $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors
[$color_name]['i'], (1 - $col_t));
15045 $this->_out($color_cmd);
15057 $line[($len - 1)] = strtolower($cmd);
15058 $this->_out($line);
15063 $this->_out($cmd . '*');
15070 $max = min(($i +
5), $cnt);
15071 for ($j = ($i +
1); $j < $max; ++
$j) {
15072 $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
15092 // restore previous graphic state
15093 $this->_out($this->epsmarker
.'Q');
15094 if (!empty($border)) {
15102 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
15107 $this->Link($ximg, $y, $w, $h, $link, 0);
15109 // set pointer to align the next text/objects
15113 $this->x
= $this->img_rb_x
;
15117 $this->y
= $y +
round($h/2);
15118 $this->x
= $this->img_rb_x
;
15122 $this->y
= $this->img_rb_y
;
15123 $this->x
= $this->img_rb_x
;
15127 $this->SetY($this->img_rb_y
);
15134 $this->endlinex
= $this->img_rb_x
;
15138 * Set document barcode.
15139 * @param $bc (string) barcode
15142 public function setBarcode($bc='') {
15143 $this->barcode
= $bc;
15147 * Get current barcode.
15150 * @since 4.0.012 (2008-07-24)
15152 public function getBarcode() {
15153 return $this->barcode
;
15157 * Print a Linear Barcode.
15158 * @param $code (string) code to print
15159 * @param $type (string) type of barcode (see tcpdf_barcodes_1d.php for supported formats).
15160 * @param $x (int) x position in user units (empty string = current x position)
15161 * @param $y (int) y position in user units (empty string = current y position)
15162 * @param $w (int) width in user units (empty string = remaining page width)
15163 * @param $h (int) height in user units (empty string = remaining page height)
15164 * @param $xres (float) width of the smallest bar in user units (empty string = default value = 0.4mm)
15165 * @param $style (array) array of options:<ul>
15166 * <li>boolean $style['border'] if true prints a border</li>
15167 * <li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li>
15168 * <li>int $style['hpadding'] horizontal padding in user units (set to 'auto' for automatic padding)</li>
15169 * <li>int $style['vpadding'] vertical padding in user units (set to 'auto' for automatic padding)</li>
15170 * <li>array $style['fgcolor'] color array for bars and text</li>
15171 * <li>mixed $style['bgcolor'] color array for background (set to false for transparent)</li>
15172 * <li>boolean $style['text'] if true prints text below the barcode</li>
15173 * <li>string $style['label'] override default label</li>
15174 * <li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li>
15175 * <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>
15176 * <li>string $style['position'] horizontal position of the containing barcode cell on the page: L = left margin; C = center; R = right margin.</li>
15177 * <li>string $style['align'] horizontal position of the barcode on the containing rectangle: L = left; C = center; R = right.</li>
15178 * <li>string $style['stretch'] if true stretch the barcode to best fit the available width, otherwise uses $xres resolution for a single bar.</li>
15179 * <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>
15180 * <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>
15181 * @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>
15182 * @author Nicola Asuni
15183 * @since 3.1.000 (2008-06-09)
15186 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
15187 if (TCPDF_STATIC
::empty_string(trim($code))) {
15190 require_once(dirname(__FILE__
).'/tcpdf_barcodes_1d.php');
15191 // save current graphic settings
15192 $gvars = $this->getGraphicVars();
15193 // create new barcode object
15194 $barcodeobj = new TCPDFBarcode($code, $type);
15195 $arrcode = $barcodeobj->getBarcodeArray();
15196 if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] <= 0)) {
15197 $this->Error('Error in 1D barcode string');
15199 if ($arrcode['maxh'] <= 0) {
15200 $arrcode['maxh'] = 1;
15202 // set default values
15203 if (!isset($style['position'])) {
15204 $style['position'] = '';
15205 } elseif ($style['position'] == 'S') {
15206 // keep this for backward compatibility
15207 $style['position'] = '';
15208 $style['stretch'] = true;
15210 if (!isset($style['fitwidth'])) {
15211 if (!isset($style['stretch'])) {
15212 $style['fitwidth'] = true;
15214 $style['fitwidth'] = false;
15217 if ($style['fitwidth']) {
15219 $style['stretch'] = false;
15221 if (!isset($style['stretch'])) {
15222 if (($w === '') OR ($w <= 0)) {
15223 $style['stretch'] = false;
15225 $style['stretch'] = true;
15228 if (!isset($style['fgcolor'])) {
15229 $style['fgcolor'] = array(0,0,0); // default black
15231 if (!isset($style['bgcolor'])) {
15232 $style['bgcolor'] = false; // default transparent
15234 if (!isset($style['border'])) {
15235 $style['border'] = false;
15238 if (!isset($style['text'])) {
15239 $style['text'] = false;
15241 if ($style['text'] AND isset($style['font'])) {
15242 if (isset($style['fontsize'])) {
15243 $fontsize = $style['fontsize'];
15245 $this->SetFont($style['font'], '', $fontsize);
15247 if (!isset($style['stretchtext'])) {
15248 $style['stretchtext'] = 4;
15256 // check page for no-write regions and adapt page margins if necessary
15257 list($x, $y) = $this->checkPageRegions($h, $x, $y);
15258 if (($w === '') OR ($w <= 0)) {
15260 $w = $x - $this->lMargin
;
15262 $w = $this->w
- $this->rMargin
- $x;
15266 if (!isset($style['padding'])) {
15268 } elseif ($style['padding'] === 'auto') {
15269 $padding = 10 * ($w / ($arrcode['maxw'] +
20));
15271 $padding = floatval($style['padding']);
15273 // horizontal padding
15274 if (!isset($style['hpadding'])) {
15275 $hpadding = $padding;
15276 } elseif ($style['hpadding'] === 'auto') {
15277 $hpadding = 10 * ($w / ($arrcode['maxw'] +
20));
15279 $hpadding = floatval($style['hpadding']);
15281 // vertical padding
15282 if (!isset($style['vpadding'])) {
15283 $vpadding = $padding;
15284 } elseif ($style['vpadding'] === 'auto') {
15285 $vpadding = ($hpadding / 2);
15287 $vpadding = floatval($style['vpadding']);
15289 // calculate xres (single bar width)
15290 $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
15291 if ($style['stretch']) {
15294 if (TCPDF_STATIC
::empty_string($xres)) {
15295 $xres = (0.141 * $this->k
); // default bar width = 0.4 mm
15297 if ($xres > $max_xres) {
15298 // correct xres to fit on $w
15301 if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
15302 OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
15303 $hpadding = 10 * $xres;
15304 if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
15305 $vpadding = ($hpadding / 2);
15309 if ($style['fitwidth']) {
15311 $w = (($arrcode['maxw'] * $xres) +
(2 * $hpadding));
15312 if (isset($style['cellfitalign'])) {
15313 switch ($style['cellfitalign']) {
15316 $x -= ($wold - $w);
15322 $x +
= ($wold - $w);
15328 $x -= (($wold - $w) / 2);
15330 $x +
= (($wold - $w) / 2);
15340 $text_height = $this->getCellHeight($fontsize / $this->k
);
15342 if (($h === '') OR ($h <= 0)) {
15343 // set default height
15344 $h = (($arrcode['maxw'] * $xres) / 3) +
(2 * $vpadding) +
$text_height;
15346 $barh = $h - $text_height - (2 * $vpadding);
15348 // try to reduce font or padding to fit barcode on available height
15349 if ($text_height > $h) {
15350 $fontsize = (($h * $this->k
) / (4 * $this->cell_height_ratio
));
15351 $text_height = $this->getCellHeight($fontsize / $this->k
);
15352 $this->SetFont($style['font'], '', $fontsize);
15354 if ($vpadding > 0) {
15355 $vpadding = (($h - $text_height) / 4);
15357 $barh = $h - $text_height - (2 * $vpadding);
15359 // fit the barcode on available space
15360 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15362 $this->img_rb_y
= $y +
$h;
15365 if ($style['position'] == 'L') {
15366 $xpos = $this->lMargin
;
15367 } elseif ($style['position'] == 'C') {
15368 $xpos = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15369 } elseif ($style['position'] == 'R') {
15370 $xpos = $this->w
- $this->rMargin
- $w;
15374 $this->img_rb_x
= $xpos;
15376 if ($style['position'] == 'L') {
15377 $xpos = $this->lMargin
;
15378 } elseif ($style['position'] == 'C') {
15379 $xpos = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15380 } elseif ($style['position'] == 'R') {
15381 $xpos = $this->w
- $this->rMargin
- $w;
15385 $this->img_rb_x
= $xpos +
$w;
15387 $xpos_rect = $xpos;
15388 if (!isset($style['align'])) {
15389 $style['align'] = 'C';
15391 switch ($style['align']) {
15393 $xpos = $xpos_rect +
$hpadding;
15397 $xpos = $xpos_rect +
($w - ($arrcode['maxw'] * $xres)) - $hpadding;
15402 $xpos = $xpos_rect +
(($w - ($arrcode['maxw'] * $xres)) / 2);
15406 $xpos_text = $xpos;
15407 // barcode is always printed in LTR direction
15408 $tempRTL = $this->rtl
;
15409 $this->rtl
= false;
15410 // print background color
15411 if ($style['bgcolor']) {
15412 $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ?
'DF' : 'F', '', $style['bgcolor']);
15413 } elseif ($style['border']) {
15414 $this->Rect($xpos_rect, $y, $w, $h, 'D');
15416 // set foreground color
15417 $this->SetDrawColorArray($style['fgcolor']);
15418 $this->SetTextColorArray($style['fgcolor']);
15420 foreach ($arrcode['bcode'] as $k => $v) {
15421 $bw = ($v['w'] * $xres);
15423 // draw a vertical bar
15424 $ypos = $y +
$vpadding +
($v['p'] * $barh / $arrcode['maxh']);
15425 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
15430 if ($style['text']) {
15431 if (isset($style['label']) AND !TCPDF_STATIC
::empty_string($style['label'])) {
15432 $label = $style['label'];
15436 $txtwidth = ($arrcode['maxw'] * $xres);
15437 if ($this->GetStringWidth($label) > $txtwidth) {
15438 $style['stretchtext'] = 2;
15441 $this->x
= $xpos_text;
15442 $this->y
= $y +
$vpadding +
$barh;
15443 $cellpadding = $this->cell_padding
;
15444 $this->SetCellPadding(0);
15445 $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
15446 $this->cell_padding
= $cellpadding;
15448 // restore original direction
15449 $this->rtl
= $tempRTL;
15450 // restore previous settings
15451 $this->setGraphicVars($gvars);
15452 // set pointer to align the next text/objects
15456 $this->x
= $this->img_rb_x
;
15460 $this->y
= $y +
round($h / 2);
15461 $this->x
= $this->img_rb_x
;
15465 $this->y
= $this->img_rb_y
;
15466 $this->x
= $this->img_rb_x
;
15470 $this->SetY($this->img_rb_y
);
15477 $this->endlinex
= $this->img_rb_x
;
15481 * Print 2D Barcode.
15482 * @param $code (string) code to print
15483 * @param $type (string) type of barcode (see tcpdf_barcodes_2d.php for supported formats).
15484 * @param $x (int) x position in user units
15485 * @param $y (int) y position in user units
15486 * @param $w (int) width in user units
15487 * @param $h (int) height in user units
15488 * @param $style (array) array of options:<ul>
15489 * <li>boolean $style['border'] if true prints a border around the barcode</li>
15490 * <li>int $style['padding'] padding to leave around the barcode in barcode units (set to 'auto' for automatic padding)</li>
15491 * <li>int $style['hpadding'] horizontal padding in barcode units (set to 'auto' for automatic padding)</li>
15492 * <li>int $style['vpadding'] vertical padding in barcode units (set to 'auto' for automatic padding)</li>
15493 * <li>int $style['module_width'] width of a single module in points</li>
15494 * <li>int $style['module_height'] height of a single module in points</li>
15495 * <li>array $style['fgcolor'] color array for bars and text</li>
15496 * <li>mixed $style['bgcolor'] color array for background or false for transparent</li>
15497 * <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>
15498 * <li>$style['module_height'] height of a single module in points</li></ul>
15499 * @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>
15500 * @param $distort (boolean) if true distort the barcode to fit width and height, otherwise preserve aspect ratio
15501 * @author Nicola Asuni
15502 * @since 4.5.037 (2009-04-07)
15505 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) {
15506 if (TCPDF_STATIC
::empty_string(trim($code))) {
15509 require_once(dirname(__FILE__
).'/tcpdf_barcodes_2d.php');
15510 // save current graphic settings
15511 $gvars = $this->getGraphicVars();
15512 // create new barcode object
15513 $barcodeobj = new TCPDF2DBarcode($code, $type);
15514 $arrcode = $barcodeobj->getBarcodeArray();
15515 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)) {
15516 $this->Error('Error in 2D barcode string');
15518 // set default values
15519 if (!isset($style['position'])) {
15520 $style['position'] = '';
15522 if (!isset($style['fgcolor'])) {
15523 $style['fgcolor'] = array(0,0,0); // default black
15525 if (!isset($style['bgcolor'])) {
15526 $style['bgcolor'] = false; // default transparent
15528 if (!isset($style['border'])) {
15529 $style['border'] = false;
15532 if (!isset($style['padding'])) {
15533 $style['padding'] = 0;
15534 } elseif ($style['padding'] === 'auto') {
15535 $style['padding'] = 4;
15537 if (!isset($style['hpadding'])) {
15538 $style['hpadding'] = $style['padding'];
15539 } elseif ($style['hpadding'] === 'auto') {
15540 $style['hpadding'] = 4;
15542 if (!isset($style['vpadding'])) {
15543 $style['vpadding'] = $style['padding'];
15544 } elseif ($style['vpadding'] === 'auto') {
15545 $style['vpadding'] = 4;
15547 $hpad = (2 * $style['hpadding']);
15548 $vpad = (2 * $style['vpadding']);
15549 // cell (module) dimension
15550 if (!isset($style['module_width'])) {
15551 $style['module_width'] = 1; // width of a single module in points
15553 if (!isset($style['module_height'])) {
15554 $style['module_height'] = 1; // height of a single module in points
15562 // check page for no-write regions and adapt page margins if necessary
15563 list($x, $y) = $this->checkPageRegions($h, $x, $y);
15564 // number of barcode columns and rows
15565 $rows = $arrcode['num_rows'];
15566 $cols = $arrcode['num_cols'];
15567 if (($rows <= 0) ||
($cols <= 0)){
15568 $this->Error('Error in 2D barcode string');
15570 // module width and height
15571 $mw = $style['module_width'];
15572 $mh = $style['module_height'];
15573 if (($mw <= 0) OR ($mh <= 0)) {
15574 $this->Error('Error in 2D barcode string');
15576 // get max dimensions
15578 $maxw = $x - $this->lMargin
;
15580 $maxw = $this->w
- $this->rMargin
- $x;
15582 $maxh = ($this->h
- $this->tMargin
- $this->bMargin
);
15583 $ratioHW = ((($rows * $mh) +
$hpad) / (($cols * $mw) +
$vpad));
15584 $ratioWH = ((($cols * $mw) +
$vpad) / (($rows * $mh) +
$hpad));
15586 if (($maxw * $ratioHW) > $maxh) {
15587 $maxw = $maxh * $ratioWH;
15589 if (($maxh * $ratioWH) > $maxw) {
15590 $maxh = $maxw * $ratioHW;
15593 // set maximum dimesions
15601 if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
15602 $w = ($cols +
$hpad) * ($mw / $this->k
);
15603 $h = ($rows +
$vpad) * ($mh / $this->k
);
15604 } elseif (($w === '') OR ($w <= 0)) {
15605 $w = $h * $ratioWH;
15606 } elseif (($h === '') OR ($h <= 0)) {
15607 $h = $w * $ratioHW;
15609 // barcode size (excluding padding)
15610 $bw = ($w * $cols) / ($cols +
$hpad);
15611 $bh = ($h * $rows) / ($rows +
$vpad);
15612 // dimension of single barcode cell unit
15616 if (($cw / $ch) > ($mw / $mh)) {
15617 // correct horizontal distortion
15618 $cw = $ch * $mw / $mh;
15620 $style['hpadding'] = ($w - $bw) / (2 * $cw);
15622 // correct vertical distortion
15623 $ch = $cw * $mh / $mw;
15625 $style['vpadding'] = ($h - $bh) / (2 * $ch);
15628 // fit the barcode on available space
15629 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15631 $this->img_rb_y
= $y +
$h;
15634 if ($style['position'] == 'L') {
15635 $xpos = $this->lMargin
;
15636 } elseif ($style['position'] == 'C') {
15637 $xpos = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15638 } elseif ($style['position'] == 'R') {
15639 $xpos = $this->w
- $this->rMargin
- $w;
15643 $this->img_rb_x
= $xpos;
15645 if ($style['position'] == 'L') {
15646 $xpos = $this->lMargin
;
15647 } elseif ($style['position'] == 'C') {
15648 $xpos = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
15649 } elseif ($style['position'] == 'R') {
15650 $xpos = $this->w
- $this->rMargin
- $w;
15654 $this->img_rb_x
= $xpos +
$w;
15656 $xstart = $xpos +
($style['hpadding'] * $cw);
15657 $ystart = $y +
($style['vpadding'] * $ch);
15658 // barcode is always printed in LTR direction
15659 $tempRTL = $this->rtl
;
15660 $this->rtl
= false;
15661 // print background color
15662 if ($style['bgcolor']) {
15663 $this->Rect($xpos, $y, $w, $h, $style['border'] ?
'DF' : 'F', '', $style['bgcolor']);
15664 } elseif ($style['border']) {
15665 $this->Rect($xpos, $y, $w, $h, 'D');
15667 // set foreground color
15668 $this->SetDrawColorArray($style['fgcolor']);
15669 // print barcode cells
15671 for ($r = 0; $r < $rows; ++
$r) {
15674 for ($c = 0; $c < $cols; ++
$c) {
15675 if ($arrcode['bcode'][$r][$c] == 1) {
15676 // draw a single barcode cell
15677 $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
15683 // restore original direction
15684 $this->rtl
= $tempRTL;
15685 // restore previous settings
15686 $this->setGraphicVars($gvars);
15687 // set pointer to align the next text/objects
15691 $this->x
= $this->img_rb_x
;
15695 $this->y
= $y +
round($h/2);
15696 $this->x
= $this->img_rb_x
;
15700 $this->y
= $this->img_rb_y
;
15701 $this->x
= $this->img_rb_x
;
15705 $this->SetY($this->img_rb_y
);
15712 $this->endlinex
= $this->img_rb_x
;
15716 * Returns an array containing current margins:
15718 <li>$ret['left'] = left margin</li>
15719 <li>$ret['right'] = right margin</li>
15720 <li>$ret['top'] = top margin</li>
15721 <li>$ret['bottom'] = bottom margin</li>
15722 <li>$ret['header'] = header margin</li>
15723 <li>$ret['footer'] = footer margin</li>
15724 <li>$ret['cell'] = cell padding array</li>
15725 <li>$ret['padding_left'] = cell left padding</li>
15726 <li>$ret['padding_top'] = cell top padding</li>
15727 <li>$ret['padding_right'] = cell right padding</li>
15728 <li>$ret['padding_bottom'] = cell bottom padding</li>
15730 * @return array containing all margins measures
15732 * @since 3.2.000 (2008-06-23)
15734 public function getMargins() {
15736 'left' => $this->lMargin
,
15737 'right' => $this->rMargin
,
15738 'top' => $this->tMargin
,
15739 'bottom' => $this->bMargin
,
15740 'header' => $this->header_margin
,
15741 'footer' => $this->footer_margin
,
15742 'cell' => $this->cell_padding
,
15743 'padding_left' => $this->cell_padding
['L'],
15744 'padding_top' => $this->cell_padding
['T'],
15745 'padding_right' => $this->cell_padding
['R'],
15746 'padding_bottom' => $this->cell_padding
['B']
15752 * Returns an array containing original margins:
15754 <li>$ret['left'] = left margin</li>
15755 <li>$ret['right'] = right margin</li>
15757 * @return array containing all margins measures
15759 * @since 4.0.012 (2008-07-24)
15761 public function getOriginalMargins() {
15763 'left' => $this->original_lMargin
,
15764 'right' => $this->original_rMargin
15770 * Returns the current font size.
15771 * @return current font size
15773 * @since 3.2.000 (2008-06-23)
15775 public function getFontSize() {
15776 return $this->FontSize
;
15780 * Returns the current font size in points unit.
15781 * @return current font size in points unit
15783 * @since 3.2.000 (2008-06-23)
15785 public function getFontSizePt() {
15786 return $this->FontSizePt
;
15790 * Returns the current font family name.
15791 * @return string current font family name
15793 * @since 4.3.008 (2008-12-05)
15795 public function getFontFamily() {
15796 return $this->FontFamily
;
15800 * Returns the current font style.
15801 * @return string current font style
15803 * @since 4.3.008 (2008-12-05)
15805 public function getFontStyle() {
15806 return $this->FontStyle
;
15810 * Cleanup HTML code (requires HTML Tidy library).
15811 * @param $html (string) htmlcode to fix
15812 * @param $default_css (string) CSS commands to add
15813 * @param $tagvs (array) parameters for setHtmlVSpace method
15814 * @param $tidy_options (array) options for tidy_parse_string function
15815 * @return string XHTML code cleaned up
15816 * @author Nicola Asuni
15818 * @since 5.9.017 (2010-11-16)
15819 * @see setHtmlVSpace()
15821 public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
15822 return TCPDF_STATIC
::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces
);
15826 * Returns the border width from CSS property
15827 * @param $width (string) border width
15828 * @return int with in user units
15830 * @since 5.7.000 (2010-08-02)
15832 protected function getCSSBorderWidth($width) {
15833 if ($width == 'thin') {
15834 $width = (2 / $this->k
);
15835 } elseif ($width == 'medium') {
15836 $width = (4 / $this->k
);
15837 } elseif ($width == 'thick') {
15838 $width = (6 / $this->k
);
15840 $width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
15846 * Returns the border dash style from CSS property
15847 * @param $style (string) border style to convert
15848 * @return int sash style (return -1 in case of none or hidden border)
15850 * @since 5.7.000 (2010-08-02)
15852 protected function getCSSBorderDashStyle($style) {
15853 switch (strtolower($style)) {
15882 * Returns the border style array from CSS border properties
15883 * @param $cssborder (string) border properties
15884 * @return array containing border properties
15886 * @since 5.7.000 (2010-08-02)
15888 protected function getCSSBorderStyle($cssborder) {
15889 $bprop = preg_split('/[\s]+/', trim($cssborder));
15890 $border = array(); // value to be returned
15891 switch (count($bprop)) {
15893 $width = $bprop[0];
15894 $style = $bprop[1];
15895 $color = $bprop[2];
15900 $style = $bprop[0];
15901 $color = $bprop[1];
15906 $style = $bprop[0];
15917 if ($style == 'none') {
15920 $border['cap'] = 'square';
15921 $border['join'] = 'miter';
15922 $border['dash'] = $this->getCSSBorderDashStyle($style);
15923 if ($border['dash'] < 0) {
15926 $border['width'] = $this->getCSSBorderWidth($width);
15927 $border['color'] = TCPDF_COLORS
::convertHTMLColorToDec($color, $this->spot_colors
);
15932 * Get the internal Cell padding from CSS attribute.
15933 * @param $csspadding (string) padding properties
15934 * @param $width (float) width of the containing element
15935 * @return array of cell paddings
15937 * @since 5.9.000 (2010-10-04)
15939 public function getCSSPadding($csspadding, $width=0) {
15940 $padding = preg_split('/[\s]+/', trim($csspadding));
15941 $cell_padding = array(); // value to be returned
15942 switch (count($padding)) {
15944 $cell_padding['T'] = $padding[0];
15945 $cell_padding['R'] = $padding[1];
15946 $cell_padding['B'] = $padding[2];
15947 $cell_padding['L'] = $padding[3];
15951 $cell_padding['T'] = $padding[0];
15952 $cell_padding['R'] = $padding[1];
15953 $cell_padding['B'] = $padding[2];
15954 $cell_padding['L'] = $padding[1];
15958 $cell_padding['T'] = $padding[0];
15959 $cell_padding['R'] = $padding[1];
15960 $cell_padding['B'] = $padding[0];
15961 $cell_padding['L'] = $padding[1];
15965 $cell_padding['T'] = $padding[0];
15966 $cell_padding['R'] = $padding[0];
15967 $cell_padding['B'] = $padding[0];
15968 $cell_padding['L'] = $padding[0];
15972 return $this->cell_padding
;
15976 $width = $this->w
- $this->lMargin
- $this->rMargin
;
15978 $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
15979 $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
15980 $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
15981 $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
15982 return $cell_padding;
15986 * Get the internal Cell margin from CSS attribute.
15987 * @param $cssmargin (string) margin properties
15988 * @param $width (float) width of the containing element
15989 * @return array of cell margins
15991 * @since 5.9.000 (2010-10-04)
15993 public function getCSSMargin($cssmargin, $width=0) {
15994 $margin = preg_split('/[\s]+/', trim($cssmargin));
15995 $cell_margin = array(); // value to be returned
15996 switch (count($margin)) {
15998 $cell_margin['T'] = $margin[0];
15999 $cell_margin['R'] = $margin[1];
16000 $cell_margin['B'] = $margin[2];
16001 $cell_margin['L'] = $margin[3];
16005 $cell_margin['T'] = $margin[0];
16006 $cell_margin['R'] = $margin[1];
16007 $cell_margin['B'] = $margin[2];
16008 $cell_margin['L'] = $margin[1];
16012 $cell_margin['T'] = $margin[0];
16013 $cell_margin['R'] = $margin[1];
16014 $cell_margin['B'] = $margin[0];
16015 $cell_margin['L'] = $margin[1];
16019 $cell_margin['T'] = $margin[0];
16020 $cell_margin['R'] = $margin[0];
16021 $cell_margin['B'] = $margin[0];
16022 $cell_margin['L'] = $margin[0];
16026 return $this->cell_margin
;
16030 $width = $this->w
- $this->lMargin
- $this->rMargin
;
16032 $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
16033 $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
16034 $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
16035 $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
16036 return $cell_margin;
16040 * Get the border-spacing from CSS attribute.
16041 * @param $cssbspace (string) border-spacing CSS properties
16042 * @param $width (float) width of the containing element
16043 * @return array of border spacings
16045 * @since 5.9.010 (2010-10-27)
16047 public function getCSSBorderMargin($cssbspace, $width=0) {
16048 $space = preg_split('/[\s]+/', trim($cssbspace));
16049 $border_spacing = array(); // value to be returned
16050 switch (count($space)) {
16052 $border_spacing['H'] = $space[0];
16053 $border_spacing['V'] = $space[1];
16057 $border_spacing['H'] = $space[0];
16058 $border_spacing['V'] = $space[0];
16062 return array('H' => 0, 'V' => 0);
16066 $width = $this->w
- $this->lMargin
- $this->rMargin
;
16068 $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
16069 $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
16070 return $border_spacing;
16074 * Returns the letter-spacing value from CSS value
16075 * @param $spacing (string) letter-spacing value
16076 * @param $parent (float) font spacing (tracking) value of the parent element
16077 * @return float quantity to increases or decreases the space between characters in a text.
16079 * @since 5.9.000 (2010-10-02)
16081 protected function getCSSFontSpacing($spacing, $parent=0) {
16082 $val = 0; // value to be returned
16083 $spacing = trim($spacing);
16084 switch ($spacing) {
16090 if ($parent == 'normal') {
16098 $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
16105 * Returns the percentage of font stretching from CSS value
16106 * @param $stretch (string) stretch mode
16107 * @param $parent (float) stretch value of the parent element
16108 * @return float font stretching percentage
16110 * @since 5.9.000 (2010-10-02)
16112 protected function getCSSFontStretching($stretch, $parent=100) {
16113 $val = 100; // value to be returned
16114 $stretch = trim($stretch);
16115 switch ($stretch) {
16116 case 'ultra-condensed': {
16120 case 'extra-condensed': {
16124 case 'condensed': {
16128 case 'semi-condensed': {
16136 case 'semi-expanded': {
16144 case 'extra-expanded': {
16148 case 'ultra-expanded': {
16153 $val = ($parent +
10);
16157 $val = ($parent - 10);
16161 if ($parent == 'normal') {
16169 $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
16176 * Convert HTML string containing font size value to points
16177 * @param $val (string) String containing font size value and unit.
16178 * @param $refsize (float) Reference font size in points.
16179 * @param $parent_size (float) Parent font size in points.
16180 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
16181 * @return float value in points
16184 public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') {
16185 $refsize = TCPDF_FONTS
::getFontRefSize($refsize);
16186 $parent_size = TCPDF_FONTS
::getFontRefSize($parent_size, $refsize);
16189 $size = ($refsize - 4);
16193 $size = ($refsize - 3);
16197 $size = ($refsize - 2);
16205 $size = ($refsize +
2);
16209 $size = ($refsize +
4);
16213 $size = ($refsize +
6);
16217 $size = ($parent_size - 3);
16221 $size = ($parent_size +
3);
16225 $size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true);
16232 * Returns the HTML DOM array.
16233 * @param $html (string) html code
16236 * @since 3.2.000 (2008-06-20)
16238 protected function getHtmlDomArray($html) {
16239 // array of CSS styles ( selector => properties).
16241 // get CSS array defined at previous call
16242 $matches = array();
16243 if (preg_match_all('/<cssarray>([^\<]*)<\/cssarray>/isU', $html, $matches) > 0) {
16244 if (isset($matches[1][0])) {
16245 $css = array_merge($css, unserialize($this->unhtmlentities($matches[1][0])));
16247 $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
16249 // extract external CSS files
16250 $matches = array();
16251 if (preg_match_all('/<link([^\>]*)>/isU', $html, $matches) > 0) {
16252 foreach ($matches[1] as $key => $link) {
16254 if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
16256 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
16257 // get 'all' and 'print' media, other media types are discarded
16258 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16259 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16261 if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
16262 // read CSS data file
16263 $cssdata = TCPDF_STATIC
::fileGetContents(trim($type[1]));
16264 if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) {
16265 $css = array_merge($css, TCPDF_STATIC
::extractCSSproperties($cssdata));
16272 // extract style tags
16273 $matches = array();
16274 if (preg_match_all('/<style([^\>]*)>([^\<]*)<\/style>/isU', $html, $matches) > 0) {
16275 foreach ($matches[1] as $key => $media) {
16277 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
16278 // get 'all' and 'print' media, other media types are discarded
16279 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16280 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16281 $cssdata = $matches[2][$key];
16282 $css = array_merge($css, TCPDF_STATIC
::extractCSSproperties($cssdata));
16286 // create a special tag to contain the CSS array (used for table content)
16287 $csstagarray = '<cssarray>'.htmlentities(serialize($css)).'</cssarray>';
16288 // remove head and style blocks
16289 $html = preg_replace('/<head([^\>]*)>(.*?)<\/head>/siU', '', $html);
16290 $html = preg_replace('/<style([^\>]*)>([^\<]*)<\/style>/isU', '', $html);
16291 // define block tags
16292 $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
16293 // define self-closing tags
16294 $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
16295 // remove all unsupported tags (the line below lists all supported tags)
16296 $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>');
16297 //replace some blank characters
16298 $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
16299 $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);
16300 $html = preg_replace('@(\r\n|\r)@', "\n", $html);
16301 $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
16302 $html = strtr($html, $repTable);
16304 while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
16305 $html_a = substr($html, 0, $offset);
16306 $html_b = substr($html, $offset, ($pos - $offset +
6));
16307 while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
16308 // preserve newlines on <pre> tag
16309 $html_b = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
16311 while (preg_match("'<xre([^\>]*)>(.*?)".$this->re_space
['p']."(.*?)</pre>'".$this->re_space
['m'], $html_b)) {
16312 // preserve spaces on <pre> tag
16313 $html_b = preg_replace("'<xre([^\>]*)>(.*?)".$this->re_space
['p']."(.*?)</pre>'".$this->re_space
['m'], "<xre\\1>\\2 \\3</pre>", $html_b);
16315 $html = $html_a.$html_b.substr($html, $pos +
6);
16316 $offset = strlen($html_a.$html_b);
16319 while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
16320 $html_a = substr($html, 0, $offset);
16321 $html_b = substr($html, $offset, ($pos - $offset +
11));
16322 while (preg_match("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
16323 // preserve newlines on <textarea> tag
16324 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
16325 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
16327 $html = $html_a.$html_b.substr($html, $pos +
11);
16328 $offset = strlen($html_a.$html_b);
16330 $html = preg_replace('/([\s]*)<option/si', '<option', $html);
16331 $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
16333 while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
16334 $html_a = substr($html, 0, $offset);
16335 $html_b = substr($html, $offset, ($pos - $offset +
9));
16336 while (preg_match("'<option([^\>]*)>(.*?)</option>'si", $html_b)) {
16337 $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
16338 $html_b = preg_replace("'<option([^\>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
16340 $html = $html_a.$html_b.substr($html, $pos +
9);
16341 $offset = strlen($html_a.$html_b);
16343 if (preg_match("'</select'si", $html)) {
16344 $html = preg_replace("'<select([^\>]*)>'si", "<select\\1 opt=\"", $html);
16345 $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
16347 $html = str_replace("\n", ' ', $html);
16348 // restore textarea newlines
16349 $html = str_replace('<TBR>', "\n", $html);
16350 // remove extra spaces from code
16351 $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
16352 $html = preg_replace('/'.$this->re_space
['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space
['m'], '</\\1>', $html);
16353 $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
16354 $html = preg_replace('/'.$this->re_space
['p'].'+<(ul|ol|dl|br)/'.$this->re_space
['m'], '<\\1', $html);
16355 $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);
16356 $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
16357 $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
16358 $html = preg_replace('/'.$this->re_space
['p'].'+<img/'.$this->re_space
['m'], chr(32).'<img', $html);
16359 $html = preg_replace('/<img([^\>]*)>[\s]+([^\<])/xi', '<img\\1> \\2', $html);
16360 $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
16361 $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
16362 $html = preg_replace('/<textarea([^\>]*)>([^\<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
16363 $html = preg_replace('/<li([^\>]*)><\/li>/', '<li\\1> </li>', $html);
16364 $html = preg_replace('/<li([^\>]*)>'.$this->re_space
['p'].'*<img/'.$this->re_space
['m'], '<li\\1><font size="1"> </font><img', $html);
16365 $html = preg_replace('/<([^\>\/]*)>[\s]/', '<\\1> ', $html); // preserve some spaces
16366 $html = preg_replace('/[\s]<\/([^\>]*)>/', ' </\\1>', $html); // preserve some spaces
16367 $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment
16368 $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment
16369 $html = preg_replace('/'.$this->re_space
['p'].'+/'.$this->re_space
['m'], chr(32), $html); // replace multiple spaces with a single space
16371 $html = $this->stringTrim($html);
16372 // fix br tag after li
16373 $html = preg_replace('/<li><br([^\>]*)>/', '<li> <br\\1>', $html);
16374 // fix first image tag alignment
16375 $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
16376 // pattern for generic tag
16377 $tagpattern = '/(<[^>]+>)/';
16378 // explodes the string
16379 $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
);
16381 $maxel = count($a);
16384 // create an array of elements
16386 $dom[$key] = array();
16387 // set inheritable properties fot the first void element
16388 // 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
16389 $dom[$key]['tag'] = false;
16390 $dom[$key]['block'] = false;
16391 $dom[$key]['value'] = '';
16392 $dom[$key]['parent'] = 0;
16393 $dom[$key]['hide'] = false;
16394 $dom[$key]['fontname'] = $this->FontFamily
;
16395 $dom[$key]['fontstyle'] = $this->FontStyle
;
16396 $dom[$key]['fontsize'] = $this->FontSizePt
;
16397 $dom[$key]['font-stretch'] = $this->font_stretching
;
16398 $dom[$key]['letter-spacing'] = $this->font_spacing
;
16399 $dom[$key]['stroke'] = $this->textstrokewidth
;
16400 $dom[$key]['fill'] = (($this->textrendermode %
2) == 0);
16401 $dom[$key]['clip'] = ($this->textrendermode
> 3);
16402 $dom[$key]['line-height'] = $this->cell_height_ratio
;
16403 $dom[$key]['bgcolor'] = false;
16404 $dom[$key]['fgcolor'] = $this->fgcolor
; // color
16405 $dom[$key]['strokecolor'] = $this->strokecolor
;
16406 $dom[$key]['align'] = '';
16407 $dom[$key]['listtype'] = '';
16408 $dom[$key]['text-indent'] = 0;
16409 $dom[$key]['text-transform'] = '';
16410 $dom[$key]['border'] = array();
16411 $dom[$key]['dir'] = $this->rtl?
'rtl':'ltr';
16412 $thead = false; // true when we are inside the THEAD tag
16415 array_push($level, 0); // root
16416 while ($elkey < $maxel) {
16417 $dom[$key] = array();
16418 $element = $a[$elkey];
16419 $dom[$key]['elkey'] = $elkey;
16420 if (preg_match($tagpattern, $element)) {
16422 $element = substr($element, 1, -1);
16424 preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
16425 $tagname = strtolower($tag[1]);
16426 // check if we are inside a table header
16427 if ($tagname == 'thead') {
16428 if ($element[0] == '/') {
16436 $dom[$key]['tag'] = true;
16437 $dom[$key]['value'] = $tagname;
16438 if (in_array($dom[$key]['value'], $blocktags)) {
16439 $dom[$key]['block'] = true;
16441 $dom[$key]['block'] = false;
16443 if ($element[0] == '/') {
16444 // *** closing html tag
16445 $dom[$key]['opening'] = false;
16446 $dom[$key]['parent'] = end($level);
16448 $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
16449 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
16450 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
16451 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
16452 $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
16453 $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
16454 $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
16455 $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
16456 $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
16457 $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
16458 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
16459 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
16460 $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
16461 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
16462 $dom[$key]['text-transform'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['text-transform'];
16463 $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
16464 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
16465 $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
16467 // set the number of columns in table tag
16468 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
16469 $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
16471 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
16472 $dom[($dom[$key]['parent'])]['content'] = $csstagarray;
16473 for ($i = ($dom[$key]['parent'] +
1); $i < $key; ++
$i) {
16474 $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
16477 // mark nested tables
16478 $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
16479 // remove thead sections from nested tables
16480 $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
16481 $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
16483 // store header rows on a new table
16484 if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
16485 if (TCPDF_STATIC
::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
16486 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
16488 for ($i = $dom[$key]['parent']; $i <= $key; ++
$i) {
16489 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
16491 if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
16492 $dom[($dom[$key]['parent'])]['attribute'] = array();
16494 // header elements must be always contained in a single page
16495 $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
16497 if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC
::empty_string($dom[($dom[$key]['parent'])]['thead']))) {
16498 // remove the nobr attributes from the table header
16499 $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
16500 $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
16503 // *** opening or self-closing html tag
16504 $dom[$key]['opening'] = true;
16505 $dom[$key]['parent'] = end($level);
16506 if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
16507 // self-closing tag
16508 $dom[$key]['self'] = true;
16511 array_push($level, $key);
16512 $dom[$key]['self'] = false;
16514 // copy some values from parent
16517 $parentkey = $dom[$key]['parent'];
16518 $dom[$key]['hide'] = $dom[$parentkey]['hide'];
16519 $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
16520 $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
16521 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
16522 $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
16523 $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
16524 $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
16525 $dom[$key]['fill'] = $dom[$parentkey]['fill'];
16526 $dom[$key]['clip'] = $dom[$parentkey]['clip'];
16527 $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16528 $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
16529 $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
16530 $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
16531 $dom[$key]['align'] = $dom[$parentkey]['align'];
16532 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16533 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16534 $dom[$key]['text-transform'] = $dom[$parentkey]['text-transform'];
16535 $dom[$key]['border'] = array();
16536 $dom[$key]['dir'] = $dom[$parentkey]['dir'];
16539 preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER
);
16540 $dom[$key]['attribute'] = array(); // reset attribute array
16541 while (list($id, $name) = each($attr_array[1])) {
16542 $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
16544 if (!empty($css)) {
16545 // merge CSS style to current style
16546 list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC
::getCSSdataArray($dom, $key, $css);
16547 $dom[$key]['attribute']['style'] = TCPDF_STATIC
::getTagStyleFromCSSarray($dom[$key]['cssdata']);
16549 // split style attributes
16550 if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
16551 // get style attributes
16552 preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER
);
16553 $dom[$key]['style'] = array(); // reset style attribute array
16554 while (list($id, $name) = each($style_array[1])) {
16555 // in case of duplicate attribute the last replace the previous
16556 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
16558 // --- get some style attributes ---
16560 if (isset($dom[$key]['style']['direction'])) {
16561 $dom[$key]['dir'] = $dom[$key]['style']['direction'];
16564 if (isset($dom[$key]['style']['display'])) {
16565 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
16568 if (isset($dom[$key]['style']['font-family'])) {
16569 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
16572 if (isset($dom[$key]['style']['list-style-type'])) {
16573 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
16574 if ($dom[$key]['listtype'] == 'inherit') {
16575 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16579 if (isset($dom[$key]['style']['text-indent'])) {
16580 $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
16581 if ($dom[$key]['text-indent'] == 'inherit') {
16582 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16586 if (isset($dom[$key]['style']['text-transform'])) {
16587 $dom[$key]['text-transform'] = $dom[$key]['style']['text-transform'];
16590 if (isset($dom[$key]['style']['font-size'])) {
16591 $fsize = trim($dom[$key]['style']['font-size']);
16592 $dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt');
16595 if (isset($dom[$key]['style']['font-stretch'])) {
16596 $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
16599 if (isset($dom[$key]['style']['letter-spacing'])) {
16600 $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
16602 // line-height (internally is the cell height ratio)
16603 if (isset($dom[$key]['style']['line-height'])) {
16604 $lineheight = trim($dom[$key]['style']['line-height']);
16605 switch ($lineheight) {
16606 // A normal line height. This is default
16608 $dom[$key]['line-height'] = $dom[0]['line-height'];
16612 $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16615 if (is_numeric($lineheight)) {
16616 // convert to percentage of font height
16617 $lineheight = ($lineheight * 100).'%';
16619 $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
16620 if (substr($lineheight, -1) !== '%') {
16621 $dom[$key]['line-height'] = (($dom[$key]['line-height'] - $this->cell_padding
['T'] - $this->cell_padding
['B']) / $dom[$key]['fontsize']);
16627 if (isset($dom[$key]['style']['font-weight'])) {
16628 if (strtolower($dom[$key]['style']['font-weight'][0]) == 'n') {
16629 if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
16630 $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
16632 } elseif (strtolower($dom[$key]['style']['font-weight'][0]) == 'b') {
16633 $dom[$key]['fontstyle'] .= 'B';
16636 if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style'][0]) == 'i')) {
16637 $dom[$key]['fontstyle'] .= 'I';
16640 if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['style']['color']))) {
16641 $dom[$key]['fgcolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors
);
16642 } elseif ($dom[$key]['value'] == 'a') {
16643 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray
;
16645 // background color
16646 if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['style']['background-color']))) {
16647 $dom[$key]['bgcolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors
);
16650 if (isset($dom[$key]['style']['text-decoration'])) {
16651 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
16652 foreach ($decors as $dec) {
16654 if (!TCPDF_STATIC
::empty_string($dec)) {
16655 if ($dec[0] == 'u') {
16657 $dom[$key]['fontstyle'] .= 'U';
16658 } elseif ($dec[0] == 'l') {
16660 $dom[$key]['fontstyle'] .= 'D';
16661 } elseif ($dec[0] == 'o') {
16663 $dom[$key]['fontstyle'] .= 'O';
16667 } elseif ($dom[$key]['value'] == 'a') {
16668 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle
;
16670 // check for width attribute
16671 if (isset($dom[$key]['style']['width'])) {
16672 $dom[$key]['width'] = $dom[$key]['style']['width'];
16674 // check for height attribute
16675 if (isset($dom[$key]['style']['height'])) {
16676 $dom[$key]['height'] = $dom[$key]['style']['height'];
16678 // check for text alignment
16679 if (isset($dom[$key]['style']['text-align'])) {
16680 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align'][0]);
16682 // check for CSS border properties
16683 if (isset($dom[$key]['style']['border'])) {
16684 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
16685 if (!empty($borderstyle)) {
16686 $dom[$key]['border']['LTRB'] = $borderstyle;
16689 if (isset($dom[$key]['style']['border-color'])) {
16690 $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
16691 if (isset($brd_colors[3])) {
16692 $dom[$key]['border']['L']['color'] = TCPDF_COLORS
::convertHTMLColorToDec($brd_colors[3], $this->spot_colors
);
16694 if (isset($brd_colors[1])) {
16695 $dom[$key]['border']['R']['color'] = TCPDF_COLORS
::convertHTMLColorToDec($brd_colors[1], $this->spot_colors
);
16697 if (isset($brd_colors[0])) {
16698 $dom[$key]['border']['T']['color'] = TCPDF_COLORS
::convertHTMLColorToDec($brd_colors[0], $this->spot_colors
);
16700 if (isset($brd_colors[2])) {
16701 $dom[$key]['border']['B']['color'] = TCPDF_COLORS
::convertHTMLColorToDec($brd_colors[2], $this->spot_colors
);
16704 if (isset($dom[$key]['style']['border-width'])) {
16705 $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
16706 if (isset($brd_widths[3])) {
16707 $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
16709 if (isset($brd_widths[1])) {
16710 $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
16712 if (isset($brd_widths[0])) {
16713 $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
16715 if (isset($brd_widths[2])) {
16716 $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
16719 if (isset($dom[$key]['style']['border-style'])) {
16720 $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
16721 if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) {
16722 $dom[$key]['border']['L']['cap'] = 'square';
16723 $dom[$key]['border']['L']['join'] = 'miter';
16724 $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
16725 if ($dom[$key]['border']['L']['dash'] < 0) {
16726 $dom[$key]['border']['L'] = array();
16729 if (isset($brd_styles[1])) {
16730 $dom[$key]['border']['R']['cap'] = 'square';
16731 $dom[$key]['border']['R']['join'] = 'miter';
16732 $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
16733 if ($dom[$key]['border']['R']['dash'] < 0) {
16734 $dom[$key]['border']['R'] = array();
16737 if (isset($brd_styles[0])) {
16738 $dom[$key]['border']['T']['cap'] = 'square';
16739 $dom[$key]['border']['T']['join'] = 'miter';
16740 $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
16741 if ($dom[$key]['border']['T']['dash'] < 0) {
16742 $dom[$key]['border']['T'] = array();
16745 if (isset($brd_styles[2])) {
16746 $dom[$key]['border']['B']['cap'] = 'square';
16747 $dom[$key]['border']['B']['join'] = 'miter';
16748 $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
16749 if ($dom[$key]['border']['B']['dash'] < 0) {
16750 $dom[$key]['border']['B'] = array();
16754 $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
16755 foreach ($cellside as $bsk => $bsv) {
16756 if (isset($dom[$key]['style']['border-'.$bsv])) {
16757 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
16758 if (!empty($borderstyle)) {
16759 $dom[$key]['border'][$bsk] = $borderstyle;
16762 if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
16763 $dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors
);
16765 if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
16766 $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
16768 if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
16769 $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
16770 if ($dom[$key]['border'][$bsk]['dash'] < 0) {
16771 $dom[$key]['border'][$bsk] = array();
16775 // check for CSS padding properties
16776 if (isset($dom[$key]['style']['padding'])) {
16777 $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
16779 $dom[$key]['padding'] = $this->cell_padding
;
16781 foreach ($cellside as $psk => $psv) {
16782 if (isset($dom[$key]['style']['padding-'.$psv])) {
16783 $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
16786 // check for CSS margin properties
16787 if (isset($dom[$key]['style']['margin'])) {
16788 $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
16790 $dom[$key]['margin'] = $this->cell_margin
;
16792 foreach ($cellside as $psk => $psv) {
16793 if (isset($dom[$key]['style']['margin-'.$psv])) {
16794 $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
16797 // check for CSS border-spacing properties
16798 if (isset($dom[$key]['style']['border-spacing'])) {
16799 $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
16801 // page-break-inside
16802 if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
16803 $dom[$key]['attribute']['nobr'] = 'true';
16805 // page-break-before
16806 if (isset($dom[$key]['style']['page-break-before'])) {
16807 if ($dom[$key]['style']['page-break-before'] == 'always') {
16808 $dom[$key]['attribute']['pagebreak'] = 'true';
16809 } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
16810 $dom[$key]['attribute']['pagebreak'] = 'left';
16811 } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
16812 $dom[$key]['attribute']['pagebreak'] = 'right';
16815 // page-break-after
16816 if (isset($dom[$key]['style']['page-break-after'])) {
16817 if ($dom[$key]['style']['page-break-after'] == 'always') {
16818 $dom[$key]['attribute']['pagebreakafter'] = 'true';
16819 } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
16820 $dom[$key]['attribute']['pagebreakafter'] = 'left';
16821 } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
16822 $dom[$key]['attribute']['pagebreakafter'] = 'right';
16826 if (isset($dom[$key]['attribute']['display'])) {
16827 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
16829 if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
16830 $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
16831 if (!empty($borderstyle)) {
16832 $dom[$key]['border']['LTRB'] = $borderstyle;
16835 // check for font tag
16836 if ($dom[$key]['value'] == 'font') {
16838 if (isset($dom[$key]['attribute']['face'])) {
16839 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
16842 if (isset($dom[$key]['attribute']['size'])) {
16844 if ($dom[$key]['attribute']['size'][0] == '+') {
16845 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] +
intval(substr($dom[$key]['attribute']['size'], 1));
16846 } elseif ($dom[$key]['attribute']['size'][0] == '-') {
16847 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
16849 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16852 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16856 // force natural alignment for lists
16857 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
16858 AND (!isset($dom[$key]['align']) OR TCPDF_STATIC
::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
16860 $dom[$key]['align'] = 'R';
16862 $dom[$key]['align'] = 'L';
16865 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
16866 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16867 $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO
;
16870 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
16871 $dom[$key]['fontstyle'] .= 'B';
16873 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
16874 $dom[$key]['fontstyle'] .= 'I';
16876 if ($dom[$key]['value'] == 'u') {
16877 $dom[$key]['fontstyle'] .= 'U';
16879 if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
16880 $dom[$key]['fontstyle'] .= 'D';
16882 if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
16883 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle
;
16885 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
16886 $dom[$key]['fontname'] = $this->default_monospaced_font
;
16888 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)) {
16889 // headings h1, h2, h3, h4, h5, h6
16890 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16891 $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
16892 $dom[$key]['fontsize'] = $dom[0]['fontsize'] +
$headsize;
16894 if (!isset($dom[$key]['style']['font-weight'])) {
16895 $dom[$key]['fontstyle'] .= 'B';
16898 if (($dom[$key]['value'] == 'table')) {
16899 $dom[$key]['rows'] = 0; // number of rows
16900 $dom[$key]['trids'] = array(); // IDs of TR elements
16901 $dom[$key]['thead'] = ''; // table header rows
16903 if (($dom[$key]['value'] == 'tr')) {
16904 $dom[$key]['cols'] = 0;
16906 $dom[$key]['thead'] = true;
16907 // rows on thead block are printed as a separate table
16909 $dom[$key]['thead'] = false;
16910 // store the number of rows on table element
16911 ++
$dom[($dom[$key]['parent'])]['rows'];
16912 // store the TR elements IDs on table element
16913 array_push($dom[($dom[$key]['parent'])]['trids'], $key);
16916 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
16917 if (isset($dom[$key]['attribute']['colspan'])) {
16918 $colspan = intval($dom[$key]['attribute']['colspan']);
16922 $dom[$key]['attribute']['colspan'] = $colspan;
16923 $dom[($dom[$key]['parent'])]['cols'] +
= $colspan;
16926 if (isset($dom[$key]['attribute']['dir'])) {
16927 $dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
16929 // set foreground color attribute
16930 if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['attribute']['color']))) {
16931 $dom[$key]['fgcolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors
);
16932 } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
16933 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray
;
16935 // set background color attribute
16936 if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['attribute']['bgcolor']))) {
16937 $dom[$key]['bgcolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors
);
16939 // set stroke color attribute
16940 if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['attribute']['strokecolor']))) {
16941 $dom[$key]['strokecolor'] = TCPDF_COLORS
::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors
);
16943 // check for width attribute
16944 if (isset($dom[$key]['attribute']['width'])) {
16945 $dom[$key]['width'] = $dom[$key]['attribute']['width'];
16947 // check for height attribute
16948 if (isset($dom[$key]['attribute']['height'])) {
16949 $dom[$key]['height'] = $dom[$key]['attribute']['height'];
16951 // check for text alignment
16952 if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC
::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
16953 $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align'][0]);
16955 // check for text rendering mode (the following attributes do not exist in HTML)
16956 if (isset($dom[$key]['attribute']['stroke'])) {
16957 // font stroke width
16958 $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
16960 if (isset($dom[$key]['attribute']['fill'])) {
16962 if ($dom[$key]['attribute']['fill'] == 'true') {
16963 $dom[$key]['fill'] = true;
16965 $dom[$key]['fill'] = false;
16968 if (isset($dom[$key]['attribute']['clip'])) {
16970 if ($dom[$key]['attribute']['clip'] == 'true') {
16971 $dom[$key]['clip'] = true;
16973 $dom[$key]['clip'] = false;
16976 } // end opening tag
16979 $dom[$key]['tag'] = false;
16980 $dom[$key]['block'] = false;
16981 $dom[$key]['parent'] = end($level);
16982 $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
16983 if (!empty($dom[$dom[$key]['parent']]['text-transform'])) {
16984 // text-transform for unicode requires mb_convert_case (Multibyte String Functions)
16985 if (function_exists('mb_convert_case')) {
16986 $ttm = array('capitalize' => MB_CASE_TITLE
, 'uppercase' => MB_CASE_UPPER
, 'lowercase' => MB_CASE_LOWER
);
16987 if (isset($ttm[$dom[$dom[$key]['parent']]['text-transform']])) {
16988 $element = mb_convert_case($element, $ttm[$dom[$dom[$key]['parent']]['text-transform']], $this->encoding
);
16990 } elseif (!$this->isunicode
) {
16991 switch ($dom[$dom[$key]['parent']]['text-transform']) {
16992 case 'capitalize': {
16993 $element = ucwords(strtolower($element));
16996 case 'uppercase': {
16997 $element = strtoupper($element);
17000 case 'lowercase': {
17001 $element = strtolower($element);
17007 $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
17016 * Returns the string used to find spaces
17019 * @author Nicola Asuni
17020 * @since 4.8.024 (2010-01-15)
17022 protected function getSpaceString() {
17023 $spacestr = chr(32);
17024 if ($this->isUnicodeFont()) {
17025 $spacestr = chr(0).chr(32);
17031 * Serialize an array of parameters to be used with TCPDF tag in HTML code.
17032 * @param $pararray (array) parameters array
17033 * @return sting containing serialized data
17034 * @since 4.9.006 (2010-04-02)
17038 public function serializeTCPDFtagParameters($pararray) {
17039 return TCPDF_STATIC
::serializeTCPDFtagParameters($pararray);
17043 * Prints a cell (rectangular area) with optional borders, background color and html text string.
17044 * 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 />
17045 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
17046 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
17047 * 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
17048 * NOTE: all the HTML attributes must be enclosed in double-quote.
17049 * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
17050 * @param $h (float) Cell minimum height. The cell extends automatically if needed.
17051 * @param $x (float) upper-left corner X coordinate
17052 * @param $y (float) upper-left corner Y coordinate
17053 * @param $html (string) html text to print. Default value: empty string.
17054 * @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)))
17055 * @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>
17056 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
17057 * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
17058 * @param $reseth (boolean) if true reset the last cell height (default true).
17059 * @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>
17060 * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
17061 * @see Multicell(), writeHTML()
17064 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
17065 return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
17069 * Allows to preserve some HTML formatting (limited support).<br />
17070 * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
17071 * 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
17072 * NOTE: all the HTML attributes must be enclosed in double-quote.
17073 * @param $html (string) text to display
17074 * @param $ln (boolean) if true add a new line after text (default = true)
17075 * @param $fill (boolean) Indicates if the background must be painted (true) or transparent (false).
17076 * @param $reseth (boolean) if true reset the last cell height (default false).
17077 * @param $cell (boolean) if true add the current left (or right for RTL) padding to each Write (default false).
17078 * @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>
17081 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
17082 $gvars = $this->getGraphicVars();
17083 // store current values
17084 $prev_cell_margin = $this->cell_margin
;
17085 $prev_cell_padding = $this->cell_padding
;
17086 $prevPage = $this->page
;
17087 $prevlMargin = $this->lMargin
;
17088 $prevrMargin = $this->rMargin
;
17089 $curfontname = $this->FontFamily
;
17090 $curfontstyle = $this->FontStyle
;
17091 $curfontsize = $this->FontSizePt
;
17092 $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
17093 $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
17094 $curfontstretcing = $this->font_stretching
;
17095 $curfonttracking = $this->font_spacing
;
17096 $this->newline
= true;
17098 $startlinepage = $this->page
;
17099 $minstartliney = $this->y
;
17100 $maxbottomliney = 0;
17101 $startlinex = $this->x
;
17102 $startliney = $this->y
;
17106 $this_method_vars = array();
17108 $fontaligned = false;
17109 $reverse_dir = false; // true when the text direction is reversed
17110 $this->premode
= false;
17111 if ($this->inxobj
) {
17112 // we are inside an XObject template
17113 $pask = count($this->xobjects
[$this->xobjid
]['annotations']);
17114 } elseif (isset($this->PageAnnots
[$this->page
])) {
17115 $pask = count($this->PageAnnots
[$this->page
]);
17119 if ($this->inxobj
) {
17120 // we are inside an XObject template
17121 $startlinepos = strlen($this->xobjects
[$this->xobjid
]['outdata']);
17122 } elseif (!$this->InFooter
) {
17123 if (isset($this->footerlen
[$this->page
])) {
17124 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
] - $this->footerlen
[$this->page
];
17126 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
];
17128 $startlinepos = $this->footerpos
[$this->page
];
17130 // we are inside the footer
17131 $startlinepos = $this->pagelen
[$this->page
];
17136 $w = $this->x
- $this->lMargin
;
17138 $w = $this->w
- $this->rMargin
- $this->x
;
17140 $w -= ($this->cell_padding
['L'] +
$this->cell_padding
['R']);
17143 $this->x
-= $this->cell_padding
['R'];
17144 $this->lMargin +
= $this->cell_padding
['R'];
17146 $this->x +
= $this->cell_padding
['L'];
17147 $this->rMargin +
= $this->cell_padding
['L'];
17150 if ($this->customlistindent
>= 0) {
17151 $this->listindent
= $this->customlistindent
;
17153 $this->listindent
= $this->GetStringWidth('000000');
17155 $this->listindentlevel
= 0;
17156 // save previous states
17157 $prev_cell_height_ratio = $this->cell_height_ratio
;
17158 $prev_listnum = $this->listnum
;
17159 $prev_listordered = $this->listordered
;
17160 $prev_listcount = $this->listcount
;
17161 $prev_lispacer = $this->lispacer
;
17162 $this->listnum
= 0;
17163 $this->listordered
= array();
17164 $this->listcount
= array();
17165 $this->lispacer
= '';
17166 if ((TCPDF_STATIC
::empty_string($this->lasth
)) OR ($reseth)) {
17167 // reset row height
17168 $this->resetLastH();
17170 $dom = $this->getHtmlDomArray($html);
17171 $maxel = count($dom);
17173 while ($key < $maxel) {
17174 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND $dom[$key]['hide']) {
17175 // store the node key
17176 $hidden_node_key = $key;
17177 if ($dom[$key]['self']) {
17178 // skip just this self-closing tag
17181 // skip this and all children tags
17182 while (($key < $maxel) AND (!$dom[$key]['tag'] OR $dom[$key]['opening'] OR ($dom[$key]['parent'] != $hidden_node_key))) {
17183 // skip hidden objects
17189 if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
17190 // check for pagebreak
17191 if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
17192 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17193 $this->checkPageBreak($this->PageBreakTrigger +
1);
17194 $this->htmlvspace
= ($this->PageBreakTrigger +
1);
17196 if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl
) AND (($this->page %
2) == 0)) OR (($this->rtl
) AND (($this->page %
2) != 0))))
17197 OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl
) AND (($this->page %
2) != 0)) OR (($this->rtl
) AND (($this->page %
2) == 0))))) {
17198 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17199 $this->checkPageBreak($this->PageBreakTrigger +
1);
17200 $this->htmlvspace
= ($this->PageBreakTrigger +
1);
17203 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
17204 if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
17205 $dom[$key]['attribute']['nobr'] = false;
17207 // store current object
17208 $this->startTransaction();
17209 // save this method vars
17210 $this_method_vars['html'] = $html;
17211 $this_method_vars['ln'] = $ln;
17212 $this_method_vars['fill'] = $fill;
17213 $this_method_vars['reseth'] = $reseth;
17214 $this_method_vars['cell'] = $cell;
17215 $this_method_vars['align'] = $align;
17216 $this_method_vars['gvars'] = $gvars;
17217 $this_method_vars['prevPage'] = $prevPage;
17218 $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
17219 $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
17220 $this_method_vars['prevlMargin'] = $prevlMargin;
17221 $this_method_vars['prevrMargin'] = $prevrMargin;
17222 $this_method_vars['curfontname'] = $curfontname;
17223 $this_method_vars['curfontstyle'] = $curfontstyle;
17224 $this_method_vars['curfontsize'] = $curfontsize;
17225 $this_method_vars['curfontascent'] = $curfontascent;
17226 $this_method_vars['curfontdescent'] = $curfontdescent;
17227 $this_method_vars['curfontstretcing'] = $curfontstretcing;
17228 $this_method_vars['curfonttracking'] = $curfonttracking;
17229 $this_method_vars['minstartliney'] = $minstartliney;
17230 $this_method_vars['maxbottomliney'] = $maxbottomliney;
17231 $this_method_vars['yshift'] = $yshift;
17232 $this_method_vars['startlinepage'] = $startlinepage;
17233 $this_method_vars['startlinepos'] = $startlinepos;
17234 $this_method_vars['startlinex'] = $startlinex;
17235 $this_method_vars['startliney'] = $startliney;
17236 $this_method_vars['newline'] = $newline;
17237 $this_method_vars['loop'] = $loop;
17238 $this_method_vars['curpos'] = $curpos;
17239 $this_method_vars['pask'] = $pask;
17240 $this_method_vars['lalign'] = $lalign;
17241 $this_method_vars['plalign'] = $plalign;
17242 $this_method_vars['w'] = $w;
17243 $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
17244 $this_method_vars['prev_listnum'] = $prev_listnum;
17245 $this_method_vars['prev_listordered'] = $prev_listordered;
17246 $this_method_vars['prev_listcount'] = $prev_listcount;
17247 $this_method_vars['prev_lispacer'] = $prev_lispacer;
17248 $this_method_vars['fontaligned'] = $fontaligned;
17249 $this_method_vars['key'] = $key;
17250 $this_method_vars['dom'] = $dom;
17253 // print THEAD block
17254 if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
17255 if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC
::empty_string($dom[$dom[$key]['parent']]['thead'])) {
17256 $this->inthead
= true;
17257 // print table header (thead)
17258 $this->writeHTML($this->thead
, false, false, false, false, '');
17259 // check if we are on a new page or on a new column
17260 if (($this->y
< $this->start_transaction_y
) OR ($this->checkPageBreak($this->lasth
, '', false))) {
17261 // we are on a new page or on a new column and the total object height is less than the available vertical space.
17262 // restore previous object
17263 $this->rollbackTransaction(true);
17264 // restore previous values
17265 foreach ($this_method_vars as $vkey => $vval) {
17268 // disable table header
17269 $tmp_thead = $this->thead
;
17271 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17273 if ((!$this->checkPageBreak($this->PageBreakTrigger +
1)) AND ($this->y
< $pre_y)) {
17274 // fix for multicolumn mode
17275 $startliney = $this->y
;
17277 $this->start_transaction_page
= $this->page
;
17278 $this->start_transaction_y
= $this->y
;
17279 // restore table header
17280 $this->thead
= $tmp_thead;
17281 // fix table border properties
17282 if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
17283 $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
17284 } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
17285 $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
17287 $tmp_cellspacing = 0;
17289 $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page
;
17290 $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column
;
17291 $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y +
$tmp_cellspacing;
17292 $xoffset = ($this->x
- $dom[$dom[$key]['parent']]['borderposition']['x']);
17293 $dom[$dom[$key]['parent']]['borderposition']['x'] +
= $xoffset;
17294 $dom[$dom[$key]['parent']]['borderposition']['xmax'] +
= $xoffset;
17295 // print table header (thead)
17296 $this->writeHTML($this->thead
, false, false, false, false, '');
17299 // move $key index forward to skip THEAD block
17300 while ( ($key < $maxel) AND (!(
17301 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
17302 OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
17306 if ($dom[$key]['tag'] OR ($key == 0)) {
17307 if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
17308 $dom[$key]['align'] = ($this->rtl
) ?
'R' : 'L';
17310 // vertically align image in line
17311 if ((!$this->newline
) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
17312 // get image height
17313 $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], ($dom[$key]['fontsize'] / $this->k
), 'px');
17314 $autolinebreak = false;
17315 if (!empty($dom[$key]['width'])) {
17316 $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], ($dom[$key]['fontsize'] / $this->k
), 'px', false);
17317 if (($imgw <= ($this->w
- $this->lMargin
- $this->rMargin
- $this->cell_padding
['L'] - $this->cell_padding
['R']))
17318 AND ((($this->rtl
) AND (($this->x
- $imgw) < ($this->lMargin +
$this->cell_padding
['L'])))
17319 OR ((!$this->rtl
) AND (($this->x +
$imgw) > ($this->w
- $this->rMargin
- $this->cell_padding
['R']))))) {
17320 // add automatic line break
17321 $autolinebreak = true;
17322 $this->Ln('', $cell);
17323 if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
17324 // go back to evaluate this line break
17329 if (!$autolinebreak) {
17330 if ($this->inPageBody()) {
17332 // check for page break
17333 if ((!$this->checkPageBreak($imgh)) AND ($this->y
< $pre_y)) {
17334 // fix for multicolumn mode
17335 $startliney = $this->y
;
17338 if ($this->page
> $startlinepage) {
17339 // fix line splitted over two pages
17340 if (isset($this->footerlen
[$startlinepage])) {
17341 $curpos = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
17343 // line to be moved one page forward
17344 $pagebuff = $this->getPageBuffer($startlinepage);
17345 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17346 $tstart = substr($pagebuff, 0, $startlinepos);
17347 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17348 // remove line from previous page
17349 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17350 $pagebuff = $this->getPageBuffer($this->page
);
17351 $tstart = substr($pagebuff, 0, $this->cntmrk
[$this->page
]);
17352 $tend = substr($pagebuff, $this->cntmrk
[$this->page
]);
17353 // add line start to current page
17354 $yshift = ($minstartliney - $this->y
);
17355 if ($fontaligned) {
17356 $yshift +
= ($curfontsize / $this->k
);
17358 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k
));
17359 $this->setPageBuffer($this->page
, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17360 // shift the annotations and links
17361 if (isset($this->PageAnnots
[$this->page
])) {
17362 $next_pask = count($this->PageAnnots
[$this->page
]);
17366 if (isset($this->PageAnnots
[$startlinepage])) {
17367 foreach ($this->PageAnnots
[$startlinepage] as $pak => $pac) {
17368 if ($pak >= $pask) {
17369 $this->PageAnnots
[$this->page
][] = $pac;
17370 unset($this->PageAnnots
[$startlinepage][$pak]);
17371 $npak = count($this->PageAnnots
[$this->page
]) - 1;
17372 $this->PageAnnots
[$this->page
][$npak]['y'] -= $yshift;
17376 $pask = $next_pask;
17377 $startlinepos = $this->cntmrk
[$this->page
];
17378 $startlinepage = $this->page
;
17379 $startliney = $this->y
;
17380 $this->newline
= false;
17382 $this->y +
= ($this->getCellHeight($curfontsize / $this->k
) - ($curfontdescent * $this->cell_height_ratio
) - $imgh);
17383 $minstartliney = min($this->y
, $minstartliney);
17384 $maxbottomliney = ($startliney +
$this->getCellHeight($curfontsize / $this->k
));
17386 } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
17387 // account for different font size
17388 $pfontname = $curfontname;
17389 $pfontstyle = $curfontstyle;
17390 $pfontsize = $curfontsize;
17391 $fontname = (isset($dom[$key]['fontname']) ?
$dom[$key]['fontname'] : $curfontname);
17392 $fontstyle = (isset($dom[$key]['fontstyle']) ?
$dom[$key]['fontstyle'] : $curfontstyle);
17393 $fontsize = (isset($dom[$key]['fontsize']) ?
$dom[$key]['fontsize'] : $curfontsize);
17394 $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
17395 $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
17396 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
17397 OR ($this->cell_height_ratio
!= $dom[$key]['line-height'])
17398 OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
17399 if (($key < ($maxel - 1)) AND (
17400 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
17401 OR ($this->cell_height_ratio
!= $dom[$key]['line-height'])
17402 OR (!$this->newline
AND is_numeric($fontsize) AND is_numeric($curfontsize) AND ($fontsize >= 0) AND ($curfontsize >= 0) AND ($fontsize != $curfontsize))
17404 if ($this->page
> $startlinepage) {
17405 // fix lines splitted over two pages
17406 if (isset($this->footerlen
[$startlinepage])) {
17407 $curpos = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
17409 // line to be moved one page forward
17410 $pagebuff = $this->getPageBuffer($startlinepage);
17411 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17412 $tstart = substr($pagebuff, 0, $startlinepos);
17413 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17414 // remove line start from previous page
17415 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17416 $pagebuff = $this->getPageBuffer($this->page
);
17417 $tstart = substr($pagebuff, 0, $this->cntmrk
[$this->page
]);
17418 $tend = substr($pagebuff, $this->cntmrk
[$this->page
]);
17419 // add line start to current page
17420 $yshift = ($minstartliney - $this->y
);
17421 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k
));
17422 $this->setPageBuffer($this->page
, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17423 // shift the annotations and links
17424 if (isset($this->PageAnnots
[$this->page
])) {
17425 $next_pask = count($this->PageAnnots
[$this->page
]);
17429 if (isset($this->PageAnnots
[$startlinepage])) {
17430 foreach ($this->PageAnnots
[$startlinepage] as $pak => $pac) {
17431 if ($pak >= $pask) {
17432 $this->PageAnnots
[$this->page
][] = $pac;
17433 unset($this->PageAnnots
[$startlinepage][$pak]);
17434 $npak = count($this->PageAnnots
[$this->page
]) - 1;
17435 $this->PageAnnots
[$this->page
][$npak]['y'] -= $yshift;
17439 $pask = $next_pask;
17440 $startlinepos = $this->cntmrk
[$this->page
];
17441 $startlinepage = $this->page
;
17442 $startliney = $this->y
;
17444 if (!isset($dom[$key]['line-height'])) {
17445 $dom[$key]['line-height'] = $this->cell_height_ratio
;
17447 if (!$dom[$key]['block']) {
17448 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']))) {
17449 $this->y +
= (((($curfontsize * $this->cell_height_ratio
) - ($fontsize * $dom[$key]['line-height'])) / $this->k
) +
$curfontascent - $fontascent - $curfontdescent +
$fontdescent) / 2;
17451 if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
17452 $current_line_align_data = array($key, $minstartliney, $maxbottomliney);
17453 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)))) {
17454 $minstartliney = min($this->y
, $line_align_data[1]);
17455 $maxbottomliney = max(($this->y +
$this->getCellHeight($fontsize / $this->k
)), $line_align_data[2]);
17457 $minstartliney = min($this->y
, $minstartliney);
17458 $maxbottomliney = max(($this->y +
$this->getCellHeight($fontsize / $this->k
)), $maxbottomliney);
17460 $line_align_data = $current_line_align_data;
17463 $this->cell_height_ratio
= $dom[$key]['line-height'];
17464 $fontaligned = true;
17466 $this->SetFont($fontname, $fontstyle, $fontsize);
17467 // reset row height
17468 $this->resetLastH();
17469 $curfontname = $fontname;
17470 $curfontstyle = $fontstyle;
17471 $curfontsize = $fontsize;
17472 $curfontascent = $fontascent;
17473 $curfontdescent = $fontdescent;
17476 // set text rendering mode
17477 $textstroke = isset($dom[$key]['stroke']) ?
$dom[$key]['stroke'] : $this->textstrokewidth
;
17478 $textfill = isset($dom[$key]['fill']) ?
$dom[$key]['fill'] : (($this->textrendermode %
2) == 0);
17479 $textclip = isset($dom[$key]['clip']) ?
$dom[$key]['clip'] : ($this->textrendermode
> 3);
17480 $this->setTextRenderingMode($textstroke, $textfill, $textclip);
17481 if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
17482 $this->setFontStretching($dom[$key]['font-stretch']);
17484 if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
17485 $this->setFontSpacing($dom[$key]['letter-spacing']);
17487 if (($plalign == 'J') AND $dom[$key]['block']) {
17490 // get current position on page buffer
17491 $curpos = $this->pagelen
[$startlinepage];
17492 if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
17493 $this->SetFillColorArray($dom[$key]['bgcolor']);
17496 $wfill = $fill |
false;
17498 if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
17499 $this->SetTextColorArray($dom[$key]['fgcolor']);
17501 if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
17502 $this->SetDrawColorArray($dom[$key]['strokecolor']);
17504 if (isset($dom[$key]['align'])) {
17505 $lalign = $dom[$key]['align'];
17507 if (TCPDF_STATIC
::empty_string($lalign)) {
17512 if ($this->newline
AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
17514 $fontaligned = false;
17515 // we are at the beginning of a new line
17516 if (isset($startlinex)) {
17517 $yshift = ($minstartliney - $startliney);
17518 if (($yshift > 0) OR ($this->page
> $startlinepage)) {
17522 // the last line must be shifted to be aligned as requested
17523 $linew = abs($this->endlinex
- $startlinex);
17524 if ($this->inxobj
) {
17525 // we are inside an XObject template
17526 $pstart = substr($this->xobjects
[$this->xobjid
]['outdata'], 0, $startlinepos);
17527 if (isset($opentagpos)) {
17528 $midpos = $opentagpos;
17533 $pmid = substr($this->xobjects
[$this->xobjid
]['outdata'], $startlinepos, ($midpos - $startlinepos));
17534 $pend = substr($this->xobjects
[$this->xobjid
]['outdata'], $midpos);
17536 $pmid = substr($this->xobjects
[$this->xobjid
]['outdata'], $startlinepos);
17540 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
17541 if (isset($opentagpos) AND isset($this->footerlen
[$startlinepage]) AND (!$this->InFooter
)) {
17542 $this->footerpos
[$startlinepage] = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
17543 $midpos = min($opentagpos, $this->footerpos
[$startlinepage]);
17544 } elseif (isset($opentagpos)) {
17545 $midpos = $opentagpos;
17546 } elseif (isset($this->footerlen
[$startlinepage]) AND (!$this->InFooter
)) {
17547 $this->footerpos
[$startlinepage] = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
17548 $midpos = $this->footerpos
[$startlinepage];
17553 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
17554 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
17556 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
17560 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl
)) OR (($plalign == 'L') AND ($this->rtl
)))))) {
17561 // calculate shifting amount
17563 if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns
> 1)) {
17564 $tw +
= $this->cell_padding
['R'];
17566 if ($this->lMargin
!= $prevlMargin) {
17567 $tw +
= ($prevlMargin - $this->lMargin
);
17569 if ($this->rMargin
!= $prevrMargin) {
17570 $tw +
= ($prevrMargin - $this->rMargin
);
17572 $one_space_width = $this->GetStringWidth(chr(32));
17573 $no = 0; // number of spaces on a line contained on a single block
17574 if ($this->isRTLTextDir()) { // RTL
17575 // remove left space if exist
17576 $pos1 = TCPDF_STATIC
::revstrpos($pmid, '[(');
17578 $pos1 = intval($pos1);
17579 if ($this->isUnicodeFont()) {
17580 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, '[('.chr(0).chr(32)));
17583 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, '[('.chr(32)));
17586 if ($pos1 == $pos2) {
17587 $pmid = substr($pmid, 0, ($pos1 +
2)).substr($pmid, ($pos1 +
2 +
$spacelen));
17588 if (substr($pmid, $pos1, 4) == '[()]') {
17589 $linew -= $one_space_width;
17590 } elseif ($pos1 == strpos($pmid, '[(')) {
17596 // remove right space if exist
17597 $pos1 = TCPDF_STATIC
::revstrpos($pmid, ')]');
17599 $pos1 = intval($pos1);
17600 if ($this->isUnicodeFont()) {
17601 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, chr(0).chr(32).')]')) +
2;
17604 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, chr(32).')]')) +
1;
17607 if ($pos1 == $pos2) {
17608 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
17609 $linew -= $one_space_width;
17613 $mdiff = ($tw - $linew);
17614 if ($plalign == 'C') {
17616 $t_x = -($mdiff / 2);
17618 $t_x = ($mdiff / 2);
17620 } elseif ($plalign == 'R') {
17621 // right alignment on LTR document
17623 } elseif ($plalign == 'L') {
17624 // left alignment on RTL document
17626 } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
17628 if ($this->isRTLTextDir()) {
17629 // align text on the left
17632 $ns = 0; // number of spaces
17634 // escape special characters
17635 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
17636 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
17638 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER
)) {
17639 $spacestr = $this->getSpaceString();
17640 $maxkk = count($lnstring[1]) - 1;
17641 for ($kk=0; $kk <= $maxkk; ++
$kk) {
17642 // restore special characters
17643 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
17644 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
17645 // store number of spaces on the strings
17646 $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
17647 // count total spaces on line
17648 $ns +
= $lnstring[2][$kk];
17649 $lnstring[3][$kk] = $ns;
17654 // calculate additional space to add to each existing space
17655 $spacewidth = ($mdiff / ($ns - $no)) * $this->k
;
17656 if ($this->FontSize
<= 0) {
17657 $this->FontSize
= 1;
17659 $spacewidthu = -1000 * ($mdiff +
(($ns +
$no) * $one_space_width)) / $ns / $this->FontSize
;
17660 if ($this->font_spacing
!= 0) {
17661 // fixed spacing mode
17662 $osw = -1000 * $this->font_spacing
/ $this->FontSize
;
17663 $spacewidthu +
= $osw;
17670 $prev_epsposbeg = 0;
17672 if ($this->isRTLTextDir()) {
17673 $textpos = $this->wPt
;
17675 while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE
, $offset) == 1) {
17676 // check if we are inside a string section '[( ... )]'
17677 $stroffset = strpos($pmid, '[(', $offset);
17678 if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
17679 // set offset to the end of string section
17680 $offset = strpos($pmid, ')]', $stroffset);
17681 while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
17682 $offset = strpos($pmid, ')]', ($offset +
1));
17684 if ($offset === false) {
17685 $this->Error('HTML Justification: malformed PDF code.');
17689 if ($this->isRTLTextDir()) {
17690 $spacew = ($spacewidth * ($nsmax - $ns));
17692 $spacew = ($spacewidth * $ns);
17694 $offset = $strpiece[2][1] +
strlen($strpiece[2][0]);
17695 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker
, $offset);
17696 $epsposend = strpos($pmid, $this->epsmarker
.'Q', $offset) +
strlen($this->epsmarker
.'Q');
17697 if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
17698 OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
17699 // shift EPS images
17700 $trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
17701 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker
, ($prev_epsposbeg - 6));
17702 $pmid_b = substr($pmid, 0, $epsposbeg);
17703 $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
17704 $pmid_e = substr($pmid, $epsposend);
17705 $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
17706 $offset = $epsposend;
17710 $prev_epsposbeg = $epsposbeg;
17712 // shift blocks of code
17713 switch ($strpiece[2][0]) {
17718 // get current X position
17719 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
17720 if (!isset($xmatches[1])) {
17723 $currentxpos = $xmatches[1];
17724 $textpos = $currentxpos;
17725 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
17726 $ns = $lnstring[3][$strcount];
17727 if ($this->isRTLTextDir()) {
17728 $spacew = ($spacewidth * ($nsmax - $ns));
17733 if (preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $pmatch) == 1) {
17734 $newpmid = sprintf('%F',(floatval($pmatch[1]) +
$spacew)).' '.$pmatch[2].' x*#!#*x'.$pmatch[3].$pmatch[4];
17735 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17736 unset($pmatch, $newpmid);
17742 if (!TCPDF_STATIC
::empty_string($this->lispacer
)) {
17743 $this->lispacer
= '';
17746 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
17747 if (!isset($xmatches[1])) {
17750 $currentxpos = $xmatches[1];
17753 if ($this->isRTLTextDir()) { // RTL
17754 if ($currentxpos < $textpos) {
17755 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
17756 $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17758 if ($strcount > 0) {
17759 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
17760 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17764 if ($currentxpos > $textpos) {
17765 if ($strcount > 0) {
17766 $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
17768 $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17770 if ($strcount > 1) {
17771 $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
17773 if ($strcount > 0) {
17774 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17778 if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $pmatch) == 1) {
17779 $newx = sprintf('%F',(floatval($pmatch[1]) +
$x_diff));
17780 $neww = sprintf('%F',(floatval($pmatch[3]) +
$w_diff));
17781 $newpmid = $newx.' '.$pmatch[2].' '.$neww.' '.$pmatch[4].' x*#!#*x'.$pmatch[5].$pmatch[6];
17782 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17783 unset($pmatch, $newpmid, $newx, $neww);
17788 // get current X position
17789 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);
17790 if (!isset($xmatches[1])) {
17793 $currentxpos = $xmatches[1];
17795 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) {
17796 $newx1 = sprintf('%F',(floatval($pmatch[1]) +
$spacew));
17797 $newx2 = sprintf('%F',(floatval($pmatch[3]) +
$spacew));
17798 $newx3 = sprintf('%F',(floatval($pmatch[5]) +
$spacew));
17799 $newpmid = $newx1.' '.$pmatch[2].' '.$newx2.' '.$pmatch[4].' '.$newx3.' '.$pmatch[6].' x*#!#*x'.$pmatch[7].$pmatch[8];
17800 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17801 unset($pmatch, $newpmid, $newx1, $newx2, $newx3);
17806 // shift the annotations and links
17807 $cxpos = ($currentxpos / $this->k
);
17808 $lmpos = ($this->lMargin +
$this->cell_padding
['L'] +
$this->feps
);
17809 if ($this->inxobj
) {
17810 // we are inside an XObject template
17811 foreach ($this->xobjects
[$this->xobjid
]['annotations'] as $pak => $pac) {
17812 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k
) >= ($currentxpos - $this->feps
)) AND (($pac['x'] * $this->k
) <= ($currentxpos +
$this->feps
))) {
17813 if ($cxpos > $lmpos) {
17814 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['x'] +
= ($spacew / $this->k
);
17815 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['w'] +
= (($spacewidth * $pac['numspaces']) / $this->k
);
17817 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['w'] +
= (($spacewidth * $pac['numspaces']) / $this->k
);
17822 } elseif (isset($this->PageAnnots
[$this->page
])) {
17823 foreach ($this->PageAnnots
[$this->page
] as $pak => $pac) {
17824 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k
) >= ($currentxpos - $this->feps
)) AND (($pac['x'] * $this->k
) <= ($currentxpos +
$this->feps
))) {
17825 if ($cxpos > $lmpos) {
17826 $this->PageAnnots
[$this->page
][$pak]['x'] +
= ($spacew / $this->k
);
17827 $this->PageAnnots
[$this->page
][$pak]['w'] +
= (($spacewidth * $pac['numspaces']) / $this->k
);
17829 $this->PageAnnots
[$this->page
][$pak]['w'] +
= (($spacewidth * $pac['numspaces']) / $this->k
);
17837 $pmid = str_replace('x*#!#*x', '', $pmid);
17838 if ($this->isUnicodeFont()) {
17839 // multibyte characters
17840 $spacew = $spacewidthu;
17841 if ($this->font_stretching
!= 100) {
17842 // word spacing is affected by stretching
17843 $spacew /= ($this->font_stretching
/ 100);
17845 // escape special characters
17847 $pmid = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmid);
17848 $pmid = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmid);
17849 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmid, $pamatch) > 0) {
17850 foreach($pamatch[0] as $pk => $pmatch) {
17851 $replace = $pamatch[1][$pk];
17852 $replace = str_replace('#!#OP#!#', '(', $replace);
17853 $replace = str_replace('#!#CP#!#', ')', $replace);
17854 $newpmid = '[('.str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacew).' (', $replace).')]';
17855 $pos = strpos($pmid, $pmatch, $pos);
17856 if ($pos !== FALSE) {
17857 $pmid = substr_replace($pmid, $newpmid, $pos, strlen($pmatch));
17863 if ($this->inxobj
) {
17864 // we are inside an XObject template
17865 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart."\n".$pmid."\n".$pend;
17867 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
17869 $endlinepos = strlen($pstart."\n".$pmid."\n");
17871 // non-unicode (single-byte characters)
17872 if ($this->font_stretching
!= 100) {
17873 // word spacing (Tw) is affected by stretching
17874 $spacewidth /= ($this->font_stretching
/ 100);
17876 $rs = sprintf('%F Tw', $spacewidth);
17877 $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
17878 if ($this->inxobj
) {
17879 // we are inside an XObject template
17880 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
17882 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
17884 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
17888 } // end if $startlinex
17889 if (($t_x != 0) OR ($yshift < 0)) {
17891 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k
), ($yshift * $this->k
));
17892 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
17893 $endlinepos = strlen($pstart);
17894 if ($this->inxobj
) {
17895 // we are inside an XObject template
17896 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$pend;
17897 foreach ($this->xobjects
[$this->xobjid
]['annotations'] as $pak => $pac) {
17898 if ($pak >= $pask) {
17899 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['x'] +
= $t_x;
17900 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['y'] -= $yshift;
17904 $this->setPageBuffer($startlinepage, $pstart.$pend);
17905 // shift the annotations and links
17906 if (isset($this->PageAnnots
[$this->page
])) {
17907 foreach ($this->PageAnnots
[$this->page
] as $pak => $pac) {
17908 if ($pak >= $pask) {
17909 $this->PageAnnots
[$this->page
][$pak]['x'] +
= $t_x;
17910 $this->PageAnnots
[$this->page
][$pak]['y'] -= $yshift;
17915 $this->y
-= $yshift;
17918 $pbrk = $this->checkPageBreak($this->lasth
);
17919 $this->newline
= false;
17920 $startlinex = $this->x
;
17921 $startliney = $this->y
;
17922 if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
17923 $startliney -= ((0.3 * $this->FontSizePt
) / $this->k
);
17924 } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
17925 $startliney -= (($this->FontSizePt
/ 0.7) / $this->k
);
17927 $minstartliney = $startliney;
17928 $maxbottomliney = ($this->y +
$this->getCellHeight($fontsize / $this->k
));
17930 $startlinepage = $this->page
;
17931 if (isset($endlinepos) AND (!$pbrk)) {
17932 $startlinepos = $endlinepos;
17934 if ($this->inxobj
) {
17935 // we are inside an XObject template
17936 $startlinepos = strlen($this->xobjects
[$this->xobjid
]['outdata']);
17937 } elseif (!$this->InFooter
) {
17938 if (isset($this->footerlen
[$this->page
])) {
17939 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
] - $this->footerlen
[$this->page
];
17941 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
];
17943 $startlinepos = $this->footerpos
[$this->page
];
17945 $startlinepos = $this->pagelen
[$this->page
];
17948 unset($endlinepos);
17949 $plalign = $lalign;
17950 if (isset($this->PageAnnots
[$this->page
])) {
17951 $pask = count($this->PageAnnots
[$this->page
]);
17955 if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
17956 AND (isset($this->emptypagemrk
[$this->page
]))
17957 AND ($this->emptypagemrk
[$this->page
] == $this->pagelen
[$this->page
]))) {
17958 $this->SetFont($fontname, $fontstyle, $fontsize);
17960 $this->SetFillColorArray($this->bgcolor
);
17964 if (isset($opentagpos)) {
17965 unset($opentagpos);
17967 if ($dom[$key]['tag']) {
17968 if ($dom[$key]['opening']) {
17969 // get text indentation (if any)
17970 if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
17971 $this->textindent
= $dom[$key]['text-indent'];
17972 $this->newline
= true;
17975 if (($dom[$key]['value'] == 'table') AND isset($dom[$key]['cols']) AND ($dom[$key]['cols'] > 0)) {
17976 // available page width
17978 $wtmp = $this->x
- $this->lMargin
;
17980 $wtmp = $this->w
- $this->rMargin
- $this->x
;
17982 // get cell spacing
17983 if (isset($dom[$key]['attribute']['cellspacing'])) {
17984 $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
17985 $cellspacing = array('H' => $clsp, 'V' => $clsp);
17986 } elseif (isset($dom[$key]['border-spacing'])) {
17987 $cellspacing = $dom[$key]['border-spacing'];
17989 $cellspacing = array('H' => 0, 'V' => 0);
17992 if (isset($dom[$key]['width'])) {
17993 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
17995 $table_width = $wtmp;
17997 $table_width -= (2 * $cellspacing['H']);
17998 if (!$this->inthead
) {
17999 $this->y +
= $cellspacing['V'];
18002 $cellspacingx = -$cellspacing['H'];
18004 $cellspacingx = $cellspacing['H'];
18006 // total table width without cellspaces
18007 $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
18008 // minimum column width
18009 $table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
18010 // array of custom column widths
18011 $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
18014 if ($dom[$key]['value'] == 'tr') {
18015 // reset column counter
18019 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
18020 $trid = $dom[$key]['parent'];
18021 $table_el = $dom[$trid]['parent'];
18022 if (!isset($dom[$table_el]['cols'])) {
18023 $dom[$table_el]['cols'] = $dom[$trid]['cols'];
18025 // store border info
18027 if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
18028 $tdborder = $dom[$key]['border'];
18030 $colspan = intval($dom[$key]['attribute']['colspan']);
18031 if ($colspan <= 0) {
18034 $old_cell_padding = $this->cell_padding
;
18035 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
18036 $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
18037 $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
18038 } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
18039 $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
18041 $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
18043 $this->cell_padding
= $current_cell_padding;
18044 if (isset($dom[$key]['height'])) {
18045 // minimum cell height
18046 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
18050 if (isset($dom[$key]['content'])) {
18051 $cell_content = stripslashes($dom[$key]['content']);
18053 $cell_content = ' ';
18055 $tagtype = $dom[$key]['value'];
18057 while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
18058 // move $key index forward
18061 if (!isset($dom[$trid]['startpage'])) {
18062 $dom[$trid]['startpage'] = $this->page
;
18064 $this->setPage($dom[$trid]['startpage']);
18066 if (!isset($dom[$trid]['startcolumn'])) {
18067 $dom[$trid]['startcolumn'] = $this->current_column
;
18068 } elseif ($this->current_column
!= $dom[$trid]['startcolumn']) {
18070 $this->selectColumn($dom[$trid]['startcolumn']);
18073 if (!isset($dom[$trid]['starty'])) {
18074 $dom[$trid]['starty'] = $this->y
;
18076 $this->y
= $dom[$trid]['starty'];
18078 if (!isset($dom[$trid]['startx'])) {
18079 $dom[$trid]['startx'] = $this->x
;
18080 $this->x +
= $cellspacingx;
18082 $this->x +
= ($cellspacingx / 2);
18084 if (isset($dom[$parentid]['attribute']['rowspan'])) {
18085 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
18089 // skip row-spanned cells started on the previous rows
18090 if (isset($dom[$table_el]['rowspans'])) {
18092 $rskmax = count($dom[$table_el]['rowspans']);
18093 while ($rsk < $rskmax) {
18094 $trwsp = $dom[$table_el]['rowspans'][$rsk];
18095 $rsstartx = $trwsp['startx'];
18096 $rsendx = $trwsp['endx'];
18097 // account for margin changes
18098 if ($trwsp['startpage'] < $this->page
) {
18099 if (($this->rtl
) AND ($this->pagedim
[$this->page
]['orm'] != $this->pagedim
[$trwsp['startpage']]['orm'])) {
18100 $dl = ($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$trwsp['startpage']]['orm']);
18103 } elseif ((!$this->rtl
) AND ($this->pagedim
[$this->page
]['olm'] != $this->pagedim
[$trwsp['startpage']]['olm'])) {
18104 $dl = ($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$trwsp['startpage']]['olm']);
18109 if (($trwsp['rowspan'] > 0)
18110 AND ($rsstartx > ($this->x
- $cellspacing['H'] - $current_cell_padding['L'] - $this->feps
))
18111 AND ($rsstartx < ($this->x +
$cellspacing['H'] +
$current_cell_padding['R'] +
$this->feps
))
18112 AND (($trwsp['starty'] < ($this->y
- $this->feps
)) OR ($trwsp['startpage'] < $this->page
) OR ($trwsp['startcolumn'] < $this->current_column
))) {
18113 // set the starting X position of the current cell
18114 $this->x
= $rsendx +
$cellspacingx;
18115 // increment column indicator
18116 $colid +
= $trwsp['colspan'];
18117 if (($trwsp['rowspan'] == 1)
18118 AND (isset($dom[$trid]['endy']))
18119 AND (isset($dom[$trid]['endpage']))
18120 AND (isset($dom[$trid]['endcolumn']))
18121 AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
18122 AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18123 // set ending Y position for row
18124 $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18125 $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
18133 if (isset($dom[$parentid]['width'])) {
18134 // user specified width
18135 $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
18136 $tmpcw = ($cellw / $colspan);
18137 for ($i = 0; $i < $colspan; ++
$i) {
18138 $table_colwidths[($colid +
$i)] = $tmpcw;
18141 // inherit column width
18143 for ($i = 0; $i < $colspan; ++
$i) {
18144 $cellw +
= $table_colwidths[($colid +
$i)];
18147 $cellw +
= (($colspan - 1) * $cellspacing['H']);
18148 // increment column indicator
18149 $colid +
= $colspan;
18150 // add rowspan information to table element
18151 if ($rowspan > 1) {
18152 $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
));
18154 $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x
));
18155 if ($rowspan > 1) {
18156 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
18158 // push background colors
18159 if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
18160 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
18162 // store border info
18163 if (isset($tdborder) AND !empty($tdborder)) {
18164 $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
18166 $prevLastH = $this->lasth
;
18167 // store some info for multicolumn mode
18169 $this->colxshift
['x'] = $this->w
- $this->x
- $this->rMargin
;
18171 $this->colxshift
['x'] = $this->x
- $this->lMargin
;
18173 $this->colxshift
['s'] = $cellspacing;
18174 $this->colxshift
['p'] = $current_cell_padding;
18175 // ****** write the cell content ******
18176 $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
18177 // restore some values
18178 $this->colxshift
= array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
18179 $this->lasth
= $prevLastH;
18180 $this->cell_padding
= $old_cell_padding;
18181 $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x
;
18182 // update the end of row position
18183 if ($rowspan <= 1) {
18184 if (isset($dom[$trid]['endy'])) {
18185 if (($this->page
== $dom[$trid]['endpage']) AND ($this->current_column
== $dom[$trid]['endcolumn'])) {
18186 $dom[$trid]['endy'] = max($this->y
, $dom[$trid]['endy']);
18187 } elseif (($this->page
> $dom[$trid]['endpage']) OR ($this->current_column
> $dom[$trid]['endcolumn'])) {
18188 $dom[$trid]['endy'] = $this->y
;
18191 $dom[$trid]['endy'] = $this->y
;
18193 if (isset($dom[$trid]['endpage'])) {
18194 $dom[$trid]['endpage'] = max($this->page
, $dom[$trid]['endpage']);
18196 $dom[$trid]['endpage'] = $this->page
;
18198 if (isset($dom[$trid]['endcolumn'])) {
18199 $dom[$trid]['endcolumn'] = max($this->current_column
, $dom[$trid]['endcolumn']);
18201 $dom[$trid]['endcolumn'] = $this->current_column
;
18204 // account for row-spanned cells
18205 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x
;
18206 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y
;
18207 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page
;
18208 $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column
;
18210 if (isset($dom[$table_el]['rowspans'])) {
18211 // update endy and endpage on rowspanned cells
18212 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
18213 if ($trwsp['rowspan'] > 0) {
18214 if (isset($dom[$trid]['endpage'])) {
18215 if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18216 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18217 } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
18218 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
18219 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
18220 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
18222 $dom[$trid]['endy'] = $this->pagedim
[$dom[$trid]['endpage']]['hk'] - $this->pagedim
[$dom[$trid]['endpage']]['bm'];
18228 $this->x +
= ($cellspacingx / 2);
18230 // opening tag (or self-closing tag)
18231 if (!isset($opentagpos)) {
18232 if ($this->inxobj
) {
18233 // we are inside an XObject template
18234 $opentagpos = strlen($this->xobjects
[$this->xobjid
]['outdata']);
18235 } elseif (!$this->InFooter
) {
18236 if (isset($this->footerlen
[$this->page
])) {
18237 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
] - $this->footerlen
[$this->page
];
18239 $this->footerpos
[$this->page
] = $this->pagelen
[$this->page
];
18241 $opentagpos = $this->footerpos
[$this->page
];
18244 $dom = $this->openHTMLTagHandler($dom, $key, $cell);
18246 } else { // closing tag
18247 $prev_numpages = $this->numpages
;
18248 $old_bordermrk = $this->bordermrk
[$this->page
];
18249 $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
18250 if ($this->bordermrk
[$this->page
] > $old_bordermrk) {
18251 $startlinepos +
= ($this->bordermrk
[$this->page
] - $old_bordermrk);
18253 if ($prev_numpages > $this->numpages
) {
18254 $startlinepage = $this->page
;
18257 } elseif (strlen($dom[$key]['value']) > 0) {
18259 if (!TCPDF_STATIC
::empty_string($this->lispacer
) AND ($this->lispacer
!= '^')) {
18260 $this->SetFont($pfontname, $pfontstyle, $pfontsize);
18261 $this->resetLastH();
18262 $minstartliney = $this->y
;
18263 $maxbottomliney = ($startliney +
$this->getCellHeight($this->FontSize
));
18264 if (is_numeric($pfontsize) AND ($pfontsize > 0)) {
18265 $this->putHtmlListBullet($this->listnum
, $this->lispacer
, $pfontsize);
18267 $this->SetFont($curfontname, $curfontstyle, $curfontsize);
18268 $this->resetLastH();
18269 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
18270 $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
18271 $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
18272 $this->y +
= ($this->getCellHeight(($pfontsize - $curfontsize) / $this->k
) +
$pfontascent - $curfontascent - $pfontdescent +
$curfontdescent) / 2;
18273 $minstartliney = min($this->y
, $minstartliney);
18274 $maxbottomliney = max(($this->y +
$this->getCellHeight($pfontsize / $this->k
)), $maxbottomliney);
18278 $this->htmlvspace
= 0;
18279 if ((!$this->premode
) AND $this->isRTLTextDir()) {
18280 // reverse spaces order
18281 $lsp = ''; // left spaces
18282 $rsp = ''; // right spaces
18283 if (preg_match('/^('.$this->re_space
['p'].'+)/'.$this->re_space
['m'], $dom[$key]['value'], $matches)) {
18284 $lsp = $matches[1];
18286 if (preg_match('/('.$this->re_space
['p'].'+)$/'.$this->re_space
['m'], $dom[$key]['value'], $matches)) {
18287 $rsp = $matches[1];
18289 $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
18292 if (!$this->premode
) {
18293 $prelen = strlen($dom[$key]['value']);
18294 if ($this->isRTLTextDir()) {
18295 // right trim except non-breaking space
18296 $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
18298 // left trim except non-breaking space
18299 $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
18301 $postlen = strlen($dom[$key]['value']);
18302 if (($postlen == 0) AND ($prelen > 0)) {
18303 $dom[$key]['trimmed_space'] = true;
18307 $firstblock = true;
18309 $firstblock = false;
18310 // replace empty multiple spaces string with a single space
18311 $dom[$key]['value'] = preg_replace('/^'.$this->re_space
['p'].'+$/'.$this->re_space
['m'], chr(32), $dom[$key]['value']);
18315 $this->x
-= $this->textindent
;
18317 $this->x +
= $this->textindent
;
18319 if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
18320 $strlinelen = $this->GetStringWidth($dom[$key]['value']);
18321 if (!empty($this->HREF
) AND (isset($this->HREF
['url']))) {
18324 if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
18325 $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
18328 if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
18329 $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
18331 $strrest = $this->addHtmlLink($this->HREF
['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
18333 $wadj = 0; // space to leave for block continuity
18335 $cwa = ($this->x
- $this->lMargin
);
18337 $cwa = ($this->w
- $this->rMargin
- $this->x
);
18339 if (($strlinelen < $cwa) AND (isset($dom[($key +
1)])) AND ($dom[($key +
1)]['tag']) AND (!$dom[($key +
1)]['block'])) {
18340 // check the next text blocks for continuity
18341 $nkey = ($key +
1);
18342 $write_block = true;
18343 $same_textdir = true;
18344 $tmp_fontname = $this->FontFamily
;
18345 $tmp_fontstyle = $this->FontStyle
;
18346 $tmp_fontsize = $this->FontSizePt
;
18347 while ($write_block AND isset($dom[$nkey])) {
18348 if ($dom[$nkey]['tag']) {
18349 if ($dom[$nkey]['block']) {
18351 $write_block = false;
18353 $tmp_fontname = isset($dom[$nkey]['fontname']) ?
$dom[$nkey]['fontname'] : $this->FontFamily
;
18354 $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ?
$dom[$nkey]['fontstyle'] : $this->FontStyle
;
18355 $tmp_fontsize = isset($dom[$nkey]['fontsize']) ?
$dom[$nkey]['fontsize'] : $this->FontSizePt
;
18356 $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
18358 $nextstr = TCPDF_STATIC
::pregSplit('/'.$this->re_space
['p'].'+/', $this->re_space
['m'], $dom[$nkey]['value']);
18359 if (isset($nextstr[0]) AND $same_textdir) {
18360 $wadj +
= $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
18361 if (isset($nextstr[1])) {
18362 $write_block = false;
18369 if (($wadj > 0) AND (($strlinelen +
$wadj) >= $cwa)) {
18371 $nextstr = TCPDF_STATIC
::pregSplit('/'.$this->re_space
['p'].'/', $this->re_space
['m'], $dom[$key]['value']);
18372 $numblks = count($nextstr);
18373 if ($numblks > 1) {
18374 // try to split on blank spaces
18375 $wadj = ($cwa - $strlinelen +
$this->GetStringWidth($nextstr[($numblks - 1)]));
18377 // set the entire block on new line
18378 $wadj = $this->GetStringWidth($nextstr[0]);
18381 // check for reversed text direction
18382 if (($wadj > 0) AND (($this->rtl
AND ($this->tmprtl
=== 'L')) OR (!$this->rtl
AND ($this->tmprtl
=== 'R')))) {
18383 // LTR text on RTL direction or RTL text on LTR direction
18384 $reverse_dir = true;
18385 $this->rtl
= !$this->rtl
;
18386 $revshift = ($strlinelen +
$wadj +
0.000001); // add little quantity for rounding problems
18388 $this->x +
= $revshift;
18390 $this->x
-= $revshift;
18394 // ****** write only until the end of the line and get the rest ******
18395 $strrest = $this->Write($this->lasth
, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
18396 // restore default direction
18397 if ($reverse_dir AND ($wadj == 0)) {
18399 $this->rtl
= !$this->rtl
;
18400 $reverse_dir = false;
18404 $this->textindent
= 0;
18405 if (strlen($strrest) > 0) {
18406 // store the remaining string on the previous $key position
18407 $this->newline
= true;
18408 if ($strrest == $dom[$key]['value']) {
18409 // used to avoid infinite loop
18414 $dom[$key]['value'] = $strrest;
18417 $this->x
-= $this->cell_padding
['R'];
18419 $this->x +
= $this->cell_padding
['L'];
18427 // add the positive font spacing of the last character (if any)
18428 if ($this->font_spacing
> 0) {
18430 $this->x
-= $this->font_spacing
;
18432 $this->x +
= $this->font_spacing
;
18438 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')) {
18439 // check if we are on a new page or on a new column
18440 if ((!$undo) AND (($this->y
< $this->start_transaction_y
) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y
)))) {
18441 // we are on a new page or on a new column and the total object height is less than the available vertical space.
18442 // restore previous object
18443 $this->rollbackTransaction(true);
18444 // restore previous values
18445 foreach ($this_method_vars as $vkey => $vval) {
18448 // add a page (or trig AcceptPageBreak() for multicolumn mode)
18450 if ((!$this->checkPageBreak($this->PageBreakTrigger +
1)) AND ($this->y
< $pre_y)) {
18451 $startliney = $this->y
;
18453 $undo = true; // avoid infinite loop
18458 } // end for each $key
18459 // align the last line
18460 if (isset($startlinex)) {
18461 $yshift = ($minstartliney - $startliney);
18462 if (($yshift > 0) OR ($this->page
> $startlinepage)) {
18466 // the last line must be shifted to be aligned as requested
18467 $linew = abs($this->endlinex
- $startlinex);
18468 if ($this->inxobj
) {
18469 // we are inside an XObject template
18470 $pstart = substr($this->xobjects
[$this->xobjid
]['outdata'], 0, $startlinepos);
18471 if (isset($opentagpos)) {
18472 $midpos = $opentagpos;
18477 $pmid = substr($this->xobjects
[$this->xobjid
]['outdata'], $startlinepos, ($midpos - $startlinepos));
18478 $pend = substr($this->xobjects
[$this->xobjid
]['outdata'], $midpos);
18480 $pmid = substr($this->xobjects
[$this->xobjid
]['outdata'], $startlinepos);
18484 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
18485 if (isset($opentagpos) AND isset($this->footerlen
[$startlinepage]) AND (!$this->InFooter
)) {
18486 $this->footerpos
[$startlinepage] = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
18487 $midpos = min($opentagpos, $this->footerpos
[$startlinepage]);
18488 } elseif (isset($opentagpos)) {
18489 $midpos = $opentagpos;
18490 } elseif (isset($this->footerlen
[$startlinepage]) AND (!$this->InFooter
)) {
18491 $this->footerpos
[$startlinepage] = $this->pagelen
[$startlinepage] - $this->footerlen
[$startlinepage];
18492 $midpos = $this->footerpos
[$startlinepage];
18497 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
18498 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
18500 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
18504 if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl
)) OR (($plalign == 'L') AND ($this->rtl
)))))) {
18505 // calculate shifting amount
18507 if ($this->lMargin
!= $prevlMargin) {
18508 $tw +
= ($prevlMargin - $this->lMargin
);
18510 if ($this->rMargin
!= $prevrMargin) {
18511 $tw +
= ($prevrMargin - $this->rMargin
);
18513 $one_space_width = $this->GetStringWidth(chr(32));
18514 $no = 0; // number of spaces on a line contained on a single block
18515 if ($this->isRTLTextDir()) { // RTL
18516 // remove left space if exist
18517 $pos1 = TCPDF_STATIC
::revstrpos($pmid, '[(');
18519 $pos1 = intval($pos1);
18520 if ($this->isUnicodeFont()) {
18521 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, '[('.chr(0).chr(32)));
18524 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, '[('.chr(32)));
18527 if ($pos1 == $pos2) {
18528 $pmid = substr($pmid, 0, ($pos1 +
2)).substr($pmid, ($pos1 +
2 +
$spacelen));
18529 if (substr($pmid, $pos1, 4) == '[()]') {
18530 $linew -= $one_space_width;
18531 } elseif ($pos1 == strpos($pmid, '[(')) {
18537 // remove right space if exist
18538 $pos1 = TCPDF_STATIC
::revstrpos($pmid, ')]');
18540 $pos1 = intval($pos1);
18541 if ($this->isUnicodeFont()) {
18542 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, chr(0).chr(32).')]')) +
2;
18545 $pos2 = intval(TCPDF_STATIC
::revstrpos($pmid, chr(32).')]')) +
1;
18548 if ($pos1 == $pos2) {
18549 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
18550 $linew -= $one_space_width;
18554 $mdiff = ($tw - $linew);
18555 if ($plalign == 'C') {
18557 $t_x = -($mdiff / 2);
18559 $t_x = ($mdiff / 2);
18561 } elseif ($plalign == 'R') {
18562 // right alignment on LTR document
18564 } elseif ($plalign == 'L') {
18565 // left alignment on RTL document
18568 } // end if startlinex
18569 if (($t_x != 0) OR ($yshift < 0)) {
18571 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k
), ($yshift * $this->k
));
18572 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18573 $endlinepos = strlen($pstart);
18574 if ($this->inxobj
) {
18575 // we are inside an XObject template
18576 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$pend;
18577 foreach ($this->xobjects
[$this->xobjid
]['annotations'] as $pak => $pac) {
18578 if ($pak >= $pask) {
18579 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['x'] +
= $t_x;
18580 $this->xobjects
[$this->xobjid
]['annotations'][$pak]['y'] -= $yshift;
18584 $this->setPageBuffer($startlinepage, $pstart.$pend);
18585 // shift the annotations and links
18586 if (isset($this->PageAnnots
[$this->page
])) {
18587 foreach ($this->PageAnnots
[$this->page
] as $pak => $pac) {
18588 if ($pak >= $pask) {
18589 $this->PageAnnots
[$this->page
][$pak]['x'] +
= $t_x;
18590 $this->PageAnnots
[$this->page
][$pak]['y'] -= $yshift;
18595 $this->y
-= $yshift;
18599 // restore previous values
18600 $this->setGraphicVars($gvars);
18601 if ($this->num_columns
> 1) {
18602 $this->selectColumn();
18603 } elseif ($this->page
> $prevPage) {
18604 $this->lMargin
= $this->pagedim
[$this->page
]['olm'];
18605 $this->rMargin
= $this->pagedim
[$this->page
]['orm'];
18607 // restore previous list state
18608 $this->cell_height_ratio
= $prev_cell_height_ratio;
18609 $this->listnum
= $prev_listnum;
18610 $this->listordered
= $prev_listordered;
18611 $this->listcount
= $prev_listcount;
18612 $this->lispacer
= $prev_lispacer;
18613 if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
18614 $this->Ln($this->lasth
);
18615 if ($this->y
< $maxbottomliney) {
18616 $this->y
= $maxbottomliney;
18623 * Process opening tags.
18624 * @param $dom (array) html dom array
18625 * @param $key (int) current element id
18626 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
18627 * @return $dom array
18630 protected function openHTMLTagHandler($dom, $key, $cell) {
18632 $parent = $dom[($dom[$key]['parent'])];
18633 $firsttag = ($key == 1);
18634 // check for text direction attribute
18635 if (isset($tag['dir'])) {
18636 $this->setTempRTL($tag['dir']);
18638 $this->tmprtl
= false;
18640 if ($tag['block']) {
18641 $hbz = 0; // distance from y to line bottom
18642 $hb = 0; // vertical space between block tags
18643 // calculate vertical space for block tags
18644 if (isset($this->tagvspaces
[$tag['value']][0]['h']) AND ($this->tagvspaces
[$tag['value']][0]['h'] >= 0)) {
18645 $cur_h = $this->tagvspaces
[$tag['value']][0]['h'];
18646 } elseif (isset($tag['fontsize'])) {
18647 $cur_h = $this->getCellHeight($tag['fontsize'] / $this->k
);
18649 $cur_h = $this->getCellHeight($this->FontSize
);
18651 if (isset($this->tagvspaces
[$tag['value']][0]['n'])) {
18652 $n = $this->tagvspaces
[$tag['value']][0]['n'];
18653 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18658 if ((!isset($this->tagvspaces
[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br')))) {
18661 $hb = ($n * $cur_h);
18663 if (($this->htmlvspace
<= 0) AND ($n > 0)) {
18664 if (isset($parent['fontsize'])) {
18665 $hbz = (($parent['fontsize'] / $this->k
) * $this->cell_height_ratio
);
18667 $hbz = $this->getCellHeight($this->FontSize
);
18670 if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) {
18671 // fix vertical space after table
18676 switch($tag['value']) {
18680 $dom[$key]['rowspans'] = array();
18681 if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
18682 $this->htmlvspace
= 0;
18683 // set table header
18684 if (!TCPDF_STATIC
::empty_string($dom[$key]['thead'])) {
18685 // set table header
18686 $this->thead
= $dom[$key]['thead'];
18687 if (!isset($this->theadMargins
) OR (empty($this->theadMargins
))) {
18688 $this->theadMargins
= array();
18689 $this->theadMargins
['cell_padding'] = $this->cell_padding
;
18690 $this->theadMargins
['lmargin'] = $this->lMargin
;
18691 $this->theadMargins
['rmargin'] = $this->rMargin
;
18692 $this->theadMargins
['page'] = $this->page
;
18693 $this->theadMargins
['cell'] = $cell;
18694 $this->theadMargins
['gvars'] = $this->getGraphicVars();
18698 // store current margins and page
18699 $dom[$key]['old_cell_padding'] = $this->cell_padding
;
18700 if (isset($tag['attribute']['cellpadding'])) {
18701 $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
18702 $this->SetCellPadding($pad);
18703 } elseif (isset($tag['padding'])) {
18704 $this->cell_padding
= $tag['padding'];
18706 if (isset($tag['attribute']['cellspacing'])) {
18707 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
18708 } elseif (isset($tag['border-spacing'])) {
18709 $cs = $tag['border-spacing']['V'];
18711 $prev_y = $this->y
;
18712 if ($this->checkPageBreak(((2 * $cp) +
(2 * $cs) +
$this->lasth
), '', false) OR ($this->y
< $prev_y)) {
18713 $this->inthead
= true;
18714 // add a page (or trig AcceptPageBreak() for multicolumn mode)
18715 $this->checkPageBreak($this->PageBreakTrigger +
1);
18720 // array of columns positions
18721 $dom[$key]['cellpos'] = array();
18725 if ((isset($tag['height'])) AND ($tag['height'] != '')) {
18726 $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
18728 $hrHeight = $this->GetLineWidth();
18730 $this->addHTMLVertSpace($hbz, ($hrHeight / 2), $cell, $firsttag);
18731 $x = $this->GetX();
18732 $y = $this->GetY();
18733 $wtmp = $this->w
- $this->lMargin
- $this->rMargin
;
18735 $wtmp -= ($this->cell_padding
['L'] +
$this->cell_padding
['R']);
18737 if ((isset($tag['width'])) AND ($tag['width'] != '')) {
18738 $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
18742 $prevlinewidth = $this->GetLineWidth();
18743 $this->SetLineWidth($hrHeight);
18744 $this->Line($x, $y, $x +
$hrWidth, $y);
18745 $this->SetLineWidth($prevlinewidth);
18746 $this->addHTMLVertSpace(($hrHeight / 2), 0, $cell, !isset($dom[($key +
1)]));
18750 if (array_key_exists('href', $tag['attribute'])) {
18751 $this->HREF
['url'] = $tag['attribute']['href'];
18756 if (!empty($tag['attribute']['src'])) {
18757 if ($tag['attribute']['src'][0] === '@') {
18759 $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1));
18763 $type = TCPDF_IMAGES
::getImageFileType($tag['attribute']['src']);
18765 if (!isset($tag['width'])) {
18768 if (!isset($tag['height'])) {
18769 $tag['height'] = 0;
18771 //if (!isset($tag['attribute']['align'])) {
18772 // the only alignment supported is "bottom"
18773 // further development is required for other modes.
18774 $tag['attribute']['align'] = 'bottom';
18776 switch($tag['attribute']['align']) {
18797 if (isset($this->HREF
['url']) AND !TCPDF_STATIC
::empty_string($this->HREF
['url'])) {
18798 $imglink = $this->HREF
['url'];
18799 if ($imglink[0] == '#') {
18800 // convert url to internal link
18801 $lnkdata = explode(',', $imglink);
18802 if (isset($lnkdata[0])) {
18803 $page = intval(substr($lnkdata[0], 1));
18804 if (empty($page) OR ($page <= 0)) {
18805 $page = $this->page
;
18807 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
18808 $lnky = floatval($lnkdata[1]);
18812 $imglink = $this->AddLink();
18813 $this->SetLink($imglink, $lnky, $page);
18818 if (isset($tag['border']) AND !empty($tag['border'])) {
18819 // currently only support 1 (frame) or a combination of 'LTRB'
18820 $border = $tag['border'];
18823 if (isset($tag['width'])) {
18824 $iw = $this->getHTMLUnitToUnits($tag['width'], ($tag['fontsize'] / $this->k
), 'px', false);
18827 if (isset($tag['height'])) {
18828 $ih = $this->getHTMLUnitToUnits($tag['height'], ($tag['fontsize'] / $this->k
), 'px', false);
18830 if (($type == 'eps') OR ($type == 'ai')) {
18831 $this->ImageEps($tag['attribute']['src'], $xpos, $this->y
, $iw, $ih, $imglink, true, $align, '', $border, true);
18832 } elseif ($type == 'svg') {
18833 $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y
, $iw, $ih, $imglink, $align, '', $border, true);
18835 $this->Image($tag['attribute']['src'], $xpos, $this->y
, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
18843 $this->y
= (($this->img_rb_y +
$prevy - ($this->getCellHeight($tag['fontsize'] / $this->k
))) / 2);
18847 $this->y
= $this->img_rb_y
- ($this->getCellHeight($tag['fontsize'] / $this->k
) - ($this->getFontDescent($tag['fontname'], $tag['fontstyle'], $tag['fontsize']) * $this->cell_height_ratio
));
18856 if ($this->listnum
== 1) {
18857 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18859 $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18864 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18869 $this->rMargin +
= $this->listindent
;
18871 $this->lMargin +
= $this->listindent
;
18873 ++
$this->listindentlevel
;
18874 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18880 if ($tag['value'] == 'ol') {
18881 $this->listordered
[$this->listnum
] = true;
18883 $this->listordered
[$this->listnum
] = false;
18885 if (isset($tag['attribute']['start'])) {
18886 $this->listcount
[$this->listnum
] = intval($tag['attribute']['start']) - 1;
18888 $this->listcount
[$this->listnum
] = 0;
18891 $this->rMargin +
= $this->listindent
;
18892 $this->x
-= $this->listindent
;
18894 $this->lMargin +
= $this->listindent
;
18895 $this->x +
= $this->listindent
;
18897 ++
$this->listindentlevel
;
18898 if ($this->listnum
== 1) {
18900 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18903 $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18909 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18911 if ($this->listordered
[$this->listnum
]) {
18913 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC
::empty_string($parent['attribute']['type'])) {
18914 $this->lispacer
= $parent['attribute']['type'];
18915 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC
::empty_string($parent['listtype'])) {
18916 $this->lispacer
= $parent['listtype'];
18917 } elseif (isset($this->lisymbol
) AND !TCPDF_STATIC
::empty_string($this->lisymbol
)) {
18918 $this->lispacer
= $this->lisymbol
;
18920 $this->lispacer
= '#';
18922 ++
$this->listcount
[$this->listnum
];
18923 if (isset($tag['attribute']['value'])) {
18924 $this->listcount
[$this->listnum
] = intval($tag['attribute']['value']);
18928 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC
::empty_string($parent['attribute']['type'])) {
18929 $this->lispacer
= $parent['attribute']['type'];
18930 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC
::empty_string($parent['listtype'])) {
18931 $this->lispacer
= $parent['listtype'];
18932 } elseif (isset($this->lisymbol
) AND !TCPDF_STATIC
::empty_string($this->lisymbol
)) {
18933 $this->lispacer
= $this->lisymbol
;
18935 $this->lispacer
= '!';
18940 case 'blockquote': {
18942 $this->rMargin +
= $this->listindent
;
18944 $this->lMargin +
= $this->listindent
;
18946 ++
$this->listindentlevel
;
18947 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18951 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18955 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18959 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18963 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18964 $this->premode
= true;
18968 $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt
) / $this->k
));
18972 $this->SetXY($this->GetX(), $this->GetY() +
((0.3 * $this->FontSizePt
) / $this->k
));
18981 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18984 // Form fields (since 4.8.000 - 2009-09-07)
18986 if (isset($tag['attribute']['action'])) {
18987 $this->form_action
= $tag['attribute']['action'];
18989 $this->Error('Please explicitly set action attribute path!');
18991 if (isset($tag['attribute']['enctype'])) {
18992 $this->form_enctype
= $tag['attribute']['enctype'];
18994 $this->form_enctype
= 'application/x-www-form-urlencoded';
18996 if (isset($tag['attribute']['method'])) {
18997 $this->form_mode
= $tag['attribute']['method'];
18999 $this->form_mode
= 'post';
19004 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['name'])) {
19005 $name = $tag['attribute']['name'];
19011 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['readonly'])) {
19012 $prop['readonly'] = true;
19014 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['value'])) {
19015 $value = $tag['attribute']['value'];
19017 if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['maxlength'])) {
19018 $opt['maxlen'] = intval($tag['attribute']['maxlength']);
19020 $h = $this->getCellHeight($this->FontSize
);
19021 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['size'])) {
19022 $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
19026 if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
19031 if (isset($tag['align'])) {
19032 switch ($tag['align']) {
19047 switch ($tag['attribute']['type']) {
19049 if (isset($value)) {
19050 $opt['v'] = $value;
19052 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19056 if (isset($value)) {
19057 $opt['v'] = $value;
19059 $prop['password'] = 'true';
19060 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19064 if (!isset($value)) {
19067 $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
19071 if (!isset($value)) {
19074 $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
19078 if (!isset($value)) {
19081 $w = $this->GetStringWidth($value) * 1.5;
19083 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19085 $action['S'] = 'SubmitForm';
19086 $action['F'] = $this->form_action
;
19087 if ($this->form_enctype
!= 'FDF') {
19088 $action['Flags'] = array('ExportFormat');
19090 if ($this->form_mode
== 'get') {
19091 $action['Flags'] = array('GetMethod');
19093 $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
19097 if (!isset($value)) {
19100 $w = $this->GetStringWidth($value) * 1.5;
19102 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19103 $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
19107 $prop['fileSelect'] = 'true';
19108 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19109 if (!isset($value)) {
19112 $w = $this->GetStringWidth($value) * 2;
19114 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19115 $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
19116 $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19120 if (isset($value)) {
19121 $opt['v'] = $value;
19123 $opt['f'] = array('invisible', 'hidden');
19124 $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
19128 // THIS TYPE MUST BE FIXED
19129 if (isset($tag['attribute']['src']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['src'])) {
19130 $img = $tag['attribute']['src'];
19135 //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
19136 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19137 $jsaction = $tag['attribute']['onclick'];
19141 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19145 if (!isset($value)) {
19148 $w = $this->GetStringWidth($value) * 1.5;
19150 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19151 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19152 $jsaction = $tag['attribute']['onclick'];
19156 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19165 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['readonly'])) {
19166 $prop['readonly'] = true;
19168 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['name'])) {
19169 $name = $tag['attribute']['name'];
19173 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['value'])) {
19174 $opt['v'] = $tag['attribute']['value'];
19176 if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['cols'])) {
19177 $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
19181 if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['rows'])) {
19182 $h = intval($tag['attribute']['rows']) * $this->getCellHeight($this->FontSize
);
19186 $prop['multiline'] = 'true';
19187 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19191 $h = $this->getCellHeight($this->FontSize
);
19192 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['size'])) {
19193 $h *= ($tag['attribute']['size'] +
1);
19197 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['name'])) {
19198 $name = $tag['attribute']['name'];
19203 if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC
::empty_string($tag['attribute']['opt'])) {
19204 $options = explode('#!NwL!#', $tag['attribute']['opt']);
19206 foreach ($options as $val) {
19207 if (strpos($val, '#!TaB!#') !== false) {
19208 $opts = explode('#!TaB!#', $val);
19210 $w = max($w, $this->GetStringWidth($opts[1]));
19213 $w = max($w, $this->GetStringWidth($val));
19220 if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
19221 $prop['multipleSelection'] = 'true';
19222 $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19224 $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19229 if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML
=== true)) {
19230 // Special tag used to call TCPDF methods
19231 if (isset($tag['attribute']['method'])) {
19232 $tcpdf_method = $tag['attribute']['method'];
19233 if (method_exists($this, $tcpdf_method)) {
19234 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
19235 $params = unserialize(urldecode($tag['attribute']['params']));
19236 call_user_func_array(array($this, $tcpdf_method), $params);
19238 $this->$tcpdf_method();
19240 $this->newline
= true;
19250 // define tags that support borders and background colors
19251 $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
19252 if (in_array($tag['value'], $bordertags)) {
19254 $dom[$key]['borderposition'] = $this->getBorderStartPosition();
19256 if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
19257 $pba = $dom[$key]['attribute']['pagebreakafter'];
19258 // check for pagebreak
19259 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19260 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19261 $this->checkPageBreak($this->PageBreakTrigger +
1);
19263 if ((($pba == 'left') AND (((!$this->rtl
) AND (($this->page %
2) == 0)) OR (($this->rtl
) AND (($this->page %
2) != 0))))
19264 OR (($pba == 'right') AND (((!$this->rtl
) AND (($this->page %
2) != 0)) OR (($this->rtl
) AND (($this->page %
2) == 0))))) {
19265 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19266 $this->checkPageBreak($this->PageBreakTrigger +
1);
19273 * Process closing tags.
19274 * @param $dom (array) html dom array
19275 * @param $key (int) current element id
19276 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
19277 * @param $maxbottomliney (int) maximum y value of current line
19278 * @return $dom array
19281 protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
19283 $parent = $dom[($dom[$key]['parent'])];
19284 $lasttag = ((!isset($dom[($key +
1)])) OR ((!isset($dom[($key +
2)])) AND ($dom[($key +
1)]['value'] == 'marker')));
19285 $in_table_head = false;
19286 // maximum x position (used to draw borders)
19292 if ($tag['block']) {
19293 $hbz = 0; // distance from y to line bottom
19294 $hb = 0; // vertical space between block tags
19295 // calculate vertical space for block tags
19296 if (isset($this->tagvspaces
[$tag['value']][1]['h']) AND ($this->tagvspaces
[$tag['value']][1]['h'] >= 0)) {
19297 $pre_h = $this->tagvspaces
[$tag['value']][1]['h'];
19298 } elseif (isset($parent['fontsize'])) {
19299 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k
);
19301 $pre_h = $this->getCellHeight($this->FontSize
);
19303 if (isset($this->tagvspaces
[$tag['value']][1]['n'])) {
19304 $n = $this->tagvspaces
[$tag['value']][1]['n'];
19305 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
19310 if ((!isset($this->tagvspaces
[$tag['value']])) AND ($tag['value'] == 'div')) {
19313 $hb = ($n * $pre_h);
19315 if ($maxbottomliney > $this->PageBreakTrigger
) {
19316 $hbz = $this->getCellHeight($this->FontSize
);
19317 } elseif ($this->y
< $maxbottomliney) {
19318 $hbz = ($maxbottomliney - $this->y
);
19322 switch($tag['value']) {
19324 $table_el = $dom[($dom[$key]['parent'])]['parent'];
19325 if (!isset($parent['endy'])) {
19326 $dom[($dom[$key]['parent'])]['endy'] = $this->y
;
19327 $parent['endy'] = $this->y
;
19329 if (!isset($parent['endpage'])) {
19330 $dom[($dom[$key]['parent'])]['endpage'] = $this->page
;
19331 $parent['endpage'] = $this->page
;
19333 if (!isset($parent['endcolumn'])) {
19334 $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column
;
19335 $parent['endcolumn'] = $this->current_column
;
19337 // update row-spanned cells
19338 if (isset($dom[$table_el]['rowspans'])) {
19339 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19340 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
19341 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19342 if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
19343 $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
19344 } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
19345 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19346 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19347 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19351 // report new endy and endpage to the rowspanned cells
19352 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19353 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19354 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
19355 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19356 $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
19357 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19358 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
19359 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19362 // update remaining rowspanned cells
19363 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19364 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19365 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
19366 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
19367 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
19371 $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
19372 if ($this->num_columns
> 1) {
19373 $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
19375 $this->y
= $dom[($dom[$key]['parent'])]['endy'];
19376 if (isset($dom[$table_el]['attribute']['cellspacing'])) {
19377 $this->y +
= $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
19378 } elseif (isset($dom[$table_el]['border-spacing'])) {
19379 $this->y +
= $dom[$table_el]['border-spacing']['V'];
19381 $this->Ln(0, $cell);
19382 if ($this->current_column
== $parent['startcolumn']) {
19383 $this->x
= $parent['startx'];
19385 // account for booklet mode
19386 if ($this->page
> $parent['startpage']) {
19387 if (($this->rtl
) AND ($this->pagedim
[$this->page
]['orm'] != $this->pagedim
[$parent['startpage']]['orm'])) {
19388 $this->x
-= ($this->pagedim
[$this->page
]['orm'] - $this->pagedim
[$parent['startpage']]['orm']);
19389 } elseif ((!$this->rtl
) AND ($this->pagedim
[$this->page
]['olm'] != $this->pagedim
[$parent['startpage']]['olm'])) {
19390 $this->x +
= ($this->pagedim
[$this->page
]['olm'] - $this->pagedim
[$parent['startpage']]['olm']);
19396 // closing tag used for the thead part
19397 $in_table_head = true;
19398 $this->inthead
= false;
19400 $table_el = $parent;
19401 // set default border
19402 if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
19403 // set default border
19404 $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
19408 $default_border = $border;
19409 // fix bottom line alignment of last line before page break
19410 foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
19411 // update row-spanned cells
19412 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19413 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19414 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) {
19415 $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
19417 if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) {
19418 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
19422 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
19423 $pgendy = $this->pagedim
[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim
[$dom[$prevtrkey]['endpage']]['bm'];
19424 $dom[$prevtrkey]['endy'] = $pgendy;
19425 // update row-spanned cells
19426 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19427 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19428 if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] > 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
19429 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
19430 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
19435 $prevtrkey = $trkey;
19436 $table_el = $dom[($dom[$key]['parent'])];
19439 if (count($table_el['trids']) > 0) {
19442 foreach ($table_el['trids'] as $j => $trkey) {
19443 $parent = $dom[$trkey];
19444 if (!isset($xmax)) {
19445 $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
19447 // for each cell on the row
19448 foreach ($parent['cellpos'] as $k => $cellpos) {
19449 if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
19450 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
19451 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
19452 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
19453 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
19454 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
19455 $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
19456 $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
19458 $endy = $parent['endy'];
19459 $startpage = $parent['startpage'];
19460 $endpage = $parent['endpage'];
19461 $startcolumn = $parent['startcolumn'];
19462 $endcolumn = $parent['endcolumn'];
19464 if ($this->num_columns
== 0) {
19465 $this->num_columns
= 1;
19467 if (isset($cellpos['border'])) {
19468 $border = $cellpos['border'];
19470 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
19471 $this->SetFillColorArray($cellpos['bgcolor']);
19476 $x = $cellpos['startx'];
19477 $y = $parent['starty'];
19479 $w = abs($cellpos['endx'] - $cellpos['startx']);
19480 // get border modes
19481 $border_start = TCPDF_STATIC
::getBorderMode($border, $position='start', $this->opencell
);
19482 $border_end = TCPDF_STATIC
::getBorderMode($border, $position='end', $this->opencell
);
19483 $border_middle = TCPDF_STATIC
::getBorderMode($border, $position='middle', $this->opencell
);
19484 // design borders around HTML cells.
19485 for ($page = $startpage; $page <= $endpage; ++
$page) { // for each page
19487 $this->setPage($page);
19488 if ($this->num_columns
< 2) {
19489 // single-column mode
19491 $this->y
= $this->tMargin
;
19493 // account for margin changes
19494 if ($page > $startpage) {
19495 if (($this->rtl
) AND ($this->pagedim
[$page]['orm'] != $this->pagedim
[$startpage]['orm'])) {
19496 $this->x
-= ($this->pagedim
[$page]['orm'] - $this->pagedim
[$startpage]['orm']);
19497 } elseif ((!$this->rtl
) AND ($this->pagedim
[$page]['olm'] != $this->pagedim
[$startpage]['olm'])) {
19498 $this->x +
= ($this->pagedim
[$page]['olm'] - $this->pagedim
[$startpage]['olm']);
19501 if ($startpage == $endpage) { // single page
19504 for ($column = $startcolumn; $column <= $endcolumn; ++
$column) { // for each column
19505 $this->selectColumn($column);
19506 if ($startcolumn == $endcolumn) { // single column
19507 $cborder = $border;
19508 $h = $endy - $parent['starty'];
19511 } elseif ($column == $startcolumn) { // first column
19512 $cborder = $border_start;
19513 $this->y
= $starty;
19515 $h = $this->h
- $this->y
- $this->bMargin
;
19517 $deltacol = $this->x +
$this->rMargin
- $this->w
;
19519 $deltacol = $this->x
- $this->lMargin
;
19521 } elseif ($column == $endcolumn) { // end column
19522 $cborder = $border_end;
19523 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19524 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19526 $this->x +
= $deltacol;
19527 $h = $endy - $this->y
;
19528 } else { // middle column
19529 $cborder = $border_middle;
19530 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19531 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19533 $this->x +
= $deltacol;
19534 $h = $this->h
- $this->y
- $this->bMargin
;
19536 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19537 } // end for each column
19538 } elseif ($page == $startpage) { // first page
19541 for ($column = $startcolumn; $column < $this->num_columns
; ++
$column) { // for each column
19542 $this->selectColumn($column);
19543 if ($column == $startcolumn) { // first column
19544 $cborder = $border_start;
19545 $this->y
= $starty;
19547 $h = $this->h
- $this->y
- $this->bMargin
;
19549 $deltacol = $this->x +
$this->rMargin
- $this->w
;
19551 $deltacol = $this->x
- $this->lMargin
;
19553 } else { // middle column
19554 $cborder = $border_middle;
19555 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19556 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19558 $this->x +
= $deltacol;
19559 $h = $this->h
- $this->y
- $this->bMargin
;
19561 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19562 } // end for each column
19563 } elseif ($page == $endpage) { // last page
19566 for ($column = 0; $column <= $endcolumn; ++
$column) { // for each column
19567 $this->selectColumn($column);
19568 if ($column == $endcolumn) { // end column
19569 $cborder = $border_end;
19570 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19571 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19573 $this->x +
= $deltacol;
19574 $h = $endy - $this->y
;
19575 } else { // middle column
19576 $cborder = $border_middle;
19577 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19578 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19580 $this->x +
= $deltacol;
19581 $h = $this->h
- $this->y
- $this->bMargin
;
19583 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19584 } // end for each column
19585 } else { // middle page
19588 for ($column = 0; $column < $this->num_columns
; ++
$column) { // for each column
19589 $this->selectColumn($column);
19590 $cborder = $border_middle;
19591 if (isset($this->columns
[$column]['th']['\''.$page.'\''])) {
19592 $this->y
= $this->columns
[$column]['th']['\''.$page.'\''];
19594 $this->x +
= $deltacol;
19595 $h = $this->h
- $this->y
- $this->bMargin
;
19596 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19597 } // end for each column
19599 if ($cborder OR $fill) {
19600 $offsetlen = strlen($ccode);
19601 // draw border and fill
19602 if ($this->inxobj
) {
19603 // we are inside an XObject template
19604 if (end($this->xobjects
[$this->xobjid
]['transfmrk']) !== false) {
19605 $pagemarkkey = key($this->xobjects
[$this->xobjid
]['transfmrk']);
19606 $pagemark = $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey];
19607 $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey] +
= $offsetlen;
19609 $pagemark = $this->xobjects
[$this->xobjid
]['intmrk'];
19610 $this->xobjects
[$this->xobjid
]['intmrk'] +
= $offsetlen;
19612 $pagebuff = $this->xobjects
[$this->xobjid
]['outdata'];
19613 $pstart = substr($pagebuff, 0, $pagemark);
19614 $pend = substr($pagebuff, $pagemark);
19615 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$ccode.$pend;
19617 // draw border and fill
19618 if (end($this->transfmrk
[$this->page
]) !== false) {
19619 $pagemarkkey = key($this->transfmrk
[$this->page
]);
19620 $pagemark = $this->transfmrk
[$this->page
][$pagemarkkey];
19621 } elseif ($this->InFooter
) {
19622 $pagemark = $this->footerpos
[$this->page
];
19624 $pagemark = $this->intmrk
[$this->page
];
19626 $pagebuff = $this->getPageBuffer($this->page
);
19627 $pstart = substr($pagebuff, 0, $pagemark);
19628 $pend = substr($pagebuff, $pagemark);
19629 $this->setPageBuffer($this->page
, $pstart.$ccode.$pend);
19632 } // end for each page
19633 // restore default border
19634 $border = $default_border;
19635 } // end for each cell on the row
19636 if (isset($table_el['attribute']['cellspacing'])) {
19637 $this->y +
= $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
19638 } elseif (isset($table_el['border-spacing'])) {
19639 $this->y +
= $table_el['border-spacing']['V'];
19641 $this->Ln(0, $cell);
19642 $this->x
= $parent['startx'];
19643 if ($endpage > $startpage) {
19644 if (($this->rtl
) AND ($this->pagedim
[$endpage]['orm'] != $this->pagedim
[$startpage]['orm'])) {
19645 $this->x +
= ($this->pagedim
[$endpage]['orm'] - $this->pagedim
[$startpage]['orm']);
19646 } elseif ((!$this->rtl
) AND ($this->pagedim
[$endpage]['olm'] != $this->pagedim
[$startpage]['olm'])) {
19647 $this->x +
= ($this->pagedim
[$endpage]['olm'] - $this->pagedim
[$startpage]['olm']);
19651 if (!$in_table_head) { // we are not inside a thead section
19652 $this->cell_padding
= $table_el['old_cell_padding'];
19653 // reset row height
19654 $this->resetLastH();
19655 if (($this->page
== ($this->numpages
- 1)) AND ($this->pageopen
[$this->numpages
])) {
19656 $plendiff = ($this->pagelen
[$this->numpages
] - $this->emptypagemrk
[$this->numpages
]);
19657 if (($plendiff > 0) AND ($plendiff < 60)) {
19658 $pagediff = substr($this->getPageBuffer($this->numpages
), $this->emptypagemrk
[$this->numpages
], $plendiff);
19659 if (substr($pagediff, 0, 5) == 'BT /F') {
19660 // the difference is only a font setting
19664 if ($plendiff == 0) {
19665 // remove last blank page
19666 $this->deletePage($this->numpages
);
19669 if (isset($this->theadMargins
['top'])) {
19670 // restore top margin
19671 $this->tMargin
= $this->theadMargins
['top'];
19673 if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
19674 // reset main table header
19676 $this->theadMargins
= array();
19677 $this->pagedim
[$this->page
]['tm'] = $this->tMargin
;
19680 $parent = $table_el;
19688 $this->SetXY($this->GetX(), $this->GetY() +
((0.7 * $parent['fontsize']) / $this->k
));
19692 $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k
));
19696 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19699 case 'blockquote': {
19701 $this->rMargin
-= $this->listindent
;
19703 $this->lMargin
-= $this->listindent
;
19705 --$this->listindentlevel
;
19706 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19710 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19714 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19715 $this->premode
= false;
19720 if ($this->listnum
<= 0) {
19721 $this->listnum
= 0;
19722 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19724 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19726 $this->resetLastH();
19730 $this->lispacer
= '';
19731 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19735 $this->lispacer
= '';
19737 $this->rMargin
-= $this->listindent
;
19739 $this->lMargin
-= $this->listindent
;
19741 --$this->listindentlevel
;
19742 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19748 $this->lispacer
= '';
19750 $this->rMargin
-= $this->listindent
;
19752 $this->lMargin
-= $this->listindent
;
19754 --$this->listindentlevel
;
19755 if ($this->listnum
<= 0) {
19756 $this->listnum
= 0;
19757 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19759 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19761 $this->resetLastH();
19765 $this->lispacer
= '';
19766 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19775 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19778 // Form fields (since 4.8.000 - 2009-09-07)
19780 $this->form_action
= '';
19781 $this->form_enctype
= 'application/x-www-form-urlencoded';
19788 // draw border and background (if any)
19789 $this->drawHTMLTagBorder($parent, $xmax);
19790 if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
19791 $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
19792 // check for pagebreak
19793 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19794 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19795 $this->checkPageBreak($this->PageBreakTrigger +
1);
19797 if ((($pba == 'left') AND (((!$this->rtl
) AND (($this->page %
2) == 0)) OR (($this->rtl
) AND (($this->page %
2) != 0))))
19798 OR (($pba == 'right') AND (((!$this->rtl
) AND (($this->page %
2) != 0)) OR (($this->rtl
) AND (($this->page %
2) == 0))))) {
19799 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19800 $this->checkPageBreak($this->PageBreakTrigger +
1);
19803 $this->tmprtl
= false;
19808 * Add vertical spaces if needed.
19809 * @param $hbz (string) Distance between current y and line bottom.
19810 * @param $hb (string) The height of the break.
19811 * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
19812 * @param $firsttag (boolean) set to true when the tag is the first.
19813 * @param $lasttag (boolean) set to true when the tag is the last.
19816 protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
19818 $this->Ln(0, $cell);
19819 $this->htmlvspace
= 0;
19823 $this->Ln($hbz, $cell);
19824 $this->htmlvspace
= 0;
19827 if ($hb < $this->htmlvspace
) {
19830 $hd = $hb - $this->htmlvspace
;
19831 $this->htmlvspace
= $hb;
19833 $this->Ln(($hbz +
$hd), $cell);
19837 * Return the starting coordinates to draw an html border
19838 * @return array containing top-left border coordinates
19840 * @since 5.7.000 (2010-08-03)
19842 protected function getBorderStartPosition() {
19844 $xmax = $this->lMargin
;
19846 $xmax = $this->w
- $this->rMargin
;
19848 return array('page' => $this->page
, 'column' => $this->current_column
, 'x' => $this->x
, 'y' => $this->y
, 'xmax' => $xmax);
19852 * Draw an HTML block border and fill
19853 * @param $tag (array) array of tag properties.
19854 * @param $xmax (int) end X coordinate for border.
19856 * @since 5.7.000 (2010-08-03)
19858 protected function drawHTMLTagBorder($tag, $xmax) {
19859 if (!isset($tag['borderposition'])) {
19863 $prev_x = $this->x
;
19864 $prev_y = $this->y
;
19865 $prev_lasth = $this->lasth
;
19869 if (isset($tag['border']) AND !empty($tag['border'])) {
19870 // get border style
19871 $border = $tag['border'];
19872 if (!TCPDF_STATIC
::empty_string($this->thead
) AND (!$this->inthead
)) {
19873 // border for table header
19874 $border = TCPDF_STATIC
::getBorderMode($border, $position='middle', $this->opencell
);
19877 if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
19878 // get background color
19879 $old_bgcolor = $this->bgcolor
;
19880 $this->SetFillColorArray($tag['bgcolor']);
19883 if (!$border AND !$fill) {
19887 if (isset($tag['attribute']['cellspacing'])) {
19888 $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
19889 $cellspacing = array('H' => $clsp, 'V' => $clsp);
19890 } elseif (isset($tag['border-spacing'])) {
19891 $cellspacing = $tag['border-spacing'];
19893 $cellspacing = array('H' => 0, 'V' => 0);
19895 if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
19896 // draw the border externally respect the sqare edge.
19897 $border['mode'] = 'ext';
19900 if ($xmax >= $tag['borderposition']['x']) {
19901 $xmax = $tag['borderposition']['xmax'];
19903 $w = ($tag['borderposition']['x'] - $xmax);
19905 if ($xmax <= $tag['borderposition']['x']) {
19906 $xmax = $tag['borderposition']['xmax'];
19908 $w = ($xmax - $tag['borderposition']['x']);
19913 $w +
= $cellspacing['H'];
19914 $startpage = $tag['borderposition']['page'];
19915 $startcolumn = $tag['borderposition']['column'];
19916 $x = $tag['borderposition']['x'];
19917 $y = $tag['borderposition']['y'];
19918 $endpage = $this->page
;
19919 $starty = $tag['borderposition']['y'] - $cellspacing['V'];
19920 $currentY = $this->y
;
19922 // get latest column
19923 $endcolumn = $this->current_column
;
19924 if ($this->num_columns
== 0) {
19925 $this->num_columns
= 1;
19927 // get border modes
19928 $border_start = TCPDF_STATIC
::getBorderMode($border, $position='start', $this->opencell
);
19929 $border_end = TCPDF_STATIC
::getBorderMode($border, $position='end', $this->opencell
);
19930 $border_middle = TCPDF_STATIC
::getBorderMode($border, $position='middle', $this->opencell
);
19931 // temporary disable page regions
19932 $temp_page_regions = $this->page_regions
;
19933 $this->page_regions
= array();
19934 // design borders around HTML cells.
19935 for ($page = $startpage; $page <= $endpage; ++
$page) { // for each page
19937 $this->setPage($page);
19938 if ($this->num_columns
< 2) {
19939 // single-column mode
19941 $this->y
= $this->tMargin
;
19943 // account for margin changes
19944 if ($page > $startpage) {
19945 if (($this->rtl
) AND ($this->pagedim
[$page]['orm'] != $this->pagedim
[$startpage]['orm'])) {
19946 $this->x
-= ($this->pagedim
[$page]['orm'] - $this->pagedim
[$startpage]['orm']);
19947 } elseif ((!$this->rtl
) AND ($this->pagedim
[$page]['olm'] != $this->pagedim
[$startpage]['olm'])) {
19948 $this->x +
= ($this->pagedim
[$page]['olm'] - $this->pagedim
[$startpage]['olm']);
19951 if ($startpage == $endpage) {
19953 for ($column = $startcolumn; $column <= $endcolumn; ++
$column) { // for each column
19954 $this->selectColumn($column);
19955 if ($startcolumn == $endcolumn) { // single column
19956 $cborder = $border;
19957 $h = ($currentY - $y) +
$cellspacing['V'];
19958 $this->y
= $starty;
19959 } elseif ($column == $startcolumn) { // first column
19960 $cborder = $border_start;
19961 $this->y
= $starty;
19962 $h = $this->h
- $this->y
- $this->bMargin
;
19963 } elseif ($column == $endcolumn) { // end column
19964 $cborder = $border_end;
19965 $h = $currentY - $this->y
;
19966 } else { // middle column
19967 $cborder = $border_middle;
19968 $h = $this->h
- $this->y
- $this->bMargin
;
19970 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19971 } // end for each column
19972 } elseif ($page == $startpage) { // first page
19973 for ($column = $startcolumn; $column < $this->num_columns
; ++
$column) { // for each column
19974 $this->selectColumn($column);
19975 if ($column == $startcolumn) { // first column
19976 $cborder = $border_start;
19977 $this->y
= $starty;
19978 $h = $this->h
- $this->y
- $this->bMargin
;
19979 } else { // middle column
19980 $cborder = $border_middle;
19981 $h = $this->h
- $this->y
- $this->bMargin
;
19983 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19984 } // end for each column
19985 } elseif ($page == $endpage) { // last page
19986 for ($column = 0; $column <= $endcolumn; ++
$column) { // for each column
19987 $this->selectColumn($column);
19988 if ($column == $endcolumn) {
19990 $cborder = $border_end;
19991 $h = $currentY - $this->y
;
19994 $cborder = $border_middle;
19995 $h = $this->h
- $this->y
- $this->bMargin
;
19997 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19998 } // end for each column
19999 } else { // middle page
20000 for ($column = 0; $column < $this->num_columns
; ++
$column) { // for each column
20001 $this->selectColumn($column);
20002 $cborder = $border_middle;
20003 $h = $this->h
- $this->y
- $this->bMargin
;
20004 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20005 } // end for each column
20007 if ($cborder OR $fill) {
20008 $offsetlen = strlen($ccode);
20009 // draw border and fill
20010 if ($this->inxobj
) {
20011 // we are inside an XObject template
20012 if (end($this->xobjects
[$this->xobjid
]['transfmrk']) !== false) {
20013 $pagemarkkey = key($this->xobjects
[$this->xobjid
]['transfmrk']);
20014 $pagemark = $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey];
20015 $this->xobjects
[$this->xobjid
]['transfmrk'][$pagemarkkey] +
= $offsetlen;
20017 $pagemark = $this->xobjects
[$this->xobjid
]['intmrk'];
20018 $this->xobjects
[$this->xobjid
]['intmrk'] +
= $offsetlen;
20020 $pagebuff = $this->xobjects
[$this->xobjid
]['outdata'];
20021 $pstart = substr($pagebuff, 0, $pagemark);
20022 $pend = substr($pagebuff, $pagemark);
20023 $this->xobjects
[$this->xobjid
]['outdata'] = $pstart.$ccode.$pend;
20025 if (end($this->transfmrk
[$this->page
]) !== false) {
20026 $pagemarkkey = key($this->transfmrk
[$this->page
]);
20027 $pagemark = $this->transfmrk
[$this->page
][$pagemarkkey];
20028 } elseif ($this->InFooter
) {
20029 $pagemark = $this->footerpos
[$this->page
];
20031 $pagemark = $this->intmrk
[$this->page
];
20033 $pagebuff = $this->getPageBuffer($this->page
);
20034 $pstart = substr($pagebuff, 0, $pagemark);
20035 $pend = substr($pagebuff, $pagemark);
20036 $this->setPageBuffer($this->page
, $pstart.$ccode.$pend);
20037 $this->bordermrk
[$this->page
] +
= $offsetlen;
20038 $this->cntmrk
[$this->page
] +
= $offsetlen;
20041 } // end for each page
20042 // restore page regions
20043 $this->page_regions
= $temp_page_regions;
20044 if (isset($old_bgcolor)) {
20045 // restore background color
20046 $this->SetFillColorArray($old_bgcolor);
20048 // restore pointer position
20049 $this->x
= $prev_x;
20050 $this->y
= $prev_y;
20051 $this->lasth
= $prev_lasth;
20055 * Set the default bullet to be used as LI bullet symbol
20056 * @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')
20058 * @since 4.0.028 (2008-09-26)
20060 public function setLIsymbol($symbol='!') {
20061 // check for custom image symbol
20062 if (substr($symbol, 0, 4) == 'img|') {
20063 $this->lisymbol
= $symbol;
20066 $symbol = strtolower($symbol);
20067 $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');
20068 if (in_array($symbol, $valid_symbols)) {
20069 $this->lisymbol
= $symbol;
20071 $this->lisymbol
= '';
20076 * Set the booklet mode for double-sided pages.
20077 * @param $booklet (boolean) true set the booklet mode on, false otherwise.
20078 * @param $inner (float) Inner page margin.
20079 * @param $outer (float) Outer page margin.
20081 * @since 4.2.000 (2008-10-29)
20083 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
20084 $this->booklet
= $booklet;
20086 $this->lMargin
= $inner;
20089 $this->rMargin
= $outer;
20094 * Swap the left and right margins.
20095 * @param $reverse (boolean) if true swap left and right margins.
20097 * @since 4.2.000 (2008-10-29)
20099 protected function swapMargins($reverse=true) {
20101 // swap left and right margins
20102 $mtemp = $this->original_lMargin
;
20103 $this->original_lMargin
= $this->original_rMargin
;
20104 $this->original_rMargin
= $mtemp;
20105 $deltam = $this->original_lMargin
- $this->original_rMargin
;
20106 $this->lMargin +
= $deltam;
20107 $this->rMargin
-= $deltam;
20112 * Set the vertical spaces for HTML tags.
20113 * The array must have the following structure (example):
20114 * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1)));
20115 * The first array level contains the tag names,
20116 * the second level contains 0 for opening tags or 1 for closing tags,
20117 * the third level contains the vertical space unit (h) and the number spaces to add (n).
20118 * If the h parameter is not specified, default values are used.
20119 * @param $tagvs (array) array of tags and relative vertical spaces.
20121 * @since 4.2.001 (2008-10-30)
20123 public function setHtmlVSpace($tagvs) {
20124 $this->tagvspaces
= $tagvs;
20128 * Set custom width for list indentation.
20129 * @param $width (float) width of the indentation. Use negative value to disable it.
20131 * @since 4.2.007 (2008-11-12)
20133 public function setListIndentWidth($width) {
20134 return $this->customlistindent
= floatval($width);
20138 * Set the top/bottom cell sides to be open or closed when the cell cross the page.
20139 * @param $isopen (boolean) if true keeps the top/bottom border open for the cell sides that cross the page.
20141 * @since 4.2.010 (2008-11-14)
20143 public function setOpenCell($isopen) {
20144 $this->opencell
= $isopen;
20148 * Set the color and font style for HTML links.
20149 * @param $color (array) RGB array of colors
20150 * @param $fontstyle (string) additional font styles to add
20152 * @since 4.4.003 (2008-12-09)
20154 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
20155 $this->htmlLinkColorArray
= $color;
20156 $this->htmlLinkFontStyle
= $fontstyle;
20160 * Convert HTML string containing value and unit of measure to user's units or points.
20161 * @param $htmlval (string) String containing values and unit.
20162 * @param $refsize (string) Reference value in points.
20163 * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
20164 * @param $points (boolean) If true returns points, otherwise returns value in user's units.
20165 * @return float value in user's unit or point if $points=true
20167 * @since 4.4.004 (2008-12-10)
20169 public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
20170 $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
20179 if (in_array($defaultunit, $supportedunits)) {
20180 $unit = $defaultunit;
20182 if (is_numeric($htmlval)) {
20183 $value = floatval($htmlval);
20184 } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
20185 $value = floatval($mnum[1]);
20186 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
20187 if (in_array($munit[1], $supportedunits)) {
20195 $retval = (($value * $refsize) / 100);
20200 $retval = ($value * $refsize);
20203 // height of lower case 'x' (about half the font-size)
20205 $retval = ($value * ($refsize / 2));
20210 $retval = (($value * $this->dpi
) / $k);
20215 $retval = (($value / 2.54 * $this->dpi
) / $k);
20220 $retval = (($value / 25.4 * $this->dpi
) / $k);
20223 // one pica is 12 points
20225 $retval = (($value * 12) / $k);
20230 $retval = ($value / $k);
20235 $retval = $this->pixelsToUnits($value);
20237 $retval *= $this->k
;
20246 * Output an HTML list bullet or ordered item symbol
20247 * @param $listdepth (int) list nesting level
20248 * @param $listtype (string) type of list
20249 * @param $size (float) current font size
20251 * @since 4.4.004 (2008-12-10)
20253 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
20254 if ($this->state
!= 2) {
20259 $bgcolor = $this->bgcolor
;
20260 $color = $this->fgcolor
;
20261 $strokecolor = $this->strokecolor
;
20265 $lspace = $this->GetStringWidth(' ');
20266 if ($listtype == '^') {
20267 // special symbol used for avoid justification of rect bullet
20268 $this->lispacer
= '';
20270 } elseif ($listtype == '!') {
20271 // set default list type for unordered list
20272 $deftypes = array('disc', 'circle', 'square');
20273 $listtype = $deftypes[($listdepth - 1) %
3];
20274 } elseif ($listtype == '#') {
20275 // set default list type for ordered list
20276 $listtype = 'decimal';
20277 } elseif (substr($listtype, 0, 4) == 'img|') {
20278 // custom image type ('img|type|width|height|image.ext')
20279 $img = explode('|', $listtype);
20282 switch ($listtype) {
20289 $lspace +
= (2 * $r);
20291 $this->x +
= $lspace;
20293 $this->x
-= $lspace;
20295 $this->Circle(($this->x +
$r), ($this->y +
($this->lasth
/ 2)), $r, 0, 360, 'F', array(), $color, 8);
20300 $lspace +
= (2 * $r);
20302 $this->x +
= $lspace;
20304 $this->x
-= $lspace;
20306 $prev_line_style = $this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
;
20307 $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
20308 $this->Circle(($this->x +
$r), ($this->y +
($this->lasth
/ 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
20309 $this->_out($prev_line_style); // restore line settings
20316 $this->x +
= $lspace;
20318 $this->x
-= $lspace;
20320 $this->Rect($this->x
, ($this->y +
(($this->lasth
- $l) / 2)), $l, $l, 'F', array(), $color);
20324 // 1=>type, 2=>width, 3=>height, 4=>image.ext
20325 $lspace +
= $img[2];
20327 $this->x +
= $lspace;
20329 $this->x
-= $lspace;
20331 $imgtype = strtolower($img[1]);
20332 $prev_y = $this->y
;
20333 switch ($imgtype) {
20335 $this->ImageSVG($img[4], $this->x
, ($this->y +
(($this->lasth
- $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
20340 $this->ImageEps($img[4], $this->x
, ($this->y +
(($this->lasth
- $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
20344 $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);
20348 $this->y
= $prev_y;
20352 // $this->listcount[$this->listnum];
20356 $textitem = $this->listcount
[$this->listnum
];
20359 case 'decimal-leading-zero': {
20360 $textitem = sprintf('%02d', $this->listcount
[$this->listnum
]);
20364 case 'lower-roman': {
20365 $textitem = strtolower(TCPDF_STATIC
::intToRoman($this->listcount
[$this->listnum
]));
20369 case 'upper-roman': {
20370 $textitem = TCPDF_STATIC
::intToRoman($this->listcount
[$this->listnum
]);
20374 case 'lower-alpha':
20375 case 'lower-latin': {
20376 $textitem = chr(97 +
$this->listcount
[$this->listnum
] - 1);
20380 case 'upper-alpha':
20381 case 'upper-latin': {
20382 $textitem = chr(65 +
$this->listcount
[$this->listnum
] - 1);
20385 case 'lower-greek': {
20386 $textitem = TCPDF_FONTS
::unichr((945 +
$this->listcount
[$this->listnum
] - 1), $this->isunicode
);
20390 // Types to be implemented (special handling)
20400 case 'cjk-ideographic': {
20409 case 'hiragana-iroha': {
20412 case 'katakana-iroha': {
20417 $textitem = $this->listcount
[$this->listnum
];
20420 if (!TCPDF_STATIC
::empty_string($textitem)) {
20421 // Check whether we need a new page or new column
20422 $prev_y = $this->y
;
20423 $h = $this->getCellHeight($this->FontSize
);
20424 if ($this->checkPageBreak($h) OR ($this->y
< $prev_y)) {
20427 // print ordered item
20429 $textitem = '.'.$textitem;
20431 $textitem = $textitem.'.';
20433 $lspace +
= $this->GetStringWidth($textitem);
20435 $this->x +
= $lspace;
20437 $this->x
-= $lspace;
20439 $this->Write($this->lasth
, $textitem, '', false, '', false, 0, false);
20442 $this->lispacer
= '^';
20444 $this->SetFillColorArray($bgcolor);
20445 $this->SetDrawColorArray($strokecolor);
20446 $this->SettextColorArray($color);
20450 * Returns current graphic variables as array.
20451 * @return array of graphic variables
20453 * @since 4.2.010 (2008-11-14)
20455 protected function getGraphicVars() {
20457 'FontFamily' => $this->FontFamily
,
20458 'FontStyle' => $this->FontStyle
,
20459 'FontSizePt' => $this->FontSizePt
,
20460 'rMargin' => $this->rMargin
,
20461 'lMargin' => $this->lMargin
,
20462 'cell_padding' => $this->cell_padding
,
20463 'cell_margin' => $this->cell_margin
,
20464 'LineWidth' => $this->LineWidth
,
20465 'linestyleWidth' => $this->linestyleWidth
,
20466 'linestyleCap' => $this->linestyleCap
,
20467 'linestyleJoin' => $this->linestyleJoin
,
20468 'linestyleDash' => $this->linestyleDash
,
20469 'textrendermode' => $this->textrendermode
,
20470 'textstrokewidth' => $this->textstrokewidth
,
20471 'DrawColor' => $this->DrawColor
,
20472 'FillColor' => $this->FillColor
,
20473 'TextColor' => $this->TextColor
,
20474 'ColorFlag' => $this->ColorFlag
,
20475 'bgcolor' => $this->bgcolor
,
20476 'fgcolor' => $this->fgcolor
,
20477 'htmlvspace' => $this->htmlvspace
,
20478 'listindent' => $this->listindent
,
20479 'listindentlevel' => $this->listindentlevel
,
20480 'listnum' => $this->listnum
,
20481 'listordered' => $this->listordered
,
20482 'listcount' => $this->listcount
,
20483 'lispacer' => $this->lispacer
,
20484 'cell_height_ratio' => $this->cell_height_ratio
,
20485 'font_stretching' => $this->font_stretching
,
20486 'font_spacing' => $this->font_spacing
,
20487 'alpha' => $this->alpha
,
20489 'lasth' => $this->lasth
,
20490 'tMargin' => $this->tMargin
,
20491 'bMargin' => $this->bMargin
,
20492 'AutoPageBreak' => $this->AutoPageBreak
,
20493 'PageBreakTrigger' => $this->PageBreakTrigger
,
20498 'wPt' => $this->wPt
,
20499 'hPt' => $this->hPt
,
20500 'fwPt' => $this->fwPt
,
20501 'fhPt' => $this->fhPt
,
20502 'page' => $this->page
,
20503 'current_column' => $this->current_column
,
20504 'num_columns' => $this->num_columns
20510 * Set graphic variables.
20511 * @param $gvars (array) array of graphic variablesto restore
20512 * @param $extended (boolean) if true restore extended graphic variables
20514 * @since 4.2.010 (2008-11-14)
20516 protected function setGraphicVars($gvars, $extended=false) {
20517 if ($this->state
!= 2) {
20520 $this->FontFamily
= $gvars['FontFamily'];
20521 $this->FontStyle
= $gvars['FontStyle'];
20522 $this->FontSizePt
= $gvars['FontSizePt'];
20523 $this->rMargin
= $gvars['rMargin'];
20524 $this->lMargin
= $gvars['lMargin'];
20525 $this->cell_padding
= $gvars['cell_padding'];
20526 $this->cell_margin
= $gvars['cell_margin'];
20527 $this->LineWidth
= $gvars['LineWidth'];
20528 $this->linestyleWidth
= $gvars['linestyleWidth'];
20529 $this->linestyleCap
= $gvars['linestyleCap'];
20530 $this->linestyleJoin
= $gvars['linestyleJoin'];
20531 $this->linestyleDash
= $gvars['linestyleDash'];
20532 $this->textrendermode
= $gvars['textrendermode'];
20533 $this->textstrokewidth
= $gvars['textstrokewidth'];
20534 $this->DrawColor
= $gvars['DrawColor'];
20535 $this->FillColor
= $gvars['FillColor'];
20536 $this->TextColor
= $gvars['TextColor'];
20537 $this->ColorFlag
= $gvars['ColorFlag'];
20538 $this->bgcolor
= $gvars['bgcolor'];
20539 $this->fgcolor
= $gvars['fgcolor'];
20540 $this->htmlvspace
= $gvars['htmlvspace'];
20541 $this->listindent
= $gvars['listindent'];
20542 $this->listindentlevel
= $gvars['listindentlevel'];
20543 $this->listnum
= $gvars['listnum'];
20544 $this->listordered
= $gvars['listordered'];
20545 $this->listcount
= $gvars['listcount'];
20546 $this->lispacer
= $gvars['lispacer'];
20547 $this->cell_height_ratio
= $gvars['cell_height_ratio'];
20548 $this->font_stretching
= $gvars['font_stretching'];
20549 $this->font_spacing
= $gvars['font_spacing'];
20550 $this->alpha
= $gvars['alpha'];
20552 // restore extended values
20553 $this->lasth
= $gvars['lasth'];
20554 $this->tMargin
= $gvars['tMargin'];
20555 $this->bMargin
= $gvars['bMargin'];
20556 $this->AutoPageBreak
= $gvars['AutoPageBreak'];
20557 $this->PageBreakTrigger
= $gvars['PageBreakTrigger'];
20558 $this->x
= $gvars['x'];
20559 $this->y
= $gvars['y'];
20560 $this->w
= $gvars['w'];
20561 $this->h
= $gvars['h'];
20562 $this->wPt
= $gvars['wPt'];
20563 $this->hPt
= $gvars['hPt'];
20564 $this->fwPt
= $gvars['fwPt'];
20565 $this->fhPt
= $gvars['fhPt'];
20566 $this->page
= $gvars['page'];
20567 $this->current_column
= $gvars['current_column'];
20568 $this->num_columns
= $gvars['num_columns'];
20570 $this->_out(''.$this->linestyleWidth
.' '.$this->linestyleCap
.' '.$this->linestyleJoin
.' '.$this->linestyleDash
.' '.$this->DrawColor
.' '.$this->FillColor
.'');
20571 if (!TCPDF_STATIC
::empty_string($this->FontFamily
)) {
20572 $this->SetFont($this->FontFamily
, $this->FontStyle
, $this->FontSizePt
);
20577 * Outputs the "save graphics state" operator 'q'
20580 protected function _outSaveGraphicsState() {
20585 * Outputs the "restore graphics state" operator 'Q'
20588 protected function _outRestoreGraphicsState() {
20593 * Writes data to a temporary file on filesystem.
20594 * @param $filename (string) file name
20595 * @param $data (mixed) data to write on file
20596 * @param $append (boolean) if true append data, false replace.
20597 * @since 4.5.000 (2008-12-31)
20600 protected function writeDiskCache($filename, $data, $append=false) {
20606 $f = @fopen
($filename, $fmode);
20608 $this->Error('Unable to write cache file: '.$filename);
20613 // update file length (needed for transactions)
20614 if (!isset($this->cache_file_length
['_'.$filename])) {
20615 $this->cache_file_length
['_'.$filename] = strlen($data);
20617 $this->cache_file_length
['_'.$filename] +
= strlen($data);
20622 * Read data from a temporary file on filesystem.
20623 * @param $filename (string) file name
20624 * @return mixed retrieved data
20625 * @since 4.5.000 (2008-12-31)
20628 protected function readDiskCache($filename) {
20629 return file_get_contents($filename);
20633 * Set buffer content (always append data).
20634 * @param $data (string) data
20636 * @since 4.5.000 (2009-01-02)
20638 protected function setBuffer($data) {
20639 $this->bufferlen +
= strlen($data);
20640 if ($this->diskcache
) {
20641 if (!isset($this->buffer
) OR TCPDF_STATIC
::empty_string($this->buffer
)) {
20642 $this->buffer
= TCPDF_STATIC
::getObjFilename('buf');
20644 $this->writeDiskCache($this->buffer
, $data, true);
20646 $this->buffer
.= $data;
20651 * Replace the buffer content
20652 * @param $data (string) data
20654 * @since 5.5.000 (2010-06-22)
20656 protected function replaceBuffer($data) {
20657 $this->bufferlen
= strlen($data);
20658 if ($this->diskcache
) {
20659 if (!isset($this->buffer
) OR TCPDF_STATIC
::empty_string($this->buffer
)) {
20660 $this->buffer
= TCPDF_STATIC
::getObjFilename('buf');
20662 $this->writeDiskCache($this->buffer
, $data, false);
20664 $this->buffer
= $data;
20669 * Get buffer content.
20670 * @return string buffer content
20672 * @since 4.5.000 (2009-01-02)
20674 protected function getBuffer() {
20675 if ($this->diskcache
) {
20676 return $this->readDiskCache($this->buffer
);
20678 return $this->buffer
;
20683 * Set page buffer content.
20684 * @param $page (int) page number
20685 * @param $data (string) page data
20686 * @param $append (boolean) if true append data, false replace.
20688 * @since 4.5.000 (2008-12-31)
20690 protected function setPageBuffer($page, $data, $append=false) {
20691 if ($this->diskcache
) {
20692 if (!isset($this->pages
[$page])) {
20693 $this->pages
[$page] = TCPDF_STATIC
::getObjFilename('page');
20695 $this->writeDiskCache($this->pages
[$page], $data, $append);
20698 $this->pages
[$page] .= $data;
20700 $this->pages
[$page] = $data;
20703 if ($append AND isset($this->pagelen
[$page])) {
20704 $this->pagelen
[$page] +
= strlen($data);
20706 $this->pagelen
[$page] = strlen($data);
20711 * Get page buffer content.
20712 * @param $page (int) page number
20713 * @return string page buffer content or false in case of error
20715 * @since 4.5.000 (2008-12-31)
20717 protected function getPageBuffer($page) {
20718 if ($this->diskcache
) {
20719 return $this->readDiskCache($this->pages
[$page]);
20720 } elseif (isset($this->pages
[$page])) {
20721 return $this->pages
[$page];
20727 * Set image buffer content.
20728 * @param $image (string) image key
20729 * @param $data (array) image data
20730 * @return int image index number
20732 * @since 4.5.000 (2008-12-31)
20734 protected function setImageBuffer($image, $data) {
20735 if (($data['i'] = array_search($image, $this->imagekeys
)) === FALSE) {
20736 $this->imagekeys
[$this->numimages
] = $image;
20737 $data['i'] = $this->numimages
;
20738 ++
$this->numimages
;
20740 if ($this->diskcache
) {
20741 if (!isset($this->images
[$image])) {
20742 $this->images
[$image] = TCPDF_STATIC
::getObjFilename('img');
20744 $this->writeDiskCache($this->images
[$image], serialize($data));
20746 $this->images
[$image] = $data;
20752 * Set image buffer content for a specified sub-key.
20753 * @param $image (string) image key
20754 * @param $key (string) image sub-key
20755 * @param $data (array) image data
20757 * @since 4.5.000 (2008-12-31)
20759 protected function setImageSubBuffer($image, $key, $data) {
20760 if (!isset($this->images
[$image])) {
20761 $this->setImageBuffer($image, array());
20763 if ($this->diskcache
) {
20764 $tmpimg = $this->getImageBuffer($image);
20765 $tmpimg[$key] = $data;
20766 $this->writeDiskCache($this->images
[$image], serialize($tmpimg));
20768 $this->images
[$image][$key] = $data;
20773 * Get image buffer content.
20774 * @param $image (string) image key
20775 * @return string image buffer content or false in case of error
20777 * @since 4.5.000 (2008-12-31)
20779 protected function getImageBuffer($image) {
20780 if ($this->diskcache
AND isset($this->images
[$image])) {
20781 return unserialize($this->readDiskCache($this->images
[$image]));
20782 } elseif (isset($this->images
[$image])) {
20783 return $this->images
[$image];
20789 * Set font buffer content.
20790 * @param $font (string) font key
20791 * @param $data (array) font data
20793 * @since 4.5.000 (2009-01-02)
20795 protected function setFontBuffer($font, $data) {
20796 if ($this->diskcache
) {
20797 if (!isset($this->fonts
[$font])) {
20798 $this->fonts
[$font] = TCPDF_STATIC
::getObjFilename('font');
20800 $this->writeDiskCache($this->fonts
[$font], serialize($data));
20802 $this->fonts
[$font] = $data;
20804 if (!in_array($font, $this->fontkeys
)) {
20805 $this->fontkeys
[] = $font;
20806 // store object ID for current font
20808 $this->font_obj_ids
[$font] = $this->n
;
20809 $this->setFontSubBuffer($font, 'n', $this->n
);
20814 * Set font buffer content.
20815 * @param $font (string) font key
20816 * @param $key (string) font sub-key
20817 * @param $data (array) font data
20819 * @since 4.5.000 (2009-01-02)
20821 protected function setFontSubBuffer($font, $key, $data) {
20822 if (!isset($this->fonts
[$font])) {
20823 $this->setFontBuffer($font, array());
20825 if ($this->diskcache
) {
20826 $tmpfont = $this->getFontBuffer($font);
20827 $tmpfont[$key] = $data;
20828 $this->writeDiskCache($this->fonts
[$font], serialize($tmpfont));
20830 $this->fonts
[$font][$key] = $data;
20835 * Get font buffer content.
20836 * @param $font (string) font key
20837 * @return string font buffer content or false in case of error
20839 * @since 4.5.000 (2009-01-02)
20841 protected function getFontBuffer($font) {
20842 if ($this->diskcache
AND isset($this->fonts
[$font])) {
20843 return unserialize($this->readDiskCache($this->fonts
[$font]));
20844 } elseif (isset($this->fonts
[$font])) {
20845 return $this->fonts
[$font];
20851 * Move a page to a previous position.
20852 * @param $frompage (int) number of the source page
20853 * @param $topage (int) number of the destination page (must be less than $frompage)
20854 * @return true in case of success, false in case of error.
20856 * @since 4.5.000 (2009-01-02)
20858 public function movePage($frompage, $topage) {
20859 if (($frompage > $this->numpages
) OR ($frompage <= $topage)) {
20862 if ($frompage == $this->page
) {
20863 // close the page before moving it
20866 // move all page-related states
20867 $tmppage = $this->getPageBuffer($frompage);
20868 $tmppagedim = $this->pagedim
[$frompage];
20869 $tmppagelen = $this->pagelen
[$frompage];
20870 $tmpintmrk = $this->intmrk
[$frompage];
20871 $tmpbordermrk = $this->bordermrk
[$frompage];
20872 $tmpcntmrk = $this->cntmrk
[$frompage];
20873 $tmppageobjects = $this->pageobjects
[$frompage];
20874 if (isset($this->footerpos
[$frompage])) {
20875 $tmpfooterpos = $this->footerpos
[$frompage];
20877 if (isset($this->footerlen
[$frompage])) {
20878 $tmpfooterlen = $this->footerlen
[$frompage];
20880 if (isset($this->transfmrk
[$frompage])) {
20881 $tmptransfmrk = $this->transfmrk
[$frompage];
20883 if (isset($this->PageAnnots
[$frompage])) {
20884 $tmpannots = $this->PageAnnots
[$frompage];
20886 if (isset($this->newpagegroup
) AND !empty($this->newpagegroup
)) {
20887 for ($i = $frompage; $i > $topage; --$i) {
20888 if (isset($this->newpagegroup
[$i]) AND (($i +
$this->pagegroups
[$this->newpagegroup
[$i]]) > $frompage)) {
20889 --$this->pagegroups
[$this->newpagegroup
[$i]];
20893 for ($i = $topage; $i > 0; --$i) {
20894 if (isset($this->newpagegroup
[$i]) AND (($i +
$this->pagegroups
[$this->newpagegroup
[$i]]) > $topage)) {
20895 ++
$this->pagegroups
[$this->newpagegroup
[$i]];
20900 for ($i = $frompage; $i > $topage; --$i) {
20902 // shift pages down
20903 $this->setPageBuffer($i, $this->getPageBuffer($j));
20904 $this->pagedim
[$i] = $this->pagedim
[$j];
20905 $this->pagelen
[$i] = $this->pagelen
[$j];
20906 $this->intmrk
[$i] = $this->intmrk
[$j];
20907 $this->bordermrk
[$i] = $this->bordermrk
[$j];
20908 $this->cntmrk
[$i] = $this->cntmrk
[$j];
20909 $this->pageobjects
[$i] = $this->pageobjects
[$j];
20910 if (isset($this->footerpos
[$j])) {
20911 $this->footerpos
[$i] = $this->footerpos
[$j];
20912 } elseif (isset($this->footerpos
[$i])) {
20913 unset($this->footerpos
[$i]);
20915 if (isset($this->footerlen
[$j])) {
20916 $this->footerlen
[$i] = $this->footerlen
[$j];
20917 } elseif (isset($this->footerlen
[$i])) {
20918 unset($this->footerlen
[$i]);
20920 if (isset($this->transfmrk
[$j])) {
20921 $this->transfmrk
[$i] = $this->transfmrk
[$j];
20922 } elseif (isset($this->transfmrk
[$i])) {
20923 unset($this->transfmrk
[$i]);
20925 if (isset($this->PageAnnots
[$j])) {
20926 $this->PageAnnots
[$i] = $this->PageAnnots
[$j];
20927 } elseif (isset($this->PageAnnots
[$i])) {
20928 unset($this->PageAnnots
[$i]);
20930 if (isset($this->newpagegroup
[$j])) {
20931 $this->newpagegroup
[$i] = $this->newpagegroup
[$j];
20932 unset($this->newpagegroup
[$j]);
20934 if ($this->currpagegroup
== $j) {
20935 $this->currpagegroup
= $i;
20938 $this->setPageBuffer($topage, $tmppage);
20939 $this->pagedim
[$topage] = $tmppagedim;
20940 $this->pagelen
[$topage] = $tmppagelen;
20941 $this->intmrk
[$topage] = $tmpintmrk;
20942 $this->bordermrk
[$topage] = $tmpbordermrk;
20943 $this->cntmrk
[$topage] = $tmpcntmrk;
20944 $this->pageobjects
[$topage] = $tmppageobjects;
20945 if (isset($tmpfooterpos)) {
20946 $this->footerpos
[$topage] = $tmpfooterpos;
20947 } elseif (isset($this->footerpos
[$topage])) {
20948 unset($this->footerpos
[$topage]);
20950 if (isset($tmpfooterlen)) {
20951 $this->footerlen
[$topage] = $tmpfooterlen;
20952 } elseif (isset($this->footerlen
[$topage])) {
20953 unset($this->footerlen
[$topage]);
20955 if (isset($tmptransfmrk)) {
20956 $this->transfmrk
[$topage] = $tmptransfmrk;
20957 } elseif (isset($this->transfmrk
[$topage])) {
20958 unset($this->transfmrk
[$topage]);
20960 if (isset($tmpannots)) {
20961 $this->PageAnnots
[$topage] = $tmpannots;
20962 } elseif (isset($this->PageAnnots
[$topage])) {
20963 unset($this->PageAnnots
[$topage]);
20966 $tmpoutlines = $this->outlines
;
20967 foreach ($tmpoutlines as $key => $outline) {
20968 if (!$outline['f']) {
20969 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
20970 $this->outlines
[$key]['p'] = ($outline['p'] +
1);
20971 } elseif ($outline['p'] == $frompage) {
20972 $this->outlines
[$key]['p'] = $topage;
20977 $tmpdests = $this->dests
;
20978 foreach ($tmpdests as $key => $dest) {
20980 if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
20981 $this->dests
[$key]['p'] = ($dest['p'] +
1);
20982 } elseif ($dest['p'] == $frompage) {
20983 $this->dests
[$key]['p'] = $topage;
20988 $tmplinks = $this->links
;
20989 foreach ($tmplinks as $key => $link) {
20991 if (($link['p'] >= $topage) AND ($link['p'] < $frompage)) {
20992 $this->links
[$key]['p'] = ($link['p'] +
1);
20993 } elseif ($link['p'] == $frompage) {
20994 $this->links
[$key]['p'] = $topage;
20998 // adjust javascript
20999 $jfrompage = $frompage;
21000 $jtopage = $topage;
21001 if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript
, $pamatch) > 0) {
21002 foreach($pamatch[0] as $pk => $pmatch) {
21003 $pagenum = intval($pamatch[3][$pk]) +
1;
21004 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
21005 $newpage = ($pagenum +
1);
21006 } elseif ($pagenum == $jfrompage) {
21007 $newpage = $jtopage;
21009 $newpage = $pagenum;
21012 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21013 $this->javascript
= str_replace($pmatch, $newjs, $this->javascript
);
21017 // return to last page
21018 $this->lastPage(true);
21023 * Remove the specified page.
21024 * @param $page (int) page to remove
21025 * @return true in case of success, false in case of error.
21027 * @since 4.6.004 (2009-04-23)
21029 public function deletePage($page) {
21030 if (($page < 1) OR ($page > $this->numpages
)) {
21033 // delete current page
21034 unset($this->pages
[$page]);
21035 unset($this->pagedim
[$page]);
21036 unset($this->pagelen
[$page]);
21037 unset($this->intmrk
[$page]);
21038 unset($this->bordermrk
[$page]);
21039 unset($this->cntmrk
[$page]);
21040 foreach ($this->pageobjects
[$page] as $oid) {
21041 if (isset($this->offsets
[$oid])){
21042 unset($this->offsets
[$oid]);
21045 unset($this->pageobjects
[$page]);
21046 if (isset($this->footerpos
[$page])) {
21047 unset($this->footerpos
[$page]);
21049 if (isset($this->footerlen
[$page])) {
21050 unset($this->footerlen
[$page]);
21052 if (isset($this->transfmrk
[$page])) {
21053 unset($this->transfmrk
[$page]);
21055 if (isset($this->PageAnnots
[$page])) {
21056 unset($this->PageAnnots
[$page]);
21058 if (isset($this->newpagegroup
) AND !empty($this->newpagegroup
)) {
21059 for ($i = $page; $i > 0; --$i) {
21060 if (isset($this->newpagegroup
[$i]) AND (($i +
$this->pagegroups
[$this->newpagegroup
[$i]]) > $page)) {
21061 --$this->pagegroups
[$this->newpagegroup
[$i]];
21066 if (isset($this->pageopen
[$page])) {
21067 unset($this->pageopen
[$page]);
21069 if ($page < $this->numpages
) {
21070 // update remaining pages
21071 for ($i = $page; $i < $this->numpages
; ++
$i) {
21074 $this->setPageBuffer($i, $this->getPageBuffer($j));
21075 $this->pagedim
[$i] = $this->pagedim
[$j];
21076 $this->pagelen
[$i] = $this->pagelen
[$j];
21077 $this->intmrk
[$i] = $this->intmrk
[$j];
21078 $this->bordermrk
[$i] = $this->bordermrk
[$j];
21079 $this->cntmrk
[$i] = $this->cntmrk
[$j];
21080 $this->pageobjects
[$i] = $this->pageobjects
[$j];
21081 if (isset($this->footerpos
[$j])) {
21082 $this->footerpos
[$i] = $this->footerpos
[$j];
21083 } elseif (isset($this->footerpos
[$i])) {
21084 unset($this->footerpos
[$i]);
21086 if (isset($this->footerlen
[$j])) {
21087 $this->footerlen
[$i] = $this->footerlen
[$j];
21088 } elseif (isset($this->footerlen
[$i])) {
21089 unset($this->footerlen
[$i]);
21091 if (isset($this->transfmrk
[$j])) {
21092 $this->transfmrk
[$i] = $this->transfmrk
[$j];
21093 } elseif (isset($this->transfmrk
[$i])) {
21094 unset($this->transfmrk
[$i]);
21096 if (isset($this->PageAnnots
[$j])) {
21097 $this->PageAnnots
[$i] = $this->PageAnnots
[$j];
21098 } elseif (isset($this->PageAnnots
[$i])) {
21099 unset($this->PageAnnots
[$i]);
21101 if (isset($this->newpagegroup
[$j])) {
21102 $this->newpagegroup
[$i] = $this->newpagegroup
[$j];
21103 unset($this->newpagegroup
[$j]);
21105 if ($this->currpagegroup
== $j) {
21106 $this->currpagegroup
= $i;
21108 if (isset($this->pageopen
[$j])) {
21109 $this->pageopen
[$i] = $this->pageopen
[$j];
21110 } elseif (isset($this->pageopen
[$i])) {
21111 unset($this->pageopen
[$i]);
21114 // remove last page
21115 unset($this->pages
[$this->numpages
]);
21116 unset($this->pagedim
[$this->numpages
]);
21117 unset($this->pagelen
[$this->numpages
]);
21118 unset($this->intmrk
[$this->numpages
]);
21119 unset($this->bordermrk
[$this->numpages
]);
21120 unset($this->cntmrk
[$this->numpages
]);
21121 foreach ($this->pageobjects
[$this->numpages
] as $oid) {
21122 if (isset($this->offsets
[$oid])){
21123 unset($this->offsets
[$oid]);
21126 unset($this->pageobjects
[$this->numpages
]);
21127 if (isset($this->footerpos
[$this->numpages
])) {
21128 unset($this->footerpos
[$this->numpages
]);
21130 if (isset($this->footerlen
[$this->numpages
])) {
21131 unset($this->footerlen
[$this->numpages
]);
21133 if (isset($this->transfmrk
[$this->numpages
])) {
21134 unset($this->transfmrk
[$this->numpages
]);
21136 if (isset($this->PageAnnots
[$this->numpages
])) {
21137 unset($this->PageAnnots
[$this->numpages
]);
21139 if (isset($this->newpagegroup
[$this->numpages
])) {
21140 unset($this->newpagegroup
[$this->numpages
]);
21142 if ($this->currpagegroup
== $this->numpages
) {
21143 $this->currpagegroup
= ($this->numpages
- 1);
21145 if (isset($this->pagegroups
[$this->numpages
])) {
21146 unset($this->pagegroups
[$this->numpages
]);
21148 if (isset($this->pageopen
[$this->numpages
])) {
21149 unset($this->pageopen
[$this->numpages
]);
21153 $this->page
= $this->numpages
;
21155 $tmpoutlines = $this->outlines
;
21156 foreach ($tmpoutlines as $key => $outline) {
21157 if (!$outline['f']) {
21158 if ($outline['p'] > $page) {
21159 $this->outlines
[$key]['p'] = $outline['p'] - 1;
21160 } elseif ($outline['p'] == $page) {
21161 unset($this->outlines
[$key]);
21166 $tmpdests = $this->dests
;
21167 foreach ($tmpdests as $key => $dest) {
21169 if ($dest['p'] > $page) {
21170 $this->dests
[$key]['p'] = $dest['p'] - 1;
21171 } elseif ($dest['p'] == $page) {
21172 unset($this->dests
[$key]);
21177 $tmplinks = $this->links
;
21178 foreach ($tmplinks as $key => $link) {
21180 if ($link['p'] > $page) {
21181 $this->links
[$key]['p'] = $link['p'] - 1;
21182 } elseif ($link['p'] == $page) {
21183 unset($this->links
[$key]);
21187 // adjust javascript
21189 if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript
, $pamatch) > 0) {
21190 foreach($pamatch[0] as $pk => $pmatch) {
21191 $pagenum = intval($pamatch[3][$pk]) +
1;
21192 if ($pagenum >= $jpage) {
21193 $newpage = ($pagenum - 1);
21194 } elseif ($pagenum == $jpage) {
21197 $newpage = $pagenum;
21200 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21201 $this->javascript
= str_replace($pmatch, $newjs, $this->javascript
);
21205 // return to last page
21206 if ($this->numpages
> 0) {
21207 $this->lastPage(true);
21213 * Clone the specified page to a new page.
21214 * @param $page (int) number of page to copy (0 = current page)
21215 * @return true in case of success, false in case of error.
21217 * @since 4.9.015 (2010-04-20)
21219 public function copyPage($page=0) {
21222 $page = $this->page
;
21224 if (($page < 1) OR ($page > $this->numpages
)) {
21227 // close the last page
21229 // copy all page-related states
21231 $this->page
= $this->numpages
;
21232 $this->setPageBuffer($this->page
, $this->getPageBuffer($page));
21233 $this->pagedim
[$this->page
] = $this->pagedim
[$page];
21234 $this->pagelen
[$this->page
] = $this->pagelen
[$page];
21235 $this->intmrk
[$this->page
] = $this->intmrk
[$page];
21236 $this->bordermrk
[$this->page
] = $this->bordermrk
[$page];
21237 $this->cntmrk
[$this->page
] = $this->cntmrk
[$page];
21238 $this->pageobjects
[$this->page
] = $this->pageobjects
[$page];
21239 $this->pageopen
[$this->page
] = false;
21240 if (isset($this->footerpos
[$page])) {
21241 $this->footerpos
[$this->page
] = $this->footerpos
[$page];
21243 if (isset($this->footerlen
[$page])) {
21244 $this->footerlen
[$this->page
] = $this->footerlen
[$page];
21246 if (isset($this->transfmrk
[$page])) {
21247 $this->transfmrk
[$this->page
] = $this->transfmrk
[$page];
21249 if (isset($this->PageAnnots
[$page])) {
21250 $this->PageAnnots
[$this->page
] = $this->PageAnnots
[$page];
21252 if (isset($this->newpagegroup
[$page])) {
21253 // start a new group
21254 $this->newpagegroup
[$this->page
] = sizeof($this->newpagegroup
) +
1;
21255 $this->currpagegroup
= $this->newpagegroup
[$this->page
];
21256 $this->pagegroups
[$this->currpagegroup
] = 1;
21257 } elseif (isset($this->currpagegroup
) AND ($this->currpagegroup
> 0)) {
21258 ++
$this->pagegroups
[$this->currpagegroup
];
21261 $tmpoutlines = $this->outlines
;
21262 foreach ($tmpoutlines as $key => $outline) {
21263 if ($outline['p'] == $page) {
21264 $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']);
21268 $tmplinks = $this->links
;
21269 foreach ($tmplinks as $key => $link) {
21270 if ($link['p'] == $page) {
21271 $this->links
[] = array('p' => $this->page
, 'y' => $link['y'], 'f' => $link['f']);
21274 // return to last page
21275 $this->lastPage(true);
21280 * Output a Table of Content Index (TOC).
21281 * This method must be called after all Bookmarks were set.
21282 * Before calling this method you have to open the page using the addTOCPage() method.
21283 * After calling this method you have to call endTOCPage() to close the TOC page.
21284 * You can override this method to achieve different styles.
21285 * @param $page (int) page number where this TOC should be inserted (leave empty for current page).
21286 * @param $numbersfont (string) set the font for page numbers (please use monospaced font for better alignment).
21287 * @param $filler (string) string used to fill the space between text and page number.
21288 * @param $toc_name (string) name to use for TOC bookmark.
21289 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
21290 * @param $color (array) RGB color array for bookmark title (values from 0 to 255).
21292 * @author Nicola Asuni
21293 * @since 4.5.000 (2009-01-02)
21294 * @see addTOCPage(), endTOCPage(), addHTMLTOC()
21296 public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
21297 $fontsize = $this->FontSizePt
;
21298 $fontfamily = $this->FontFamily
;
21299 $fontstyle = $this->FontStyle
;
21300 $w = $this->w
- $this->lMargin
- $this->rMargin
;
21301 $spacer = $this->GetStringWidth(chr(32)) * 4;
21302 $lmargin = $this->lMargin
;
21303 $rmargin = $this->rMargin
;
21304 $x_start = $this->GetX();
21305 $page_first = $this->page
;
21306 $current_page = $this->page
;
21307 $page_fill_start = false;
21308 $page_fill_end = false;
21309 $current_column = $this->current_column
;
21310 if (TCPDF_STATIC
::empty_string($numbersfont)) {
21311 $numbersfont = $this->default_monospaced_font
;
21313 if (TCPDF_STATIC
::empty_string($filler)) {
21316 if (TCPDF_STATIC
::empty_string($page)) {
21324 $this->SetFont($numbersfont, $fontstyle, $fontsize);
21325 $numwidth = $this->GetStringWidth('00000');
21326 $maxpage = 0; //used for pages on attached documents
21327 foreach ($this->outlines
as $key => $outline) {
21328 // check for extra pages (used for attachments)
21329 if (($this->page
> $page_first) AND ($outline['p'] >= $this->numpages
)) {
21330 $outline['p'] +
= ($this->page
- $page_first);
21339 if ($outline['l'] == 0) {
21340 $this->SetFont($fontfamily, $outline['s'].'B', $fontsize);
21342 $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']);
21344 $this->SetTextColorArray($outline['c']);
21345 // check for page break
21346 $this->checkPageBreak(2 * $this->getCellHeight($this->FontSize
));
21347 // set margins and X position
21348 if (($this->page
== $current_page) AND ($this->current_column
== $current_column)) {
21349 $this->lMargin
= $lmargin;
21350 $this->rMargin
= $rmargin;
21352 if ($this->current_column
!= $current_column) {
21354 $x_start = $this->w
- $this->columns
[$this->current_column
]['x'];
21356 $x_start = $this->columns
[$this->current_column
]['x'];
21359 $lmargin = $this->lMargin
;
21360 $rmargin = $this->rMargin
;
21361 $current_page = $this->page
;
21362 $current_column = $this->current_column
;
21364 $this->SetX($x_start);
21365 $indent = ($spacer * $outline['l']);
21367 $this->x
-= $indent;
21368 $this->rMargin
= $this->w
- $this->x
;
21370 $this->x +
= $indent;
21371 $this->lMargin
= $this->x
;
21373 $link = $this->AddLink();
21374 $this->SetLink($link, $outline['y'], $outline['p']);
21377 $txt = ' '.$outline['t'];
21379 $txt = $outline['t'].' ';
21381 $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
21383 $tw = $this->x
- $this->lMargin
;
21385 $tw = $this->w
- $this->rMargin
- $this->x
;
21387 $this->SetFont($numbersfont, $fontstyle, $fontsize);
21388 if (TCPDF_STATIC
::empty_string($page)) {
21389 $pagenum = $outline['p'];
21391 // placemark to be replaced with the correct number
21392 $pagenum = '{#'.($outline['p']).'}';
21393 if ($this->isUnicodeFont()) {
21394 $pagenum = '{'.$pagenum.'}';
21396 $maxpage = max($maxpage, $outline['p']);
21398 $fw = ($tw - $this->GetStringWidth($pagenum.$filler));
21399 $wfiller = $this->GetStringWidth($filler);
21400 if ($wfiller > 0) {
21401 $numfills = floor($fw / $wfiller);
21405 if ($numfills > 0) {
21406 $rowfill = str_repeat($filler, $numfills);
21411 $pagenum = $pagenum.$gap.$rowfill;
21413 $pagenum = $rowfill.$gap.$pagenum;
21415 // write the number
21416 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
21418 $page_last = $this->getPage();
21419 $numpages = ($page_last - $page_first +
1);
21420 // account for booklet mode
21421 if ($this->booklet
) {
21422 // check if a blank page is required before TOC
21423 $page_fill_start = ((($page_first %
2) == 0) XOR (($page %
2) == 0));
21424 $page_fill_end = (!((($numpages %
2) == 0) XOR ($page_fill_start)));
21425 if ($page_fill_start) {
21426 // add a page at the end (to be moved before TOC)
21431 if ($page_fill_end) {
21432 // add a page at the end
21438 $maxpage = max($maxpage, $page_last);
21439 if (!TCPDF_STATIC
::empty_string($page)) {
21440 for ($p = $page_first; $p <= $page_last; ++
$p) {
21442 $temppage = $this->getPageBuffer($p);
21443 for ($n = 1; $n <= $maxpage; ++
$n) {
21444 // update page numbers
21446 // get page number aliases
21447 $pnalias = $this->getInternalPageNumberAliases($a);
21448 // calculate replacement number
21449 if (($n >= $page) AND ($n <= $this->numpages
)) {
21450 $np = $n +
$numpages;
21454 $na = TCPDF_STATIC
::formatTOCPageNumber(($this->starting_page_number +
$np - 1));
21455 $nu = TCPDF_FONTS
::UTF8ToUTF16BE($na, false, $this->isunicode
, $this->CurrentFont
);
21456 // replace aliases with numbers
21457 foreach ($pnalias['u'] as $u) {
21458 $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
21460 $nr = $nu.TCPDF_FONTS
::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode
, $this->CurrentFont
);
21462 $nr = TCPDF_FONTS
::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode
, $this->CurrentFont
).$nu;
21464 $temppage = str_replace($u, $nr, $temppage);
21466 foreach ($pnalias['a'] as $a) {
21467 $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
21469 $nr = $na.' '.$sfill;
21471 $nr = $sfill.' '.$na;
21473 $temppage = str_replace($a, $nr, $temppage);
21477 $this->setPageBuffer($p, $temppage);
21480 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21481 if ($page_fill_start) {
21482 $this->movePage($page_last, $page_first);
21484 for ($i = 0; $i < $numpages; ++
$i) {
21485 $this->movePage($page_last, $page);
21491 * Output a Table Of Content Index (TOC) using HTML templates.
21492 * This method must be called after all Bookmarks were set.
21493 * Before calling this method you have to open the page using the addTOCPage() method.
21494 * After calling this method you have to call endTOCPage() to close the TOC page.
21495 * @param $page (int) page number where this TOC should be inserted (leave empty for current page).
21496 * @param $toc_name (string) name to use for TOC bookmark.
21497 * @param $templates (array) array of html templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number.
21498 * @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)
21499 * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
21500 * @param $color (array) RGB color array for title (values from 0 to 255).
21502 * @author Nicola Asuni
21503 * @since 5.0.001 (2010-05-06)
21504 * @see addTOCPage(), endTOCPage(), addTOC()
21506 public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
21508 $prev_htmlLinkColorArray = $this->htmlLinkColorArray
;
21509 $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle
;
21510 // set new style for link
21511 $this->htmlLinkColorArray
= array();
21512 $this->htmlLinkFontStyle
= '';
21513 $page_first = $this->getPage();
21514 $page_fill_start = false;
21515 $page_fill_end = false;
21516 // get the font type used for numbers in each template
21517 $current_font = $this->FontFamily
;
21518 foreach ($templates as $level => $html) {
21519 $dom = $this->getHtmlDomArray($html);
21520 foreach ($dom as $key => $value) {
21521 if ($value['value'] == '#TOC_PAGE_NUMBER#') {
21522 $this->SetFont($dom[($key - 1)]['fontname']);
21523 $templates['F'.$level] = $this->isUnicodeFont();
21527 $this->SetFont($current_font);
21528 $maxpage = 0; //used for pages on attached documents
21529 foreach ($this->outlines
as $key => $outline) {
21530 // get HTML template
21531 $row = $templates[$outline['l']];
21532 if (TCPDF_STATIC
::empty_string($page)) {
21533 $pagenum = $outline['p'];
21535 // placemark to be replaced with the correct number
21536 $pagenum = '{#'.($outline['p']).'}';
21537 if ($templates['F'.$outline['l']]) {
21538 $pagenum = '{'.$pagenum.'}';
21540 $maxpage = max($maxpage, $outline['p']);
21542 // replace templates with current values
21543 $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
21544 $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
21545 // add link to page
21546 $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
21547 // write bookmark entry
21548 $this->writeHTML($row, false, false, true, false, '');
21550 // restore link styles
21551 $this->htmlLinkColorArray
= $prev_htmlLinkColorArray;
21552 $this->htmlLinkFontStyle
= $prev_htmlLinkFontStyle;
21553 // move TOC page and replace numbers
21554 $page_last = $this->getPage();
21555 $numpages = ($page_last - $page_first +
1);
21556 // account for booklet mode
21557 if ($this->booklet
) {
21558 // check if a blank page is required before TOC
21559 $page_fill_start = ((($page_first %
2) == 0) XOR (($page %
2) == 0));
21560 $page_fill_end = (!((($numpages %
2) == 0) XOR ($page_fill_start)));
21561 if ($page_fill_start) {
21562 // add a page at the end (to be moved before TOC)
21567 if ($page_fill_end) {
21568 // add a page at the end
21574 $maxpage = max($maxpage, $page_last);
21575 if (!TCPDF_STATIC
::empty_string($page)) {
21576 for ($p = $page_first; $p <= $page_last; ++
$p) {
21578 $temppage = $this->getPageBuffer($p);
21579 for ($n = 1; $n <= $maxpage; ++
$n) {
21580 // update page numbers
21582 // get page number aliases
21583 $pnalias = $this->getInternalPageNumberAliases($a);
21584 // calculate replacement number
21586 $np = $n +
$numpages;
21590 $na = TCPDF_STATIC
::formatTOCPageNumber(($this->starting_page_number +
$np - 1));
21591 $nu = TCPDF_FONTS
::UTF8ToUTF16BE($na, false, $this->isunicode
, $this->CurrentFont
);
21592 // replace aliases with numbers
21593 foreach ($pnalias['u'] as $u) {
21594 if ($correct_align) {
21595 $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
21597 $nr = $nu.TCPDF_FONTS
::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode
, $this->CurrentFont
);
21599 $nr = TCPDF_FONTS
::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode
, $this->CurrentFont
).$nu;
21604 $temppage = str_replace($u, $nr, $temppage);
21606 foreach ($pnalias['a'] as $a) {
21607 if ($correct_align) {
21608 $sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
21610 $nr = $na.' '.$sfill;
21612 $nr = $sfill.' '.$na;
21617 $temppage = str_replace($a, $nr, $temppage);
21621 $this->setPageBuffer($p, $temppage);
21624 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21625 if ($page_fill_start) {
21626 $this->movePage($page_last, $page_first);
21628 for ($i = 0; $i < $numpages; ++
$i) {
21629 $this->movePage($page_last, $page);
21635 * Stores a copy of the current TCPDF object used for undo operation.
21637 * @since 4.5.029 (2009-03-19)
21639 public function startTransaction() {
21640 if (isset($this->objcopy
)) {
21641 // remove previous copy
21642 $this->commitTransaction();
21644 // record current page number and Y position
21645 $this->start_transaction_page
= $this->page
;
21646 $this->start_transaction_y
= $this->y
;
21647 // clone current object
21648 $this->objcopy
= TCPDF_STATIC
::objclone($this);
21652 * Delete the copy of the current TCPDF object used for undo operation.
21654 * @since 4.5.029 (2009-03-19)
21656 public function commitTransaction() {
21657 if (isset($this->objcopy
)) {
21658 $this->objcopy
->_destroy(true, true);
21659 unset($this->objcopy
);
21664 * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction().
21665 * @param $self (boolean) if true restores current class object to previous state without the need of reassignment via the returned value.
21666 * @return TCPDF object.
21668 * @since 4.5.029 (2009-03-19)
21670 public function rollbackTransaction($self=false) {
21671 if (isset($this->objcopy
)) {
21672 if (isset($this->objcopy
->diskcache
) AND $this->objcopy
->diskcache
) {
21673 // truncate files to previous values
21674 foreach ($this->objcopy
->cache_file_length
as $file => $length) {
21675 $file = substr($file, 1);
21676 $handle = fopen($file, 'r+');
21677 ftruncate($handle, $length);
21680 $this->_destroy(true, true);
21682 $objvars = get_object_vars($this->objcopy
);
21683 foreach ($objvars as $key => $value) {
21684 $this->$key = $value;
21687 return $this->objcopy
;
21692 // --- MULTI COLUMNS METHODS -----------------------
21695 * Set multiple columns of the same size
21696 * @param $numcols (int) number of columns (set to zero to disable columns mode)
21697 * @param $width (int) column width
21698 * @param $y (int) column starting Y position (leave empty for current Y position)
21700 * @since 4.9.001 (2010-03-28)
21702 public function setEqualColumns($numcols=0, $width=0, $y='') {
21703 $this->columns
= array();
21704 if ($numcols < 2) {
21706 $this->columns
= array();
21708 // maximum column width
21709 $maxwidth = ($this->w
- $this->original_lMargin
- $this->original_rMargin
) / $numcols;
21710 if (($width == 0) OR ($width > $maxwidth)) {
21711 $width = $maxwidth;
21713 if (TCPDF_STATIC
::empty_string($y)) {
21716 // space between columns
21717 $space = (($this->w
- $this->original_lMargin
- $this->original_rMargin
- ($numcols * $width)) / ($numcols - 1));
21718 // fill the columns array (with, space, starting Y position)
21719 for ($i = 0; $i < $numcols; ++
$i) {
21720 $this->columns
[$i] = array('w' => $width, 's' => $space, 'y' => $y);
21723 $this->num_columns
= $numcols;
21724 $this->current_column
= 0;
21725 $this->column_start_page
= $this->page
;
21726 $this->selectColumn(0);
21730 * Remove columns and reset page margins.
21732 * @since 5.9.072 (2011-04-26)
21734 public function resetColumns() {
21735 $this->lMargin
= $this->original_lMargin
;
21736 $this->rMargin
= $this->original_rMargin
;
21737 $this->setEqualColumns();
21741 * Set columns array.
21742 * Each column is represented by an array of arrays with the following keys: (w = width, s = space between columns, y = column top position).
21743 * @param $columns (array)
21745 * @since 4.9.001 (2010-03-28)
21747 public function setColumnsArray($columns) {
21748 $this->columns
= $columns;
21749 $this->num_columns
= count($columns);
21750 $this->current_column
= 0;
21751 $this->column_start_page
= $this->page
;
21752 $this->selectColumn(0);
21756 * Set position at a given column
21757 * @param $col (int) column number (from 0 to getNumberOfColumns()-1); empty string = current column.
21759 * @since 4.9.001 (2010-03-28)
21761 public function selectColumn($col='') {
21762 if (is_string($col)) {
21763 $col = $this->current_column
;
21764 } elseif ($col >= $this->num_columns
) {
21767 $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
21768 $enable_thead = false;
21769 if ($this->num_columns
> 1) {
21770 if ($col != $this->current_column
) {
21771 // move Y pointer at the top of the column
21772 if ($this->column_start_page
== $this->page
) {
21773 $this->y
= $this->columns
[$col]['y'];
21775 $this->y
= $this->tMargin
;
21777 // Avoid to write table headers more than once
21778 if (($this->page
> $this->maxselcol
['page']) OR (($this->page
== $this->maxselcol
['page']) AND ($col > $this->maxselcol
['column']))) {
21779 $enable_thead = true;
21780 $this->maxselcol
['page'] = $this->page
;
21781 $this->maxselcol
['column'] = $col;
21784 $xshift = $this->colxshift
;
21785 // set X position of the current column by case
21786 $listindent = ($this->listindentlevel
* $this->listindent
);
21787 // calculate column X position
21789 for ($i = 0; $i < $col; ++
$i) {
21790 $colpos +
= ($this->columns
[$i]['w'] +
$this->columns
[$i]['s']);
21793 $x = $this->w
- $this->original_rMargin
- $colpos;
21794 $this->rMargin
= ($this->w
- $x +
$listindent);
21795 $this->lMargin
= ($x - $this->columns
[$col]['w']);
21796 $this->x
= $x - $listindent;
21798 $x = $this->original_lMargin +
$colpos;
21799 $this->lMargin
= ($x +
$listindent);
21800 $this->rMargin
= ($this->w
- $x - $this->columns
[$col]['w']);
21801 $this->x
= $x +
$listindent;
21803 $this->columns
[$col]['x'] = $x;
21805 $this->current_column
= $col;
21806 // fix for HTML mode
21807 $this->newline
= true;
21808 // print HTML table header (if any)
21809 if ((!TCPDF_STATIC
::empty_string($this->thead
)) AND (!$this->inthead
)) {
21810 if ($enable_thead) {
21811 // print table header
21812 $this->writeHTML($this->thead
, false, false, false, false, '');
21813 $this->y +
= $xshift['s']['V'];
21814 // store end of header position
21815 if (!isset($this->columns
[$col]['th'])) {
21816 $this->columns
[$col]['th'] = array();
21818 $this->columns
[$col]['th']['\''.$this->page
.'\''] = $this->y
;
21820 } elseif (isset($this->columns
[$col]['th']['\''.$this->page
.'\''])) {
21821 $this->y
= $this->columns
[$col]['th']['\''.$this->page
.'\''];
21824 // account for an html table cell over multiple columns
21826 $this->rMargin +
= $xshift['x'];
21827 $this->x
-= ($xshift['x'] +
$xshift['p']['R']);
21829 $this->lMargin +
= $xshift['x'];
21830 $this->x +
= $xshift['x'] +
$xshift['p']['L'];
21835 * Return the current column number
21836 * @return int current column number
21838 * @since 5.5.011 (2010-07-08)
21840 public function getColumn() {
21841 return $this->current_column
;
21845 * Return the current number of columns.
21846 * @return int number of columns
21848 * @since 5.8.018 (2010-08-25)
21850 public function getNumberOfColumns() {
21851 return $this->num_columns
;
21855 * Set Text rendering mode.
21856 * @param $stroke (int) outline size in user units (0 = disable).
21857 * @param $fill (boolean) if true fills the text (default).
21858 * @param $clip (boolean) if true activate clipping mode
21860 * @since 4.9.008 (2009-04-02)
21862 public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
21863 // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
21864 // convert text rendering parameters
21868 if ($fill === true) {
21870 if ($clip === true) {
21871 // Fill, then stroke text and add to path for clipping
21872 $textrendermode = 6;
21874 // Fill, then stroke text
21875 $textrendermode = 2;
21877 $textstrokewidth = $stroke;
21879 if ($clip === true) {
21880 // Fill text and add to path for clipping
21881 $textrendermode = 4;
21884 $textrendermode = 0;
21889 if ($clip === true) {
21890 // Stroke text and add to path for clipping
21891 $textrendermode = 5;
21894 $textrendermode = 1;
21896 $textstrokewidth = $stroke;
21898 if ($clip === true) {
21899 // Add text to path for clipping
21900 $textrendermode = 7;
21902 // Neither fill nor stroke text (invisible)
21903 $textrendermode = 3;
21907 $this->textrendermode
= $textrendermode;
21908 $this->textstrokewidth
= $stroke;
21912 * Set parameters for drop shadow effect for text.
21913 * @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.
21914 * @since 5.9.174 (2012-07-25)
21917 public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) {
21918 if (isset($params['enabled'])) {
21919 $this->txtshadow
['enabled'] = $params['enabled']?
true:false;
21921 $this->txtshadow
['enabled'] = false;
21923 if (isset($params['depth_w'])) {
21924 $this->txtshadow
['depth_w'] = floatval($params['depth_w']);
21926 $this->txtshadow
['depth_w'] = 0;
21928 if (isset($params['depth_h'])) {
21929 $this->txtshadow
['depth_h'] = floatval($params['depth_h']);
21931 $this->txtshadow
['depth_h'] = 0;
21933 if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) {
21934 $this->txtshadow
['color'] = $params['color'];
21936 $this->txtshadow
['color'] = $this->strokecolor
;
21938 if (isset($params['opacity'])) {
21939 $this->txtshadow
['opacity'] = min(1, max(0, floatval($params['opacity'])));
21941 $this->txtshadow
['opacity'] = 1;
21943 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'))) {
21944 $this->txtshadow
['blend_mode'] = $params['blend_mode'];
21946 $this->txtshadow
['blend_mode'] = 'Normal';
21948 if ((($this->txtshadow
['depth_w'] == 0) AND ($this->txtshadow
['depth_h'] == 0)) OR ($this->txtshadow
['opacity'] == 0)) {
21949 $this->txtshadow
['enabled'] = false;
21954 * Return the text shadow parameters array.
21955 * @return Array of parameters.
21956 * @since 5.9.174 (2012-07-25)
21959 public function getTextShadow() {
21960 return $this->txtshadow
;
21964 * Returns an array of chars containing soft hyphens.
21965 * @param $word (array) array of chars
21966 * @param $patterns (array) Array of hypenation patterns.
21967 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm.
21968 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
21969 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
21970 * @param $charmin (int) Minimum word length to apply the hyphenation algoritm.
21971 * @param $charmax (int) Maximum length of broken piece of word.
21972 * @return array text with soft hyphens
21973 * @author Nicola Asuni
21974 * @since 4.9.012 (2010-04-12)
21977 protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
21978 $hyphenword = array(); // hyphens positions
21979 $numchars = count($word);
21980 if ($numchars <= $charmin) {
21983 $word_string = TCPDF_FONTS
::UTF8ArrSubString($word, '', '', $this->isunicode
);
21984 // some words will be returned as-is
21985 $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})(\]?)$/';
21986 if (preg_match($pattern, $word_string) > 0) {
21990 $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})(\]?)$/';
21991 if (preg_match($pattern, $word_string) > 0) {
21995 if (isset($dictionary[$word_string])) {
21996 return TCPDF_FONTS
::UTF8StringToArray($dictionary[$word_string], $this->isunicode
, $this->CurrentFont
);
21998 // suround word with '_' characters
21999 $tmpword = array_merge(array(95), $word, array(95));
22000 $tmpnumchars = $numchars +
2;
22001 $maxpos = $tmpnumchars - $charmin;
22002 for ($pos = 0; $pos < $maxpos; ++
$pos) {
22003 $imax = min(($tmpnumchars - $pos), $charmax);
22004 for ($i = $charmin; $i <= $imax; ++
$i) {
22005 $subword = strtolower(TCPDF_FONTS
::UTF8ArrSubString($tmpword, $pos, ($pos +
$i), $this->isunicode
));
22006 if (isset($patterns[$subword])) {
22007 $pattern = TCPDF_FONTS
::UTF8StringToArray($patterns[$subword], $this->isunicode
, $this->CurrentFont
);
22008 $pattern_length = count($pattern);
22010 for ($j = 0; $j < $pattern_length; ++
$j) {
22011 // check if $pattern[$j] is a number
22012 if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
22016 $zero = $pos +
$j - $digits;
22018 if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] != $pattern[$j])) {
22019 $hyphenword[$zero] = TCPDF_FONTS
::unichr($pattern[$j], $this->isunicode
);
22028 $maxpos = $numchars - $rightmin;
22029 for ($i = $leftmin; $i <= $maxpos; ++
$i) {
22030 if (isset($hyphenword[$i]) AND (($hyphenword[$i] %
2) != 0)) {
22031 // 173 = soft hyphen character
22032 array_splice($word, $i +
$inserted, 0, 173);
22040 * Returns text with soft hyphens.
22041 * @param $text (string) text to process
22042 * @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/
22043 * @param $dictionary (array) Array of words to be returned without applying the hyphenation algoritm.
22044 * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
22045 * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
22046 * @param $charmin (int) Minimum word length to apply the hyphenation algoritm.
22047 * @param $charmax (int) Maximum length of broken piece of word.
22048 * @return array text with soft hyphens
22049 * @author Nicola Asuni
22050 * @since 4.9.012 (2010-04-12)
22053 public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22054 $text = $this->unhtmlentities($text);
22055 $word = array(); // last word
22056 $txtarr = array(); // text to be returned
22057 $intag = false; // true if we are inside an HTML tag
22058 if (!is_array($patterns)) {
22059 $patterns = TCPDF_STATIC
::getHyphenPatternsFromTEX($patterns);
22061 // get array of characters
22062 $unichars = TCPDF_FONTS
::UTF8StringToArray($text, $this->isunicode
, $this->CurrentFont
);
22064 foreach ($unichars as $char) {
22065 if ((!$intag) AND TCPDF_FONT_DATA
::$uni_type[$char] == 'L') {
22066 // letter character
22069 // other type of character
22070 if (!TCPDF_STATIC
::empty_string($word)) {
22071 // hypenate the word
22072 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22076 if (chr($char) == '<') {
22077 // we are inside an HTML tag
22079 } elseif ($intag AND (chr($char) == '>')) {
22085 if (!TCPDF_STATIC
::empty_string($word)) {
22086 // hypenate the word
22087 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22089 // convert char array to string and return
22090 return TCPDF_FONTS
::UTF8ArrSubString($txtarr, '', '', $this->isunicode
);
22094 * Enable/disable rasterization of vector images using ImageMagick library.
22095 * @param $mode (boolean) if true enable rasterization, false otherwise.
22097 * @since 5.0.000 (2010-04-27)
22099 public function setRasterizeVectorImages($mode) {
22100 $this->rasterize_vector_images
= $mode;
22104 * Enable or disable default option for font subsetting.
22105 * @param $enable (boolean) if true enable font subsetting by default.
22106 * @author Nicola Asuni
22108 * @since 5.3.002 (2010-06-07)
22110 public function setFontSubsetting($enable=true) {
22111 if ($this->pdfa_mode
) {
22112 $this->font_subsetting
= false;
22114 $this->font_subsetting
= $enable ?
true : false;
22119 * Return the default option for font subsetting.
22120 * @return boolean default font subsetting state.
22121 * @author Nicola Asuni
22123 * @since 5.3.002 (2010-06-07)
22125 public function getFontSubsetting() {
22126 return $this->font_subsetting
;
22130 * Left trim the input string
22131 * @param $str (string) string to trim
22132 * @param $replace (string) string that replace spaces.
22133 * @return left trimmed string
22134 * @author Nicola Asuni
22136 * @since 5.8.000 (2010-08-11)
22138 public function stringLeftTrim($str, $replace='') {
22139 return preg_replace('/^'.$this->re_space
['p'].'+/'.$this->re_space
['m'], $replace, $str);
22143 * Right trim the input string
22144 * @param $str (string) string to trim
22145 * @param $replace (string) string that replace spaces.
22146 * @return right trimmed string
22147 * @author Nicola Asuni
22149 * @since 5.8.000 (2010-08-11)
22151 public function stringRightTrim($str, $replace='') {
22152 return preg_replace('/'.$this->re_space
['p'].'+$/'.$this->re_space
['m'], $replace, $str);
22156 * Trim the input string
22157 * @param $str (string) string to trim
22158 * @param $replace (string) string that replace spaces.
22159 * @return trimmed string
22160 * @author Nicola Asuni
22162 * @since 5.8.000 (2010-08-11)
22164 public function stringTrim($str, $replace='') {
22165 $str = $this->stringLeftTrim($str, $replace);
22166 $str = $this->stringRightTrim($str, $replace);
22171 * Return true if the current font is unicode type.
22172 * @return true for unicode font, false otherwise.
22173 * @author Nicola Asuni
22175 * @since 5.8.002 (2010-08-14)
22177 public function isUnicodeFont() {
22178 return (($this->CurrentFont
['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont
['type'] == 'cidfont0'));
22182 * Return normalized font name
22183 * @param $fontfamily (string) property string containing font family names
22184 * @return string normalized font name
22185 * @author Nicola Asuni
22187 * @since 5.8.004 (2010-08-17)
22189 public function getFontFamilyName($fontfamily) {
22190 // remove spaces and symbols
22191 $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily));
22192 // extract all font names
22193 $fontslist = preg_split('/[,]/', $fontfamily);
22194 // find first valid font name
22195 foreach ($fontslist as $font) {
22196 // replace font variations
22197 $font = preg_replace('/italic$/', 'I', $font);
22198 $font = preg_replace('/oblique$/', 'I', $font);
22199 $font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
22200 // replace common family names and core fonts
22201 $pattern = array();
22202 $replacement = array();
22203 $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
22204 $replacement[] = 'times';
22205 $pattern[] = '/^sansserif/';
22206 $replacement[] = 'helvetica';
22207 $pattern[] = '/^monospace/';
22208 $replacement[] = 'courier';
22209 $font = preg_replace($pattern, $replacement, $font);
22210 if (in_array(strtolower($font), $this->fontlist
) OR in_array($font, $this->fontkeys
)) {
22214 // return current font as default
22215 return $this->CurrentFont
['fontkey'];
22219 * Start a new XObject Template.
22220 * 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).
22221 * 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.
22222 * Note: X,Y coordinates will be reset to 0,0.
22223 * @param $w (int) Template width in user units (empty string or zero = page width less margins).
22224 * @param $h (int) Template height in user units (empty string or zero = page height less margins).
22225 * @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).
22226 * @return int the XObject Template ID in case of success or false in case of error.
22227 * @author Nicola Asuni
22229 * @since 5.8.017 (2010-08-24)
22230 * @see endTemplate(), printTemplate()
22232 public function startTemplate($w=0, $h=0, $group=false) {
22233 if ($this->inxobj
) {
22234 // we are already inside an XObject template
22237 $this->inxobj
= true;
22240 $this->xobjid
= 'XT'.$this->n
;
22242 $this->xobjects
[$this->xobjid
] = array('n' => $this->n
);
22243 // store current graphic state
22244 $this->xobjects
[$this->xobjid
]['gvars'] = $this->getGraphicVars();
22246 $this->xobjects
[$this->xobjid
]['intmrk'] = 0;
22247 $this->xobjects
[$this->xobjid
]['transfmrk'] = array();
22248 $this->xobjects
[$this->xobjid
]['outdata'] = '';
22249 $this->xobjects
[$this->xobjid
]['xobjects'] = array();
22250 $this->xobjects
[$this->xobjid
]['images'] = array();
22251 $this->xobjects
[$this->xobjid
]['fonts'] = array();
22252 $this->xobjects
[$this->xobjid
]['annotations'] = array();
22253 $this->xobjects
[$this->xobjid
]['extgstates'] = array();
22254 $this->xobjects
[$this->xobjid
]['gradients'] = array();
22255 $this->xobjects
[$this->xobjid
]['spot_colors'] = array();
22256 // set new environment
22257 $this->num_columns
= 1;
22258 $this->current_column
= 0;
22259 $this->SetAutoPageBreak(false);
22260 if (($w === '') OR ($w <= 0)) {
22261 $w = $this->w
- $this->lMargin
- $this->rMargin
;
22263 if (($h === '') OR ($h <= 0)) {
22264 $h = $this->h
- $this->tMargin
- $this->bMargin
;
22266 $this->xobjects
[$this->xobjid
]['x'] = 0;
22267 $this->xobjects
[$this->xobjid
]['y'] = 0;
22268 $this->xobjects
[$this->xobjid
]['w'] = $w;
22269 $this->xobjects
[$this->xobjid
]['h'] = $h;
22272 $this->wPt
= $this->w
* $this->k
;
22273 $this->hPt
= $this->h
* $this->k
;
22274 $this->fwPt
= $this->wPt
;
22275 $this->fhPt
= $this->hPt
;
22278 $this->lMargin
= 0;
22279 $this->rMargin
= 0;
22280 $this->tMargin
= 0;
22281 $this->bMargin
= 0;
22283 $this->xobjects
[$this->xobjid
]['group'] = $group;
22284 return $this->xobjid
;
22288 * End the current XObject Template started with startTemplate() and restore the previous graphic state.
22289 * 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).
22290 * 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.
22291 * @return int the XObject Template ID in case of success or false in case of error.
22292 * @author Nicola Asuni
22294 * @since 5.8.017 (2010-08-24)
22295 * @see startTemplate(), printTemplate()
22297 public function endTemplate() {
22298 if (!$this->inxobj
) {
22299 // we are not inside a template
22302 $this->inxobj
= false;
22303 // restore previous graphic state
22304 $this->setGraphicVars($this->xobjects
[$this->xobjid
]['gvars'], true);
22305 return $this->xobjid
;
22309 * Print an XObject Template.
22310 * You can print an XObject Template inside the currently opened Template.
22311 * 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).
22312 * 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.
22313 * @param $id (string) The ID of XObject Template to print.
22314 * @param $x (int) X position in user units (empty string = current x position)
22315 * @param $y (int) Y position in user units (empty string = current y position)
22316 * @param $w (int) Width in user units (zero = remaining page width)
22317 * @param $h (int) Height in user units (zero = remaining page height)
22318 * @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>
22319 * @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>
22320 * @param $fitonpage (boolean) If true the template is resized to not exceed page dimensions.
22321 * @author Nicola Asuni
22323 * @since 5.8.017 (2010-08-24)
22324 * @see startTemplate(), endTemplate()
22326 public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
22327 if ($this->state
!= 2) {
22330 if (!isset($this->xobjects
[$id])) {
22331 $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
22333 if ($this->inxobj
) {
22334 if ($id == $this->xobjid
) {
22335 // close current template
22336 $this->endTemplate();
22338 // use the template as resource for the template currently opened
22339 $this->xobjects
[$this->xobjid
]['xobjects'][$id] = $this->xobjects
[$id];
22342 // set default values
22349 // check page for no-write regions and adapt page margins if necessary
22350 list($x, $y) = $this->checkPageRegions($h, $x, $y);
22351 $ow = $this->xobjects
[$id]['w'];
22355 $oh = $this->xobjects
[$id]['h'];
22359 // calculate template width and height on document
22360 if (($w <= 0) AND ($h <= 0)) {
22363 } elseif ($w <= 0) {
22364 $w = $h * $ow / $oh;
22365 } elseif ($h <= 0) {
22366 $h = $w * $oh / $ow;
22368 // fit the template on available space
22369 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22370 // set page alignment
22374 if ($palign == 'L') {
22375 $xt = $this->lMargin
;
22376 } elseif ($palign == 'C') {
22377 $xt = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
22378 } elseif ($palign == 'R') {
22379 $xt = $this->w
- $this->rMargin
- $w;
22385 if ($palign == 'L') {
22386 $xt = $this->lMargin
;
22387 } elseif ($palign == 'C') {
22388 $xt = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
22389 } elseif ($palign == 'R') {
22390 $xt = $this->w
- $this->rMargin
- $w;
22396 // print XObject Template + Transformation matrix
22397 $this->StartTransform();
22398 // translate and scale
22406 $tm[4] = $xt * $this->k
;
22407 $tm[5] = ($this->h
- $h - $y) * $this->k
;
22408 $this->Transform($tm);
22410 $this->_out('/'.$id.' Do');
22411 $this->StopTransform();
22413 if (!empty($this->xobjects
[$id]['annotations'])) {
22414 foreach ($this->xobjects
[$id]['annotations'] as $annot) {
22415 // transform original coordinates
22416 $coordlt = TCPDF_STATIC
::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k
), (-$annot['y'] * $this->k
)));
22417 $ax = ($coordlt[4] / $this->k
);
22418 $ay = ($this->h
- $h - ($coordlt[5] / $this->k
));
22419 $coordrb = TCPDF_STATIC
::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] +
$annot['w']) * $this->k
), ((-$annot['y'] - $annot['h']) * $this->k
)));
22420 $aw = ($coordrb[4] / $this->k
) - $ax;
22421 $ah = ($this->h
- $h - ($coordrb[5] / $this->k
)) - $ay;
22422 $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
22425 // set pointer to align the next text/objects
22433 $this->y
= $y +
round($h/2);
22443 $this->SetY($rb_y);
22453 * Set the percentage of character stretching.
22454 * @param $perc (int) percentage of stretching (100 = no stretching)
22455 * @author Nicola Asuni
22457 * @since 5.9.000 (2010-09-29)
22459 public function setFontStretching($perc=100) {
22460 $this->font_stretching
= $perc;
22464 * Get the percentage of character stretching.
22465 * @return float stretching value
22466 * @author Nicola Asuni
22468 * @since 5.9.000 (2010-09-29)
22470 public function getFontStretching() {
22471 return $this->font_stretching
;
22475 * Set the amount to increase or decrease the space between characters in a text.
22476 * @param $spacing (float) amount to increase or decrease the space between characters in a text (0 = default spacing)
22477 * @author Nicola Asuni
22479 * @since 5.9.000 (2010-09-29)
22481 public function setFontSpacing($spacing=0) {
22482 $this->font_spacing
= $spacing;
22486 * Get the amount to increase or decrease the space between characters in a text.
22487 * @return int font spacing (tracking) value
22488 * @author Nicola Asuni
22490 * @since 5.9.000 (2010-09-29)
22492 public function getFontSpacing() {
22493 return $this->font_spacing
;
22497 * Return an array of no-write page regions
22498 * @return array of no-write page regions
22499 * @author Nicola Asuni
22501 * @since 5.9.003 (2010-10-13)
22502 * @see setPageRegions(), addPageRegion()
22504 public function getPageRegions() {
22505 return $this->page_regions
;
22509 * Set no-write regions on page.
22510 * 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.
22511 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22512 * You can set multiple regions for the same page.
22513 * @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.
22514 * @author Nicola Asuni
22516 * @since 5.9.003 (2010-10-13)
22517 * @see addPageRegion(), getPageRegions()
22519 public function setPageRegions($regions=array()) {
22520 // empty current regions array
22521 $this->page_regions
= array();
22523 foreach ($regions as $data) {
22524 $this->addPageRegion($data);
22529 * Add a single no-write region on selected page.
22530 * 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.
22531 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22532 * You can set multiple regions for the same page.
22533 * @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).
22534 * @author Nicola Asuni
22536 * @since 5.9.003 (2010-10-13)
22537 * @see setPageRegions(), getPageRegions()
22539 public function addPageRegion($region) {
22540 if (!isset($region['page']) OR empty($region['page'])) {
22541 $region['page'] = $this->page
;
22543 if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
22544 AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
22545 AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
22546 $this->page_regions
[] = $region;
22551 * Remove a single no-write region.
22552 * @param $key (int) region key
22553 * @author Nicola Asuni
22555 * @since 5.9.003 (2010-10-13)
22556 * @see setPageRegions(), getPageRegions()
22558 public function removePageRegion($key) {
22559 if (isset($this->page_regions
[$key])) {
22560 unset($this->page_regions
[$key]);
22565 * Check page for no-write regions and adapt current coordinates and page margins if necessary.
22566 * 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.
22567 * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22568 * @param $h (float) height of the text/image/object to print in user units
22569 * @param $x (float) current X coordinate in user units
22570 * @param $y (float) current Y coordinate in user units
22571 * @return array($x, $y)
22572 * @author Nicola Asuni
22574 * @since 5.9.003 (2010-10-13)
22576 protected function checkPageRegions($h, $x, $y) {
22577 // set default values
22584 if (!$this->check_page_regions
OR empty($this->page_regions
)) {
22585 // no page regions defined
22586 return array($x, $y);
22589 $h = $this->getCellHeight($this->FontSize
);
22591 // check for page break
22592 if ($this->checkPageBreak($h, $y)) {
22593 // the content will be printed on a new page
22597 if ($this->num_columns
> 1) {
22599 $this->lMargin
= ($this->columns
[$this->current_column
]['x'] - $this->columns
[$this->current_column
]['w']);
22601 $this->rMargin
= ($this->w
- $this->columns
[$this->current_column
]['x'] - $this->columns
[$this->current_column
]['w']);
22605 $this->lMargin
= max($this->clMargin
, $this->original_lMargin
);
22607 $this->rMargin
= max($this->crMargin
, $this->original_rMargin
);
22610 // adjust coordinates and page margins
22611 foreach ($this->page_regions
as $regid => $regdata) {
22612 if ($regdata['page'] == $this->page
) {
22613 // check region boundaries
22614 if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
22615 // Y is inside the region
22616 $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
22617 $yt = max($y, $regdata['yt']);
22618 $yb = min(($yt +
$h), $regdata['yb']);
22619 $xt = (($yt - $regdata['yt']) * $minv) +
$regdata['xt'];
22620 $xb = (($yb - $regdata['yt']) * $minv) +
$regdata['xt'];
22621 if ($regdata['side'] == 'L') { // left side
22622 $new_margin = max($xt, $xb);
22623 if ($this->lMargin
< $new_margin) {
22625 // adjust left page margin
22626 $this->lMargin
= max(0, $new_margin);
22628 if ($x < $new_margin) {
22629 // adjust x position
22631 if ($new_margin > ($this->w
- $this->rMargin
)) {
22632 // adjust y position
22633 $y = $regdata['yb'] - $h;
22637 } elseif ($regdata['side'] == 'R') { // right side
22638 $new_margin = min($xt, $xb);
22639 if (($this->w
- $this->rMargin
) > $new_margin) {
22641 // adjust right page margin
22642 $this->rMargin
= max(0, ($this->w
- $new_margin));
22644 if ($x > $new_margin) {
22645 // adjust x position
22647 if ($new_margin > $this->lMargin
) {
22648 // adjust y position
22649 $y = $regdata['yb'] - $h;
22657 return array($x, $y);
22660 // --- SVG METHODS ---------------------------------------------------------
22663 * Embedd a Scalable Vector Graphics (SVG) image.
22664 * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
22665 * @param $file (string) Name of the SVG file or a '@' character followed by the SVG data string.
22666 * @param $x (float) Abscissa of the upper-left corner.
22667 * @param $y (float) Ordinate of the upper-left corner.
22668 * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
22669 * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
22670 * @param $link (mixed) URL or identifier returned by AddLink().
22671 * @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.
22672 * @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>
22673 * @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)))
22674 * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
22675 * @author Nicola Asuni
22676 * @since 5.0.000 (2010-05-02)
22679 public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
22680 if ($this->state
!= 2) {
22684 $this->svggradients
= array();
22685 $this->svggradientid
= 0;
22686 $this->svgdefsmode
= false;
22687 $this->svgdefs
= array();
22688 $this->svgclipmode
= false;
22689 $this->svgclippaths
= array();
22690 $this->svgcliptm
= array();
22691 $this->svgclipid
= 0;
22692 $this->svgtext
= '';
22693 $this->svgtextmode
= array();
22694 if ($this->rasterize_vector_images
AND ($w > 0) AND ($h > 0)) {
22695 // convert SVG to raster image using GD or ImageMagick libraries
22696 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22698 if ($file[0] === '@') { // image from string
22699 $this->svgdir
= '';
22700 $svgdata = substr($file, 1);
22701 } else { // SVG file
22702 $this->svgdir
= dirname($file);
22703 $svgdata = TCPDF_STATIC
::fileGetContents($file);
22705 if ($svgdata === FALSE) {
22706 $this->Error('SVG file not found: '.$file);
22714 // check page for no-write regions and adapt page margins if necessary
22715 list($x, $y) = $this->checkPageRegions($h, $x, $y);
22721 $aspect_ratio_align = 'xMidYMid';
22722 $aspect_ratio_ms = 'meet';
22724 // get original image width and height
22725 preg_match('/<svg([^\>]*)>/si', $svgdata, $regs);
22726 if (isset($regs[1]) AND !empty($regs[1])) {
22728 if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22729 $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit
, false);
22732 if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22733 $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit
, false);
22736 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22737 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit
, false);
22740 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22741 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit
, false);
22744 $view_box = array();
22745 if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
22746 if (count($tmp) == 5) {
22748 foreach ($tmp as $key => $val) {
22749 $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit
, false);
22751 $ox = $view_box[0];
22752 $oy = $view_box[1];
22754 // get aspect ratio
22756 if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22757 $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
22758 switch (count($aspect_ratio)) {
22760 $aspect_ratio_align = $aspect_ratio[1];
22761 $aspect_ratio_ms = $aspect_ratio[2];
22765 $aspect_ratio_align = $aspect_ratio[0];
22766 $aspect_ratio_ms = $aspect_ratio[1];
22770 $aspect_ratio_align = $aspect_ratio[0];
22771 $aspect_ratio_ms = 'meet';
22784 // calculate image width and height on document
22785 if (($w <= 0) AND ($h <= 0)) {
22786 // convert image size to document unit
22789 } elseif ($w <= 0) {
22790 $w = $h * $ow / $oh;
22791 } elseif ($h <= 0) {
22792 $h = $w * $oh / $ow;
22794 // fit the image on available space
22795 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22796 if ($this->rasterize_vector_images
) {
22797 // convert SVG to raster image using GD or ImageMagick libraries
22798 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22801 $this->img_rb_y
= $y +
$h;
22804 if ($palign == 'L') {
22805 $ximg = $this->lMargin
;
22806 } elseif ($palign == 'C') {
22807 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
22808 } elseif ($palign == 'R') {
22809 $ximg = $this->w
- $this->rMargin
- $w;
22813 $this->img_rb_x
= $ximg;
22815 if ($palign == 'L') {
22816 $ximg = $this->lMargin
;
22817 } elseif ($palign == 'C') {
22818 $ximg = ($this->w +
$this->lMargin
- $this->rMargin
- $w) / 2;
22819 } elseif ($palign == 'R') {
22820 $ximg = $this->w
- $this->rMargin
- $w;
22824 $this->img_rb_x
= $ximg +
$w;
22826 // store current graphic vars
22827 $gvars = $this->getGraphicVars();
22828 // store SVG position and scale factors
22829 $svgoffset_x = ($ximg - $ox) * $this->k
;
22830 $svgoffset_y = -($y - $oy) * $this->k
;
22831 if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
22832 $ow = $view_box[2];
22833 $oh = $view_box[3];
22842 $svgscale_x = $w / $ow;
22843 $svgscale_y = $h / $oh;
22844 // scaling and alignment
22845 if ($aspect_ratio_align != 'none') {
22846 // store current scaling values
22847 $svgscale_old_x = $svgscale_x;
22848 $svgscale_old_y = $svgscale_y;
22849 // force uniform scaling
22850 if ($aspect_ratio_ms == 'slice') {
22851 // the entire viewport is covered by the viewBox
22852 if ($svgscale_x > $svgscale_y) {
22853 $svgscale_y = $svgscale_x;
22854 } elseif ($svgscale_x < $svgscale_y) {
22855 $svgscale_x = $svgscale_y;
22858 // the entire viewBox is visible within the viewport
22859 if ($svgscale_x < $svgscale_y) {
22860 $svgscale_y = $svgscale_x;
22861 } elseif ($svgscale_x > $svgscale_y) {
22862 $svgscale_x = $svgscale_y;
22865 // correct X alignment
22866 switch (substr($aspect_ratio_align, 1, 3)) {
22872 $svgoffset_x +
= (($w * $this->k
) - ($ow * $this->k
* $svgscale_x));
22877 $svgoffset_x +
= ((($w * $this->k
) - ($ow * $this->k
* $svgscale_x)) / 2);
22881 // correct Y alignment
22882 switch (substr($aspect_ratio_align, 5)) {
22888 $svgoffset_y -= (($h * $this->k
) - ($oh * $this->k
* $svgscale_y));
22893 $svgoffset_y -= ((($h * $this->k
) - ($oh * $this->k
* $svgscale_y)) / 2);
22898 // store current page break mode
22899 $page_break_mode = $this->AutoPageBreak
;
22900 $page_break_margin = $this->getBreakMargin();
22901 $cell_padding = $this->cell_padding
;
22902 $this->SetCellPadding(0);
22903 $this->SetAutoPageBreak(false);
22904 // save the current graphic state
22905 $this->_out('q'.$this->epsmarker
);
22906 // set initial clipping mask
22907 $this->Rect($ximg, $y, $w, $h, 'CNZ', array(), array());
22908 // scale and translate
22909 $e = $ox * $this->k
* (1 - $svgscale_x);
22910 $f = ($this->h
- $oy) * $this->k
* (1 - $svgscale_y);
22911 $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e +
$svgoffset_x), ($f +
$svgoffset_y)));
22912 // creates a new XML parser to be used by the other XML functions
22913 $this->parser
= xml_parser_create('UTF-8');
22914 // the following function allows to use parser inside object
22915 xml_set_object($this->parser
, $this);
22916 // disable case-folding for this XML parser
22917 xml_parser_set_option($this->parser
, XML_OPTION_CASE_FOLDING
, 0);
22918 // sets the element handler functions for the XML parser
22919 xml_set_element_handler($this->parser
, 'startSVGElementHandler', 'endSVGElementHandler');
22920 // sets the character data handler function for the XML parser
22921 xml_set_character_data_handler($this->parser
, 'segSVGContentHandler');
22922 // start parsing an XML document
22923 if (!xml_parse($this->parser
, $svgdata)) {
22924 $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
));
22925 $this->Error($error_message);
22927 // free this XML parser
22928 xml_parser_free($this->parser
);
22929 // restore previous graphic state
22930 $this->_out($this->epsmarker
.'Q');
22931 // restore graphic vars
22932 $this->setGraphicVars($gvars);
22933 $this->lasth
= $gvars['lasth'];
22934 if (!empty($border)) {
22942 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
22947 $this->Link($ximg, $y, $w, $h, $link, 0);
22949 // set pointer to align the next text/objects
22953 $this->x
= $this->img_rb_x
;
22957 $this->y
= $y +
round($h/2);
22958 $this->x
= $this->img_rb_x
;
22962 $this->y
= $this->img_rb_y
;
22963 $this->x
= $this->img_rb_x
;
22967 $this->SetY($this->img_rb_y
);
22971 // restore pointer to starting position
22972 $this->x
= $gvars['x'];
22973 $this->y
= $gvars['y'];
22974 $this->page
= $gvars['page'];
22975 $this->current_column
= $gvars['current_column'];
22976 $this->tMargin
= $gvars['tMargin'];
22977 $this->bMargin
= $gvars['bMargin'];
22978 $this->w
= $gvars['w'];
22979 $this->h
= $gvars['h'];
22980 $this->wPt
= $gvars['wPt'];
22981 $this->hPt
= $gvars['hPt'];
22982 $this->fwPt
= $gvars['fwPt'];
22983 $this->fhPt
= $gvars['fhPt'];
22987 $this->endlinex
= $this->img_rb_x
;
22988 // restore page break
22989 $this->SetAutoPageBreak($page_break_mode, $page_break_margin);
22990 $this->cell_padding
= $cell_padding;
22994 * Convert SVG transformation matrix to PDF.
22995 * @param $tm (array) original SVG transformation matrix
22996 * @return array transformation matrix
22998 * @since 5.0.000 (2010-05-02)
23000 protected function convertSVGtMatrix($tm) {
23005 $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit
, false) * $this->k
;
23006 $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit
, false) * $this->k
;
23008 $y = $this->h
* $this->k
;
23009 $e = ($x * (1 - $a)) - ($y * $c) +
$e;
23010 $f = ($y * (1 - $d)) - ($x * $b) +
$f;
23011 return array($a, $b, $c, $d, $e, $f);
23015 * Apply SVG graphic transformation matrix.
23016 * @param $tm (array) original SVG transformation matrix
23018 * @since 5.0.000 (2010-05-02)
23020 protected function SVGTransform($tm) {
23021 $this->Transform($this->convertSVGtMatrix($tm));
23025 * Apply the requested SVG styles (*** TO BE COMPLETED ***)
23026 * @param $svgstyle (array) array of SVG styles to apply
23027 * @param $prevsvgstyle (array) array of previous SVG style
23028 * @param $x (int) X origin of the bounding box
23029 * @param $y (int) Y origin of the bounding box
23030 * @param $w (int) width of the bounding box
23031 * @param $h (int) height of the bounding box
23032 * @param $clip_function (string) clip function
23033 * @param $clip_params (array) array of parameters for clipping function
23034 * @return object style
23035 * @author Nicola Asuni
23036 * @since 5.0.000 (2010-05-02)
23039 protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
23040 if ($this->state
!= 2) {
23044 $minlen = (0.01 / $this->k
); // minimum acceptable length
23045 if (!isset($svgstyle['opacity'])) {
23050 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
23051 $clip_path = $this->svgclippaths
[$regs[1]];
23052 foreach ($clip_path as $cp) {
23053 $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
23057 if ($svgstyle['opacity'] != 1) {
23058 $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false);
23061 $fill_color = TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors
);
23062 $this->SetFillColorArray($fill_color);
23064 $text_color = TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors
);
23065 $this->SetTextColorArray($text_color);
23067 if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
23068 $top = (isset($regs[1])?
$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit
, false):0);
23069 $right = (isset($regs[2])?
$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit
, false):0);
23070 $bottom = (isset($regs[3])?
$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit
, false):0);
23071 $left = (isset($regs[4])?
$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit
, false):0);
23074 $cw = $w - $left - $right;
23075 $ch = $h - $top - $bottom;
23076 if ($svgstyle['clip-rule'] == 'evenodd') {
23077 $clip_rule = 'CNZ';
23079 $clip_rule = 'CEO';
23081 $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
23085 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
23087 $gradient = $this->svggradients
[$regs[1]];
23088 if (isset($gradient['xref'])) {
23089 // reference to another gradient definition
23090 $newgradient = $this->svggradients
[$gradient['xref']];
23091 $newgradient['coords'] = $gradient['coords'];
23092 $newgradient['mode'] = $gradient['mode'];
23093 $newgradient['type'] = $gradient['type'];
23094 $newgradient['gradientUnits'] = $gradient['gradientUnits'];
23095 if (isset($gradient['gradientTransform'])) {
23096 $newgradient['gradientTransform'] = $gradient['gradientTransform'];
23098 $gradient = $newgradient;
23100 //save current Graphic State
23101 $this->_outSaveGraphicsState();
23102 //set clipping area
23103 if (!empty($clip_function) AND method_exists($this, $clip_function)) {
23104 $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
23105 if ((!isset($gradient['type']) OR ($gradient['type'] != 3)) AND is_array($bbox) AND (count($bbox) == 4)) {
23106 list($x, $y, $w, $h) = $bbox;
23109 if ($gradient['mode'] == 'measure') {
23110 if (!isset($gradient['coords'][4])) {
23111 $gradient['coords'][4] = 0.5;
23113 if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
23114 $gtm = $gradient['gradientTransform'];
23115 // apply transformation matrix
23116 $xa = ($gtm[0] * $gradient['coords'][0]) +
($gtm[2] * $gradient['coords'][1]) +
$gtm[4];
23117 $ya = ($gtm[1] * $gradient['coords'][0]) +
($gtm[3] * $gradient['coords'][1]) +
$gtm[5];
23118 $xb = ($gtm[0] * $gradient['coords'][2]) +
($gtm[2] * $gradient['coords'][3]) +
$gtm[4];
23119 $yb = ($gtm[1] * $gradient['coords'][2]) +
($gtm[3] * $gradient['coords'][3]) +
$gtm[5];
23120 $r = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) +
pow(($gtm[1] * $gradient['coords'][4]), 2));
23121 $gradient['coords'][0] = $xa;
23122 $gradient['coords'][1] = $ya;
23123 $gradient['coords'][2] = $xb;
23124 $gradient['coords'][3] = $yb;
23125 $gradient['coords'][4] = $r;
23127 // convert SVG coordinates to user units
23128 $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit
, false);
23129 $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit
, false);
23130 $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit
, false);
23131 $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit
, false);
23132 $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit
, false);
23133 if ($w <= $minlen) {
23136 if ($h <= $minlen) {
23140 if ($gradient['gradientUnits'] == 'objectBoundingBox') {
23141 // convert to SVG coordinate system
23142 $gradient['coords'][0] +
= $x;
23143 $gradient['coords'][1] +
= $y;
23144 $gradient['coords'][2] +
= $x;
23145 $gradient['coords'][3] +
= $y;
23147 // calculate percentages
23148 $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w);
23149 $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h);
23150 $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w);
23151 $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h);
23152 $gradient['coords'][4] /= $w;
23153 } elseif ($gradient['mode'] == 'percentage') {
23154 foreach($gradient['coords'] as $key => $val) {
23155 $gradient['coords'][$key] = (intval($val) / 100);
23157 $gradient['coords'][$key] = 0;
23158 } elseif ($val > 1) {
23159 $gradient['coords'][$key] = 1;
23163 if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
23164 // single color (no shading)
23165 $gradient['coords'][0] = 1;
23166 $gradient['coords'][1] = 0;
23167 $gradient['coords'][2] = 0.999;
23168 $gradient['coords'][3] = 0;
23170 // swap Y coordinates
23171 $tmp = $gradient['coords'][1];
23172 $gradient['coords'][1] = $gradient['coords'][3];
23173 $gradient['coords'][3] = $tmp;
23174 // set transformation map for gradient
23175 $cy = ($this->h
- $y);
23176 if ($gradient['type'] == 3) {
23177 // circular gradient
23178 $cy -= ($gradient['coords'][1] * ($w +
$h));
23182 $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k
), ($h * $this->k
), ($x * $this->k
), ($cy * $this->k
)));
23183 if (count($gradient['stops']) > 1) {
23184 $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
23186 } elseif ($svgstyle['fill'] != 'none') {
23187 $fill_color = TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors
);
23188 if ($svgstyle['fill-opacity'] != 1) {
23189 $this->setAlpha($this->alpha
['CA'], 'Normal', $svgstyle['fill-opacity'], false);
23191 $this->SetFillColorArray($fill_color);
23192 if ($svgstyle['fill-rule'] == 'evenodd') {
23199 if ($svgstyle['stroke'] != 'none') {
23200 if ($svgstyle['stroke-opacity'] != 1) {
23201 $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha
['ca'], false);
23203 $stroke_style = array(
23204 'color' => TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors
),
23205 'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit
, false),
23206 'cap' => $svgstyle['stroke-linecap'],
23207 'join' => $svgstyle['stroke-linejoin']
23209 if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
23210 $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
23212 $this->SetLineStyle($stroke_style);
23217 if (!empty($svgstyle['font'])) {
23218 if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
23219 $font_family = $this->getFontFamilyName($regs[1]);
23221 $font_family = $svgstyle['font-family'];
23223 if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23224 $font_size = trim($regs[1]);
23226 $font_size = $svgstyle['font-size'];
23228 if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23229 $font_style = trim($regs[1]);
23231 $font_style = $svgstyle['font-style'];
23233 if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23234 $font_weight = trim($regs[1]);
23236 $font_weight = $svgstyle['font-weight'];
23238 if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23239 $font_stretch = trim($regs[1]);
23241 $font_stretch = $svgstyle['font-stretch'];
23243 if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23244 $font_spacing = trim($regs[1]);
23246 $font_spacing = $svgstyle['letter-spacing'];
23249 $font_family = $this->getFontFamilyName($svgstyle['font-family']);
23250 $font_size = $svgstyle['font-size'];
23251 $font_style = $svgstyle['font-style'];
23252 $font_weight = $svgstyle['font-weight'];
23253 $font_stretch = $svgstyle['font-stretch'];
23254 $font_spacing = $svgstyle['letter-spacing'];
23256 $font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles
[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit
);
23257 $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
23258 $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
23259 switch ($font_style) {
23274 switch ($font_weight) {
23277 $font_style .= 'B';
23281 switch ($svgstyle['text-decoration']) {
23282 case 'underline': {
23283 $font_style .= 'U';
23287 $font_style .= 'O';
23290 case 'line-through': {
23291 $font_style .= 'D';
23299 $this->SetFont($font_family, $font_style, $font_size);
23300 $this->setFontStretching($font_stretch);
23301 $this->setFontSpacing($font_spacing);
23306 * Draws an SVG path
23307 * @param $d (string) attribute d of the path SVG element
23308 * @param $style (string) Style of rendering. Possible values are:
23310 * <li>D or empty string: Draw (default).</li>
23311 * <li>F: Fill.</li>
23312 * <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
23313 * <li>DF or FD: Draw and fill.</li>
23314 * <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
23315 * <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
23316 * <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
23318 * @return array of container box measures (x, y, w, h)
23319 * @author Nicola Asuni
23320 * @since 5.0.000 (2010-05-02)
23323 protected function SVGPath($d, $style='') {
23324 if ($this->state
!= 2) {
23327 // set fill/stroke style
23328 $op = TCPDF_STATIC
::getPathPaintOperator($style, '');
23333 $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
23334 preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER
);
23341 $xmin = 2147483647;
23343 $ymin = 2147483647;
23346 $minlen = (0.01 / $this->k
); // minimum acceptable length (3 point)
23347 $firstcmd = true; // used to print first point
23348 // draw curve pieces
23349 foreach ($paths as $key => $val) {
23351 $cmd = trim($val[1]);
23352 if (strtolower($cmd) == $cmd) {
23353 // use relative coordinated instead of absolute
23363 if (isset($val[2])) {
23364 // get curve parameters
23365 $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
23367 foreach ($rawparams as $ck => $cp) {
23368 $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit
, false);
23369 if (abs($params[$ck]) < $minlen) {
23370 // aproximate little values to zero
23375 // store current origin point
23378 switch (strtoupper($cmd)) {
23379 case 'M': { // moveto
23380 foreach ($params as $ck => $cp) {
23381 if (($ck %
2) == 0) {
23382 $x = $cp +
$xoffset;
23384 $y = $cp +
$yoffset;
23385 if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23387 $this->_outPoint($x, $y);
23390 $this->_outLine($x, $y);
23395 $xmin = min($xmin, $x);
23396 $ymin = min($ymin, $y);
23397 $xmax = max($xmax, $x);
23398 $ymax = max($ymax, $y);
23407 case 'L': { // lineto
23408 foreach ($params as $ck => $cp) {
23409 if (($ck %
2) == 0) {
23410 $x = $cp +
$xoffset;
23412 $y = $cp +
$yoffset;
23413 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23414 $this->_outLine($x, $y);
23418 $xmin = min($xmin, $x);
23419 $ymin = min($ymin, $y);
23420 $xmax = max($xmax, $x);
23421 $ymax = max($ymax, $y);
23430 case 'H': { // horizontal lineto
23431 foreach ($params as $ck => $cp) {
23432 $x = $cp +
$xoffset;
23433 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23434 $this->_outLine($x, $y);
23438 $xmin = min($xmin, $x);
23439 $xmax = max($xmax, $x);
23446 case 'V': { // vertical lineto
23447 foreach ($params as $ck => $cp) {
23448 $y = $cp +
$yoffset;
23449 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23450 $this->_outLine($x, $y);
23454 $ymin = min($ymin, $y);
23455 $ymax = max($ymax, $y);
23462 case 'C': { // curveto
23463 foreach ($params as $ck => $cp) {
23464 $params[$ck] = $cp;
23465 if ((($ck +
1) %
6) == 0) {
23466 $x1 = $params[($ck - 5)] +
$xoffset;
23467 $y1 = $params[($ck - 4)] +
$yoffset;
23468 $x2 = $params[($ck - 3)] +
$xoffset;
23469 $y2 = $params[($ck - 2)] +
$yoffset;
23470 $x = $params[($ck - 1)] +
$xoffset;
23471 $y = $params[($ck)] +
$yoffset;
23472 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23473 $xmin = min($xmin, $x, $x1, $x2);
23474 $ymin = min($ymin, $y, $y1, $y2);
23475 $xmax = max($xmax, $x, $x1, $x2);
23476 $ymax = max($ymax, $y, $y1, $y2);
23485 case 'S': { // shorthand/smooth curveto
23486 foreach ($params as $ck => $cp) {
23487 $params[$ck] = $cp;
23488 if ((($ck +
1) %
4) == 0) {
23489 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
23490 $x1 = (2 * $x) - $x2;
23491 $y1 = (2 * $y) - $y2;
23496 $x2 = $params[($ck - 3)] +
$xoffset;
23497 $y2 = $params[($ck - 2)] +
$yoffset;
23498 $x = $params[($ck - 1)] +
$xoffset;
23499 $y = $params[($ck)] +
$yoffset;
23500 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23501 $xmin = min($xmin, $x, $x1, $x2);
23502 $ymin = min($ymin, $y, $y1, $y2);
23503 $xmax = max($xmax, $x, $x1, $x2);
23504 $ymax = max($ymax, $y, $y1, $y2);
23513 case 'Q': { // quadratic Bezier curveto
23514 foreach ($params as $ck => $cp) {
23515 $params[$ck] = $cp;
23516 if ((($ck +
1) %
4) == 0) {
23517 // convert quadratic points to cubic points
23518 $x1 = $params[($ck - 3)] +
$xoffset;
23519 $y1 = $params[($ck - 2)] +
$yoffset;
23520 $xa = ($x +
(2 * $x1)) / 3;
23521 $ya = ($y +
(2 * $y1)) / 3;
23522 $x = $params[($ck - 1)] +
$xoffset;
23523 $y = $params[($ck)] +
$yoffset;
23524 $xb = ($x +
(2 * $x1)) / 3;
23525 $yb = ($y +
(2 * $y1)) / 3;
23526 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23527 $xmin = min($xmin, $x, $xa, $xb);
23528 $ymin = min($ymin, $y, $ya, $yb);
23529 $xmax = max($xmax, $x, $xa, $xb);
23530 $ymax = max($ymax, $y, $ya, $yb);
23539 case 'T': { // shorthand/smooth quadratic Bezier curveto
23540 foreach ($params as $ck => $cp) {
23541 $params[$ck] = $cp;
23542 if (($ck %
2) != 0) {
23543 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
23544 $x1 = (2 * $x) - $x1;
23545 $y1 = (2 * $y) - $y1;
23550 // convert quadratic points to cubic points
23551 $xa = ($x +
(2 * $x1)) / 3;
23552 $ya = ($y +
(2 * $y1)) / 3;
23553 $x = $params[($ck - 1)] +
$xoffset;
23554 $y = $params[($ck)] +
$yoffset;
23555 $xb = ($x +
(2 * $x1)) / 3;
23556 $yb = ($y +
(2 * $y1)) / 3;
23557 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23558 $xmin = min($xmin, $x, $xa, $xb);
23559 $ymin = min($ymin, $y, $ya, $yb);
23560 $xmax = max($xmax, $x, $xa, $xb);
23561 $ymax = max($ymax, $y, $ya, $yb);
23570 case 'A': { // elliptical arc
23571 foreach ($params as $ck => $cp) {
23572 $params[$ck] = $cp;
23573 if ((($ck +
1) %
7) == 0) {
23576 $rx = abs($params[($ck - 6)]);
23577 $ry = abs($params[($ck - 5)]);
23578 $ang = -$rawparams[($ck - 4)];
23579 $angle = deg2rad($ang);
23580 $fa = $rawparams[($ck - 3)]; // large-arc-flag
23581 $fs = $rawparams[($ck - 2)]; // sweep-flag
23582 $x = $params[($ck - 1)] +
$xoffset;
23583 $y = $params[$ck] +
$yoffset;
23584 if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
23585 // endpoints are almost identical
23586 $xmin = min($xmin, $x);
23587 $ymin = min($ymin, $y);
23588 $xmax = max($xmax, $x);
23589 $ymax = max($ymax, $y);
23591 $cos_ang = cos($angle);
23592 $sin_ang = sin($angle);
23593 $a = (($x0 - $x) / 2);
23594 $b = (($y0 - $y) / 2);
23595 $xa = ($a * $cos_ang) - ($b * $sin_ang);
23596 $ya = ($a * $sin_ang) +
($b * $cos_ang);
23601 $delta = ($xa2 / $rx2) +
($ya2 / $ry2);
23603 $rx *= sqrt($delta);
23604 $ry *= sqrt($delta);
23608 $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
23609 if ($numerator < 0) {
23612 $root = sqrt($numerator / (($rx2 * $ya2) +
($ry2 * $xa2)));
23617 $cax = $root * (($rx * $ya) / $ry);
23618 $cay = -$root * (($ry * $xa) / $rx);
23619 // coordinates of ellipse center
23620 $cx = ($cax * $cos_ang) - ($cay * $sin_ang) +
(($x0 +
$x) / 2);
23621 $cy = ($cax * $sin_ang) +
($cay * $cos_ang) +
(($y0 +
$y) / 2);
23623 $angs = TCPDF_STATIC
::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
23624 $dang = TCPDF_STATIC
::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
23625 if (($fs == 0) AND ($dang > 0)) {
23626 $dang -= (2 * M_PI
);
23627 } elseif (($fs == 1) AND ($dang < 0)) {
23628 $dang +
= (2 * M_PI
);
23630 $angf = $angs - $dang;
23631 if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
23637 $angs = round(rad2deg($angs), 6);
23638 $angf = round(rad2deg($angf), 6);
23639 // covent angles to positive values
23640 if (($angs < 0) AND ($angf < 0)) {
23645 if (($key == 0) AND (isset($paths[($key +
1)][1])) AND (trim($paths[($key +
1)][1]) == 'z')) {
23648 list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
23649 $xmin = min($xmin, $x, $axmin);
23650 $ymin = min($ymin, $y, $aymin);
23651 $xmax = max($xmax, $x, $axmax);
23652 $ymax = max($ymax, $y, $aymax);
23672 return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
23676 * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***)
23677 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
23678 * @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.
23679 * @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.
23680 * @param $ctm (array) tranformation matrix for clipping mode (starting transformation matrix).
23681 * @author Nicola Asuni
23682 * @since 5.0.000 (2010-05-02)
23685 protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
23686 // check if we are in clip mode
23687 if ($this->svgclipmode
) {
23688 $this->svgclippaths
[$this->svgclipid
][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm
[$this->svgclipid
]);
23691 if ($this->svgdefsmode
AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
23692 if (isset($attribs['id'])) {
23693 $attribs['child_elements'] = array();
23694 $this->svgdefs
[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23697 if (end($this->svgdefs
) !== FALSE) {
23698 $last_svgdefs_id = key($this->svgdefs
);
23699 if (isset($this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'])) {
23700 $attribs['id'] = 'DF_'.(count($this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements']) +
1);
23701 $this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23708 if ($parser == 'clip-path') {
23709 // set clipping mode
23712 // get styling properties
23713 $prev_svgstyle = $this->svgstyles
[max(0,(count($this->svgstyles
) - 1))]; // previous style
23714 $svgstyle = $this->svgstyles
[0]; // set default style
23715 if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) {
23716 // default fill attribute for clipping
23717 $attribs['fill'] = 'none';
23719 if (isset($attribs['style']) AND !TCPDF_STATIC
::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) {
23720 // fix style for regular expression
23721 $attribs['style'] = ';'.$attribs['style'];
23723 foreach ($prev_svgstyle as $key => $val) {
23724 if (in_array($key, TCPDF_IMAGES
::$svginheritprop)) {
23725 // inherit previous value
23726 $svgstyle[$key] = $val;
23728 if (isset($attribs[$key]) AND !TCPDF_STATIC
::empty_string($attribs[$key])) {
23729 // specific attribute settings
23730 if ($attribs[$key] == 'inherit') {
23731 $svgstyle[$key] = $val;
23733 $svgstyle[$key] = $attribs[$key];
23735 } elseif (isset($attribs['style']) AND !TCPDF_STATIC
::empty_string($attribs['style'])) {
23736 // CSS style syntax
23737 $attrval = array();
23738 if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
23739 if ($attrval[1] == 'inherit') {
23740 $svgstyle[$key] = $val;
23742 $svgstyle[$key] = $attrval[1];
23747 // transformation matrix
23748 if (!empty($ctm)) {
23751 $tm = array(1,0,0,1,0,0);
23753 if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
23754 $tm = TCPDF_STATIC
::getTransformationMatrixProduct($tm, TCPDF_STATIC
::getSVGTransformMatrix($attribs['transform']));
23756 $svgstyle['transfmatrix'] = $tm;
23757 $invisible = false;
23758 if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
23759 // the current graphics element is invisible (nothing is painted)
23765 $this->svgdefsmode
= true;
23773 $this->svgclipmode
= true;
23774 if (!isset($attribs['id'])) {
23775 $attribs['id'] = 'CP_'.(count($this->svgcliptm
) +
1);
23777 $this->svgclipid
= $attribs['id'];
23778 $this->svgclippaths
[$this->svgclipid
] = array();
23779 $this->svgcliptm
[$this->svgclipid
] = $tm;
23783 // start of SVG object
23787 // group together related graphics elements
23788 array_push($this->svgstyles
, $svgstyle);
23789 $this->StartTransform();
23790 $x = (isset($attribs['x'])?
$attribs['x']:0);
23791 $y = (isset($attribs['y'])?
$attribs['y']:0);
23792 $w = 1;//(isset($attribs['width'])?$attribs['width']:1);
23793 $h = 1;//(isset($attribs['height'])?$attribs['height']:1);
23794 $tm = TCPDF_STATIC
::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
23795 $this->SVGTransform($tm);
23796 $this->setSVGStyles($svgstyle, $prev_svgstyle);
23799 case 'linearGradient': {
23800 if ($this->pdfa_mode
) {
23803 if (!isset($attribs['id'])) {
23804 $attribs['id'] = 'GR_'.(count($this->svggradients
) +
1);
23806 $this->svggradientid
= $attribs['id'];
23807 $this->svggradients
[$this->svggradientid
] = array();
23808 $this->svggradients
[$this->svggradientid
]['type'] = 2;
23809 $this->svggradients
[$this->svggradientid
]['stops'] = array();
23810 if (isset($attribs['gradientUnits'])) {
23811 $this->svggradients
[$this->svggradientid
]['gradientUnits'] = $attribs['gradientUnits'];
23813 $this->svggradients
[$this->svggradientid
]['gradientUnits'] = 'objectBoundingBox';
23815 //$attribs['spreadMethod']
23816 if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2'])))
23817 OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%'))
23818 OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%'))
23819 OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%'))
23820 OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) {
23821 $this->svggradients
[$this->svggradientid
]['mode'] = 'percentage';
23823 $this->svggradients
[$this->svggradientid
]['mode'] = 'measure';
23825 $x1 = (isset($attribs['x1'])?
$attribs['x1']:'0');
23826 $y1 = (isset($attribs['y1'])?
$attribs['y1']:'0');
23827 $x2 = (isset($attribs['x2'])?
$attribs['x2']:'100');
23828 $y2 = (isset($attribs['y2'])?
$attribs['y2']:'0');
23829 if (isset($attribs['gradientTransform'])) {
23830 $this->svggradients
[$this->svggradientid
]['gradientTransform'] = TCPDF_STATIC
::getSVGTransformMatrix($attribs['gradientTransform']);
23832 $this->svggradients
[$this->svggradientid
]['coords'] = array($x1, $y1, $x2, $y2);
23833 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23834 // gradient is defined on another place
23835 $this->svggradients
[$this->svggradientid
]['xref'] = substr($attribs['xlink:href'], 1);
23839 case 'radialGradient': {
23840 if ($this->pdfa_mode
) {
23843 if (!isset($attribs['id'])) {
23844 $attribs['id'] = 'GR_'.(count($this->svggradients
) +
1);
23846 $this->svggradientid
= $attribs['id'];
23847 $this->svggradients
[$this->svggradientid
] = array();
23848 $this->svggradients
[$this->svggradientid
]['type'] = 3;
23849 $this->svggradients
[$this->svggradientid
]['stops'] = array();
23850 if (isset($attribs['gradientUnits'])) {
23851 $this->svggradients
[$this->svggradientid
]['gradientUnits'] = $attribs['gradientUnits'];
23853 $this->svggradients
[$this->svggradientid
]['gradientUnits'] = 'objectBoundingBox';
23855 //$attribs['spreadMethod']
23856 if (((!isset($attribs['cx'])) AND (!isset($attribs['cy'])))
23857 OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%'))
23858 OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')) )) {
23859 $this->svggradients
[$this->svggradientid
]['mode'] = 'percentage';
23861 $this->svggradients
[$this->svggradientid
]['mode'] = 'measure';
23863 $cx = (isset($attribs['cx']) ?
$attribs['cx'] : 0.5);
23864 $cy = (isset($attribs['cy']) ?
$attribs['cy'] : 0.5);
23865 $fx = (isset($attribs['fx']) ?
$attribs['fx'] : $cx);
23866 $fy = (isset($attribs['fy']) ?
$attribs['fy'] : $cy);
23867 $r = (isset($attribs['r']) ?
$attribs['r'] : 0.5);
23868 if (isset($attribs['gradientTransform'])) {
23869 $this->svggradients
[$this->svggradientid
]['gradientTransform'] = TCPDF_STATIC
::getSVGTransformMatrix($attribs['gradientTransform']);
23871 $this->svggradients
[$this->svggradientid
]['coords'] = array($cx, $cy, $fx, $fy, $r);
23872 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23873 // gradient is defined on another place
23874 $this->svggradients
[$this->svggradientid
]['xref'] = substr($attribs['xlink:href'], 1);
23880 if (substr($attribs['offset'], -1) == '%') {
23881 $offset = floatval(substr($attribs['offset'], -1)) / 100;
23883 $offset = floatval($attribs['offset']);
23888 $stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS
::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors
):'black';
23889 $opacity = isset($svgstyle['stop-opacity'])?
$svgstyle['stop-opacity']:1;
23890 $this->svggradients
[$this->svggradientid
]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
23898 if (isset($attribs['d'])) {
23899 $d = trim($attribs['d']);
23901 $x = (isset($attribs['x'])?
$attribs['x']:0);
23902 $y = (isset($attribs['y'])?
$attribs['y']:0);
23903 $w = (isset($attribs['width'])?
$attribs['width']:1);
23904 $h = (isset($attribs['height'])?
$attribs['height']:1);
23905 $tm = TCPDF_STATIC
::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
23907 $this->SVGTransform($tm);
23908 $this->SVGPath($d, 'CNZ');
23910 $this->StartTransform();
23911 $this->SVGTransform($tm);
23912 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ'));
23913 if (!empty($obstyle)) {
23914 $this->SVGPath($d, $obstyle);
23916 $this->StopTransform();
23927 $x = (isset($attribs['x'])?
$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false):0);
23928 $y = (isset($attribs['y'])?
$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false):0);
23929 $w = (isset($attribs['width'])?
$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit
, false):0);
23930 $h = (isset($attribs['height'])?
$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit
, false):0);
23931 $rx = (isset($attribs['rx'])?
$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit
, false):0);
23932 $ry = (isset($attribs['ry'])?
$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit
, false):$rx);
23934 $this->SVGTransform($tm);
23935 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
23937 $this->StartTransform();
23938 $this->SVGTransform($tm);
23939 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
23940 if (!empty($obstyle)) {
23941 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
23943 $this->StopTransform();
23951 $r = (isset($attribs['r']) ?
$this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit
, false) : 0);
23952 $cx = (isset($attribs['cx']) ?
$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit
, false) : (isset($attribs['x']) ?
$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false) : 0));
23953 $cy = (isset($attribs['cy']) ?
$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit
, false) : (isset($attribs['y']) ?
$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false) : 0));
23959 $this->SVGTransform($tm);
23960 $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
23962 $this->StartTransform();
23963 $this->SVGTransform($tm);
23964 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
23965 if (!empty($obstyle)) {
23966 $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
23968 $this->StopTransform();
23976 $rx = (isset($attribs['rx']) ?
$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit
, false) : 0);
23977 $ry = (isset($attribs['ry']) ?
$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit
, false) : 0);
23978 $cx = (isset($attribs['cx']) ?
$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit
, false) : (isset($attribs['x']) ?
$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false) : 0));
23979 $cy = (isset($attribs['cy']) ?
$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit
, false) : (isset($attribs['y']) ?
$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false) : 0));
23985 $this->SVGTransform($tm);
23986 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
23988 $this->StartTransform();
23989 $this->SVGTransform($tm);
23990 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
23991 if (!empty($obstyle)) {
23992 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
23994 $this->StopTransform();
24002 $x1 = (isset($attribs['x1'])?
$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit
, false):0);
24003 $y1 = (isset($attribs['y1'])?
$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit
, false):0);
24004 $x2 = (isset($attribs['x2'])?
$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit
, false):0);
24005 $y2 = (isset($attribs['y2'])?
$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit
, false):0);
24008 $w = abs($x2 - $x1);
24009 $h = abs($y2 - $y1);
24011 $this->StartTransform();
24012 $this->SVGTransform($tm);
24013 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
24014 $this->Line($x1, $y1, $x2, $y2);
24015 $this->StopTransform();
24024 $points = (isset($attribs['points'])?
$attribs['points']:'0 0');
24025 $points = trim($points);
24026 // note that point may use a complex syntax not covered here
24027 $points = preg_split('/[\,\s]+/si', $points);
24028 if (count($points) < 4) {
24032 $xmin = 2147483647;
24034 $ymin = 2147483647;
24036 foreach ($points as $key => $val) {
24037 $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit
, false);
24038 if (($key %
2) == 0) {
24040 $xmin = min($xmin, $p[$key]);
24041 $xmax = max($xmax, $p[$key]);
24044 $ymin = min($ymin, $p[$key]);
24045 $ymax = max($ymax, $p[$key]);
24050 $w = ($xmax - $xmin);
24051 $h = ($ymax - $ymin);
24052 if ($name == 'polyline') {
24053 $this->StartTransform();
24054 $this->SVGTransform($tm);
24055 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
24056 if (!empty($obstyle)) {
24057 $this->PolyLine($p, $obstyle, array(), array());
24059 $this->StopTransform();
24060 } else { // polygon
24062 $this->SVGTransform($tm);
24063 $this->Polygon($p, 'CNZ', array(), array(), true);
24065 $this->StartTransform();
24066 $this->SVGTransform($tm);
24067 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
24068 if (!empty($obstyle)) {
24069 $this->Polygon($p, $obstyle, array(), array(), true);
24071 $this->StopTransform();
24081 if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
24084 $x = (isset($attribs['x'])?
$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false):0);
24085 $y = (isset($attribs['y'])?
$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false):0);
24086 $w = (isset($attribs['width'])?
$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit
, false):0);
24087 $h = (isset($attribs['height'])?
$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit
, false):0);
24088 $img = $attribs['xlink:href'];
24090 $this->StartTransform();
24091 $this->SVGTransform($tm);
24092 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
24093 if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
24094 // embedded image encoded as base64
24095 $img = '@'.base64_decode(substr($img, strlen($m[0])));
24098 if (!TCPDF_STATIC
::empty_string($this->svgdir
) AND (($img[0] == '.') OR (basename($img) == $img))) {
24099 // replace relative path with full server path
24100 $img = $this->svgdir
.'/'.$img;
24102 if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
24103 $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
24104 if (($findroot === false) OR ($findroot > 1)) {
24105 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
24106 $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
24108 $img = $_SERVER['DOCUMENT_ROOT'].$img;
24112 $img = urldecode($img);
24113 $testscrtype = @parse_url
($img);
24114 if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
24115 // convert URL to server path
24116 $img = str_replace(K_PATH_URL
, K_PATH_MAIN
, $img);
24120 $imgtype = TCPDF_IMAGES
::getImageFileType($img);
24121 if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
24122 $this->ImageEps($img, $x, $y, $w, $h);
24123 } elseif ($imgtype == 'svg') {
24124 $this->ImageSVG($img, $x, $y, $w, $h);
24126 $this->Image($img, $x, $y, $w, $h);
24128 $this->StopTransform();
24135 // only basic support - advanced features must be implemented
24136 $this->svgtextmode
['invisible'] = $invisible;
24140 array_push($this->svgstyles
, $svgstyle);
24141 if (isset($attribs['x'])) {
24142 $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit
, false);
24143 } elseif ($name == 'tspan') {
24148 if (isset($attribs['dx'])) {
24149 $x +
= $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit
, false);
24151 if (isset($attribs['y'])) {
24152 $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit
, false);
24153 } elseif ($name == 'tspan') {
24158 if (isset($attribs['dy'])) {
24159 $y +
= $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit
, false);
24161 $svgstyle['text-color'] = $svgstyle['fill'];
24162 $this->svgtext
= '';
24163 if (isset($svgstyle['text-anchor'])) {
24164 $this->svgtextmode
['text-anchor'] = $svgstyle['text-anchor'];
24166 $this->svgtextmode
['text-anchor'] = 'start';
24168 if (isset($svgstyle['direction'])) {
24169 if ($svgstyle['direction'] == 'rtl') {
24170 $this->svgtextmode
['rtl'] = true;
24172 $this->svgtextmode
['rtl'] = false;
24175 $this->svgtextmode
['rtl'] = false;
24177 if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
24178 $this->svgtextmode
['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit
, false);
24180 $this->svgtextmode
['stroke'] = false;
24182 $this->StartTransform();
24183 $this->SVGTransform($tm);
24184 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
24191 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24192 $svgdefid = substr($attribs['xlink:href'], 1);
24193 if (isset($this->svgdefs
[$svgdefid])) {
24194 $use = $this->svgdefs
[$svgdefid];
24195 if (isset($attribs['xlink:href'])) {
24196 unset($attribs['xlink:href']);
24198 if (isset($attribs['id'])) {
24199 unset($attribs['id']);
24201 if (isset($use['attribs']['x']) AND isset($attribs['x'])) {
24202 $attribs['x'] +
= $use['attribs']['x'];
24204 if (isset($use['attribs']['y']) AND isset($attribs['y'])) {
24205 $attribs['y'] +
= $use['attribs']['y'];
24207 if (empty($attribs['style'])) {
24208 $attribs['style'] = '';
24210 if (!empty($use['attribs']['style'])) {
24212 $attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']);
24214 $attribs = array_merge($use['attribs'], $attribs);
24215 $this->startSVGElementHandler($parser, $use['name'], $attribs);
24225 // process child elements
24226 if (!empty($attribs['child_elements'])) {
24227 $child_elements = $attribs['child_elements'];
24228 unset($attribs['child_elements']);
24229 foreach($child_elements as $child_element) {
24230 if (empty($child_element['attribs']['closing_tag'])) {
24231 $this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']);
24233 if (isset($child_element['attribs']['content'])) {
24234 $this->svgtext
= $child_element['attribs']['content'];
24236 $this->endSVGElementHandler('child-tag', $child_element['name']);
24243 * Sets the closing SVG element handler function for the XML parser.
24244 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
24245 * @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.
24246 * @author Nicola Asuni
24247 * @since 5.0.000 (2010-05-02)
24250 protected function endSVGElementHandler($parser, $name) {
24251 if ($this->svgdefsmode
AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {;
24252 if (end($this->svgdefs
) !== FALSE) {
24253 $last_svgdefs_id = key($this->svgdefs
);
24254 if (isset($this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'])) {
24255 foreach($this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) {
24256 if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) {
24257 $this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext
));
24261 if ($this->svgdefs
[$last_svgdefs_id]['name'] == $name) {
24262 $this->svgdefs
[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext
));
24271 $this->svgdefsmode
= false;
24276 $this->svgclipmode
= false;
24280 // ungroup: remove last style from array
24281 array_pop($this->svgstyles
);
24282 $this->StopTransform();
24287 if ($this->svgtextmode
['invisible']) {
24288 // This implementation must be fixed to following the rule:
24289 // 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.
24293 $text = $this->svgtext
;
24294 //$text = $this->stringTrim($text);
24295 $textlen = $this->GetStringWidth($text);
24296 if ($this->svgtextmode
['text-anchor'] != 'start') {
24297 // check if string is RTL text
24298 if ($this->svgtextmode
['text-anchor'] == 'end') {
24299 if ($this->svgtextmode
['rtl']) {
24300 $this->x +
= $textlen;
24302 $this->x
-= $textlen;
24304 } elseif ($this->svgtextmode
['text-anchor'] == 'middle') {
24305 if ($this->svgtextmode
['rtl']) {
24306 $this->x +
= ($textlen / 2);
24308 $this->x
-= ($textlen / 2);
24312 $textrendermode = $this->textrendermode
;
24313 $textstrokewidth = $this->textstrokewidth
;
24314 $this->setTextRenderingMode($this->svgtextmode
['stroke'], true, false);
24315 if ($name == 'text') {
24316 // store current coordinates
24320 $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
24321 if ($name == 'text') {
24322 // restore coordinates
24326 // restore previous rendering mode
24327 $this->textrendermode
= $textrendermode;
24328 $this->textstrokewidth
= $textstrokewidth;
24329 $this->svgtext
= '';
24330 $this->StopTransform();
24331 if (!$this->svgdefsmode
) {
24332 array_pop($this->svgstyles
);
24343 * Sets the character data handler function for the XML parser.
24344 * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
24345 * @param $data (string) The second parameter, data, contains the character data as a string.
24346 * @author Nicola Asuni
24347 * @since 5.0.000 (2010-05-02)
24350 protected function segSVGContentHandler($parser, $data) {
24351 $this->svgtext
.= $data;
24354 // --- END SVG METHODS -----------------------------------------------------
24356 } // END OF TCPDF CLASS
24358 //============================================================+
24360 //============================================================+