ILIAS  release_5-0 Revision 5.0.0-1144-gc4397b1f870
tcpdf.php
Go to the documentation of this file.
1<?php
2//============================================================+
3// File name : tcpdf.php
4// Version : 6.0.015
5// Begin : 2002-08-03
6// Last Update : 2013-05-13
7// Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
8// License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
9// -------------------------------------------------------------------
10// Copyright (C) 2002-2013 Nicola Asuni - Tecnick.com LTD
11//
12// This file is part of TCPDF software library.
13//
14// TCPDF is free software: you can redistribute it and/or modify it
15// under the terms of the GNU Lesser General Public License as
16// published by the Free Software Foundation, either version 3 of the
17// License, or (at your option) any later version.
18//
19// TCPDF is distributed in the hope that it will be useful, but
20// WITHOUT ANY WARRANTY; without even the implied warranty of
21// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22// See the GNU Lesser General Public License for more details.
23//
24// You should have received a copy of the License
25// along with TCPDF. If not, see
26// <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>.
27//
28// See LICENSE.TXT file for more information.
29// -------------------------------------------------------------------
30//
31// Description :
32// This is a PHP class for generating PDF documents without requiring external extensions.
33//
34// NOTE:
35// This class was originally derived in 2002 from the Public
36// Domain FPDF class by Olivier Plathey (http://www.fpdf.org),
37// but now is almost entirely rewritten and contains thousands of
38// new lines of code and hundreds new features.
39//
40// Main features:
41// * no external libraries are required for the basic functions;
42// * all standard page formats, custom page formats, custom margins and units of measure;
43// * UTF-8 Unicode and Right-To-Left languages;
44// * TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;
45// * font subsetting;
46// * methods to publish some XHTML + CSS code, Javascript and Forms;
47// * images, graphic (geometric figures) and transformation methods;
48// * supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)
49// * 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417;
50// * JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;
51// * automatic page header and footer management;
52// * document encryption up to 256 bit and digital signature certifications;
53// * transactions to UNDO commands;
54// * PDF annotations, including links, text and file attachments;
55// * text rendering modes (fill, stroke and clipping);
56// * multiple columns mode;
57// * no-write page regions;
58// * bookmarks, named destinations and table of content;
59// * text hyphenation;
60// * text stretching and spacing (tracking);
61// * automatic page break, line break and text alignments including justification;
62// * automatic page numbering and page groups;
63// * move and delete pages;
64// * page compression (requires php-zlib extension);
65// * XOBject Templates;
66// * Layers and object visibility.
67// * PDF/A-1b support.
68//
69// -----------------------------------------------------------
70// THANKS TO:
71//
72// Olivier Plathey (http://www.fpdf.org) for original FPDF.
73// Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support.
74// Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
75// Warren Sherliker (wsherliker@gmail.com) for better image handling.
76// dullus for text Justification.
77// Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute.
78// Patrick Benny for text stretch suggestion on Cell().
79// Johannes G�ntert for JavaScript support.
80// Denis Van Nuffelen for Dynamic Form.
81// Jacek Czekaj for multibyte justification
82// Anthony Ferrara for the reintroduction of legacy image methods.
83// Sourceforge user 1707880 (hucste) for line-through mode.
84// Larry Stanbery for page groups.
85// Martin Hall-May for transparency.
86// Aaron C. Spike for Polycurve method.
87// Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
88// Moritz Wagner and Andreas Wurmser for graphic functions.
89// Andrew Whitehead for core fonts support.
90// Esteban Jo�l Mar�n for OpenType font conversion.
91// Teus Hagen for several suggestions and fixes.
92// Yukihiro Nakadaira for CID-0 CJK fonts fixes.
93// Kosmas Papachristos for some CSS improvements.
94// Marcel Partap for some fixes.
95// Won Kyu Park for several suggestions, fixes and patches.
96// Dominik Dzienia for QR-code support.
97// Laurent Minguet for some suggestions.
98// Christian Deligant for some suggestions and fixes.
99// Travis Harris for crop mark suggestion.
100// Aleksey Kuznetsov for some suggestions and text shadows.
101// Jim Hanlon for several suggestions and patches.
102// Anyone else that has reported a bug or sent a suggestion.
103//============================================================+
104
145// Load main configuration file only if the K_TCPDF_EXTERNAL_CONFIG constant is set to false.
146if (!defined('K_TCPDF_EXTERNAL_CONFIG')) {
147 // define a list of default config files in order of priority
148 $tcpdf_config_files = array(dirname(__FILE__).'/config/tcpdf_config.php', '/etc/php-tcpdf/tcpdf_config.php', '/etc/tcpdf/tcpdf_config.php', '/etc/tcpdf_config.php');
149 foreach ($tcpdf_config_files as $tcpdf_config) {
150 if (file_exists($tcpdf_config) AND is_readable($tcpdf_config)) {
151 require_once($tcpdf_config);
152 break;
153 }
154 }
155}
156if (!defined('K_PATH_MAIN')) {
157 $this->Error('Unable to include configuration file.');
158}
159
160// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
161
162// TCPDF static font methods and data
163require_once(dirname( __FILE__ ) . '/include/tcpdf_font_data.php');
164// TCPDF static font methods and data
165require_once(dirname( __FILE__ ) . '/include/tcpdf_fonts.php');
166// TCPDF static color methods and data
167require_once(dirname( __FILE__ ) . '/include/tcpdf_colors.php');
168// TCPDF static image methods and data
169require_once(dirname( __FILE__ ) . '/include/tcpdf_images.php');
170// TCPDF static methods and data
171require_once(dirname( __FILE__ ) . '/include/tcpdf_static.php');
172
173// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
174
184class TCPDF {
185
186 // Protected properties
187
192 protected $page;
193
198 protected $n;
199
204 protected $offsets = array();
205
210 protected $pageobjects = array();
211
216 protected $buffer;
217
222 protected $pages = array();
223
228 protected $state;
229
234 protected $compress;
235
241
246 protected $pagedim = array();
247
252 protected $k;
253
258 protected $fwPt;
259
264 protected $fhPt;
265
270 protected $wPt;
271
276 protected $hPt;
277
282 protected $w;
283
288 protected $h;
289
294 protected $lMargin;
295
300 protected $rMargin;
301
306 protected $clMargin;
307
312 protected $crMargin;
313
318 protected $tMargin;
319
324 protected $bMargin;
325
331 protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
332
338 protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
339
344 protected $x;
345
350 protected $y;
351
356 protected $lasth;
357
362 protected $LineWidth;
363
368 protected $CoreFonts;
369
374 protected $fonts = array();
375
380 protected $FontFiles = array();
381
386 protected $diffs = array();
387
392 protected $images = array();
393
398 protected $cached_files = array();
399
404 protected $PageAnnots = array();
405
410 protected $links = array();
411
416 protected $FontFamily;
417
422 protected $FontStyle;
423
429 protected $FontAscent;
430
436 protected $FontDescent;
437
442 protected $underline;
443
448 protected $overline;
449
454 protected $CurrentFont;
455
460 protected $FontSizePt;
461
466 protected $FontSize;
467
472 protected $DrawColor;
473
478 protected $FillColor;
479
484 protected $TextColor;
485
490 protected $ColorFlag;
491
496 protected $AutoPageBreak;
497
503
508 protected $InHeader = false;
509
514 protected $InFooter = false;
515
520 protected $ZoomMode;
521
526 protected $LayoutMode;
527
532 protected $docinfounicode = true;
533
538 protected $title = '';
539
544 protected $subject = '';
545
550 protected $author = '';
551
556 protected $keywords = '';
557
562 protected $creator = '';
563
569
576 protected $img_rb_x;
577
584 protected $img_rb_y;
585
592 protected $imgscale = 1;
593
600 protected $isunicode = false;
601
607 protected $PDFVersion = '1.7';
608
613 protected $header_xobjid = -1;
614
619 protected $header_xobj_autoreset = false;
620
625 protected $header_margin;
626
631 protected $footer_margin;
632
639
646
651 protected $header_font;
652
657 protected $footer_font;
658
663 protected $l;
664
669 protected $barcode = false;
670
675 protected $print_header = true;
676
681 protected $print_footer = true;
682
687 protected $header_logo = '';
688
693 protected $header_logo_width = 30;
694
699 protected $header_title = '';
700
705 protected $header_string = '';
706
712 protected $header_text_color = array(0,0,0);
713
719 protected $header_line_color = array(0,0,0);
720
726 protected $footer_text_color = array(0,0,0);
727
733 protected $footer_line_color = array(0,0,0);
734
740 protected $txtshadow = array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal');
741
747
748 // variables for html parser
749
754 protected $HREF = array();
755
760 protected $fontlist = array();
761
766 protected $fgcolor;
767
772 protected $listordered = array();
773
778 protected $listcount = array();
779
784 protected $listnum = 0;
785
790 protected $listindent = 0;
791
796 protected $listindentlevel = 0;
797
802 protected $bgcolor;
803
808 protected $tempfontsize = 10;
809
814 protected $lispacer = '';
815
821 protected $encoding = 'UTF-8';
822
829
835 protected $rtl = false;
836
842 protected $tmprtl = false;
843
844 // --- Variables used for document encryption:
845
851 protected $encrypted;
852
858 protected $encryptdata = array();
859
865 protected $last_enc_key;
866
873
879 protected $file_id;
880
881 // --- bookmark ---
882
888 protected $outlines = array();
889
895 protected $OutlineRoot;
896
897 // --- javascript and form ---
898
904 protected $javascript = '';
905
911 protected $n_js;
912
918 protected $linethrough;
919
925 protected $ur = array();
926
932 protected $dpi = 72;
933
939 protected $newpagegroup = array();
940
946 protected $pagegroups = array();
947
953 protected $currpagegroup = 0;
954
960 protected $extgstates;
961
967 protected $jpeg_quality;
968
974 protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
975
982
988 protected $PageMode;
989
995 protected $gradients = array();
996
1002 protected $intmrk = array();
1003
1009 protected $bordermrk = array();
1010
1016 protected $emptypagemrk = array();
1017
1023 protected $cntmrk = array();
1024
1030 protected $footerpos = array();
1031
1037 protected $footerlen = array();
1038
1044 protected $newline = true;
1045
1051 protected $endlinex = 0;
1052
1058 protected $linestyleWidth = '';
1059
1065 protected $linestyleCap = '0 J';
1066
1072 protected $linestyleJoin = '0 j';
1073
1079 protected $linestyleDash = '[] 0 d';
1080
1086 protected $openMarkedContent = false;
1087
1093 protected $htmlvspace = 0;
1094
1100 protected $spot_colors = array();
1101
1107 protected $lisymbol = '';
1108
1114 protected $epsmarker = 'x#!#EPS#!#x';
1115
1121 protected $transfmatrix = array();
1122
1128 protected $transfmatrix_key = 0;
1129
1135 protected $booklet = false;
1136
1142 protected $feps = 0.005;
1143
1149 protected $tagvspaces = array();
1150
1156 protected $customlistindent = -1;
1157
1163 protected $opencell = true;
1164
1170 protected $embeddedfiles = array();
1171
1177 protected $premode = false;
1178
1185 protected $transfmrk = array();
1186
1192 protected $htmlLinkColorArray = array(0, 0, 255);
1193
1199 protected $htmlLinkFontStyle = 'U';
1200
1206 protected $numpages = 0;
1207
1213 protected $pagelen = array();
1214
1220 protected $numimages = 0;
1221
1227 protected $imagekeys = array();
1228
1234 protected $bufferlen = 0;
1235
1241 protected $diskcache = false;
1242
1248 protected $numfonts = 0;
1249
1255 protected $fontkeys = array();
1256
1262 protected $font_obj_ids = array();
1263
1269 protected $pageopen = array();
1270
1276 protected $default_monospaced_font = 'courier';
1277
1283 protected $objcopy;
1284
1290 protected $cache_file_length = array();
1291
1297 protected $thead = '';
1298
1304 protected $theadMargins = array();
1305
1311 protected $sign = false;
1312
1318 protected $signature_data = array();
1319
1325 protected $signature_max_length = 11742;
1326
1332 protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
1333
1339 protected $empty_signature_appearance = array();
1340
1346 protected $re_spaces = '/[^\S\xa0]/';
1347
1353 protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
1354
1360 protected $sig_obj_id = 0;
1361
1367 protected $page_obj_id = array();
1368
1374 protected $form_obj_id = array();
1375
1381 protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1382
1388 protected $js_objects = array();
1389
1395 protected $form_action = '';
1396
1402 protected $form_enctype = 'application/x-www-form-urlencoded';
1403
1409 protected $form_mode = 'post';
1410
1416 protected $annotation_fonts = array();
1417
1423 protected $radiobutton_groups = array();
1424
1430 protected $radio_groups = array();
1431
1437 protected $textindent = 0;
1438
1445
1452
1458 protected $inthead = false;
1459
1465 protected $columns = array();
1466
1472 protected $num_columns = 1;
1473
1479 protected $current_column = 0;
1480
1486 protected $column_start_page = 0;
1487
1493 protected $maxselcol = array('page' => 0, 'column' => 0);
1494
1500 protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
1501
1507 protected $textrendermode = 0;
1508
1514 protected $textstrokewidth = 0;
1515
1521 protected $strokecolor;
1522
1528 protected $pdfunit = 'mm';
1529
1534 protected $tocpage = false;
1535
1541 protected $rasterize_vector_images = false;
1542
1548 protected $font_subsetting = true;
1549
1555 protected $default_graphic_vars = array();
1556
1562 protected $xobjects = array();
1563
1569 protected $inxobj = false;
1570
1576 protected $xobjid = '';
1577
1583 protected $font_stretching = 100;
1584
1590 protected $font_spacing = 0;
1591
1598 protected $page_regions = array();
1599
1604 protected $check_page_regions = true;
1605
1611 protected $pdflayers = array();
1612
1618 protected $dests = array();
1619
1625 protected $n_dests;
1626
1632 protected $efnames = array();
1633
1639 protected $svgdir = '';
1640
1646 protected $svgunit = 'px';
1647
1653 protected $svggradients = array();
1654
1660 protected $svggradientid = 0;
1661
1667 protected $svgdefsmode = false;
1668
1674 protected $svgdefs = array();
1675
1681 protected $svgclipmode = false;
1682
1688 protected $svgclippaths = array();
1689
1695 protected $svgcliptm = array();
1696
1702 protected $svgclipid = 0;
1703
1709 protected $svgtext = '';
1710
1716 protected $svgtextmode = array();
1717
1723 protected $svgstyles = array(array(
1724 'alignment-baseline' => 'auto',
1725 'baseline-shift' => 'baseline',
1726 'clip' => 'auto',
1727 'clip-path' => 'none',
1728 'clip-rule' => 'nonzero',
1729 'color' => 'black',
1730 'color-interpolation' => 'sRGB',
1731 'color-interpolation-filters' => 'linearRGB',
1732 'color-profile' => 'auto',
1733 'color-rendering' => 'auto',
1734 'cursor' => 'auto',
1735 'direction' => 'ltr',
1736 'display' => 'inline',
1737 'dominant-baseline' => 'auto',
1738 'enable-background' => 'accumulate',
1739 'fill' => 'black',
1740 'fill-opacity' => 1,
1741 'fill-rule' => 'nonzero',
1742 'filter' => 'none',
1743 'flood-color' => 'black',
1744 'flood-opacity' => 1,
1745 'font' => '',
1746 'font-family' => 'helvetica',
1747 'font-size' => 'medium',
1748 'font-size-adjust' => 'none',
1749 'font-stretch' => 'normal',
1750 'font-style' => 'normal',
1751 'font-variant' => 'normal',
1752 'font-weight' => 'normal',
1753 'glyph-orientation-horizontal' => '0deg',
1754 'glyph-orientation-vertical' => 'auto',
1755 'image-rendering' => 'auto',
1756 'kerning' => 'auto',
1757 'letter-spacing' => 'normal',
1758 'lighting-color' => 'white',
1759 'marker' => '',
1760 'marker-end' => 'none',
1761 'marker-mid' => 'none',
1762 'marker-start' => 'none',
1763 'mask' => 'none',
1764 'opacity' => 1,
1765 'overflow' => 'auto',
1766 'pointer-events' => 'visiblePainted',
1767 'shape-rendering' => 'auto',
1768 'stop-color' => 'black',
1769 'stop-opacity' => 1,
1770 'stroke' => 'none',
1771 'stroke-dasharray' => 'none',
1772 'stroke-dashoffset' => 0,
1773 'stroke-linecap' => 'butt',
1774 'stroke-linejoin' => 'miter',
1775 'stroke-miterlimit' => 4,
1776 'stroke-opacity' => 1,
1777 'stroke-width' => 1,
1778 'text-anchor' => 'start',
1779 'text-decoration' => 'none',
1780 'text-rendering' => 'auto',
1781 'unicode-bidi' => 'normal',
1782 'visibility' => 'visible',
1783 'word-spacing' => 'normal',
1784 'writing-mode' => 'lr-tb',
1785 'text-color' => 'black',
1786 'transfmatrix' => array(1, 0, 0, 1, 0, 0)
1787 ));
1788
1794 protected $force_srgb = false;
1795
1801 protected $pdfa_mode = false;
1802
1809
1816
1822 protected $custom_xmp = '';
1823
1830 protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0);
1831
1838 protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false);
1839
1845 protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
1846
1852 protected $tcpdflink = true;
1853
1859 protected $gdgammacache = array();
1860
1861 //------------------------------------------------------------
1862 // METHODS
1863 //------------------------------------------------------------
1864
1878 public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) {
1879 /* Set internal character encoding to ASCII */
1880 if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1881 $this->internal_encoding = mb_internal_encoding();
1882 mb_internal_encoding('ASCII');
1883 }
1884 $this->font_obj_ids = array();
1885 $this->page_obj_id = array();
1886 $this->form_obj_id = array();
1887 // set pdf/a mode
1888 $this->pdfa_mode = $pdfa;
1889 $this->force_srgb = false;
1890 // set disk caching
1891 $this->diskcache = $diskcache ? true : false;
1892 // set language direction
1893 $this->rtl = false;
1894 $this->tmprtl = false;
1895 // some checks
1896 $this->_dochecks();
1897 // initialization of properties
1898 $this->isunicode = $unicode;
1899 $this->page = 0;
1900 $this->transfmrk[0] = array();
1901 $this->pagedim = array();
1902 $this->n = 2;
1903 $this->buffer = '';
1904 $this->pages = array();
1905 $this->state = 0;
1906 $this->fonts = array();
1907 $this->FontFiles = array();
1908 $this->diffs = array();
1909 $this->images = array();
1910 $this->links = array();
1911 $this->gradients = array();
1912 $this->InFooter = false;
1913 $this->lasth = 0;
1914 $this->FontFamily = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
1915 $this->FontStyle = '';
1916 $this->FontSizePt = 12;
1917 $this->underline = false;
1918 $this->overline = false;
1919 $this->linethrough = false;
1920 $this->DrawColor = '0 G';
1921 $this->FillColor = '0 g';
1922 $this->TextColor = '0 g';
1923 $this->ColorFlag = false;
1924 $this->pdflayers = array();
1925 // encryption values
1926 $this->encrypted = false;
1927 $this->last_enc_key = '';
1928 // standard Unicode fonts
1929 $this->CoreFonts = array(
1930 'courier'=>'Courier',
1931 'courierB'=>'Courier-Bold',
1932 'courierI'=>'Courier-Oblique',
1933 'courierBI'=>'Courier-BoldOblique',
1934 'helvetica'=>'Helvetica',
1935 'helveticaB'=>'Helvetica-Bold',
1936 'helveticaI'=>'Helvetica-Oblique',
1937 'helveticaBI'=>'Helvetica-BoldOblique',
1938 'times'=>'Times-Roman',
1939 'timesB'=>'Times-Bold',
1940 'timesI'=>'Times-Italic',
1941 'timesBI'=>'Times-BoldItalic',
1942 'symbol'=>'Symbol',
1943 'zapfdingbats'=>'ZapfDingbats'
1944 );
1945 // set scale factor
1946 $this->setPageUnit($unit);
1947 // set page format and orientation
1948 $this->setPageFormat($format, $orientation);
1949 // page margins (1 cm)
1950 $margin = 28.35 / $this->k;
1951 $this->SetMargins($margin, $margin);
1952 $this->clMargin = $this->lMargin;
1953 $this->crMargin = $this->rMargin;
1954 // internal cell padding
1955 $cpadding = $margin / 10;
1956 $this->setCellPaddings($cpadding, 0, $cpadding, 0);
1957 // cell margins
1958 $this->setCellMargins(0, 0, 0, 0);
1959 // line width (0.2 mm)
1960 $this->LineWidth = 0.57 / $this->k;
1961 $this->linestyleWidth = sprintf('%F w', ($this->LineWidth * $this->k));
1962 $this->linestyleCap = '0 J';
1963 $this->linestyleJoin = '0 j';
1964 $this->linestyleDash = '[] 0 d';
1965 // automatic page break
1966 $this->SetAutoPageBreak(true, (2 * $margin));
1967 // full width display mode
1968 $this->SetDisplayMode('fullwidth');
1969 // compression
1970 $this->SetCompression();
1971 // set default PDF version number
1972 $this->setPDFVersion();
1973 $this->tcpdflink = true;
1974 $this->encoding = $encoding;
1975 $this->HREF = array();
1976 $this->getFontsList();
1977 $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1978 $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0);
1979 $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1980 $this->extgstates = array();
1981 $this->setTextShadow();
1982 // user's rights
1983 $this->sign = false;
1984 $this->ur['enabled'] = false;
1985 $this->ur['document'] = '/FullSave';
1986 $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
1987 $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1988 $this->ur['signature'] = '/Modify';
1989 $this->ur['ef'] = '/Create/Delete/Modify/Import';
1990 $this->ur['formex'] = '';
1991 $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
1992 $this->empty_signature_appearance = array();
1993 // set default JPEG quality
1994 $this->jpeg_quality = 75;
1995 // initialize some settings
1996 TCPDF_FONTS::utf8Bidi(array(''), '', false, $this->isunicode, $this->CurrentFont);
1997 // set default font
1998 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
1999 // check if PCRE Unicode support is enabled
2000 if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
2001 // PCRE unicode support is turned ON
2002 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
2003 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
2004 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
2005 //$this->setSpacesRE('/[^\S\P{Z}\P{Lo}\xa0]/u');
2006 $this->setSpacesRE('/[^\S\P{Z}\xa0]/u');
2007 } else {
2008 // PCRE unicode support is turned OFF
2009 $this->setSpacesRE('/[^\S\xa0]/');
2010 }
2011 $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
2012 // set file ID for trailer
2013 $serformat = (is_array($format) ? serialize($format) : $format);
2014 $this->file_id = md5(TCPDF_STATIC::getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding));
2015 // set document creation and modification timestamp
2016 $this->doc_creation_timestamp = time();
2017 $this->doc_modification_timestamp = $this->doc_creation_timestamp;
2018 // get default graphic vars
2019 $this->default_graphic_vars = $this->getGraphicVars();
2020 $this->header_xobj_autoreset = false;
2021 $this->custom_xmp = '';
2022 }
2023
2029 public function __destruct() {
2030 // restore internal encoding
2031 if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
2032 mb_internal_encoding($this->internal_encoding);
2033 }
2034 // unset all class variables
2035 $this->_destroy(true);
2036 }
2037
2044 public function setPageUnit($unit) {
2045 $unit = strtolower($unit);
2046 //Set scale factor
2047 switch ($unit) {
2048 // points
2049 case 'px':
2050 case 'pt': {
2051 $this->k = 1;
2052 break;
2053 }
2054 // millimeters
2055 case 'mm': {
2056 $this->k = $this->dpi / 25.4;
2057 break;
2058 }
2059 // centimeters
2060 case 'cm': {
2061 $this->k = $this->dpi / 2.54;
2062 break;
2063 }
2064 // inches
2065 case 'in': {
2066 $this->k = $this->dpi;
2067 break;
2068 }
2069 // unsupported unit
2070 default : {
2071 $this->Error('Incorrect unit: '.$unit);
2072 break;
2073 }
2074 }
2075 $this->pdfunit = $unit;
2076 if (isset($this->CurOrientation)) {
2077 $this->setPageOrientation($this->CurOrientation);
2078 }
2079 }
2080
2136 protected function setPageFormat($format, $orientation='P') {
2137 if (!empty($format) AND isset($this->pagedim[$this->page])) {
2138 // remove inherited values
2139 unset($this->pagedim[$this->page]);
2140 }
2141 if (is_string($format)) {
2142 // get page measures from format name
2144 $this->fwPt = $pf[0];
2145 $this->fhPt = $pf[1];
2146 } else {
2147 // the boundaries of the physical medium on which the page shall be displayed or printed
2148 if (isset($format['MediaBox'])) {
2149 $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);
2150 $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k);
2151 $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k);
2152 } else {
2153 if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) {
2154 $pf = array(($format[0] * $this->k), ($format[1] * $this->k));
2155 } else {
2156 if (!isset($format['format'])) {
2157 // default value
2158 $format['format'] = 'A4';
2159 }
2160 $pf = TCPDF_STATIC::getPageSizeFromFormat($format['format']);
2161 }
2162 $this->fwPt = $pf[0];
2163 $this->fhPt = $pf[1];
2164 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2165 }
2166 // the visible region of default user space
2167 if (isset($format['CropBox'])) {
2168 $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);
2169 }
2170 // the region to which the contents of the page shall be clipped when output in a production environment
2171 if (isset($format['BleedBox'])) {
2172 $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);
2173 }
2174 // the intended dimensions of the finished page after trimming
2175 if (isset($format['TrimBox'])) {
2176 $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);
2177 }
2178 // the page's meaningful content (including potential white space)
2179 if (isset($format['ArtBox'])) {
2180 $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);
2181 }
2182 // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
2183 if (isset($format['BoxColorInfo'])) {
2184 $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo'];
2185 }
2186 if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) {
2187 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2188 $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']);
2189 }
2190 if (isset($format['PZ'])) {
2191 // The page's preferred zoom (magnification) factor
2192 $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']);
2193 }
2194 if (isset($format['trans'])) {
2195 // The style and duration of the visual transition to use when moving from another page to the given page during a presentation
2196 if (isset($format['trans']['Dur'])) {
2197 // The page's display duration
2198 $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']);
2199 }
2200 $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
2201 if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
2202 // The transition style that shall be used when moving to this page from another during a presentation
2203 $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S'];
2204 $valid_effect = array('Split', 'Blinds');
2205 $valid_vals = array('H', 'V');
2206 if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
2207 $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm'];
2208 }
2209 $valid_effect = array('Split', 'Box', 'Fly');
2210 $valid_vals = array('I', 'O');
2211 if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
2212 $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M'];
2213 }
2214 $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
2215 if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
2216 if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
2217 OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
2218 OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
2219 $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']);
2220 }
2221 }
2222 if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
2223 $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']);
2224 }
2225 if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
2226 $this->pagedim[$this->page]['trans']['B'] = 'true';
2227 }
2228 } else {
2229 $this->pagedim[$this->page]['trans']['S'] = 'R';
2230 }
2231 if (isset($format['trans']['D'])) {
2232 // The duration of the transition effect, in seconds
2233 $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']);
2234 } else {
2235 $this->pagedim[$this->page]['trans']['D'] = 1;
2236 }
2237 }
2238 }
2239 $this->setPageOrientation($orientation);
2240 }
2241
2250 public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
2251 if (!isset($this->pagedim[$this->page]['MediaBox'])) {
2252 // the boundaries of the physical medium on which the page shall be displayed or printed
2253 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2254 }
2255 if (!isset($this->pagedim[$this->page]['CropBox'])) {
2256 // the visible region of default user space
2257 $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);
2258 }
2259 if (!isset($this->pagedim[$this->page]['BleedBox'])) {
2260 // the region to which the contents of the page shall be clipped when output in a production environment
2261 $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);
2262 }
2263 if (!isset($this->pagedim[$this->page]['TrimBox'])) {
2264 // the intended dimensions of the finished page after trimming
2265 $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);
2266 }
2267 if (!isset($this->pagedim[$this->page]['ArtBox'])) {
2268 // the page's meaningful content (including potential white space)
2269 $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);
2270 }
2271 if (!isset($this->pagedim[$this->page]['Rotate'])) {
2272 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2273 $this->pagedim[$this->page]['Rotate'] = 0;
2274 }
2275 if (!isset($this->pagedim[$this->page]['PZ'])) {
2276 // The page's preferred zoom (magnification) factor
2277 $this->pagedim[$this->page]['PZ'] = 1;
2278 }
2279 if ($this->fwPt > $this->fhPt) {
2280 // landscape
2281 $default_orientation = 'L';
2282 } else {
2283 // portrait
2284 $default_orientation = 'P';
2285 }
2286 $valid_orientations = array('P', 'L');
2287 if (empty($orientation)) {
2288 $orientation = $default_orientation;
2289 } else {
2290 $orientation = strtoupper($orientation{0});
2291 }
2292 if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
2293 $this->CurOrientation = $orientation;
2294 $this->wPt = $this->fhPt;
2295 $this->hPt = $this->fwPt;
2296 } else {
2297 $this->CurOrientation = $default_orientation;
2298 $this->wPt = $this->fwPt;
2299 $this->hPt = $this->fhPt;
2300 }
2301 if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
2302 // swap X and Y coordinates (change page orientation)
2303 $this->pagedim = TCPDF_STATIC::swapPageBoxCoordinates($this->page, $this->pagedim);
2304 }
2305 $this->w = ($this->wPt / $this->k);
2306 $this->h = ($this->hPt / $this->k);
2307 if (TCPDF_STATIC::empty_string($autopagebreak)) {
2308 if (isset($this->AutoPageBreak)) {
2309 $autopagebreak = $this->AutoPageBreak;
2310 } else {
2311 $autopagebreak = true;
2312 }
2313 }
2314 if (TCPDF_STATIC::empty_string($bottommargin)) {
2315 if (isset($this->bMargin)) {
2316 $bottommargin = $this->bMargin;
2317 } else {
2318 // default value = 2 cm
2319 $bottommargin = 2 * 28.35 / $this->k;
2320 }
2321 }
2322 $this->SetAutoPageBreak($autopagebreak, $bottommargin);
2323 // store page dimensions
2324 $this->pagedim[$this->page]['w'] = $this->wPt;
2325 $this->pagedim[$this->page]['h'] = $this->hPt;
2326 $this->pagedim[$this->page]['wk'] = $this->w;
2327 $this->pagedim[$this->page]['hk'] = $this->h;
2328 $this->pagedim[$this->page]['tm'] = $this->tMargin;
2329 $this->pagedim[$this->page]['bm'] = $bottommargin;
2330 $this->pagedim[$this->page]['lm'] = $this->lMargin;
2331 $this->pagedim[$this->page]['rm'] = $this->rMargin;
2332 $this->pagedim[$this->page]['pb'] = $autopagebreak;
2333 $this->pagedim[$this->page]['or'] = $this->CurOrientation;
2334 $this->pagedim[$this->page]['olm'] = $this->original_lMargin;
2335 $this->pagedim[$this->page]['orm'] = $this->original_rMargin;
2336 }
2337
2355 public function setSpacesRE($re='/[^\S\xa0]/') {
2356 $this->re_spaces = $re;
2357 $re_parts = explode('/', $re);
2358 // get pattern parts
2359 $this->re_space = array();
2360 if (isset($re_parts[1]) AND !empty($re_parts[1])) {
2361 $this->re_space['p'] = $re_parts[1];
2362 } else {
2363 $this->re_space['p'] = '[\s]';
2364 }
2365 // set pattern modifiers
2366 if (isset($re_parts[2]) AND !empty($re_parts[2])) {
2367 $this->re_space['m'] = $re_parts[2];
2368 } else {
2369 $this->re_space['m'] = '';
2370 }
2371 }
2372
2380 public function setRTL($enable, $resetx=true) {
2381 $enable = $enable ? true : false;
2382 $resetx = ($resetx AND ($enable != $this->rtl));
2383 $this->rtl = $enable;
2384 $this->tmprtl = false;
2385 if ($resetx) {
2386 $this->Ln(0);
2387 }
2388 }
2389
2396 public function getRTL() {
2397 return $this->rtl;
2398 }
2399
2406 public function setTempRTL($mode) {
2407 $newmode = false;
2408 switch (strtoupper($mode)) {
2409 case 'LTR':
2410 case 'L': {
2411 if ($this->rtl) {
2412 $newmode = 'L';
2413 }
2414 break;
2415 }
2416 case 'RTL':
2417 case 'R': {
2418 if (!$this->rtl) {
2419 $newmode = 'R';
2420 }
2421 break;
2422 }
2423 case false:
2424 default: {
2425 $newmode = false;
2426 break;
2427 }
2428 }
2429 $this->tmprtl = $newmode;
2430 }
2431
2438 public function isRTLTextDir() {
2439 return ($this->rtl OR ($this->tmprtl == 'R'));
2440 }
2441
2449 public function setLastH($h) {
2450 $this->lasth = $h;
2451 }
2452
2458 public function resetLastH() {
2459 $this->lasth = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
2460 }
2461
2468 public function getLastH() {
2469 return $this->lasth;
2470 }
2471
2479 public function setImageScale($scale) {
2480 $this->imgscale = $scale;
2481 }
2482
2490 public function getImageScale() {
2491 return $this->imgscale;
2492 }
2493
2503 public function getPageDimensions($pagenum='') {
2504 if (empty($pagenum)) {
2505 $pagenum = $this->page;
2506 }
2507 return $this->pagedim[$pagenum];
2508 }
2509
2519 public function getPageWidth($pagenum='') {
2520 if (empty($pagenum)) {
2521 return $this->w;
2522 }
2523 return $this->pagedim[$pagenum]['w'];
2524 }
2525
2535 public function getPageHeight($pagenum='') {
2536 if (empty($pagenum)) {
2537 return $this->h;
2538 }
2539 return $this->pagedim[$pagenum]['h'];
2540 }
2541
2551 public function getBreakMargin($pagenum='') {
2552 if (empty($pagenum)) {
2553 return $this->bMargin;
2554 }
2555 return $this->pagedim[$pagenum]['bm'];
2556 }
2557
2565 public function getScaleFactor() {
2566 return $this->k;
2567 }
2568
2579 public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
2580 //Set left, top and right margins
2581 $this->lMargin = $left;
2582 $this->tMargin = $top;
2583 if ($right == -1) {
2584 $right = $left;
2585 }
2586 $this->rMargin = $right;
2587 if ($keepmargins) {
2588 // overwrite original values
2589 $this->original_lMargin = $this->lMargin;
2590 $this->original_rMargin = $this->rMargin;
2591 }
2592 }
2593
2601 public function SetLeftMargin($margin) {
2602 //Set left margin
2603 $this->lMargin = $margin;
2604 if (($this->page > 0) AND ($this->x < $margin)) {
2605 $this->x = $margin;
2606 }
2607 }
2608
2616 public function SetTopMargin($margin) {
2617 //Set top margin
2618 $this->tMargin = $margin;
2619 if (($this->page > 0) AND ($this->y < $margin)) {
2620 $this->y = $margin;
2621 }
2622 }
2623
2631 public function SetRightMargin($margin) {
2632 $this->rMargin = $margin;
2633 if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
2634 $this->x = $this->w - $margin;
2635 }
2636 }
2637
2645 public function SetCellPadding($pad) {
2646 if ($pad >= 0) {
2647 $this->cell_padding['L'] = $pad;
2648 $this->cell_padding['T'] = $pad;
2649 $this->cell_padding['R'] = $pad;
2650 $this->cell_padding['B'] = $pad;
2651 }
2652 }
2653
2664 public function setCellPaddings($left='', $top='', $right='', $bottom='') {
2665 if (($left !== '') AND ($left >= 0)) {
2666 $this->cell_padding['L'] = $left;
2667 }
2668 if (($top !== '') AND ($top >= 0)) {
2669 $this->cell_padding['T'] = $top;
2670 }
2671 if (($right !== '') AND ($right >= 0)) {
2672 $this->cell_padding['R'] = $right;
2673 }
2674 if (($bottom !== '') AND ($bottom >= 0)) {
2675 $this->cell_padding['B'] = $bottom;
2676 }
2677 }
2678
2686 public function getCellPaddings() {
2687 return $this->cell_padding;
2688 }
2689
2700 public function setCellMargins($left='', $top='', $right='', $bottom='') {
2701 if (($left !== '') AND ($left >= 0)) {
2702 $this->cell_margin['L'] = $left;
2703 }
2704 if (($top !== '') AND ($top >= 0)) {
2705 $this->cell_margin['T'] = $top;
2706 }
2707 if (($right !== '') AND ($right >= 0)) {
2708 $this->cell_margin['R'] = $right;
2709 }
2710 if (($bottom !== '') AND ($bottom >= 0)) {
2711 $this->cell_margin['B'] = $bottom;
2712 }
2713 }
2714
2722 public function getCellMargins() {
2723 return $this->cell_margin;
2724 }
2725
2733 protected function adjustCellPadding($brd=0) {
2734 if (empty($brd)) {
2735 return;
2736 }
2737 if (is_string($brd)) {
2738 // convert string to array
2739 $slen = strlen($brd);
2740 $newbrd = array();
2741 for ($i = 0; $i < $slen; ++$i) {
2742 $newbrd[$brd[$i]] = true;
2743 }
2744 $brd = $newbrd;
2745 } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) {
2746 $brd = array('LRTB' => true);
2747 }
2748 if (!is_array($brd)) {
2749 return;
2750 }
2751 // store current cell padding
2752 $cp = $this->cell_padding;
2753 // select border mode
2754 if (isset($brd['mode'])) {
2755 $mode = $brd['mode'];
2756 unset($brd['mode']);
2757 } else {
2758 $mode = 'normal';
2759 }
2760 // process borders
2761 foreach ($brd as $border => $style) {
2762 $line_width = $this->LineWidth;
2763 if (is_array($style) AND isset($style['width'])) {
2764 // get border width
2765 $line_width = $style['width'];
2766 }
2767 $adj = 0; // line width inside the cell
2768 switch ($mode) {
2769 case 'ext': {
2770 $adj = 0;
2771 break;
2772 }
2773 case 'int': {
2774 $adj = $line_width;
2775 break;
2776 }
2777 case 'normal':
2778 default: {
2779 $adj = ($line_width / 2);
2780 break;
2781 }
2782 }
2783 // correct internal cell padding if required to avoid overlap between text and lines
2784 if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) {
2785 $this->cell_padding['T'] = $adj;
2786 }
2787 if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) {
2788 $this->cell_padding['R'] = $adj;
2789 }
2790 if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) {
2791 $this->cell_padding['B'] = $adj;
2792 }
2793 if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) {
2794 $this->cell_padding['L'] = $adj;
2795 }
2796 }
2797 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']));
2798 }
2799
2808 public function SetAutoPageBreak($auto, $margin=0) {
2809 $this->AutoPageBreak = $auto ? true : false;
2810 $this->bMargin = $margin;
2811 $this->PageBreakTrigger = $this->h - $margin;
2812 }
2813
2820 public function getAutoPageBreak() {
2821 return $this->AutoPageBreak;
2822 }
2823
2832 public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
2833 if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
2834 $this->ZoomMode = $zoom;
2835 } else {
2836 $this->Error('Incorrect zoom display mode: '.$zoom);
2837 }
2838 $this->LayoutMode = TCPDF_STATIC::getPageLayoutMode($layout);
2839 $this->PageMode = TCPDF_STATIC::getPageMode($mode);
2840 }
2841
2849 public function SetCompression($compress=true) {
2850 if (function_exists('gzcompress')) {
2851 $this->compress = $compress ? true : false;
2852 } else {
2853 $this->compress = false;
2854 }
2855 }
2856
2863 public function setSRGBmode($mode=false) {
2864 $this->force_srgb = $mode ? true : false;
2865 }
2866
2874 public function SetDocInfoUnicode($unicode=true) {
2875 $this->docinfounicode = $unicode ? true : false;
2876 }
2877
2885 public function SetTitle($title) {
2886 $this->title = $title;
2887 }
2888
2896 public function SetSubject($subject) {
2897 $this->subject = $subject;
2898 }
2899
2907 public function SetAuthor($author) {
2908 $this->author = $author;
2909 }
2910
2918 public function SetKeywords($keywords) {
2919 $this->keywords = $keywords;
2920 }
2921
2929 public function SetCreator($creator) {
2930 $this->creator = $creator;
2931 }
2932
2940 public function Error($msg) {
2941 // unset all class variables
2942 $this->_destroy(true);
2943 $phpmainver = PHP_VERSION;
2944 // exit program and print error
2945 if ((intval($phpmainver[0]) < 5) OR !defined('K_TCPDF_THROW_EXCEPTION_ERROR') OR !K_TCPDF_THROW_EXCEPTION_ERROR) {
2946 die('<strong>TCPDF ERROR: </strong>'.$msg);
2947 } else {
2948 throw new Exception('TCPDF ERROR: '.$msg);
2949 }
2950 }
2951
2960 public function Open() {
2961 $this->state = 1;
2962 }
2963
2972 public function Close() {
2973 if ($this->state == 3) {
2974 return;
2975 }
2976 if ($this->page == 0) {
2977 $this->AddPage();
2978 }
2979 $this->endLayer();
2980 if ($this->tcpdflink) {
2981 // save current graphic settings
2982 $gvars = $this->getGraphicVars();
2983 $this->setEqualColumns();
2984 $this->lastpage(true);
2985 $this->SetAutoPageBreak(false);
2986 $this->x = 0;
2987 $this->y = $this->h - (1 / $this->k);
2988 $this->lMargin = 0;
2989 $this->_out('q');
2990 $font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
2991 $this->SetFont($font, '', 1);
2992 $this->setTextRenderingMode(0, false, false);
2993 $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";
2994 $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
2995 $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
2996 $this->_out('Q');
2997 // restore graphic settings
2998 $this->setGraphicVars($gvars);
2999 }
3000 // close page
3001 $this->endPage();
3002 // close document
3003 $this->_enddoc();
3004 // unset all class variables (except critical ones)
3005 $this->_destroy(false);
3006 }
3007
3016 public function setPage($pnum, $resetmargins=false) {
3017 if (($pnum == $this->page) AND ($this->state == 2)) {
3018 return;
3019 }
3020 if (($pnum > 0) AND ($pnum <= $this->numpages)) {
3021 $this->state = 2;
3022 // save current graphic settings
3023 //$gvars = $this->getGraphicVars();
3024 $oldpage = $this->page;
3025 $this->page = $pnum;
3026 $this->wPt = $this->pagedim[$this->page]['w'];
3027 $this->hPt = $this->pagedim[$this->page]['h'];
3028 $this->w = $this->pagedim[$this->page]['wk'];
3029 $this->h = $this->pagedim[$this->page]['hk'];
3030 $this->tMargin = $this->pagedim[$this->page]['tm'];
3031 $this->bMargin = $this->pagedim[$this->page]['bm'];
3032 $this->original_lMargin = $this->pagedim[$this->page]['olm'];
3033 $this->original_rMargin = $this->pagedim[$this->page]['orm'];
3034 $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
3035 $this->CurOrientation = $this->pagedim[$this->page]['or'];
3036 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
3037 // restore graphic settings
3038 //$this->setGraphicVars($gvars);
3039 if ($resetmargins) {
3040 $this->lMargin = $this->pagedim[$this->page]['olm'];
3041 $this->rMargin = $this->pagedim[$this->page]['orm'];
3042 $this->SetY($this->tMargin);
3043 } else {
3044 // account for booklet mode
3045 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3046 $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
3047 $this->lMargin += $deltam;
3048 $this->rMargin -= $deltam;
3049 }
3050 }
3051 } else {
3052 $this->Error('Wrong page number on setPage() function: '.$pnum);
3053 }
3054 }
3055
3063 public function lastPage($resetmargins=false) {
3064 $this->setPage($this->getNumPages(), $resetmargins);
3065 }
3066
3074 public function getPage() {
3075 return $this->page;
3076 }
3077
3085 public function getNumPages() {
3086 return $this->numpages;
3087 }
3088
3098 public function addTOCPage($orientation='', $format='', $keepmargins=false) {
3099 $this->AddPage($orientation, $format, $keepmargins, true);
3100 }
3101
3108 public function endTOCPage() {
3109 $this->endPage(true);
3110 }
3111
3123 public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
3124 if ($this->inxobj) {
3125 // we are inside an XObject template
3126 return;
3127 }
3128 if (!isset($this->original_lMargin) OR $keepmargins) {
3129 $this->original_lMargin = $this->lMargin;
3130 }
3131 if (!isset($this->original_rMargin) OR $keepmargins) {
3132 $this->original_rMargin = $this->rMargin;
3133 }
3134 // terminate previous page
3135 $this->endPage();
3136 // start new page
3137 $this->startPage($orientation, $format, $tocpage);
3138 }
3139
3147 public function endPage($tocpage=false) {
3148 // check if page is already closed
3149 if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
3150 return;
3151 }
3152 // print page footer
3153 $this->setFooter();
3154 // close page
3155 $this->_endpage();
3156 // mark page as closed
3157 $this->pageopen[$this->page] = false;
3158 if ($tocpage) {
3159 $this->tocpage = false;
3160 }
3161 }
3162
3173 public function startPage($orientation='', $format='', $tocpage=false) {
3174 if ($tocpage) {
3175 $this->tocpage = true;
3176 }
3177 // move page numbers of documents to be attached
3178 if ($this->tocpage) {
3179 // move reference to unexistent pages (used for page attachments)
3180 // adjust outlines
3181 $tmpoutlines = $this->outlines;
3182 foreach ($tmpoutlines as $key => $outline) {
3183 if ($outline['p'] > $this->numpages) {
3184 $this->outlines[$key]['p'] = ($outline['p'] + 1);
3185 }
3186 }
3187 // adjust dests
3188 $tmpdests = $this->dests;
3189 foreach ($tmpdests as $key => $dest) {
3190 if ($dest['p'] > $this->numpages) {
3191 $this->dests[$key]['p'] = ($dest['p'] + 1);
3192 }
3193 }
3194 // adjust links
3195 $tmplinks = $this->links;
3196 foreach ($tmplinks as $key => $link) {
3197 if ($link[0] > $this->numpages) {
3198 $this->links[$key][0] = ($link[0] + 1);
3199 }
3200 }
3201 }
3202 if ($this->numpages > $this->page) {
3203 // this page has been already added
3204 $this->setPage($this->page + 1);
3205 $this->SetY($this->tMargin);
3206 return;
3207 }
3208 // start a new page
3209 if ($this->state == 0) {
3210 $this->Open();
3211 }
3213 $this->swapMargins($this->booklet);
3214 // save current graphic settings
3215 $gvars = $this->getGraphicVars();
3216 // start new page
3217 $this->_beginpage($orientation, $format);
3218 // mark page as open
3219 $this->pageopen[$this->page] = true;
3220 // restore graphic settings
3221 $this->setGraphicVars($gvars);
3222 // mark this point
3223 $this->setPageMark();
3224 // print page header
3225 $this->setHeader();
3226 // restore graphic settings
3227 $this->setGraphicVars($gvars);
3228 // mark this point
3229 $this->setPageMark();
3230 // print table header (if any)
3231 $this->setTableHeader();
3232 // set mark for empty page check
3233 $this->emptypagemrk[$this->page]= $this->pagelen[$this->page];
3234 }
3235
3244 public function setPageMark() {
3245 $this->intmrk[$this->page] = $this->pagelen[$this->page];
3246 $this->bordermrk[$this->page] = $this->intmrk[$this->page];
3247 $this->setContentMark();
3248 }
3249
3257 protected function setContentMark($page=0) {
3258 if ($page <= 0) {
3260 }
3261 if (isset($this->footerlen[$page])) {
3262 $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
3263 } else {
3264 $this->cntmrk[$page] = $this->pagelen[$page];
3265 }
3266 }
3267
3278 public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) {
3279 $this->header_logo = $ln;
3280 $this->header_logo_width = $lw;
3281 $this->header_title = $ht;
3282 $this->header_string = $hs;
3283 $this->header_text_color = $tc;
3284 $this->header_line_color = $lc;
3285 }
3286
3293 public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) {
3294 $this->footer_text_color = $tc;
3295 $this->footer_line_color = $lc;
3296 }
3297
3305 public function getHeaderData() {
3306 $ret = array();
3307 $ret['logo'] = $this->header_logo;
3308 $ret['logo_width'] = $this->header_logo_width;
3309 $ret['title'] = $this->header_title;
3310 $ret['string'] = $this->header_string;
3311 $ret['text_color'] = $this->header_text_color;
3312 $ret['line_color'] = $this->header_line_color;
3313 return $ret;
3314 }
3315
3322 public function setHeaderMargin($hm=10) {
3323 $this->header_margin = $hm;
3324 }
3325
3332 public function getHeaderMargin() {
3333 return $this->header_margin;
3334 }
3335
3342 public function setFooterMargin($fm=10) {
3343 $this->footer_margin = $fm;
3344 }
3345
3352 public function getFooterMargin() {
3353 return $this->footer_margin;
3354 }
3360 public function setPrintHeader($val=true) {
3361 $this->print_header = $val ? true : false;
3362 }
3363
3369 public function setPrintFooter($val=true) {
3370 $this->print_footer = $val ? true : false;
3371 }
3372
3378 public function getImageRBX() {
3379 return $this->img_rb_x;
3380 }
3381
3387 public function getImageRBY() {
3388 return $this->img_rb_y;
3389 }
3390
3395 public function resetHeaderTemplate() {
3396 $this->header_xobjid = -1;
3397 }
3398
3404 public function setHeaderTemplateAutoreset($val=true) {
3405 $this->header_xobj_autoreset = $val ? true : false;
3406 }
3407
3413 public function Header() {
3414 if ($this->header_xobjid < 0) {
3415 // start a new XObject Template
3416 $this->header_xobjid = $this->startTemplate($this->w, $this->tMargin);
3417 $headerfont = $this->getHeaderFont();
3418 $headerdata = $this->getHeaderData();
3419 $this->y = $this->header_margin;
3420 if ($this->rtl) {
3421 $this->x = $this->w - $this->original_rMargin;
3422 } else {
3423 $this->x = $this->original_lMargin;
3424 }
3425 if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
3426 $imgtype = TCPDF_IMAGES::getImageFileType(K_PATH_IMAGES.$headerdata['logo']);
3427 if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
3428 $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3429 } elseif ($imgtype == 'svg') {
3430 $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3431 } else {
3432 $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3433 }
3434 $imgy = $this->getImageRBY();
3435 } else {
3436 $imgy = $this->y;
3437 }
3438 $cell_height = round(($this->cell_height_ratio * $headerfont[2]) / $this->k, 2);
3439 // set starting margin for text data cell
3440 if ($this->getRTL()) {
3441 $header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1);
3442 } else {
3443 $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1);
3444 }
3445 $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1);
3446 $this->SetTextColorArray($this->header_text_color);
3447 // header title
3448 $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
3449 $this->SetX($header_x);
3450 $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
3451 // header string
3452 $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
3453 $this->SetX($header_x);
3454 $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false);
3455 // print an ending header line
3456 $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color']));
3457 $this->SetY((2.835 / $this->k) + max($imgy, $this->y));
3458 if ($this->rtl) {
3459 $this->SetX($this->original_rMargin);
3460 } else {
3461 $this->SetX($this->original_lMargin);
3462 }
3463 $this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C');
3464 $this->endTemplate();
3465 }
3466 // print header template
3467 $x = 0;
3468 $dx = 0;
3469 if (!$this->header_xobj_autoreset AND $this->booklet AND (($this->page % 2) == 0)) {
3470 // adjust margins for booklet mode
3471 $dx = ($this->original_lMargin - $this->original_rMargin);
3472 }
3473 if ($this->rtl) {
3474 $x = $this->w + $dx;
3475 } else {
3476 $x = 0 + $dx;
3477 }
3478 $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false);
3479 if ($this->header_xobj_autoreset) {
3480 // reset header xobject template at each page
3481 $this->header_xobjid = -1;
3482 }
3483 }
3484
3490 public function Footer() {
3491 $cur_y = $this->y;
3492 $this->SetTextColorArray($this->footer_text_color);
3493 //set style for cell border
3494 $line_width = (0.85 / $this->k);
3495 $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color));
3496 //print document barcode
3497 $barcode = $this->getBarcode();
3498 if (!empty($barcode)) {
3499 $this->Ln($line_width);
3500 $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3);
3501 $style = array(
3502 'position' => $this->rtl?'R':'L',
3503 'align' => $this->rtl?'R':'L',
3504 'stretch' => false,
3505 'fitwidth' => true,
3506 'cellfitalign' => '',
3507 'border' => false,
3508 'padding' => 0,
3509 'fgcolor' => array(0,0,0),
3510 'bgcolor' => false,
3511 'text' => false
3512 );
3513 $this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, '');
3514 }
3515 $w_page = isset($this->l['w_page']) ? $this->l['w_page'].' ' : '';
3516 if (empty($this->pagegroups)) {
3517 $pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
3518 } else {
3519 $pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
3520 }
3521 $this->SetY($cur_y);
3522 //Print page number
3523 if ($this->getRTL()) {
3524 $this->SetX($this->original_rMargin);
3525 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
3526 } else {
3527 $this->SetX($this->original_lMargin);
3528 $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R');
3529 }
3530 }
3531
3537 protected function setHeader() {
3538 if (!$this->print_header OR ($this->state != 2)) {
3539 return;
3540 }
3541 $this->InHeader = true;
3542 $this->setGraphicVars($this->default_graphic_vars);
3543 $temp_thead = $this->thead;
3544 $temp_theadMargins = $this->theadMargins;
3546 $this->_out('q');
3547 $this->rMargin = $this->original_rMargin;
3548 $this->lMargin = $this->original_lMargin;
3549 $this->SetCellPadding(0);
3550 //set current position
3551 if ($this->rtl) {
3552 $this->SetXY($this->original_rMargin, $this->header_margin);
3553 } else {
3554 $this->SetXY($this->original_lMargin, $this->header_margin);
3555 }
3556 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
3557 $this->Header();
3558 //restore position
3559 if ($this->rtl) {
3560 $this->SetXY($this->original_rMargin, $this->tMargin);
3561 } else {
3562 $this->SetXY($this->original_lMargin, $this->tMargin);
3563 }
3564 $this->_out('Q');
3565 $this->lasth = $lasth;
3566 $this->thead = $temp_thead;
3567 $this->theadMargins = $temp_theadMargins;
3568 $this->newline = false;
3569 $this->InHeader = false;
3570 }
3571
3577 protected function setFooter() {
3578 if ($this->state != 2) {
3579 return;
3580 }
3581 $this->InFooter = true;
3582 // save current graphic settings
3583 $gvars = $this->getGraphicVars();
3584 // mark this point
3585 $this->footerpos[$this->page] = $this->pagelen[$this->page];
3586 $this->_out("\n");
3587 if ($this->print_footer) {
3588 $this->setGraphicVars($this->default_graphic_vars);
3589 $this->current_column = 0;
3590 $this->num_columns = 1;
3591 $temp_thead = $this->thead;
3592 $temp_theadMargins = $this->theadMargins;
3594 $this->_out('q');
3595 $this->rMargin = $this->original_rMargin;
3596 $this->lMargin = $this->original_lMargin;
3597 $this->SetCellPadding(0);
3598 //set current position
3599 $footer_y = $this->h - $this->footer_margin;
3600 if ($this->rtl) {
3601 $this->SetXY($this->original_rMargin, $footer_y);
3602 } else {
3603 $this->SetXY($this->original_lMargin, $footer_y);
3604 }
3605 $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
3606 $this->Footer();
3607 //restore position
3608 if ($this->rtl) {
3609 $this->SetXY($this->original_rMargin, $this->tMargin);
3610 } else {
3611 $this->SetXY($this->original_lMargin, $this->tMargin);
3612 }
3613 $this->_out('Q');
3614 $this->lasth = $lasth;
3615 $this->thead = $temp_thead;
3616 $this->theadMargins = $temp_theadMargins;
3617 }
3618 // restore graphic settings
3619 $this->setGraphicVars($gvars);
3620 $this->current_column = $gvars['current_column'];
3621 $this->num_columns = $gvars['num_columns'];
3622 // calculate footer length
3623 $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
3624 $this->InFooter = false;
3625 }
3626
3633 protected function inPageBody() {
3634 return (($this->InHeader === false) AND ($this->InFooter === false));
3635 }
3636
3642 protected function setTableHeader() {
3643 if ($this->num_columns > 1) {
3644 // multi column mode
3645 return;
3646 }
3647 if (isset($this->theadMargins['top'])) {
3648 // restore the original top-margin
3649 $this->tMargin = $this->theadMargins['top'];
3650 $this->pagedim[$this->page]['tm'] = $this->tMargin;
3651 $this->y = $this->tMargin;
3652 }
3653 if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
3654 // set margins
3655 $prev_lMargin = $this->lMargin;
3656 $prev_rMargin = $this->rMargin;
3657 $prev_cell_padding = $this->cell_padding;
3658 $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']);
3659 $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']);
3660 $this->cell_padding = $this->theadMargins['cell_padding'];
3661 if ($this->rtl) {
3662 $this->x = $this->w - $this->rMargin;
3663 } else {
3664 $this->x = $this->lMargin;
3665 }
3666 // account for special "cell" mode
3667 if ($this->theadMargins['cell']) {
3668 if ($this->rtl) {
3669 $this->x -= $this->cell_padding['R'];
3670 } else {
3671 $this->x += $this->cell_padding['L'];
3672 }
3673 }
3674 // print table header
3675 $this->writeHTML($this->thead, false, false, false, false, '');
3676 // set new top margin to skip the table headers
3677 if (!isset($this->theadMargins['top'])) {
3678 $this->theadMargins['top'] = $this->tMargin;
3679 }
3680 // store end of header position
3681 if (!isset($this->columns[0]['th'])) {
3682 $this->columns[0]['th'] = array();
3683 }
3684 $this->columns[0]['th']['\''.$this->page.'\''] = $this->y;
3685 $this->tMargin = $this->y;
3686 $this->pagedim[$this->page]['tm'] = $this->tMargin;
3687 $this->lasth = 0;
3688 $this->lMargin = $prev_lMargin;
3689 $this->rMargin = $prev_rMargin;
3690 $this->cell_padding = $prev_cell_padding;
3691 }
3692 }
3693
3701 public function PageNo() {
3702 return $this->page;
3703 }
3704
3718 public function AddSpotColor($name, $c, $m, $y, $k) {
3719 if (!isset($this->spot_colors[$name])) {
3720 $i = (1 + count($this->spot_colors));
3721 $this->spot_colors[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i);
3722 }
3723 }
3724
3734 public function setSpotColor($type, $name, $tint=100) {
3735 $spotcolor = TCPDF_COLORS::getSpotColor($name, $this->spot_colors);
3736 if ($spotcolor === false) {
3737 $this->Error('Undefined spot color: '.$name.', you must add it on the spotcolors.php file.');
3738 }
3739 $tint = (max(0, min(100, $tint)) / 100);
3740 $pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']);
3741 switch ($type) {
3742 case 'draw': {
3743 $pdfcolor .= sprintf('CS %F SCN', $tint);
3744 $this->DrawColor = $pdfcolor;
3745 $this->strokecolor = $spotcolor;
3746 break;
3747 }
3748 case 'fill': {
3749 $pdfcolor .= sprintf('cs %F scn', $tint);
3750 $this->FillColor = $pdfcolor;
3751 $this->bgcolor = $spotcolor;
3752 break;
3753 }
3754 case 'text': {
3755 $pdfcolor .= sprintf('cs %F scn', $tint);
3756 $this->TextColor = $pdfcolor;
3757 $this->fgcolor = $spotcolor;
3758 break;
3759 }
3760 }
3761 $this->ColorFlag = ($this->FillColor != $this->TextColor);
3762 if ($this->state == 2) {
3763 $this->_out($pdfcolor);
3764 }
3765 if ($this->inxobj) {
3766 // we are inside an XObject template
3767 $this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name];
3768 }
3769 return $pdfcolor;
3770 }
3771
3780 public function SetDrawSpotColor($name, $tint=100) {
3781 $this->setSpotColor('draw', $name, $tint);
3782 }
3783
3792 public function SetFillSpotColor($name, $tint=100) {
3793 $this->setSpotColor('fill', $name, $tint);
3794 }
3795
3804 public function SetTextSpotColor($name, $tint=100) {
3805 $this->setSpotColor('text', $name, $tint);
3806 }
3807
3819 public function setColorArray($type, $color, $ret=false) {
3820 if (is_array($color)) {
3821 $color = array_values($color);
3822 // component: grey, RGB red or CMYK cyan
3823 $c = isset($color[0]) ? $color[0] : -1;
3824 // component: RGB green or CMYK magenta
3825 $m = isset($color[1]) ? $color[1] : -1;
3826 // component: RGB blue or CMYK yellow
3827 $y = isset($color[2]) ? $color[2] : -1;
3828 // component: CMYK black
3829 $k = isset($color[3]) ? $color[3] : -1;
3830 // color name
3831 $name = isset($color[4]) ? $color[4] : '';
3832 if ($c >= 0) {
3833 return $this->setColor($type, $c, $m, $y, $k, $ret, $name);
3834 }
3835 }
3836 return '';
3837 }
3838
3850 public function SetDrawColorArray($color, $ret=false) {
3851 return $this->setColorArray('draw', $color, $ret);
3852 }
3853
3864 public function SetFillColorArray($color, $ret=false) {
3865 return $this->setColorArray('fill', $color, $ret);
3866 }
3867
3877 public function SetTextColorArray($color, $ret=false) {
3878 return $this->setColorArray('text', $color, $ret);
3879 }
3880
3894 public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3895 // set default values
3896 if (!is_numeric($col1)) {
3897 $col1 = 0;
3898 }
3899 if (!is_numeric($col2)) {
3900 $col2 = -1;
3901 }
3902 if (!is_numeric($col3)) {
3903 $col3 = -1;
3904 }
3905 if (!is_numeric($col4)) {
3906 $col4 = -1;
3907 }
3908 // set color by case
3909 $suffix = '';
3910 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
3911 // Grey scale
3912 $col1 = max(0, min(255, $col1));
3913 $intcolor = array('G' => $col1);
3914 $pdfcolor = sprintf('%F ', ($col1 / 255));
3915 $suffix = 'g';
3916 } elseif ($col4 == -1) {
3917 // RGB
3918 $col1 = max(0, min(255, $col1));
3919 $col2 = max(0, min(255, $col2));
3920 $col3 = max(0, min(255, $col3));
3921 $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
3922 $pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255));
3923 $suffix = 'rg';
3924 } else {
3925 $col1 = max(0, min(100, $col1));
3926 $col2 = max(0, min(100, $col2));
3927 $col3 = max(0, min(100, $col3));
3928 $col4 = max(0, min(100, $col4));
3929 if (empty($name)) {
3930 // CMYK
3931 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
3932 $pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
3933 $suffix = 'k';
3934 } else {
3935 // SPOT COLOR
3936 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
3937 $this->AddSpotColor($name, $col1, $col2, $col3, $col4);
3938 $pdfcolor = $this->setSpotColor($type, $name, 100);
3939 }
3940 }
3941 switch ($type) {
3942 case 'draw': {
3943 $pdfcolor .= strtoupper($suffix);
3944 $this->DrawColor = $pdfcolor;
3945 $this->strokecolor = $intcolor;
3946 break;
3947 }
3948 case 'fill': {
3949 $pdfcolor .= $suffix;
3950 $this->FillColor = $pdfcolor;
3951 $this->bgcolor = $intcolor;
3952 break;
3953 }
3954 case 'text': {
3955 $pdfcolor .= $suffix;
3956 $this->TextColor = $pdfcolor;
3957 $this->fgcolor = $intcolor;
3958 break;
3959 }
3960 }
3961 $this->ColorFlag = ($this->FillColor != $this->TextColor);
3962 if (($type != 'text') AND ($this->state == 2)) {
3963 if (!$ret) {
3964 $this->_out($pdfcolor);
3965 }
3966 return $pdfcolor;
3967 }
3968 return '';
3969 }
3970
3984 public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3985 return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name);
3986 }
3987
4001 public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4002 return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name);
4003 }
4004
4018 public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4019 return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name);
4020 }
4021
4034 public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4035 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);
4036 }
4037
4050 public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4051 // store current values
4052 if (!TCPDF_STATIC::empty_string($fontname)) {
4053 $prev_FontFamily = $this->FontFamily;
4054 $prev_FontStyle = $this->FontStyle;
4055 $prev_FontSizePt = $this->FontSizePt;
4056 $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false);
4057 }
4058 // convert UTF-8 array to Latin1 if required
4059 if ($this->isunicode AND (!$this->isUnicodeFont())) {
4061 }
4062 $w = 0; // total width
4063 $wa = array(); // array of characters widths
4064 foreach ($sa as $ck => $char) {
4065 // character width
4066 $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)]));
4067 $wa[] = $cw;
4068 $w += $cw;
4069 }
4070 // restore previous values
4071 if (!TCPDF_STATIC::empty_string($fontname)) {
4072 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
4073 }
4074 if ($getarray) {
4075 return $wa;
4076 }
4077 return $w;
4078 }
4079
4089 public function GetCharWidth($char, $notlast=true) {
4090 // get raw width
4091 $chw = $this->getRawCharWidth($char);
4092 if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) {
4093 // increase/decrease font spacing
4094 $chw += $this->font_spacing;
4095 }
4096 if ($this->font_stretching != 100) {
4097 // fixed stretching mode
4098 $chw *= ($this->font_stretching / 100);
4099 }
4100 return $chw;
4101 }
4102
4111 public function getRawCharWidth($char) {
4112 if ($char == 173) {
4113 // SHY character will not be printed
4114 return (0);
4115 }
4116 if (isset($this->CurrentFont['cw'][$char])) {
4117 $w = $this->CurrentFont['cw'][$char];
4118 } elseif (isset($this->CurrentFont['dw'])) {
4119 // default width
4120 $w = $this->CurrentFont['dw'];
4121 } elseif (isset($this->CurrentFont['cw'][32])) {
4122 // default width
4123 $w = $this->CurrentFont['cw'][32];
4124 } else {
4125 $w = 600;
4126 }
4127 return $this->getAbsFontMeasure($w);
4128 }
4129
4137 public function GetNumChars($s) {
4138 if ($this->isUnicodeFont()) {
4139 return count(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont));
4140 }
4141 return strlen($s);
4142 }
4143
4149 protected function getFontsList() {
4150 if (($fontsdir = opendir(TCPDF_FONTS::_getfontpath())) !== false) {
4151 while (($file = readdir($fontsdir)) !== false) {
4152 if (substr($file, -4) == '.php') {
4153 array_push($this->fontlist, strtolower(basename($file, '.php')));
4154 }
4155 }
4156 closedir($fontsdir);
4157 }
4158 }
4159
4168 public function unichr($c) {
4169 return TCPDF_FONTS::unichr($c, $this->isunicode);
4170 }
4171
4188 public function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false) {
4189 return TCPDF_FONTS::addTTFfont($fontfile, $fonttype, $enc, $flags, $outpath, $platid, $encid, $addcbbox);
4190 }
4191
4205 public function AddFont($family, $style='', $fontfile='', $subset='default') {
4206 if ($subset === 'default') {
4207 $subset = $this->font_subsetting;
4208 }
4209 if ($this->pdfa_mode) {
4210 $subset = false;
4211 }
4212 if (TCPDF_STATIC::empty_string($family)) {
4213 if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
4214 $family = $this->FontFamily;
4215 } else {
4216 $this->Error('Empty font family');
4217 }
4218 }
4219 // move embedded styles on $style
4220 if (substr($family, -1) == 'I') {
4221 $style .= 'I';
4222 $family = substr($family, 0, -1);
4223 }
4224 if (substr($family, -1) == 'B') {
4225 $style .= 'B';
4226 $family = substr($family, 0, -1);
4227 }
4228 // normalize family name
4229 $family = strtolower($family);
4230 if ((!$this->isunicode) AND ($family == 'arial')) {
4231 $family = 'helvetica';
4232 }
4233 if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
4234 $style = '';
4235 }
4236 if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) {
4237 // all fonts must be embedded
4238 $family = 'pdfa'.$family;
4239 }
4240 $tempstyle = strtoupper($style);
4241 $style = '';
4242 // underline
4243 if (strpos($tempstyle, 'U') !== false) {
4244 $this->underline = true;
4245 } else {
4246 $this->underline = false;
4247 }
4248 // line-through (deleted)
4249 if (strpos($tempstyle, 'D') !== false) {
4250 $this->linethrough = true;
4251 } else {
4252 $this->linethrough = false;
4253 }
4254 // overline
4255 if (strpos($tempstyle, 'O') !== false) {
4256 $this->overline = true;
4257 } else {
4258 $this->overline = false;
4259 }
4260 // bold
4261 if (strpos($tempstyle, 'B') !== false) {
4262 $style .= 'B';
4263 }
4264 // oblique
4265 if (strpos($tempstyle, 'I') !== false) {
4266 $style .= 'I';
4267 }
4268 $bistyle = $style;
4269 $fontkey = $family.$style;
4270 $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : '');
4271 $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
4272 // check if the font has been already added
4273 $fb = $this->getFontBuffer($fontkey);
4274 if ($fb !== false) {
4275 if ($this->inxobj) {
4276 // we are inside an XObject template
4277 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i'];
4278 }
4279 return $fontdata;
4280 }
4281 // get specified font directory (if any)
4282 $fontdir = false;
4283 if (!TCPDF_STATIC::empty_string($fontfile)) {
4284 $fontdir = dirname($fontfile);
4285 if (TCPDF_STATIC::empty_string($fontdir) OR ($fontdir == '.')) {
4286 $fontdir = '';
4287 } else {
4288 $fontdir .= '/';
4289 }
4290 }
4291 $missing_style = false; // true when the font style variation is missing
4292 // search and include font file
4293 if (TCPDF_STATIC::empty_string($fontfile) OR (!file_exists($fontfile))) {
4294 // build a standard filenames for specified font
4295 $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
4296 // search files on various directories
4297 if (($fontdir !== false) AND file_exists($fontdir.$tmp_fontfile)) {
4298 $fontfile = $fontdir.$tmp_fontfile;
4299 } elseif (file_exists(TCPDF_FONTS::_getfontpath().$tmp_fontfile)) {
4300 $fontfile = TCPDF_FONTS::_getfontpath().$tmp_fontfile;
4301 } elseif (file_exists($tmp_fontfile)) {
4302 $fontfile = $tmp_fontfile;
4303 } elseif (!TCPDF_STATIC::empty_string($style)) {
4304 $missing_style = true;
4305 // try to remove the style part
4306 $tmp_fontfile = str_replace(' ', '', $family).'.php';
4307 if (($fontdir !== false) AND file_exists($fontdir.$tmp_fontfile)) {
4308 $fontfile = $fontdir.$tmp_fontfile;
4309 } elseif (file_exists(TCPDF_FONTS::_getfontpath().$tmp_fontfile)) {
4310 $fontfile = TCPDF_FONTS::_getfontpath().$tmp_fontfile;
4311 } else {
4312 $fontfile = $tmp_fontfile;
4313 }
4314 }
4315 }
4316 // include font file
4317 if (file_exists($fontfile)) {
4318 include($fontfile);
4319 } else {
4320 $this->Error('Could not include font definition file: '.$family.'');
4321 }
4322 // check font parameters
4323 if ((!isset($type)) OR (!isset($cw))) {
4324 $this->Error('The font definition file has a bad format: '.$fontfile.'');
4325 }
4326 // SET default parameters
4327 if (!isset($file) OR TCPDF_STATIC::empty_string($file)) {
4328 $file = '';
4329 }
4330 if (!isset($enc) OR TCPDF_STATIC::empty_string($enc)) {
4331 $enc = '';
4332 }
4333 if (!isset($cidinfo) OR TCPDF_STATIC::empty_string($cidinfo)) {
4334 $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0);
4335 $cidinfo['uni2cid'] = array();
4336 }
4337 if (!isset($ctg) OR TCPDF_STATIC::empty_string($ctg)) {
4338 $ctg = '';
4339 }
4340 if (!isset($desc) OR TCPDF_STATIC::empty_string($desc)) {
4341 $desc = array();
4342 }
4343 if (!isset($up) OR TCPDF_STATIC::empty_string($up)) {
4344 $up = -100;
4345 }
4346 if (!isset($ut) OR TCPDF_STATIC::empty_string($ut)) {
4347 $ut = 50;
4348 }
4349 if (!isset($cw) OR TCPDF_STATIC::empty_string($cw)) {
4350 $cw = array();
4351 }
4352 if (!isset($dw) OR TCPDF_STATIC::empty_string($dw)) {
4353 // set default width
4354 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
4355 $dw = $desc['MissingWidth'];
4356 } elseif (isset($cw[32])) {
4357 $dw = $cw[32];
4358 } else {
4359 $dw = 600;
4360 }
4361 }
4363 if ($type == 'core') {
4364 $name = $this->CoreFonts[$fontkey];
4365 $subset = false;
4366 } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
4367 $subset = false;
4368 } elseif ($type == 'TrueTypeUnicode') {
4369 $enc = 'Identity-H';
4370 } elseif ($type == 'cidfont0') {
4371 if ($this->pdfa_mode) {
4372 $this->Error('All fonts must be embedded in PDF/A mode!');
4373 }
4374 } else {
4375 $this->Error('Unknow font type: '.$type.'');
4376 }
4377 // set name if unset
4378 if (!isset($name) OR empty($name)) {
4379 $name = $fontkey;
4380 }
4381 // create artificial font style variations if missing (only works with non-embedded fonts)
4382 if (($type != 'core') AND $missing_style) {
4383 // style variations
4384 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
4385 $name .= $styles[$bistyle];
4386 // artificial bold
4387 if (strpos($bistyle, 'B') !== false) {
4388 if (isset($desc['StemV'])) {
4389 // from normal to bold
4390 $desc['StemV'] = round($desc['StemV'] * 1.75);
4391 } else {
4392 // bold
4393 $desc['StemV'] = 123;
4394 }
4395 }
4396 // artificial italic
4397 if (strpos($bistyle, 'I') !== false) {
4398 if (isset($desc['ItalicAngle'])) {
4399 $desc['ItalicAngle'] -= 11;
4400 } else {
4401 $desc['ItalicAngle'] = -11;
4402 }
4403 if (isset($desc['Flags'])) {
4404 $desc['Flags'] |= 64; //bit 7
4405 } else {
4406 $desc['Flags'] = 64;
4407 }
4408 }
4409 }
4410 // check if the array of characters bounding boxes is defined
4411 if (!isset($cbbox)) {
4412 $cbbox = array();
4413 }
4414 // initialize subsetchars
4415 $subsetchars = array_fill(0, 255, true);
4416 $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));
4417 if ($this->inxobj) {
4418 // we are inside an XObject template
4419 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts;
4420 }
4421 if (isset($diff) AND (!empty($diff))) {
4422 //Search existing encodings
4423 $d = 0;
4424 $nb = count($this->diffs);
4425 for ($i=1; $i <= $nb; ++$i) {
4426 if ($this->diffs[$i] == $diff) {
4427 $d = $i;
4428 break;
4429 }
4430 }
4431 if ($d == 0) {
4432 $d = $nb + 1;
4433 $this->diffs[$d] = $diff;
4434 }
4435 $this->setFontSubBuffer($fontkey, 'diff', $d);
4436 }
4438 if (!isset($this->FontFiles[$file])) {
4439 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
4440 $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4441 } elseif ($type != 'core') {
4442 $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4443 }
4444 } else {
4445 // update fontkeys that are sharing this font file
4446 $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset);
4447 if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) {
4448 $this->FontFiles[$file]['fontkeys'][] = $fontkey;
4449 }
4450 }
4451 }
4452 return $fontdata;
4453 }
4454
4472 public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) {
4473 //Select a font; size given in points
4474 if ($size === null) {
4476 }
4477 if ($size < 0) {
4478 $size = 0;
4479 }
4480 // try to add font (if not already added)
4481 $fontdata = $this->AddFont($family, $style, $fontfile, $subset);
4482 $this->FontFamily = $fontdata['family'];
4483 $this->FontStyle = $fontdata['style'];
4484 if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
4485 // save subset chars of the previous font
4486 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
4487 }
4488 $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
4489 $this->SetFontSize($size, $out);
4490 }
4491
4500 public function SetFontSize($size, $out=true) {
4501 // font size in points
4502 $this->FontSizePt = $size;
4503 // font size in user units
4504 $this->FontSize = $size / $this->k;
4505 // calculate some font metrics
4506 if (isset($this->CurrentFont['desc']['FontBBox'])) {
4507 $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4508 $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
4509 } else {
4510 $font_height = $size * 1.219;
4511 }
4512 if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
4513 $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000);
4514 }
4515 if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) {
4516 $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000);
4517 }
4518 if (!isset($font_ascent) AND !isset($font_descent)) {
4519 // core font
4520 $font_ascent = 0.76 * $font_height;
4521 $font_descent = $font_height - $font_ascent;
4522 } elseif (!isset($font_descent)) {
4523 $font_descent = $font_height - $font_ascent;
4524 } elseif (!isset($font_ascent)) {
4525 $font_ascent = $font_height - $font_descent;
4526 }
4527 $this->FontAscent = ($font_ascent / $this->k);
4528 $this->FontDescent = ($font_descent / $this->k);
4529 if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i'])) AND ($this->state == 2)) {
4530 $this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4531 }
4532 }
4533
4540 public function getFontBBox() {
4541 $fbbox = array();
4542 if (isset($this->CurrentFont['desc']['FontBBox'])) {
4543 $tmpbbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4544 $fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox);
4545 } else {
4546 // Find max width
4547 if (isset($this->CurrentFont['desc']['MaxWidth'])) {
4548 $maxw = $this->getAbsFontMeasure(intval($this->CurrentFont['desc']['MaxWidth']));
4549 } else {
4550 $maxw = 0;
4551 if (isset($this->CurrentFont['desc']['MissingWidth'])) {
4552 $maxw = max($maxw, $this->CurrentFont['desc']['MissingWidth']);
4553 }
4554 if (isset($this->CurrentFont['desc']['AvgWidth'])) {
4555 $maxw = max($maxw, $this->CurrentFont['desc']['AvgWidth']);
4556 }
4557 if (isset($this->CurrentFont['dw'])) {
4558 $maxw = max($maxw, $this->CurrentFont['dw']);
4559 }
4560 foreach ($this->CurrentFont['cw'] as $char => $w) {
4561 $maxw = max($maxw, $w);
4562 }
4563 if ($maxw == 0) {
4564 $maxw = 600;
4565 }
4566 $maxw = $this->getAbsFontMeasure($maxw);
4567 }
4568 $fbbox = array(0, (0 - $this->FontDescent), $maxw, $this->FontAscent);
4569 }
4570 return $fbbox;
4571 }
4572
4579 public function getAbsFontMeasure($s) {
4580 return ($s * $this->FontSize / 1000);
4581 }
4582
4589 public function getCharBBox($char) {
4590 if (isset($this->CurrentFont['cbbox'][$char])) {
4591 return array_map(array($this,'getAbsFontMeasure'), $this->CurrentFont['cbbox'][intval($char)]);
4592 }
4593 return false;
4594 }
4595
4606 public function getFontDescent($font, $style='', $size=0) {
4607 $fontdata = $this->AddFont($font, $style);
4608 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4609 if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
4610 $descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
4611 } else {
4612 $descent = (1.219 * 0.24 * $size);
4613 }
4614 return ($descent / $this->k);
4615 }
4616
4627 public function getFontAscent($font, $style='', $size=0) {
4628 $fontdata = $this->AddFont($font, $style);
4629 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4630 if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
4631 $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
4632 } else {
4633 $ascent = 1.219 * 0.76 * $size;
4634 }
4635 return ($ascent / $this->k);
4636 }
4637
4647 public function isCharDefined($char, $font='', $style='') {
4648 if (is_string($char)) {
4649 // get character code
4650 $char = TCPDF_FONTS::UTF8StringToArray($char, $this->isunicode, $this->CurrentFont);
4651 $char = $char[0];
4652 }
4653 if (TCPDF_STATIC::empty_string($font)) {
4654 if (TCPDF_STATIC::empty_string($style)) {
4655 return (isset($this->CurrentFont['cw'][intval($char)]));
4656 }
4657 $font = $this->FontFamily;
4658 }
4659 $fontdata = $this->AddFont($font, $style);
4660 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4661 return (isset($fontinfo['cw'][intval($char)]));
4662 }
4663
4674 public function replaceMissingChars($text, $font='', $style='', $subs=array()) {
4675 if (empty($subs)) {
4676 return $text;
4677 }
4678 if (TCPDF_STATIC::empty_string($font)) {
4679 $font = $this->FontFamily;
4680 }
4681 $fontdata = $this->AddFont($font, $style);
4682 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4683 $uniarr = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
4684 foreach ($uniarr as $k => $chr) {
4685 if (!isset($fontinfo['cw'][$chr])) {
4686 // this character is missing on the selected font
4687 if (isset($subs[$chr])) {
4688 // we have available substitutions
4689 if (is_array($subs[$chr])) {
4690 foreach($subs[$chr] as $s) {
4691 if (isset($fontinfo['cw'][$s])) {
4692 $uniarr[$k] = $s;
4693 break;
4694 }
4695 }
4696 } elseif (isset($fontinfo['cw'][$subs[$chr]])) {
4697 $uniarr[$k] = $subs[$chr];
4698 }
4699 }
4700 }
4701 }
4702 return TCPDF_FONTS::UniArrSubString(TCPDF_FONTS::UTF8ArrayToUniArray($uniarr, $this->isunicode));
4703 }
4704
4711 public function SetDefaultMonospacedFont($font) {
4712 $this->default_monospaced_font = $font;
4713 }
4714
4722 public function AddLink() {
4723 //Create a new internal link
4724 $n = count($this->links) + 1;
4725 $this->links[$n] = array(0, 0);
4726 return $n;
4727 }
4728
4738 public function SetLink($link, $y=0, $page=-1) {
4739 if ($y == -1) {
4740 $y = $this->y;
4741 }
4742 if ($page == -1) {
4744 }
4745 $this->links[$link] = array($page, $y);
4746 }
4747
4761 public function Link($x, $y, $w, $h, $link, $spaces=0) {
4762 $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
4763 }
4764
4778 public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
4779 if ($this->inxobj) {
4780 // store parameters for later use on template
4781 $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
4782 return;
4783 }
4784 if ($x === '') {
4785 $x = $this->x;
4786 }
4787 if ($y === '') {
4788 $y = $this->y;
4789 }
4790 // check page for no-write regions and adapt page margins if necessary
4791 list($x, $y) = $this->checkPageRegions($h, $x, $y);
4792 // recalculate coordinates to account for graphic transformations
4793 if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) {
4794 for ($i=$this->transfmatrix_key; $i > 0; --$i) {
4795 $maxid = count($this->transfmatrix[$i]) - 1;
4796 for ($j=$maxid; $j >= 0; --$j) {
4797 $ctm = $this->transfmatrix[$i][$j];
4798 if (isset($ctm['a'])) {
4799 $x = $x * $this->k;
4800 $y = ($this->h - $y) * $this->k;
4801 $w = $w * $this->k;
4802 $h = $h * $this->k;
4803 // top left
4804 $xt = $x;
4805 $yt = $y;
4806 $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4807 $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4808 // top right
4809 $xt = $x + $w;
4810 $yt = $y;
4811 $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4812 $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4813 // bottom left
4814 $xt = $x;
4815 $yt = $y - $h;
4816 $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4817 $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4818 // bottom right
4819 $xt = $x + $w;
4820 $yt = $y - $h;
4821 $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4822 $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4823 // new coordinates (rectangle area)
4824 $x = min($x1, $x2, $x3, $x4);
4825 $y = max($y1, $y2, $y3, $y4);
4826 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
4827 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
4828 $x = $x / $this->k;
4829 $y = $this->h - ($y / $this->k);
4830 }
4831 }
4832 }
4833 }
4834 if ($this->page <= 0) {
4835 $page = 1;
4836 } else {
4838 }
4839 if (!isset($this->PageAnnots[$page])) {
4840 $this->PageAnnots[$page] = array();
4841 }
4842 $this->PageAnnots[$page][] = array('n' => ++$this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
4843 if (!$this->pdfa_mode) {
4844 if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!TCPDF_STATIC::empty_string($opt['FS']))
4845 AND (file_exists($opt['FS']) OR TCPDF_STATIC::isValidURL($opt['FS']))
4846 AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
4847 $this->embeddedfiles[basename($opt['FS'])] = array('f' => ++$this->n, 'n' => ++$this->n, 'file' => $opt['FS']);
4848 }
4849 }
4850 // Add widgets annotation's icons
4851 if (isset($opt['mk']['i']) AND file_exists($opt['mk']['i'])) {
4852 $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
4853 }
4854 if (isset($opt['mk']['ri']) AND file_exists($opt['mk']['ri'])) {
4855 $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4856 }
4857 if (isset($opt['mk']['ix']) AND file_exists($opt['mk']['ix'])) {
4858 $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4859 }
4860 }
4861
4868 protected function _putEmbeddedFiles() {
4869 if ($this->pdfa_mode) {
4870 // embedded files are not allowed in PDF/A mode
4871 return;
4872 }
4873 reset($this->embeddedfiles);
4874 foreach ($this->embeddedfiles as $filename => $filedata) {
4875 // update name tree
4876 $this->efnames[$filename] = $filedata['f'].' 0 R';
4877 // embedded file specification object
4878 $out = $this->_getobj($filedata['f'])."\n";
4879 $out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']).' /EF <</F '.$filedata['n'].' 0 R>> >>';
4880 $out .= "\n".'endobj';
4881 $this->_out($out);
4882 // embedded file object
4883 $data = file_get_contents($filedata['file']);
4884 $filter = '';
4885 $rawsize = strlen($data);
4886 if ($this->compress) {
4887 $data = gzcompress($data);
4888 $filter = ' /Filter /FlateDecode';
4889 }
4890 $stream = $this->_getrawstream($data, $filedata['n']);
4891 $out = $this->_getobj($filedata['n'])."\n";
4892 $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>';
4893 $out .= ' stream'."\n".$stream."\n".'endstream';
4894 $out .= "\n".'endobj';
4895 $this->_out($out);
4896 }
4897 }
4898
4922 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) {
4925 $this->setTextRenderingMode($fstroke, $ffill, $fclip);
4926 $this->SetXY($x, $y, $rtloff);
4927 $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
4928 // restore previous rendering mode
4929 $this->textrendermode = $textrendermode;
4930 $this->textstrokewidth = $textstrokewidth;
4931 }
4932
4942 public function AcceptPageBreak() {
4943 if ($this->num_columns > 1) {
4944 // multi column mode
4945 if ($this->current_column < ($this->num_columns - 1)) {
4946 // go to next column
4947 $this->selectColumn($this->current_column + 1);
4948 } elseif ($this->AutoPageBreak) {
4949 // add a new page
4950 $this->AddPage();
4951 // set first column
4952 $this->selectColumn(0);
4953 }
4954 // avoid page breaking from checkPageBreak()
4955 return false;
4956 }
4957 return $this->AutoPageBreak;
4958 }
4959
4969 protected function checkPageBreak($h=0, $y='', $addpage=true) {
4971 $y = $this->y;
4972 }
4973 $current_page = $this->page;
4974 if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) {
4975 if ($addpage) {
4976 //Automatic page break
4977 $x = $this->x;
4978 $this->AddPage($this->CurOrientation);
4979 $this->y = $this->tMargin;
4980 $oldpage = $this->page - 1;
4981 if ($this->rtl) {
4982 if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
4983 $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
4984 } else {
4985 $this->x = $x;
4986 }
4987 } else {
4988 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
4989 $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
4990 } else {
4991 $this->x = $x;
4992 }
4993 }
4994 }
4995 return true;
4996 }
4997 if ($current_page != $this->page) {
4998 // account for columns mode
4999 return true;
5000 }
5001 return false;
5002 }
5003
5023 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') {
5024 $prev_cell_margin = $this->cell_margin;
5025 $prev_cell_padding = $this->cell_padding;
5026 $this->adjustCellPadding($border);
5027 if (!$ignore_min_height) {
5028 $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
5029 if ($h < $min_cell_height) {
5030 $h = $min_cell_height;
5031 }
5032 }
5033 $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']);
5034 // apply text shadow if enabled
5035 if ($this->txtshadow['enabled']) {
5036 // save data
5037 $x = $this->x;
5038 $y = $this->y;
5039 $bc = $this->bgcolor;
5040 $fc = $this->fgcolor;
5041 $sc = $this->strokecolor;
5043 // print shadow
5044 $this->x += $this->txtshadow['depth_w'];
5045 $this->y += $this->txtshadow['depth_h'];
5046 $this->SetFillColorArray($this->txtshadow['color']);
5047 $this->SetTextColorArray($this->txtshadow['color']);
5048 $this->SetDrawColorArray($this->txtshadow['color']);
5049 if ($this->txtshadow['opacity'] != $alpha['CA']) {
5050 $this->setAlpha($this->txtshadow['opacity'], $this->txtshadow['blend_mode']);
5051 }
5052 if ($this->state == 2) {
5053 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5054 }
5055 //restore data
5056 $this->x = $x;
5057 $this->y = $y;
5058 $this->SetFillColorArray($bc);
5059 $this->SetTextColorArray($fc);
5060 $this->SetDrawColorArray($sc);
5061 if ($this->txtshadow['opacity'] != $alpha['CA']) {
5062 $this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']);
5063 }
5064 }
5065 if ($this->state == 2) {
5066 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5067 }
5068 $this->cell_padding = $prev_cell_padding;
5069 $this->cell_margin = $prev_cell_margin;
5070 }
5071
5092 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') {
5093 // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space
5094 $txt = str_replace(TCPDF_FONTS::unichr(160, $this->isunicode), ' ', $txt);
5095 $prev_cell_margin = $this->cell_margin;
5096 $prev_cell_padding = $this->cell_padding;
5097 $txt = TCPDF_STATIC::removeSHY($txt, $this->isunicode);
5098 $rs = ''; //string to be returned
5099 $this->adjustCellPadding($border);
5100 if (!$ignore_min_height) {
5101 $min_cell_height = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
5102 if ($h < $min_cell_height) {
5103 $h = $min_cell_height;
5104 }
5105 }
5106 $k = $this->k;
5107 // check page for no-write regions and adapt page margins if necessary
5108 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
5109 if ($this->rtl) {
5110 $x = $this->x - $this->cell_margin['R'];
5111 } else {
5112 $x = $this->x + $this->cell_margin['L'];
5113 }
5114 $y = $this->y + $this->cell_margin['T'];
5115 $prev_font_stretching = $this->font_stretching;
5116 $prev_font_spacing = $this->font_spacing;
5117 // cell vertical alignment
5118 switch ($calign) {
5119 case 'A': {
5120 // font top
5121 switch ($valign) {
5122 case 'T': {
5123 // top
5124 $y -= $this->cell_padding['T'];
5125 break;
5126 }
5127 case 'B': {
5128 // bottom
5129 $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent);
5130 break;
5131 }
5132 default:
5133 case 'C':
5134 case 'M': {
5135 // center
5136 $y -= (($h - $this->FontAscent - $this->FontDescent) / 2);
5137 break;
5138 }
5139 }
5140 break;
5141 }
5142 case 'L': {
5143 // font baseline
5144 switch ($valign) {
5145 case 'T': {
5146 // top
5147 $y -= ($this->cell_padding['T'] + $this->FontAscent);
5148 break;
5149 }
5150 case 'B': {
5151 // bottom
5152 $y -= ($h - $this->cell_padding['B'] - $this->FontDescent);
5153 break;
5154 }
5155 default:
5156 case 'C':
5157 case 'M': {
5158 // center
5159 $y -= (($h + $this->FontAscent - $this->FontDescent) / 2);
5160 break;
5161 }
5162 }
5163 break;
5164 }
5165 case 'D': {
5166 // font bottom
5167 switch ($valign) {
5168 case 'T': {
5169 // top
5170 $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent);
5171 break;
5172 }
5173 case 'B': {
5174 // bottom
5175 $y -= ($h - $this->cell_padding['B']);
5176 break;
5177 }
5178 default:
5179 case 'C':
5180 case 'M': {
5181 // center
5182 $y -= (($h + $this->FontAscent + $this->FontDescent) / 2);
5183 break;
5184 }
5185 }
5186 break;
5187 }
5188 case 'B': {
5189 // cell bottom
5190 $y -= $h;
5191 break;
5192 }
5193 case 'C':
5194 case 'M': {
5195 // cell center
5196 $y -= ($h / 2);
5197 break;
5198 }
5199 default:
5200 case 'T': {
5201 // cell top
5202 break;
5203 }
5204 }
5205 // text vertical alignment
5206 switch ($valign) {
5207 case 'T': {
5208 // top
5209 $yt = $y + $this->cell_padding['T'];
5210 break;
5211 }
5212 case 'B': {
5213 // bottom
5214 $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent;
5215 break;
5216 }
5217 default:
5218 case 'C':
5219 case 'M': {
5220 // center
5221 $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2);
5222 break;
5223 }
5224 }
5225 $basefonty = $yt + $this->FontAscent;
5226 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5227 if ($this->rtl) {
5228 $w = $x - $this->lMargin;
5229 } else {
5230 $w = $this->w - $this->rMargin - $x;
5231 }
5232 }
5233 $s = '';
5234 // fill and borders
5235 if (is_string($border) AND (strlen($border) == 4)) {
5236 // full border
5237 $border = 1;
5238 }
5239 if ($fill OR ($border == 1)) {
5240 if ($fill) {
5241 $op = ($border == 1) ? 'B' : 'f';
5242 } else {
5243 $op = 'S';
5244 }
5245 if ($this->rtl) {
5246 $xk = (($x - $w) * $k);
5247 } else {
5248 $xk = ($x * $k);
5249 }
5250 $s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op);
5251 }
5252 // draw borders
5253 $s .= $this->getCellBorder($x, $y, $w, $h, $border);
5254 if ($txt != '') {
5255 $txt2 = $txt;
5256 if ($this->isunicode) {
5257 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5258 $txt2 = TCPDF_FONTS::UTF8ToLatin1($txt2, $this->isunicode, $this->CurrentFont);
5259 } else {
5260 $unicode = TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont); // array of UTF-8 unicode values
5261 $unicode = TCPDF_FONTS::utf8Bidi($unicode, '', $this->tmprtl, $this->isunicode, $this->CurrentFont);
5262 // replace thai chars (if any)
5263 if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) {
5264 // number of chars
5265 $numchars = count($unicode);
5266 // po pla, for far, for fan
5267 $longtail = array(0x0e1b, 0x0e1d, 0x0e1f);
5268 // do chada, to patak
5269 $lowtail = array(0x0e0e, 0x0e0f);
5270 // mai hun arkad, sara i, sara ii, sara ue, sara uee
5271 $upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37);
5272 // mai ek, mai tho, mai tri, mai chattawa, karan
5273 $tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c);
5274 // sara u, sara uu, pinthu
5275 $lowvowel = array(0x0e38, 0x0e39, 0x0e3a);
5276 $output = array();
5277 for ($i = 0; $i < $numchars; $i++) {
5278 if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) {
5279 $ch0 = $unicode[$i];
5280 $ch1 = ($i > 0) ? $unicode[($i - 1)] : 0;
5281 $ch2 = ($i > 1) ? $unicode[($i - 2)] : 0;
5282 $chn = ($i < ($numchars - 1)) ? $unicode[($i + 1)] : 0;
5283 if (in_array($ch0, $tonemark)) {
5284 if ($chn == 0x0e33) {
5285 // sara um
5286 if (in_array($ch1, $longtail)) {
5287 // tonemark at upper left
5288 $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5289 } else {
5290 // tonemark at upper right (normal position)
5291 $output[] = $ch0;
5292 }
5293 } elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) {
5294 // tonemark at lower left
5295 $output[] = $this->replaceChar($ch0, (0xf705 + $ch0 - 0x0e48));
5296 } elseif (in_array($ch1, $upvowel)) {
5297 if (in_array($ch2, $longtail)) {
5298 // tonemark at upper left
5299 $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5300 } else {
5301 // tonemark at upper right (normal position)
5302 $output[] = $ch0;
5303 }
5304 } else {
5305 // tonemark at lower right
5306 $output[] = $this->replaceChar($ch0, (0xf70a + $ch0 - 0x0e48));
5307 }
5308 } elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) {
5309 // add lower left nikhahit and sara aa
5310 if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) {
5311 $output[] = 0xf711;
5312 $this->CurrentFont['subsetchars'][0xf711] = true;
5313 $output[] = 0x0e32;
5314 $this->CurrentFont['subsetchars'][0x0e32] = true;
5315 } else {
5316 $output[] = $ch0;
5317 }
5318 } elseif (in_array($ch1, $longtail)) {
5319 if ($ch0 == 0x0e31) {
5320 // lower left mai hun arkad
5321 $output[] = $this->replaceChar($ch0, 0xf710);
5322 } elseif (in_array($ch0, $upvowel)) {
5323 // lower left
5324 $output[] = $this->replaceChar($ch0, (0xf701 + $ch0 - 0x0e34));
5325 } elseif ($ch0 == 0x0e47) {
5326 // lower left mai tai koo
5327 $output[] = $this->replaceChar($ch0, 0xf712);
5328 } else {
5329 // normal character
5330 $output[] = $ch0;
5331 }
5332 } elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) {
5333 // lower vowel
5334 $output[] = $this->replaceChar($ch0, (0xf718 + $ch0 - 0x0e38));
5335 } elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) {
5336 // yo ying without lower part
5337 $output[] = $this->replaceChar($ch0, 0xf70f);
5338 } elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) {
5339 // tho santan without lower part
5340 $output[] = $this->replaceChar($ch0, 0xf700);
5341 } else {
5342 $output[] = $ch0;
5343 }
5344 } else {
5345 // non-thai character
5346 $output[] = $unicode[$i];
5347 }
5348 }
5349 $unicode = $output;
5350 // update font subsetchars
5351 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
5352 } // end of K_THAI_TOPCHARS
5353 $txt2 = TCPDF_FONTS::arrUTF8ToUTF16BE($unicode, false);
5354 }
5355 }
5356 $txt2 = TCPDF_STATIC::_escape($txt2);
5357 // get current text width (considering general font stretching and spacing)
5358 $txwidth = $this->GetStringWidth($txt);
5359 $width = $txwidth;
5360 // check for stretch mode
5361 if ($stretch > 0) {
5362 // calculate ratio between cell width and text width
5363 if ($width <= 0) {
5364 $ratio = 1;
5365 } else {
5366 $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width);
5367 }
5368 // check if stretching is required
5369 if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) {
5370 // the text will be stretched to fit cell width
5371 if ($stretch > 2) {
5372 // set new character spacing
5373 $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100));
5374 } else {
5375 // set new horizontal stretching
5376 $this->font_stretching *= $ratio;
5377 }
5378 // recalculate text width (the text fills the entire cell)
5379 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5380 // reset alignment
5381 $align = '';
5382 }
5383 }
5384 if ($this->font_stretching != 100) {
5385 // apply font stretching
5386 $rs .= sprintf('BT %F Tz ET ', $this->font_stretching);
5387 }
5388 if ($this->font_spacing != 0) {
5389 // increase/decrease font spacing
5390 $rs .= sprintf('BT %F Tc ET ', ($this->font_spacing * $this->k));
5391 }
5392 if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5393 $s .= 'q '.$this->TextColor.' ';
5394 }
5395 // rendering mode
5396 $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, ($this->textstrokewidth * $this->k));
5397 // count number of spaces
5398 $ns = substr_count($txt, chr(32));
5399 // Justification
5400 $spacewidth = 0;
5401 if (($align == 'J') AND ($ns > 0)) {
5402 if ($this->isUnicodeFont()) {
5403 // get string width without spaces
5404 $width = $this->GetStringWidth(str_replace(' ', '', $txt));
5405 // calculate average space width
5406 $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / $this->FontSize;
5407 if ($this->font_stretching != 100) {
5408 // word spacing is affected by stretching
5409 $spacewidth /= ($this->font_stretching / 100);
5410 }
5411 // set word position to be used with TJ operator
5412 $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2);
5413 $unicode_justification = true;
5414 } else {
5415 // get string width
5416 $width = $txwidth;
5417 // new space width
5418 $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k;
5419 if ($this->font_stretching != 100) {
5420 // word spacing (Tw) is affected by stretching
5421 $spacewidth /= ($this->font_stretching / 100);
5422 }
5423 // set word spacing
5424 $rs .= sprintf('BT %F Tw ET ', $spacewidth);
5425 }
5426 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5427 }
5428 // replace carriage return characters
5429 $txt2 = str_replace("\r", ' ', $txt2);
5430 switch ($align) {
5431 case 'C': {
5432 $dx = ($w - $width) / 2;
5433 break;
5434 }
5435 case 'R': {
5436 if ($this->rtl) {
5437 $dx = $this->cell_padding['R'];
5438 } else {
5439 $dx = $w - $width - $this->cell_padding['R'];
5440 }
5441 break;
5442 }
5443 case 'L': {
5444 if ($this->rtl) {
5445 $dx = $w - $width - $this->cell_padding['L'];
5446 } else {
5447 $dx = $this->cell_padding['L'];
5448 }
5449 break;
5450 }
5451 case 'J':
5452 default: {
5453 if ($this->rtl) {
5454 $dx = $this->cell_padding['R'];
5455 } else {
5456 $dx = $this->cell_padding['L'];
5457 }
5458 break;
5459 }
5460 }
5461 if ($this->rtl) {
5462 $xdx = $x - $dx - $width;
5463 } else {
5464 $xdx = $x + $dx;
5465 }
5466 $xdk = $xdx * $k;
5467 // print text
5468 $s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
5469 if (isset($uniblock)) {
5470 // print overlapping characters as separate string
5471 $xshift = 0; // horizontal shift
5472 $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k);
5473 $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1));
5474 foreach ($uniblock as $uk => $uniarr) {
5475 if (($uk % 2) == 0) {
5476 // x space to skip
5477 if ($spacewidth != 0) {
5478 // justification shift
5479 $xshift += (count(array_keys($uniarr, 32)) * $spw);
5480 }
5481 $xshift += $this->GetArrStringWidth($uniarr); // + shift justification
5482 } else {
5483 // character to print
5484 $topchr = TCPDF_FONTS::arrUTF8ToUTF16BE($uniarr, false);
5485 $topchr = TCPDF_STATIC::_escape($topchr);
5486 $s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr);
5487 }
5488 }
5489 }
5490 if ($this->underline) {
5491 $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
5492 }
5493 if ($this->linethrough) {
5494 $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
5495 }
5496 if ($this->overline) {
5497 $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
5498 }
5499 if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5500 $s .= ' Q';
5501 }
5502 if ($link) {
5503 $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns);
5504 }
5505 }
5506 // output cell
5507 if ($s) {
5508 // output cell
5509 $rs .= $s;
5510 if ($this->font_spacing != 0) {
5511 // reset font spacing mode
5512 $rs .= ' BT 0 Tc ET';
5513 }
5514 if ($this->font_stretching != 100) {
5515 // reset font stretching mode
5516 $rs .= ' BT 100 Tz ET';
5517 }
5518 }
5519 // reset word spacing
5520 if (!$this->isUnicodeFont() AND ($align == 'J')) {
5521 $rs .= ' BT 0 Tw ET';
5522 }
5523 // reset stretching and spacing
5524 $this->font_stretching = $prev_font_stretching;
5525 $this->font_spacing = $prev_font_spacing;
5526 $this->lasth = $h;
5527 if ($ln > 0) {
5528 //Go to the beginning of the next line
5529 $this->y = $y + $h + $this->cell_margin['B'];
5530 if ($ln == 1) {
5531 if ($this->rtl) {
5532 $this->x = $this->w - $this->rMargin;
5533 } else {
5534 $this->x = $this->lMargin;
5535 }
5536 }
5537 } else {
5538 // go left or right by case
5539 if ($this->rtl) {
5540 $this->x = $x - $w - $this->cell_margin['L'];
5541 } else {
5542 $this->x = $x + $w + $this->cell_margin['R'];
5543 }
5544 }
5545 $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
5546 $rs = $gstyles.$rs;
5547 $this->cell_padding = $prev_cell_padding;
5548 $this->cell_margin = $prev_cell_margin;
5549 return $rs;
5550 }
5551
5560 protected function replaceChar($oldchar, $newchar) {
5561 if ($this->isCharDefined($newchar)) {
5562 // add the new char on the subset list
5563 $this->CurrentFont['subsetchars'][$newchar] = true;
5564 // return the new character
5565 return $newchar;
5566 }
5567 // return the old char
5568 return $oldchar;
5569 }
5570
5583 protected function getCellBorder($x, $y, $w, $h, $brd) {
5584 $s = ''; // string to be returned
5585 if (empty($brd)) {
5586 return $s;
5587 }
5588 if ($brd == 1) {
5589 $brd = array('LRTB' => true);
5590 }
5591 // calculate coordinates for border
5592 $k = $this->k;
5593 if ($this->rtl) {
5594 $xeL = ($x - $w) * $k;
5595 $xeR = $x * $k;
5596 } else {
5597 $xeL = $x * $k;
5598 $xeR = ($x + $w) * $k;
5599 }
5600 $yeL = (($this->h - ($y + $h)) * $k);
5601 $yeT = (($this->h - $y) * $k);
5602 $xeT = $xeL;
5603 $xeB = $xeR;
5604 $yeR = $yeT;
5605 $yeB = $yeL;
5606 if (is_string($brd)) {
5607 // convert string to array
5608 $slen = strlen($brd);
5609 $newbrd = array();
5610 for ($i = 0; $i < $slen; ++$i) {
5611 $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
5612 }
5613 $brd = $newbrd;
5614 }
5615 if (isset($brd['mode'])) {
5616 $mode = $brd['mode'];
5617 unset($brd['mode']);
5618 } else {
5619 $mode = 'normal';
5620 }
5621 foreach ($brd as $border => $style) {
5622 if (is_array($style) AND !empty($style)) {
5623 // apply border style
5624 $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' ';
5625 $s .= $this->SetLineStyle($style, true)."\n";
5626 }
5627 switch ($mode) {
5628 case 'ext': {
5629 $off = (($this->LineWidth / 2) * $k);
5630 $xL = $xeL - $off;
5631 $xR = $xeR + $off;
5632 $yT = $yeT + $off;
5633 $yL = $yeL - $off;
5634 $xT = $xL;
5635 $xB = $xR;
5636 $yR = $yT;
5637 $yB = $yL;
5640 break;
5641 }
5642 case 'int': {
5643 $off = ($this->LineWidth / 2) * $k;
5644 $xL = $xeL + $off;
5645 $xR = $xeR - $off;
5646 $yT = $yeT - $off;
5647 $yL = $yeL + $off;
5648 $xT = $xL;
5649 $xB = $xR;
5650 $yR = $yT;
5651 $yB = $yL;
5654 break;
5655 }
5656 case 'normal':
5657 default: {
5658 $xL = $xeL;
5659 $xT = $xeT;
5660 $xB = $xeB;
5661 $xR = $xeR;
5662 $yL = $yeL;
5663 $yT = $yeT;
5664 $yB = $yeB;
5665 $yR = $yeR;
5666 break;
5667 }
5668 }
5669 // draw borders by case
5670 if (strlen($border) == 4) {
5671 $s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k));
5672 } elseif (strlen($border) == 3) {
5673 if (strpos($border,'B') === false) { // LTR
5674 $s .= sprintf('%F %F m ', $xL, $yL);
5675 $s .= sprintf('%F %F l ', $xT, $yT);
5676 $s .= sprintf('%F %F l ', $xR, $yR);
5677 $s .= sprintf('%F %F l ', $xB, $yB);
5678 $s .= 'S ';
5679 } elseif (strpos($border,'L') === false) { // TRB
5680 $s .= sprintf('%F %F m ', $xT, $yT);
5681 $s .= sprintf('%F %F l ', $xR, $yR);
5682 $s .= sprintf('%F %F l ', $xB, $yB);
5683 $s .= sprintf('%F %F l ', $xL, $yL);
5684 $s .= 'S ';
5685 } elseif (strpos($border,'T') === false) { // RBL
5686 $s .= sprintf('%F %F m ', $xR, $yR);
5687 $s .= sprintf('%F %F l ', $xB, $yB);
5688 $s .= sprintf('%F %F l ', $xL, $yL);
5689 $s .= sprintf('%F %F l ', $xT, $yT);
5690 $s .= 'S ';
5691 } elseif (strpos($border,'R') === false) { // BLT
5692 $s .= sprintf('%F %F m ', $xB, $yB);
5693 $s .= sprintf('%F %F l ', $xL, $yL);
5694 $s .= sprintf('%F %F l ', $xT, $yT);
5695 $s .= sprintf('%F %F l ', $xR, $yR);
5696 $s .= 'S ';
5697 }
5698 } elseif (strlen($border) == 2) {
5699 if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
5700 $s .= sprintf('%F %F m ', $xL, $yL);
5701 $s .= sprintf('%F %F l ', $xT, $yT);
5702 $s .= sprintf('%F %F l ', $xR, $yR);
5703 $s .= 'S ';
5704 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
5705 $s .= sprintf('%F %F m ', $xT, $yT);
5706 $s .= sprintf('%F %F l ', $xR, $yR);
5707 $s .= sprintf('%F %F l ', $xB, $yB);
5708 $s .= 'S ';
5709 } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
5710 $s .= sprintf('%F %F m ', $xR, $yR);
5711 $s .= sprintf('%F %F l ', $xB, $yB);
5712 $s .= sprintf('%F %F l ', $xL, $yL);
5713 $s .= 'S ';
5714 } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
5715 $s .= sprintf('%F %F m ', $xB, $yB);
5716 $s .= sprintf('%F %F l ', $xL, $yL);
5717 $s .= sprintf('%F %F l ', $xT, $yT);
5718 $s .= 'S ';
5719 } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
5720 $s .= sprintf('%F %F m ', $xL, $yL);
5721 $s .= sprintf('%F %F l ', $xT, $yT);
5722 $s .= 'S ';
5723 $s .= sprintf('%F %F m ', $xR, $yR);
5724 $s .= sprintf('%F %F l ', $xB, $yB);
5725 $s .= 'S ';
5726 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
5727 $s .= sprintf('%F %F m ', $xT, $yT);
5728 $s .= sprintf('%F %F l ', $xR, $yR);
5729 $s .= 'S ';
5730 $s .= sprintf('%F %F m ', $xB, $yB);
5731 $s .= sprintf('%F %F l ', $xL, $yL);
5732 $s .= 'S ';
5733 }
5734 } else { // strlen($border) == 1
5735 if (strpos($border,'L') !== false) { // L
5736 $s .= sprintf('%F %F m ', $xL, $yL);
5737 $s .= sprintf('%F %F l ', $xT, $yT);
5738 $s .= 'S ';
5739 } elseif (strpos($border,'T') !== false) { // T
5740 $s .= sprintf('%F %F m ', $xT, $yT);
5741 $s .= sprintf('%F %F l ', $xR, $yR);
5742 $s .= 'S ';
5743 } elseif (strpos($border,'R') !== false) { // R
5744 $s .= sprintf('%F %F m ', $xR, $yR);
5745 $s .= sprintf('%F %F l ', $xB, $yB);
5746 $s .= 'S ';
5747 } elseif (strpos($border,'B') !== false) { // B
5748 $s .= sprintf('%F %F m ', $xB, $yB);
5749 $s .= sprintf('%F %F l ', $xL, $yL);
5750 $s .= 'S ';
5751 }
5752 }
5753 if (is_array($style) AND !empty($style)) {
5754 // reset border style to previous value
5755 $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n";
5756 }
5757 }
5758 return $s;
5759 }
5760
5786 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) {
5787 $prev_cell_margin = $this->cell_margin;
5788 $prev_cell_padding = $this->cell_padding;
5789 // adjust internal padding
5790 $this->adjustCellPadding($border);
5791 $mc_padding = $this->cell_padding;
5792 $mc_margin = $this->cell_margin;
5793 $this->cell_padding['T'] = 0;
5794 $this->cell_padding['B'] = 0;
5795 $this->setCellMargins(0, 0, 0, 0);
5796 if (TCPDF_STATIC::empty_string($this->lasth) OR $reseth) {
5797 // reset row height
5798 $this->resetLastH();
5799 }
5801 $this->SetY($y);
5802 } else {
5803 $y = $this->GetY();
5804 }
5805 $resth = 0;
5806 if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) {
5807 // spit cell in more pages/columns
5808 $newh = ($this->PageBreakTrigger - $y);
5809 $resth = ($h - $newh); // cell to be printed on the next page/column
5810 $h = $newh;
5811 }
5812 // get current page number
5813 $startpage = $this->page;
5814 // get current column
5815 $startcolumn = $this->current_column;
5817 $this->SetX($x);
5818 } else {
5819 $x = $this->GetX();
5820 }
5821 // check page for no-write regions and adapt page margins if necessary
5822 list($x, $y) = $this->checkPageRegions(0, $x, $y);
5823 // apply margins
5824 $oy = $y + $mc_margin['T'];
5825 if ($this->rtl) {
5826 $ox = ($this->w - $x - $mc_margin['R']);
5827 } else {
5828 $ox = ($x + $mc_margin['L']);
5829 }
5830 $this->x = $ox;
5831 $this->y = $oy;
5832 // set width
5833 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5834 if ($this->rtl) {
5835 $w = ($this->x - $this->lMargin - $mc_margin['L']);
5836 } else {
5837 $w = ($this->w - $this->x - $this->rMargin - $mc_margin['R']);
5838 }
5839 }
5840 // store original margin values
5843 if ($this->rtl) {
5844 $this->rMargin = ($this->w - $this->x);
5845 $this->lMargin = ($this->x - $w);
5846 } else {
5847 $this->lMargin = ($this->x);
5848 $this->rMargin = ($this->w - $this->x - $w);
5849 }
5850 $this->clMargin = $this->lMargin;
5851 $this->crMargin = $this->rMargin;
5852 if ($autopadding) {
5853 // add top padding
5854 $this->y += $mc_padding['T'];
5855 }
5856 if ($ishtml) { // ******* Write HTML text
5857 $this->writeHTML($txt, true, false, $reseth, true, $align);
5858 $nl = 1;
5859 } else { // ******* Write simple text
5860 $prev_FontSizePt = $this->FontSizePt;
5861 // vertical alignment
5862 if ($maxh > 0) {
5863 // get text height
5864 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5865 if ($fitcell) {
5866 // try to reduce font size to fit text on cell (use a quick search algorithm)
5867 $fmin = 1;
5868 $fmax = $this->FontSizePt;
5869 $prev_text_height = $text_height;
5870 $maxit = 100; // max number of iterations
5871 while ($maxit > 0) {
5872 $fmid = (($fmax + $fmin) / 2);
5873 $this->SetFontSize($fmid, false);
5874 $this->resetLastH();
5875 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5876 if (($text_height == $maxh) OR (($text_height < $maxh) AND ($fmin >= ($fmax - 0.01)))) {
5877 break;
5878 } elseif ($text_height < $maxh) {
5879 $fmin = $fmid;
5880 } else {
5881 $fmax = $fmid;
5882 }
5883 --$maxit;
5884 }
5885 $this->SetFontSize($this->FontSizePt);
5886 }
5887 if ($text_height < $maxh) {
5888 if ($valign == 'M') {
5889 // text vertically centered
5890 $this->y += (($maxh - $text_height) / 2);
5891 } elseif ($valign == 'B') {
5892 // text vertically aligned on bottom
5893 $this->y += ($maxh - $text_height);
5894 }
5895 }
5896 }
5897 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
5898 if ($fitcell) {
5899 // restore font size
5900 $this->SetFontSize($prev_FontSizePt);
5901 }
5902 }
5903 if ($autopadding) {
5904 // add bottom padding
5905 $this->y += $mc_padding['B'];
5906 }
5907 // Get end-of-text Y position
5908 $currentY = $this->y;
5909 // get latest page number
5910 $endpage = $this->page;
5911 if ($resth > 0) {
5912 $skip = ($endpage - $startpage);
5913 $tmpresth = $resth;
5914 while ($tmpresth > 0) {
5915 if ($skip <= 0) {
5916 // add a page (or trig AcceptPageBreak() for multicolumn mode)
5917 $this->checkPageBreak($this->PageBreakTrigger + 1);
5918 }
5919 if ($this->num_columns > 1) {
5920 $tmpresth -= ($this->h - $this->y - $this->bMargin);
5921 } else {
5922 $tmpresth -= ($this->h - $this->tMargin - $this->bMargin);
5923 }
5924 --$skip;
5925 }
5926 $currentY = $this->y;
5927 $endpage = $this->page;
5928 }
5929 // get latest column
5930 $endcolumn = $this->current_column;
5931 if ($this->num_columns == 0) {
5932 $this->num_columns = 1;
5933 }
5934 // disable page regions check
5936 $this->check_page_regions = false;
5937 // get border modes
5938 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
5939 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
5940 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
5941 // design borders around HTML cells.
5942 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
5943 $ccode = '';
5944 $this->setPage($page);
5945 if ($this->num_columns < 2) {
5946 // single-column mode
5947 $this->SetX($x);
5948 $this->y = $this->tMargin;
5949 }
5950 // account for margin changes
5951 if ($page > $startpage) {
5952 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
5953 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
5954 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
5955 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
5956 }
5957 }
5958 if ($startpage == $endpage) {
5959 // single page
5960 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
5961 $this->selectColumn($column);
5962 if ($this->rtl) {
5963 $this->x -= $mc_margin['R'];
5964 } else {
5965 $this->x += $mc_margin['L'];
5966 }
5967 if ($startcolumn == $endcolumn) { // single column
5968 $cborder = $border;
5969 $h = max($h, ($currentY - $oy));
5970 $this->y = $oy;
5971 } elseif ($column == $startcolumn) { // first column
5972 $cborder = $border_start;
5973 $this->y = $oy;
5974 $h = $this->h - $this->y - $this->bMargin;
5975 } elseif ($column == $endcolumn) { // end column
5976 $cborder = $border_end;
5977 $h = $currentY - $this->y;
5978 if ($resth > $h) {
5979 $h = $resth;
5980 }
5981 } else { // middle column
5982 $cborder = $border_middle;
5983 $h = $this->h - $this->y - $this->bMargin;
5984 $resth -= $h;
5985 }
5986 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
5987 } // end for each column
5988 } elseif ($page == $startpage) { // first page
5989 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
5990 $this->selectColumn($column);
5991 if ($this->rtl) {
5992 $this->x -= $mc_margin['R'];
5993 } else {
5994 $this->x += $mc_margin['L'];
5995 }
5996 if ($column == $startcolumn) { // first column
5997 $cborder = $border_start;
5998 $this->y = $oy;
5999 $h = $this->h - $this->y - $this->bMargin;
6000 } else { // middle column
6001 $cborder = $border_middle;
6002 $h = $this->h - $this->y - $this->bMargin;
6003 $resth -= $h;
6004 }
6005 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6006 } // end for each column
6007 } elseif ($page == $endpage) { // last page
6008 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
6009 $this->selectColumn($column);
6010 if ($this->rtl) {
6011 $this->x -= $mc_margin['R'];
6012 } else {
6013 $this->x += $mc_margin['L'];
6014 }
6015 if ($column == $endcolumn) {
6016 // end column
6017 $cborder = $border_end;
6018 $h = $currentY - $this->y;
6019 if ($resth > $h) {
6020 $h = $resth;
6021 }
6022 } else {
6023 // middle column
6024 $cborder = $border_middle;
6025 $h = $this->h - $this->y - $this->bMargin;
6026 $resth -= $h;
6027 }
6028 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6029 } // end for each column
6030 } else { // middle page
6031 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
6032 $this->selectColumn($column);
6033 if ($this->rtl) {
6034 $this->x -= $mc_margin['R'];
6035 } else {
6036 $this->x += $mc_margin['L'];
6037 }
6038 $cborder = $border_middle;
6039 $h = $this->h - $this->y - $this->bMargin;
6040 $resth -= $h;
6041 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6042 } // end for each column
6043 }
6044 if ($cborder OR $fill) {
6045 $offsetlen = strlen($ccode);
6046 // draw border and fill
6047 if ($this->inxobj) {
6048 // we are inside an XObject template
6049 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
6050 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
6051 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
6052 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
6053 } else {
6054 $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
6055 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
6056 }
6057 $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
6058 $pstart = substr($pagebuff, 0, $pagemark);
6059 $pend = substr($pagebuff, $pagemark);
6060 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
6061 } else {
6062 if (end($this->transfmrk[$this->page]) !== false) {
6063 $pagemarkkey = key($this->transfmrk[$this->page]);
6064 $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
6065 $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
6066 } elseif ($this->InFooter) {
6067 $pagemark = $this->footerpos[$this->page];
6068 $this->footerpos[$this->page] += $offsetlen;
6069 } else {
6070 $pagemark = $this->intmrk[$this->page];
6071 $this->intmrk[$this->page] += $offsetlen;
6072 }
6073 $pagebuff = $this->getPageBuffer($this->page);
6074 $pstart = substr($pagebuff, 0, $pagemark);
6075 $pend = substr($pagebuff, $pagemark);
6076 $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
6077 }
6078 }
6079 } // end for each page
6080 // restore page regions check
6081 $this->check_page_regions = $check_page_regions;
6082 // Get end-of-cell Y position
6083 $currentY = $this->GetY();
6084 // restore previous values
6085 if ($this->num_columns > 1) {
6086 $this->selectColumn();
6087 } else {
6088 // restore original margins
6089 $this->lMargin = $lMargin;
6090 $this->rMargin = $rMargin;
6091 if ($this->page > $startpage) {
6092 // check for margin variations between pages (i.e. booklet mode)
6093 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']);
6094 $dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']);
6095 if (($dl != 0) OR ($dr != 0)) {
6096 $this->lMargin += $dl;
6097 $this->rMargin += $dr;
6098 }
6099 }
6100 }
6101 if ($ln > 0) {
6102 //Go to the beginning of the next line
6103 $this->SetY($currentY + $mc_margin['B']);
6104 if ($ln == 2) {
6105 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6106 }
6107 } else {
6108 // go left or right by case
6109 $this->setPage($startpage);
6110 $this->y = $y;
6111 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6112 }
6113 $this->setContentMark();
6114 $this->cell_padding = $prev_cell_padding;
6115 $this->cell_margin = $prev_cell_margin;
6116 $this->clMargin = $this->lMargin;
6117 $this->crMargin = $this->rMargin;
6118 return $nl;
6119 }
6120
6134 public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6135 if ($txt === '') {
6136 // empty string
6137 return 1;
6138 }
6139 // adjust internal padding
6140 $prev_cell_padding = $this->cell_padding;
6141 $prev_lasth = $this->lasth;
6142 if (is_array($cellpadding)) {
6143 $this->cell_padding = $cellpadding;
6144 }
6145 $this->adjustCellPadding($border);
6146 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
6147 if ($this->rtl) {
6148 $w = $this->x - $this->lMargin;
6149 } else {
6150 $w = $this->w - $this->rMargin - $this->x;
6151 }
6152 }
6153 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6154 if ($reseth) {
6155 // reset row height
6156 $this->resetLastH();
6157 }
6158 $lines = 1;
6159 $sum = 0;
6160 $chars = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont), $txt, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6161 $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
6162 $length = count($chars);
6163 $lastSeparator = -1;
6164 for ($i = 0; $i < $length; ++$i) {
6165 $charWidth = $charsWidth[$i];
6166 if (preg_match($this->re_spaces, TCPDF_FONTS::unichr($chars[$i], $this->isunicode))) {
6167 $lastSeparator = $i;
6168 }
6169 if ((($sum + $charWidth) > $wmax) OR ($chars[$i] == 10)) {
6170 ++$lines;
6171 if ($chars[$i] == 10) {
6172 $lastSeparator = -1;
6173 $sum = 0;
6174 } elseif ($lastSeparator != -1) {
6175 $i = $lastSeparator;
6176 $lastSeparator = -1;
6177 $sum = 0;
6178 } else {
6179 $sum = $charWidth;
6180 }
6181 } else {
6182 $sum += $charWidth;
6183 }
6184 }
6185 if ($chars[($length - 1)] == 10) {
6186 --$lines;
6187 }
6188 $this->cell_padding = $prev_cell_padding;
6189 $this->lasth = $prev_lasth;
6190 return $lines;
6191 }
6192
6240 public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6241 // adjust internal padding
6242 $prev_cell_padding = $this->cell_padding;
6243 $prev_lasth = $this->lasth;
6244 if (is_array($cellpadding)) {
6245 $this->cell_padding = $cellpadding;
6246 }
6247 $this->adjustCellPadding($border);
6248 $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
6249 $height = $lines * ($this->FontSize * $this->cell_height_ratio);
6250 if ($autopadding) {
6251 // add top and bottom padding
6252 $height += ($this->cell_padding['T'] + $this->cell_padding['B']);
6253 }
6254 $this->cell_padding = $prev_cell_padding;
6255 $this->lasth = $prev_lasth;
6256 return $height;
6257 }
6258
6277 public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
6278 // check page for no-write regions and adapt page margins if necessary
6279 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
6280 if (strlen($txt) == 0) {
6281 // fix empty text
6282 $txt = ' ';
6283 }
6284 if ($margin === '') {
6285 // set default margins
6286 $margin = $this->cell_margin;
6287 }
6288 // remove carriage returns
6289 $s = str_replace("\r", '', $txt);
6290 // check if string contains arabic text
6291 if (preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_ARABIC, $s)) {
6292 $arabic = true;
6293 } else {
6294 $arabic = false;
6295 }
6296 // check if string contains RTL text
6297 if ($arabic OR ($this->tmprtl == 'R') OR preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $s)) {
6298 $rtlmode = true;
6299 } else {
6300 $rtlmode = false;
6301 }
6302 // get a char width
6303 $chrwidth = $this->GetCharWidth(46); // dot character
6304 // get array of unicode values
6305 $chars = TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont);
6306 // calculate maximum width for a single character on string
6307 $chrw = $this->GetArrStringWidth($chars, '', '', 0, true);
6308 array_walk($chrw, array($this, 'getRawCharWidth'));
6309 $maxchwidth = max($chrw);
6310 // get array of chars
6311 $uchars = TCPDF_FONTS::UTF8ArrayToUniArray($chars, $this->isunicode);
6312 // get the number of characters
6313 $nb = count($chars);
6314 // replacement for SHY character (minus symbol)
6315 $shy_replacement = 45;
6316 $shy_replacement_char = TCPDF_FONTS::unichr($shy_replacement, $this->isunicode);
6317 // widht for SHY replacement
6318 $shy_replacement_width = $this->GetCharWidth($shy_replacement);
6319 // max Y
6320 $maxy = $this->y + $maxh - $h - $this->cell_padding['T'] - $this->cell_padding['B'];
6321 // page width
6322 $pw = $w = $this->w - $this->lMargin - $this->rMargin;
6323 // calculate remaining line width ($w)
6324 if ($this->rtl) {
6325 $w = $this->x - $this->lMargin;
6326 } else {
6327 $w = $this->w - $this->rMargin - $this->x;
6328 }
6329 // max column width
6330 $wmax = ($w - $wadj);
6331 if (!$firstline) {
6332 $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']);
6333 }
6334 if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) {
6335 // the maximum width character do not fit on column
6336 return '';
6337 }
6338 // minimum row height
6339 $row_height = max($h, $this->FontSize * $this->cell_height_ratio);
6340 $start_page = $this->page;
6341 $i = 0; // character position
6342 $j = 0; // current starting position
6343 $sep = -1; // position of the last blank space
6344 $shy = false; // true if the last blank is a soft hypen (SHY)
6345 $l = 0; // current string length
6346 $nl = 0; //number of lines
6347 $linebreak = false;
6348 $pc = 0; // previous character
6349 // for each character
6350 while ($i < $nb) {
6351 if (($maxh > 0) AND ($this->y >= $maxy) ) {
6352 break;
6353 }
6354 //Get the current character
6355 $c = $chars[$i];
6356 if ($c == 10) { // 10 = "\n" = new line
6357 //Explicit line break
6358 if ($align == 'J') {
6359 if ($this->rtl) {
6360 $talign = 'R';
6361 } else {
6362 $talign = 'L';
6363 }
6364 } else {
6365 $talign = $align;
6366 }
6367 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6368 if ($firstline) {
6369 $startx = $this->x;
6370 $tmparr = array_slice($chars, $j, ($i - $j));
6371 if ($rtlmode) {
6372 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6373 }
6374 $linew = $this->GetArrStringWidth($tmparr);
6375 unset($tmparr);
6376 if ($this->rtl) {
6377 $this->endlinex = $startx - $linew;
6378 } else {
6379 $this->endlinex = $startx + $linew;
6380 }
6381 $w = $linew;
6382 $tmpcellpadding = $this->cell_padding;
6383 if ($maxh == 0) {
6384 $this->SetCellPadding(0);
6385 }
6386 }
6387 if ($firstblock AND $this->isRTLTextDir()) {
6388 $tmpstr = $this->stringRightTrim($tmpstr);
6389 }
6390 // Skip newlines at the begining of a page or column
6391 if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) {
6392 $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
6393 }
6394 unset($tmpstr);
6395 if ($firstline) {
6396 $this->cell_padding = $tmpcellpadding;
6397 return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6398 }
6399 ++$nl;
6400 $j = $i + 1;
6401 $l = 0;
6402 $sep = -1;
6403 $shy = false;
6404 // account for margin changes
6405 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6406 $this->AcceptPageBreak();
6407 if ($this->rtl) {
6408 $this->x -= $margin['R'];
6409 } else {
6410 $this->x += $margin['L'];
6411 }
6412 $this->lMargin += $margin['L'];
6413 $this->rMargin += $margin['R'];
6414 }
6415 $w = $this->getRemainingWidth();
6416 $wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']);
6417 } else {
6418 // 160 is the non-breaking space.
6419 // 173 is SHY (Soft Hypen).
6420 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
6421 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
6422 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
6423 if (($c != 160)
6424 AND (($c == 173)
6425 OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6426 OR (($c == 45)
6427 AND ($i < ($nb - 1))
6428 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($pc, $this->isunicode))
6429 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6430 )
6431 )
6432 ) {
6433 // update last blank space position
6434 $sep = $i;
6435 // check if is a SHY
6436 if (($c == 173) OR ($c == 45)) {
6437 $shy = true;
6438 if ($pc == 45) {
6439 $tmp_shy_replacement_width = 0;
6440 $tmp_shy_replacement_char = '';
6441 } else {
6442 $tmp_shy_replacement_width = $shy_replacement_width;
6443 $tmp_shy_replacement_char = $shy_replacement_char;
6444 }
6445 } else {
6446 $shy = false;
6447 }
6448 }
6449 // update string length
6450 if ($this->isUnicodeFont() AND ($arabic)) {
6451 // with bidirectional algorithm some chars may be changed affecting the line length
6452 // *** very slow ***
6453 $l = $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl, $this->isunicode, $this->CurrentFont));
6454 } else {
6455 $l += $this->GetCharWidth($c);
6456 }
6457 if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) ) {
6458 // we have reached the end of column
6459 if ($sep == -1) {
6460 // check if the line was already started
6461 if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth)))
6462 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) {
6463 // print a void cell and go to next line
6464 $this->Cell($w, $h, '', 0, 1);
6465 $linebreak = true;
6466 if ($firstline) {
6467 return (TCPDF_FONTS::UniArrSubString($uchars, $j));
6468 }
6469 } else {
6470 // truncate the word because do not fit on column
6471 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6472 if ($firstline) {
6473 $startx = $this->x;
6474 $tmparr = array_slice($chars, $j, ($i - $j));
6475 if ($rtlmode) {
6476 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6477 }
6478 $linew = $this->GetArrStringWidth($tmparr);
6479 unset($tmparr);
6480 if ($this->rtl) {
6481 $this->endlinex = $startx - $linew;
6482 } else {
6483 $this->endlinex = $startx + $linew;
6484 }
6485 $w = $linew;
6486 $tmpcellpadding = $this->cell_padding;
6487 if ($maxh == 0) {
6488 $this->SetCellPadding(0);
6489 }
6490 }
6491 if ($firstblock AND $this->isRTLTextDir()) {
6492 $tmpstr = $this->stringRightTrim($tmpstr);
6493 }
6494 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6495 unset($tmpstr);
6496 if ($firstline) {
6497 $this->cell_padding = $tmpcellpadding;
6498 return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6499 }
6500 $j = $i;
6501 --$i;
6502 }
6503 } else {
6504 // word wrapping
6505 if ($this->rtl AND (!$firstblock) AND ($sep < $i)) {
6506 $endspace = 1;
6507 } else {
6508 $endspace = 0;
6509 }
6510 // check the length of the next string
6511 $strrest = TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace));
6512 $nextstr = preg_split('/'.$this->re_space['p'].'/'.$this->re_space['m'], $this->stringTrim($strrest));
6513 if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
6514 // truncate the word because do not fit on a full page width
6515 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6516 if ($firstline) {
6517 $startx = $this->x;
6518 $tmparr = array_slice($chars, $j, ($i - $j));
6519 if ($rtlmode) {
6520 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6521 }
6522 $linew = $this->GetArrStringWidth($tmparr);
6523 unset($tmparr);
6524 if ($this->rtl) {
6525 $this->endlinex = ($startx - $linew);
6526 } else {
6527 $this->endlinex = ($startx + $linew);
6528 }
6529 $w = $linew;
6530 $tmpcellpadding = $this->cell_padding;
6531 if ($maxh == 0) {
6532 $this->SetCellPadding(0);
6533 }
6534 }
6535 if ($firstblock AND $this->isRTLTextDir()) {
6536 $tmpstr = $this->stringRightTrim($tmpstr);
6537 }
6538 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6539 unset($tmpstr);
6540 if ($firstline) {
6541 $this->cell_padding = $tmpcellpadding;
6542 return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6543 }
6544 $j = $i;
6545 --$i;
6546 } else {
6547 // word wrapping
6548 if ($shy) {
6549 // add hypen (minus symbol) at the end of the line
6550 $shy_width = $tmp_shy_replacement_width;
6551 if ($this->rtl) {
6552 $shy_char_left = $tmp_shy_replacement_char;
6553 $shy_char_right = '';
6554 } else {
6555 $shy_char_left = '';
6556 $shy_char_right = $tmp_shy_replacement_char;
6557 }
6558 } else {
6559 $shy_width = 0;
6560 $shy_char_left = '';
6561 $shy_char_right = '';
6562 }
6563 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, ($sep + $endspace));
6564 if ($firstline) {
6565 $startx = $this->x;
6566 $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
6567 if ($rtlmode) {
6568 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6569 }
6570 $linew = $this->GetArrStringWidth($tmparr);
6571 unset($tmparr);
6572 if ($this->rtl) {
6573 $this->endlinex = $startx - $linew - $shy_width;
6574 } else {
6575 $this->endlinex = $startx + $linew + $shy_width;
6576 }
6577 $w = $linew;
6578 $tmpcellpadding = $this->cell_padding;
6579 if ($maxh == 0) {
6580 $this->SetCellPadding(0);
6581 }
6582 }
6583 // print the line
6584 if ($firstblock AND $this->isRTLTextDir()) {
6585 $tmpstr = $this->stringRightTrim($tmpstr);
6586 }
6587 $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
6588 unset($tmpstr);
6589 if ($firstline) {
6590 if ($chars[$sep] == 45) {
6591 $endspace += 1;
6592 }
6593 // return the remaining text
6594 $this->cell_padding = $tmpcellpadding;
6595 return (TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace)));
6596 }
6597 $i = $sep;
6598 $sep = -1;
6599 $shy = false;
6600 $j = ($i + 1);
6601 }
6602 }
6603 // account for margin changes
6604 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6605 $this->AcceptPageBreak();
6606 if ($this->rtl) {
6607 $this->x -= $margin['R'];
6608 } else {
6609 $this->x += $margin['L'];
6610 }
6611 $this->lMargin += $margin['L'];
6612 $this->rMargin += $margin['R'];
6613 }
6614 $w = $this->getRemainingWidth();
6615 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6616 if ($linebreak) {
6617 $linebreak = false;
6618 } else {
6619 ++$nl;
6620 $l = 0;
6621 }
6622 }
6623 }
6624 // save last character
6625 $pc = $c;
6626 ++$i;
6627 } // end while i < nb
6628 // print last substring (if any)
6629 if ($l > 0) {
6630 switch ($align) {
6631 case 'J':
6632 case 'C': {
6633 $w = $w;
6634 break;
6635 }
6636 case 'L': {
6637 if ($this->rtl) {
6638 $w = $w;
6639 } else {
6640 $w = $l;
6641 }
6642 break;
6643 }
6644 case 'R': {
6645 if ($this->rtl) {
6646 $w = $l;
6647 } else {
6648 $w = $w;
6649 }
6650 break;
6651 }
6652 default: {
6653 $w = $l;
6654 break;
6655 }
6656 }
6657 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $nb);
6658 if ($firstline) {
6659 $startx = $this->x;
6660 $tmparr = array_slice($chars, $j, ($nb - $j));
6661 if ($rtlmode) {
6662 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6663 }
6664 $linew = $this->GetArrStringWidth($tmparr);
6665 unset($tmparr);
6666 if ($this->rtl) {
6667 $this->endlinex = $startx - $linew;
6668 } else {
6669 $this->endlinex = $startx + $linew;
6670 }
6671 $w = $linew;
6672 $tmpcellpadding = $this->cell_padding;
6673 if ($maxh == 0) {
6674 $this->SetCellPadding(0);
6675 }
6676 }
6677 if ($firstblock AND $this->isRTLTextDir()) {
6678 $tmpstr = $this->stringRightTrim($tmpstr);
6679 }
6680 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
6681 unset($tmpstr);
6682 if ($firstline) {
6683 $this->cell_padding = $tmpcellpadding;
6684 return (TCPDF_FONTS::UniArrSubString($uchars, $nb));
6685 }
6686 ++$nl;
6687 }
6688 if ($firstline) {
6689 return '';
6690 }
6691 return $nl;
6692 }
6693
6699 protected function getRemainingWidth() {
6700 list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y);
6701 if ($this->rtl) {
6702 return ($this->x - $this->lMargin);
6703 } else {
6704 return ($this->w - $this->rMargin - $this->x);
6705 }
6706 }
6707
6719 protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
6720 if ($w <= 0) {
6721 // set maximum width
6722 $w = ($this->w - $this->lMargin - $this->rMargin);
6723 }
6724 if ($h <= 0) {
6725 // set maximum height
6726 $h = ($this->PageBreakTrigger - $this->tMargin);
6727 }
6728 // resize the block to be vertically contained on a single page or single column
6729 if ($fitonpage OR $this->AutoPageBreak) {
6730 $ratio_wh = ($w / $h);
6731 if ($h > ($this->PageBreakTrigger - $this->tMargin)) {
6732 $h = $this->PageBreakTrigger - $this->tMargin;
6733 $w = ($h * $ratio_wh);
6734 }
6735 // resize the block to be horizontally contained on a single page or single column
6736 if ($fitonpage) {
6737 $maxw = ($this->w - $this->lMargin - $this->rMargin);
6738 if ($w > $maxw) {
6739 $w = $maxw;
6740 $h = ($w / $ratio_wh);
6741 }
6742 }
6743 }
6744 // Check whether we need a new page or new column first as this does not fit
6745 $prev_x = $this->x;
6746 $prev_y = $this->y;
6747 if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) {
6748 $y = $this->y;
6749 if ($this->rtl) {
6750 $x += ($prev_x - $this->x);
6751 } else {
6752 $x += ($this->x - $prev_x);
6753 }
6754 $this->newline = true;
6755 }
6756 // resize the block to be contained on the remaining available page or column space
6757 if ($fitonpage) {
6758 $ratio_wh = ($w / $h);
6759 if (($y + $h) > $this->PageBreakTrigger) {
6760 $h = $this->PageBreakTrigger - $y;
6761 $w = ($h * $ratio_wh);
6762 }
6763 if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
6764 $w = $this->w - $this->rMargin - $x;
6765 $h = ($w / $ratio_wh);
6766 } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
6767 $w = $x - $this->lMargin;
6768 $h = ($w / $ratio_wh);
6769 }
6770 }
6771 return array($w, $h, $x, $y);
6772 }
6773
6808 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()) {
6809 if ($this->state != 2) {
6810 return;
6811 }
6812 if ($x === '') {
6813 $x = $this->x;
6814 }
6815 if ($y === '') {
6816 $y = $this->y;
6817 }
6818 // check page for no-write regions and adapt page margins if necessary
6819 list($x, $y) = $this->checkPageRegions($h, $x, $y);
6820 $exurl = ''; // external streams
6821 // check if we are passing an image as file or string
6822 if ($file[0] === '@') {
6823 // image from string
6824 $imgdata = substr($file, 1);
6825 } else { // image file
6826 if ($file{0} === '*') {
6827 // image as external stream
6828 $file = substr($file, 1);
6829 $exurl = $file;
6830 }
6831 // check if is local file
6832 if (!@file_exists($file)) {
6833 // encode spaces on filename (file is probably an URL)
6834 $file = str_replace(' ', '%20', $file);
6835 }
6836 if (@file_exists($file)) {
6837 // get image dimensions
6838 $imsize = @getimagesize($file);
6839 } else {
6840 $imsize = FALSE;
6841 }
6842 if ($imsize === FALSE) {
6843 if (function_exists('curl_init')) {
6844 // try to get remote file data using cURL
6845 $cs = curl_init(); // curl session
6846 curl_setopt($cs, CURLOPT_URL, $file);
6847 curl_setopt($cs, CURLOPT_BINARYTRANSFER, true);
6848 curl_setopt($cs, CURLOPT_FAILONERROR, true);
6849 curl_setopt($cs, CURLOPT_RETURNTRANSFER, true);
6850 if ((!ini_get('safe_mode'))) {
6851 curl_setopt($cs, CURLOPT_FOLLOWLOCATION, true);
6852 }
6853 curl_setopt($cs, CURLOPT_CONNECTTIMEOUT, 5);
6854 curl_setopt($cs, CURLOPT_TIMEOUT, 30);
6855 curl_setopt($cs, CURLOPT_SSL_VERIFYPEER, false);
6856 curl_setopt($cs, CURLOPT_SSL_VERIFYHOST, false);
6857 curl_setopt($cs, CURLOPT_USERAGENT, 'TCPDF');
6858 $imgdata = curl_exec($cs);
6859 curl_close($cs);
6860 } else {
6861 $imgdata = @file_get_contents($file);
6862 }
6863 }
6864 }
6865 if (isset($imgdata) AND ($imgdata !== FALSE)) {
6866 // copy image to cache
6868 $fp = fopen($file, 'w');
6869 fwrite($fp, $imgdata);
6870 fclose($fp);
6871 unset($imgdata);
6872 $imsize = @getimagesize($file);
6873 if ($imsize === FALSE) {
6874 unlink($file);
6875 } else {
6876 $this->cached_files[] = $file;
6877 }
6878 }
6879 if ($imsize === FALSE) {
6880 if (($w > 0) AND ($h > 0)) {
6881 // get measures from specified data
6882 $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6883 $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6884 $imsize = array($pw, $ph);
6885 } else {
6886 $this->Error('[Image] Unable to get image: '.$file);
6887 }
6888 }
6889 // file hash
6890 $filehash = md5($this->file_id.$file);
6891 // get original image width and height in pixels
6892 list($pixw, $pixh) = $imsize;
6893 // calculate image width and height on document
6894 if (($w <= 0) AND ($h <= 0)) {
6895 // convert image size to document unit
6896 $w = $this->pixelsToUnits($pixw);
6897 $h = $this->pixelsToUnits($pixh);
6898 } elseif ($w <= 0) {
6899 $w = $h * $pixw / $pixh;
6900 } elseif ($h <= 0) {
6901 $h = $w * $pixh / $pixw;
6902 } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
6903 if (strlen($fitbox) !== 2) {
6904 // set default alignment
6905 $fitbox = '--';
6906 }
6907 // scale image dimensions proportionally to fit within the ($w, $h) box
6908 if ((($w * $pixh) / ($h * $pixw)) < 1) {
6909 // store current height
6910 $oldh = $h;
6911 // calculate new height
6912 $h = $w * $pixh / $pixw;
6913 // height difference
6914 $hdiff = ($oldh - $h);
6915 // vertical alignment
6916 switch (strtoupper($fitbox{1})) {
6917 case 'T': {
6918 break;
6919 }
6920 case 'M': {
6921 $y += ($hdiff / 2);
6922 break;
6923 }
6924 case 'B': {
6925 $y += $hdiff;
6926 break;
6927 }
6928 }
6929 } else {
6930 // store current width
6931 $oldw = $w;
6932 // calculate new width
6933 $w = $h * $pixw / $pixh;
6934 // width difference
6935 $wdiff = ($oldw - $w);
6936 // horizontal alignment
6937 switch (strtoupper($fitbox{0})) {
6938 case 'L': {
6939 if ($this->rtl) {
6940 $x -= $wdiff;
6941 }
6942 break;
6943 }
6944 case 'C': {
6945 if ($this->rtl) {
6946 $x -= ($wdiff / 2);
6947 } else {
6948 $x += ($wdiff / 2);
6949 }
6950 break;
6951 }
6952 case 'R': {
6953 if (!$this->rtl) {
6954 $x += $wdiff;
6955 }
6956 break;
6957 }
6958 }
6959 }
6960 }
6961 // fit the image on available space
6962 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
6963 // calculate new minimum dimensions in pixels
6964 $neww = round($w * $this->k * $dpi / $this->dpi);
6965 $newh = round($h * $this->k * $dpi / $this->dpi);
6966 // check if resize is necessary (resize is used only to reduce the image)
6967 $newsize = ($neww * $newh);
6968 $pixsize = ($pixw * $pixh);
6969 if (intval($resize) == 2) {
6970 $resize = true;
6971 } elseif ($newsize >= $pixsize) {
6972 $resize = false;
6973 }
6974 // check if image has been already added on document
6975 $newimage = true;
6976 if (in_array($file, $this->imagekeys)) {
6977 $newimage = false;
6978 // get existing image data
6979 $info = $this->getImageBuffer($file);
6980 if (substr($file, 0, -34) != K_PATH_CACHE.'msk') {
6981 // check if the newer image is larger
6982 $oldsize = ($info['w'] * $info['h']);
6983 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
6984 $newimage = true;
6985 }
6986 }
6987 } elseif (substr($file, 0, -34) != K_PATH_CACHE.'msk') {
6988 // check for cached images with alpha channel
6989 $tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash;
6990 $tempfile_alpha = K_PATH_CACHE.'mska_'.$filehash;
6991 if (in_array($tempfile_plain, $this->imagekeys)) {
6992 // get existing image data
6993 $info = $this->getImageBuffer($tempfile_plain);
6994 // check if the newer image is larger
6995 $oldsize = ($info['w'] * $info['h']);
6996 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
6997 $newimage = true;
6998 } else {
6999 $newimage = false;
7000 // embed mask image
7001 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7002 // embed image, masked with previously embedded mask
7003 return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7004 }
7005 }
7006 }
7007 if ($newimage) {
7008 //First use of image, get info
7009 $type = strtolower($type);
7010 if ($type == '') {
7011 $type = TCPDF_IMAGES::getImageFileType($file, $imsize);
7012 } elseif ($type == 'jpg') {
7013 $type = 'jpeg';
7014 }
7015 $mqr = TCPDF_STATIC::get_mqr();
7016 TCPDF_STATIC::set_mqr(false);
7017 // Specific image handlers (defined on TCPDF_IMAGES CLASS)
7018 $mtd = '_parse'.$type;
7019 // GD image handler function
7020 $gdfunction = 'imagecreatefrom'.$type;
7021 $info = false;
7022 if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
7023 // TCPDF image functions
7024 $info = TCPDF_IMAGES::$mtd($file);
7025 if ($info == 'pngalpha') {
7026 return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
7027 }
7028 }
7029 if (!$info) {
7030 if (function_exists($gdfunction)) {
7031 // GD library
7032 $img = $gdfunction($file);
7033 if ($resize) {
7034 $imgr = imagecreatetruecolor($neww, $newh);
7035 if (($type == 'gif') OR ($type == 'png')) {
7037 }
7038 imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
7039 if (($type == 'gif') OR ($type == 'png')) {
7040 $info = TCPDF_IMAGES::_toPNG($imgr);
7041 } else {
7042 $info = TCPDF_IMAGES::_toJPEG($imgr, $this->jpeg_quality);
7043 }
7044 } else {
7045 if (($type == 'gif') OR ($type == 'png')) {
7046 $info = TCPDF_IMAGES::_toPNG($img);
7047 } else {
7048 $info = TCPDF_IMAGES::_toJPEG($img, $this->jpeg_quality);
7049 }
7050 }
7051 } elseif (extension_loaded('imagick')) {
7052 // ImageMagick library
7053 $img = new Imagick();
7054 if ($type == 'SVG') {
7055 // get SVG file content
7056 $svgimg = file_get_contents($file);
7057 // get width and height
7058 $regs = array();
7059 if (preg_match('/<svg([^>]*)>/si', $svgimg, $regs)) {
7060 $svgtag = $regs[1];
7061 $tmp = array();
7062 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7063 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7064 $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit;
7065 $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
7066 } else {
7067 $ow = $w;
7068 }
7069 $tmp = array();
7070 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7071 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7072 $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit;
7073 $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
7074 } else {
7075 $oh = $h;
7076 }
7077 $tmp = array();
7078 if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
7079 $vbw = ($ow * $this->imgscale * $this->k);
7080 $vbh = ($oh * $this->imgscale * $this->k);
7081 $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh);
7082 $svgtag = $vbox.$svgtag;
7083 }
7084 $svgimg = preg_replace('/<svg([^>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
7085 }
7086 $img->readImageBlob($svgimg);
7087 } else {
7088 $img->readImage($file);
7089 }
7090 if ($resize) {
7091 $img->resizeImage($neww, $newh, 10, 1, false);
7092 }
7093 $img->setCompressionQuality($this->jpeg_quality);
7094 $img->setImageFormat('jpeg');
7095 $tempname = TCPDF_STATIC::getObjFilename('jpg');
7096 $img->writeImage($tempname);
7097 $info = TCPDF_IMAGES::_parsejpeg($tempname);
7098 unlink($tempname);
7099 $img->destroy();
7100 } else {
7101 return;
7102 }
7103 }
7104 if ($info === false) {
7105 //If false, we cannot process image
7106 return;
7107 }
7109 if ($ismask) {
7110 // force grayscale
7111 $info['cs'] = 'DeviceGray';
7112 }
7113 if ($imgmask !== false) {
7114 $info['masked'] = $imgmask;
7115 }
7116 if (!empty($exurl)) {
7117 $info['exurl'] = $exurl;
7118 }
7119 // array of alternative images
7120 $info['altimgs'] = $altimgs;
7121 // add image to document
7122 $info['i'] = $this->setImageBuffer($file, $info);
7123 }
7124 // set alignment
7125 $this->img_rb_y = $y + $h;
7126 // set alignment
7127 if ($this->rtl) {
7128 if ($palign == 'L') {
7129 $ximg = $this->lMargin;
7130 } elseif ($palign == 'C') {
7131 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7132 } elseif ($palign == 'R') {
7133 $ximg = $this->w - $this->rMargin - $w;
7134 } else {
7135 $ximg = $x - $w;
7136 }
7137 $this->img_rb_x = $ximg;
7138 } else {
7139 if ($palign == 'L') {
7140 $ximg = $this->lMargin;
7141 } elseif ($palign == 'C') {
7142 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7143 } elseif ($palign == 'R') {
7144 $ximg = $this->w - $this->rMargin - $w;
7145 } else {
7146 $ximg = $x;
7147 }
7148 $this->img_rb_x = $ximg + $w;
7149 }
7150 if ($ismask OR $hidden) {
7151 // image is not displayed
7152 return $info['i'];
7153 }
7154 $xkimg = $ximg * $this->k;
7155 if (!$alt) {
7156 // only non-alternative immages will be set
7157 $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']));
7158 }
7159 if (!empty($border)) {
7160 $bx = $this->x;
7161 $by = $this->y;
7162 $this->x = $ximg;
7163 if ($this->rtl) {
7164 $this->x += $w;
7165 }
7166 $this->y = $y;
7167 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
7168 $this->x = $bx;
7169 $this->y = $by;
7170 }
7171 if ($link) {
7172 $this->Link($ximg, $y, $w, $h, $link, 0);
7173 }
7174 // set pointer to align the next text/objects
7175 switch($align) {
7176 case 'T': {
7177 $this->y = $y;
7178 $this->x = $this->img_rb_x;
7179 break;
7180 }
7181 case 'M': {
7182 $this->y = $y + round($h/2);
7183 $this->x = $this->img_rb_x;
7184 break;
7185 }
7186 case 'B': {
7187 $this->y = $this->img_rb_y;
7188 $this->x = $this->img_rb_x;
7189 break;
7190 }
7191 case 'N': {
7192 $this->SetY($this->img_rb_y);
7193 break;
7194 }
7195 default:{
7196 break;
7197 }
7198 }
7199 $this->endlinex = $this->img_rb_x;
7200 if ($this->inxobj) {
7201 // we are inside an XObject template
7202 $this->xobjects[$this->xobjid]['images'][] = $info['i'];
7203 }
7204 return $info['i'];
7205 }
7206
7228 protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
7229 if (empty($filehash)) {
7230 $filehash = md5($this->file_id.$file);
7231 }
7232 // create temp image file (without alpha channel)
7233 $tempfile_plain = K_PATH_CACHE.'mskp_'.$filehash;
7234 // create temp alpha file
7235 $tempfile_alpha = K_PATH_CACHE.'mska_'.$filehash;
7236 if (extension_loaded('imagick')) { // ImageMagick extension
7237 // ImageMagick library
7238 $img = new Imagick();
7239 $img->readImage($file);
7240 // clone image object
7242 // extract alpha channel
7243 if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) {
7244 $img->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT);
7245 } else {
7246 $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
7247 $img->negateImage(true);
7248 }
7249 $img->setImageFormat('png');
7250 $img->writeImage($tempfile_alpha);
7251 // remove alpha channel
7252 if (method_exists($imga, 'setImageMatte')) {
7253 $imga->setImageMatte(false);
7254 } else {
7255 $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
7256 }
7257 $imga->setImageFormat('png');
7258 $imga->writeImage($tempfile_plain);
7259 } elseif (function_exists('imagecreatefrompng')) { // GD extension
7260 // generate images
7261 $img = imagecreatefrompng($file);
7262 $imgalpha = imagecreate($wpx, $hpx);
7263 // generate gray scale palette (0 -> 255)
7264 for ($c = 0; $c < 256; ++$c) {
7265 ImageColorAllocate($imgalpha, $c, $c, $c);
7266 }
7267 // extract alpha channel
7268 for ($xpx = 0; $xpx < $wpx; ++$xpx) {
7269 for ($ypx = 0; $ypx < $hpx; ++$ypx) {
7270 $color = imagecolorat($img, $xpx, $ypx);
7271 $alpha = $this->getGDgamma($color); // correct gamma
7272 imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
7273 }
7274 }
7275 imagepng($imgalpha, $tempfile_alpha);
7276 imagedestroy($imgalpha);
7277 // extract image without alpha channel
7278 $imgplain = imagecreatetruecolor($wpx, $hpx);
7279 imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
7280 imagepng($imgplain, $tempfile_plain);
7281 imagedestroy($imgplain);
7282 } else {
7283 $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
7284 }
7285 // embed mask image
7286 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7287 // embed image, masked with previously embedded mask
7288 $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7289 // remove temp files
7290 unlink($tempfile_alpha);
7291 unlink($tempfile_plain);
7292 }
7293
7300 protected function getGDgamma($c) {
7301 if (!isset($this->gdgammacache["'".$c."'"])) {
7302 // shifts off the first 24 bits (where 8x3 are used for each color),
7303 // and returns the remaining 7 allocated bits (commonly used for alpha)
7304 $alpha = ($c >> 24);
7305 // GD alpha is only 7 bit (0 -> 127)
7306 $alpha = (((127 - $alpha) / 127) * 255);
7307 // correct gamma
7308 $this->gdgammacache["'".$c."'"] = (pow(($alpha / 255), 2.2) * 255);
7309 // store the latest values on cache to improve performances
7310 if (count($this->gdgammacache) > 8) {
7311 // remove one element from the cache array
7312 array_shift($this->gdgammacache);
7313 }
7314 }
7315 return $this->gdgammacache["'".$c."'"];
7316 }
7317
7327 public function Ln($h='', $cell=false) {
7328 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'])) {
7329 // revove vertical space from the top of the column
7330 return;
7331 }
7332 if ($cell) {
7333 if ($this->rtl) {
7334 $cellpadding = $this->cell_padding['R'];
7335 } else {
7336 $cellpadding = $this->cell_padding['L'];
7337 }
7338 } else {
7339 $cellpadding = 0;
7340 }
7341 if ($this->rtl) {
7342 $this->x = $this->w - $this->rMargin - $cellpadding;
7343 } else {
7344 $this->x = $this->lMargin + $cellpadding;
7345 }
7346 if (is_string($h)) {
7347 $this->y += $this->lasth;
7348 } else {
7349 $this->y += $h;
7350 }
7351 $this->newline = true;
7352 }
7353
7362 public function GetX() {
7363 //Get x position
7364 if ($this->rtl) {
7365 return ($this->w - $this->x);
7366 } else {
7367 return $this->x;
7368 }
7369 }
7370
7378 public function GetAbsX() {
7379 return $this->x;
7380 }
7381
7389 public function GetY() {
7390 return $this->y;
7391 }
7392
7402 public function SetX($x, $rtloff=false) {
7403 $x = floatval($x);
7404 if (!$rtloff AND $this->rtl) {
7405 if ($x >= 0) {
7406 $this->x = $this->w - $x;
7407 } else {
7408 $this->x = abs($x);
7409 }
7410 } else {
7411 if ($x >= 0) {
7412 $this->x = $x;
7413 } else {
7414 $this->x = $this->w + $x;
7415 }
7416 }
7417 if ($this->x < 0) {
7418 $this->x = 0;
7419 }
7420 if ($this->x > $this->w) {
7421 $this->x = $this->w;
7422 }
7423 }
7424
7435 public function SetY($y, $resetx=true, $rtloff=false) {
7436 $y = floatval($y);
7437 if ($resetx) {
7438 //reset x
7439 if (!$rtloff AND $this->rtl) {
7440 $this->x = $this->w - $this->rMargin;
7441 } else {
7442 $this->x = $this->lMargin;
7443 }
7444 }
7445 if ($y >= 0) {
7446 $this->y = $y;
7447 } else {
7448 $this->y = $this->h + $y;
7449 }
7450 if ($this->y < 0) {
7451 $this->y = 0;
7452 }
7453 if ($this->y > $this->h) {
7454 $this->y = $this->h;
7455 }
7456 }
7457
7468 public function SetXY($x, $y, $rtloff=false) {
7469 $this->SetY($y, false, $rtloff);
7470 $this->SetX($x, $rtloff);
7471 }
7472
7480 public function SetAbsX($x) {
7481 $this->x = floatval($x);
7482 }
7483
7491 public function SetAbsY($y) {
7492 $this->y = floatval($y);
7493 }
7494
7503 public function SetAbsXY($x, $y) {
7504 $this->SetAbsX($x);
7505 $this->SetAbsY($y);
7506 }
7507
7518 public function Output($name='doc.pdf', $dest='I') {
7519 //Output PDF to some destination
7520 //Finish document if necessary
7521 if ($this->state < 3) {
7522 $this->Close();
7523 }
7524 //Normalize parameters
7525 if (is_bool($dest)) {
7526 $dest = $dest ? 'D' : 'F';
7527 }
7528 $dest = strtoupper($dest);
7529 if ($dest{0} != 'F') {
7530 $name = preg_replace('/[\s]+/', '_', $name);
7531 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
7532 }
7533 if ($this->sign) {
7534 // *** apply digital signature to the document ***
7535 // get the document content
7536 $pdfdoc = $this->getBuffer();
7537 // remove last newline
7538 $pdfdoc = substr($pdfdoc, 0, -1);
7539 // Remove the original buffer
7540 if (isset($this->diskcache) AND $this->diskcache) {
7541 // remove buffer file from cache
7542 unlink($this->buffer);
7543 }
7544 unset($this->buffer);
7545 // remove filler space
7546 $byterange_string_len = strlen(TCPDF_STATIC::$byterange_string);
7547 // define the ByteRange
7548 $byte_range = array();
7549 $byte_range[0] = 0;
7550 $byte_range[1] = strpos($pdfdoc, TCPDF_STATIC::$byterange_string) + $byterange_string_len + 10;
7551 $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
7552 $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
7553 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
7554 // replace the ByteRange
7555 $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
7556 $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
7557 $pdfdoc = str_replace(TCPDF_STATIC::$byterange_string, $byterange, $pdfdoc);
7558 // write the document to a temporary folder
7559 $tempdoc = TCPDF_STATIC::getObjFilename('tmppdf');
7560 $f = fopen($tempdoc, 'wb');
7561 if (!$f) {
7562 $this->Error('Unable to create temporary file: '.$tempdoc);
7563 }
7564 $pdfdoc_length = strlen($pdfdoc);
7565 fwrite($f, $pdfdoc, $pdfdoc_length);
7566 fclose($f);
7567 // get digital signature via openssl library
7568 $tempsign = TCPDF_STATIC::getObjFilename('tmpsig');
7569 if (empty($this->signature_data['extracerts'])) {
7570 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
7571 } else {
7572 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']);
7573 }
7574 unlink($tempdoc);
7575 // read signature
7576 $signature = file_get_contents($tempsign);
7577 unlink($tempsign);
7578 // extract signature
7579 $signature = substr($signature, $pdfdoc_length);
7580 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
7581 $tmparr = explode("\n\n", $signature);
7582 $signature = $tmparr[1];
7583 unset($tmparr);
7584 // decode signature
7585 $signature = base64_decode(trim($signature));
7586 // convert signature to hex
7587 $signature = current(unpack('H*', $signature));
7588 $signature = str_pad($signature, $this->signature_max_length, '0');
7589 // disable disk caching
7590 $this->diskcache = false;
7591 // Add signature to the document
7592 $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
7593 $this->bufferlen = strlen($this->buffer);
7594 }
7595 switch($dest) {
7596 case 'I': {
7597 // Send PDF to the standard output
7598 if (ob_get_contents()) {
7599 $this->Error('Some data has already been output, can\'t send PDF file');
7600 }
7601 if (php_sapi_name() != 'cli') {
7602 // send output to a browser
7603 header('Content-Type: application/pdf');
7604 if (headers_sent()) {
7605 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7606 }
7607 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7608 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7609 header('Pragma: public');
7610 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7611 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7612 header('Content-Disposition: inline; filename="'.basename($name).'"');
7613 TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7614 } else {
7615 echo $this->getBuffer();
7616 }
7617 break;
7618 }
7619 case 'D': {
7620 // download PDF as file
7621 if (ob_get_contents()) {
7622 $this->Error('Some data has already been output, can\'t send PDF file');
7623 }
7624 header('Content-Description: File Transfer');
7625 if (headers_sent()) {
7626 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7627 }
7628 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7629 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7630 header('Pragma: public');
7631 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7632 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7633 // force download dialog
7634 if (strpos(php_sapi_name(), 'cgi') === false) {
7635 header('Content-Type: application/force-download');
7636 header('Content-Type: application/octet-stream', false);
7637 header('Content-Type: application/download', false);
7638 header('Content-Type: application/pdf', false);
7639 } else {
7640 header('Content-Type: application/pdf');
7641 }
7642 // use the Content-Disposition header to supply a recommended filename
7643 header('Content-Disposition: attachment; filename="'.basename($name).'"');
7644 header('Content-Transfer-Encoding: binary');
7645 TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7646 break;
7647 }
7648 case 'F':
7649 case 'FI':
7650 case 'FD': {
7651 // save PDF to a local file
7652 if ($this->diskcache) {
7653 copy($this->buffer, $name);
7654 } else {
7655 $f = fopen($name, 'wb');
7656 if (!$f) {
7657 $this->Error('Unable to create output file: '.$name);
7658 }
7659 fwrite($f, $this->getBuffer(), $this->bufferlen);
7660 fclose($f);
7661 }
7662 if ($dest == 'FI') {
7663 // send headers to browser
7664 header('Content-Type: application/pdf');
7665 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7666 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7667 header('Pragma: public');
7668 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7669 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7670 header('Content-Disposition: inline; filename="'.basename($name).'"');
7671 TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7672 } elseif ($dest == 'FD') {
7673 // send headers to browser
7674 if (ob_get_contents()) {
7675 $this->Error('Some data has already been output, can\'t send PDF file');
7676 }
7677 header('Content-Description: File Transfer');
7678 if (headers_sent()) {
7679 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7680 }
7681 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7682 header('Pragma: public');
7683 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7684 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7685 // force download dialog
7686 if (strpos(php_sapi_name(), 'cgi') === false) {
7687 header('Content-Type: application/force-download');
7688 header('Content-Type: application/octet-stream', false);
7689 header('Content-Type: application/download', false);
7690 header('Content-Type: application/pdf', false);
7691 } else {
7692 header('Content-Type: application/pdf');
7693 }
7694 // use the Content-Disposition header to supply a recommended filename
7695 header('Content-Disposition: attachment; filename="'.basename($name).'"');
7696 header('Content-Transfer-Encoding: binary');
7697 TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7698 }
7699 break;
7700 }
7701 case 'E': {
7702 // return PDF as base64 mime multi-part email attachment (RFC 2045)
7703 $retval = 'Content-Type: application/pdf;'."\r\n";
7704 $retval .= ' name="'.$name.'"'."\r\n";
7705 $retval .= 'Content-Transfer-Encoding: base64'."\r\n";
7706 $retval .= 'Content-Disposition: attachment;'."\r\n";
7707 $retval .= ' filename="'.$name.'"'."\r\n\r\n";
7708 $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
7709 return $retval;
7710 }
7711 case 'S': {
7712 // returns PDF as a string
7713 return $this->getBuffer();
7714 }
7715 default: {
7716 $this->Error('Incorrect output destination: '.$dest);
7717 }
7718 }
7719 return '';
7720 }
7721
7729 public function _destroy($destroyall=false, $preserve_objcopy=false) {
7730 if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!TCPDF_STATIC::empty_string($this->buffer))) {
7731 // remove buffer file from cache
7732 unlink($this->buffer);
7733 }
7734 if ($destroyall AND isset($this->cached_files) AND !empty($this->cached_files)) {
7735 // remove cached files
7736 foreach ($this->cached_files as $cachefile) {
7737 if (is_file($cachefile)) {
7738 unlink($cachefile);
7739 }
7740 }
7741 unset($this->cached_files);
7742 }
7743 foreach (array_keys(get_object_vars($this)) as $val) {
7744 if ($destroyall OR (
7745 ($val != 'internal_encoding')
7746 AND ($val != 'state')
7747 AND ($val != 'bufferlen')
7748 AND ($val != 'buffer')
7749 AND ($val != 'diskcache')
7750 AND ($val != 'cached_files')
7751 AND ($val != 'sign')
7752 AND ($val != 'signature_data')
7753 AND ($val != 'signature_max_length')
7754 AND ($val != 'byterange_string')
7755 )) {
7756 if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) {
7757 unset($this->$val);
7758 }
7759 }
7760 }
7761 }
7762
7767 protected function _dochecks() {
7768 //Check for locale-related bug
7769 if (1.1 == 1) {
7770 $this->Error('Don\'t alter the locale before including class file');
7771 }
7772 //Check for decimal separator
7773 if (sprintf('%.1F', 1.0) != '1.0') {
7774 setlocale(LC_NUMERIC, 'C');
7775 }
7776 }
7777
7784 protected function getInternalPageNumberAliases($a= '') {
7785 $alias = array();
7786 // build array of Unicode + ASCII variants (the order is important)
7787 $alias = array('u' => array(), 'a' => array());
7788 $u = '{'.$a.'}';
7789 $alias['u'][] = TCPDF_STATIC::_escape($u);
7790 if ($this->isunicode) {
7791 $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($u, $this->isunicode, $this->CurrentFont));
7792 $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($u, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7793 $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($a, $this->isunicode, $this->CurrentFont));
7794 $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($a, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7795 }
7796 $alias['a'][] = TCPDF_STATIC::_escape($a);
7797 return $alias;
7798 }
7799
7805 protected function getAllInternalPageNumberAliases() {
7807 $pnalias = array();
7808 foreach($basic_alias as $k => $a) {
7809 $pnalias[$k] = $this->getInternalPageNumberAliases($a);
7810 }
7811 return $pnalias;
7812 }
7813
7823 protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
7824 foreach ($aliases as $type => $alias) {
7825 foreach ($alias as $a) {
7826 // find position of compensation factor
7827 $startnum = (strpos($a, ':') + 1);
7828 $a = substr($a, 0, $startnum);
7829 if (($pos = strpos($page, $a)) !== false) {
7830 // end of alias
7831 $endnum = strpos($page, '}', $pos);
7832 // string to be replaced
7833 $aa = substr($page, $pos, ($endnum - $pos + 1));
7834 // get compensation factor
7835 $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum));
7836 $ratio = preg_replace('/[^0-9\.]/', '', $ratio);
7837 $ratio = floatval($ratio);
7838 if ($type == 'u') {
7839 $chrdiff = floor(($diff + 12) * $ratio);
7840 $shift = str_repeat(' ', $chrdiff);
7841 $shift = TCPDF_FONTS::UTF8ToUTF16BE($shift, false, $this->isunicode, $this->CurrentFont);
7842 } else {
7843 $chrdiff = floor(($diff + 11) * $ratio);
7844 $shift = str_repeat(' ', $chrdiff);
7845 }
7846 $page = str_replace($aa, $shift, $page);
7847 }
7848 }
7849 }
7850 return $page;
7851 }
7852
7858 protected function setPageBoxTypes($boxes) {
7859 $this->page_boxes = array();
7860 foreach ($boxes as $box) {
7861 if (in_array($box, TCPDF_STATIC::$pageboxes)) {
7862 $this->page_boxes[] = $box;
7863 }
7864 }
7865 }
7866
7871 protected function _putpages() {
7872 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
7873 // get internal aliases for page numbers
7874 $pnalias = $this->getAllInternalPageNumberAliases();
7875 $num_pages = $this->numpages;
7876 $ptpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $num_pages - 1));
7877 $ptpu = TCPDF_FONTS::UTF8ToUTF16BE($ptpa, false, $this->isunicode, $this->CurrentFont);
7878 $ptp_num_chars = $this->GetNumChars($ptpa);
7879 $pagegroupnum = 0;
7880 $groupnum = 0;
7881 $ptgu = 1;
7882 $ptga = 1;
7883 for ($n = 1; $n <= $num_pages; ++$n) {
7884 // get current page
7885 $temppage = $this->getPageBuffer($n);
7886 $pagelen = strlen($temppage);
7887 // set replacements for total pages number
7888 $pnpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $n - 1));
7889 $pnpu = TCPDF_FONTS::UTF8ToUTF16BE($pnpa, false, $this->isunicode, $this->CurrentFont);
7890 $pnp_num_chars = $this->GetNumChars($pnpa);
7891 $pdiff = 0; // difference used for right shift alignment of page numbers
7892 $gdiff = 0; // difference used for right shift alignment of page group numbers
7893 if (!empty($this->pagegroups)) {
7894 if (isset($this->newpagegroup[$n])) {
7895 $pagegroupnum = 0;
7896 ++$groupnum;
7897 $ptga = TCPDF_STATIC::formatPageNumber($this->pagegroups[$groupnum]);
7898 $ptgu = TCPDF_FONTS::UTF8ToUTF16BE($ptga, false, $this->isunicode, $this->CurrentFont);
7899 $ptg_num_chars = $this->GetNumChars($ptga);
7900 }
7901 ++$pagegroupnum;
7902 $pnga = TCPDF_STATIC::formatPageNumber($pagegroupnum);
7903 $pngu = TCPDF_FONTS::UTF8ToUTF16BE($pnga, false, $this->isunicode, $this->CurrentFont);
7904 $png_num_chars = $this->GetNumChars($pnga);
7905 // replace page numbers
7906 $replace = array();
7907 $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
7908 $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
7909 $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
7910 $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
7911 list($temppage, $gdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $gdiff);
7912 }
7913 // replace page numbers
7914 $replace = array();
7915 $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
7916 $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
7917 $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
7918 $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
7919 list($temppage, $pdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $pdiff);
7920 // replace right shift alias
7921 $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
7922 // replace EPS marker
7923 $temppage = str_replace($this->epsmarker, '', $temppage);
7924 //Page
7925 $this->page_obj_id[$n] = $this->_newobj();
7926 $out = '<<';
7927 $out .= ' /Type /Page';
7928 $out .= ' /Parent 1 0 R';
7929 $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp);
7930 $out .= ' /Resources 2 0 R';
7931 foreach ($this->page_boxes as $box) {
7932 $out .= ' /'.$box;
7933 $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']);
7934 }
7935 if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
7936 $out .= ' /BoxColorInfo <<';
7937 foreach ($this->page_boxes as $box) {
7938 if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
7939 $out .= ' /'.$box.' <<';
7940 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
7941 $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
7942 $out .= ' /C [';
7943 $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
7944 $out .= ' ]';
7945 }
7946 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
7947 $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
7948 }
7949 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
7950 $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
7951 }
7952 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
7953 $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
7954 $out .= ' /D [';
7955 foreach ($dashes as $dash) {
7956 $out .= sprintf(' %F', ($dash * $this->k));
7957 }
7958 $out .= ' ]';
7959 }
7960 $out .= ' >>';
7961 }
7962 }
7963 $out .= ' >>';
7964 }
7965 $out .= ' /Contents '.($this->n + 1).' 0 R';
7966 $out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
7967 if (!$this->pdfa_mode) {
7968 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
7969 }
7970 if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
7971 // page transitions
7972 if (isset($this->pagedim[$n]['trans']['Dur'])) {
7973 $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
7974 }
7975 $out .= ' /Trans <<';
7976 $out .= ' /Type /Trans';
7977 if (isset($this->pagedim[$n]['trans']['S'])) {
7978 $out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
7979 }
7980 if (isset($this->pagedim[$n]['trans']['D'])) {
7981 $out .= ' /D '.$this->pagedim[$n]['trans']['D'];
7982 }
7983 if (isset($this->pagedim[$n]['trans']['Dm'])) {
7984 $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
7985 }
7986 if (isset($this->pagedim[$n]['trans']['M'])) {
7987 $out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
7988 }
7989 if (isset($this->pagedim[$n]['trans']['Di'])) {
7990 $out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
7991 }
7992 if (isset($this->pagedim[$n]['trans']['SS'])) {
7993 $out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
7994 }
7995 if (isset($this->pagedim[$n]['trans']['B'])) {
7996 $out .= ' /B '.$this->pagedim[$n]['trans']['B'];
7997 }
7998 $out .= ' >>';
7999 }
8000 $out .= $this->_getannotsrefs($n);
8001 $out .= ' /PZ '.$this->pagedim[$n]['PZ'];
8002 $out .= ' >>';
8003 $out .= "\n".'endobj';
8004 $this->_out($out);
8005 //Page content
8006 $p = ($this->compress) ? gzcompress($temppage) : $temppage;
8007 $this->_newobj();
8008 $p = $this->_getrawstream($p);
8009 $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
8010 if ($this->diskcache) {
8011 // remove temporary files
8012 unlink($this->pages[$n]);
8013 }
8014 }
8015 //Pages root
8016 $out = $this->_getobj(1)."\n";
8017 $out .= '<< /Type /Pages /Kids [';
8018 foreach($this->page_obj_id as $page_obj) {
8019 $out .= ' '.$page_obj.' 0 R';
8020 }
8021 $out .= ' ] /Count '.$num_pages.' >>';
8022 $out .= "\n".'endobj';
8023 $this->_out($out);
8024 }
8025
8034 protected function _putannotsrefs($n) {
8035 $this->_out($this->_getannotsrefs($n));
8036 }
8037
8046 protected function _getannotsrefs($n) {
8047 if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
8048 return '';
8049 }
8050 $out = ' /Annots [';
8051 if (isset($this->PageAnnots[$n])) {
8052 foreach ($this->PageAnnots[$n] as $key => $val) {
8053 if (!in_array($val['n'], $this->radio_groups)) {
8054 $out .= ' '.$val['n'].' 0 R';
8055 }
8056 }
8057 // add radiobutton groups
8058 if (isset($this->radiobutton_groups[$n])) {
8059 foreach ($this->radiobutton_groups[$n] as $key => $data) {
8060 if (isset($data['n'])) {
8061 $out .= ' '.$data['n'].' 0 R';
8062 }
8063 }
8064 }
8065 }
8066 if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
8067 // set reference for signature object
8068 $out .= ' '.$this->sig_obj_id.' 0 R';
8069 }
8070 if (!empty($this->empty_signature_appearance)) {
8071 foreach ($this->empty_signature_appearance as $esa) {
8072 if ($esa['page'] == $n) {
8073 // set reference for empty signature objects
8074 $out .= ' '.$esa['objid'].' 0 R';
8075 }
8076 }
8077 }
8078 $out .= ' ]';
8079 return $out;
8080 }
8081
8090 protected function _putannotsobjs() {
8091 // reset object counter
8092 for ($n=1; $n <= $this->numpages; ++$n) {
8093 if (isset($this->PageAnnots[$n])) {
8094 // set page annotations
8095 foreach ($this->PageAnnots[$n] as $key => $pl) {
8096 $annot_obj_id = $this->PageAnnots[$n][$key]['n'];
8097 // create annotation object for grouping radiobuttons
8098 if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
8099 $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n'];
8100 $annots = '<<';
8101 $annots .= ' /Type /Annot';
8102 $annots .= ' /Subtype /Widget';
8103 $annots .= ' /Rect [0 0 0 0]';
8104 if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) {
8105 // read only
8106 $annots .= ' /F 68';
8107 $annots .= ' /Ff 49153';
8108 } else {
8109 $annots .= ' /F 4'; // default print for PDF/A
8110 $annots .= ' /Ff 49152';
8111 }
8112 $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
8113 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8114 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id);
8115 }
8116 $annots .= ' /FT /Btn';
8117 $annots .= ' /Kids [';
8118 $defval = '';
8119 foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) {
8120 if (isset($data['kid'])) {
8121 $annots .= ' '.$data['kid'].' 0 R';
8122 if ($data['def'] !== 'Off') {
8123 $defval = $data['def'];
8124 }
8125 }
8126 }
8127 $annots .= ' ]';
8128 if (!empty($defval)) {
8129 $annots .= ' /V /'.$defval;
8130 }
8131 $annots .= ' >>';
8132 $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
8133 $this->form_obj_id[] = $radio_button_obj_id;
8134 // store object id to be used on Parent entry of Kids
8135 $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id;
8136 }
8137 $formfield = false;
8138 $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
8139 $a = $pl['x'] * $this->k;
8140 $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k);
8141 $c = $pl['w'] * $this->k;
8142 $d = $pl['h'] * $this->k;
8143 $rect = sprintf('%F %F %F %F', $a, $b, $a+$c, $b+$d);
8144 // create new annotation object
8145 $annots = '<</Type /Annot';
8146 $annots .= ' /Subtype /'.$pl['opt']['subtype'];
8147 $annots .= ' /Rect ['.$rect.']';
8148 $ft = array('Btn', 'Tx', 'Ch', 'Sig');
8149 if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
8150 $annots .= ' /FT /'.$pl['opt']['ft'];
8151 $formfield = true;
8152 }
8153 $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
8154 $annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
8155 $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
8156 $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp);
8157 if (isset($pl['opt']['f'])) {
8158 $fval = 0;
8159 if (is_array($pl['opt']['f'])) {
8160 foreach ($pl['opt']['f'] as $f) {
8161 switch (strtolower($f)) {
8162 case 'invisible': {
8163 $fval += 1 << 0;
8164 break;
8165 }
8166 case 'hidden': {
8167 $fval += 1 << 1;
8168 break;
8169 }
8170 case 'print': {
8171 $fval += 1 << 2;
8172 break;
8173 }
8174 case 'nozoom': {
8175 $fval += 1 << 3;
8176 break;
8177 }
8178 case 'norotate': {
8179 $fval += 1 << 4;
8180 break;
8181 }
8182 case 'noview': {
8183 $fval += 1 << 5;
8184 break;
8185 }
8186 case 'readonly': {
8187 $fval += 1 << 6;
8188 break;
8189 }
8190 case 'locked': {
8191 $fval += 1 << 8;
8192 break;
8193 }
8194 case 'togglenoview': {
8195 $fval += 1 << 9;
8196 break;
8197 }
8198 case 'lockedcontents': {
8199 $fval += 1 << 10;
8200 break;
8201 }
8202 default: {
8203 break;
8204 }
8205 }
8206 }
8207 } else {
8208 $fval = intval($pl['opt']['f']);
8209 }
8210 } else {
8211 $fval = 4;
8212 }
8213 if ($this->pdfa_mode) {
8214 // force print flag for PDF/A mode
8215 $fval |= 4;
8216 }
8217 $annots .= ' /F '.intval($fval);
8218 if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
8219 $annots .= ' /AS /'.$pl['opt']['as'];
8220 }
8221 if (isset($pl['opt']['ap'])) {
8222 // appearance stream
8223 $annots .= ' /AP <<';
8224 if (is_array($pl['opt']['ap'])) {
8225 foreach ($pl['opt']['ap'] as $apmode => $apdef) {
8226 // $apmode can be: n = normal; r = rollover; d = down;
8227 $annots .= ' /'.strtoupper($apmode);
8228 if (is_array($apdef)) {
8229 $annots .= ' <<';
8230 foreach ($apdef as $apstate => $stream) {
8231 // reference to XObject that define the appearance for this mode-state
8232 $apsobjid = $this->_putAPXObject($c, $d, $stream);
8233 $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
8234 }
8235 $annots .= ' >>';
8236 } else {
8237 // reference to XObject that define the appearance for this mode
8238 $apsobjid = $this->_putAPXObject($c, $d, $apdef);
8239 $annots .= ' '.$apsobjid.' 0 R';
8240 }
8241 }
8242 } else {
8243 $annots .= $pl['opt']['ap'];
8244 }
8245 $annots .= ' >>';
8246 }
8247 if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
8248 $annots .= ' /BS <<';
8249 $annots .= ' /Type /Border';
8250 if (isset($pl['opt']['bs']['w'])) {
8251 $annots .= ' /W '.intval($pl['opt']['bs']['w']);
8252 }
8253 $bstyles = array('S', 'D', 'B', 'I', 'U');
8254 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
8255 $annots .= ' /S /'.$pl['opt']['bs']['s'];
8256 }
8257 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
8258 $annots .= ' /D [';
8259 foreach ($pl['opt']['bs']['d'] as $cord) {
8260 $annots .= ' '.intval($cord);
8261 }
8262 $annots .= ']';
8263 }
8264 $annots .= ' >>';
8265 } else {
8266 $annots .= ' /Border [';
8267 if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
8268 $annots .= intval($pl['opt']['border'][0]).' ';
8269 $annots .= intval($pl['opt']['border'][1]).' ';
8270 $annots .= intval($pl['opt']['border'][2]);
8271 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
8272 $annots .= ' [';
8273 foreach ($pl['opt']['border'][3] as $dash) {
8274 $annots .= intval($dash).' ';
8275 }
8276 $annots .= ']';
8277 }
8278 } else {
8279 $annots .= '0 0 0';
8280 }
8281 $annots .= ']';
8282 }
8283 if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
8284 $annots .= ' /BE <<';
8285 $bstyles = array('S', 'C');
8286 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) {
8287 $annots .= ' /S /'.$pl['opt']['bs']['s'];
8288 } else {
8289 $annots .= ' /S /S';
8290 }
8291 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
8292 $annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']);
8293 }
8294 $annots .= '>>';
8295 }
8296 if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
8297 $annots .= ' /C '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['c']);
8298 }
8299 //$annots .= ' /StructParent ';
8300 //$annots .= ' /OC ';
8301 $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
8302 if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
8303 // this is a markup type
8304 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8305 $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
8306 }
8307 //$annots .= ' /Popup ';
8308 if (isset($pl['opt']['ca'])) {
8309 $annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca']));
8310 }
8311 if (isset($pl['opt']['rc'])) {
8312 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8313 }
8314 $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp);
8315 //$annots .= ' /IRT ';
8316 if (isset($pl['opt']['subj'])) {
8317 $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
8318 }
8319 //$annots .= ' /RT ';
8320 //$annots .= ' /IT ';
8321 //$annots .= ' /ExData ';
8322 }
8323 $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
8324 // Annotation types
8325 switch (strtolower($pl['opt']['subtype'])) {
8326 case 'text': {
8327 if (isset($pl['opt']['open'])) {
8328 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
8329 }
8330 $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
8331 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8332 $annots .= ' /Name /'.$pl['opt']['name'];
8333 } else {
8334 $annots .= ' /Name /Note';
8335 }
8336 $statemodels = array('Marked', 'Review');
8337 if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
8338 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8339 } else {
8340 $pl['opt']['statemodel'] = 'Marked';
8341 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8342 }
8343 if ($pl['opt']['statemodel'] == 'Marked') {
8344 $states = array('Accepted', 'Unmarked');
8345 } else {
8346 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
8347 }
8348 if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
8349 $annots .= ' /State /'.$pl['opt']['state'];
8350 } else {
8351 if ($pl['opt']['statemodel'] == 'Marked') {
8352 $annots .= ' /State /Unmarked';
8353 } else {
8354 $annots .= ' /State /None';
8355 }
8356 }
8357 break;
8358 }
8359 case 'link': {
8360 if (is_string($pl['txt'])) {
8361 if ($pl['txt'][0] == '#') {
8362 // internal destination
8363 $annots .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($pl['txt'], 1));
8364 } elseif ($pl['txt'][0] == '%') {
8365 // embedded PDF file
8366 $filename = basename(substr($pl['txt'], 1));
8367 $annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
8368 } elseif ($pl['txt'][0] == '*') {
8369 // embedded generic file
8370 $filename = basename(substr($pl['txt'], 1));
8371 $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});';
8372 $annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>';
8373 } else {
8374 // external URI link
8375 $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
8376 }
8377 } elseif (isset($this->links[$pl['txt']])) {
8378 // internal link ID
8379 $l = $this->links[$pl['txt']];
8380 if (isset($this->page_obj_id[($l[0])])) {
8381 $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
8382 }
8383 }
8384 $hmodes = array('N', 'I', 'O', 'P');
8385 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
8386 $annots .= ' /H /'.$pl['opt']['h'];
8387 } else {
8388 $annots .= ' /H /I';
8389 }
8390 //$annots .= ' /PA ';
8391 //$annots .= ' /Quadpoints ';
8392 break;
8393 }
8394 case 'freetext': {
8395 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8396 $annots .= ' /DA ('.$pl['opt']['da'].')';
8397 }
8398 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8399 $annots .= ' /Q '.intval($pl['opt']['q']);
8400 }
8401 if (isset($pl['opt']['rc'])) {
8402 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8403 }
8404 if (isset($pl['opt']['ds'])) {
8405 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
8406 }
8407 if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
8408 $annots .= ' /CL [';
8409 foreach ($pl['opt']['cl'] as $cl) {
8410 $annots .= sprintf('%F ', $cl * $this->k);
8411 }
8412 $annots .= ']';
8413 }
8414 $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
8415 if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
8416 $annots .= ' /IT /'.$pl['opt']['it'];
8417 }
8418 if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
8419 $l = $pl['opt']['rd'][0] * $this->k;
8420 $r = $pl['opt']['rd'][1] * $this->k;
8421 $t = $pl['opt']['rd'][2] * $this->k;
8422 $b = $pl['opt']['rd'][3] * $this->k;
8423 $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']';
8424 }
8425 if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
8426 $annots .= ' /LE /'.$pl['opt']['le'];
8427 }
8428 break;
8429 }
8430 case 'line': {
8431 break;
8432 }
8433 case 'square': {
8434 break;
8435 }
8436 case 'circle': {
8437 break;
8438 }
8439 case 'polygon': {
8440 break;
8441 }
8442 case 'polyline': {
8443 break;
8444 }
8445 case 'highlight': {
8446 break;
8447 }
8448 case 'underline': {
8449 break;
8450 }
8451 case 'squiggly': {
8452 break;
8453 }
8454 case 'strikeout': {
8455 break;
8456 }
8457 case 'stamp': {
8458 break;
8459 }
8460 case 'caret': {
8461 break;
8462 }
8463 case 'ink': {
8464 break;
8465 }
8466 case 'popup': {
8467 break;
8468 }
8469 case 'fileattachment': {
8470 if ($this->pdfa_mode) {
8471 // embedded files are not allowed in PDF/A mode
8472 break;
8473 }
8474 if (!isset($pl['opt']['fs'])) {
8475 break;
8476 }
8477 $filename = basename($pl['opt']['fs']);
8478 if (isset($this->embeddedfiles[$filename]['f'])) {
8479 $annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R';
8480 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
8481 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8482 $annots .= ' /Name /'.$pl['opt']['name'];
8483 } else {
8484 $annots .= ' /Name /PushPin';
8485 }
8486 // index (zero-based) of the annotation in the Annots array of this page
8487 $this->embeddedfiles[$filename]['a'] = $key;
8488 }
8489 break;
8490 }
8491 case 'sound': {
8492 if (!isset($pl['opt']['fs'])) {
8493 break;
8494 }
8495 $filename = basename($pl['opt']['fs']);
8496 if (isset($this->embeddedfiles[$filename]['f'])) {
8497 // ... TO BE COMPLETED ...
8498 // /R /C /B /E /CO /CP
8499 $annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R';
8500 $iconsapp = array('Speaker', 'Mic');
8501 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8502 $annots .= ' /Name /'.$pl['opt']['name'];
8503 } else {
8504 $annots .= ' /Name /Speaker';
8505 }
8506 }
8507 break;
8508 }
8509 case 'movie': {
8510 break;
8511 }
8512 case 'widget': {
8513 $hmode = array('N', 'I', 'O', 'P', 'T');
8514 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
8515 $annots .= ' /H /'.$pl['opt']['h'];
8516 }
8517 if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
8518 $annots .= ' /MK <<';
8519 if (isset($pl['opt']['mk']['r'])) {
8520 $annots .= ' /R '.$pl['opt']['mk']['r'];
8521 }
8522 if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
8523 $annots .= ' /BC '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bc']);
8524 }
8525 if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
8526 $annots .= ' /BG '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bg']);
8527 }
8528 if (isset($pl['opt']['mk']['ca'])) {
8529 $annots .= ' /CA '.$pl['opt']['mk']['ca'];
8530 }
8531 if (isset($pl['opt']['mk']['rc'])) {
8532 $annots .= ' /RC '.$pl['opt']['mk']['rc'];
8533 }
8534 if (isset($pl['opt']['mk']['ac'])) {
8535 $annots .= ' /AC '.$pl['opt']['mk']['ac'];
8536 }
8537 if (isset($pl['opt']['mk']['i'])) {
8538 $info = $this->getImageBuffer($pl['opt']['mk']['i']);
8539 if ($info !== false) {
8540 $annots .= ' /I '.$info['n'].' 0 R';
8541 }
8542 }
8543 if (isset($pl['opt']['mk']['ri'])) {
8544 $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
8545 if ($info !== false) {
8546 $annots .= ' /RI '.$info['n'].' 0 R';
8547 }
8548 }
8549 if (isset($pl['opt']['mk']['ix'])) {
8550 $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
8551 if ($info !== false) {
8552 $annots .= ' /IX '.$info['n'].' 0 R';
8553 }
8554 }
8555 if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
8556 $annots .= ' /IF <<';
8557 $if_sw = array('A', 'B', 'S', 'N');
8558 if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
8559 $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
8560 }
8561 $if_s = array('A', 'P');
8562 if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
8563 $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
8564 }
8565 if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
8566 $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
8567 }
8568 if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
8569 $annots .= ' /FB true';
8570 }
8571 $annots .= '>>';
8572 }
8573 if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
8574 $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
8575 }
8576 $annots .= '>>';
8577 } // end MK
8578 // --- Entries for field dictionaries ---
8579 if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
8580 // set parent
8581 $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
8582 }
8583 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8584 $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
8585 }
8586 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8587 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
8588 }
8589 if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
8590 $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
8591 }
8592 if (isset($pl['opt']['ff'])) {
8593 if (is_array($pl['opt']['ff'])) {
8594 // array of bit settings
8595 $flag = 0;
8596 foreach($pl['opt']['ff'] as $val) {
8597 $flag += 1 << ($val - 1);
8598 }
8599 } else {
8600 $flag = intval($pl['opt']['ff']);
8601 }
8602 $annots .= ' /Ff '.$flag;
8603 }
8604 if (isset($pl['opt']['maxlen'])) {
8605 $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
8606 }
8607 if (isset($pl['opt']['v'])) {
8608 $annots .= ' /V';
8609 if (is_array($pl['opt']['v'])) {
8610 foreach ($pl['opt']['v'] AS $optval) {
8611 if (is_float($optval)) {
8612 $optval = sprintf('%F', $optval);
8613 }
8614 $annots .= ' '.$optval;
8615 }
8616 } else {
8617 $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
8618 }
8619 }
8620 if (isset($pl['opt']['dv'])) {
8621 $annots .= ' /DV';
8622 if (is_array($pl['opt']['dv'])) {
8623 foreach ($pl['opt']['dv'] AS $optval) {
8624 if (is_float($optval)) {
8625 $optval = sprintf('%F', $optval);
8626 }
8627 $annots .= ' '.$optval;
8628 }
8629 } else {
8630 $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
8631 }
8632 }
8633 if (isset($pl['opt']['rv'])) {
8634 $annots .= ' /RV';
8635 if (is_array($pl['opt']['rv'])) {
8636 foreach ($pl['opt']['rv'] AS $optval) {
8637 if (is_float($optval)) {
8638 $optval = sprintf('%F', $optval);
8639 }
8640 $annots .= ' '.$optval;
8641 }
8642 } else {
8643 $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
8644 }
8645 }
8646 if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
8647 $annots .= ' /A << '.$pl['opt']['a'].' >>';
8648 }
8649 if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
8650 $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
8651 }
8652 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8653 $annots .= ' /DA ('.$pl['opt']['da'].')';
8654 }
8655 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8656 $annots .= ' /Q '.intval($pl['opt']['q']);
8657 }
8658 if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
8659 $annots .= ' /Opt [';
8660 foreach($pl['opt']['opt'] AS $copt) {
8661 if (is_array($copt)) {
8662 $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
8663 } else {
8664 $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
8665 }
8666 }
8667 $annots .= ']';
8668 }
8669 if (isset($pl['opt']['ti'])) {
8670 $annots .= ' /TI '.intval($pl['opt']['ti']);
8671 }
8672 if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
8673 $annots .= ' /I [';
8674 foreach($pl['opt']['i'] AS $copt) {
8675 $annots .= intval($copt).' ';
8676 }
8677 $annots .= ']';
8678 }
8679 break;
8680 }
8681 case 'screen': {
8682 break;
8683 }
8684 case 'printermark': {
8685 break;
8686 }
8687 case 'trapnet': {
8688 break;
8689 }
8690 case 'watermark': {
8691 break;
8692 }
8693 case '3d': {
8694 break;
8695 }
8696 default: {
8697 break;
8698 }
8699 }
8700 $annots .= '>>';
8701 // create new annotation object
8702 $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
8703 if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
8704 // store reference of form object
8705 $this->form_obj_id[] = $annot_obj_id;
8706 }
8707 }
8708 }
8709 } // end for each page
8710 }
8711
8721 protected function _putAPXObject($w=0, $h=0, $stream='') {
8722 $stream = trim($stream);
8723 $out = $this->_getobj()."\n";
8724 $this->xobjects['AX'.$this->n] = array('n' => $this->n);
8725 $out .= '<<';
8726 $out .= ' /Type /XObject';
8727 $out .= ' /Subtype /Form';
8728 $out .= ' /FormType 1';
8729 if ($this->compress) {
8730 $stream = gzcompress($stream);
8731 $out .= ' /Filter /FlateDecode';
8732 }
8733 $rect = sprintf('%F %F', $w, $h);
8734 $out .= ' /BBox [0 0 '.$rect.']';
8735 $out .= ' /Matrix [1 0 0 1 0 0]';
8736 $out .= ' /Resources 2 0 R';
8737 $stream = $this->_getrawstream($stream);
8738 $out .= ' /Length '.strlen($stream);
8739 $out .= ' >>';
8740 $out .= ' stream'."\n".$stream."\n".'endstream';
8741 $out .= "\n".'endobj';
8742 $this->_out($out);
8743 return $this->n;
8744 }
8745
8751 protected function _putfonts() {
8752 $nf = $this->n;
8753 foreach ($this->diffs as $diff) {
8754 //Encodings
8755 $this->_newobj();
8756 $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
8757 }
8758 $mqr = TCPDF_STATIC::get_mqr();
8759 TCPDF_STATIC::set_mqr(false);
8760 foreach ($this->FontFiles as $file => $info) {
8761 // search and get font file to embedd
8762 $fontdir = $info['fontdir'];
8763 $file = strtolower($file);
8764 $fontfile = '';
8765 // search files on various directories
8766 if (($fontdir !== false) AND file_exists($fontdir.$file)) {
8767 $fontfile = $fontdir.$file;
8768 } elseif (file_exists(TCPDF_FONTS::_getfontpath().$file)) {
8769 $fontfile = TCPDF_FONTS::_getfontpath().$file;
8770 } elseif (file_exists($file)) {
8771 $fontfile = $file;
8772 }
8773 if (!TCPDF_STATIC::empty_string($fontfile)) {
8774 $font = file_get_contents($fontfile);
8775 $compressed = (substr($file, -2) == '.z');
8776 if ((!$compressed) AND (isset($info['length2']))) {
8777 $header = (ord($font{0}) == 128);
8778 if ($header) {
8779 // strip first binary header
8780 $font = substr($font, 6);
8781 }
8782 if ($header AND (ord($font[$info['length1']]) == 128)) {
8783 // strip second binary header
8784 $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
8785 }
8786 } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
8787 if ($compressed) {
8788 // uncompress font
8789 $font = gzuncompress($font);
8790 }
8791 // merge subset characters
8792 $subsetchars = array(); // used chars
8793 foreach ($info['fontkeys'] as $fontkey) {
8794 $fontinfo = $this->getFontBuffer($fontkey);
8795 $subsetchars += $fontinfo['subsetchars'];
8796 }
8797 // rebuild a font subset
8798 $font = TCPDF_FONTS::_getTrueTypeFontSubset($font, $subsetchars);
8799 // calculate new font length
8800 $info['length1'] = strlen($font);
8801 if ($compressed) {
8802 // recompress font
8803 $font = gzcompress($font);
8804 }
8805 }
8806 $this->_newobj();
8807 $this->FontFiles[$file]['n'] = $this->n;
8808 $stream = $this->_getrawstream($font);
8809 $out = '<< /Length '.strlen($stream);
8810 if ($compressed) {
8811 $out .= ' /Filter /FlateDecode';
8812 }
8813 $out .= ' /Length1 '.$info['length1'];
8814 if (isset($info['length2'])) {
8815 $out .= ' /Length2 '.$info['length2'].' /Length3 0';
8816 }
8817 $out .= ' >>';
8818 $out .= ' stream'."\n".$stream."\n".'endstream';
8819 $out .= "\n".'endobj';
8820 $this->_out($out);
8821 }
8822 }
8824 foreach ($this->fontkeys as $k) {
8825 //Font objects
8826 $font = $this->getFontBuffer($k);
8827 $type = $font['type'];
8828 $name = $font['name'];
8829 if ($type == 'core') {
8830 // standard core font
8831 $out = $this->_getobj($this->font_obj_ids[$k])."\n";
8832 $out .= '<</Type /Font';
8833 $out .= ' /Subtype /Type1';
8834 $out .= ' /BaseFont /'.$name;
8835 $out .= ' /Name /F'.$font['i'];
8836 if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
8837 $out .= ' /Encoding /WinAnsiEncoding';
8838 }
8839 if ($k == 'helvetica') {
8840 // add default font for annotations
8841 $this->annotation_fonts[$k] = $font['i'];
8842 }
8843 $out .= ' >>';
8844 $out .= "\n".'endobj';
8845 $this->_out($out);
8846 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
8847 // additional Type1 or TrueType font
8848 $out = $this->_getobj($this->font_obj_ids[$k])."\n";
8849 $out .= '<</Type /Font';
8850 $out .= ' /Subtype /'.$type;
8851 $out .= ' /BaseFont /'.$name;
8852 $out .= ' /Name /F'.$font['i'];
8853 $out .= ' /FirstChar 32 /LastChar 255';
8854 $out .= ' /Widths '.($this->n + 1).' 0 R';
8855 $out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
8856 if ($font['enc']) {
8857 if (isset($font['diff'])) {
8858 $out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
8859 } else {
8860 $out .= ' /Encoding /WinAnsiEncoding';
8861 }
8862 }
8863 $out .= ' >>';
8864 $out .= "\n".'endobj';
8865 $this->_out($out);
8866 // Widths
8867 $this->_newobj();
8868 $s = '[';
8869 for ($i = 32; $i < 256; ++$i) {
8870 if (isset($font['cw'][$i])) {
8871 $s .= $font['cw'][$i].' ';
8872 } else {
8873 $s .= $font['dw'].' ';
8874 }
8875 }
8876 $s .= ']';
8877 $s .= "\n".'endobj';
8878 $this->_out($s);
8879 //Descriptor
8880 $this->_newobj();
8881 $s = '<</Type /FontDescriptor /FontName /'.$name;
8882 foreach ($font['desc'] as $fdk => $fdv) {
8883 if (is_float($fdv)) {
8884 $fdv = sprintf('%F', $fdv);
8885 }
8886 $s .= ' /'.$fdk.' '.$fdv.'';
8887 }
8888 if (!TCPDF_STATIC::empty_string($font['file'])) {
8889 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
8890 }
8891 $s .= '>>';
8892 $s .= "\n".'endobj';
8893 $this->_out($s);
8894 } else {
8895 // additional types
8896 $mtd = '_put'.strtolower($type);
8897 if (!method_exists($this, $mtd)) {
8898 $this->Error('Unsupported font type: '.$type);
8899 }
8900 $this->$mtd($font);
8901 }
8902 }
8903 }
8904
8913 protected function _puttruetypeunicode($font) {
8914 $fontname = '';
8915 if ($font['subset']) {
8916 // change name for font subsetting
8917 $subtag = sprintf('%06u', $font['i']);
8918 $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
8919 $fontname .= $subtag.'+';
8920 }
8921 $fontname .= $font['name'];
8922 // Type0 Font
8923 // A composite font composed of other fonts, organized hierarchically
8924 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
8925 $out .= '<< /Type /Font';
8926 $out .= ' /Subtype /Type0';
8927 $out .= ' /BaseFont /'.$fontname;
8928 $out .= ' /Name /F'.$font['i'];
8929 $out .= ' /Encoding /'.$font['enc'];
8930 $out .= ' /ToUnicode '.($this->n + 1).' 0 R';
8931 $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
8932 $out .= ' >>';
8933 $out .= "\n".'endobj';
8934 $this->_out($out);
8935 // ToUnicode map for Identity-H
8937 // ToUnicode Object
8938 $this->_newobj();
8939 $stream = ($this->compress) ? gzcompress($stream) : $stream;
8940 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
8941 $stream = $this->_getrawstream($stream);
8942 $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
8943 // CIDFontType2
8944 // A CIDFont whose glyph descriptions are based on TrueType font technology
8945 $oid = $this->_newobj();
8946 $out = '<< /Type /Font';
8947 $out .= ' /Subtype /CIDFontType2';
8948 $out .= ' /BaseFont /'.$fontname;
8949 // A dictionary containing entries that define the character collection of the CIDFont.
8950 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
8951 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
8952 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
8953 $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
8954 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
8955 $out .= ' /DW '.$font['dw']; // default width
8956 $out .= "\n".TCPDF_FONTS::_putfontwidths($font, 0);
8957 if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
8958 $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
8959 }
8960 $out .= ' >>';
8961 $out .= "\n".'endobj';
8962 $this->_out($out);
8963 // Font descriptor
8964 // A font descriptor describing the CIDFont default metrics other than its glyph widths
8965 $this->_newobj();
8966 $out = '<< /Type /FontDescriptor';
8967 $out .= ' /FontName /'.$fontname;
8968 foreach ($font['desc'] as $key => $value) {
8969 if (is_float($value)) {
8970 $value = sprintf('%F', $value);
8971 }
8972 $out .= ' /'.$key.' '.$value;
8973 }
8974 $fontdir = false;
8975 if (!TCPDF_STATIC::empty_string($font['file'])) {
8976 // A stream containing a TrueType font
8977 $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
8978 $fontdir = $this->FontFiles[$font['file']]['fontdir'];
8979 }
8980 $out .= ' >>';
8981 $out .= "\n".'endobj';
8982 $this->_out($out);
8983 if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
8984 $this->_newobj();
8985 // Embed CIDToGIDMap
8986 // A specification of the mapping from CIDs to glyph indices
8987 // search and get CTG font file to embedd
8988 $ctgfile = strtolower($font['ctg']);
8989 // search and get ctg font file to embedd
8990 $fontfile = '';
8991 // search files on various directories
8992 if (($fontdir !== false) AND file_exists($fontdir.$ctgfile)) {
8993 $fontfile = $fontdir.$ctgfile;
8994 } elseif (file_exists(TCPDF_FONTS::_getfontpath().$ctgfile)) {
8995 $fontfile = TCPDF_FONTS::_getfontpath().$ctgfile;
8996 } elseif (file_exists($ctgfile)) {
8997 $fontfile = $ctgfile;
8998 }
8999 if (TCPDF_STATIC::empty_string($fontfile)) {
9000 $this->Error('Font file not found: '.$ctgfile);
9001 }
9002 $stream = $this->_getrawstream(file_get_contents($fontfile));
9003 $out = '<< /Length '.strlen($stream).'';
9004 if (substr($fontfile, -2) == '.z') { // check file extension
9005 // Decompresses data encoded using the public-domain
9006 // zlib/deflate compression method, reproducing the
9007 // original text or binary data
9008 $out .= ' /Filter /FlateDecode';
9009 }
9010 $out .= ' >>';
9011 $out .= ' stream'."\n".$stream."\n".'endstream';
9012 $out .= "\n".'endobj';
9013 $this->_out($out);
9014 }
9015 }
9016
9025 protected function _putcidfont0($font) {
9026 $cidoffset = 0;
9027 if (!isset($font['cw'][1])) {
9028 $cidoffset = 31;
9029 }
9030 if (isset($font['cidinfo']['uni2cid'])) {
9031 // convert unicode to cid.
9032 $uni2cid = $font['cidinfo']['uni2cid'];
9033 $cw = array();
9034 foreach ($font['cw'] as $uni => $width) {
9035 if (isset($uni2cid[$uni])) {
9036 $cw[($uni2cid[$uni] + $cidoffset)] = $width;
9037 } elseif ($uni < 256) {
9038 $cw[$uni] = $width;
9039 } // else unknown character
9040 }
9041 $font = array_merge($font, array('cw' => $cw));
9042 }
9043 $name = $font['name'];
9044 $enc = $font['enc'];
9045 if ($enc) {
9046 $longname = $name.'-'.$enc;
9047 } else {
9048 $longname = $name;
9049 }
9050 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
9051 $out .= '<</Type /Font';
9052 $out .= ' /Subtype /Type0';
9053 $out .= ' /BaseFont /'.$longname;
9054 $out .= ' /Name /F'.$font['i'];
9055 if ($enc) {
9056 $out .= ' /Encoding /'.$enc;
9057 }
9058 $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
9059 $out .= ' >>';
9060 $out .= "\n".'endobj';
9061 $this->_out($out);
9062 $oid = $this->_newobj();
9063 $out = '<</Type /Font';
9064 $out .= ' /Subtype /CIDFontType0';
9065 $out .= ' /BaseFont /'.$name;
9066 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9067 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9068 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9069 $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
9070 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
9071 $out .= ' /DW '.$font['dw'];
9072 $out .= "\n".TCPDF_FONTS::_putfontwidths($font, $cidoffset);
9073 $out .= ' >>';
9074 $out .= "\n".'endobj';
9075 $this->_out($out);
9076 $this->_newobj();
9077 $s = '<</Type /FontDescriptor /FontName /'.$name;
9078 foreach ($font['desc'] as $k => $v) {
9079 if ($k != 'Style') {
9080 if (is_float($v)) {
9081 $v = sprintf('%F', $v);
9082 }
9083 $s .= ' /'.$k.' '.$v.'';
9084 }
9085 }
9086 $s .= '>>';
9087 $s .= "\n".'endobj';
9088 $this->_out($s);
9089 }
9090
9095 protected function _putimages() {
9096 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9097 foreach ($this->imagekeys as $file) {
9098 $info = $this->getImageBuffer($file);
9099 // set object for alternate images array
9100 if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
9101 $altoid = $this->_newobj();
9102 $out = '[';
9103 foreach ($info['altimgs'] as $altimage) {
9104 if (isset($this->xobjects['I'.$altimage[0]]['n'])) {
9105 $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R';
9106 $out .= ' /DefaultForPrinting';
9107 if ($altimage[1] === true) {
9108 $out .= ' true';
9109 } else {
9110 $out .= ' false';
9111 }
9112 $out .= ' >>';
9113 }
9114 }
9115 $out .= ' ]';
9116 $out .= "\n".'endobj';
9117 $this->_out($out);
9118 }
9119 // set image object
9120 $oid = $this->_newobj();
9121 $this->xobjects['I'.$info['i']] = array('n' => $oid);
9122 $this->setImageSubBuffer($file, 'n', $this->n);
9123 $out = '<</Type /XObject';
9124 $out .= ' /Subtype /Image';
9125 $out .= ' /Width '.$info['w'];
9126 $out .= ' /Height '.$info['h'];
9127 if (array_key_exists('masked', $info)) {
9128 $out .= ' /SMask '.($this->n - 1).' 0 R';
9129 }
9130 // set color space
9131 $icc = false;
9132 if (isset($info['icc']) AND ($info['icc'] !== false)) {
9133 // ICC Colour Space
9134 $icc = true;
9135 $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]';
9136 } elseif ($info['cs'] == 'Indexed') {
9137 // Indexed Colour Space
9138 $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
9139 } else {
9140 // Device Colour Space
9141 $out .= ' /ColorSpace /'.$info['cs'];
9142 }
9143 if ($info['cs'] == 'DeviceCMYK') {
9144 $out .= ' /Decode [1 0 1 0 1 0 1 0]';
9145 }
9146 $out .= ' /BitsPerComponent '.$info['bpc'];
9147 if (isset($altoid) AND ($altoid > 0)) {
9148 // reference to alternate images dictionary
9149 $out .= ' /Alternates '.$altoid.' 0 R';
9150 }
9151 if (isset($info['exurl']) AND !empty($info['exurl'])) {
9152 // external stream
9153 $out .= ' /Length 0';
9154 $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
9155 if (isset($info['f'])) {
9156 $out .= ' /FFilter /'.$info['f'];
9157 }
9158 $out .= ' >>';
9159 $out .= ' stream'."\n".'endstream';
9160 } else {
9161 if (isset($info['f'])) {
9162 $out .= ' /Filter /'.$info['f'];
9163 }
9164 if (isset($info['parms'])) {
9165 $out .= ' '.$info['parms'];
9166 }
9167 if (isset($info['trns']) AND is_array($info['trns'])) {
9168 $trns = '';
9169 $count_info = count($info['trns']);
9170 for ($i=0; $i < $count_info; ++$i) {
9171 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
9172 }
9173 $out .= ' /Mask ['.$trns.']';
9174 }
9175 $stream = $this->_getrawstream($info['data']);
9176 $out .= ' /Length '.strlen($stream).' >>';
9177 $out .= ' stream'."\n".$stream."\n".'endstream';
9178 }
9179 $out .= "\n".'endobj';
9180 $this->_out($out);
9181 if ($icc) {
9182 // ICC colour profile
9183 $this->_newobj();
9184 $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
9185 $icc = $this->_getrawstream($icc);
9186 $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9187 } elseif ($info['cs'] == 'Indexed') {
9188 // colour palette
9189 $this->_newobj();
9190 $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
9191 $pal = $this->_getrawstream($pal);
9192 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
9193 }
9194 }
9195 }
9196
9204 protected function _putxobjects() {
9205 foreach ($this->xobjects as $key => $data) {
9206 if (isset($data['outdata'])) {
9207 $stream = str_replace($this->epsmarker, '', trim($data['outdata']));
9208 $out = $this->_getobj($data['n'])."\n";
9209 $out .= '<<';
9210 $out .= ' /Type /XObject';
9211 $out .= ' /Subtype /Form';
9212 $out .= ' /FormType 1';
9213 if ($this->compress) {
9214 $stream = gzcompress($stream);
9215 $out .= ' /Filter /FlateDecode';
9216 }
9217 $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));
9218 $out .= ' /Matrix [1 0 0 1 0 0]';
9219 $out .= ' /Resources <<';
9220 $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9221 if (!$this->pdfa_mode) {
9222 // transparency
9223 if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
9224 $out .= ' /ExtGState <<';
9225 foreach ($data['extgstates'] as $k => $extgstate) {
9226 if (isset($this->extgstates[$k]['name'])) {
9227 $out .= ' /'.$this->extgstates[$k]['name'];
9228 } else {
9229 $out .= ' /GS'.$k;
9230 }
9231 $out .= ' '.$this->extgstates[$k]['n'].' 0 R';
9232 }
9233 $out .= ' >>';
9234 }
9235 if (isset($data['gradients']) AND !empty($data['gradients'])) {
9236 $gp = '';
9237 $gs = '';
9238 foreach ($data['gradients'] as $id => $grad) {
9239 // gradient patterns
9240 $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R';
9241 // gradient shadings
9242 $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R';
9243 }
9244 $out .= ' /Pattern <<'.$gp.' >>';
9245 $out .= ' /Shading <<'.$gs.' >>';
9246 }
9247 }
9248 // spot colors
9249 if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
9250 $out .= ' /ColorSpace <<';
9251 foreach ($data['spot_colors'] as $name => $color) {
9252 $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R';
9253 }
9254 $out .= ' >>';
9255 }
9256 // fonts
9257 if (!empty($data['fonts'])) {
9258 $out .= ' /Font <<';
9259 foreach ($data['fonts'] as $fontkey => $fontid) {
9260 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9261 }
9262 $out .= ' >>';
9263 }
9264 // images or nested xobjects
9265 if (!empty($data['images']) OR !empty($data['xobjects'])) {
9266 $out .= ' /XObject <<';
9267 foreach ($data['images'] as $imgid) {
9268 $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
9269 }
9270 foreach ($data['xobjects'] as $sub_id => $sub_objid) {
9271 $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
9272 }
9273 $out .= ' >>';
9274 }
9275 $out .= ' >>'; //end resources
9276 if (isset($data['group']) AND ($data['group'] !== false)) {
9277 // set transparency group
9278 $out .= ' /Group << /Type /Group /S /Transparency';
9279 if (is_array($data['group'])) {
9280 if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
9281 $out .= ' /CS /'.$data['group']['CS'];
9282 }
9283 if (isset($data['group']['I'])) {
9284 $out .= ' /I /'.($data['group']['I']===true?'true':'false');
9285 }
9286 if (isset($data['group']['K'])) {
9287 $out .= ' /K /'.($data['group']['K']===true?'true':'false');
9288 }
9289 }
9290 $out .= ' >>';
9291 }
9292 $stream = $this->_getrawstream($stream, $data['n']);
9293 $out .= ' /Length '.strlen($stream);
9294 $out .= ' >>';
9295 $out .= ' stream'."\n".$stream."\n".'endstream';
9296 $out .= "\n".'endobj';
9297 $this->_out($out);
9298 }
9299 }
9300 }
9301
9307 protected function _putspotcolors() {
9308 foreach ($this->spot_colors as $name => $color) {
9309 $this->_newobj();
9310 $this->spot_colors[$name]['n'] = $this->n;
9311 $out = '[/Separation /'.str_replace(' ', '#20', $name);
9312 $out .= ' /DeviceCMYK <<';
9313 $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
9314 $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
9315 $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
9316 $out .= "\n".'endobj';
9317 $this->_out($out);
9318 }
9319 }
9320
9327 protected function _getxobjectdict() {
9328 $out = '';
9329 foreach ($this->xobjects as $id => $objid) {
9330 $out .= ' /'.$id.' '.$objid['n'].' 0 R';
9331 }
9332 return $out;
9333 }
9334
9339 protected function _putresourcedict() {
9340 $out = $this->_getobj(2)."\n";
9341 $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9342 $out .= ' /Font <<';
9343 foreach ($this->fontkeys as $fontkey) {
9344 $font = $this->getFontBuffer($fontkey);
9345 $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
9346 }
9347 $out .= ' >>';
9348 $out .= ' /XObject <<';
9349 $out .= $this->_getxobjectdict();
9350 $out .= ' >>';
9351 // layers
9352 if (!empty($this->pdflayers)) {
9353 $out .= ' /Properties <<';
9354 foreach ($this->pdflayers as $layer) {
9355 $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
9356 }
9357 $out .= ' >>';
9358 }
9359 if (!$this->pdfa_mode) {
9360 // transparency
9361 if (isset($this->extgstates) AND !empty($this->extgstates)) {
9362 $out .= ' /ExtGState <<';
9363 foreach ($this->extgstates as $k => $extgstate) {
9364 if (isset($extgstate['name'])) {
9365 $out .= ' /'.$extgstate['name'];
9366 } else {
9367 $out .= ' /GS'.$k;
9368 }
9369 $out .= ' '.$extgstate['n'].' 0 R';
9370 }
9371 $out .= ' >>';
9372 }
9373 if (isset($this->gradients) AND !empty($this->gradients)) {
9374 $gp = '';
9375 $gs = '';
9376 foreach ($this->gradients as $id => $grad) {
9377 // gradient patterns
9378 $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
9379 // gradient shadings
9380 $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
9381 }
9382 $out .= ' /Pattern <<'.$gp.' >>';
9383 $out .= ' /Shading <<'.$gs.' >>';
9384 }
9385 }
9386 // spot colors
9387 if (isset($this->spot_colors) AND !empty($this->spot_colors)) {
9388 $out .= ' /ColorSpace <<';
9389 foreach ($this->spot_colors as $color) {
9390 $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
9391 }
9392 $out .= ' >>';
9393 }
9394 $out .= ' >>';
9395 $out .= "\n".'endobj';
9396 $this->_out($out);
9397 }
9398
9403 protected function _putresources() {
9404 $this->_putextgstates();
9405 $this->_putocg();
9406 $this->_putfonts();
9407 $this->_putimages();
9408 $this->_putspotcolors();
9409 $this->_putshaders();
9410 $this->_putxobjects();
9411 $this->_putresourcedict();
9412 $this->_putdests();
9413 $this->_putEmbeddedFiles();
9414 $this->_putannotsobjs();
9415 $this->_putjavascript();
9416 $this->_putbookmarks();
9417 $this->_putencryption();
9418 }
9419
9426 protected function _putinfo() {
9427 $oid = $this->_newobj();
9428 $out = '<<';
9429 // store current isunicode value
9430 $prev_isunicode = $this->isunicode;
9431 if ($this->docinfounicode) {
9432 $this->isunicode = true;
9433 }
9434 if (!TCPDF_STATIC::empty_string($this->title)) {
9435 // The document's title.
9436 $out .= ' /Title '.$this->_textstring($this->title, $oid);
9437 }
9438 if (!TCPDF_STATIC::empty_string($this->author)) {
9439 // The name of the person who created the document.
9440 $out .= ' /Author '.$this->_textstring($this->author, $oid);
9441 }
9442 if (!TCPDF_STATIC::empty_string($this->subject)) {
9443 // The subject of the document.
9444 $out .= ' /Subject '.$this->_textstring($this->subject, $oid);
9445 }
9446 if (!TCPDF_STATIC::empty_string($this->keywords)) {
9447 // Keywords associated with the document.
9448 $out .= ' /Keywords '.$this->_textstring($this->keywords.' TCPDF', $oid);
9449 }
9450 if (!TCPDF_STATIC::empty_string($this->creator)) {
9451 // 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.
9452 $out .= ' /Creator '.$this->_textstring($this->creator, $oid);
9453 }
9454 // restore previous isunicode value
9455 $this->isunicode = $prev_isunicode;
9456 // default producer
9457 $out .= ' /Producer '.$this->_textstring(TCPDF_STATIC::getTCPDFProducer(), $oid);
9458 // The date and time the document was created, in human-readable form
9459 $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp);
9460 // The date and time the document was most recently modified, in human-readable form
9461 $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp);
9462 // A name object indicating whether the document has been modified to include trapping information
9463 $out .= ' /Trapped /False';
9464 $out .= ' >>';
9465 $out .= "\n".'endobj';
9466 $this->_out($out);
9467 return $oid;
9468 }
9469
9477 public function setExtraXMP($xmp) {
9478 $this->custom_xmp = $xmp;
9479 }
9480
9487 protected function _putXMP() {
9488 $oid = $this->_newobj();
9489 // store current isunicode value
9490 $prev_isunicode = $this->isunicode;
9491 $this->isunicode = true;
9492 $prev_encrypted = $this->encrypted;
9493 $this->encrypted = false;
9494 // set XMP data
9495 $xmp = '<?xpacket begin="'.TCPDF_FONTS::unichr(0xfeff, $this->isunicode).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
9496 $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";
9497 $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
9498 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
9499 $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
9500 $xmp .= "\t\t\t".'<dc:title>'."\n";
9501 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9502 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->title).'</rdf:li>'."\n";
9503 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9504 $xmp .= "\t\t\t".'</dc:title>'."\n";
9505 $xmp .= "\t\t\t".'<dc:creator>'."\n";
9506 $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
9507 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->author).'</rdf:li>'."\n";
9508 $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
9509 $xmp .= "\t\t\t".'</dc:creator>'."\n";
9510 $xmp .= "\t\t\t".'<dc:description>'."\n";
9511 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9512 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->subject).'</rdf:li>'."\n";
9513 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9514 $xmp .= "\t\t\t".'</dc:description>'."\n";
9515 $xmp .= "\t\t\t".'<dc:subject>'."\n";
9516 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9517 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->keywords).' TCPDF</rdf:li>'."\n";
9518 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9519 $xmp .= "\t\t\t".'</dc:subject>'."\n";
9520 $xmp .= "\t\t".'</rdf:Description>'."\n";
9521 // convert doc creation date format
9522 $dcdate = TCPDF_STATIC::getFormattedDate($this->doc_creation_timestamp);
9523 $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2);
9524 $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2);
9525 $doccreationdate .= '+'.substr($dcdate, 15, 2).':'.substr($dcdate, 18, 2);
9526 $doccreationdate = TCPDF_STATIC::_escapeXML($doccreationdate);
9527 // convert doc modification date format
9528 $dmdate = TCPDF_STATIC::getFormattedDate($this->doc_modification_timestamp);
9529 $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2);
9530 $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2);
9531 $docmoddate .= '+'.substr($dmdate, 15, 2).':'.substr($dmdate, 18, 2);
9532 $docmoddate = TCPDF_STATIC::_escapeXML($docmoddate);
9533 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
9534 $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n";
9535 $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n";
9536 $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n";
9537 $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n";
9538 $xmp .= "\t\t".'</rdf:Description>'."\n";
9539 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
9540 $xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC::_escapeXML($this->keywords).' TCPDF</pdf:Keywords>'."\n";
9541 $xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC::_escapeXML(TCPDF_STATIC::getTCPDFProducer()).'</pdf:Producer>'."\n";
9542 $xmp .= "\t\t".'</rdf:Description>'."\n";
9543 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
9544 $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);
9545 $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
9546 $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
9547 $xmp .= "\t\t".'</rdf:Description>'."\n";
9548 if ($this->pdfa_mode) {
9549 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
9550 $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n";
9551 $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
9552 $xmp .= "\t\t".'</rdf:Description>'."\n";
9553 }
9554 // XMP extension schemas
9555 $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";
9556 $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
9557 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9558 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9559 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
9560 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
9561 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
9562 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9563 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9564 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
9565 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
9566 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
9567 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9568 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9569 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9570 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9571 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
9572 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9573 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9574 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9575 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9576 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9577 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9578 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9579 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
9580 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
9581 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
9582 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9583 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9584 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9585 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9586 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
9587 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
9588 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
9589 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9590 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9591 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9592 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
9593 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
9594 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9595 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9596 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9597 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9598 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
9599 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
9600 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9601 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9602 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9603 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9604 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9605 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9606 $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
9607 $xmp .= "\t\t".'</rdf:Description>'."\n";
9608 $xmp .= "\t".'</rdf:RDF>'."\n";
9609 $xmp .= $this->custom_xmp;
9610 $xmp .= '</x:xmpmeta>'."\n";
9611 $xmp .= '<?xpacket end="w"?>';
9612 $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
9613 // restore previous isunicode value
9614 $this->isunicode = $prev_isunicode;
9615 $this->encrypted = $prev_encrypted;
9616 $this->_out($out);
9617 return $oid;
9618 }
9619
9625 protected function _putcatalog() {
9626 // put XMP
9627 $xmpobj = $this->_putXMP();
9628 // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile
9629 if ($this->pdfa_mode OR $this->force_srgb) {
9630 $iccobj = $this->_newobj();
9631 $icc = file_get_contents(dirname(__FILE__).'/include/sRGB.icc');
9632 $filter = '';
9633 if ($this->compress) {
9634 $filter = ' /Filter /FlateDecode';
9635 $icc = gzcompress($icc);
9636 }
9637 $icc = $this->_getrawstream($icc);
9638 $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9639 }
9640 // start catalog
9641 $oid = $this->_newobj();
9642 $out = '<< /Type /Catalog';
9643 $out .= ' /Version /'.$this->PDFVersion;
9644 //$out .= ' /Extensions <<>>';
9645 $out .= ' /Pages 1 0 R';
9646 //$out .= ' /PageLabels ' //...;
9647 $out .= ' /Names <<';
9648 if ((!$this->pdfa_mode) AND !empty($this->n_js)) {
9649 $out .= ' /JavaScript '.$this->n_js;
9650 }
9651 if (!empty($this->efnames)) {
9652 $out .= ' /EmbeddedFiles <</Names [';
9653 foreach ($this->efnames AS $fn => $fref) {
9654 $out .= ' '.$this->_datastring($fn).' '.$fref;
9655 }
9656 $out .= ' ]>>';
9657 }
9658 $out .= ' >>';
9659 if (!empty($this->dests)) {
9660 $out .= ' /Dests '.($this->n_dests).' 0 R';
9661 }
9662 $out .= $this->_putviewerpreferences();
9663 if (isset($this->LayoutMode) AND (!TCPDF_STATIC::empty_string($this->LayoutMode))) {
9664 $out .= ' /PageLayout /'.$this->LayoutMode;
9665 }
9666 if (isset($this->PageMode) AND (!TCPDF_STATIC::empty_string($this->PageMode))) {
9667 $out .= ' /PageMode /'.$this->PageMode;
9668 }
9669 if (count($this->outlines) > 0) {
9670 $out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
9671 $out .= ' /PageMode /UseOutlines';
9672 }
9673 //$out .= ' /Threads []';
9674 if ($this->ZoomMode == 'fullpage') {
9675 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
9676 } elseif ($this->ZoomMode == 'fullwidth') {
9677 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
9678 } elseif ($this->ZoomMode == 'real') {
9679 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
9680 } elseif (!is_string($this->ZoomMode)) {
9681 $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100));
9682 }
9683 //$out .= ' /AA <<>>';
9684 //$out .= ' /URI <<>>';
9685 $out .= ' /Metadata '.$xmpobj.' 0 R';
9686 //$out .= ' /StructTreeRoot <<>>';
9687 //$out .= ' /MarkInfo <<>>';
9688 if (isset($this->l['a_meta_language'])) {
9689 $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
9690 }
9691 //$out .= ' /SpiderInfo <<>>';
9692 // set OutputIntent to sRGB IEC61966-2.1 if required
9693 if ($this->pdfa_mode OR $this->force_srgb) {
9694 $out .= ' /OutputIntents [<<';
9695 $out .= ' /Type /OutputIntent';
9696 $out .= ' /S /GTS_PDFA1';
9697 $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9698 $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9699 $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
9700 $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9701 $out .= ' /DestOutputProfile '.$iccobj.' 0 R';
9702 $out .= ' >>]';
9703 }
9704 //$out .= ' /PieceInfo <<>>';
9705 if (!empty($this->pdflayers)) {
9706 $lyrobjs = '';
9707 $lyrobjs_print = '';
9708 $lyrobjs_view = '';
9709 foreach ($this->pdflayers as $layer) {
9710 $lyrobjs .= ' '.$layer['objid'].' 0 R';
9711 if ($layer['print']) {
9712 $lyrobjs_print .= ' '.$layer['objid'].' 0 R';
9713 }
9714 if ($layer['view']) {
9715 $lyrobjs_view .= ' '.$layer['objid'].' 0 R';
9716 }
9717 }
9718 $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
9719 $out .= ' /D <<';
9720 $out .= ' /Name '.$this->_textstring('Layers', $oid);
9721 $out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
9722 $out .= ' /BaseState /ON';
9723 $out .= ' /ON ['.$lyrobjs_print.']';
9724 $out .= ' /OFF ['.$lyrobjs_view.']';
9725 $out .= ' /Intent /View';
9726 $out .= ' /AS [';
9727 $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
9728 $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
9729 $out .= ' ]';
9730 $out .= ' /Order ['.$lyrobjs.']';
9731 $out .= ' /ListMode /AllPages';
9732 //$out .= ' /RBGroups ['..']';
9733 //$out .= ' /Locked ['..']';
9734 $out .= ' >>';
9735 $out .= ' >>';
9736 }
9737 // AcroForm
9738 if (!empty($this->form_obj_id) OR ($this->sign AND isset($this->signature_data['cert_type']))) {
9739 $out .= ' /AcroForm <<';
9740 $objrefs = '';
9741 if ($this->sign AND isset($this->signature_data['cert_type'])) {
9742 // set reference for signature object
9743 $objrefs .= $this->sig_obj_id.' 0 R';
9744 }
9745 if (!empty($this->empty_signature_appearance)) {
9746 foreach ($this->empty_signature_appearance as $esa) {
9747 // set reference for empty signature objects
9748 $objrefs .= ' '.$esa['objid'].' 0 R';
9749 }
9750 }
9751 if (!empty($this->form_obj_id)) {
9752 foreach($this->form_obj_id as $objid) {
9753 $objrefs .= ' '.$objid.' 0 R';
9754 }
9755 }
9756 $out .= ' /Fields ['.$objrefs.']';
9757 // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
9758 $out .= ' /NeedAppearances false';
9759 if ($this->sign AND isset($this->signature_data['cert_type'])) {
9760 if ($this->signature_data['cert_type'] > 0) {
9761 $out .= ' /SigFlags 3';
9762 } else {
9763 $out .= ' /SigFlags 1';
9764 }
9765 }
9766 //$out .= ' /CO ';
9767 if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
9768 $out .= ' /DR <<';
9769 $out .= ' /Font <<';
9770 foreach ($this->annotation_fonts as $fontkey => $fontid) {
9771 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9772 }
9773 $out .= ' >> >>';
9774 }
9775 $font = $this->getFontBuffer('helvetica');
9776 $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
9777 $out .= ' /Q '.(($this->rtl)?'2':'0');
9778 //$out .= ' /XFA ';
9779 $out .= ' >>';
9780 // signatures
9781 if ($this->sign AND isset($this->signature_data['cert_type'])) {
9782 if ($this->signature_data['cert_type'] > 0) {
9783 $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
9784 } else {
9785 $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
9786 }
9787 }
9788 }
9789 //$out .= ' /Legal <<>>';
9790 //$out .= ' /Requirements []';
9791 //$out .= ' /Collection <<>>';
9792 //$out .= ' /NeedsRendering true';
9793 $out .= ' >>';
9794 $out .= "\n".'endobj';
9795 $this->_out($out);
9796 return $oid;
9797 }
9798
9806 protected function _putviewerpreferences() {
9808 $out = ' /ViewerPreferences <<';
9809 if ($this->rtl) {
9810 $out .= ' /Direction /R2L';
9811 } else {
9812 $out .= ' /Direction /L2R';
9813 }
9814 if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) {
9815 $out .= ' /HideToolbar true';
9816 }
9817 if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) {
9818 $out .= ' /HideMenubar true';
9819 }
9820 if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) {
9821 $out .= ' /HideWindowUI true';
9822 }
9823 if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) {
9824 $out .= ' /FitWindow true';
9825 }
9826 if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) {
9827 $out .= ' /CenterWindow true';
9828 }
9829 if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) {
9830 $out .= ' /DisplayDocTitle true';
9831 }
9832 if (isset($vp['NonFullScreenPageMode'])) {
9833 $out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode'];
9834 }
9835 if (isset($vp['ViewArea'])) {
9836 $out .= ' /ViewArea /'.$vp['ViewArea'];
9837 }
9838 if (isset($vp['ViewClip'])) {
9839 $out .= ' /ViewClip /'.$vp['ViewClip'];
9840 }
9841 if (isset($vp['PrintArea'])) {
9842 $out .= ' /PrintArea /'.$vp['PrintArea'];
9843 }
9844 if (isset($vp['PrintClip'])) {
9845 $out .= ' /PrintClip /'.$vp['PrintClip'];
9846 }
9847 if (isset($vp['PrintScaling'])) {
9848 $out .= ' /PrintScaling /'.$vp['PrintScaling'];
9849 }
9850 if (isset($vp['Duplex']) AND (!TCPDF_STATIC::empty_string($vp['Duplex']))) {
9851 $out .= ' /Duplex /'.$vp['Duplex'];
9852 }
9853 if (isset($vp['PickTrayByPDFSize'])) {
9854 if ($vp['PickTrayByPDFSize']) {
9855 $out .= ' /PickTrayByPDFSize true';
9856 } else {
9857 $out .= ' /PickTrayByPDFSize false';
9858 }
9859 }
9860 if (isset($vp['PrintPageRange'])) {
9861 $PrintPageRangeNum = '';
9862 foreach ($vp['PrintPageRange'] as $k => $v) {
9863 $PrintPageRangeNum .= ' '.($v - 1).'';
9864 }
9865 $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
9866 }
9867 if (isset($vp['NumCopies'])) {
9868 $out .= ' /NumCopies '.intval($vp['NumCopies']);
9869 }
9870 $out .= ' >>';
9871 return $out;
9872 }
9873
9878 protected function _putheader() {
9879 $this->_out('%PDF-'.$this->PDFVersion);
9880 $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
9881 }
9882
9887 protected function _enddoc() {
9888 if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
9889 // save subset chars of the previous font
9890 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
9891 }
9892 $this->state = 1;
9893 $this->_putheader();
9894 $this->_putpages();
9895 $this->_putresources();
9896 // empty signature fields
9897 if (!empty($this->empty_signature_appearance)) {
9898 foreach ($this->empty_signature_appearance as $key => $esa) {
9899 // widget annotation for empty signature
9900 $out = $this->_getobj($esa['objid'])."\n";
9901 $out .= '<< /Type /Annot';
9902 $out .= ' /Subtype /Widget';
9903 $out .= ' /Rect ['.$esa['rect'].']';
9904 $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page
9905 $out .= ' /F 4';
9906 $out .= ' /FT /Sig';
9907 $signame = $esa['name'].sprintf(' [%03d]', ($key + 1));
9908 $out .= ' /T '.$this->_textstring($signame, $esa['objid']);
9909 $out .= ' /Ff 0';
9910 $out .= ' >>';
9911 $out .= "\n".'endobj';
9912 $this->_out($out);
9913 }
9914 }
9915 // Signature
9916 if ($this->sign AND isset($this->signature_data['cert_type'])) {
9917 // widget annotation for signature
9918 $out = $this->_getobj($this->sig_obj_id)."\n";
9919 $out .= '<< /Type /Annot';
9920 $out .= ' /Subtype /Widget';
9921 $out .= ' /Rect ['.$this->signature_appearance['rect'].']';
9922 $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
9923 $out .= ' /F 4';
9924 $out .= ' /FT /Sig';
9925 $out .= ' /T '.$this->_textstring($this->signature_appearance['name'], $this->sig_obj_id);
9926 $out .= ' /Ff 0';
9927 $out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
9928 $out .= ' >>';
9929 $out .= "\n".'endobj';
9930 $this->_out($out);
9931 // signature
9932 $this->_putsignature();
9933 }
9934 // Info
9935 $objid_info = $this->_putinfo();
9936 // Catalog
9937 $objid_catalog = $this->_putcatalog();
9938 // Cross-ref
9939 $o = $this->bufferlen;
9940 // XREF section
9941 $this->_out('xref');
9942 $this->_out('0 '.($this->n + 1));
9943 $this->_out('0000000000 65535 f ');
9944 $freegen = ($this->n + 2);
9945 for ($i=1; $i <= $this->n; ++$i) {
9946 if (!isset($this->offsets[$i]) AND ($i > 1)) {
9947 $this->_out(sprintf('0000000000 %05d f ', $freegen));
9948 ++$freegen;
9949 } else {
9950 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
9951 }
9952 }
9953 // TRAILER
9954 $out = 'trailer'."\n";
9955 $out .= '<<';
9956 $out .= ' /Size '.($this->n + 1);
9957 $out .= ' /Root '.$objid_catalog.' 0 R';
9958 $out .= ' /Info '.$objid_info.' 0 R';
9959 if ($this->encrypted) {
9960 $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
9961 }
9962 $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
9963 $out .= ' >>';
9964 $this->_out($out);
9965 $this->_out('startxref');
9966 $this->_out($o);
9967 $this->_out('%%EOF');
9968 $this->state = 3; // end-of-doc
9969 if ($this->diskcache) {
9970 // remove temporary files used for images
9971 foreach ($this->imagekeys as $key) {
9972 // remove temporary files
9973 unlink($this->images[$key]);
9974 }
9975 foreach ($this->fontkeys as $key) {
9976 // remove temporary files
9977 unlink($this->fonts[$key]);
9978 }
9979 }
9980 }
9981
9989 protected function _beginpage($orientation='', $format='') {
9990 ++$this->page;
9991 $this->pageobjects[$this->page] = array();
9992 $this->setPageBuffer($this->page, '');
9993 // initialize array for graphics tranformation positions inside a page buffer
9994 $this->transfmrk[$this->page] = array();
9995 $this->state = 2;
9996 if (TCPDF_STATIC::empty_string($orientation)) {
9997 if (isset($this->CurOrientation)) {
9998 $orientation = $this->CurOrientation;
9999 } elseif ($this->fwPt > $this->fhPt) {
10000 // landscape
10001 $orientation = 'L';
10002 } else {
10003 // portrait
10004 $orientation = 'P';
10005 }
10006 }
10007 if (TCPDF_STATIC::empty_string($format)) {
10008 $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
10009 $this->setPageOrientation($orientation);
10010 } else {
10011 $this->setPageFormat($format, $orientation);
10012 }
10013 if ($this->rtl) {
10014 $this->x = $this->w - $this->rMargin;
10015 } else {
10016 $this->x = $this->lMargin;
10017 }
10018 $this->y = $this->tMargin;
10019 if (isset($this->newpagegroup[$this->page])) {
10020 // start a new group
10021 $this->currpagegroup = $this->newpagegroup[$this->page];
10022 $this->pagegroups[$this->currpagegroup] = 1;
10023 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
10024 ++$this->pagegroups[$this->currpagegroup];
10025 }
10026 }
10027
10032 protected function _endpage() {
10033 $this->setVisibility('all');
10034 $this->state = 1;
10035 }
10036
10042 protected function _newobj() {
10043 $this->_out($this->_getobj());
10044 return $this->n;
10045 }
10046
10054 protected function _getobj($objid='') {
10055 if ($objid === '') {
10056 ++$this->n;
10057 $objid = $this->n;
10058 }
10059 $this->offsets[$objid] = $this->bufferlen;
10060 $this->pageobjects[$this->page][] = $objid;
10061 return $objid.' 0 obj';
10062 }
10063
10071 protected function _dounderline($x, $y, $txt) {
10072 $w = $this->GetStringWidth($txt);
10073 return $this->_dounderlinew($x, $y, $w);
10074 }
10075
10084 protected function _dounderlinew($x, $y, $w) {
10085 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10086 return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
10087 }
10088
10096 protected function _dolinethrough($x, $y, $txt) {
10097 $w = $this->GetStringWidth($txt);
10098 return $this->_dolinethroughw($x, $y, $w);
10099 }
10100
10109 protected function _dolinethroughw($x, $y, $w) {
10110 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10111 return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew);
10112 }
10113
10122 protected function _dooverline($x, $y, $txt) {
10123 $w = $this->GetStringWidth($txt);
10124 return $this->_dooverlinew($x, $y, $w);
10125 }
10126
10135 protected function _dooverlinew($x, $y, $w) {
10136 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10137 return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
10138
10139 }
10140
10148 protected function _datastring($s, $n=0) {
10149 if ($n == 0) {
10150 $n = $this->n;
10151 }
10152 $s = $this->_encrypt_data($n, $s);
10153 return '('. TCPDF_STATIC::_escape($s).')';
10154 }
10155
10162 public function setDocCreationTimestamp($time) {
10163 if (is_string($time)) {
10164 $time = TCPDF_STATIC::getTimestamp($time);
10165 }
10166 $this->doc_creation_timestamp = intval($time);
10167 }
10168
10175 public function setDocModificationTimestamp($time) {
10176 if (is_string($time)) {
10177 $time = TCPDF_STATIC::getTimestamp($time);
10178 }
10179 $this->doc_modification_timestamp = intval($time);
10180 }
10181
10188 public function getDocCreationTimestamp() {
10190 }
10191
10200 }
10201
10210 protected function _datestring($n=0, $timestamp=0) {
10211 if ((empty($timestamp)) OR ($timestamp < 0)) {
10213 }
10215 }
10216
10224 protected function _textstring($s, $n=0) {
10225 if ($this->isunicode) {
10226 //Convert string to UTF-16BE
10227 $s = TCPDF_FONTS::UTF8ToUTF16BE($s, true, $this->isunicode, $this->CurrentFont);
10228 }
10229 return $this->_datastring($s, $n);
10230 }
10231
10240 protected function _escapetext($s) {
10241 if ($this->isunicode) {
10242 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
10243 $s = TCPDF_FONTS::UTF8ToLatin1($s, $this->isunicode, $this->CurrentFont);
10244 } else {
10245 //Convert string to UTF-16BE and reverse RTL language
10246 $s = TCPDF_FONTS::utf8StrRev($s, false, $this->tmprtl, $this->isunicode, $this->CurrentFont);
10247 }
10248 }
10249 return TCPDF_STATIC::_escape($s);
10250 }
10251
10260 protected function _getrawstream($s, $n=0) {
10261 if ($n <= 0) {
10262 // default to current object
10263 $n = $this->n;
10264 }
10265 return $this->_encrypt_data($n, $s);
10266 }
10267
10275 protected function _getstream($s, $n=0) {
10276 return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream';
10277 }
10278
10286 protected function _putstream($s, $n=0) {
10287 $this->_out($this->_getstream($s, $n));
10288 }
10289
10295 protected function _out($s) {
10296 if ($this->state == 2) {
10297 if ($this->inxobj) {
10298 // we are inside an XObject template
10299 $this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
10300 } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
10301 // puts data before page footer
10302 $pagebuff = $this->getPageBuffer($this->page);
10303 $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
10304 $footer = substr($pagebuff, -$this->footerlen[$this->page]);
10305 $this->setPageBuffer($this->page, $page.$s."\n".$footer);
10306 // update footer position
10307 $this->footerpos[$this->page] += strlen($s."\n");
10308 } else {
10309 // set page data
10310 $this->setPageBuffer($this->page, $s."\n", true);
10311 }
10312 } elseif ($this->state > 0) {
10313 // set general data
10314 $this->setBuffer($s."\n");
10315 }
10316 }
10317
10324 public function setHeaderFont($font) {
10325 $this->header_font = $font;
10326 }
10327
10334 public function getHeaderFont() {
10335 return $this->header_font;
10336 }
10337
10344 public function setFooterFont($font) {
10345 $this->footer_font = $font;
10346 }
10347
10354 public function getFooterFont() {
10355 return $this->footer_font;
10356 }
10357
10364 public function setLanguageArray($language) {
10365 $this->l = $language;
10366 if (isset($this->l['a_meta_dir'])) {
10367 $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
10368 } else {
10369 $this->rtl = false;
10370 }
10371 }
10372
10377 public function getPDFData() {
10378 if ($this->state < 3) {
10379 $this->Close();
10380 }
10381 return $this->buffer;
10382 }
10383
10396 public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
10397 if (isset($url[1]) AND ($url[0] == '#') AND is_numeric($url[1])) {
10398 // convert url to internal link
10399 $lnkdata = explode(',', $url);
10400 if (isset($lnkdata[0])) {
10401 $page = intval(substr($lnkdata[0], 1));
10402 if (empty($page) OR ($page <= 0)) {
10404 }
10405 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
10406 $lnky = floatval($lnkdata[1]);
10407 } else {
10408 $lnky = 0;
10409 }
10410 $url = $this->AddLink();
10411 $this->SetLink($url, $lnky, $page);
10412 }
10413 }
10414 // store current settings
10415 $prevcolor = $this->fgcolor;
10416 $prevstyle = $this->FontStyle;
10417 if (empty($color)) {
10418 $this->SetTextColorArray($this->htmlLinkColorArray);
10419 } else {
10420 $this->SetTextColorArray($color);
10421 }
10422 if ($style == -1) {
10423 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
10424 } else {
10425 $this->SetFont('', $this->FontStyle.$style);
10426 }
10427 $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
10428 // restore settings
10429 $this->SetFont('', $prevstyle);
10430 $this->SetTextColorArray($prevcolor);
10431 return $ret;
10432 }
10433
10441 public function pixelsToUnits($px) {
10442 return ($px / ($this->imgscale * $this->k));
10443 }
10444
10452 public function unhtmlentities($text_to_convert) {
10453 return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
10454 }
10455
10456 // ENCRYPTION METHODS ----------------------------------
10457
10467 protected function _objectkey($n) {
10468 $objkey = $this->encryptdata['key'].pack('VXxx', $n);
10469 if ($this->encryptdata['mode'] == 2) { // AES-128
10470 // AES padding
10471 $objkey .= "\x73\x41\x6C\x54"; // sAlT
10472 }
10473 $objkey = substr(TCPDF_STATIC::_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
10474 $objkey = substr($objkey, 0, 16);
10475 return $objkey;
10476 }
10477
10487 protected function _encrypt_data($n, $s) {
10488 if (!$this->encrypted) {
10489 return $s;
10490 }
10491 switch ($this->encryptdata['mode']) {
10492 case 0: // RC4-40
10493 case 1: { // RC4-128
10494 $s = TCPDF_STATIC::_RC4($this->_objectkey($n), $s, $this->last_enc_key, $this->last_enc_key_c);
10495 break;
10496 }
10497 case 2: { // AES-128
10498 $s = TCPDF_STATIC::_AES($this->_objectkey($n), $s);
10499 break;
10500 }
10501 case 3: { // AES-256
10502 $s = TCPDF_STATIC::_AES($this->encryptdata['key'], $s);
10503 break;
10504 }
10505 }
10506 return $s;
10507 }
10508
10515 protected function _putencryption() {
10516 if (!$this->encrypted) {
10517 return;
10518 }
10519 $this->encryptdata['objid'] = $this->_newobj();
10520 $out = '<<';
10521 if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
10522 $this->encryptdata['Filter'] = 'Standard';
10523 }
10524 $out .= ' /Filter /'.$this->encryptdata['Filter'];
10525 if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
10526 $out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
10527 }
10528 if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
10529 $this->encryptdata['V'] = 1;
10530 }
10531 // V is a code specifying the algorithm to be used in encrypting and decrypting the document
10532 $out .= ' /V '.$this->encryptdata['V'];
10533 if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
10534 // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
10535 $out .= ' /Length '.$this->encryptdata['Length'];
10536 } else {
10537 $out .= ' /Length 40';
10538 }
10539 if ($this->encryptdata['V'] >= 4) {
10540 if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
10541 $this->encryptdata['StmF'] = 'Identity';
10542 }
10543 if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
10544 // The name of the crypt filter that shall be used when decrypting all strings in the document.
10545 $this->encryptdata['StrF'] = 'Identity';
10546 }
10547 // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
10548 if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
10549 $out .= ' /CF <<';
10550 $out .= ' /'.$this->encryptdata['StmF'].' <<';
10551 $out .= ' /Type /CryptFilter';
10552 if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
10553 // The method used
10554 $out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
10555 if ($this->encryptdata['pubkey']) {
10556 $out .= ' /Recipients [';
10557 foreach ($this->encryptdata['Recipients'] as $rec) {
10558 $out .= ' <'.$rec.'>';
10559 }
10560 $out .= ' ]';
10561 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
10562 $out .= ' /EncryptMetadata false';
10563 } else {
10564 $out .= ' /EncryptMetadata true';
10565 }
10566 }
10567 } else {
10568 $out .= ' /CFM /None';
10569 }
10570 if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
10571 // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
10572 $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
10573 } else {
10574 $out .= ' /AuthEvent /DocOpen';
10575 }
10576 if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
10577 // The bit length of the encryption key.
10578 $out .= ' /Length '.$this->encryptdata['CF']['Length'];
10579 }
10580 $out .= ' >> >>';
10581 }
10582 // The name of the crypt filter that shall be used by default when decrypting streams.
10583 $out .= ' /StmF /'.$this->encryptdata['StmF'];
10584 // The name of the crypt filter that shall be used when decrypting all strings in the document.
10585 $out .= ' /StrF /'.$this->encryptdata['StrF'];
10586 if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
10587 // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
10588 $out .= ' /EFF /'.$this->encryptdata[''];
10589 }
10590 }
10591 // Additional encryption dictionary entries for the standard security handler
10592 if ($this->encryptdata['pubkey']) {
10593 if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
10594 $out .= ' /Recipients [';
10595 foreach ($this->encryptdata['Recipients'] as $rec) {
10596 $out .= ' <'.$rec.'>';
10597 }
10598 $out .= ' ]';
10599 }
10600 } else {
10601 $out .= ' /R';
10602 if ($this->encryptdata['V'] == 5) { // AES-256
10603 $out .= ' 5';
10604 $out .= ' /OE ('.TCPDF_STATIC::_escape($this->encryptdata['OE']).')';
10605 $out .= ' /UE ('.TCPDF_STATIC::_escape($this->encryptdata['UE']).')';
10606 $out .= ' /Perms ('.TCPDF_STATIC::_escape($this->encryptdata['perms']).')';
10607 } elseif ($this->encryptdata['V'] == 4) { // AES-128
10608 $out .= ' 4';
10609 } elseif ($this->encryptdata['V'] < 2) { // RC-40
10610 $out .= ' 2';
10611 } else { // RC-128
10612 $out .= ' 3';
10613 }
10614 $out .= ' /O ('.TCPDF_STATIC::_escape($this->encryptdata['O']).')';
10615 $out .= ' /U ('.TCPDF_STATIC::_escape($this->encryptdata['U']).')';
10616 $out .= ' /P '.$this->encryptdata['P'];
10617 if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
10618 $out .= ' /EncryptMetadata false';
10619 } else {
10620 $out .= ' /EncryptMetadata true';
10621 }
10622 }
10623 $out .= ' >>';
10624 $out .= "\n".'endobj';
10625 $this->_out($out);
10626 }
10627
10635 protected function _Uvalue() {
10636 if ($this->encryptdata['mode'] == 0) { // RC4-40
10637 return TCPDF_STATIC::_RC4($this->encryptdata['key'], TCPDF_STATIC::$enc_padding, $this->last_enc_key, $this->last_enc_key_c);
10638 } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
10639 $tmp = TCPDF_STATIC::_md5_16(TCPDF_STATIC::$enc_padding.$this->encryptdata['fileid']);
10640 $enc = TCPDF_STATIC::_RC4($this->encryptdata['key'], $tmp, $this->last_enc_key, $this->last_enc_key_c);
10641 $len = strlen($tmp);
10642 for ($i = 1; $i <= 19; ++$i) {
10643 $ek = '';
10644 for ($j = 0; $j < $len; ++$j) {
10645 $ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i);
10646 }
10647 $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10648 }
10649 $enc .= str_repeat("\x00", 16);
10650 return substr($enc, 0, 32);
10651 } elseif ($this->encryptdata['mode'] == 3) { // AES-256
10653 // User Validation Salt
10654 $this->encryptdata['UVS'] = substr($seed, 0, 8);
10655 // User Key Salt
10656 $this->encryptdata['UKS'] = substr($seed, 8, 16);
10657 return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
10658 }
10659 }
10660
10668 protected function _UEvalue() {
10669 $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
10670 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
10671 return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
10672 }
10673
10681 protected function _Ovalue() {
10682 if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
10683 $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['owner_password']);
10684 if ($this->encryptdata['mode'] > 0) {
10685 for ($i = 0; $i < 50; ++$i) {
10686 $tmp = TCPDF_STATIC::_md5_16($tmp);
10687 }
10688 }
10689 $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
10690 $enc = TCPDF_STATIC::_RC4($owner_key, $this->encryptdata['user_password'], $this->last_enc_key, $this->last_enc_key_c);
10691 if ($this->encryptdata['mode'] > 0) {
10692 $len = strlen($owner_key);
10693 for ($i = 1; $i <= 19; ++$i) {
10694 $ek = '';
10695 for ($j = 0; $j < $len; ++$j) {
10696 $ek .= chr(ord($owner_key[$j]) ^ $i);
10697 }
10698 $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10699 }
10700 }
10701 return $enc;
10702 } elseif ($this->encryptdata['mode'] == 3) { // AES-256
10704 // Owner Validation Salt
10705 $this->encryptdata['OVS'] = substr($seed, 0, 8);
10706 // Owner Key Salt
10707 $this->encryptdata['OKS'] = substr($seed, 8, 16);
10708 return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
10709 }
10710 }
10711
10719 protected function _OEvalue() {
10720 $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
10721 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
10722 return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
10723 }
10724
10733 protected function _fixAES256Password($password) {
10734 $psw = ''; // password to be returned
10735 $psw_array = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($password, $this->isunicode, $this->CurrentFont), $password, $this->rtl, $this->isunicode, $this->CurrentFont);
10736 foreach ($psw_array as $c) {
10737 $psw .= TCPDF_FONTS::unichr($c, $this->isunicode);
10738 }
10739 return substr($psw, 0, 127);
10740 }
10741
10748 protected function _generateencryptionkey() {
10749 $keybytelen = ($this->encryptdata['Length'] / 8);
10750 if (!$this->encryptdata['pubkey']) { // standard mode
10751 if ($this->encryptdata['mode'] == 3) { // AES-256
10752 // generate 256 bit random key
10753 $this->encryptdata['key'] = substr(hash('sha256', TCPDF_STATIC::getRandomSeed(), true), 0, $keybytelen);
10754 // truncate passwords
10755 $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
10756 $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
10757 // Compute U value
10758 $this->encryptdata['U'] = $this->_Uvalue();
10759 // Compute UE value
10760 $this->encryptdata['UE'] = $this->_UEvalue();
10761 // Compute O value
10762 $this->encryptdata['O'] = $this->_Ovalue();
10763 // Compute OE value
10764 $this->encryptdata['OE'] = $this->_OEvalue();
10765 // Compute P value
10766 $this->encryptdata['P'] = $this->encryptdata['protection'];
10767 // Computing the encryption dictionary's Perms (permissions) value
10768 $perms = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
10769 $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
10770 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
10771 $perms .= 'F';
10772 } else {
10773 $perms .= 'T';
10774 }
10775 $perms .= 'adb'; // bytes 9-11
10776 $perms .= 'nick'; // bytes 12-15
10777 $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB));
10778 $this->encryptdata['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->encryptdata['key'], $perms, MCRYPT_MODE_ECB, $iv);
10779 } else { // RC4-40, RC4-128, AES-128
10780 // Pad passwords
10781 $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10782 $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10783 // Compute O value
10784 $this->encryptdata['O'] = $this->_Ovalue();
10785 // get default permissions (reverse byte order)
10786 $permissions = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']);
10787 // Compute encryption key
10788 $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
10789 if ($this->encryptdata['mode'] > 0) {
10790 for ($i = 0; $i < 50; ++$i) {
10791 $tmp = TCPDF_STATIC::_md5_16(substr($tmp, 0, $keybytelen));
10792 }
10793 }
10794 $this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
10795 // Compute U value
10796 $this->encryptdata['U'] = $this->_Uvalue();
10797 // Compute P value
10798 $this->encryptdata['P'] = $this->encryptdata['protection'];
10799 }
10800 } else { // Public-Key mode
10801 // random 20-byte seed
10802 $seed = sha1(TCPDF_STATIC::getRandomSeed(), true);
10803 $recipient_bytes = '';
10804 foreach ($this->encryptdata['pubkeys'] as $pubkey) {
10805 // for each public certificate
10806 if (isset($pubkey['p'])) {
10807 $pkprotection = TCPDF_STATIC::getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
10808 } else {
10809 $pkprotection = $this->encryptdata['protection'];
10810 }
10811 // get default permissions (reverse byte order)
10812 $pkpermissions = TCPDF_STATIC::getEncPermissionsString($pkprotection);
10813 // envelope data
10814 $envelope = $seed.$pkpermissions;
10815 // write the envelope data to a temporary file
10816 $tempkeyfile = TCPDF_STATIC::getObjFilename('tmpkey');
10817 $f = fopen($tempkeyfile, 'wb');
10818 if (!$f) {
10819 $this->Error('Unable to create temporary key file: '.$tempkeyfile);
10820 }
10821 $envelope_length = strlen($envelope);
10822 fwrite($f, $envelope, $envelope_length);
10823 fclose($f);
10824 $tempencfile = TCPDF_STATIC::getObjFilename('tmpenc');
10825 if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) {
10826 $this->Error('Unable to encrypt the file: '.$tempkeyfile);
10827 }
10828 unlink($tempkeyfile);
10829 // read encryption signature
10830 $signature = file_get_contents($tempencfile, false, null, $envelope_length);
10831 unlink($tempencfile);
10832 // extract signature
10833 $signature = substr($signature, strpos($signature, 'Content-Disposition'));
10834 $tmparr = explode("\n\n", $signature);
10835 $signature = trim($tmparr[1]);
10836 unset($tmparr);
10837 // decode signature
10838 $signature = base64_decode($signature);
10839 // convert signature to hex
10840 $hexsignature = current(unpack('H*', $signature));
10841 // store signature on recipients array
10842 $this->encryptdata['Recipients'][] = $hexsignature;
10843 // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
10844 $recipient_bytes .= $signature;
10845 }
10846 // calculate encryption key
10847 if ($this->encryptdata['mode'] == 3) { // AES-256
10848 $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
10849 } else { // RC4-40, RC4-128, AES-128
10850 $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
10851 }
10852 }
10853 }
10854
10869 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) {
10870 if ($this->pdfa_mode) {
10871 // encryption is not allowed in PDF/A mode
10872 return;
10873 }
10874 $this->encryptdata['protection'] = TCPDF_STATIC::getUserPermissionCode($permissions, $mode);
10875 if (($pubkeys !== null) AND (is_array($pubkeys))) {
10876 // public-key mode
10877 $this->encryptdata['pubkeys'] = $pubkeys;
10878 if ($mode == 0) {
10879 // public-Key Security requires at least 128 bit
10880 $mode = 1;
10881 }
10882 if (!function_exists('openssl_pkcs7_encrypt')) {
10883 $this->Error('Public-Key Security requires openssl library.');
10884 }
10885 // Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
10886 $this->encryptdata['pubkey'] = true;
10887 $this->encryptdata['Filter'] = 'Adobe.PubSec';
10888 $this->encryptdata['StmF'] = 'DefaultCryptFilter';
10889 $this->encryptdata['StrF'] = 'DefaultCryptFilter';
10890 } else {
10891 // standard mode (password mode)
10892 $this->encryptdata['pubkey'] = false;
10893 $this->encryptdata['Filter'] = 'Standard';
10894 $this->encryptdata['StmF'] = 'StdCF';
10895 $this->encryptdata['StrF'] = 'StdCF';
10896 }
10897 if ($mode > 1) { // AES
10898 if (!extension_loaded('mcrypt')) {
10899 $this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).');
10900 }
10901 if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
10902 $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
10903 }
10904 if (($mode == 3) AND !function_exists('hash')) {
10905 // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
10906 $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
10907 }
10908 }
10909 if ($owner_pass === null) {
10910 $owner_pass = md5(TCPDF_STATIC::getRandomSeed());
10911 }
10912 $this->encryptdata['user_password'] = $user_pass;
10913 $this->encryptdata['owner_password'] = $owner_pass;
10914 $this->encryptdata['mode'] = $mode;
10915 switch ($mode) {
10916 case 0: { // RC4 40 bit
10917 $this->encryptdata['V'] = 1;
10918 $this->encryptdata['Length'] = 40;
10919 $this->encryptdata['CF']['CFM'] = 'V2';
10920 break;
10921 }
10922 case 1: { // RC4 128 bit
10923 $this->encryptdata['V'] = 2;
10924 $this->encryptdata['Length'] = 128;
10925 $this->encryptdata['CF']['CFM'] = 'V2';
10926 if ($this->encryptdata['pubkey']) {
10927 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
10928 $this->encryptdata['Recipients'] = array();
10929 }
10930 break;
10931 }
10932 case 2: { // AES 128 bit
10933 $this->encryptdata['V'] = 4;
10934 $this->encryptdata['Length'] = 128;
10935 $this->encryptdata['CF']['CFM'] = 'AESV2';
10936 $this->encryptdata['CF']['Length'] = 128;
10937 if ($this->encryptdata['pubkey']) {
10938 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
10939 $this->encryptdata['Recipients'] = array();
10940 }
10941 break;
10942 }
10943 case 3: { // AES 256 bit
10944 $this->encryptdata['V'] = 5;
10945 $this->encryptdata['Length'] = 256;
10946 $this->encryptdata['CF']['CFM'] = 'AESV3';
10947 $this->encryptdata['CF']['Length'] = 256;
10948 if ($this->encryptdata['pubkey']) {
10949 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
10950 $this->encryptdata['Recipients'] = array();
10951 }
10952 break;
10953 }
10954 }
10955 $this->encrypted = true;
10956 $this->encryptdata['fileid'] = TCPDF_STATIC::convertHexStringToString($this->file_id);
10957 $this->_generateencryptionkey();
10958 }
10959
10960 // END OF ENCRYPTION FUNCTIONS -------------------------
10961
10962 // START TRANSFORMATIONS SECTION -----------------------
10963
10972 public function StartTransform() {
10973 if ($this->state != 2) {
10974 return;
10975 }
10976 $this->_out('q');
10977 if ($this->inxobj) {
10978 // we are inside an XObject template
10979 $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
10980 } else {
10981 $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
10982 }
10984 $this->transfmatrix[$this->transfmatrix_key] = array();
10985 }
10986
10995 public function StopTransform() {
10996 if ($this->state != 2) {
10997 return;
10998 }
10999 $this->_out('Q');
11000 if (isset($this->transfmatrix[$this->transfmatrix_key])) {
11001 array_pop($this->transfmatrix[$this->transfmatrix_key]);
11003 }
11004 if ($this->inxobj) {
11005 // we are inside an XObject template
11006 array_pop($this->xobjects[$this->xobjid]['transfmrk']);
11007 } else {
11008 array_pop($this->transfmrk[$this->page]);
11009 }
11010 }
11020 public function ScaleX($s_x, $x='', $y='') {
11021 $this->Scale($s_x, 100, $x, $y);
11022 }
11023
11033 public function ScaleY($s_y, $x='', $y='') {
11034 $this->Scale(100, $s_y, $x, $y);
11035 }
11036
11046 public function ScaleXY($s, $x='', $y='') {
11047 $this->Scale($s, $s, $x, $y);
11048 }
11049
11060 public function Scale($s_x, $s_y, $x='', $y='') {
11061 if ($x === '') {
11062 $x = $this->x;
11063 }
11064 if ($y === '') {
11065 $y = $this->y;
11066 }
11067 if (($s_x == 0) OR ($s_y == 0)) {
11068 $this->Error('Please do not use values equal to zero for scaling');
11069 }
11070 $y = ($this->h - $y) * $this->k;
11071 $x *= $this->k;
11072 //calculate elements of transformation matrix
11073 $s_x /= 100;
11074 $s_y /= 100;
11075 $tm = array();
11076 $tm[0] = $s_x;
11077 $tm[1] = 0;
11078 $tm[2] = 0;
11079 $tm[3] = $s_y;
11080 $tm[4] = $x * (1 - $s_x);
11081 $tm[5] = $y * (1 - $s_y);
11082 //scale the coordinate system
11083 $this->Transform($tm);
11084 }
11085
11093 public function MirrorH($x='') {
11094 $this->Scale(-100, 100, $x);
11095 }
11096
11104 public function MirrorV($y='') {
11105 $this->Scale(100, -100, '', $y);
11106 }
11107
11116 public function MirrorP($x='',$y='') {
11117 $this->Scale(-100, -100, $x, $y);
11118 }
11119
11129 public function MirrorL($angle=0, $x='',$y='') {
11130 $this->Scale(-100, 100, $x, $y);
11131 $this->Rotate(-2*($angle-90), $x, $y);
11132 }
11133
11141 public function TranslateX($t_x) {
11142 $this->Translate($t_x, 0);
11143 }
11144
11152 public function TranslateY($t_y) {
11153 $this->Translate(0, $t_y);
11154 }
11155
11164 public function Translate($t_x, $t_y) {
11165 //calculate elements of transformation matrix
11166 $tm = array();
11167 $tm[0] = 1;
11168 $tm[1] = 0;
11169 $tm[2] = 0;
11170 $tm[3] = 1;
11171 $tm[4] = $t_x * $this->k;
11172 $tm[5] = -$t_y * $this->k;
11173 //translate the coordinate system
11174 $this->Transform($tm);
11175 }
11176
11186 public function Rotate($angle, $x='', $y='') {
11187 if ($x === '') {
11188 $x = $this->x;
11189 }
11190 if ($y === '') {
11191 $y = $this->y;
11192 }
11193 $y = ($this->h - $y) * $this->k;
11194 $x *= $this->k;
11195 //calculate elements of transformation matrix
11196 $tm = array();
11197 $tm[0] = cos(deg2rad($angle));
11198 $tm[1] = sin(deg2rad($angle));
11199 $tm[2] = -$tm[1];
11200 $tm[3] = $tm[0];
11201 $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
11202 $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
11203 //rotate the coordinate system around ($x,$y)
11204 $this->Transform($tm);
11205 }
11206
11216 public function SkewX($angle_x, $x='', $y='') {
11217 $this->Skew($angle_x, 0, $x, $y);
11218 }
11219
11229 public function SkewY($angle_y, $x='', $y='') {
11230 $this->Skew(0, $angle_y, $x, $y);
11231 }
11232
11243 public function Skew($angle_x, $angle_y, $x='', $y='') {
11244 if ($x === '') {
11245 $x = $this->x;
11246 }
11247 if ($y === '') {
11248 $y = $this->y;
11249 }
11250 if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
11251 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
11252 }
11253 $x *= $this->k;
11254 $y = ($this->h - $y) * $this->k;
11255 //calculate elements of transformation matrix
11256 $tm = array();
11257 $tm[0] = 1;
11258 $tm[1] = tan(deg2rad($angle_y));
11259 $tm[2] = tan(deg2rad($angle_x));
11260 $tm[3] = 1;
11261 $tm[4] = -$tm[2] * $y;
11262 $tm[5] = -$tm[1] * $x;
11263 //skew the coordinate system
11264 $this->Transform($tm);
11265 }
11266
11274 protected function Transform($tm) {
11275 if ($this->state != 2) {
11276 return;
11277 }
11278 $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
11279 // add tranformation matrix
11280 $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
11281 // update transformation mark
11282 if ($this->inxobj) {
11283 // we are inside an XObject template
11284 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
11285 $key = key($this->xobjects[$this->xobjid]['transfmrk']);
11286 $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
11287 }
11288 } elseif (end($this->transfmrk[$this->page]) !== false) {
11289 $key = key($this->transfmrk[$this->page]);
11290 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
11291 }
11292 }
11293
11294 // END TRANSFORMATIONS SECTION -------------------------
11295
11296 // START GRAPHIC FUNCTIONS SECTION ---------------------
11297 // The following section is based on the code provided by David Hernandez Sanz
11298
11306 public function SetLineWidth($width) {
11307 //Set line width
11308 $this->LineWidth = $width;
11309 $this->linestyleWidth = sprintf('%F w', ($width * $this->k));
11310 if ($this->state == 2) {
11311 $this->_out($this->linestyleWidth);
11312 }
11313 }
11314
11322 public function GetLineWidth() {
11323 return $this->LineWidth;
11324 }
11325
11349 public function SetLineStyle($style, $ret=false) {
11350 $s = ''; // string to be returned
11351 if (!is_array($style)) {
11352 return;
11353 }
11354 if (isset($style['width'])) {
11355 $this->LineWidth = $style['width'];
11356 $this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k));
11357 $s .= $this->linestyleWidth.' ';
11358 }
11359 if (isset($style['cap'])) {
11360 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
11361 if (isset($ca[$style['cap']])) {
11362 $this->linestyleCap = $ca[$style['cap']].' J';
11363 $s .= $this->linestyleCap.' ';
11364 }
11365 }
11366 if (isset($style['join'])) {
11367 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
11368 if (isset($ja[$style['join']])) {
11369 $this->linestyleJoin = $ja[$style['join']].' j';
11370 $s .= $this->linestyleJoin.' ';
11371 }
11372 }
11373 if (isset($style['dash'])) {
11374 $dash_string = '';
11375 if ($style['dash']) {
11376 if (preg_match('/^.+,/', $style['dash']) > 0) {
11377 $tab = explode(',', $style['dash']);
11378 } else {
11379 $tab = array($style['dash']);
11380 }
11381 $dash_string = '';
11382 foreach ($tab as $i => $v) {
11383 if ($i) {
11384 $dash_string .= ' ';
11385 }
11386 $dash_string .= sprintf('%F', $v);
11387 }
11388 }
11389 if (!isset($style['phase']) OR !$style['dash']) {
11390 $style['phase'] = 0;
11391 }
11392 $this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']);
11393 $s .= $this->linestyleDash.' ';
11394 }
11395 if (isset($style['color'])) {
11396 $s .= $this->SetDrawColorArray($style['color'], true).' ';
11397 }
11398 if (!$ret AND ($this->state == 2)) {
11399 $this->_out($s);
11400 }
11401 return $s;
11402 }
11403
11411 protected function _outPoint($x, $y) {
11412 if ($this->state == 2) {
11413 $this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k)));
11414 }
11415 }
11416
11425 protected function _outLine($x, $y) {
11426 if ($this->state == 2) {
11427 $this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k)));
11428 }
11429 }
11430
11441 protected function _outRect($x, $y, $w, $h, $op) {
11442 if ($this->state == 2) {
11443 $this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k), (($this->h - $y) * $this->k), ($w * $this->k), (-$h * $this->k), $op));
11444 }
11445 }
11446
11459 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
11460 if ($this->state == 2) {
11461 $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)));
11462 }
11463 }
11464
11475 protected function _outCurveV($x2, $y2, $x3, $y3) {
11476 if ($this->state == 2) {
11477 $this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11478 }
11479 }
11480
11491 protected function _outCurveY($x1, $y1, $x3, $y3) {
11492 if ($this->state == 2) {
11493 $this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11494 }
11495 }
11496
11508 public function Line($x1, $y1, $x2, $y2, $style=array()) {
11509 if ($this->state != 2) {
11510 return;
11511 }
11512 if (is_array($style)) {
11513 $this->SetLineStyle($style);
11514 }
11515 $this->_outPoint($x1, $y1);
11516 $this->_outLine($x2, $y2);
11517 $this->_out('S');
11518 }
11519
11538 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
11539 if ($this->state != 2) {
11540 return;
11541 }
11542 if (empty($style)) {
11543 $style = 'S';
11544 }
11545 if (!(strpos($style, 'F') === false) AND !empty($fill_color)) {
11546 // set background color
11547 $this->SetFillColorArray($fill_color);
11548 }
11549 if (!empty($border_style)) {
11550 if (isset($border_style['all']) AND !empty($border_style['all'])) {
11551 //set global style for border
11552 $this->SetLineStyle($border_style['all']);
11553 $border_style = array();
11554 } else {
11555 // remove stroke operator from style
11556 $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*' );
11557 if (isset($opnostroke[$style])) {
11558 $style = $opnostroke[$style];
11559 }
11560 }
11561 }
11562 if (!empty($style)) {
11564 $this->_outRect($x, $y, $w, $h, $op);
11565 }
11566 if (!empty($border_style)) {
11567 $border_style2 = array();
11568 foreach ($border_style as $line => $value) {
11569 $length = strlen($line);
11570 for ($i = 0; $i < $length; ++$i) {
11571 $border_style2[$line[$i]] = $value;
11572 }
11573 }
11574 $border_style = $border_style2;
11575 if (isset($border_style['L']) AND $border_style['L']) {
11576 $this->Line($x, $y, $x, $y + $h, $border_style['L']);
11577 }
11578 if (isset($border_style['T']) AND $border_style['T']) {
11579 $this->Line($x, $y, $x + $w, $y, $border_style['T']);
11580 }
11581 if (isset($border_style['R']) AND $border_style['R']) {
11582 $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
11583 }
11584 if (isset($border_style['B']) AND $border_style['B']) {
11585 $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
11586 }
11587 }
11588 }
11589
11609 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
11610 if ($this->state != 2) {
11611 return;
11612 }
11613 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11614 $this->SetFillColorArray($fill_color);
11615 }
11617 if ($line_style) {
11618 $this->SetLineStyle($line_style);
11619 }
11620 $this->_outPoint($x0, $y0);
11621 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11622 $this->_out($op);
11623 }
11624
11639 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
11640 if ($this->state != 2) {
11641 return;
11642 }
11643 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11644 $this->SetFillColorArray($fill_color);
11645 }
11647 if ($op == 'f') {
11648 $line_style = array();
11649 }
11650 if ($line_style) {
11651 $this->SetLineStyle($line_style);
11652 }
11653 $this->_outPoint($x0, $y0);
11654 foreach ($segments as $segment) {
11655 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
11656 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11657 }
11658 $this->_out($op);
11659 }
11660
11679 public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11680 if ($this->state != 2) {
11681 return;
11682 }
11683 if (TCPDF_STATIC::empty_string($ry) OR ($ry == 0)) {
11684 $ry = $rx;
11685 }
11686 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11687 $this->SetFillColorArray($fill_color);
11688 }
11690 if ($op == 'f') {
11691 $line_style = array();
11692 }
11693 if ($line_style) {
11694 $this->SetLineStyle($line_style);
11695 }
11696 $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
11697 $this->_out($op);
11698 }
11699
11720 protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
11721 $k = $this->k;
11722 if ($nc < 2) {
11723 $nc = 2;
11724 }
11725 $xmin = 2147483647;
11726 $ymin = 2147483647;
11727 $xmax = 0;
11728 $ymax = 0;
11729 if ($pie) {
11730 // center of the arc
11731 $this->_outPoint($xc, $yc);
11732 }
11733 $xang = deg2rad((float) $xang);
11734 $angs = deg2rad((float) $angs);
11735 $angf = deg2rad((float) $angf);
11736 if ($svg) {
11737 $as = $angs;
11738 $af = $angf;
11739 } else {
11740 $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
11741 $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
11742 }
11743 if ($as < 0) {
11744 $as += (2 * M_PI);
11745 }
11746 if ($af < 0) {
11747 $af += (2 * M_PI);
11748 }
11749 if ($ccw AND ($as > $af)) {
11750 // reverse rotation
11751 $as -= (2 * M_PI);
11752 } elseif (!$ccw AND ($as < $af)) {
11753 // reverse rotation
11754 $af -= (2 * M_PI);
11755 }
11756 $total_angle = ($af - $as);
11757 if ($nc < 2) {
11758 $nc = 2;
11759 }
11760 // total arcs to draw
11761 $nc *= (2 * abs($total_angle) / M_PI);
11762 $nc = round($nc) + 1;
11763 // angle of each arc
11764 $arcang = ($total_angle / $nc);
11765 // center point in PDF coordinates
11766 $x0 = $xc;
11767 $y0 = ($this->h - $yc);
11768 // starting angle
11769 $ang = $as;
11770 $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
11771 $cos_xang = cos($xang);
11772 $sin_xang = sin($xang);
11773 $cos_ang = cos($ang);
11774 $sin_ang = sin($ang);
11775 // first arc point
11776 $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11777 $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11778 // first Bezier control point
11779 $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11780 $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11781 if ($pie) {
11782 // line from center to arc starting point
11783 $this->_outLine($px1, $this->h - $py1);
11784 } elseif ($startpoint) {
11785 // arc starting point
11786 $this->_outPoint($px1, $this->h - $py1);
11787 }
11788 // draw arcs
11789 for ($i = 1; $i <= $nc; ++$i) {
11790 // starting angle
11791 $ang = $as + ($i * $arcang);
11792 if ($i == $nc) {
11793 $ang = $af;
11794 }
11795 $cos_ang = cos($ang);
11796 $sin_ang = sin($ang);
11797 // second arc point
11798 $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11799 $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11800 // second Bezier control point
11801 $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11802 $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11803 // draw arc
11804 $cx1 = ($px1 + $qx1);
11805 $cy1 = ($this->h - ($py1 + $qy1));
11806 $cx2 = ($px2 - $qx2);
11807 $cy2 = ($this->h - ($py2 - $qy2));
11808 $cx3 = $px2;
11809 $cy3 = ($this->h - $py2);
11810 $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
11811 // get bounding box coordinates
11812 $xmin = min($xmin, $cx1, $cx2, $cx3);
11813 $ymin = min($ymin, $cy1, $cy2, $cy3);
11814 $xmax = max($xmax, $cx1, $cx2, $cx3);
11815 $ymax = max($ymax, $cy1, $cy2, $cy3);
11816 // move to next point
11817 $px1 = $px2;
11818 $py1 = $py2;
11819 $qx1 = $qx2;
11820 $qy1 = $qy2;
11821 }
11822 if ($pie) {
11823 $this->_outLine($xc, $yc);
11824 // get bounding box coordinates
11825 $xmin = min($xmin, $xc);
11826 $ymin = min($ymin, $yc);
11827 $xmax = max($xmax, $xc);
11828 $ymax = max($ymax, $yc);
11829 }
11830 return array($xmin, $ymin, $xmax, $ymax);
11831 }
11832
11848 public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11849 $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
11850 }
11851
11866 public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
11867 $this->Polygon($p, $style, $line_style, $fill_color, false);
11868 }
11869
11885 public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
11886 if ($this->state != 2) {
11887 return;
11888 }
11889 $nc = count($p); // number of coordinates
11890 $np = $nc / 2; // number of points
11891 if ($closed) {
11892 // close polygon by adding the first 2 points at the end (one line)
11893 for ($i = 0; $i < 4; ++$i) {
11894 $p[$nc + $i] = $p[$i];
11895 }
11896 // copy style for the last added line
11897 if (isset($line_style[0])) {
11898 $line_style[$np] = $line_style[0];
11899 }
11900 $nc += 4;
11901 }
11902 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11903 $this->SetFillColorArray($fill_color);
11904 }
11906 if ($op == 'f') {
11907 $line_style = array();
11908 }
11909 $draw = true;
11910 if ($line_style) {
11911 if (isset($line_style['all'])) {
11912 $this->SetLineStyle($line_style['all']);
11913 } else {
11914 $draw = false;
11915 if ($op == 'B') {
11916 // draw fill
11917 $op = 'f';
11918 $this->_outPoint($p[0], $p[1]);
11919 for ($i = 2; $i < $nc; $i = $i + 2) {
11920 $this->_outLine($p[$i], $p[$i + 1]);
11921 }
11922 $this->_out($op);
11923 }
11924 // draw outline
11925 $this->_outPoint($p[0], $p[1]);
11926 for ($i = 2; $i < $nc; $i = $i + 2) {
11927 $line_num = ($i / 2) - 1;
11928 if (isset($line_style[$line_num])) {
11929 if ($line_style[$line_num] != 0) {
11930 if (is_array($line_style[$line_num])) {
11931 $this->_out('S');
11932 $this->SetLineStyle($line_style[$line_num]);
11933 $this->_outPoint($p[$i - 2], $p[$i - 1]);
11934 $this->_outLine($p[$i], $p[$i + 1]);
11935 $this->_out('S');
11936 $this->_outPoint($p[$i], $p[$i + 1]);
11937 } else {
11938 $this->_outLine($p[$i], $p[$i + 1]);
11939 }
11940 }
11941 } else {
11942 $this->_outLine($p[$i], $p[$i + 1]);
11943 }
11944 }
11945 $this->_out($op);
11946 }
11947 }
11948 if ($draw) {
11949 $this->_outPoint($p[0], $p[1]);
11950 for ($i = 2; $i < $nc; $i = $i + 2) {
11951 $this->_outLine($p[$i], $p[$i + 1]);
11952 }
11953 $this->_out($op);
11954 }
11955 }
11956
11986 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()) {
11987 if (3 > $ns) {
11988 $ns = 3;
11989 }
11990 if ($draw_circle) {
11991 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
11992 }
11993 $p = array();
11994 for ($i = 0; $i < $ns; ++$i) {
11995 $a = $angle + ($i * 360 / $ns);
11996 $a_rad = deg2rad((float) $a);
11997 $p[] = $x0 + ($r * sin($a_rad));
11998 $p[] = $y0 + ($r * cos($a_rad));
11999 }
12000 $this->Polygon($p, $style, $line_style, $fill_color);
12001 }
12002
12034 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()) {
12035 if ($nv < 2) {
12036 $nv = 2;
12037 }
12038 if ($draw_circle) {
12039 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12040 }
12041 $p2 = array();
12042 $visited = array();
12043 for ($i = 0; $i < $nv; ++$i) {
12044 $a = $angle + ($i * 360 / $nv);
12045 $a_rad = deg2rad((float) $a);
12046 $p2[] = $x0 + ($r * sin($a_rad));
12047 $p2[] = $y0 + ($r * cos($a_rad));
12048 $visited[] = false;
12049 }
12050 $p = array();
12051 $i = 0;
12052 do {
12053 $p[] = $p2[$i * 2];
12054 $p[] = $p2[($i * 2) + 1];
12055 $visited[$i] = true;
12056 $i += $ng;
12057 $i %= $nv;
12058 } while (!$visited[$i]);
12059 $this->Polygon($p, $style, $line_style, $fill_color);
12060 }
12061
12076 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12077 $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
12078 }
12079
12095 public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12096 if ($this->state != 2) {
12097 return;
12098 }
12099 if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
12100 // Not rounded
12101 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
12102 return;
12103 }
12104 // Rounded
12105 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12106 $this->SetFillColorArray($fill_color);
12107 }
12109 if ($op == 'f') {
12110 $border_style = array();
12111 }
12112 if ($border_style) {
12113 $this->SetLineStyle($border_style);
12114 }
12115 $MyArc = 4 / 3 * (sqrt(2) - 1);
12116 $this->_outPoint($x + $rx, $y);
12117 $xc = $x + $w - $rx;
12118 $yc = $y + $ry;
12119 $this->_outLine($xc, $y);
12120 if ($round_corner[0]) {
12121 $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
12122 } else {
12123 $this->_outLine($x + $w, $y);
12124 }
12125 $xc = $x + $w - $rx;
12126 $yc = $y + $h - $ry;
12127 $this->_outLine($x + $w, $yc);
12128 if ($round_corner[1]) {
12129 $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
12130 } else {
12131 $this->_outLine($x + $w, $y + $h);
12132 }
12133 $xc = $x + $rx;
12134 $yc = $y + $h - $ry;
12135 $this->_outLine($xc, $y + $h);
12136 if ($round_corner[2]) {
12137 $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
12138 } else {
12139 $this->_outLine($x, $y + $h);
12140 }
12141 $xc = $x + $rx;
12142 $yc = $y + $ry;
12143 $this->_outLine($x, $yc);
12144 if ($round_corner[3]) {
12145 $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
12146 } else {
12147 $this->_outLine($x, $y);
12148 $this->_outLine($x + $rx, $y);
12149 }
12150 $this->_out($op);
12151 }
12152
12165 public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
12166 // getting arrow direction angle
12167 // 0 deg angle is when both arms go along X axis. angle grows clockwise.
12168 $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
12169 if ($dir_angle < 0) {
12170 $dir_angle += (2 * M_PI);
12171 }
12172 $arm_angle = deg2rad($arm_angle);
12173 $sx1 = $x1;
12174 $sy1 = $y1;
12175 if ($head_style > 0) {
12176 // calculate the stopping point for the arrow shaft
12177 $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
12178 $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
12179 }
12180 // main arrow line / shaft
12181 $this->Line($x0, $y0, $sx1, $sy1);
12182 // left arrowhead arm tip
12183 $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
12184 $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
12185 // right arrowhead arm tip
12186 $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
12187 $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
12188 $mode = 'D';
12189 $style = array();
12190 switch ($head_style) {
12191 case 0: {
12192 // draw only arrowhead arms
12193 $mode = 'D';
12194 $style = array(1, 1, 0);
12195 break;
12196 }
12197 case 1: {
12198 // draw closed arrowhead, but no fill
12199 $mode = 'D';
12200 break;
12201 }
12202 case 2: {
12203 // closed and filled arrowhead
12204 $mode = 'DF';
12205 break;
12206 }
12207 case 3: {
12208 // filled arrowhead
12209 $mode = 'F';
12210 break;
12211 }
12212 }
12213 $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
12214 }
12215
12216 // END GRAPHIC FUNCTIONS SECTION -----------------------
12217
12230 public function setDestination($name, $y=-1, $page='', $x=-1) {
12231 // remove unsupported characters
12232 $name = TCPDF_STATIC::encodeNameObject($name);
12233 if (TCPDF_STATIC::empty_string($name)) {
12234 return false;
12235 }
12236 if ($y == -1) {
12237 $y = $this->GetY();
12238 } elseif ($y < 0) {
12239 $y = 0;
12240 } elseif ($y > $this->h) {
12241 $y = $this->h;
12242 }
12243 if ($x == -1) {
12244 $x = $this->GetX();
12245 } elseif ($x < 0) {
12246 $x = 0;
12247 } elseif ($x > $this->w) {
12248 $x = $this->w;
12249 }
12250 if (empty($page)) {
12251 $page = $this->PageNo();
12252 if (empty($page)) {
12253 return;
12254 }
12255 }
12256 $this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page);
12257 return $name;
12258 }
12259
12267 public function getDestination() {
12268 return $this->dests;
12269 }
12270
12277 protected function _putdests() {
12278 if (empty($this->dests)) {
12279 return;
12280 }
12281 $this->n_dests = $this->_newobj();
12282 $out = ' <<';
12283 foreach($this->dests as $name => $o) {
12284 $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)));
12285 }
12286 $out .= ' >>';
12287 $out .= "\n".'endobj';
12288 $this->_out($out);
12289 }
12290
12303 public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12304 $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link);
12305 }
12306
12320 public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12321 if ($level < 0) {
12322 $level = 0;
12323 }
12324 if (isset($this->outlines[0])) {
12325 $lastoutline = end($this->outlines);
12326 $maxlevel = $lastoutline['l'] + 1;
12327 } else {
12328 $maxlevel = 0;
12329 }
12330 if ($level > $maxlevel) {
12331 $level = $maxlevel;
12332 }
12333 if ($y == -1) {
12334 $y = $this->GetY();
12335 } elseif ($y < 0) {
12336 $y = 0;
12337 } elseif ($y > $this->h) {
12338 $y = $this->h;
12339 }
12340 if ($x == -1) {
12341 $x = $this->GetX();
12342 } elseif ($x < 0) {
12343 $x = 0;
12344 } elseif ($x > $this->w) {
12345 $x = $this->w;
12346 }
12347 if (empty($page)) {
12348 $page = $this->PageNo();
12349 if (empty($page)) {
12350 return;
12351 }
12352 }
12353 $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 's' => strtoupper($style), 'c' => $color, 'u' => $link);
12354 }
12355
12361 protected function sortBookmarks() {
12362 // get sorting columns
12363 $outline_p = array();
12364 $outline_y = array();
12365 foreach ($this->outlines as $key => $row) {
12366 $outline_p[$key] = $row['p'];
12367 $outline_k[$key] = $key;
12368 }
12369 // sort outlines by page and original position
12370 array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
12371 }
12372
12379 protected function _putbookmarks() {
12380 $nb = count($this->outlines);
12381 if ($nb == 0) {
12382 return;
12383 }
12384 // sort bookmarks
12385 $this->sortBookmarks();
12386 $lru = array();
12387 $level = 0;
12388 foreach ($this->outlines as $i => $o) {
12389 if ($o['l'] > 0) {
12390 $parent = $lru[($o['l'] - 1)];
12391 //Set parent and last pointers
12392 $this->outlines[$i]['parent'] = $parent;
12393 $this->outlines[$parent]['last'] = $i;
12394 if ($o['l'] > $level) {
12395 //Level increasing: set first pointer
12396 $this->outlines[$parent]['first'] = $i;
12397 }
12398 } else {
12399 $this->outlines[$i]['parent'] = $nb;
12400 }
12401 if (($o['l'] <= $level) AND ($i > 0)) {
12402 //Set prev and next pointers
12403 $prev = $lru[$o['l']];
12404 $this->outlines[$prev]['next'] = $i;
12405 $this->outlines[$i]['prev'] = $prev;
12406 }
12407 $lru[$o['l']] = $i;
12408 $level = $o['l'];
12409 }
12410 //Outline items
12411 $n = $this->n + 1;
12412 $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';
12413 foreach ($this->outlines as $i => $o) {
12414 $oid = $this->_newobj();
12415 // covert HTML title to string
12416 $title = preg_replace($nltags, "\n", $o['t']);
12417 $title = preg_replace("/[\r]+/si", '', $title);
12418 $title = preg_replace("/[\n]+/si", "\n", $title);
12419 $title = strip_tags($title);
12420 $title = $this->stringTrim($title);
12421 $out = '<</Title '.$this->_textstring($title, $oid);
12422 $out .= ' /Parent '.($n + $o['parent']).' 0 R';
12423 if (isset($o['prev'])) {
12424 $out .= ' /Prev '.($n + $o['prev']).' 0 R';
12425 }
12426 if (isset($o['next'])) {
12427 $out .= ' /Next '.($n + $o['next']).' 0 R';
12428 }
12429 if (isset($o['first'])) {
12430 $out .= ' /First '.($n + $o['first']).' 0 R';
12431 }
12432 if (isset($o['last'])) {
12433 $out .= ' /Last '.($n + $o['last']).' 0 R';
12434 }
12435 if (isset($o['u']) AND !empty($o['u'])) {
12436 // link
12437 if (is_string($o['u'])) {
12438 if ($o['u'][0] == '#') {
12439 // internal destination
12440 $out .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($o['u'], 1));
12441 } elseif ($o['u'][0] == '%') {
12442 // embedded PDF file
12443 $filename = basename(substr($o['u'], 1));
12444 $out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
12445 } elseif ($o['u'][0] == '*') {
12446 // embedded generic file
12447 $filename = basename(substr($o['u'], 1));
12448 $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});';
12449 $out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>';
12450 } else {
12451 // external URI link
12452 $out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>';
12453 }
12454 } elseif (isset($this->links[$o['u']])) {
12455 // internal link ID
12456 $l = $this->links[$o['u']];
12457 if (isset($this->page_obj_id[($l[0])])) {
12458 $out .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
12459 }
12460 }
12461 } elseif (isset($this->page_obj_id[($o['p'])])) {
12462 // link to a page
12463 $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)));
12464 }
12465 // set font style
12466 $style = 0;
12467 if (!empty($o['s'])) {
12468 // bold
12469 if (strpos($o['s'], 'B') !== false) {
12470 $style |= 2;
12471 }
12472 // oblique
12473 if (strpos($o['s'], 'I') !== false) {
12474 $style |= 1;
12475 }
12476 }
12477 $out .= sprintf(' /F %d', $style);
12478 // set bookmark color
12479 if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
12480 $color = array_values($o['c']);
12481 $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
12482 } else {
12483 // black
12484 $out .= ' /C [0.0 0.0 0.0]';
12485 }
12486 $out .= ' /Count 0'; // normally closed item
12487 $out .= ' >>';
12488 $out .= "\n".'endobj';
12489 $this->_out($out);
12490 }
12491 //Outline root
12492 $this->OutlineRoot = $this->_newobj();
12493 $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj');
12494 }
12495
12496 // --- JAVASCRIPT ------------------------------------------------------
12497
12505 public function IncludeJS($script) {
12506 $this->javascript .= $script;
12507 }
12508
12518 public function addJavascriptObject($script, $onload=false) {
12519 if ($this->pdfa_mode) {
12520 // javascript is not allowed in PDF/A mode
12521 return false;
12522 }
12523 ++$this->n;
12524 $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload);
12525 return $this->n;
12526 }
12527
12534 protected function _putjavascript() {
12535 if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) {
12536 return;
12537 }
12538 if (strpos($this->javascript, 'this.addField') > 0) {
12539 if (!$this->ur['enabled']) {
12540 //$this->setUserRights();
12541 }
12542 // the following two lines are used to avoid form fields duplication after saving
12543 // The addField method only works when releasing user rights (UR3)
12544 $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
12545 $jsb = "getField('tcpdfdocsaved').value='saved';";
12546 $this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
12547 }
12548 // name tree for javascript
12549 $this->n_js = '<< /Names [';
12550 if (!empty($this->javascript)) {
12551 $this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
12552 }
12553 if (!empty($this->js_objects)) {
12554 foreach ($this->js_objects as $key => $val) {
12555 if ($val['onload']) {
12556 $this->n_js .= ' (JS'.$key.') '.$key.' 0 R';
12557 }
12558 }
12559 }
12560 $this->n_js .= ' ] >>';
12561 // default Javascript object
12562 if (!empty($this->javascript)) {
12563 $obj_id = $this->_newobj();
12564 $out = '<< /S /JavaScript';
12565 $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id);
12566 $out .= ' >>';
12567 $out .= "\n".'endobj';
12568 $this->_out($out);
12569 }
12570 // additional Javascript objects
12571 if (!empty($this->js_objects)) {
12572 foreach ($this->js_objects as $key => $val) {
12573 $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
12574 $this->_out($out);
12575 }
12576 }
12577 }
12578
12592 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
12593 if ($this->rtl) {
12594 $x = $x - $w;
12595 }
12596 // the followind avoid fields duplication after saving the document
12597 $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {";
12598 $k = $this->k;
12599 $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";
12600 $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
12601 while (list($key, $val) = each($prop)) {
12602 if (strcmp(substr($key, -5), 'Color') == 0) {
12603 $val = TCPDF_COLORS::_JScolor($val);
12604 } else {
12605 $val = "'".$val."'";
12606 }
12607 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
12608 }
12609 if ($this->rtl) {
12610 $this->x -= $w;
12611 } else {
12612 $this->x += $w;
12613 }
12614 $this->javascript .= '}';
12615 }
12616
12617 // --- FORM FIELDS -----------------------------------------------------
12618
12619
12620
12628 public function setFormDefaultProp($prop=array()) {
12629 $this->default_form_prop = $prop;
12630 }
12631
12639 public function getFormDefaultProp() {
12641 }
12642
12657 public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12658 if ($x === '') {
12659 $x = $this->x;
12660 }
12661 if ($y === '') {
12662 $y = $this->y;
12663 }
12664 // check page for no-write regions and adapt page margins if necessary
12665 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12666 if ($js) {
12667 $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
12668 return;
12669 }
12670 // get default style
12671 $prop = array_merge($this->getFormDefaultProp(), $prop);
12672 // get annotation data
12673 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12674 // set default appearance stream
12675 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12676 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12677 $popt['da'] = $fontstyle;
12678 // build appearance stream
12679 $popt['ap'] = array();
12680 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12681 $text = '';
12682 if (isset($prop['value']) AND !empty($prop['value'])) {
12683 $text = $prop['value'];
12684 } elseif (isset($opt['v']) AND !empty($opt['v'])) {
12685 $text = $opt['v'];
12686 }
12687 $tmpid = $this->startTemplate($w, $h, false);
12688 $align = '';
12689 if (isset($popt['q'])) {
12690 switch ($popt['q']) {
12691 case 0: {
12692 $align = 'L';
12693 break;
12694 }
12695 case 1: {
12696 $align = 'C';
12697 break;
12698 }
12699 case 2: {
12700 $align = 'R';
12701 break;
12702 }
12703 default: {
12704 $align = '';
12705 break;
12706 }
12707 }
12708 }
12709 $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12710 $this->endTemplate();
12711 --$this->n;
12712 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12713 unset($this->xobjects[$tmpid]);
12714 $popt['ap']['n'] .= 'Q EMC';
12715 // merge options
12716 $opt = array_merge($popt, $opt);
12717 // remove some conflicting options
12718 unset($opt['bs']);
12719 // set remaining annotation data
12720 $opt['Subtype'] = 'Widget';
12721 $opt['ft'] = 'Tx';
12722 $opt['t'] = $name;
12723 // Additional annotation's parameters (check _putannotsobj() method):
12724 //$opt['f']
12725 //$opt['as']
12726 //$opt['bs']
12727 //$opt['be']
12728 //$opt['c']
12729 //$opt['border']
12730 //$opt['h']
12731 //$opt['mk'];
12732 //$opt['mk']['r']
12733 //$opt['mk']['bc'];
12734 //$opt['mk']['bg'];
12735 unset($opt['mk']['ca']);
12736 unset($opt['mk']['rc']);
12737 unset($opt['mk']['ac']);
12738 unset($opt['mk']['i']);
12739 unset($opt['mk']['ri']);
12740 unset($opt['mk']['ix']);
12741 unset($opt['mk']['if']);
12742 //$opt['mk']['if']['sw'];
12743 //$opt['mk']['if']['s'];
12744 //$opt['mk']['if']['a'];
12745 //$opt['mk']['if']['fb'];
12746 unset($opt['mk']['tp']);
12747 //$opt['tu']
12748 //$opt['tm']
12749 //$opt['ff']
12750 //$opt['v']
12751 //$opt['dv']
12752 //$opt['a']
12753 //$opt['aa']
12754 //$opt['q']
12755 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12756 if ($this->rtl) {
12757 $this->x -= $w;
12758 } else {
12759 $this->x += $w;
12760 }
12761 }
12762
12778 public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
12779 if ($x === '') {
12780 $x = $this->x;
12781 }
12782 if ($y === '') {
12783 $y = $this->y;
12784 }
12785 // check page for no-write regions and adapt page margins if necessary
12786 list($x, $y) = $this->checkPageRegions($w, $x, $y);
12787 if ($js) {
12788 $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
12789 return;
12790 }
12791 if (TCPDF_STATIC::empty_string($onvalue)) {
12792 $onvalue = 'On';
12793 }
12794 if ($checked) {
12795 $defval = $onvalue;
12796 } else {
12797 $defval = 'Off';
12798 }
12799 // set font
12800 $font = 'zapfdingbats';
12801 if ($this->pdfa_mode) {
12802 // all fonts must be embedded
12803 $font = 'pdfa'.$font;
12804 }
12805 $this->AddFont($font);
12806 $tmpfont = $this->getFontBuffer($font);
12807 // set data for parent group
12808 if (!isset($this->radiobutton_groups[$this->page])) {
12809 $this->radiobutton_groups[$this->page] = array();
12810 }
12811 if (!isset($this->radiobutton_groups[$this->page][$name])) {
12812 $this->radiobutton_groups[$this->page][$name] = array();
12813 ++$this->n;
12814 $this->radiobutton_groups[$this->page][$name]['n'] = $this->n;
12815 $this->radio_groups[] = $this->n;
12816 }
12817 $kid = ($this->n + 1);
12818 // save object ID to be added on Kids entry on parent object
12819 $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval);
12820 // get default style
12821 $prop = array_merge($this->getFormDefaultProp(), $prop);
12822 $prop['NoToggleToOff'] = 'true';
12823 $prop['Radio'] = 'true';
12824 $prop['borderStyle'] = 'inset';
12825 // get annotation data
12826 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12827 // set additional default options
12828 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
12829 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
12830 $popt['da'] = $fontstyle;
12831 // build appearance stream
12832 $popt['ap'] = array();
12833 $popt['ap']['n'] = array();
12834 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k);
12835 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
12836 $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);
12837 $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);
12838 if (!isset($popt['mk'])) {
12839 $popt['mk'] = array();
12840 }
12841 $popt['mk']['ca'] = '(l)';
12842 // merge options
12843 $opt = array_merge($popt, $opt);
12844 // set remaining annotation data
12845 $opt['Subtype'] = 'Widget';
12846 $opt['ft'] = 'Btn';
12847 if ($checked) {
12848 $opt['v'] = array('/'.$onvalue);
12849 $opt['as'] = $onvalue;
12850 } else {
12851 $opt['as'] = 'Off';
12852 }
12853 // store readonly flag
12854 if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) {
12855 $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false;
12856 }
12857 $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64);
12858 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
12859 if ($this->rtl) {
12860 $this->x -= $w;
12861 } else {
12862 $this->x += $w;
12863 }
12864 }
12865
12881 public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12882 if ($x === '') {
12883 $x = $this->x;
12884 }
12885 if ($y === '') {
12886 $y = $this->y;
12887 }
12888 // check page for no-write regions and adapt page margins if necessary
12889 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12890 if ($js) {
12891 $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
12892 $s = '';
12893 foreach ($values as $value) {
12894 if (is_array($value)) {
12895 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12896 } else {
12897 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12898 }
12899 }
12900 $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12901 return;
12902 }
12903 // get default style
12904 $prop = array_merge($this->getFormDefaultProp(), $prop);
12905 // get annotation data
12906 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12907 // set additional default values
12908 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12909 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12910 $popt['da'] = $fontstyle;
12911 // build appearance stream
12912 $popt['ap'] = array();
12913 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12914 $text = '';
12915 foreach($values as $item) {
12916 if (is_array($item)) {
12917 $text .= $item[1]."\n";
12918 } else {
12919 $text .= $item."\n";
12920 }
12921 }
12922 $tmpid = $this->startTemplate($w, $h, false);
12923 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12924 $this->endTemplate();
12925 --$this->n;
12926 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12927 unset($this->xobjects[$tmpid]);
12928 $popt['ap']['n'] .= 'Q EMC';
12929 // merge options
12930 $opt = array_merge($popt, $opt);
12931 // set remaining annotation data
12932 $opt['Subtype'] = 'Widget';
12933 $opt['ft'] = 'Ch';
12934 $opt['t'] = $name;
12935 $opt['opt'] = $values;
12936 unset($opt['mk']['ca']);
12937 unset($opt['mk']['rc']);
12938 unset($opt['mk']['ac']);
12939 unset($opt['mk']['i']);
12940 unset($opt['mk']['ri']);
12941 unset($opt['mk']['ix']);
12942 unset($opt['mk']['if']);
12943 unset($opt['mk']['tp']);
12944 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12945 if ($this->rtl) {
12946 $this->x -= $w;
12947 } else {
12948 $this->x += $w;
12949 }
12950 }
12951
12967 public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12968 if ($x === '') {
12969 $x = $this->x;
12970 }
12971 if ($y === '') {
12972 $y = $this->y;
12973 }
12974 // check page for no-write regions and adapt page margins if necessary
12975 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12976 if ($js) {
12977 $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
12978 $s = '';
12979 foreach ($values as $value) {
12980 if (is_array($value)) {
12981 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12982 } else {
12983 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12984 }
12985 }
12986 $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12987 return;
12988 }
12989 // get default style
12990 $prop = array_merge($this->getFormDefaultProp(), $prop);
12991 $prop['Combo'] = true;
12992 // get annotation data
12993 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12994 // set additional default options
12995 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12996 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12997 $popt['da'] = $fontstyle;
12998 // build appearance stream
12999 $popt['ap'] = array();
13000 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13001 $text = '';
13002 foreach($values as $item) {
13003 if (is_array($item)) {
13004 $text .= $item[1]."\n";
13005 } else {
13006 $text .= $item."\n";
13007 }
13008 }
13009 $tmpid = $this->startTemplate($w, $h, false);
13010 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13011 $this->endTemplate();
13012 --$this->n;
13013 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13014 unset($this->xobjects[$tmpid]);
13015 $popt['ap']['n'] .= 'Q EMC';
13016 // merge options
13017 $opt = array_merge($popt, $opt);
13018 // set remaining annotation data
13019 $opt['Subtype'] = 'Widget';
13020 $opt['ft'] = 'Ch';
13021 $opt['t'] = $name;
13022 $opt['opt'] = $values;
13023 unset($opt['mk']['ca']);
13024 unset($opt['mk']['rc']);
13025 unset($opt['mk']['ac']);
13026 unset($opt['mk']['i']);
13027 unset($opt['mk']['ri']);
13028 unset($opt['mk']['ix']);
13029 unset($opt['mk']['if']);
13030 unset($opt['mk']['tp']);
13031 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13032 if ($this->rtl) {
13033 $this->x -= $w;
13034 } else {
13035 $this->x += $w;
13036 }
13037 }
13038
13054 public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
13055 if ($x === '') {
13056 $x = $this->x;
13057 }
13058 if ($y === '') {
13059 $y = $this->y;
13060 }
13061 // check page for no-write regions and adapt page margins if necessary
13062 list($x, $y) = $this->checkPageRegions($w, $x, $y);
13063 if ($js) {
13064 $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
13065 return;
13066 }
13067 if (!isset($prop['value'])) {
13068 $prop['value'] = array('Yes');
13069 }
13070 // get default style
13071 $prop = array_merge($this->getFormDefaultProp(), $prop);
13072 $prop['borderStyle'] = 'inset';
13073 // get annotation data
13074 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13075 // set additional default options
13076 $font = 'zapfdingbats';
13077 if ($this->pdfa_mode) {
13078 // all fonts must be embedded
13079 $font = 'pdfa'.$font;
13080 }
13081 $this->AddFont($font);
13082 $tmpfont = $this->getFontBuffer($font);
13083 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
13084 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
13085 $popt['da'] = $fontstyle;
13086 // build appearance stream
13087 $popt['ap'] = array();
13088 $popt['ap']['n'] = array();
13089 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k);
13090 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
13091 $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);
13092 $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);
13093 // merge options
13094 $opt = array_merge($popt, $opt);
13095 // set remaining annotation data
13096 $opt['Subtype'] = 'Widget';
13097 $opt['ft'] = 'Btn';
13098 $opt['t'] = $name;
13099 if (TCPDF_STATIC::empty_string($onvalue)) {
13100 $onvalue = 'Yes';
13101 }
13102 $opt['opt'] = array($onvalue);
13103 if ($checked) {
13104 $opt['v'] = array('/Yes');
13105 $opt['as'] = 'Yes';
13106 } else {
13107 $opt['v'] = array('/Off');
13108 $opt['as'] = 'Off';
13109 }
13110 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
13111 if ($this->rtl) {
13112 $this->x -= $w;
13113 } else {
13114 $this->x += $w;
13115 }
13116 }
13117
13134 public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13135 if ($x === '') {
13136 $x = $this->x;
13137 }
13138 if ($y === '') {
13139 $y = $this->y;
13140 }
13141 // check page for no-write regions and adapt page margins if necessary
13142 list($x, $y) = $this->checkPageRegions($h, $x, $y);
13143 if ($js) {
13144 $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
13145 $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
13146 $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
13147 $this->javascript .= 'f'.$name.".highlight='push';\n";
13148 $this->javascript .= 'f'.$name.".print=false;\n";
13149 return;
13150 }
13151 // get default style
13152 $prop = array_merge($this->getFormDefaultProp(), $prop);
13153 $prop['Pushbutton'] = 'true';
13154 $prop['highlight'] = 'push';
13155 $prop['display'] = 'display.noPrint';
13156 // get annotation data
13157 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13158 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13159 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13160 $popt['da'] = $fontstyle;
13161 // build appearance stream
13162 $popt['ap'] = array();
13163 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13164 $tmpid = $this->startTemplate($w, $h, false);
13165 $bw = (2 / $this->k); // border width
13166 $border = array(
13167 'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13168 'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
13169 'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13170 'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
13171 $this->SetFillColor(204);
13172 $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
13173 $this->endTemplate();
13174 --$this->n;
13175 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13176 unset($this->xobjects[$tmpid]);
13177 $popt['ap']['n'] .= 'Q EMC';
13178 // set additional default options
13179 if (!isset($popt['mk'])) {
13180 $popt['mk'] = array();
13181 }
13182 $ann_obj_id = ($this->n + 1);
13183 if (!empty($action) AND !is_array($action)) {
13184 $ann_obj_id = ($this->n + 2);
13185 }
13186 $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
13187 $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
13188 $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
13189 // merge options
13190 $opt = array_merge($popt, $opt);
13191 // set remaining annotation data
13192 $opt['Subtype'] = 'Widget';
13193 $opt['ft'] = 'Btn';
13194 $opt['t'] = $caption;
13195 $opt['v'] = $name;
13196 if (!empty($action)) {
13197 if (is_array($action)) {
13198 // form action options as on section 12.7.5 of PDF32000_2008.
13199 $opt['aa'] = '/D <<';
13200 $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
13201 foreach ($action AS $key => $val) {
13202 if (($key == 'S') AND in_array($val, $bmode)) {
13203 $opt['aa'] .= ' /S /'.$val;
13204 } elseif (($key == 'F') AND (!empty($val))) {
13205 $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
13206 } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
13207 $opt['aa'] .= ' /Fields [';
13208 foreach ($val AS $field) {
13209 $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
13210 }
13211 $opt['aa'] .= ']';
13212 } elseif (($key == 'Flags')) {
13213 $ff = 0;
13214 if (is_array($val)) {
13215 foreach ($val AS $flag) {
13216 switch ($flag) {
13217 case 'Include/Exclude': {
13218 $ff += 1 << 0;
13219 break;
13220 }
13221 case 'IncludeNoValueFields': {
13222 $ff += 1 << 1;
13223 break;
13224 }
13225 case 'ExportFormat': {
13226 $ff += 1 << 2;
13227 break;
13228 }
13229 case 'GetMethod': {
13230 $ff += 1 << 3;
13231 break;
13232 }
13233 case 'SubmitCoordinates': {
13234 $ff += 1 << 4;
13235 break;
13236 }
13237 case 'XFDF': {
13238 $ff += 1 << 5;
13239 break;
13240 }
13241 case 'IncludeAppendSaves': {
13242 $ff += 1 << 6;
13243 break;
13244 }
13245 case 'IncludeAnnotations': {
13246 $ff += 1 << 7;
13247 break;
13248 }
13249 case 'SubmitPDF': {
13250 $ff += 1 << 8;
13251 break;
13252 }
13253 case 'CanonicalFormat': {
13254 $ff += 1 << 9;
13255 break;
13256 }
13257 case 'ExclNonUserAnnots': {
13258 $ff += 1 << 10;
13259 break;
13260 }
13261 case 'ExclFKey': {
13262 $ff += 1 << 11;
13263 break;
13264 }
13265 case 'EmbedForm': {
13266 $ff += 1 << 13;
13267 break;
13268 }
13269 }
13270 }
13271 } else {
13272 $ff = intval($val);
13273 }
13274 $opt['aa'] .= ' /Flags '.$ff;
13275 }
13276 }
13277 $opt['aa'] .= ' >>';
13278 } else {
13279 // Javascript action or raw action command
13280 $js_obj_id = $this->addJavascriptObject($action);
13281 $opt['aa'] = '/D '.$js_obj_id.' 0 R';
13282 }
13283 }
13284 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13285 if ($this->rtl) {
13286 $this->x -= $w;
13287 } else {
13288 $this->x += $w;
13289 }
13290 }
13291
13292 // --- END FORMS FIELDS ------------------------------------------------
13293
13301 protected function _putsignature() {
13302 if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
13303 return;
13304 }
13305 $sigobjid = ($this->sig_obj_id + 1);
13306 $out = $this->_getobj($sigobjid)."\n";
13307 $out .= '<< /Type /Sig';
13308 $out .= ' /Filter /Adobe.PPKLite';
13309 $out .= ' /SubFilter /adbe.pkcs7.detached';
13310 $out .= ' '.TCPDF_STATIC::$byterange_string;
13311 $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>';
13312 $out .= ' /Reference ['; // array of signature reference dictionaries
13313 $out .= ' << /Type /SigRef';
13314 if ($this->signature_data['cert_type'] > 0) {
13315 $out .= ' /TransformMethod /DocMDP';
13316 $out .= ' /TransformParams <<';
13317 $out .= ' /Type /TransformParams';
13318 $out .= ' /P '.$this->signature_data['cert_type'];
13319 $out .= ' /V /1.2';
13320 } else {
13321 $out .= ' /TransformMethod /UR3';
13322 $out .= ' /TransformParams <<';
13323 $out .= ' /Type /TransformParams';
13324 $out .= ' /V /2.2';
13325 if (!TCPDF_STATIC::empty_string($this->ur['document'])) {
13326 $out .= ' /Document['.$this->ur['document'].']';
13327 }
13328 if (!TCPDF_STATIC::empty_string($this->ur['form'])) {
13329 $out .= ' /Form['.$this->ur['form'].']';
13330 }
13331 if (!TCPDF_STATIC::empty_string($this->ur['signature'])) {
13332 $out .= ' /Signature['.$this->ur['signature'].']';
13333 }
13334 if (!TCPDF_STATIC::empty_string($this->ur['annots'])) {
13335 $out .= ' /Annots['.$this->ur['annots'].']';
13336 }
13337 if (!TCPDF_STATIC::empty_string($this->ur['ef'])) {
13338 $out .= ' /EF['.$this->ur['ef'].']';
13339 }
13340 if (!TCPDF_STATIC::empty_string($this->ur['formex'])) {
13341 $out .= ' /FormEX['.$this->ur['formex'].']';
13342 }
13343 }
13344 $out .= ' >>'; // close TransformParams
13345 // optional digest data (values must be calculated and replaced later)
13346 //$out .= ' /Data ********** 0 R';
13347 //$out .= ' /DigestMethod/MD5';
13348 //$out .= ' /DigestLocation[********** 34]';
13349 //$out .= ' /DigestValue<********************************>';
13350 $out .= ' >>';
13351 $out .= ' ]'; // end of reference
13352 if (isset($this->signature_data['info']['Name']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Name'])) {
13353 $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid);
13354 }
13355 if (isset($this->signature_data['info']['Location']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Location'])) {
13356 $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid);
13357 }
13358 if (isset($this->signature_data['info']['Reason']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Reason'])) {
13359 $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid);
13360 }
13361 if (isset($this->signature_data['info']['ContactInfo']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['ContactInfo'])) {
13362 $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid);
13363 }
13364 $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp);
13365 $out .= ' >>';
13366 $out .= "\n".'endobj';
13367 $this->_out($out);
13368 }
13369
13387 public function setUserRights(
13388 $enable=true,
13389 $document='/FullSave',
13390 $annots='/Create/Delete/Modify/Copy/Import/Export',
13391 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
13392 $signature='/Modify',
13393 $ef='/Create/Delete/Modify/Import',
13394 $formex='') {
13395 $this->ur['enabled'] = $enable;
13396 $this->ur['document'] = $document;
13397 $this->ur['annots'] = $annots;
13398 $this->ur['form'] = $form;
13399 $this->ur['signature'] = $signature;
13400 $this->ur['ef'] = $ef;
13401 $this->ur['formex'] = $formex;
13402 if (!$this->sign) {
13403 $this->setSignature('', '', '', '', 0, array());
13404 }
13405 }
13406
13423 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
13424 // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13425 // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13426 // to convert pfx certificate to pem: openssl
13427 // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
13428 $this->sign = true;
13429 ++$this->n;
13430 $this->sig_obj_id = $this->n; // signature widget
13431 ++$this->n; // signature object ($this->sig_obj_id + 1)
13432 $this->signature_data = array();
13433 if (strlen($signing_cert) == 0) {
13434 $this->Error('Please provide a certificate file and password!');
13435 }
13436 if (strlen($private_key) == 0) {
13437 $private_key = $signing_cert;
13438 }
13439 $this->signature_data['signcert'] = $signing_cert;
13440 $this->signature_data['privkey'] = $private_key;
13441 $this->signature_data['password'] = $private_key_password;
13442 $this->signature_data['extracerts'] = $extracerts;
13443 $this->signature_data['cert_type'] = $cert_type;
13444 $this->signature_data['info'] = $info;
13445 }
13446
13459 public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13460 $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13461 }
13462
13475 public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13476 ++$this->n;
13477 $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13478 }
13479
13493 protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13494 $sigapp = array();
13495 if (($page < 1) OR ($page > $this->numpages)) {
13496 $sigapp['page'] = $this->page;
13497 } else {
13498 $sigapp['page'] = intval($page);
13499 }
13500 if (empty($name)) {
13501 $sigapp['name'] = 'Signature';
13502 } else {
13503 $sigapp['name'] = $name;
13504 }
13505 $a = $x * $this->k;
13506 $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k);
13507 $c = $w * $this->k;
13508 $d = $h * $this->k;
13509 $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d));
13510 return $sigapp;
13511 }
13512
13520 public function startPageGroup($page='') {
13521 if (empty($page)) {
13522 $page = $this->page + 1;
13523 }
13524 $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1;
13525 }
13526
13533 public function setStartingPageNumber($num=1) {
13534 $this->starting_page_number = max(0, intval($num));
13535 }
13536
13544 public function getAliasRightShift() {
13545 // calculate aproximatively the ratio between widths of aliases and replacements.
13546 $ref = '{'.TCPDF_STATIC::$alias_right_shift.'}{'.TCPDF_STATIC::$alias_tot_pages.'}{'.TCPDF_STATIC::$alias_num_page.'}';
13547 $rep = str_repeat(' ', $this->GetNumChars($ref));
13548 $wdiff = max(1, ($this->GetStringWidth($ref) / $this->GetStringWidth($rep)));
13549 $sdiff = sprintf('%F', $wdiff);
13550 $alias = TCPDF_STATIC::$alias_right_shift.$sdiff.'}';
13551 if ($this->isUnicodeFont()) {
13552 $alias = '{'.$alias;
13553 }
13554 return $alias;
13555 }
13556
13565 public function getAliasNbPages() {
13566 if ($this->isUnicodeFont()) {
13567 return '{'.TCPDF_STATIC::$alias_tot_pages.'}';
13568 }
13570 }
13571
13580 public function getAliasNumPage() {
13581 if ($this->isUnicodeFont()) {
13582 return '{'.TCPDF_STATIC::$alias_num_page.'}';
13583 }
13585 }
13586
13595 public function getPageGroupAlias() {
13596 if ($this->isUnicodeFont()) {
13597 return '{'.TCPDF_STATIC::$alias_group_tot_pages.'}';
13598 }
13600 }
13601
13610 public function getPageNumGroupAlias() {
13611 if ($this->isUnicodeFont()) {
13612 return '{'.TCPDF_STATIC::$alias_group_num_page.'}';
13613 }
13615 }
13616
13623 public function getGroupPageNo() {
13624 return $this->pagegroups[$this->currpagegroup];
13625 }
13626
13633 public function getGroupPageNoFormatted() {
13635 }
13636
13643 public function PageNoFormatted() {
13644 return TCPDF_STATIC::formatPageNumber($this->PageNo());
13645 }
13646
13652 protected function _putocg() {
13653 if (empty($this->pdflayers)) {
13654 return;
13655 }
13656 foreach ($this->pdflayers as $key => $layer) {
13657 $this->pdflayers[$key]['objid'] = $this->_newobj();
13658 $out = '<< /Type /OCG';
13659 $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']);
13660 $out .= ' /Usage <<';
13661 $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>';
13662 $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>';
13663 $out .= ' >> >>';
13664 $out .= "\n".'endobj';
13665 $this->_out($out);
13666 }
13667 }
13668
13677 public function startLayer($name='', $print=true, $view=true) {
13678 if ($this->state != 2) {
13679 return;
13680 }
13681 $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1));
13682 if (empty($name)) {
13683 $name = $layer;
13684 } else {
13685 $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
13686 }
13687 $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view);
13688 $this->openMarkedContent = true;
13689 $this->_out('/OC /'.$layer.' BDC');
13690 }
13691
13697 public function endLayer() {
13698 if ($this->state != 2) {
13699 return;
13700 }
13701 if ($this->openMarkedContent) {
13702 // close existing open marked-content layer
13703 $this->_out('EMC');
13704 $this->openMarkedContent = false;
13705 }
13706 }
13707
13716 public function setVisibility($v) {
13717 if ($this->state != 2) {
13718 return;
13719 }
13720 $this->endLayer();
13721 switch($v) {
13722 case 'print': {
13723 $this->startLayer('Print', true, false);
13724 break;
13725 }
13726 case 'view':
13727 case 'screen': {
13728 $this->startLayer('View', false, true);
13729 break;
13730 }
13731 case 'all': {
13732 $this->_out('');
13733 break;
13734 }
13735 default: {
13736 $this->Error('Incorrect visibility: '.$v);
13737 break;
13738 }
13739 }
13740 }
13741
13749 protected function addExtGState($parms) {
13750 if ($this->pdfa_mode) {
13751 // transparencies are not allowed in PDF/A mode
13752 return;
13753 }
13754 // check if this ExtGState already exist
13755 foreach ($this->extgstates as $i => $ext) {
13756 if ($ext['parms'] == $parms) {
13757 if ($this->inxobj) {
13758 // we are inside an XObject template
13759 $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext;
13760 }
13761 // return reference to existing ExtGState
13762 return $i;
13763 }
13764 }
13765 $n = (count($this->extgstates) + 1);
13766 $this->extgstates[$n] = array('parms' => $parms);
13767 if ($this->inxobj) {
13768 // we are inside an XObject template
13769 $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n];
13770 }
13771 return $n;
13772 }
13773
13780 protected function setExtGState($gs) {
13781 if ($this->pdfa_mode OR ($this->state != 2)) {
13782 // transparency is not allowed in PDF/A mode
13783 return;
13784 }
13785 $this->_out(sprintf('/GS%d gs', $gs));
13786 }
13787
13793 protected function _putextgstates() {
13794 foreach ($this->extgstates as $i => $ext) {
13795 $this->extgstates[$i]['n'] = $this->_newobj();
13796 $out = '<< /Type /ExtGState';
13797 foreach ($ext['parms'] as $k => $v) {
13798 if (is_float($v)) {
13799 $v = sprintf('%F', $v);
13800 } elseif ($v === true) {
13801 $v = 'true';
13802 } elseif ($v === false) {
13803 $v = 'false';
13804 }
13805 $out .= ' /'.$k.' '.$v;
13806 }
13807 $out .= ' >>';
13808 $out .= "\n".'endobj';
13809 $this->_out($out);
13810 }
13811 }
13812
13822 public function setOverprint($stroking=true, $nonstroking='', $mode=0) {
13823 if ($this->state != 2) {
13824 return;
13825 }
13826 $stroking = $stroking ? true : false;
13827 if (TCPDF_STATIC::empty_string($nonstroking)) {
13828 // default value if not set
13829 $nonstroking = $stroking;
13830 } else {
13831 $nonstroking = $nonstroking ? true : false;
13832 }
13833 if (($mode != 0) AND ($mode != 1)) {
13834 $mode = 0;
13835 }
13836 $this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode);
13837 $gs = $this->addExtGState($this->overprint);
13838 $this->setExtGState($gs);
13839 }
13840
13848 public function getOverprint() {
13849 return $this->overprint;
13850 }
13851
13861 public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) {
13862 if ($this->pdfa_mode) {
13863 // transparency is not allowed in PDF/A mode
13864 return;
13865 }
13866 $stroking = floatval($stroking);
13867 if (TCPDF_STATIC::empty_string($nonstroking)) {
13868 // default value if not set
13869 $nonstroking = $stroking;
13870 } else {
13871 $nonstroking = floatval($nonstroking);
13872 }
13873 if ($bm[0] == '/') {
13874 // remove trailing slash
13875 $bm = substr($bm, 1);
13876 }
13877 if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
13878 $bm = 'Normal';
13879 }
13880 $ais = $ais ? true : false;
13881 $this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais);
13882 $gs = $this->addExtGState($this->alpha);
13883 $this->setExtGState($gs);
13884 }
13885
13893 public function getAlpha() {
13894 return $this->alpha;
13895 }
13896
13903 public function setJPEGQuality($quality) {
13904 if (($quality < 1) OR ($quality > 100)) {
13905 $quality = 75;
13906 }
13907 $this->jpeg_quality = intval($quality);
13908 }
13909
13916 public function setDefaultTableColumns($cols=4) {
13917 $this->default_table_columns = intval($cols);
13918 }
13919
13926 public function setCellHeightRatio($h) {
13927 $this->cell_height_ratio = $h;
13928 }
13929
13935 public function getCellHeightRatio() {
13937 }
13938
13945 public function setPDFVersion($version='1.7') {
13946 if ($this->pdfa_mode) {
13947 // PDF/A mode
13948 $this->PDFVersion = '1.4';
13949 } else {
13950 $this->PDFVersion = $version;
13951 }
13952 }
13953
13963 public function setViewerPreferences($preferences) {
13964 $this->viewer_preferences = $preferences;
13965 }
13966
13980 public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
13981 $bars = explode(',', $colors);
13982 $numbars = count($bars); // number of bars to print
13983 // set bar measures
13984 if ($vertical) {
13985 $coords = array(0, 0, 0, 1);
13986 $wb = $w / $numbars; // bar width
13987 $hb = $h; // bar height
13988 $xd = $wb; // delta x
13989 $yd = 0; // delta y
13990 } else {
13991 $coords = array(1, 0, 0, 0);
13992 $wb = $w; // bar width
13993 $hb = $h / $numbars; // bar height
13994 $xd = 0; // delta x
13995 $yd = $hb; // delta y
13996 }
13997 $xb = $x;
13998 $yb = $y;
13999 foreach ($bars as $col) {
14000 switch ($col) {
14001 // set transition colors
14002 case 'A': { // BLACK
14003 $col_a = array(255);
14004 $col_b = array(0);
14005 break;
14006 }
14007 case 'W': { // WHITE
14008 $col_a = array(0);
14009 $col_b = array(255);
14010 break;
14011 }
14012 case 'R': { // R
14013 $col_a = array(255,255,255);
14014 $col_b = array(255,0,0);
14015 break;
14016 }
14017 case 'G': { // G
14018 $col_a = array(255,255,255);
14019 $col_b = array(0,255,0);
14020 break;
14021 }
14022 case 'B': { // B
14023 $col_a = array(255,255,255);
14024 $col_b = array(0,0,255);
14025 break;
14026 }
14027 case 'C': { // C
14028 $col_a = array(0,0,0,0);
14029 $col_b = array(100,0,0,0);
14030 break;
14031 }
14032 case 'M': { // M
14033 $col_a = array(0,0,0,0);
14034 $col_b = array(0,100,0,0);
14035 break;
14036 }
14037 case 'Y': { // Y
14038 $col_a = array(0,0,0,0);
14039 $col_b = array(0,0,100,0);
14040 break;
14041 }
14042 case 'K': { // K
14043 $col_a = array(0,0,0,0);
14044 $col_b = array(0,0,0,100);
14045 break;
14046 }
14047 default: { // GRAY
14048 $col_a = array(255);
14049 $col_b = array(0);
14050 break;
14051 }
14052 }
14053 if ($transition) {
14054 // color gradient
14055 $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
14056 } else {
14057 // color rectangle
14058 $this->SetFillColorArray($col_b);
14059 $this->Rect($xb, $yb, $wb, $hb, 'F', array());
14060 }
14061 $xb += $xd;
14062 $yb += $yd;
14063 }
14064 }
14065
14078 public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(0,0,0)) {
14079 $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
14080 $type = strtoupper($type);
14081 $type = preg_replace('/[^A-Z\-\,]*/', '', $type);
14082 // split type in single components
14083 $type = str_replace('-', ',', $type);
14084 $type = str_replace('TL', 'T,L', $type);
14085 $type = str_replace('TR', 'T,R', $type);
14086 $type = str_replace('BL', 'F,L', $type);
14087 $type = str_replace('BR', 'F,R', $type);
14088 $type = str_replace('A', 'T,L', $type);
14089 $type = str_replace('B', 'T,R', $type);
14090 $type = str_replace('T,RO', 'BO', $type);
14091 $type = str_replace('C', 'F,L', $type);
14092 $type = str_replace('D', 'F,R', $type);
14093 $crops = explode(',', strtoupper($type));
14094 // remove duplicates
14095 $crops = array_unique($crops);
14096 $dw = ($w / 4); // horizontal space to leave before the intersection point
14097 $dh = ($h / 4); // vertical space to leave before the intersection point
14098 foreach ($crops as $crop) {
14099 switch ($crop) {
14100 case 'T':
14101 case 'TOP': {
14102 $x1 = $x;
14103 $y1 = ($y - $h);
14104 $x2 = $x;
14105 $y2 = ($y - $dh);
14106 break;
14107 }
14108 case 'F':
14109 case 'BOTTOM': {
14110 $x1 = $x;
14111 $y1 = ($y + $dh);
14112 $x2 = $x;
14113 $y2 = ($y + $h);
14114 break;
14115 }
14116 case 'L':
14117 case 'LEFT': {
14118 $x1 = ($x - $w);
14119 $y1 = $y;
14120 $x2 = ($x - $dw);
14121 $y2 = $y;
14122 break;
14123 }
14124 case 'R':
14125 case 'RIGHT': {
14126 $x1 = ($x + $dw);
14127 $y1 = $y;
14128 $x2 = ($x + $w);
14129 $y2 = $y;
14130 break;
14131 }
14132 }
14133 $this->Line($x1, $y1, $x2, $y2);
14134 }
14135 }
14136
14149 public function registrationMark($x, $y, $r, $double=false, $cola=array(0,0,0), $colb=array(255,255,255)) {
14150 $line_style = array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
14151 $this->SetFillColorArray($cola);
14152 $this->PieSector($x, $y, $r, 90, 180, 'F');
14153 $this->PieSector($x, $y, $r, 270, 360, 'F');
14154 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14155 if ($double) {
14156 $r2 = $r * 0.5;
14157 $this->SetFillColorArray($colb);
14158 $this->PieSector($x, $y, $r2, 90, 180, 'F');
14159 $this->PieSector($x, $y, $r2, 270, 360, 'F');
14160 $this->SetFillColorArray($cola);
14161 $this->PieSector($x, $y, $r2, 0, 90, 'F');
14162 $this->PieSector($x, $y, $r2, 180, 270, 'F');
14163 $this->Circle($x, $y, $r2, 0, 360, 'C', $line_style, array(), 8);
14164 }
14165 }
14166
14180 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
14181 $this->Clip($x, $y, $w, $h);
14182 $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14183 }
14184
14198 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
14199 $this->Clip($x, $y, $w, $h);
14200 $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14201 }
14202
14221 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) {
14222 if ($this->pdfa_mode OR ($this->state != 2)) {
14223 return;
14224 }
14225 $this->Clip($x, $y, $w, $h);
14226 $n = count($this->gradients) + 1;
14227 $this->gradients[$n] = array();
14228 $this->gradients[$n]['type'] = 6; //coons patch mesh
14229 $this->gradients[$n]['coords'] = array();
14230 $this->gradients[$n]['antialias'] = $antialias;
14231 $this->gradients[$n]['colors'] = array();
14232 $this->gradients[$n]['transparency'] = false;
14233 //check the coords array if it is the simple array or the multi patch array
14234 if (!isset($coords[0]['f'])) {
14235 //simple array -> convert to multi patch array
14236 if (!isset($col1[1])) {
14237 $col1[1] = $col1[2] = $col1[0];
14238 }
14239 if (!isset($col2[1])) {
14240 $col2[1] = $col2[2] = $col2[0];
14241 }
14242 if (!isset($col3[1])) {
14243 $col3[1] = $col3[2] = $col3[0];
14244 }
14245 if (!isset($col4[1])) {
14246 $col4[1] = $col4[2] = $col4[0];
14247 }
14248 $patch_array[0]['f'] = 0;
14249 $patch_array[0]['points'] = $coords;
14250 $patch_array[0]['colors'][0]['r'] = $col1[0];
14251 $patch_array[0]['colors'][0]['g'] = $col1[1];
14252 $patch_array[0]['colors'][0]['b'] = $col1[2];
14253 $patch_array[0]['colors'][1]['r'] = $col2[0];
14254 $patch_array[0]['colors'][1]['g'] = $col2[1];
14255 $patch_array[0]['colors'][1]['b'] = $col2[2];
14256 $patch_array[0]['colors'][2]['r'] = $col3[0];
14257 $patch_array[0]['colors'][2]['g'] = $col3[1];
14258 $patch_array[0]['colors'][2]['b'] = $col3[2];
14259 $patch_array[0]['colors'][3]['r'] = $col4[0];
14260 $patch_array[0]['colors'][3]['g'] = $col4[1];
14261 $patch_array[0]['colors'][3]['b'] = $col4[2];
14262 } else {
14263 //multi patch array
14264 $patch_array = $coords;
14265 }
14266 $bpcd = 65535; //16 bits per coordinate
14267 //build the data stream
14268 $this->gradients[$n]['stream'] = '';
14269 $count_patch = count($patch_array);
14270 for ($i=0; $i < $count_patch; ++$i) {
14271 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
14272 $count_points = count($patch_array[$i]['points']);
14273 for ($j=0; $j < $count_points; ++$j) {
14274 //each point as 16 bit
14275 $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
14276 if ($patch_array[$i]['points'][$j] < 0) {
14277 $patch_array[$i]['points'][$j] = 0;
14278 }
14279 if ($patch_array[$i]['points'][$j] > $bpcd) {
14280 $patch_array[$i]['points'][$j] = $bpcd;
14281 }
14282 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
14283 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
14284 }
14285 $count_cols = count($patch_array[$i]['colors']);
14286 for ($j=0; $j < $count_cols; ++$j) {
14287 //each color component as 8 bit
14288 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
14289 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
14290 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
14291 }
14292 }
14293 //paint the gradient
14294 $this->_out('/Sh'.$n.' sh');
14295 //restore previous Graphic State
14296 $this->_out('Q');
14297 if ($this->inxobj) {
14298 // we are inside an XObject template
14299 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14300 }
14301 }
14302
14313 protected function Clip($x, $y, $w, $h) {
14314 if ($this->state != 2) {
14315 return;
14316 }
14317 if ($this->rtl) {
14318 $x = $this->w - $x - $w;
14319 }
14320 //save current Graphic State
14321 $s = 'q';
14322 //set clipping area
14323 $s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
14324 //set up transformation matrix for gradient
14325 $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
14326 $this->_out($s);
14327 }
14328
14340 public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
14341 if ($this->pdfa_mode OR ($this->state != 2)) {
14342 return;
14343 }
14344 $n = count($this->gradients) + 1;
14345 $this->gradients[$n] = array();
14346 $this->gradients[$n]['type'] = $type;
14347 $this->gradients[$n]['coords'] = $coords;
14348 $this->gradients[$n]['antialias'] = $antialias;
14349 $this->gradients[$n]['colors'] = array();
14350 $this->gradients[$n]['transparency'] = false;
14351 // color space
14352 $numcolspace = count($stops[0]['color']);
14353 $bcolor = array_values($background);
14354 switch($numcolspace) {
14355 case 4: { // CMYK
14356 $this->gradients[$n]['colspace'] = 'DeviceCMYK';
14357 if (!empty($background)) {
14358 $this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
14359 }
14360 break;
14361 }
14362 case 3: { // RGB
14363 $this->gradients[$n]['colspace'] = 'DeviceRGB';
14364 if (!empty($background)) {
14365 $this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
14366 }
14367 break;
14368 }
14369 case 1: { // Gray scale
14370 $this->gradients[$n]['colspace'] = 'DeviceGray';
14371 if (!empty($background)) {
14372 $this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255);
14373 }
14374 break;
14375 }
14376 }
14377 $num_stops = count($stops);
14378 $last_stop_id = $num_stops - 1;
14379 foreach ($stops as $key => $stop) {
14380 $this->gradients[$n]['colors'][$key] = array();
14381 // offset represents a location along the gradient vector
14382 if (isset($stop['offset'])) {
14383 $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
14384 } else {
14385 if ($key == 0) {
14386 $this->gradients[$n]['colors'][$key]['offset'] = 0;
14387 } elseif ($key == $last_stop_id) {
14388 $this->gradients[$n]['colors'][$key]['offset'] = 1;
14389 } else {
14390 $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
14391 $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
14392 }
14393 }
14394 if (isset($stop['opacity'])) {
14395 $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
14396 if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) {
14397 $this->gradients[$n]['transparency'] = true;
14398 }
14399 } else {
14400 $this->gradients[$n]['colors'][$key]['opacity'] = 1;
14401 }
14402 // exponent for the exponential interpolation function
14403 if (isset($stop['exponent'])) {
14404 $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
14405 } else {
14406 $this->gradients[$n]['colors'][$key]['exponent'] = 1;
14407 }
14408 // set colors
14409 $color = array_values($stop['color']);
14410 switch($numcolspace) {
14411 case 4: { // CMYK
14412 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
14413 break;
14414 }
14415 case 3: { // RGB
14416 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255);
14417 break;
14418 }
14419 case 1: { // Gray scale
14420 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255);
14421 break;
14422 }
14423 }
14424 }
14425 if ($this->gradients[$n]['transparency']) {
14426 // paint luminosity gradient
14427 $this->_out('/TGS'.$n.' gs');
14428 }
14429 //paint the gradient
14430 $this->_out('/Sh'.$n.' sh');
14431 //restore previous Graphic State
14432 $this->_out('Q');
14433 if ($this->inxobj) {
14434 // we are inside an XObject template
14435 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14436 }
14437 }
14438
14445 function _putshaders() {
14446 if ($this->pdfa_mode) {
14447 return;
14448 }
14449 $idt = count($this->gradients); //index for transparency gradients
14450 foreach ($this->gradients as $id => $grad) {
14451 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
14452 $fc = $this->_newobj();
14453 $out = '<<';
14454 $out .= ' /FunctionType 3';
14455 $out .= ' /Domain [0 1]';
14456 $functions = '';
14457 $bounds = '';
14458 $encode = '';
14459 $i = 1;
14460 $num_cols = count($grad['colors']);
14461 $lastcols = $num_cols - 1;
14462 for ($i = 1; $i < $num_cols; ++$i) {
14463 $functions .= ($fc + $i).' 0 R ';
14464 if ($i < $lastcols) {
14465 $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']);
14466 }
14467 $encode .= '0 1 ';
14468 }
14469 $out .= ' /Functions ['.trim($functions).']';
14470 $out .= ' /Bounds ['.trim($bounds).']';
14471 $out .= ' /Encode ['.trim($encode).']';
14472 $out .= ' >>';
14473 $out .= "\n".'endobj';
14474 $this->_out($out);
14475 for ($i = 1; $i < $num_cols; ++$i) {
14476 $this->_newobj();
14477 $out = '<<';
14478 $out .= ' /FunctionType 2';
14479 $out .= ' /Domain [0 1]';
14480 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
14481 $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
14482 $out .= ' /N '.$grad['colors'][$i]['exponent'];
14483 $out .= ' >>';
14484 $out .= "\n".'endobj';
14485 $this->_out($out);
14486 }
14487 // set transparency fuctions
14488 if ($grad['transparency']) {
14489 $ft = $this->_newobj();
14490 $out = '<<';
14491 $out .= ' /FunctionType 3';
14492 $out .= ' /Domain [0 1]';
14493 $functions = '';
14494 $i = 1;
14495 $num_cols = count($grad['colors']);
14496 for ($i = 1; $i < $num_cols; ++$i) {
14497 $functions .= ($ft + $i).' 0 R ';
14498 }
14499 $out .= ' /Functions ['.trim($functions).']';
14500 $out .= ' /Bounds ['.trim($bounds).']';
14501 $out .= ' /Encode ['.trim($encode).']';
14502 $out .= ' >>';
14503 $out .= "\n".'endobj';
14504 $this->_out($out);
14505 for ($i = 1; $i < $num_cols; ++$i) {
14506 $this->_newobj();
14507 $out = '<<';
14508 $out .= ' /FunctionType 2';
14509 $out .= ' /Domain [0 1]';
14510 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
14511 $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
14512 $out .= ' /N '.$grad['colors'][$i]['exponent'];
14513 $out .= ' >>';
14514 $out .= "\n".'endobj';
14515 $this->_out($out);
14516 }
14517 }
14518 }
14519 // set shading object
14520 $this->_newobj();
14521 $out = '<< /ShadingType '.$grad['type'];
14522 if (isset($grad['colspace'])) {
14523 $out .= ' /ColorSpace /'.$grad['colspace'];
14524 } else {
14525 $out .= ' /ColorSpace /DeviceRGB';
14526 }
14527 if (isset($grad['background']) AND !empty($grad['background'])) {
14528 $out .= ' /Background ['.$grad['background'].']';
14529 }
14530 if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
14531 $out .= ' /AntiAlias true';
14532 }
14533 if ($grad['type'] == 2) {
14534 $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
14535 $out .= ' /Domain [0 1]';
14536 $out .= ' /Function '.$fc.' 0 R';
14537 $out .= ' /Extend [true true]';
14538 $out .= ' >>';
14539 } elseif ($grad['type'] == 3) {
14540 //x0, y0, r0, x1, y1, r1
14541 //at this this time radius of inner circle is 0
14542 $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
14543 $out .= ' /Domain [0 1]';
14544 $out .= ' /Function '.$fc.' 0 R';
14545 $out .= ' /Extend [true true]';
14546 $out .= ' >>';
14547 } elseif ($grad['type'] == 6) {
14548 $out .= ' /BitsPerCoordinate 16';
14549 $out .= ' /BitsPerComponent 8';
14550 $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
14551 $out .= ' /BitsPerFlag 8';
14552 $stream = $this->_getrawstream($grad['stream']);
14553 $out .= ' /Length '.strlen($stream);
14554 $out .= ' >>';
14555 $out .= ' stream'."\n".$stream."\n".'endstream';
14556 }
14557 $out .= "\n".'endobj';
14558 $this->_out($out);
14559 if ($grad['transparency']) {
14560 $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
14561 $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
14562 }
14563 $this->gradients[$id]['id'] = $this->n;
14564 // set pattern object
14565 $this->_newobj();
14566 $out = '<< /Type /Pattern /PatternType 2';
14567 $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
14568 $out .= ' >>';
14569 $out .= "\n".'endobj';
14570 $this->_out($out);
14571 $this->gradients[$id]['pattern'] = $this->n;
14572 // set shading and pattern for transparency mask
14573 if ($grad['transparency']) {
14574 // luminosity pattern
14575 $idgs = $id + $idt;
14576 $this->_newobj();
14577 $this->_out($shading_transparency);
14578 $this->gradients[$idgs]['id'] = $this->n;
14579 $this->_newobj();
14580 $out = '<< /Type /Pattern /PatternType 2';
14581 $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
14582 $out .= ' >>';
14583 $out .= "\n".'endobj';
14584 $this->_out($out);
14585 $this->gradients[$idgs]['pattern'] = $this->n;
14586 // luminosity XObject
14587 $oid = $this->_newobj();
14588 $this->xobjects['LX'.$oid] = array('n' => $oid);
14589 $filter = '';
14590 $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
14591 if ($this->compress) {
14592 $filter = ' /Filter /FlateDecode';
14593 $stream = gzcompress($stream);
14594 }
14595 $stream = $this->_getrawstream($stream);
14596 $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
14597 $out .= ' /Length '.strlen($stream);
14598 $rect = sprintf('%F %F', $this->wPt, $this->hPt);
14599 $out .= ' /BBox [0 0 '.$rect.']';
14600 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
14601 $out .= ' /Resources <<';
14602 $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
14603 $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
14604 $out .= ' >>';
14605 $out .= ' >> ';
14606 $out .= ' stream'."\n".$stream."\n".'endstream';
14607 $out .= "\n".'endobj';
14608 $this->_out($out);
14609 // SMask
14610 $this->_newobj();
14611 $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj';
14612 $this->_out($out);
14613 // ExtGState
14614 $this->_newobj();
14615 $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj';
14616 $this->_out($out);
14617 $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
14618 }
14619 }
14620 }
14621
14637 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
14638 $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
14639 }
14640
14658 public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
14659 if ($this->state != 2) {
14660 return;
14661 }
14662 if ($this->rtl) {
14663 $xc = ($this->w - $xc);
14664 }
14666 if ($op == 'f') {
14667 $line_style = array();
14668 }
14669 if ($cw) {
14670 $d = $b;
14671 $b = (360 - $a + $o);
14672 $a = (360 - $d + $o);
14673 } else {
14674 $b += $o;
14675 $a += $o;
14676 }
14677 $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
14678 $this->_out($op);
14679 }
14680
14702 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
14703 if ($this->state != 2) {
14704 return;
14705 }
14706 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
14707 // convert EPS to raster image using GD or ImageMagick libraries
14708 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14709 }
14710 if ($x === '') {
14711 $x = $this->x;
14712 }
14713 if ($y === '') {
14714 $y = $this->y;
14715 }
14716 // check page for no-write regions and adapt page margins if necessary
14717 list($x, $y) = $this->checkPageRegions($h, $x, $y);
14718 $k = $this->k;
14719 if ($file{0} === '@') { // image from string
14720 $data = substr($file, 1);
14721 } else { // EPS/AI file
14722 $data = file_get_contents($file);
14723 }
14724 if ($data === false) {
14725 $this->Error('EPS file not found: '.$file);
14726 }
14727 $regs = array();
14728 // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
14729 preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
14730 if (count($regs) > 1) {
14731 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
14732 if (strpos($version_str, 'Adobe Illustrator') !== false) {
14733 $versexp = explode(' ', $version_str);
14734 $version = (float)array_pop($versexp);
14735 if ($version >= 9) {
14736 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
14737 }
14738 }
14739 }
14740 // strip binary bytes in front of PS-header
14741 $start = strpos($data, '%!PS-Adobe');
14742 if ($start > 0) {
14743 $data = substr($data, $start);
14744 }
14745 // find BoundingBox params
14746 preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
14747 if (count($regs) > 1) {
14748 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
14749 } else {
14750 $this->Error('No BoundingBox found in EPS/AI file: '.$file);
14751 }
14752 $start = strpos($data, '%%EndSetup');
14753 if ($start === false) {
14754 $start = strpos($data, '%%EndProlog');
14755 }
14756 if ($start === false) {
14757 $start = strpos($data, '%%BoundingBox');
14758 }
14759 $data = substr($data, $start);
14760 $end = strpos($data, '%%PageTrailer');
14761 if ($end===false) {
14762 $end = strpos($data, 'showpage');
14763 }
14764 if ($end) {
14765 $data = substr($data, 0, $end);
14766 }
14767 // calculate image width and height on document
14768 if (($w <= 0) AND ($h <= 0)) {
14769 $w = ($x2 - $x1) / $k;
14770 $h = ($y2 - $y1) / $k;
14771 } elseif ($w <= 0) {
14772 $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
14773 } elseif ($h <= 0) {
14774 $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
14775 }
14776 // fit the image on available space
14777 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
14778 if ($this->rasterize_vector_images) {
14779 // convert EPS to raster image using GD or ImageMagick libraries
14780 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14781 }
14782 // set scaling factors
14783 $scale_x = $w / (($x2 - $x1) / $k);
14784 $scale_y = $h / (($y2 - $y1) / $k);
14785 // set alignment
14786 $this->img_rb_y = $y + $h;
14787 // set alignment
14788 if ($this->rtl) {
14789 if ($palign == 'L') {
14790 $ximg = $this->lMargin;
14791 } elseif ($palign == 'C') {
14792 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
14793 } elseif ($palign == 'R') {
14794 $ximg = $this->w - $this->rMargin - $w;
14795 } else {
14796 $ximg = $x - $w;
14797 }
14798 $this->img_rb_x = $ximg;
14799 } else {
14800 if ($palign == 'L') {
14801 $ximg = $this->lMargin;
14802 } elseif ($palign == 'C') {
14803 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
14804 } elseif ($palign == 'R') {
14805 $ximg = $this->w - $this->rMargin - $w;
14806 } else {
14807 $ximg = $x;
14808 }
14809 $this->img_rb_x = $ximg + $w;
14810 }
14811 if ($useBoundingBox) {
14812 $dx = $ximg * $k - $x1;
14813 $dy = $y * $k - $y1;
14814 } else {
14815 $dx = $ximg * $k;
14816 $dy = $y * $k;
14817 }
14818 // save the current graphic state
14819 $this->_out('q'.$this->epsmarker);
14820 // translate
14821 $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
14822 // scale
14823 if (isset($scale_x)) {
14824 $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
14825 }
14826 // handle pc/unix/mac line endings
14827 $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY);
14828 $u=0;
14829 $cnt = count($lines);
14830 for ($i=0; $i < $cnt; ++$i) {
14831 $line = $lines[$i];
14832 if (($line == '') OR ($line{0} == '%')) {
14833 continue;
14834 }
14835 $len = strlen($line);
14836 // check for spot color names
14837 $color_name = '';
14838 if (strcasecmp('x', substr(trim($line), -1)) == 0) {
14839 if (preg_match('/\‍([^\‍)]*\‍)/', $line, $matches) > 0) {
14840 // extract spot color name
14841 $color_name = $matches[0];
14842 // remove color name from string
14843 $line = str_replace(' '.$color_name, '', $line);
14844 // remove pharentesis from color name
14845 $color_name = substr($color_name, 1, -1);
14846 }
14847 }
14848 $chunks = explode(' ', $line);
14849 $cmd = trim(array_pop($chunks));
14850 // RGB
14851 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
14852 $b = array_pop($chunks);
14853 $g = array_pop($chunks);
14854 $r = array_pop($chunks);
14855 $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!
14856 continue;
14857 }
14858 $skip = false;
14859 if ($fixoutvals) {
14860 // check for values outside the bounding box
14861 switch ($cmd) {
14862 case 'm':
14863 case 'l':
14864 case 'L': {
14865 // skip values outside bounding box
14866 foreach ($chunks as $key => $val) {
14867 if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) {
14868 $skip = true;
14869 } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) {
14870 $skip = true;
14871 }
14872 }
14873 }
14874 }
14875 }
14876 switch ($cmd) {
14877 case 'm':
14878 case 'l':
14879 case 'v':
14880 case 'y':
14881 case 'c':
14882 case 'k':
14883 case 'K':
14884 case 'g':
14885 case 'G':
14886 case 's':
14887 case 'S':
14888 case 'J':
14889 case 'j':
14890 case 'w':
14891 case 'M':
14892 case 'd':
14893 case 'n': {
14894 if ($skip) {
14895 break;
14896 }
14897 $this->_out($line);
14898 break;
14899 }
14900 case 'x': {// custom fill color
14901 if (empty($color_name)) {
14902 // CMYK color
14903 list($col_c, $col_m, $col_y, $col_k) = $chunks;
14904 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
14905 } else {
14906 // Spot Color (CMYK + tint)
14907 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
14908 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
14909 $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t));
14910 $this->_out($color_cmd);
14911 }
14912 break;
14913 }
14914 case 'X': { // custom stroke color
14915 if (empty($color_name)) {
14916 // CMYK color
14917 list($col_c, $col_m, $col_y, $col_k) = $chunks;
14918 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
14919 } else {
14920 // Spot Color (CMYK + tint)
14921 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
14922 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
14923 $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t));
14924 $this->_out($color_cmd);
14925 }
14926 break;
14927 }
14928 case 'Y':
14929 case 'N':
14930 case 'V':
14931 case 'L':
14932 case 'C': {
14933 if ($skip) {
14934 break;
14935 }
14936 $line[($len - 1)] = strtolower($cmd);
14937 $this->_out($line);
14938 break;
14939 }
14940 case 'b':
14941 case 'B': {
14942 $this->_out($cmd . '*');
14943 break;
14944 }
14945 case 'f':
14946 case 'F': {
14947 if ($u > 0) {
14948 $isU = false;
14949 $max = min(($i + 5), $cnt);
14950 for ($j = ($i + 1); $j < $max; ++$j) {
14951 $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
14952 }
14953 if ($isU) {
14954 $this->_out('f*');
14955 }
14956 } else {
14957 $this->_out('f*');
14958 }
14959 break;
14960 }
14961 case '*u': {
14962 ++$u;
14963 break;
14964 }
14965 case '*U': {
14966 --$u;
14967 break;
14968 }
14969 }
14970 }
14971 // restore previous graphic state
14972 $this->_out($this->epsmarker.'Q');
14973 if (!empty($border)) {
14974 $bx = $this->x;
14975 $by = $this->y;
14976 $this->x = $ximg;
14977 if ($this->rtl) {
14978 $this->x += $w;
14979 }
14980 $this->y = $y;
14981 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
14982 $this->x = $bx;
14983 $this->y = $by;
14984 }
14985 if ($link) {
14986 $this->Link($ximg, $y, $w, $h, $link, 0);
14987 }
14988 // set pointer to align the next text/objects
14989 switch($align) {
14990 case 'T':{
14991 $this->y = $y;
14992 $this->x = $this->img_rb_x;
14993 break;
14994 }
14995 case 'M':{
14996 $this->y = $y + round($h/2);
14997 $this->x = $this->img_rb_x;
14998 break;
14999 }
15000 case 'B':{
15001 $this->y = $this->img_rb_y;
15002 $this->x = $this->img_rb_x;
15003 break;
15004 }
15005 case 'N':{
15006 $this->SetY($this->img_rb_y);
15007 break;
15008 }
15009 default:{
15010 break;
15011 }
15012 }
15013 $this->endlinex = $this->img_rb_x;
15014 }
15015
15021 public function setBarcode($bc='') {
15022 $this->barcode = $bc;
15023 }
15024
15031 public function getBarcode() {
15032 return $this->barcode;
15033 }
15034
15065 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
15066 if (TCPDF_STATIC::empty_string(trim($code))) {
15067 return;
15068 }
15069 require_once(dirname( __FILE__ ) . '/tcpdf_barcodes_1d.php');
15070 // save current graphic settings
15071 $gvars = $this->getGraphicVars();
15072 // create new barcode object
15073 $barcodeobj = new TCPDFBarcode($code, $type);
15074 $arrcode = $barcodeobj->getBarcodeArray();
15075 if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] == 0)) {
15076 $this->Error('Error in 1D barcode string');
15077 }
15078 // set default values
15079 if (!isset($style['position'])) {
15080 $style['position'] = '';
15081 } elseif ($style['position'] == 'S') {
15082 // keep this for backward compatibility
15083 $style['position'] = '';
15084 $style['stretch'] = true;
15085 }
15086 if (!isset($style['fitwidth'])) {
15087 if (!isset($style['stretch'])) {
15088 $style['fitwidth'] = true;
15089 } else {
15090 $style['fitwidth'] = false;
15091 }
15092 }
15093 if ($style['fitwidth']) {
15094 // disable stretch
15095 $style['stretch'] = false;
15096 }
15097 if (!isset($style['stretch'])) {
15098 if (($w === '') OR ($w <= 0)) {
15099 $style['stretch'] = false;
15100 } else {
15101 $style['stretch'] = true;
15102 }
15103 }
15104 if (!isset($style['fgcolor'])) {
15105 $style['fgcolor'] = array(0,0,0); // default black
15106 }
15107 if (!isset($style['bgcolor'])) {
15108 $style['bgcolor'] = false; // default transparent
15109 }
15110 if (!isset($style['border'])) {
15111 $style['border'] = false;
15112 }
15113 $fontsize = 0;
15114 if (!isset($style['text'])) {
15115 $style['text'] = false;
15116 }
15117 if ($style['text'] AND isset($style['font'])) {
15118 if (isset($style['fontsize'])) {
15119 $fontsize = $style['fontsize'];
15120 }
15121 $this->SetFont($style['font'], '', $fontsize);
15122 }
15123 if (!isset($style['stretchtext'])) {
15124 $style['stretchtext'] = 4;
15125 }
15126 if ($x === '') {
15127 $x = $this->x;
15128 }
15129 if ($y === '') {
15130 $y = $this->y;
15131 }
15132 // check page for no-write regions and adapt page margins if necessary
15133 list($x, $y) = $this->checkPageRegions($h, $x, $y);
15134 if (($w === '') OR ($w <= 0)) {
15135 if ($this->rtl) {
15136 $w = $x - $this->lMargin;
15137 } else {
15138 $w = $this->w - $this->rMargin - $x;
15139 }
15140 }
15141 // padding
15142 if (!isset($style['padding'])) {
15143 $padding = 0;
15144 } elseif ($style['padding'] === 'auto') {
15145 $padding = 10 * ($w / ($arrcode['maxw'] + 20));
15146 } else {
15147 $padding = floatval($style['padding']);
15148 }
15149 // horizontal padding
15150 if (!isset($style['hpadding'])) {
15151 $hpadding = $padding;
15152 } elseif ($style['hpadding'] === 'auto') {
15153 $hpadding = 10 * ($w / ($arrcode['maxw'] + 20));
15154 } else {
15155 $hpadding = floatval($style['hpadding']);
15156 }
15157 // vertical padding
15158 if (!isset($style['vpadding'])) {
15159 $vpadding = $padding;
15160 } elseif ($style['vpadding'] === 'auto') {
15161 $vpadding = ($hpadding / 2);
15162 } else {
15163 $vpadding = floatval($style['vpadding']);
15164 }
15165 // calculate xres (single bar width)
15166 $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
15167 if ($style['stretch']) {
15168 $xres = $max_xres;
15169 } else {
15170 if (TCPDF_STATIC::empty_string($xres)) {
15171 $xres = (0.141 * $this->k); // default bar width = 0.4 mm
15172 }
15173 if ($xres > $max_xres) {
15174 // correct xres to fit on $w
15175 $xres = $max_xres;
15176 }
15177 if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
15178 OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
15179 $hpadding = 10 * $xres;
15180 if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
15181 $vpadding = ($hpadding / 2);
15182 }
15183 }
15184 }
15185 if ($style['fitwidth']) {
15186 $wold = $w;
15187 $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding));
15188 if (isset($style['cellfitalign'])) {
15189 switch ($style['cellfitalign']) {
15190 case 'L': {
15191 if ($this->rtl) {
15192 $x -= ($wold - $w);
15193 }
15194 break;
15195 }
15196 case 'R': {
15197 if (!$this->rtl) {
15198 $x += ($wold - $w);
15199 }
15200 break;
15201 }
15202 case 'C': {
15203 if ($this->rtl) {
15204 $x -= (($wold - $w) / 2);
15205 } else {
15206 $x += (($wold - $w) / 2);
15207 }
15208 break;
15209 }
15210 default : {
15211 break;
15212 }
15213 }
15214 }
15215 }
15216 $text_height = ($this->cell_height_ratio * $fontsize / $this->k);
15217 // height
15218 if (($h === '') OR ($h <= 0)) {
15219 // set default height
15220 $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height;
15221 }
15222 $barh = $h - $text_height - (2 * $vpadding);
15223 if ($barh <=0) {
15224 // try to reduce font or padding to fit barcode on available height
15225 if ($text_height > $h) {
15226 $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio));
15227 $text_height = ($this->cell_height_ratio * $fontsize / $this->k);
15228 $this->SetFont($style['font'], '', $fontsize);
15229 }
15230 if ($vpadding > 0) {
15231 $vpadding = (($h - $text_height) / 4);
15232 }
15233 $barh = $h - $text_height - (2 * $vpadding);
15234 }
15235 // fit the barcode on available space
15236 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15237 // set alignment
15238 $this->img_rb_y = $y + $h;
15239 // set alignment
15240 if ($this->rtl) {
15241 if ($style['position'] == 'L') {
15242 $xpos = $this->lMargin;
15243 } elseif ($style['position'] == 'C') {
15244 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15245 } elseif ($style['position'] == 'R') {
15246 $xpos = $this->w - $this->rMargin - $w;
15247 } else {
15248 $xpos = $x - $w;
15249 }
15250 $this->img_rb_x = $xpos;
15251 } else {
15252 if ($style['position'] == 'L') {
15253 $xpos = $this->lMargin;
15254 } elseif ($style['position'] == 'C') {
15255 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15256 } elseif ($style['position'] == 'R') {
15257 $xpos = $this->w - $this->rMargin - $w;
15258 } else {
15259 $xpos = $x;
15260 }
15261 $this->img_rb_x = $xpos + $w;
15262 }
15263 $xpos_rect = $xpos;
15264 if (!isset($style['align'])) {
15265 $style['align'] = 'C';
15266 }
15267 switch ($style['align']) {
15268 case 'L': {
15269 $xpos = $xpos_rect + $hpadding;
15270 break;
15271 }
15272 case 'R': {
15273 $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding;
15274 break;
15275 }
15276 case 'C':
15277 default : {
15278 $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2);
15279 break;
15280 }
15281 }
15282 $xpos_text = $xpos;
15283 // barcode is always printed in LTR direction
15284 $tempRTL = $this->rtl;
15285 $this->rtl = false;
15286 // print background color
15287 if ($style['bgcolor']) {
15288 $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15289 } elseif ($style['border']) {
15290 $this->Rect($xpos_rect, $y, $w, $h, 'D');
15291 }
15292 // set foreground color
15293 $this->SetDrawColorArray($style['fgcolor']);
15294 $this->SetTextColorArray($style['fgcolor']);
15295 // print bars
15296 foreach ($arrcode['bcode'] as $k => $v) {
15297 $bw = ($v['w'] * $xres);
15298 if ($v['t']) {
15299 // draw a vertical bar
15300 $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']);
15301 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
15302 }
15303 $xpos += $bw;
15304 }
15305 // print text
15306 if ($style['text']) {
15307 if (isset($style['label']) AND !TCPDF_STATIC::empty_string($style['label'])) {
15308 $label = $style['label'];
15309 } else {
15310 $label = $code;
15311 }
15312 $txtwidth = ($arrcode['maxw'] * $xres);
15313 if ($this->GetStringWidth($label) > $txtwidth) {
15314 $style['stretchtext'] = 2;
15315 }
15316 // print text
15317 $this->x = $xpos_text;
15318 $this->y = $y + $vpadding + $barh;
15319 $cellpadding = $this->cell_padding;
15320 $this->SetCellPadding(0);
15321 $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
15322 $this->cell_padding = $cellpadding;
15323 }
15324 // restore original direction
15325 $this->rtl = $tempRTL;
15326 // restore previous settings
15327 $this->setGraphicVars($gvars);
15328 // set pointer to align the next text/objects
15329 switch($align) {
15330 case 'T':{
15331 $this->y = $y;
15332 $this->x = $this->img_rb_x;
15333 break;
15334 }
15335 case 'M':{
15336 $this->y = $y + round($h / 2);
15337 $this->x = $this->img_rb_x;
15338 break;
15339 }
15340 case 'B':{
15341 $this->y = $this->img_rb_y;
15342 $this->x = $this->img_rb_x;
15343 break;
15344 }
15345 case 'N':{
15346 $this->SetY($this->img_rb_y);
15347 break;
15348 }
15349 default:{
15350 break;
15351 }
15352 }
15353 $this->endlinex = $this->img_rb_x;
15354 }
15355
15381 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) {
15382 if (TCPDF_STATIC::empty_string(trim($code))) {
15383 return;
15384 }
15385 require_once(dirname( __FILE__ ) . '/tcpdf_barcodes_2d.php');
15386 // save current graphic settings
15387 $gvars = $this->getGraphicVars();
15388 // create new barcode object
15389 $barcodeobj = new TCPDF2DBarcode($code, $type);
15390 $arrcode = $barcodeobj->getBarcodeArray();
15391 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)) {
15392 $this->Error('Error in 2D barcode string');
15393 }
15394 // set default values
15395 if (!isset($style['position'])) {
15396 $style['position'] = '';
15397 }
15398 if (!isset($style['fgcolor'])) {
15399 $style['fgcolor'] = array(0,0,0); // default black
15400 }
15401 if (!isset($style['bgcolor'])) {
15402 $style['bgcolor'] = false; // default transparent
15403 }
15404 if (!isset($style['border'])) {
15405 $style['border'] = false;
15406 }
15407 // padding
15408 if (!isset($style['padding'])) {
15409 $style['padding'] = 0;
15410 } elseif ($style['padding'] === 'auto') {
15411 $style['padding'] = 4;
15412 }
15413 if (!isset($style['hpadding'])) {
15414 $style['hpadding'] = $style['padding'];
15415 } elseif ($style['hpadding'] === 'auto') {
15416 $style['hpadding'] = 4;
15417 }
15418 if (!isset($style['vpadding'])) {
15419 $style['vpadding'] = $style['padding'];
15420 } elseif ($style['vpadding'] === 'auto') {
15421 $style['vpadding'] = 4;
15422 }
15423 $hpad = (2 * $style['hpadding']);
15424 $vpad = (2 * $style['vpadding']);
15425 // cell (module) dimension
15426 if (!isset($style['module_width'])) {
15427 $style['module_width'] = 1; // width of a single module in points
15428 }
15429 if (!isset($style['module_height'])) {
15430 $style['module_height'] = 1; // height of a single module in points
15431 }
15432 if ($x === '') {
15433 $x = $this->x;
15434 }
15435 if ($y === '') {
15436 $y = $this->y;
15437 }
15438 // check page for no-write regions and adapt page margins if necessary
15439 list($x, $y) = $this->checkPageRegions($h, $x, $y);
15440 // number of barcode columns and rows
15441 $rows = $arrcode['num_rows'];
15442 $cols = $arrcode['num_cols'];
15443 // module width and height
15444 $mw = $style['module_width'];
15445 $mh = $style['module_height'];
15446 if (($mw == 0) OR ($mh == 0)) {
15447 $this->Error('Error in 2D barcode string');
15448 }
15449 // get max dimensions
15450 if ($this->rtl) {
15451 $maxw = $x - $this->lMargin;
15452 } else {
15453 $maxw = $this->w - $this->rMargin - $x;
15454 }
15455 $maxh = ($this->h - $this->tMargin - $this->bMargin);
15456 $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad));
15457 $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad));
15458 if (!$distort) {
15459 if (($maxw * $ratioHW) > $maxh) {
15460 $maxw = $maxh * $ratioWH;
15461 }
15462 if (($maxh * $ratioWH) > $maxw) {
15463 $maxh = $maxw * $ratioHW;
15464 }
15465 }
15466 // set maximum dimesions
15467 if ($w > $maxw) {
15468 $w = $maxw;
15469 }
15470 if ($h > $maxh) {
15471 $h = $maxh;
15472 }
15473 // set dimensions
15474 if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
15475 $w = ($cols + $hpad) * ($mw / $this->k);
15476 $h = ($rows + $vpad) * ($mh / $this->k);
15477 } elseif (($w === '') OR ($w <= 0)) {
15478 $w = $h * $ratioWH;
15479 } elseif (($h === '') OR ($h <= 0)) {
15480 $h = $w * $ratioHW;
15481 }
15482 // barcode size (excluding padding)
15483 $bw = ($w * $cols) / ($cols + $hpad);
15484 $bh = ($h * $rows) / ($rows + $vpad);
15485 // dimension of single barcode cell unit
15486 $cw = $bw / $cols;
15487 $ch = $bh / $rows;
15488 if (!$distort) {
15489 if (($cw / $ch) > ($mw / $mh)) {
15490 // correct horizontal distortion
15491 $cw = $ch * $mw / $mh;
15492 $bw = $cw * $cols;
15493 $style['hpadding'] = ($w - $bw) / (2 * $cw);
15494 } else {
15495 // correct vertical distortion
15496 $ch = $cw * $mh / $mw;
15497 $bh = $ch * $rows;
15498 $style['vpadding'] = ($h - $bh) / (2 * $ch);
15499 }
15500 }
15501 // fit the barcode on available space
15502 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15503 // set alignment
15504 $this->img_rb_y = $y + $h;
15505 // set alignment
15506 if ($this->rtl) {
15507 if ($style['position'] == 'L') {
15508 $xpos = $this->lMargin;
15509 } elseif ($style['position'] == 'C') {
15510 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15511 } elseif ($style['position'] == 'R') {
15512 $xpos = $this->w - $this->rMargin - $w;
15513 } else {
15514 $xpos = $x - $w;
15515 }
15516 $this->img_rb_x = $xpos;
15517 } else {
15518 if ($style['position'] == 'L') {
15519 $xpos = $this->lMargin;
15520 } elseif ($style['position'] == 'C') {
15521 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15522 } elseif ($style['position'] == 'R') {
15523 $xpos = $this->w - $this->rMargin - $w;
15524 } else {
15525 $xpos = $x;
15526 }
15527 $this->img_rb_x = $xpos + $w;
15528 }
15529 $xstart = $xpos + ($style['hpadding'] * $cw);
15530 $ystart = $y + ($style['vpadding'] * $ch);
15531 // barcode is always printed in LTR direction
15532 $tempRTL = $this->rtl;
15533 $this->rtl = false;
15534 // print background color
15535 if ($style['bgcolor']) {
15536 $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15537 } elseif ($style['border']) {
15538 $this->Rect($xpos, $y, $w, $h, 'D');
15539 }
15540 // set foreground color
15541 $this->SetDrawColorArray($style['fgcolor']);
15542 // print barcode cells
15543 // for each row
15544 for ($r = 0; $r < $rows; ++$r) {
15545 $xr = $xstart;
15546 // for each column
15547 for ($c = 0; $c < $cols; ++$c) {
15548 if ($arrcode['bcode'][$r][$c] == 1) {
15549 // draw a single barcode cell
15550 $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
15551 }
15552 $xr += $cw;
15553 }
15554 $ystart += $ch;
15555 }
15556 // restore original direction
15557 $this->rtl = $tempRTL;
15558 // restore previous settings
15559 $this->setGraphicVars($gvars);
15560 // set pointer to align the next text/objects
15561 switch($align) {
15562 case 'T':{
15563 $this->y = $y;
15564 $this->x = $this->img_rb_x;
15565 break;
15566 }
15567 case 'M':{
15568 $this->y = $y + round($h/2);
15569 $this->x = $this->img_rb_x;
15570 break;
15571 }
15572 case 'B':{
15573 $this->y = $this->img_rb_y;
15574 $this->x = $this->img_rb_x;
15575 break;
15576 }
15577 case 'N':{
15578 $this->SetY($this->img_rb_y);
15579 break;
15580 }
15581 default:{
15582 break;
15583 }
15584 }
15585 $this->endlinex = $this->img_rb_x;
15586 }
15587
15607 public function getMargins() {
15608 $ret = array(
15609 'left' => $this->lMargin,
15610 'right' => $this->rMargin,
15611 'top' => $this->tMargin,
15612 'bottom' => $this->bMargin,
15613 'header' => $this->header_margin,
15614 'footer' => $this->footer_margin,
15615 'cell' => $this->cell_padding,
15616 'padding_left' => $this->cell_padding['L'],
15617 'padding_top' => $this->cell_padding['T'],
15618 'padding_right' => $this->cell_padding['R'],
15619 'padding_bottom' => $this->cell_padding['B']
15620 );
15621 return $ret;
15622 }
15623
15634 public function getOriginalMargins() {
15635 $ret = array(
15636 'left' => $this->original_lMargin,
15637 'right' => $this->original_rMargin
15638 );
15639 return $ret;
15640 }
15641
15648 public function getFontSize() {
15649 return $this->FontSize;
15650 }
15651
15658 public function getFontSizePt() {
15659 return $this->FontSizePt;
15660 }
15661
15668 public function getFontFamily() {
15669 return $this->FontFamily;
15670 }
15671
15678 public function getFontStyle() {
15679 return $this->FontStyle;
15680 }
15681
15694 public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
15695 return TCPDF_STATIC::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces);
15696 }
15697
15705 protected function getCSSBorderWidth($width) {
15706 if ($width == 'thin') {
15707 $width = (2 / $this->k);
15708 } elseif ($width == 'medium') {
15709 $width = (4 / $this->k);
15710 } elseif ($width == 'thick') {
15711 $width = (6 / $this->k);
15712 } else {
15713 $width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
15714 }
15715 return $width;
15716 }
15717
15725 protected function getCSSBorderDashStyle($style) {
15726 switch (strtolower($style)) {
15727 case 'none':
15728 case 'hidden': {
15729 $dash = -1;
15730 break;
15731 }
15732 case 'dotted': {
15733 $dash = 1;
15734 break;
15735 }
15736 case 'dashed': {
15737 $dash = 3;
15738 break;
15739 }
15740 case 'double':
15741 case 'groove':
15742 case 'ridge':
15743 case 'inset':
15744 case 'outset':
15745 case 'solid':
15746 default: {
15747 $dash = 0;
15748 break;
15749 }
15750 }
15751 return $dash;
15752 }
15753
15761 protected function getCSSBorderStyle($cssborder) {
15762 $bprop = preg_split('/[\s]+/', trim($cssborder));
15763 $border = array(); // value to be returned
15764 switch (count($bprop)) {
15765 case 3: {
15766 $width = $bprop[0];
15767 $style = $bprop[1];
15768 $color = $bprop[2];
15769 break;
15770 }
15771 case 2: {
15772 $width = 'medium';
15773 $style = $bprop[0];
15774 $color = $bprop[1];
15775 break;
15776 }
15777 case 1: {
15778 $width = 'medium';
15779 $style = $bprop[0];
15780 $color = 'black';
15781 break;
15782 }
15783 default: {
15784 $width = 'medium';
15785 $style = 'solid';
15786 $color = 'black';
15787 break;
15788 }
15789 }
15790 if ($style == 'none') {
15791 return array();
15792 }
15793 $border['cap'] = 'square';
15794 $border['join'] = 'miter';
15795 $border['dash'] = $this->getCSSBorderDashStyle($style);
15796 if ($border['dash'] < 0) {
15797 return array();
15798 }
15799 $border['width'] = $this->getCSSBorderWidth($width);
15800 $border['color'] = TCPDF_COLORS::convertHTMLColorToDec($color, $this->spot_colors);
15801 return $border;
15802 }
15803
15812 public function getCSSPadding($csspadding, $width=0) {
15813 $padding = preg_split('/[\s]+/', trim($csspadding));
15814 $cell_padding = array(); // value to be returned
15815 switch (count($padding)) {
15816 case 4: {
15817 $cell_padding['T'] = $padding[0];
15818 $cell_padding['R'] = $padding[1];
15819 $cell_padding['B'] = $padding[2];
15820 $cell_padding['L'] = $padding[3];
15821 break;
15822 }
15823 case 3: {
15824 $cell_padding['T'] = $padding[0];
15825 $cell_padding['R'] = $padding[1];
15826 $cell_padding['B'] = $padding[2];
15827 $cell_padding['L'] = $padding[1];
15828 break;
15829 }
15830 case 2: {
15831 $cell_padding['T'] = $padding[0];
15832 $cell_padding['R'] = $padding[1];
15833 $cell_padding['B'] = $padding[0];
15834 $cell_padding['L'] = $padding[1];
15835 break;
15836 }
15837 case 1: {
15838 $cell_padding['T'] = $padding[0];
15839 $cell_padding['R'] = $padding[0];
15840 $cell_padding['B'] = $padding[0];
15841 $cell_padding['L'] = $padding[0];
15842 break;
15843 }
15844 default: {
15845 return $this->cell_padding;
15846 }
15847 }
15848 if ($width == 0) {
15849 $width = $this->w - $this->lMargin - $this->rMargin;
15850 }
15851 $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
15852 $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
15853 $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
15854 $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
15855 return $cell_padding;
15856 }
15857
15866 public function getCSSMargin($cssmargin, $width=0) {
15867 $margin = preg_split('/[\s]+/', trim($cssmargin));
15868 $cell_margin = array(); // value to be returned
15869 switch (count($margin)) {
15870 case 4: {
15871 $cell_margin['T'] = $margin[0];
15872 $cell_margin['R'] = $margin[1];
15873 $cell_margin['B'] = $margin[2];
15874 $cell_margin['L'] = $margin[3];
15875 break;
15876 }
15877 case 3: {
15878 $cell_margin['T'] = $margin[0];
15879 $cell_margin['R'] = $margin[1];
15880 $cell_margin['B'] = $margin[2];
15881 $cell_margin['L'] = $margin[1];
15882 break;
15883 }
15884 case 2: {
15885 $cell_margin['T'] = $margin[0];
15886 $cell_margin['R'] = $margin[1];
15887 $cell_margin['B'] = $margin[0];
15888 $cell_margin['L'] = $margin[1];
15889 break;
15890 }
15891 case 1: {
15892 $cell_margin['T'] = $margin[0];
15893 $cell_margin['R'] = $margin[0];
15894 $cell_margin['B'] = $margin[0];
15895 $cell_margin['L'] = $margin[0];
15896 break;
15897 }
15898 default: {
15899 return $this->cell_margin;
15900 }
15901 }
15902 if ($width == 0) {
15903 $width = $this->w - $this->lMargin - $this->rMargin;
15904 }
15905 $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
15906 $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
15907 $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
15908 $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
15909 return $cell_margin;
15910 }
15911
15920 public function getCSSBorderMargin($cssbspace, $width=0) {
15921 $space = preg_split('/[\s]+/', trim($cssbspace));
15922 $border_spacing = array(); // value to be returned
15923 switch (count($space)) {
15924 case 2: {
15925 $border_spacing['H'] = $space[0];
15926 $border_spacing['V'] = $space[1];
15927 break;
15928 }
15929 case 1: {
15930 $border_spacing['H'] = $space[0];
15931 $border_spacing['V'] = $space[0];
15932 break;
15933 }
15934 default: {
15935 return array('H' => 0, 'V' => 0);
15936 }
15937 }
15938 if ($width == 0) {
15939 $width = $this->w - $this->lMargin - $this->rMargin;
15940 }
15941 $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
15942 $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
15943 return $border_spacing;
15944 }
15945
15954 protected function getCSSFontSpacing($spacing, $parent=0) {
15955 $val = 0; // value to be returned
15956 $spacing = trim($spacing);
15957 switch ($spacing) {
15958 case 'normal': {
15959 $val = 0;
15960 break;
15961 }
15962 case 'inherit': {
15963 if ($parent == 'normal') {
15964 $val = 0;
15965 } else {
15966 $val = $parent;
15967 }
15968 break;
15969 }
15970 default: {
15971 $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
15972 }
15973 }
15974 return $val;
15975 }
15976
15985 protected function getCSSFontStretching($stretch, $parent=100) {
15986 $val = 100; // value to be returned
15987 $stretch = trim($stretch);
15988 switch ($stretch) {
15989 case 'ultra-condensed': {
15990 $val = 40;
15991 break;
15992 }
15993 case 'extra-condensed': {
15994 $val = 55;
15995 break;
15996 }
15997 case 'condensed': {
15998 $val = 70;
15999 break;
16000 }
16001 case 'semi-condensed': {
16002 $val = 85;
16003 break;
16004 }
16005 case 'normal': {
16006 $val = 100;
16007 break;
16008 }
16009 case 'semi-expanded': {
16010 $val = 115;
16011 break;
16012 }
16013 case 'expanded': {
16014 $val = 130;
16015 break;
16016 }
16017 case 'extra-expanded': {
16018 $val = 145;
16019 break;
16020 }
16021 case 'ultra-expanded': {
16022 $val = 160;
16023 break;
16024 }
16025 case 'wider': {
16026 $val = ($parent + 10);
16027 break;
16028 }
16029 case 'narrower': {
16030 $val = ($parent - 10);
16031 break;
16032 }
16033 case 'inherit': {
16034 if ($parent == 'normal') {
16035 $val = 100;
16036 } else {
16037 $val = $parent;
16038 }
16039 break;
16040 }
16041 default: {
16042 $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
16043 }
16044 }
16045 return $val;
16046 }
16047
16057 public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') {
16058 $refsize = TCPDF_FONTS::getFontRefSize($refsize);
16059 $parent_size = TCPDF_FONTS::getFontRefSize($parent_size, $refsize);
16060 switch ($val) {
16061 case 'xx-small': {
16062 $size = ($refsize - 4);
16063 break;
16064 }
16065 case 'x-small': {
16066 $size = ($refsize - 3);
16067 break;
16068 }
16069 case 'small': {
16070 $size = ($refsize - 2);
16071 break;
16072 }
16073 case 'medium': {
16074 $size = $refsize;
16075 break;
16076 }
16077 case 'large': {
16078 $size = ($refsize + 2);
16079 break;
16080 }
16081 case 'x-large': {
16082 $size = ($refsize + 4);
16083 break;
16084 }
16085 case 'xx-large': {
16086 $size = ($refsize + 6);
16087 break;
16088 }
16089 case 'smaller': {
16090 $size = ($parent_size - 3);
16091 break;
16092 }
16093 case 'larger': {
16094 $size = ($parent_size + 3);
16095 break;
16096 }
16097 default: {
16098 $size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true);
16099 }
16100 }
16101 return $size;
16102 }
16103
16111 protected function getHtmlDomArray($html) {
16112 // array of CSS styles ( selector => properties).
16113 $css = array();
16114 // get CSS array defined at previous call
16115 $matches = array();
16116 if (preg_match_all('/<cssarray>([^<]*)<\/cssarray>/isU', $html, $matches) > 0) {
16117 if (isset($matches[1][0])) {
16118 $css = array_merge($css, unserialize($this->unhtmlentities($matches[1][0])));
16119 }
16120 $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
16121 }
16122 // extract external CSS files
16123 $matches = array();
16124 if (preg_match_all('/<link([^>]*)>/isU', $html, $matches) > 0) {
16125 foreach ($matches[1] as $key => $link) {
16126 $type = array();
16127 if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
16128 $type = array();
16129 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
16130 // get 'all' and 'print' media, other media types are discarded
16131 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16132 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16133 $type = array();
16134 if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
16135 // read CSS data file
16136 $cssdata = file_get_contents(trim($type[1]));
16137 $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16138 }
16139 }
16140 }
16141 }
16142 }
16143 // extract style tags
16144 $matches = array();
16145 if (preg_match_all('/<style([^>]*)>([^<]*)<\/style>/isU', $html, $matches) > 0) {
16146 foreach ($matches[1] as $key => $media) {
16147 $type = array();
16148 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
16149 // get 'all' and 'print' media, other media types are discarded
16150 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16151 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16152 $cssdata = $matches[2][$key];
16153 $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16154 }
16155 }
16156 }
16157 // create a special tag to contain the CSS array (used for table content)
16158 $csstagarray = '<cssarray>'.htmlentities(serialize($css)).'</cssarray>';
16159 // remove head and style blocks
16160 $html = preg_replace('/<head([^>]*)>(.*?)<\/head>/siU', '', $html);
16161 $html = preg_replace('/<style([^>]*)>([^<]*)<\/style>/isU', '', $html);
16162 // define block tags
16163 $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
16164 // define self-closing tags
16165 $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
16166 // remove all unsupported tags (the line below lists all supported tags)
16167 $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>');
16168 //replace some blank characters
16169 $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
16170 $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);
16171 $html = preg_replace('@(\r\n|\r)@', "\n", $html);
16172 $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
16173 $html = strtr($html, $repTable);
16174 $offset = 0;
16175 while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
16176 $html_a = substr($html, 0, $offset);
16177 $html_b = substr($html, $offset, ($pos - $offset + 6));
16178 while (preg_match("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
16179 // preserve newlines on <pre> tag
16180 $html_b = preg_replace("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
16181 }
16182 while (preg_match("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) {
16183 // preserve spaces on <pre> tag
16184 $html_b = preg_replace("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2&nbsp;\\3</pre>", $html_b);
16185 }
16186 $html = $html_a.$html_b.substr($html, $pos + 6);
16187 $offset = strlen($html_a.$html_b);
16188 }
16189 $offset = 0;
16190 while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
16191 $html_a = substr($html, 0, $offset);
16192 $html_b = substr($html, $offset, ($pos - $offset + 11));
16193 while (preg_match("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
16194 // preserve newlines on <textarea> tag
16195 $html_b = preg_replace("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
16196 $html_b = preg_replace("'<textarea([^>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
16197 }
16198 $html = $html_a.$html_b.substr($html, $pos + 11);
16199 $offset = strlen($html_a.$html_b);
16200 }
16201 $html = preg_replace('/([\s]*)<option/si', '<option', $html);
16202 $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
16203 $offset = 0;
16204 while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
16205 $html_a = substr($html, 0, $offset);
16206 $html_b = substr($html, $offset, ($pos - $offset + 9));
16207 while (preg_match("'<option([^>]*)>(.*?)</option>'si", $html_b)) {
16208 $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
16209 $html_b = preg_replace("'<option([^>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
16210 }
16211 $html = $html_a.$html_b.substr($html, $pos + 9);
16212 $offset = strlen($html_a.$html_b);
16213 }
16214 if (preg_match("'</select'si", $html)) {
16215 $html = preg_replace("'<select([^>]*)>'si", "<select\\1 opt=\"", $html);
16216 $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
16217 }
16218 $html = str_replace("\n", ' ', $html);
16219 // restore textarea newlines
16220 $html = str_replace('<TBR>', "\n", $html);
16221 // remove extra spaces from code
16222 $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
16223 $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html);
16224 $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
16225 $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html);
16226 $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);
16227 $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
16228 $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
16229 $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html);
16230 $html = preg_replace('/<img([^>]*)>[\s]+([^<])/xi', '<img\\1>&nbsp;\\2', $html);
16231 $html = preg_replace('/<img([^>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
16232 $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
16233 $html = preg_replace('/<textarea([^>]*)>([^<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
16234 $html = preg_replace('/<li([^>]*)><\/li>/', '<li\\1>&nbsp;</li>', $html);
16235 $html = preg_replace('/<li([^>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1">&nbsp;</font><img', $html);
16236 $html = preg_replace('/<([^>\/]*)>[\s]/', '<\\1>&nbsp;', $html); // preserve some spaces
16237 $html = preg_replace('/[\s]<\/([^>]*)>/', '&nbsp;</\\1>', $html); // preserve some spaces
16238 $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment
16239 $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment
16240 $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space
16241 // trim string
16242 $html = $this->stringTrim($html);
16243 // fix first image tag alignment
16244 $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
16245 // pattern for generic tag
16246 $tagpattern = '/(<[^>]+>)/';
16247 // explodes the string
16248 $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
16249 // count elements
16250 $maxel = count($a);
16251 $elkey = 0;
16252 $key = 0;
16253 // create an array of elements
16254 $dom = array();
16255 $dom[$key] = array();
16256 // set inheritable properties fot the first void element
16257 // 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
16258 $dom[$key]['tag'] = false;
16259 $dom[$key]['block'] = false;
16260 $dom[$key]['value'] = '';
16261 $dom[$key]['parent'] = 0;
16262 $dom[$key]['hide'] = false;
16263 $dom[$key]['fontname'] = $this->FontFamily;
16264 $dom[$key]['fontstyle'] = $this->FontStyle;
16265 $dom[$key]['fontsize'] = $this->FontSizePt;
16266 $dom[$key]['font-stretch'] = $this->font_stretching;
16267 $dom[$key]['letter-spacing'] = $this->font_spacing;
16268 $dom[$key]['stroke'] = $this->textstrokewidth;
16269 $dom[$key]['fill'] = (($this->textrendermode % 2) == 0);
16270 $dom[$key]['clip'] = ($this->textrendermode > 3);
16271 $dom[$key]['line-height'] = $this->cell_height_ratio;
16272 $dom[$key]['bgcolor'] = false;
16273 $dom[$key]['fgcolor'] = $this->fgcolor; // color
16274 $dom[$key]['strokecolor'] = $this->strokecolor;
16275 $dom[$key]['align'] = '';
16276 $dom[$key]['listtype'] = '';
16277 $dom[$key]['text-indent'] = 0;
16278 $dom[$key]['border'] = array();
16279 $dom[$key]['dir'] = $this->rtl?'rtl':'ltr';
16280 $thead = false; // true when we are inside the THEAD tag
16281 ++$key;
16282 $level = array();
16283 array_push($level, 0); // root
16284 while ($elkey < $maxel) {
16285 $dom[$key] = array();
16286 $element = $a[$elkey];
16287 $dom[$key]['elkey'] = $elkey;
16288 if (preg_match($tagpattern, $element)) {
16289 // html tag
16290 $element = substr($element, 1, -1);
16291 // get tag name
16292 preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
16293 $tagname = strtolower($tag[1]);
16294 // check if we are inside a table header
16295 if ($tagname == 'thead') {
16296 if ($element{0} == '/') {
16297 $thead = false;
16298 } else {
16299 $thead = true;
16300 }
16301 ++$elkey;
16302 continue;
16303 }
16304 $dom[$key]['tag'] = true;
16305 $dom[$key]['value'] = $tagname;
16306 if (in_array($dom[$key]['value'], $blocktags)) {
16307 $dom[$key]['block'] = true;
16308 } else {
16309 $dom[$key]['block'] = false;
16310 }
16311 if ($element{0} == '/') {
16312 // *** closing html tag
16313 $dom[$key]['opening'] = false;
16314 $dom[$key]['parent'] = end($level);
16315 array_pop($level);
16316 $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
16317 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
16318 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
16319 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
16320 $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
16321 $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
16322 $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
16323 $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
16324 $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
16325 $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
16326 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
16327 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
16328 $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
16329 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
16330 $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
16331 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
16332 $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
16333 }
16334 // set the number of columns in table tag
16335 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
16336 $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
16337 }
16338 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
16339 $dom[($dom[$key]['parent'])]['content'] = $csstagarray;
16340 for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
16341 $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
16342 }
16343 $key = $i;
16344 // mark nested tables
16345 $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
16346 // remove thead sections from nested tables
16347 $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
16348 $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
16349 }
16350 // store header rows on a new table
16351 if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
16352 if (TCPDF_STATIC::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
16353 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
16354 }
16355 for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
16356 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
16357 }
16358 if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
16359 $dom[($dom[$key]['parent'])]['attribute'] = array();
16360 }
16361 // header elements must be always contained in a single page
16362 $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
16363 }
16364 if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC::empty_string($dom[($dom[$key]['parent'])]['thead']))) {
16365 // remove the nobr attributes from the table header
16366 $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
16367 $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
16368 }
16369 } else {
16370 // *** opening or self-closing html tag
16371 $dom[$key]['opening'] = true;
16372 $dom[$key]['parent'] = end($level);
16373 if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
16374 // self-closing tag
16375 $dom[$key]['self'] = true;
16376 } else {
16377 // opening tag
16378 array_push($level, $key);
16379 $dom[$key]['self'] = false;
16380 }
16381 // copy some values from parent
16382 $parentkey = 0;
16383 if ($key > 0) {
16384 $parentkey = $dom[$key]['parent'];
16385 $dom[$key]['hide'] = $dom[$parentkey]['hide'];
16386 $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
16387 $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
16388 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
16389 $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
16390 $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
16391 $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
16392 $dom[$key]['fill'] = $dom[$parentkey]['fill'];
16393 $dom[$key]['clip'] = $dom[$parentkey]['clip'];
16394 $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16395 $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
16396 $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
16397 $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
16398 $dom[$key]['align'] = $dom[$parentkey]['align'];
16399 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16400 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16401 $dom[$key]['border'] = array();
16402 $dom[$key]['dir'] = $dom[$parentkey]['dir'];
16403 }
16404 // get attributes
16405 preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER);
16406 $dom[$key]['attribute'] = array(); // reset attribute array
16407 while (list($id, $name) = each($attr_array[1])) {
16408 $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
16409 }
16410 if (!empty($css)) {
16411 // merge CSS style to current style
16412 list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC::getCSSdataArray($dom, $key, $css);
16413 $dom[$key]['attribute']['style'] = TCPDF_STATIC::getTagStyleFromCSSarray($dom[$key]['cssdata']);
16414 }
16415 // split style attributes
16416 if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
16417 // get style attributes
16418 preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
16419 $dom[$key]['style'] = array(); // reset style attribute array
16420 while (list($id, $name) = each($style_array[1])) {
16421 // in case of duplicate attribute the last replace the previous
16422 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
16423 }
16424 // --- get some style attributes ---
16425 // text direction
16426 if (isset($dom[$key]['style']['direction'])) {
16427 $dom[$key]['dir'] = $dom[$key]['style']['direction'];
16428 }
16429 // display
16430 if (isset($dom[$key]['style']['display'])) {
16431 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
16432 }
16433 // font family
16434 if (isset($dom[$key]['style']['font-family'])) {
16435 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
16436 }
16437 // list-style-type
16438 if (isset($dom[$key]['style']['list-style-type'])) {
16439 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
16440 if ($dom[$key]['listtype'] == 'inherit') {
16441 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16442 }
16443 }
16444 // text-indent
16445 if (isset($dom[$key]['style']['text-indent'])) {
16446 $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
16447 if ($dom[$key]['text-indent'] == 'inherit') {
16448 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16449 }
16450 }
16451 // font size
16452 if (isset($dom[$key]['style']['font-size'])) {
16453 $fsize = trim($dom[$key]['style']['font-size']);
16454 $dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt');
16455 }
16456 // font-stretch
16457 if (isset($dom[$key]['style']['font-stretch'])) {
16458 $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
16459 }
16460 // letter-spacing
16461 if (isset($dom[$key]['style']['letter-spacing'])) {
16462 $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
16463 }
16464 // line-height
16465 if (isset($dom[$key]['style']['line-height'])) {
16466 $lineheight = trim($dom[$key]['style']['line-height']);
16467 switch ($lineheight) {
16468 // A normal line height. This is default
16469 case 'normal': {
16470 $dom[$key]['line-height'] = $dom[0]['line-height'];
16471 break;
16472 }
16473 default: {
16474 if (is_numeric($lineheight)) {
16475 $lineheight = $lineheight * 100;
16476 }
16477 $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
16478 }
16479 }
16480 }
16481 // font style
16482 if (isset($dom[$key]['style']['font-weight'])) {
16483 if (strtolower($dom[$key]['style']['font-weight']{0}) == 'n') {
16484 if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
16485 $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
16486 }
16487 } elseif (strtolower($dom[$key]['style']['font-weight']{0}) == 'b') {
16488 $dom[$key]['fontstyle'] .= 'B';
16489 }
16490 }
16491 if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
16492 $dom[$key]['fontstyle'] .= 'I';
16493 }
16494 // font color
16495 if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['color']))) {
16496 $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors);
16497 } elseif ($dom[$key]['value'] == 'a') {
16498 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16499 }
16500 // background color
16501 if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['background-color']))) {
16502 $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors);
16503 }
16504 // text-decoration
16505 if (isset($dom[$key]['style']['text-decoration'])) {
16506 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
16507 foreach ($decors as $dec) {
16508 $dec = trim($dec);
16509 if (!TCPDF_STATIC::empty_string($dec)) {
16510 if ($dec{0} == 'u') {
16511 // underline
16512 $dom[$key]['fontstyle'] .= 'U';
16513 } elseif ($dec{0} == 'l') {
16514 // line-through
16515 $dom[$key]['fontstyle'] .= 'D';
16516 } elseif ($dec{0} == 'o') {
16517 // overline
16518 $dom[$key]['fontstyle'] .= 'O';
16519 }
16520 }
16521 }
16522 } elseif ($dom[$key]['value'] == 'a') {
16523 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16524 }
16525 // check for width attribute
16526 if (isset($dom[$key]['style']['width'])) {
16527 $dom[$key]['width'] = $dom[$key]['style']['width'];
16528 }
16529 // check for height attribute
16530 if (isset($dom[$key]['style']['height'])) {
16531 $dom[$key]['height'] = $dom[$key]['style']['height'];
16532 }
16533 // check for text alignment
16534 if (isset($dom[$key]['style']['text-align'])) {
16535 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
16536 }
16537 // check for CSS border properties
16538 if (isset($dom[$key]['style']['border'])) {
16539 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
16540 if (!empty($borderstyle)) {
16541 $dom[$key]['border']['LTRB'] = $borderstyle;
16542 }
16543 }
16544 if (isset($dom[$key]['style']['border-color'])) {
16545 $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
16546 if (isset($brd_colors[3])) {
16547 $dom[$key]['border']['L']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[3], $this->spot_colors);
16548 }
16549 if (isset($brd_colors[1])) {
16550 $dom[$key]['border']['R']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[1], $this->spot_colors);
16551 }
16552 if (isset($brd_colors[0])) {
16553 $dom[$key]['border']['T']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[0], $this->spot_colors);
16554 }
16555 if (isset($brd_colors[2])) {
16556 $dom[$key]['border']['B']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[2], $this->spot_colors);
16557 }
16558 }
16559 if (isset($dom[$key]['style']['border-width'])) {
16560 $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
16561 if (isset($brd_widths[3])) {
16562 $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
16563 }
16564 if (isset($brd_widths[1])) {
16565 $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
16566 }
16567 if (isset($brd_widths[0])) {
16568 $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
16569 }
16570 if (isset($brd_widths[2])) {
16571 $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
16572 }
16573 }
16574 if (isset($dom[$key]['style']['border-style'])) {
16575 $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
16576 if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) {
16577 $dom[$key]['border']['L']['cap'] = 'square';
16578 $dom[$key]['border']['L']['join'] = 'miter';
16579 $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
16580 if ($dom[$key]['border']['L']['dash'] < 0) {
16581 $dom[$key]['border']['L'] = array();
16582 }
16583 }
16584 if (isset($brd_styles[1])) {
16585 $dom[$key]['border']['R']['cap'] = 'square';
16586 $dom[$key]['border']['R']['join'] = 'miter';
16587 $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
16588 if ($dom[$key]['border']['R']['dash'] < 0) {
16589 $dom[$key]['border']['R'] = array();
16590 }
16591 }
16592 if (isset($brd_styles[0])) {
16593 $dom[$key]['border']['T']['cap'] = 'square';
16594 $dom[$key]['border']['T']['join'] = 'miter';
16595 $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
16596 if ($dom[$key]['border']['T']['dash'] < 0) {
16597 $dom[$key]['border']['T'] = array();
16598 }
16599 }
16600 if (isset($brd_styles[2])) {
16601 $dom[$key]['border']['B']['cap'] = 'square';
16602 $dom[$key]['border']['B']['join'] = 'miter';
16603 $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
16604 if ($dom[$key]['border']['B']['dash'] < 0) {
16605 $dom[$key]['border']['B'] = array();
16606 }
16607 }
16608 }
16609 $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
16610 foreach ($cellside as $bsk => $bsv) {
16611 if (isset($dom[$key]['style']['border-'.$bsv])) {
16612 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
16613 if (!empty($borderstyle)) {
16614 $dom[$key]['border'][$bsk] = $borderstyle;
16615 }
16616 }
16617 if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
16618 $dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors);
16619 }
16620 if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
16621 $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
16622 }
16623 if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
16624 $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
16625 if ($dom[$key]['border'][$bsk]['dash'] < 0) {
16626 $dom[$key]['border'][$bsk] = array();
16627 }
16628 }
16629 }
16630 // check for CSS padding properties
16631 if (isset($dom[$key]['style']['padding'])) {
16632 $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
16633 } else {
16634 $dom[$key]['padding'] = $this->cell_padding;
16635 }
16636 foreach ($cellside as $psk => $psv) {
16637 if (isset($dom[$key]['style']['padding-'.$psv])) {
16638 $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
16639 }
16640 }
16641 // check for CSS margin properties
16642 if (isset($dom[$key]['style']['margin'])) {
16643 $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
16644 } else {
16645 $dom[$key]['margin'] = $this->cell_margin;
16646 }
16647 foreach ($cellside as $psk => $psv) {
16648 if (isset($dom[$key]['style']['margin-'.$psv])) {
16649 $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
16650 }
16651 }
16652 // check for CSS border-spacing properties
16653 if (isset($dom[$key]['style']['border-spacing'])) {
16654 $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
16655 }
16656 // page-break-inside
16657 if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
16658 $dom[$key]['attribute']['nobr'] = 'true';
16659 }
16660 // page-break-before
16661 if (isset($dom[$key]['style']['page-break-before'])) {
16662 if ($dom[$key]['style']['page-break-before'] == 'always') {
16663 $dom[$key]['attribute']['pagebreak'] = 'true';
16664 } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
16665 $dom[$key]['attribute']['pagebreak'] = 'left';
16666 } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
16667 $dom[$key]['attribute']['pagebreak'] = 'right';
16668 }
16669 }
16670 // page-break-after
16671 if (isset($dom[$key]['style']['page-break-after'])) {
16672 if ($dom[$key]['style']['page-break-after'] == 'always') {
16673 $dom[$key]['attribute']['pagebreakafter'] = 'true';
16674 } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
16675 $dom[$key]['attribute']['pagebreakafter'] = 'left';
16676 } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
16677 $dom[$key]['attribute']['pagebreakafter'] = 'right';
16678 }
16679 }
16680 }
16681 if (isset($dom[$key]['attribute']['display'])) {
16682 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
16683 }
16684 if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
16685 $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
16686 if (!empty($borderstyle)) {
16687 $dom[$key]['border']['LTRB'] = $borderstyle;
16688 }
16689 }
16690 // check for font tag
16691 if ($dom[$key]['value'] == 'font') {
16692 // font family
16693 if (isset($dom[$key]['attribute']['face'])) {
16694 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
16695 }
16696 // font size
16697 if (isset($dom[$key]['attribute']['size'])) {
16698 if ($key > 0) {
16699 if ($dom[$key]['attribute']['size']{0} == '+') {
16700 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
16701 } elseif ($dom[$key]['attribute']['size']{0} == '-') {
16702 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
16703 } else {
16704 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16705 }
16706 } else {
16707 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16708 }
16709 }
16710 }
16711 // force natural alignment for lists
16712 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
16713 AND (!isset($dom[$key]['align']) OR TCPDF_STATIC::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
16714 if ($this->rtl) {
16715 $dom[$key]['align'] = 'R';
16716 } else {
16717 $dom[$key]['align'] = 'L';
16718 }
16719 }
16720 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
16721 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16722 $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
16723 }
16724 }
16725 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
16726 $dom[$key]['fontstyle'] .= 'B';
16727 }
16728 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
16729 $dom[$key]['fontstyle'] .= 'I';
16730 }
16731 if ($dom[$key]['value'] == 'u') {
16732 $dom[$key]['fontstyle'] .= 'U';
16733 }
16734 if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
16735 $dom[$key]['fontstyle'] .= 'D';
16736 }
16737 if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
16738 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16739 }
16740 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
16741 $dom[$key]['fontname'] = $this->default_monospaced_font;
16742 }
16743 if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
16744 // headings h1, h2, h3, h4, h5, h6
16745 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16746 $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
16747 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
16748 }
16749 if (!isset($dom[$key]['style']['font-weight'])) {
16750 $dom[$key]['fontstyle'] .= 'B';
16751 }
16752 }
16753 if (($dom[$key]['value'] == 'table')) {
16754 $dom[$key]['rows'] = 0; // number of rows
16755 $dom[$key]['trids'] = array(); // IDs of TR elements
16756 $dom[$key]['thead'] = ''; // table header rows
16757 }
16758 if (($dom[$key]['value'] == 'tr')) {
16759 $dom[$key]['cols'] = 0;
16760 if ($thead) {
16761 $dom[$key]['thead'] = true;
16762 // rows on thead block are printed as a separate table
16763 } else {
16764 $dom[$key]['thead'] = false;
16765 // store the number of rows on table element
16766 ++$dom[($dom[$key]['parent'])]['rows'];
16767 // store the TR elements IDs on table element
16768 array_push($dom[($dom[$key]['parent'])]['trids'], $key);
16769 }
16770 }
16771 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
16772 if (isset($dom[$key]['attribute']['colspan'])) {
16773 $colspan = intval($dom[$key]['attribute']['colspan']);
16774 } else {
16775 $colspan = 1;
16776 }
16777 $dom[$key]['attribute']['colspan'] = $colspan;
16778 $dom[($dom[$key]['parent'])]['cols'] += $colspan;
16779 }
16780 // text direction
16781 if (isset($dom[$key]['attribute']['dir'])) {
16782 $dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
16783 }
16784 // set foreground color attribute
16785 if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['color']))) {
16786 $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors);
16787 } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
16788 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16789 }
16790 // set background color attribute
16791 if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['bgcolor']))) {
16792 $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors);
16793 }
16794 // set stroke color attribute
16795 if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['strokecolor']))) {
16796 $dom[$key]['strokecolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors);
16797 }
16798 // check for width attribute
16799 if (isset($dom[$key]['attribute']['width'])) {
16800 $dom[$key]['width'] = $dom[$key]['attribute']['width'];
16801 }
16802 // check for height attribute
16803 if (isset($dom[$key]['attribute']['height'])) {
16804 $dom[$key]['height'] = $dom[$key]['attribute']['height'];
16805 }
16806 // check for text alignment
16807 if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
16808 $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
16809 }
16810 // check for text rendering mode (the following attributes do not exist in HTML)
16811 if (isset($dom[$key]['attribute']['stroke'])) {
16812 // font stroke width
16813 $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
16814 }
16815 if (isset($dom[$key]['attribute']['fill'])) {
16816 // font fill
16817 if ($dom[$key]['attribute']['fill'] == 'true') {
16818 $dom[$key]['fill'] = true;
16819 } else {
16820 $dom[$key]['fill'] = false;
16821 }
16822 }
16823 if (isset($dom[$key]['attribute']['clip'])) {
16824 // clipping mode
16825 if ($dom[$key]['attribute']['clip'] == 'true') {
16826 $dom[$key]['clip'] = true;
16827 } else {
16828 $dom[$key]['clip'] = false;
16829 }
16830 }
16831 } // end opening tag
16832 } else {
16833 // text
16834 $dom[$key]['tag'] = false;
16835 $dom[$key]['block'] = false;
16836 //$element = str_replace('&nbsp;', TCPDF_FONTS::unichr(160, $this->isunicode), $element);
16837 $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
16838 $dom[$key]['parent'] = end($level);
16839 $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
16840 }
16841 ++$elkey;
16842 ++$key;
16843 }
16844 return $dom;
16845 }
16846
16854 protected function getSpaceString() {
16855 $spacestr = chr(32);
16856 if ($this->isUnicodeFont()) {
16857 $spacestr = chr(0).chr(32);
16858 }
16859 return $spacestr;
16860 }
16861
16870 public function serializeTCPDFtagParameters($pararray) {
16872 }
16873
16896 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
16897 return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
16898 }
16899
16913 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
16914 $gvars = $this->getGraphicVars();
16915 // store current values
16916 $prev_cell_margin = $this->cell_margin;
16917 $prev_cell_padding = $this->cell_padding;
16918 $prevPage = $this->page;
16919 $prevlMargin = $this->lMargin;
16920 $prevrMargin = $this->rMargin;
16921 $curfontname = $this->FontFamily;
16922 $curfontstyle = $this->FontStyle;
16923 $curfontsize = $this->FontSizePt;
16924 $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
16925 $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
16926 $curfontstretcing = $this->font_stretching;
16927 $curfonttracking = $this->font_spacing;
16928 $this->newline = true;
16929 $newline = true;
16930 $startlinepage = $this->page;
16931 $minstartliney = $this->y;
16932 $maxbottomliney = 0;
16933 $startlinex = $this->x;
16934 $startliney = $this->y;
16935 $yshift = 0;
16936 $loop = 0;
16937 $curpos = 0;
16938 $this_method_vars = array();
16939 $undo = false;
16940 $fontaligned = false;
16941 $reverse_dir = false; // true when the text direction is reversed
16942 $this->premode = false;
16943 if ($this->inxobj) {
16944 // we are inside an XObject template
16945 $pask = count($this->xobjects[$this->xobjid]['annotations']);
16946 } elseif (isset($this->PageAnnots[$this->page])) {
16947 $pask = count($this->PageAnnots[$this->page]);
16948 } else {
16949 $pask = 0;
16950 }
16951 if ($this->inxobj) {
16952 // we are inside an XObject template
16953 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
16954 } elseif (!$this->InFooter) {
16955 if (isset($this->footerlen[$this->page])) {
16956 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
16957 } else {
16958 $this->footerpos[$this->page] = $this->pagelen[$this->page];
16959 }
16960 $startlinepos = $this->footerpos[$this->page];
16961 } else {
16962 // we are inside the footer
16963 $startlinepos = $this->pagelen[$this->page];
16964 }
16965 $lalign = $align;
16966 $plalign = $align;
16967 if ($this->rtl) {
16968 $w = $this->x - $this->lMargin;
16969 } else {
16970 $w = $this->w - $this->rMargin - $this->x;
16971 }
16972 $w -= ($this->cell_padding['L'] + $this->cell_padding['R']);
16973 if ($cell) {
16974 if ($this->rtl) {
16975 $this->x -= $this->cell_padding['R'];
16976 $this->lMargin += $this->cell_padding['R'];
16977 } else {
16978 $this->x += $this->cell_padding['L'];
16979 $this->rMargin += $this->cell_padding['L'];
16980 }
16981 }
16982 if ($this->customlistindent >= 0) {
16983 $this->listindent = $this->customlistindent;
16984 } else {
16985 $this->listindent = $this->GetStringWidth('000000');
16986 }
16987 $this->listindentlevel = 0;
16988 // save previous states
16989 $prev_cell_height_ratio = $this->cell_height_ratio;
16990 $prev_listnum = $this->listnum;
16991 $prev_listordered = $this->listordered;
16992 $prev_listcount = $this->listcount;
16993 $prev_lispacer = $this->lispacer;
16994 $this->listnum = 0;
16995 $this->listordered = array();
16996 $this->listcount = array();
16997 $this->lispacer = '';
16998 if ((TCPDF_STATIC::empty_string($this->lasth)) OR ($reseth)) {
16999 // reset row height
17000 $this->resetLastH();
17001 }
17002 $dom = $this->getHtmlDomArray($html);
17003 $maxel = count($dom);
17004 $key = 0;
17005 $hidden_node_key = -1;
17006 while ($key < $maxel) {
17007 if ($dom[$key]['tag']) {
17008 if ($dom[$key]['opening']) {
17009 if (($hidden_node_key <= 0) AND $dom[$key]['hide']) {
17010 // store the node key
17011 $hidden_node_key = $key;
17012 }
17013 } elseif (($hidden_node_key > 0) AND ($dom[$key]['parent'] == $hidden_node_key)) {
17014 // we have reached the closing tag of the hidden node
17015 $hidden_node_key = 0;
17016 }
17017 }
17018 if ($hidden_node_key >= 0) {
17019 // skip this node
17020 ++$key;
17021 if ($hidden_node_key == 0) {
17022 // reset hidden mode
17023 $hidden_node_key = -1;
17024 }
17025 continue;
17026 }
17027 if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
17028 // check for pagebreak
17029 if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
17030 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17031 $this->checkPageBreak($this->PageBreakTrigger + 1);
17032 $this->htmlvspace = ($this->PageBreakTrigger + 1);
17033 }
17034 if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
17035 OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
17036 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17037 $this->checkPageBreak($this->PageBreakTrigger + 1);
17038 $this->htmlvspace = ($this->PageBreakTrigger + 1);
17039 }
17040 }
17041 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
17042 if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
17043 $dom[$key]['attribute']['nobr'] = false;
17044 } else {
17045 // store current object
17046 $this->startTransaction();
17047 // save this method vars
17048 $this_method_vars['html'] = $html;
17049 $this_method_vars['ln'] = $ln;
17050 $this_method_vars['fill'] = $fill;
17051 $this_method_vars['reseth'] = $reseth;
17052 $this_method_vars['cell'] = $cell;
17053 $this_method_vars['align'] = $align;
17054 $this_method_vars['gvars'] = $gvars;
17055 $this_method_vars['prevPage'] = $prevPage;
17056 $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
17057 $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
17058 $this_method_vars['prevlMargin'] = $prevlMargin;
17059 $this_method_vars['prevrMargin'] = $prevrMargin;
17060 $this_method_vars['curfontname'] = $curfontname;
17061 $this_method_vars['curfontstyle'] = $curfontstyle;
17062 $this_method_vars['curfontsize'] = $curfontsize;
17063 $this_method_vars['curfontascent'] = $curfontascent;
17064 $this_method_vars['curfontdescent'] = $curfontdescent;
17065 $this_method_vars['curfontstretcing'] = $curfontstretcing;
17066 $this_method_vars['curfonttracking'] = $curfonttracking;
17067 $this_method_vars['minstartliney'] = $minstartliney;
17068 $this_method_vars['maxbottomliney'] = $maxbottomliney;
17069 $this_method_vars['yshift'] = $yshift;
17070 $this_method_vars['startlinepage'] = $startlinepage;
17071 $this_method_vars['startlinepos'] = $startlinepos;
17072 $this_method_vars['startlinex'] = $startlinex;
17073 $this_method_vars['startliney'] = $startliney;
17074 $this_method_vars['newline'] = $newline;
17075 $this_method_vars['loop'] = $loop;
17076 $this_method_vars['curpos'] = $curpos;
17077 $this_method_vars['pask'] = $pask;
17078 $this_method_vars['lalign'] = $lalign;
17079 $this_method_vars['plalign'] = $plalign;
17080 $this_method_vars['w'] = $w;
17081 $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
17082 $this_method_vars['prev_listnum'] = $prev_listnum;
17083 $this_method_vars['prev_listordered'] = $prev_listordered;
17084 $this_method_vars['prev_listcount'] = $prev_listcount;
17085 $this_method_vars['prev_lispacer'] = $prev_lispacer;
17086 $this_method_vars['fontaligned'] = $fontaligned;
17087 $this_method_vars['key'] = $key;
17088 $this_method_vars['dom'] = $dom;
17089 }
17090 }
17091 // print THEAD block
17092 if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
17093 if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC::empty_string($dom[$dom[$key]['parent']]['thead'])) {
17094 $this->inthead = true;
17095 // print table header (thead)
17096 $this->writeHTML($this->thead, false, false, false, false, '');
17097 // check if we are on a new page or on a new column
17098 if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
17099 // we are on a new page or on a new column and the total object height is less than the available vertical space.
17100 // restore previous object
17101 $this->rollbackTransaction(true);
17102 // restore previous values
17103 foreach ($this_method_vars as $vkey => $vval) {
17104 $$vkey = $vval;
17105 }
17106 // disable table header
17107 $tmp_thead = $this->thead;
17108 $this->thead = '';
17109 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17110 $pre_y = $this->y;
17111 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
17112 // fix for multicolumn mode
17113 $startliney = $this->y;
17114 }
17115 $this->start_transaction_page = $this->page;
17116 $this->start_transaction_y = $this->y;
17117 // restore table header
17118 $this->thead = $tmp_thead;
17119 // fix table border properties
17120 if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
17121 $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
17122 } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
17123 $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
17124 } else {
17125 $tmp_cellspacing = 0;
17126 }
17127 $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
17128 $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
17129 $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
17130 $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']);
17131 $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
17132 $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
17133 // print table header (thead)
17134 $this->writeHTML($this->thead, false, false, false, false, '');
17135 }
17136 }
17137 // move $key index forward to skip THEAD block
17138 while ( ($key < $maxel) AND (!(
17139 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
17140 OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
17141 ++$key;
17142 }
17143 }
17144 if ($dom[$key]['tag'] OR ($key == 0)) {
17145 if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
17146 $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
17147 }
17148 // vertically align image in line
17149 if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
17150 // get image height
17151 $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], $this->lasth, 'px');
17152 $autolinebreak = false;
17153 if (isset($dom[$key]['width']) AND ($dom[$key]['width'] > 0)) {
17154 $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], 1, 'px', false);
17155 if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R']))
17156 AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L'])))
17157 OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) {
17158 // add automatic line break
17159 $autolinebreak = true;
17160 $this->Ln('', $cell);
17161 if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
17162 // go back to evaluate this line break
17163 --$key;
17164 }
17165 }
17166 }
17167 if (!$autolinebreak) {
17168 if ($this->inPageBody()) {
17169 $pre_y = $this->y;
17170 // check for page break
17171 if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
17172 // fix for multicolumn mode
17173 $startliney = $this->y;
17174 }
17175 }
17176 if ($this->page > $startlinepage) {
17177 // fix line splitted over two pages
17178 if (isset($this->footerlen[$startlinepage])) {
17179 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17180 }
17181 // line to be moved one page forward
17182 $pagebuff = $this->getPageBuffer($startlinepage);
17183 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17184 $tstart = substr($pagebuff, 0, $startlinepos);
17185 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17186 // remove line from previous page
17187 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17188 $pagebuff = $this->getPageBuffer($this->page);
17189 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17190 $tend = substr($pagebuff, $this->cntmrk[$this->page]);
17191 // add line start to current page
17192 $yshift = ($minstartliney - $this->y);
17193 if ($fontaligned) {
17194 $yshift += ($curfontsize / $this->k);
17195 }
17196 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17197 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17198 // shift the annotations and links
17199 if (isset($this->PageAnnots[$this->page])) {
17200 $next_pask = count($this->PageAnnots[$this->page]);
17201 } else {
17202 $next_pask = 0;
17203 }
17204 if (isset($this->PageAnnots[$startlinepage])) {
17205 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17206 if ($pak >= $pask) {
17207 $this->PageAnnots[$this->page][] = $pac;
17208 unset($this->PageAnnots[$startlinepage][$pak]);
17209 $npak = count($this->PageAnnots[$this->page]) - 1;
17210 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17211 }
17212 }
17213 }
17214 $pask = $next_pask;
17215 $startlinepos = $this->cntmrk[$this->page];
17216 $startlinepage = $this->page;
17217 $startliney = $this->y;
17218 $this->newline = false;
17219 }
17220 $this->y += ((($curfontsize * $this->cell_height_ratio / $this->k) + $curfontascent - $curfontdescent) / 2) - $imgh;
17221 $minstartliney = min($this->y, $minstartliney);
17222 $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio));
17223 }
17224 } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
17225 // account for different font size
17226 $pfontname = $curfontname;
17227 $pfontstyle = $curfontstyle;
17228 $pfontsize = $curfontsize;
17229 $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
17230 $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
17231 $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
17232 $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
17233 $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
17234 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
17235 OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17236 OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
17237 if (($key < ($maxel - 1)) AND (
17238 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
17239 OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17240 OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize) AND ($fontsize >= 0) AND ($curfontsize >= 0) AND ($fontsize != $curfontsize))
17241 )) {
17242 if ($this->page > $startlinepage) {
17243 // fix lines splitted over two pages
17244 if (isset($this->footerlen[$startlinepage])) {
17245 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17246 }
17247 // line to be moved one page forward
17248 $pagebuff = $this->getPageBuffer($startlinepage);
17249 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17250 $tstart = substr($pagebuff, 0, $startlinepos);
17251 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17252 // remove line start from previous page
17253 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17254 $pagebuff = $this->getPageBuffer($this->page);
17255 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17256 $tend = substr($pagebuff, $this->cntmrk[$this->page]);
17257 // add line start to current page
17258 $yshift = ($minstartliney - $this->y);
17259 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17260 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17261 // shift the annotations and links
17262 if (isset($this->PageAnnots[$this->page])) {
17263 $next_pask = count($this->PageAnnots[$this->page]);
17264 } else {
17265 $next_pask = 0;
17266 }
17267 if (isset($this->PageAnnots[$startlinepage])) {
17268 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17269 if ($pak >= $pask) {
17270 $this->PageAnnots[$this->page][] = $pac;
17271 unset($this->PageAnnots[$startlinepage][$pak]);
17272 $npak = count($this->PageAnnots[$this->page]) - 1;
17273 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17274 }
17275 }
17276 }
17277 $pask = $next_pask;
17278 $startlinepos = $this->cntmrk[$this->page];
17279 $startlinepage = $this->page;
17280 $startliney = $this->y;
17281 }
17282 if (!isset($dom[$key]['line-height'])) {
17283 $dom[$key]['line-height'] = $this->cell_height_ratio;
17284 }
17285 if (!$dom[$key]['block']) {
17286 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']))) {
17287 $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
17288 }
17289 if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
17290 $current_line_align_data = array($key, $minstartliney, $maxbottomliney);
17291 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)))) {
17292 $minstartliney = min($this->y, $line_align_data[1]);
17293 $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $line_align_data[2]);
17294 } else {
17295 $minstartliney = min($this->y, $minstartliney);
17296 $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);
17297 }
17298 $line_align_data = $current_line_align_data;
17299 }
17300 }
17301 $this->cell_height_ratio = $dom[$key]['line-height'];
17302 $fontaligned = true;
17303 }
17304 $this->SetFont($fontname, $fontstyle, $fontsize);
17305 // reset row height
17306 $this->resetLastH();
17307 $curfontname = $fontname;
17308 $curfontstyle = $fontstyle;
17309 $curfontsize = $fontsize;
17310 $curfontascent = $fontascent;
17311 $curfontdescent = $fontdescent;
17312 }
17313 }
17314 // set text rendering mode
17315 $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
17316 $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
17317 $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
17318 $this->setTextRenderingMode($textstroke, $textfill, $textclip);
17319 if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
17320 $this->setFontStretching($dom[$key]['font-stretch']);
17321 }
17322 if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
17323 $this->setFontSpacing($dom[$key]['letter-spacing']);
17324 }
17325 if (($plalign == 'J') AND $dom[$key]['block']) {
17326 $plalign = '';
17327 }
17328 // get current position on page buffer
17329 $curpos = $this->pagelen[$startlinepage];
17330 if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
17331 $this->SetFillColorArray($dom[$key]['bgcolor']);
17332 $wfill = true;
17333 } else {
17334 $wfill = $fill | false;
17335 }
17336 if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
17337 $this->SetTextColorArray($dom[$key]['fgcolor']);
17338 }
17339 if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
17340 $this->SetDrawColorArray($dom[$key]['strokecolor']);
17341 }
17342 if (isset($dom[$key]['align'])) {
17343 $lalign = $dom[$key]['align'];
17344 }
17345 if (TCPDF_STATIC::empty_string($lalign)) {
17346 $lalign = $align;
17347 }
17348 }
17349 // align lines
17350 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
17351 $newline = true;
17352 $fontaligned = false;
17353 // we are at the beginning of a new line
17354 if (isset($startlinex)) {
17355 $yshift = ($minstartliney - $startliney);
17356 if (($yshift > 0) OR ($this->page > $startlinepage)) {
17357 $yshift = 0;
17358 }
17359 $t_x = 0;
17360 // the last line must be shifted to be aligned as requested
17361 $linew = abs($this->endlinex - $startlinex);
17362 if ($this->inxobj) {
17363 // we are inside an XObject template
17364 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
17365 if (isset($opentagpos)) {
17366 $midpos = $opentagpos;
17367 } else {
17368 $midpos = 0;
17369 }
17370 if ($midpos > 0) {
17371 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
17372 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
17373 } else {
17374 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
17375 $pend = '';
17376 }
17377 } else {
17378 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
17379 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17380 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17381 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
17382 } elseif (isset($opentagpos)) {
17383 $midpos = $opentagpos;
17384 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17385 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17386 $midpos = $this->footerpos[$startlinepage];
17387 } else {
17388 $midpos = 0;
17389 }
17390 if ($midpos > 0) {
17391 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
17392 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
17393 } else {
17394 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
17395 $pend = '';
17396 }
17397 }
17398 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
17399 // calculate shifting amount
17400 $tw = $w;
17401 if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
17402 $tw += $this->cell_padding['R'];
17403 }
17404 if ($this->lMargin != $prevlMargin) {
17405 $tw += ($prevlMargin - $this->lMargin);
17406 }
17407 if ($this->rMargin != $prevrMargin) {
17408 $tw += ($prevrMargin - $this->rMargin);
17409 }
17410 $one_space_width = $this->GetStringWidth(chr(32));
17411 $no = 0; // number of spaces on a line contained on a single block
17412 if ($this->isRTLTextDir()) { // RTL
17413 // remove left space if exist
17414 $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
17415 if ($pos1 > 0) {
17416 $pos1 = intval($pos1);
17417 if ($this->isUnicodeFont()) {
17418 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
17419 $spacelen = 2;
17420 } else {
17421 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
17422 $spacelen = 1;
17423 }
17424 if ($pos1 == $pos2) {
17425 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
17426 if (substr($pmid, $pos1, 4) == '[()]') {
17427 $linew -= $one_space_width;
17428 } elseif ($pos1 == strpos($pmid, '[(')) {
17429 $no = 1;
17430 }
17431 }
17432 }
17433 } else { // LTR
17434 // remove right space if exist
17435 $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
17436 if ($pos1 > 0) {
17437 $pos1 = intval($pos1);
17438 if ($this->isUnicodeFont()) {
17439 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
17440 $spacelen = 2;
17441 } else {
17442 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
17443 $spacelen = 1;
17444 }
17445 if ($pos1 == $pos2) {
17446 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
17447 $linew -= $one_space_width;
17448 }
17449 }
17450 }
17451 $mdiff = ($tw - $linew);
17452 if ($plalign == 'C') {
17453 if ($this->rtl) {
17454 $t_x = -($mdiff / 2);
17455 } else {
17456 $t_x = ($mdiff / 2);
17457 }
17458 } elseif ($plalign == 'R') {
17459 // right alignment on LTR document
17460 $t_x = $mdiff;
17461 } elseif ($plalign == 'L') {
17462 // left alignment on RTL document
17463 $t_x = -$mdiff;
17464 } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
17465 // Justification
17466 if ($this->isRTLTextDir()) {
17467 // align text on the left
17468 $t_x = -$mdiff;
17469 }
17470 $ns = 0; // number of spaces
17471 $pmidtemp = $pmid;
17472 // escape special characters
17473 $pmidtemp = preg_replace('/[\\\][\‍(]/x', '\\#!#OP#!#', $pmidtemp);
17474 $pmidtemp = preg_replace('/[\\\][\‍)]/x', '\\#!#CP#!#', $pmidtemp);
17475 // search spaces
17476 if (preg_match_all('/\[\‍(([^\‍)]*)\‍)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
17477 $spacestr = $this->getSpaceString();
17478 $maxkk = count($lnstring[1]) - 1;
17479 for ($kk=0; $kk <= $maxkk; ++$kk) {
17480 // restore special characters
17481 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
17482 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
17483 // store number of spaces on the strings
17484 $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
17485 // count total spaces on line
17486 $ns += $lnstring[2][$kk];
17487 $lnstring[3][$kk] = $ns;
17488 }
17489 if ($ns == 0) {
17490 $ns = 1;
17491 }
17492 // calculate additional space to add to each existing space
17493 $spacewidth = ($mdiff / ($ns - $no)) * $this->k;
17494 $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize;
17495 if ($this->font_spacing != 0) {
17496 // fixed spacing mode
17497 $osw = -1000 * $this->font_spacing / $this->FontSize;
17498 $spacewidthu += $osw;
17499 }
17500 $nsmax = $ns;
17501 $ns = 0;
17502 reset($lnstring);
17503 $offset = 0;
17504 $strcount = 0;
17505 $prev_epsposbeg = 0;
17506 $textpos = 0;
17507 if ($this->isRTLTextDir()) {
17508 $textpos = $this->wPt;
17509 }
17510 global $spacew;
17511 while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
17512 // check if we are inside a string section '[( ... )]'
17513 $stroffset = strpos($pmid, '[(', $offset);
17514 if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
17515 // set offset to the end of string section
17516 $offset = strpos($pmid, ')]', $stroffset);
17517 while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
17518 $offset = strpos($pmid, ')]', ($offset + 1));
17519 }
17520 if ($offset === false) {
17521 $this->Error('HTML Justification: malformed PDF code.');
17522 }
17523 continue;
17524 }
17525 if ($this->isRTLTextDir()) {
17526 $spacew = ($spacewidth * ($nsmax - $ns));
17527 } else {
17528 $spacew = ($spacewidth * $ns);
17529 }
17530 $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
17531 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
17532 $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
17533 if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
17534 OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
17535 // shift EPS images
17536 $trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
17537 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
17538 $pmid_b = substr($pmid, 0, $epsposbeg);
17539 $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
17540 $pmid_e = substr($pmid, $epsposend);
17541 $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
17542 $offset = $epsposend;
17543 continue;
17544
17545 }
17546 $prev_epsposbeg = $epsposbeg;
17547 $currentxpos = 0;
17548 // shift blocks of code
17549 switch ($strpiece[2][0]) {
17550 case 'Td':
17551 case 'cm':
17552 case 'm':
17553 case 'l': {
17554 // get current X position
17555 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
17556 $currentxpos = $xmatches[1];
17557 $textpos = $currentxpos;
17558 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
17559 $ns = $lnstring[3][$strcount];
17560 if ($this->isRTLTextDir()) {
17561 $spacew = ($spacewidth * ($nsmax - $ns));
17562 }
17563 ++$strcount;
17564 }
17565 // justify block
17566 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
17567 create_function('$matches', 'global $spacew;
17568 $newx = sprintf("%F",(floatval($matches[1]) + $spacew));
17569 return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
17570 break;
17571 }
17572 case 're': {
17573 // justify block
17574 if (!TCPDF_STATIC::empty_string($this->lispacer)) {
17575 $this->lispacer = '';
17576 continue;
17577 }
17578 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
17579 $currentxpos = $xmatches[1];
17580 global $x_diff, $w_diff;
17581 $x_diff = 0;
17582 $w_diff = 0;
17583 if ($this->isRTLTextDir()) { // RTL
17584 if ($currentxpos < $textpos) {
17585 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
17586 $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17587 } else {
17588 if ($strcount > 0) {
17589 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
17590 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17591 }
17592 }
17593 } else { // LTR
17594 if ($currentxpos > $textpos) {
17595 if ($strcount > 0) {
17596 $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
17597 }
17598 $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17599 } else {
17600 if ($strcount > 1) {
17601 $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
17602 }
17603 if ($strcount > 0) {
17604 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17605 }
17606 }
17607 }
17608 $pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x',
17609 create_function('$matches', 'global $x_diff, $w_diff;
17610 $newx = sprintf("%F",(floatval($matches[1]) + $x_diff));
17611 $neww = sprintf("%F",(floatval($matches[3]) + $w_diff));
17612 return "".$newx." ".$matches[2]." ".$neww." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
17613 break;
17614 }
17615 case 'c': {
17616 // get current X position
17617 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);
17618 $currentxpos = $xmatches[1];
17619 // justify block
17620 $pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$xmatches[4].')[\s]('.$xmatches[5].')[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x',
17621 create_function('$matches', 'global $spacew;
17622 $newx1 = sprintf("%F",(floatval($matches[1]) + $spacew));
17623 $newx2 = sprintf("%F",(floatval($matches[3]) + $spacew));
17624 $newx3 = sprintf("%F",(floatval($matches[5]) + $spacew));
17625 return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
17626 break;
17627 }
17628 }
17629 // shift the annotations and links
17630 $cxpos = ($currentxpos / $this->k);
17631 $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps);
17632 if ($this->inxobj) {
17633 // we are inside an XObject template
17634 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17635 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17636 if ($cxpos > $lmpos) {
17637 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k);
17638 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17639 } else {
17640 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17641 }
17642 break;
17643 }
17644 }
17645 } elseif (isset($this->PageAnnots[$this->page])) {
17646 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17647 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17648 if ($cxpos > $lmpos) {
17649 $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
17650 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17651 } else {
17652 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17653 }
17654 break;
17655 }
17656 }
17657 }
17658 } // end of while
17659 // remove markers
17660 $pmid = str_replace('x*#!#*x', '', $pmid);
17661 if ($this->isUnicodeFont()) {
17662 // multibyte characters
17663 $spacew = $spacewidthu;
17664 if ($this->font_stretching != 100) {
17665 // word spacing is affected by stretching
17666 $spacew /= ($this->font_stretching / 100);
17667 }
17668 $pmidtemp = $pmid;
17669 // escape special characters
17670 $pmidtemp = preg_replace('/[\\\][\‍(]/x', '\\#!#OP#!#', $pmidtemp);
17671 $pmidtemp = preg_replace('/[\\\][\‍)]/x', '\\#!#CP#!#', $pmidtemp);
17672 $pmid = preg_replace_callback("/\[\‍(([^\‍)]*)\‍)\]/x",
17673 create_function('$matches', 'global $spacew;
17674 $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
17675 $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
17676 return "[(".str_replace(chr(0).chr(32), ") ".sprintf("%F", $spacew)." (", $matches[1]).")]";'), $pmidtemp);
17677 if ($this->inxobj) {
17678 // we are inside an XObject template
17679 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend;
17680 } else {
17681 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
17682 }
17683 $endlinepos = strlen($pstart."\n".$pmid."\n");
17684 } else {
17685 // non-unicode (single-byte characters)
17686 if ($this->font_stretching != 100) {
17687 // word spacing (Tw) is affected by stretching
17688 $spacewidth /= ($this->font_stretching / 100);
17689 }
17690 $rs = sprintf('%F Tw', $spacewidth);
17691 $pmid = preg_replace("/\[\‍(/x", $rs.' [(', $pmid);
17692 if ($this->inxobj) {
17693 // we are inside an XObject template
17694 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
17695 } else {
17696 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
17697 }
17698 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
17699 }
17700 }
17701 } // end of J
17702 } // end if $startlinex
17703 if (($t_x != 0) OR ($yshift < 0)) {
17704 // shift the line
17705 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
17706 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
17707 $endlinepos = strlen($pstart);
17708 if ($this->inxobj) {
17709 // we are inside an XObject template
17710 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
17711 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17712 if ($pak >= $pask) {
17713 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
17714 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
17715 }
17716 }
17717 } else {
17718 $this->setPageBuffer($startlinepage, $pstart.$pend);
17719 // shift the annotations and links
17720 if (isset($this->PageAnnots[$this->page])) {
17721 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17722 if ($pak >= $pask) {
17723 $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
17724 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
17725 }
17726 }
17727 }
17728 }
17729 $this->y -= $yshift;
17730 }
17731 }
17732 $pbrk = $this->checkPageBreak($this->lasth);
17733 $this->newline = false;
17734 $startlinex = $this->x;
17735 $startliney = $this->y;
17736 if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
17737 $startliney -= ((0.3 * $this->FontSizePt) / $this->k);
17738 } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
17739 $startliney -= (($this->FontSizePt / 0.7) / $this->k);
17740 } else {
17741 $minstartliney = $startliney;
17742 $maxbottomliney = ($this->y + (($fontsize * $this->cell_height_ratio) / $this->k));
17743 }
17744 $startlinepage = $this->page;
17745 if (isset($endlinepos) AND (!$pbrk)) {
17746 $startlinepos = $endlinepos;
17747 } else {
17748 if ($this->inxobj) {
17749 // we are inside an XObject template
17750 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17751 } elseif (!$this->InFooter) {
17752 if (isset($this->footerlen[$this->page])) {
17753 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17754 } else {
17755 $this->footerpos[$this->page] = $this->pagelen[$this->page];
17756 }
17757 $startlinepos = $this->footerpos[$this->page];
17758 } else {
17759 $startlinepos = $this->pagelen[$this->page];
17760 }
17761 }
17762 unset($endlinepos);
17763 $plalign = $lalign;
17764 if (isset($this->PageAnnots[$this->page])) {
17765 $pask = count($this->PageAnnots[$this->page]);
17766 } else {
17767 $pask = 0;
17768 }
17769 if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
17770 AND (isset($this->emptypagemrk[$this->page]))
17771 AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) {
17772 $this->SetFont($fontname, $fontstyle, $fontsize);
17773 if ($wfill) {
17774 $this->SetFillColorArray($this->bgcolor);
17775 }
17776 }
17777 } // end newline
17778 if (isset($opentagpos)) {
17779 unset($opentagpos);
17780 }
17781 if ($dom[$key]['tag']) {
17782 if ($dom[$key]['opening']) {
17783 // get text indentation (if any)
17784 if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
17785 $this->textindent = $dom[$key]['text-indent'];
17786 $this->newline = true;
17787 }
17788 // table
17789 if ($dom[$key]['value'] == 'table') {
17790 // available page width
17791 if ($this->rtl) {
17792 $wtmp = $this->x - $this->lMargin;
17793 } else {
17794 $wtmp = $this->w - $this->rMargin - $this->x;
17795 }
17796 // get cell spacing
17797 if (isset($dom[$key]['attribute']['cellspacing'])) {
17798 $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
17799 $cellspacing = array('H' => $clsp, 'V' => $clsp);
17800 } elseif (isset($dom[$key]['border-spacing'])) {
17801 $cellspacing = $dom[$key]['border-spacing'];
17802 } else {
17803 $cellspacing = array('H' => 0, 'V' => 0);
17804 }
17805 // table width
17806 if (isset($dom[$key]['width'])) {
17807 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
17808 } else {
17809 $table_width = $wtmp;
17810 }
17811 $table_width -= (2 * $cellspacing['H']);
17812 if (!$this->inthead) {
17813 $this->y += $cellspacing['V'];
17814 }
17815 if ($this->rtl) {
17816 $cellspacingx = -$cellspacing['H'];
17817 } else {
17818 $cellspacingx = $cellspacing['H'];
17819 }
17820 // total table width without cellspaces
17821 $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
17822 // minimum column width
17823
17824 // Begin Patch: Get rid of division by zero warning
17825 if ($table_columns_width == 0 || $dom[$key]['cols'] == 0)
17826 {
17827 $table_min_column_width = 1;
17828 }
17829 else
17830 {
17831 $table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
17832 }
17833 if ($dom[$key]['cols'] != 0)
17834 {
17835 // array of custom column widths
17836 $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
17837 }
17838 // End Patch: Get rid of division by zero warning
17839
17840 }
17841 // table row
17842 if ($dom[$key]['value'] == 'tr') {
17843 // reset column counter
17844 $colid = 0;
17845 }
17846 // table cell
17847 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
17848 $trid = $dom[$key]['parent'];
17849 $table_el = $dom[$trid]['parent'];
17850 if (!isset($dom[$table_el]['cols'])) {
17851 $dom[$table_el]['cols'] = $dom[$trid]['cols'];
17852 }
17853 // store border info
17854 $tdborder = 0;
17855 if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
17856 $tdborder = $dom[$key]['border'];
17857 }
17858 $colspan = $dom[$key]['attribute']['colspan'];
17859 $old_cell_padding = $this->cell_padding;
17860 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
17861 $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
17862 $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
17863 } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
17864 $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
17865 } else {
17866 $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
17867 }
17868 $this->cell_padding = $current_cell_padding;
17869 if (isset($dom[$key]['height'])) {
17870 // minimum cell height
17871 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
17872 } else {
17873 $cellh = 0;
17874 }
17875 if (isset($dom[$key]['content'])) {
17876 $cell_content = stripslashes($dom[$key]['content']);
17877 } else {
17878 $cell_content = '&nbsp;';
17879 }
17880 $tagtype = $dom[$key]['value'];
17881 $parentid = $key;
17882 while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
17883 // move $key index forward
17884 ++$key;
17885 }
17886 if (!isset($dom[$trid]['startpage'])) {
17887 $dom[$trid]['startpage'] = $this->page;
17888 } else {
17889 $this->setPage($dom[$trid]['startpage']);
17890 }
17891 if (!isset($dom[$trid]['startcolumn'])) {
17892 $dom[$trid]['startcolumn'] = $this->current_column;
17893 } elseif ($this->current_column != $dom[$trid]['startcolumn']) {
17894 $tmpx = $this->x;
17895 $this->selectColumn($dom[$trid]['startcolumn']);
17896 $this->x = $tmpx;
17897 }
17898 if (!isset($dom[$trid]['starty'])) {
17899 $dom[$trid]['starty'] = $this->y;
17900 } else {
17901 $this->y = $dom[$trid]['starty'];
17902 }
17903 if (!isset($dom[$trid]['startx'])) {
17904 $dom[$trid]['startx'] = $this->x;
17905 $this->x += $cellspacingx;
17906 } else {
17907 $this->x += ($cellspacingx / 2);
17908 }
17909 if (isset($dom[$parentid]['attribute']['rowspan'])) {
17910 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
17911 } else {
17912 $rowspan = 1;
17913 }
17914 // skip row-spanned cells started on the previous rows
17915 if (isset($dom[$table_el]['rowspans'])) {
17916 $rsk = 0;
17917 $rskmax = count($dom[$table_el]['rowspans']);
17918 while ($rsk < $rskmax) {
17919 $trwsp = $dom[$table_el]['rowspans'][$rsk];
17920 $rsstartx = $trwsp['startx'];
17921 $rsendx = $trwsp['endx'];
17922 // account for margin changes
17923 if ($trwsp['startpage'] < $this->page) {
17924 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
17925 $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
17926 $rsstartx -= $dl;
17927 $rsendx -= $dl;
17928 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
17929 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
17930 $rsstartx += $dl;
17931 $rsendx += $dl;
17932 }
17933 }
17934 if (($trwsp['rowspan'] > 0)
17935 AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps))
17936 AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps))
17937 AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) {
17938 // set the starting X position of the current cell
17939 $this->x = $rsendx + $cellspacingx;
17940 // increment column indicator
17941 $colid += $trwsp['colspan'];
17942 if (($trwsp['rowspan'] == 1)
17943 AND (isset($dom[$trid]['endy']))
17944 AND (isset($dom[$trid]['endpage']))
17945 AND (isset($dom[$trid]['endcolumn']))
17946 AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
17947 AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
17948 // set ending Y position for row
17949 $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
17950 $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
17951 }
17952 $rsk = 0;
17953 } else {
17954 ++$rsk;
17955 }
17956 }
17957 }
17958 if (isset($dom[$parentid]['width'])) {
17959 // user specified width
17960 $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
17961 $tmpcw = ($cellw / $colspan);
17962 for ($i = 0; $i < $colspan; ++$i) {
17963 $table_colwidths[($colid + $i)] = $tmpcw;
17964 }
17965 } else {
17966 // inherit column width
17967 $cellw = 0;
17968 for ($i = 0; $i < $colspan; ++$i) {
17969 $cellw += $table_colwidths[($colid + $i)];
17970 }
17971 }
17972 $cellw += (($colspan - 1) * $cellspacing['H']);
17973 // increment column indicator
17974 $colid += $colspan;
17975 // add rowspan information to table element
17976 if ($rowspan > 1) {
17977 $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));
17978 }
17979 $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
17980 if ($rowspan > 1) {
17981 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
17982 }
17983 // push background colors
17984 if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
17985 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
17986 }
17987 // store border info
17988 if (isset($tdborder) AND !empty($tdborder)) {
17989 $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
17990 }
17991 $prevLastH = $this->lasth;
17992 // store some info for multicolumn mode
17993 if ($this->rtl) {
17994 $this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
17995 } else {
17996 $this->colxshift['x'] = $this->x - $this->lMargin;
17997 }
17998 $this->colxshift['s'] = $cellspacing;
17999 $this->colxshift['p'] = $current_cell_padding;
18000 // ****** write the cell content ******
18001 $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
18002 // restore some values
18003 $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
18004 $this->lasth = $prevLastH;
18005 $this->cell_padding = $old_cell_padding;
18006 $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
18007 // update the end of row position
18008 if ($rowspan <= 1) {
18009 if (isset($dom[$trid]['endy'])) {
18010 if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) {
18011 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
18012 } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) {
18013 $dom[$trid]['endy'] = $this->y;
18014 }
18015 } else {
18016 $dom[$trid]['endy'] = $this->y;
18017 }
18018 if (isset($dom[$trid]['endpage'])) {
18019 $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
18020 } else {
18021 $dom[$trid]['endpage'] = $this->page;
18022 }
18023 if (isset($dom[$trid]['endcolumn'])) {
18024 $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
18025 } else {
18026 $dom[$trid]['endcolumn'] = $this->current_column;
18027 }
18028 } else {
18029 // account for row-spanned cells
18030 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
18031 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
18032 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
18033 $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column;
18034 }
18035 if (isset($dom[$table_el]['rowspans'])) {
18036 // update endy and endpage on rowspanned cells
18037 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
18038 if ($trwsp['rowspan'] > 0) {
18039 if (isset($dom[$trid]['endpage'])) {
18040 if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18041 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18042 } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
18043 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
18044 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
18045 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
18046 } else {
18047 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
18048 }
18049 }
18050 }
18051 }
18052 }
18053 $this->x += ($cellspacingx / 2);
18054 } else {
18055 // opening tag (or self-closing tag)
18056 if (!isset($opentagpos)) {
18057 if ($this->inxobj) {
18058 // we are inside an XObject template
18059 $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
18060 } elseif (!$this->InFooter) {
18061 if (isset($this->footerlen[$this->page])) {
18062 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
18063 } else {
18064 $this->footerpos[$this->page] = $this->pagelen[$this->page];
18065 }
18066 $opentagpos = $this->footerpos[$this->page];
18067 }
18068 }
18069 $dom = $this->openHTMLTagHandler($dom, $key, $cell);
18070 }
18071 } else { // closing tag
18072 $prev_numpages = $this->numpages;
18073 $old_bordermrk = $this->bordermrk[$this->page];
18074 $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
18075 if ($this->bordermrk[$this->page] > $old_bordermrk) {
18076 $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk);
18077 }
18078 if ($prev_numpages > $this->numpages) {
18079 $startlinepage = $this->page;
18080 }
18081 }
18082 } elseif (strlen($dom[$key]['value']) > 0) {
18083 // print list-item
18084 if (!TCPDF_STATIC::empty_string($this->lispacer) AND ($this->lispacer != '^')) {
18085 $this->SetFont($pfontname, $pfontstyle, $pfontsize);
18086 $this->resetLastH();
18087 $minstartliney = $this->y;
18088 $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio));
18089 $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
18090 $this->SetFont($curfontname, $curfontstyle, $curfontsize);
18091 $this->resetLastH();
18092 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
18093 $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
18094 $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
18095 $this->y += ((($pfontsize - $curfontsize) * $this->cell_height_ratio / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
18096 $minstartliney = min($this->y, $minstartliney);
18097 $maxbottomliney = max(($this->y + (($pfontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);
18098 }
18099 }
18100 // text
18101 $this->htmlvspace = 0;
18102 if ((!$this->premode) AND $this->isRTLTextDir()) {
18103 // reverse spaces order
18104 $lsp = ''; // left spaces
18105 $rsp = ''; // right spaces
18106 if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18107 $lsp = $matches[1];
18108 }
18109 if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18110 $rsp = $matches[1];
18111 }
18112 $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
18113 }
18114 if ($newline) {
18115 if (!$this->premode) {
18116 $prelen = strlen($dom[$key]['value']);
18117 if ($this->isRTLTextDir()) {
18118 // right trim except non-breaking space
18119 $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
18120 } else {
18121 // left trim except non-breaking space
18122 $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
18123 }
18124 $postlen = strlen($dom[$key]['value']);
18125 if (($postlen == 0) AND ($prelen > 0)) {
18126 $dom[$key]['trimmed_space'] = true;
18127 }
18128 }
18129 $newline = false;
18130 $firstblock = true;
18131 } else {
18132 $firstblock = false;
18133 // replace empty multiple spaces string with a single space
18134 $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']);
18135 }
18136 $strrest = '';
18137 if ($this->rtl) {
18138 $this->x -= $this->textindent;
18139 } else {
18140 $this->x += $this->textindent;
18141 }
18142 if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
18143 $strlinelen = $this->GetStringWidth($dom[$key]['value']);
18144 if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
18145 // HTML <a> Link
18146 $hrefcolor = '';
18147 if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
18148 $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
18149 }
18150 $hrefstyle = -1;
18151 if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
18152 $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
18153 }
18154 $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
18155 } else {
18156 $wadj = 0; // space to leave for block continuity
18157 if ($this->rtl) {
18158 $cwa = ($this->x - $this->lMargin);
18159 } else {
18160 $cwa = ($this->w - $this->rMargin - $this->x);
18161 }
18162 if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) {
18163 // check the next text blocks for continuity
18164 $nkey = ($key + 1);
18165 $write_block = true;
18166 $same_textdir = true;
18167 $tmp_fontname = $this->FontFamily;
18168 $tmp_fontstyle = $this->FontStyle;
18169 $tmp_fontsize = $this->FontSizePt;
18170 while ($write_block AND isset($dom[$nkey])) {
18171 if ($dom[$nkey]['tag']) {
18172 if ($dom[$nkey]['block']) {
18173 // end of block
18174 $write_block = false;
18175 }
18176 $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
18177 $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
18178 $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
18179 $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
18180 } else {
18181 $nextstr = preg_split('/'.$this->re_space['p'].'+/'.$this->re_space['m'], $dom[$nkey]['value']);
18182 if (isset($nextstr[0]) AND $same_textdir) {
18183 $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
18184 if (isset($nextstr[1])) {
18185 $write_block = false;
18186 }
18187 }
18188 }
18189 ++$nkey;
18190 }
18191 }
18192 if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) {
18193 $wadj = 0;
18194 $nextstr = preg_split('/'.$this->re_space['p'].'/'.$this->re_space['m'], $dom[$key]['value']);
18195 $numblks = count($nextstr);
18196 if ($numblks > 1) {
18197 // try to split on blank spaces
18198 $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)]));
18199 } else {
18200 // set the entire block on new line
18201 $wadj = $this->GetStringWidth($nextstr[0]);
18202 }
18203 }
18204 // check for reversed text direction
18205 if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) {
18206 // LTR text on RTL direction or RTL text on LTR direction
18207 $reverse_dir = true;
18208 $this->rtl = !$this->rtl;
18209 $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems
18210 if ($this->rtl) {
18211 $this->x += $revshift;
18212 } else {
18213 $this->x -= $revshift;
18214 }
18215 $xws = $this->x;
18216 }
18217 // ****** write only until the end of the line and get the rest ******
18218 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
18219 // restore default direction
18220 if ($reverse_dir AND ($wadj == 0)) {
18221 $this->x = $xws;
18222 $this->rtl = !$this->rtl;
18223 $reverse_dir = false;
18224 }
18225 }
18226 }
18227 $this->textindent = 0;
18228 if (strlen($strrest) > 0) {
18229 // store the remaining string on the previous $key position
18230 $this->newline = true;
18231 if ($strrest == $dom[$key]['value']) {
18232 // used to avoid infinite loop
18233 ++$loop;
18234 } else {
18235 $loop = 0;
18236 }
18237 $dom[$key]['value'] = $strrest;
18238 if ($cell) {
18239 if ($this->rtl) {
18240 $this->x -= $this->cell_padding['R'];
18241 } else {
18242 $this->x += $this->cell_padding['L'];
18243 }
18244 }
18245 if ($loop < 3) {
18246 --$key;
18247 }
18248 } else {
18249 $loop = 0;
18250 // add the positive font spacing of the last character (if any)
18251 if ($this->font_spacing > 0) {
18252 if ($this->rtl) {
18253 $this->x -= $this->font_spacing;
18254 } else {
18255 $this->x += $this->font_spacing;
18256 }
18257 }
18258 }
18259 }
18260 ++$key;
18261 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')) {
18262 // check if we are on a new page or on a new column
18263 if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) {
18264 // we are on a new page or on a new column and the total object height is less than the available vertical space.
18265 // restore previous object
18266 $this->rollbackTransaction(true);
18267 // restore previous values
18268 foreach ($this_method_vars as $vkey => $vval) {
18269 $$vkey = $vval;
18270 }
18271 // add a page (or trig AcceptPageBreak() for multicolumn mode)
18272 $pre_y = $this->y;
18273 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
18274 $startliney = $this->y;
18275 }
18276 $undo = true; // avoid infinite loop
18277 } else {
18278 $undo = false;
18279 }
18280 }
18281 } // end for each $key
18282 // align the last line
18283 if (isset($startlinex)) {
18284 $yshift = ($minstartliney - $startliney);
18285 if (($yshift > 0) OR ($this->page > $startlinepage)) {
18286 $yshift = 0;
18287 }
18288 $t_x = 0;
18289 // the last line must be shifted to be aligned as requested
18290 $linew = abs($this->endlinex - $startlinex);
18291 if ($this->inxobj) {
18292 // we are inside an XObject template
18293 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
18294 if (isset($opentagpos)) {
18295 $midpos = $opentagpos;
18296 } else {
18297 $midpos = 0;
18298 }
18299 if ($midpos > 0) {
18300 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
18301 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
18302 } else {
18303 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
18304 $pend = '';
18305 }
18306 } else {
18307 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
18308 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18309 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18310 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
18311 } elseif (isset($opentagpos)) {
18312 $midpos = $opentagpos;
18313 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18314 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18315 $midpos = $this->footerpos[$startlinepage];
18316 } else {
18317 $midpos = 0;
18318 }
18319 if ($midpos > 0) {
18320 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
18321 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
18322 } else {
18323 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
18324 $pend = '';
18325 }
18326 }
18327 if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
18328 // calculate shifting amount
18329 $tw = $w;
18330 if ($this->lMargin != $prevlMargin) {
18331 $tw += ($prevlMargin - $this->lMargin);
18332 }
18333 if ($this->rMargin != $prevrMargin) {
18334 $tw += ($prevrMargin - $this->rMargin);
18335 }
18336 $one_space_width = $this->GetStringWidth(chr(32));
18337 $no = 0; // number of spaces on a line contained on a single block
18338 if ($this->isRTLTextDir()) { // RTL
18339 // remove left space if exist
18340 $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
18341 if ($pos1 > 0) {
18342 $pos1 = intval($pos1);
18343 if ($this->isUnicodeFont()) {
18344 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
18345 $spacelen = 2;
18346 } else {
18347 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
18348 $spacelen = 1;
18349 }
18350 if ($pos1 == $pos2) {
18351 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
18352 if (substr($pmid, $pos1, 4) == '[()]') {
18353 $linew -= $one_space_width;
18354 } elseif ($pos1 == strpos($pmid, '[(')) {
18355 $no = 1;
18356 }
18357 }
18358 }
18359 } else { // LTR
18360 // remove right space if exist
18361 $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
18362 if ($pos1 > 0) {
18363 $pos1 = intval($pos1);
18364 if ($this->isUnicodeFont()) {
18365 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
18366 $spacelen = 2;
18367 } else {
18368 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
18369 $spacelen = 1;
18370 }
18371 if ($pos1 == $pos2) {
18372 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
18373 $linew -= $one_space_width;
18374 }
18375 }
18376 }
18377 $mdiff = ($tw - $linew);
18378 if ($plalign == 'C') {
18379 if ($this->rtl) {
18380 $t_x = -($mdiff / 2);
18381 } else {
18382 $t_x = ($mdiff / 2);
18383 }
18384 } elseif ($plalign == 'R') {
18385 // right alignment on LTR document
18386 $t_x = $mdiff;
18387 } elseif ($plalign == 'L') {
18388 // left alignment on RTL document
18389 $t_x = -$mdiff;
18390 }
18391 } // end if startlinex
18392 if (($t_x != 0) OR ($yshift < 0)) {
18393 // shift the line
18394 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
18395 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18396 $endlinepos = strlen($pstart);
18397 if ($this->inxobj) {
18398 // we are inside an XObject template
18399 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
18400 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
18401 if ($pak >= $pask) {
18402 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
18403 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
18404 }
18405 }
18406 } else {
18407 $this->setPageBuffer($startlinepage, $pstart.$pend);
18408 // shift the annotations and links
18409 if (isset($this->PageAnnots[$this->page])) {
18410 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
18411 if ($pak >= $pask) {
18412 $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
18413 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
18414 }
18415 }
18416 }
18417 }
18418 $this->y -= $yshift;
18419 $yshift = 0;
18420 }
18421 }
18422 // restore previous values
18423 $this->setGraphicVars($gvars);
18424 if ($this->num_columns > 1) {
18425 $this->selectColumn();
18426 } elseif ($this->page > $prevPage) {
18427 $this->lMargin = $this->pagedim[$this->page]['olm'];
18428 $this->rMargin = $this->pagedim[$this->page]['orm'];
18429 }
18430 // restore previous list state
18431 $this->cell_height_ratio = $prev_cell_height_ratio;
18432 $this->listnum = $prev_listnum;
18433 $this->listordered = $prev_listordered;
18434 $this->listcount = $prev_listcount;
18435 $this->lispacer = $prev_lispacer;
18436 if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
18437 $this->Ln($this->lasth);
18438 if ($this->y < $maxbottomliney) {
18439 $this->y = $maxbottomliney;
18440 }
18441 }
18442 unset($dom);
18443 }
18444
18453 protected function openHTMLTagHandler($dom, $key, $cell) {
18454 $tag = $dom[$key];
18455 $parent = $dom[($dom[$key]['parent'])];
18456 $firsttag = ($key == 1);
18457 // check for text direction attribute
18458 if (isset($tag['dir'])) {
18459 $this->setTempRTL($tag['dir']);
18460 } else {
18461 $this->tmprtl = false;
18462 }
18463 if ($tag['block']) {
18464 $hbz = 0; // distance from y to line bottom
18465 $hb = 0; // vertical space between block tags
18466 // calculate vertical space for block tags
18467 if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
18468 $cur_h = $this->tagvspaces[$tag['value']][0]['h'];
18469 } elseif (isset($tag['fontsize'])) {
18470 $cur_h = ($tag['fontsize'] / $this->k) * $this->cell_height_ratio;
18471 } else {
18472 $cur_h = $this->FontSize * $this->cell_height_ratio;
18473 }
18474 if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
18475 $n = $this->tagvspaces[$tag['value']][0]['n'];
18476 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18477 $n = 0.6;
18478 } else {
18479 $n = 1;
18480 }
18481 if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br')))) {
18482 $hb = 0;
18483 } else {
18484 $hb = ($n * $cur_h);
18485 }
18486 if (($this->htmlvspace <= 0) AND ($n > 0)) {
18487 if (isset($parent['fontsize'])) {
18488 $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
18489 } else {
18490 $hbz = $this->FontSize * $this->cell_height_ratio;
18491 }
18492 }
18493 if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) {
18494 // fix vertical space after table
18495 $hbz = 0;
18496 }
18497 }
18498 // Opening tag
18499 switch($tag['value']) {
18500 case 'table': {
18501 $cp = 0;
18502 $cs = 0;
18503 $dom[$key]['rowspans'] = array();
18504 if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
18505 $this->htmlvspace = 0;
18506 // set table header
18507 if (!TCPDF_STATIC::empty_string($dom[$key]['thead'])) {
18508 // set table header
18509 $this->thead = $dom[$key]['thead'];
18510 if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
18511 $this->theadMargins = array();
18512 $this->theadMargins['cell_padding'] = $this->cell_padding;
18513 $this->theadMargins['lmargin'] = $this->lMargin;
18514 $this->theadMargins['rmargin'] = $this->rMargin;
18515 $this->theadMargins['page'] = $this->page;
18516 $this->theadMargins['cell'] = $cell;
18517 }
18518 }
18519 }
18520 // store current margins and page
18521 $dom[$key]['old_cell_padding'] = $this->cell_padding;
18522 if (isset($tag['attribute']['cellpadding'])) {
18523 $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
18524 $this->SetCellPadding($pad);
18525 } elseif (isset($tag['padding'])) {
18526 $this->cell_padding = $tag['padding'];
18527 }
18528 if (isset($tag['attribute']['cellspacing'])) {
18529 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
18530 } elseif (isset($tag['border-spacing'])) {
18531 $cs = $tag['border-spacing']['V'];
18532 }
18533 $prev_y = $this->y;
18534 if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) {
18535 $this->inthead = true;
18536 // add a page (or trig AcceptPageBreak() for multicolumn mode)
18537 $this->checkPageBreak($this->PageBreakTrigger + 1);
18538 }
18539 break;
18540 }
18541 case 'tr': {
18542 // array of columns positions
18543 $dom[$key]['cellpos'] = array();
18544 break;
18545 }
18546 case 'hr': {
18547 if ((isset($tag['height'])) AND ($tag['height'] != '')) {
18548 $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
18549 } else {
18550 $hrHeight = $this->GetLineWidth();
18551 }
18552 $this->addHTMLVertSpace($hbz, ($hrHeight / 2), $cell, $firsttag);
18553 $x = $this->GetX();
18554 $y = $this->GetY();
18555 $wtmp = $this->w - $this->lMargin - $this->rMargin;
18556 if ($cell) {
18557 $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']);
18558 }
18559 if ((isset($tag['width'])) AND ($tag['width'] != '')) {
18560 $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
18561 } else {
18562 $hrWidth = $wtmp;
18563 }
18564 $prevlinewidth = $this->GetLineWidth();
18565 $this->SetLineWidth($hrHeight);
18566 $this->Line($x, $y, $x + $hrWidth, $y);
18567 $this->SetLineWidth($prevlinewidth);
18568 $this->addHTMLVertSpace(($hrHeight / 2), 0, $cell, !isset($dom[($key + 1)]));
18569 break;
18570 }
18571 case 'a': {
18572 if (array_key_exists('href', $tag['attribute'])) {
18573 $this->HREF['url'] = $tag['attribute']['href'];
18574 }
18575 break;
18576 }
18577 case 'img': {
18578 if (isset($tag['attribute']['src'])) {
18579 if ($tag['attribute']['src']{0} === '@') {
18580 // data stream
18581 $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1));
18582 $type = '';
18583 } else {
18584 // check for images without protocol
18585 if (preg_match('%^/{2}%', $tag['attribute']['src'])) {
18586 $tag['attribute']['src'] = 'http:'.$tag['attribute']['src'];
18587 }
18588 // replace relative path with real server path
18589 if (($tag['attribute']['src'][0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
18590 $findroot = strpos($tag['attribute']['src'], $_SERVER['DOCUMENT_ROOT']);
18591 if (($findroot === false) OR ($findroot > 1)) {
18592 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
18593 $tag['attribute']['src'] = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$tag['attribute']['src'];
18594 } else {
18595 $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
18596 }
18597 }
18598 }
18599 $tag['attribute']['src'] = htmlspecialchars_decode(urldecode($tag['attribute']['src']));
18600 $type = TCPDF_IMAGES::getImageFileType($tag['attribute']['src']);
18601 $testscrtype = @parse_url($tag['attribute']['src']);
18602 if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
18603 // convert URL to server path
18604 $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
18605 }
18606 }
18607 if (!isset($tag['width'])) {
18608 $tag['width'] = 0;
18609 }
18610 if (!isset($tag['height'])) {
18611 $tag['height'] = 0;
18612 }
18613 //if (!isset($tag['attribute']['align'])) {
18614 // the only alignment supported is "bottom"
18615 // further development is required for other modes.
18616 $tag['attribute']['align'] = 'bottom';
18617 //}
18618 switch($tag['attribute']['align']) {
18619 case 'top': {
18620 $align = 'T';
18621 break;
18622 }
18623 case 'middle': {
18624 $align = 'M';
18625 break;
18626 }
18627 case 'bottom': {
18628 $align = 'B';
18629 break;
18630 }
18631 default: {
18632 $align = 'B';
18633 break;
18634 }
18635 }
18636 $prevy = $this->y;
18637 $xpos = $this->x;
18638 $imglink = '';
18639 if (isset($this->HREF['url']) AND !TCPDF_STATIC::empty_string($this->HREF['url'])) {
18640 $imglink = $this->HREF['url'];
18641 if ($imglink{0} == '#') {
18642 // convert url to internal link
18643 $lnkdata = explode(',', $imglink);
18644 if (isset($lnkdata[0])) {
18645 $page = intval(substr($lnkdata[0], 1));
18646 if (empty($page) OR ($page <= 0)) {
18648 }
18649 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
18650 $lnky = floatval($lnkdata[1]);
18651 } else {
18652 $lnky = 0;
18653 }
18654 $imglink = $this->AddLink();
18655 $this->SetLink($imglink, $lnky, $page);
18656 }
18657 }
18658 }
18659 $border = 0;
18660 if (isset($tag['border']) AND !empty($tag['border'])) {
18661 // currently only support 1 (frame) or a combination of 'LTRB'
18662 $border = $tag['border'];
18663 }
18664 $iw = '';
18665 if (isset($tag['width'])) {
18666 $iw = $this->getHTMLUnitToUnits($tag['width'], 1, 'px', false);
18667 }
18668 $ih = '';
18669 if (isset($tag['height'])) {
18670 $ih = $this->getHTMLUnitToUnits($tag['height'], 1, 'px', false);
18671 }
18672 if (($type == 'eps') OR ($type == 'ai')) {
18673 $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
18674 } elseif ($type == 'svg') {
18675 $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
18676 } else {
18677 $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
18678 }
18679 switch($align) {
18680 case 'T': {
18681 $this->y = $prevy;
18682 break;
18683 }
18684 case 'M': {
18685 $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
18686 break;
18687 }
18688 case 'B': {
18689 $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
18690 break;
18691 }
18692 }
18693 }
18694 break;
18695 }
18696 case 'dl': {
18698 if ($this->listnum == 1) {
18699 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18700 } else {
18701 $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18702 }
18703 break;
18704 }
18705 case 'dt': {
18706 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18707 break;
18708 }
18709 case 'dd': {
18710 if ($this->rtl) {
18711 $this->rMargin += $this->listindent;
18712 } else {
18713 $this->lMargin += $this->listindent;
18714 }
18716 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18717 break;
18718 }
18719 case 'ul':
18720 case 'ol': {
18722 if ($tag['value'] == 'ol') {
18723 $this->listordered[$this->listnum] = true;
18724 } else {
18725 $this->listordered[$this->listnum] = false;
18726 }
18727 if (isset($tag['attribute']['start'])) {
18728 $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
18729 } else {
18730 $this->listcount[$this->listnum] = 0;
18731 }
18732 if ($this->rtl) {
18733 $this->rMargin += $this->listindent;
18734 $this->x -= $this->listindent;
18735 } else {
18736 $this->lMargin += $this->listindent;
18737 $this->x += $this->listindent;
18738 }
18740 if ($this->listnum == 1) {
18741 if ($key > 1) {
18742 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18743 }
18744 } else {
18745 $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18746 }
18747 break;
18748 }
18749 case 'li': {
18750 if ($key > 2) {
18751 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18752 }
18753 if ($this->listordered[$this->listnum]) {
18754 // ordered item
18755 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
18756 $this->lispacer = $parent['attribute']['type'];
18757 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
18758 $this->lispacer = $parent['listtype'];
18759 } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
18760 $this->lispacer = $this->lisymbol;
18761 } else {
18762 $this->lispacer = '#';
18763 }
18764 ++$this->listcount[$this->listnum];
18765 if (isset($tag['attribute']['value'])) {
18766 $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
18767 }
18768 } else {
18769 // unordered item
18770 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
18771 $this->lispacer = $parent['attribute']['type'];
18772 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
18773 $this->lispacer = $parent['listtype'];
18774 } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
18775 $this->lispacer = $this->lisymbol;
18776 } else {
18777 $this->lispacer = '!';
18778 }
18779 }
18780 break;
18781 }
18782 case 'blockquote': {
18783 if ($this->rtl) {
18784 $this->rMargin += $this->listindent;
18785 } else {
18786 $this->lMargin += $this->listindent;
18787 }
18789 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18790 break;
18791 }
18792 case 'br': {
18793 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18794 break;
18795 }
18796 case 'div': {
18797 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18798 break;
18799 }
18800 case 'p': {
18801 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18802 break;
18803 }
18804 case 'pre': {
18805 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18806 $this->premode = true;
18807 break;
18808 }
18809 case 'sup': {
18810 $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
18811 break;
18812 }
18813 case 'sub': {
18814 $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
18815 break;
18816 }
18817 case 'h1':
18818 case 'h2':
18819 case 'h3':
18820 case 'h4':
18821 case 'h5':
18822 case 'h6': {
18823 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18824 break;
18825 }
18826 // Form fields (since 4.8.000 - 2009-09-07)
18827 case 'form': {
18828 if (isset($tag['attribute']['action'])) {
18829 $this->form_action = $tag['attribute']['action'];
18830 } else {
18831 $this->Error('Please explicitly set action attribute path!');
18832 }
18833 if (isset($tag['attribute']['enctype'])) {
18834 $this->form_enctype = $tag['attribute']['enctype'];
18835 } else {
18836 $this->form_enctype = 'application/x-www-form-urlencoded';
18837 }
18838 if (isset($tag['attribute']['method'])) {
18839 $this->form_mode = $tag['attribute']['method'];
18840 } else {
18841 $this->form_mode = 'post';
18842 }
18843 break;
18844 }
18845 case 'input': {
18846 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
18847 $name = $tag['attribute']['name'];
18848 } else {
18849 break;
18850 }
18851 $prop = array();
18852 $opt = array();
18853 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
18854 $prop['readonly'] = true;
18855 }
18856 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
18857 $value = $tag['attribute']['value'];
18858 }
18859 if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC::empty_string($tag['attribute']['maxlength'])) {
18860 $opt['maxlen'] = intval($tag['attribute']['maxlength']);
18861 }
18862 $h = $this->FontSize * $this->cell_height_ratio;
18863 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
18864 $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
18865 } else {
18866 $w = $h;
18867 }
18868 if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
18869 $checked = true;
18870 } else {
18871 $checked = false;
18872 }
18873 if (isset($tag['align'])) {
18874 switch ($tag['align']) {
18875 case 'C': {
18876 $opt['q'] = 1;
18877 break;
18878 }
18879 case 'R': {
18880 $opt['q'] = 2;
18881 break;
18882 }
18883 case 'L':
18884 default: {
18885 break;
18886 }
18887 }
18888 }
18889 switch ($tag['attribute']['type']) {
18890 case 'text': {
18891 if (isset($value)) {
18892 $opt['v'] = $value;
18893 }
18894 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
18895 break;
18896 }
18897 case 'password': {
18898 if (isset($value)) {
18899 $opt['v'] = $value;
18900 }
18901 $prop['password'] = 'true';
18902 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
18903 break;
18904 }
18905 case 'checkbox': {
18906 if (!isset($value)) {
18907 break;
18908 }
18909 $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
18910 break;
18911 }
18912 case 'radio': {
18913 if (!isset($value)) {
18914 break;
18915 }
18916 $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
18917 break;
18918 }
18919 case 'submit': {
18920 if (!isset($value)) {
18921 $value = 'submit';
18922 }
18923 $w = $this->GetStringWidth($value) * 1.5;
18924 $h *= 1.6;
18925 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
18926 $action = array();
18927 $action['S'] = 'SubmitForm';
18928 $action['F'] = $this->form_action;
18929 if ($this->form_enctype != 'FDF') {
18930 $action['Flags'] = array('ExportFormat');
18931 }
18932 if ($this->form_mode == 'get') {
18933 $action['Flags'] = array('GetMethod');
18934 }
18935 $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
18936 break;
18937 }
18938 case 'reset': {
18939 if (!isset($value)) {
18940 $value = 'reset';
18941 }
18942 $w = $this->GetStringWidth($value) * 1.5;
18943 $h *= 1.6;
18944 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
18945 $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
18946 break;
18947 }
18948 case 'file': {
18949 $prop['fileSelect'] = 'true';
18950 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
18951 if (!isset($value)) {
18952 $value = '*';
18953 }
18954 $w = $this->GetStringWidth($value) * 2;
18955 $h *= 1.2;
18956 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
18957 $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
18958 $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
18959 break;
18960 }
18961 case 'hidden': {
18962 if (isset($value)) {
18963 $opt['v'] = $value;
18964 }
18965 $opt['f'] = array('invisible', 'hidden');
18966 $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
18967 break;
18968 }
18969 case 'image': {
18970 // THIS TYPE MUST BE FIXED
18971 if (isset($tag['attribute']['src']) AND !TCPDF_STATIC::empty_string($tag['attribute']['src'])) {
18972 $img = $tag['attribute']['src'];
18973 } else {
18974 break;
18975 }
18976 $value = 'img';
18977 //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
18978 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
18979 $jsaction = $tag['attribute']['onclick'];
18980 } else {
18981 $jsaction = '';
18982 }
18983 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
18984 break;
18985 }
18986 case 'button': {
18987 if (!isset($value)) {
18988 $value = ' ';
18989 }
18990 $w = $this->GetStringWidth($value) * 1.5;
18991 $h *= 1.6;
18992 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
18993 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
18994 $jsaction = $tag['attribute']['onclick'];
18995 } else {
18996 $jsaction = '';
18997 }
18998 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
18999 break;
19000 }
19001 }
19002 break;
19003 }
19004 case 'textarea': {
19005 $prop = array();
19006 $opt = array();
19007 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19008 $prop['readonly'] = true;
19009 }
19010 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19011 $name = $tag['attribute']['name'];
19012 } else {
19013 break;
19014 }
19015 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19016 $opt['v'] = $tag['attribute']['value'];
19017 }
19018 if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC::empty_string($tag['attribute']['cols'])) {
19019 $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
19020 } else {
19021 $w = 40;
19022 }
19023 if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC::empty_string($tag['attribute']['rows'])) {
19024 $h = intval($tag['attribute']['rows']) * $this->FontSize * $this->cell_height_ratio;
19025 } else {
19026 $h = 10;
19027 }
19028 $prop['multiline'] = 'true';
19029 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19030 break;
19031 }
19032 case 'select': {
19033 $h = $this->FontSize * $this->cell_height_ratio;
19034 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19035 $h *= ($tag['attribute']['size'] + 1);
19036 }
19037 $prop = array();
19038 $opt = array();
19039 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19040 $name = $tag['attribute']['name'];
19041 } else {
19042 break;
19043 }
19044 $w = 0;
19045 if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC::empty_string($tag['attribute']['opt'])) {
19046 $options = explode('#!NwL!#', $tag['attribute']['opt']);
19047 $values = array();
19048 foreach ($options as $val) {
19049 if (strpos($val, '#!TaB!#') !== false) {
19050 $opts = explode('#!TaB!#', $val);
19051 $values[] = $opts;
19052 $w = max($w, $this->GetStringWidth($opts[1]));
19053 } else {
19054 $values[] = $val;
19055 $w = max($w, $this->GetStringWidth($val));
19056 }
19057 }
19058 } else {
19059 break;
19060 }
19061 $w *= 2;
19062 if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
19063 $prop['multipleSelection'] = 'true';
19064 $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19065 } else {
19066 $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19067 }
19068 break;
19069 }
19070 case 'tcpdf': {
19071 if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
19072 // Special tag used to call TCPDF methods
19073 if (isset($tag['attribute']['method'])) {
19074 $tcpdf_method = $tag['attribute']['method'];
19075 if (method_exists($this, $tcpdf_method)) {
19076 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
19077 $params = unserialize(urldecode($tag['attribute']['params']));
19078 call_user_func_array(array($this, $tcpdf_method), $params);
19079 } else {
19080 $this->$tcpdf_method();
19081 }
19082 $this->newline = true;
19083 }
19084 }
19085 }
19086 break;
19087 }
19088 default: {
19089 break;
19090 }
19091 }
19092 // define tags that support borders and background colors
19093 $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
19094 if (in_array($tag['value'], $bordertags)) {
19095 // set border
19096 $dom[$key]['borderposition'] = $this->getBorderStartPosition();
19097 }
19098 if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
19099 $pba = $dom[$key]['attribute']['pagebreakafter'];
19100 // check for pagebreak
19101 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19102 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19103 $this->checkPageBreak($this->PageBreakTrigger + 1);
19104 }
19105 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19106 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19107 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19108 $this->checkPageBreak($this->PageBreakTrigger + 1);
19109 }
19110 }
19111 return $dom;
19112 }
19113
19123 protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
19124 $tag = $dom[$key];
19125 $parent = $dom[($dom[$key]['parent'])];
19126 $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
19127 $in_table_head = false;
19128 // maximum x position (used to draw borders)
19129 if ($this->rtl) {
19130 $xmax = $this->w;
19131 } else {
19132 $xmax = 0;
19133 }
19134 if ($tag['block']) {
19135 $hbz = 0; // distance from y to line bottom
19136 $hb = 0; // vertical space between block tags
19137 // calculate vertical space for block tags
19138 if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
19139 $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
19140 } elseif (isset($parent['fontsize'])) {
19141 $pre_h = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
19142 } else {
19143 $pre_h = $this->FontSize * $this->cell_height_ratio;
19144 }
19145 if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
19146 $n = $this->tagvspaces[$tag['value']][1]['n'];
19147 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
19148 $n = 0.6;
19149 } else {
19150 $n = 1;
19151 }
19152 if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) {
19153 $hb = 0;
19154 } else {
19155 $hb = ($n * $pre_h);
19156 }
19157 if ($maxbottomliney > $this->PageBreakTrigger) {
19158 $hbz = ($this->FontSize * $this->cell_height_ratio);
19159 } elseif ($this->y < $maxbottomliney) {
19160 $hbz = ($maxbottomliney - $this->y);
19161 }
19162 }
19163 // Closing tag
19164 switch($tag['value']) {
19165 case 'tr': {
19166 $table_el = $dom[($dom[$key]['parent'])]['parent'];
19167 if (!isset($parent['endy'])) {
19168 $dom[($dom[$key]['parent'])]['endy'] = $this->y;
19169 $parent['endy'] = $this->y;
19170 }
19171 if (!isset($parent['endpage'])) {
19172 $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
19173 $parent['endpage'] = $this->page;
19174 }
19175 if (!isset($parent['endcolumn'])) {
19176 $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column;
19177 $parent['endcolumn'] = $this->current_column;
19178 }
19179 // update row-spanned cells
19180 if (isset($dom[$table_el]['rowspans'])) {
19181 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19182 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
19183 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19184 if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
19185 $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
19186 } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
19187 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19188 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19189 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19190 }
19191 }
19192 }
19193 // report new endy and endpage to the rowspanned cells
19194 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19195 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19196 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
19197 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19198 $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
19199 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19200 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
19201 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19202 }
19203 }
19204 // update remaining rowspanned cells
19205 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19206 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19207 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
19208 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
19209 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
19210 }
19211 }
19212 }
19213 $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
19214 if ($this->num_columns > 1) {
19215 $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
19216 }
19217 $this->y = $dom[($dom[$key]['parent'])]['endy'];
19218 if (isset($dom[$table_el]['attribute']['cellspacing'])) {
19219 $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
19220 } elseif (isset($dom[$table_el]['border-spacing'])) {
19221 $this->y += $dom[$table_el]['border-spacing']['V'];
19222 }
19223 $this->Ln(0, $cell);
19224 if ($this->current_column == $parent['startcolumn']) {
19225 $this->x = $parent['startx'];
19226 }
19227 // account for booklet mode
19228 if ($this->page > $parent['startpage']) {
19229 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
19230 $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
19231 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
19232 $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
19233 }
19234 }
19235 break;
19236 }
19237 case 'tablehead':
19238 // closing tag used for the thead part
19239 $in_table_head = true;
19240 $this->inthead = false;
19241 case 'table': {
19242 $table_el = $parent;
19243 // set default border
19244 if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
19245 // set default border
19246 $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
19247 } else {
19248 $border = 0;
19249 }
19250 $default_border = $border;
19251 // fix bottom line alignment of last line before page break
19252 foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
19253 // update row-spanned cells
19254 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19255 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19256 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) {
19257 $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
19258 }
19259 if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) {
19260 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
19261 }
19262 }
19263 }
19264 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
19265 $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
19266 $dom[$prevtrkey]['endy'] = $pgendy;
19267 // update row-spanned cells
19268 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19269 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19270 if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] > 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
19271 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
19272 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
19273 }
19274 }
19275 }
19276 }
19277 $prevtrkey = $trkey;
19278 $table_el = $dom[($dom[$key]['parent'])];
19279 }
19280 // for each row
19281 if (count($table_el['trids']) > 0) {
19282 unset($xmax);
19283 }
19284 foreach ($table_el['trids'] as $j => $trkey) {
19285 $parent = $dom[$trkey];
19286 if (!isset($xmax)) {
19287 $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
19288 }
19289 // for each cell on the row
19290 foreach ($parent['cellpos'] as $k => $cellpos) {
19291 if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
19292 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
19293 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
19294 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
19295 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
19296 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
19297 $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
19298 $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
19299 } else {
19300 $endy = $parent['endy'];
19301 $startpage = $parent['startpage'];
19302 $endpage = $parent['endpage'];
19303 $startcolumn = $parent['startcolumn'];
19304 $endcolumn = $parent['endcolumn'];
19305 }
19306 if ($this->num_columns == 0) {
19307 $this->num_columns = 1;
19308 }
19309 if (isset($cellpos['border'])) {
19310 $border = $cellpos['border'];
19311 }
19312 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
19313 $this->SetFillColorArray($cellpos['bgcolor']);
19314 $fill = true;
19315 } else {
19316 $fill = false;
19317 }
19318 $x = $cellpos['startx'];
19319 $y = $parent['starty'];
19320 $starty = $y;
19321 $w = abs($cellpos['endx'] - $cellpos['startx']);
19322 // get border modes
19323 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
19324 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
19325 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19326 // design borders around HTML cells.
19327 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
19328 $ccode = '';
19329 $this->setPage($page);
19330 if ($this->num_columns < 2) {
19331 // single-column mode
19332 $this->x = $x;
19333 $this->y = $this->tMargin;
19334 }
19335 // account for margin changes
19336 if ($page > $startpage) {
19337 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
19338 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
19339 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
19340 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
19341 }
19342 }
19343 if ($startpage == $endpage) { // single page
19344 $deltacol = 0;
19345 $deltath = 0;
19346 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
19347 $this->selectColumn($column);
19348 if ($startcolumn == $endcolumn) { // single column
19349 $cborder = $border;
19350 $h = $endy - $parent['starty'];
19351 $this->y = $y;
19352 $this->x = $x;
19353 } elseif ($column == $startcolumn) { // first column
19354 $cborder = $border_start;
19355 $this->y = $starty;
19356 $this->x = $x;
19357 $h = $this->h - $this->y - $this->bMargin;
19358 if ($this->rtl) {
19359 $deltacol = $this->x + $this->rMargin - $this->w;
19360 } else {
19361 $deltacol = $this->x - $this->lMargin;
19362 }
19363 } elseif ($column == $endcolumn) { // end column
19364 $cborder = $border_end;
19365 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19366 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19367 }
19368 $this->x += $deltacol;
19369 $h = $endy - $this->y;
19370 } else { // middle column
19371 $cborder = $border_middle;
19372 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19373 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19374 }
19375 $this->x += $deltacol;
19376 $h = $this->h - $this->y - $this->bMargin;
19377 }
19378 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19379 } // end for each column
19380 } elseif ($page == $startpage) { // first page
19381 $deltacol = 0;
19382 $deltath = 0;
19383 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
19384 $this->selectColumn($column);
19385 if ($column == $startcolumn) { // first column
19386 $cborder = $border_start;
19387 $this->y = $starty;
19388 $this->x = $x;
19389 $h = $this->h - $this->y - $this->bMargin;
19390 if ($this->rtl) {
19391 $deltacol = $this->x + $this->rMargin - $this->w;
19392 } else {
19393 $deltacol = $this->x - $this->lMargin;
19394 }
19395 } else { // middle column
19396 $cborder = $border_middle;
19397 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19398 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19399 }
19400 $this->x += $deltacol;
19401 $h = $this->h - $this->y - $this->bMargin;
19402 }
19403 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19404 } // end for each column
19405 } elseif ($page == $endpage) { // last page
19406 $deltacol = 0;
19407 $deltath = 0;
19408 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
19409 $this->selectColumn($column);
19410 if ($column == $endcolumn) { // end column
19411 $cborder = $border_end;
19412 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19413 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19414 }
19415 $this->x += $deltacol;
19416 $h = $endy - $this->y;
19417 } else { // middle column
19418 $cborder = $border_middle;
19419 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19420 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19421 }
19422 $this->x += $deltacol;
19423 $h = $this->h - $this->y - $this->bMargin;
19424 }
19425 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19426 } // end for each column
19427 } else { // middle page
19428 $deltacol = 0;
19429 $deltath = 0;
19430 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
19431 $this->selectColumn($column);
19432 $cborder = $border_middle;
19433 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19434 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19435 }
19436 $this->x += $deltacol;
19437 $h = $this->h - $this->y - $this->bMargin;
19438 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19439 } // end for each column
19440 }
19441 if ($cborder OR $fill) {
19442 $offsetlen = strlen($ccode);
19443 // draw border and fill
19444 if ($this->inxobj) {
19445 // we are inside an XObject template
19446 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
19447 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
19448 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
19449 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
19450 } else {
19451 $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
19452 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
19453 }
19454 $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
19455 $pstart = substr($pagebuff, 0, $pagemark);
19456 $pend = substr($pagebuff, $pagemark);
19457 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
19458 } else {
19459 // draw border and fill
19460 if (end($this->transfmrk[$this->page]) !== false) {
19461 $pagemarkkey = key($this->transfmrk[$this->page]);
19462 $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
19463 } elseif ($this->InFooter) {
19464 $pagemark = $this->footerpos[$this->page];
19465 } else {
19466 $pagemark = $this->intmrk[$this->page];
19467 }
19468 $pagebuff = $this->getPageBuffer($this->page);
19469 $pstart = substr($pagebuff, 0, $pagemark);
19470 $pend = substr($pagebuff, $pagemark);
19471 $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
19472 }
19473 }
19474 } // end for each page
19475 // restore default border
19476 $border = $default_border;
19477 } // end for each cell on the row
19478 if (isset($table_el['attribute']['cellspacing'])) {
19479 $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
19480 } elseif (isset($table_el['border-spacing'])) {
19481 $this->y += $table_el['border-spacing']['V'];
19482 }
19483 $this->Ln(0, $cell);
19484 $this->x = $parent['startx'];
19485 if ($endpage > $startpage) {
19486 if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
19487 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
19488 } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
19489 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
19490 }
19491 }
19492 }
19493 if (!$in_table_head) { // we are not inside a thead section
19494 $this->cell_padding = $table_el['old_cell_padding'];
19495 // reset row height
19496 $this->resetLastH();
19497 if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) {
19498 $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]);
19499 if (($plendiff > 0) AND ($plendiff < 60)) {
19500 $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff);
19501 if (substr($pagediff, 0, 5) == 'BT /F') {
19502 // the difference is only a font setting
19503 $plendiff = 0;
19504 }
19505 }
19506 if ($plendiff == 0) {
19507 // remove last blank page
19508 $this->deletePage($this->numpages);
19509 }
19510 }
19511 if (isset($this->theadMargins['top'])) {
19512 // restore top margin
19513 $this->tMargin = $this->theadMargins['top'];
19514 }
19515 if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
19516 // reset main table header
19517 $this->thead = '';
19518 $this->theadMargins = array();
19519 $this->pagedim[$this->page]['tm'] = $this->tMargin;
19520 }
19521 }
19522 $parent = $table_el;
19523 break;
19524 }
19525 case 'a': {
19526 $this->HREF = '';
19527 break;
19528 }
19529 case 'sup': {
19530 $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
19531 break;
19532 }
19533 case 'sub': {
19534 $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k));
19535 break;
19536 }
19537 case 'div': {
19538 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19539 break;
19540 }
19541 case 'blockquote': {
19542 if ($this->rtl) {
19543 $this->rMargin -= $this->listindent;
19544 } else {
19545 $this->lMargin -= $this->listindent;
19546 }
19548 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19549 break;
19550 }
19551 case 'p': {
19552 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19553 break;
19554 }
19555 case 'pre': {
19556 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19557 $this->premode = false;
19558 break;
19559 }
19560 case 'dl': {
19562 if ($this->listnum <= 0) {
19563 $this->listnum = 0;
19564 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19565 } else {
19566 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19567 }
19568 $this->resetLastH();
19569 break;
19570 }
19571 case 'dt': {
19572 $this->lispacer = '';
19573 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19574 break;
19575 }
19576 case 'dd': {
19577 $this->lispacer = '';
19578 if ($this->rtl) {
19579 $this->rMargin -= $this->listindent;
19580 } else {
19581 $this->lMargin -= $this->listindent;
19582 }
19584 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19585 break;
19586 }
19587 case 'ul':
19588 case 'ol': {
19590 $this->lispacer = '';
19591 if ($this->rtl) {
19592 $this->rMargin -= $this->listindent;
19593 } else {
19594 $this->lMargin -= $this->listindent;
19595 }
19597 if ($this->listnum <= 0) {
19598 $this->listnum = 0;
19599 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19600 } else {
19601 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19602 }
19603 $this->resetLastH();
19604 break;
19605 }
19606 case 'li': {
19607 $this->lispacer = '';
19608 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19609 break;
19610 }
19611 case 'h1':
19612 case 'h2':
19613 case 'h3':
19614 case 'h4':
19615 case 'h5':
19616 case 'h6': {
19617 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19618 break;
19619 }
19620 // Form fields (since 4.8.000 - 2009-09-07)
19621 case 'form': {
19622 $this->form_action = '';
19623 $this->form_enctype = 'application/x-www-form-urlencoded';
19624 break;
19625 }
19626 default : {
19627 break;
19628 }
19629 }
19630 // draw border and background (if any)
19631 $this->drawHTMLTagBorder($parent, $xmax);
19632 if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
19633 $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
19634 // check for pagebreak
19635 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19636 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19637 $this->checkPageBreak($this->PageBreakTrigger + 1);
19638 }
19639 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19640 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19641 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19642 $this->checkPageBreak($this->PageBreakTrigger + 1);
19643 }
19644 }
19645 $this->tmprtl = false;
19646 return $dom;
19647 }
19648
19658 protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
19659 if ($firsttag) {
19660 $this->Ln(0, $cell);
19661 $this->htmlvspace = 0;
19662 return;
19663 }
19664 if ($lasttag) {
19665 $this->Ln($hbz, $cell);
19666 $this->htmlvspace = 0;
19667 return;
19668 }
19669 if ($hb < $this->htmlvspace) {
19670 $hd = 0;
19671 } else {
19672 $hd = $hb - $this->htmlvspace;
19673 $this->htmlvspace = $hb;
19674 }
19675 $this->Ln(($hbz + $hd), $cell);
19676 }
19677
19684 protected function getBorderStartPosition() {
19685 if ($this->rtl) {
19686 $xmax = $this->lMargin;
19687 } else {
19688 $xmax = $this->w - $this->rMargin;
19689 }
19690 return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax);
19691 }
19692
19700 protected function drawHTMLTagBorder($tag, $xmax) {
19701 if (!isset($tag['borderposition'])) {
19702 // nothing to draw
19703 return;
19704 }
19705 $prev_x = $this->x;
19706 $prev_y = $this->y;
19707 $prev_lasth = $this->lasth;
19708 $border = 0;
19709 $fill = false;
19710 $this->lasth = 0;
19711 if (isset($tag['border']) AND !empty($tag['border'])) {
19712 // get border style
19713 $border = $tag['border'];
19714 if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
19715 // border for table header
19716 $border = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19717 }
19718 }
19719 if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
19720 // get background color
19721 $old_bgcolor = $this->bgcolor;
19722 $this->SetFillColorArray($tag['bgcolor']);
19723 $fill = true;
19724 }
19725 if (!$border AND !$fill) {
19726 // nothing to draw
19727 return;
19728 }
19729 if (isset($tag['attribute']['cellspacing'])) {
19730 $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
19731 $cellspacing = array('H' => $clsp, 'V' => $clsp);
19732 } elseif (isset($tag['border-spacing'])) {
19733 $cellspacing = $tag['border-spacing'];
19734 } else {
19735 $cellspacing = array('H' => 0, 'V' => 0);
19736 }
19737 if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
19738 // draw the border externally respect the sqare edge.
19739 $border['mode'] = 'ext';
19740 }
19741 if ($this->rtl) {
19742 if ($xmax >= $tag['borderposition']['x']) {
19743 $xmax = $tag['borderposition']['xmax'];
19744 }
19745 $w = ($tag['borderposition']['x'] - $xmax);
19746 } else {
19747 if ($xmax <= $tag['borderposition']['x']) {
19748 $xmax = $tag['borderposition']['xmax'];
19749 }
19750 $w = ($xmax - $tag['borderposition']['x']);
19751 }
19752 if ($w <= 0) {
19753 return;
19754 }
19755 $w += $cellspacing['H'];
19756 $startpage = $tag['borderposition']['page'];
19757 $startcolumn = $tag['borderposition']['column'];
19758 $x = $tag['borderposition']['x'];
19759 $y = $tag['borderposition']['y'];
19760 $endpage = $this->page;
19761 $starty = $tag['borderposition']['y'] - $cellspacing['V'];
19762 $currentY = $this->y;
19763 $this->x = $x;
19764 // get latest column
19765 $endcolumn = $this->current_column;
19766 if ($this->num_columns == 0) {
19767 $this->num_columns = 1;
19768 }
19769 // get border modes
19770 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
19771 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
19772 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19773 // temporary disable page regions
19774 $temp_page_regions = $this->page_regions;
19775 $this->page_regions = array();
19776 // design borders around HTML cells.
19777 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
19778 $ccode = '';
19779 $this->setPage($page);
19780 if ($this->num_columns < 2) {
19781 // single-column mode
19782 $this->x = $x;
19783 $this->y = $this->tMargin;
19784 }
19785 // account for margin changes
19786 if ($page > $startpage) {
19787 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
19788 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
19789 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
19790 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
19791 }
19792 }
19793 if ($startpage == $endpage) {
19794 // single page
19795 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
19796 $this->selectColumn($column);
19797 if ($startcolumn == $endcolumn) { // single column
19798 $cborder = $border;
19799 $h = ($currentY - $y) + $cellspacing['V'];
19800 $this->y = $starty;
19801 } elseif ($column == $startcolumn) { // first column
19802 $cborder = $border_start;
19803 $this->y = $starty;
19804 $h = $this->h - $this->y - $this->bMargin;
19805 } elseif ($column == $endcolumn) { // end column
19806 $cborder = $border_end;
19807 $h = $currentY - $this->y;
19808 } else { // middle column
19809 $cborder = $border_middle;
19810 $h = $this->h - $this->y - $this->bMargin;
19811 }
19812 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19813 } // end for each column
19814 } elseif ($page == $startpage) { // first page
19815 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
19816 $this->selectColumn($column);
19817 if ($column == $startcolumn) { // first column
19818 $cborder = $border_start;
19819 $this->y = $starty;
19820 $h = $this->h - $this->y - $this->bMargin;
19821 } else { // middle column
19822 $cborder = $border_middle;
19823 $h = $this->h - $this->y - $this->bMargin;
19824 }
19825 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19826 } // end for each column
19827 } elseif ($page == $endpage) { // last page
19828 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
19829 $this->selectColumn($column);
19830 if ($column == $endcolumn) {
19831 // end column
19832 $cborder = $border_end;
19833 $h = $currentY - $this->y;
19834 } else {
19835 // middle column
19836 $cborder = $border_middle;
19837 $h = $this->h - $this->y - $this->bMargin;
19838 }
19839 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19840 } // end for each column
19841 } else { // middle page
19842 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
19843 $this->selectColumn($column);
19844 $cborder = $border_middle;
19845 $h = $this->h - $this->y - $this->bMargin;
19846 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19847 } // end for each column
19848 }
19849 if ($cborder OR $fill) {
19850 $offsetlen = strlen($ccode);
19851 // draw border and fill
19852 if ($this->inxobj) {
19853 // we are inside an XObject template
19854 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
19855 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
19856 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
19857 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
19858 } else {
19859 $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
19860 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
19861 }
19862 $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
19863 $pstart = substr($pagebuff, 0, $pagemark);
19864 $pend = substr($pagebuff, $pagemark);
19865 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
19866 } else {
19867 if (end($this->transfmrk[$this->page]) !== false) {
19868 $pagemarkkey = key($this->transfmrk[$this->page]);
19869 $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
19870 } elseif ($this->InFooter) {
19871 $pagemark = $this->footerpos[$this->page];
19872 } else {
19873 $pagemark = $this->intmrk[$this->page];
19874 }
19875 $pagebuff = $this->getPageBuffer($this->page);
19876 $pstart = substr($pagebuff, 0, $pagemark);
19877 $pend = substr($pagebuff, $pagemark);
19878 $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
19879 $this->bordermrk[$this->page] += $offsetlen;
19880 $this->cntmrk[$this->page] += $offsetlen;
19881 }
19882 }
19883 } // end for each page
19884 // restore page regions
19885 $this->page_regions = $temp_page_regions;
19886 if (isset($old_bgcolor)) {
19887 // restore background color
19888 $this->SetFillColorArray($old_bgcolor);
19889 }
19890 // restore pointer position
19891 $this->x = $prev_x;
19892 $this->y = $prev_y;
19893 $this->lasth = $prev_lasth;
19894 }
19895
19902 public function setLIsymbol($symbol='!') {
19903 // check for custom image symbol
19904 if (substr($symbol, 0, 4) == 'img|') {
19905 $this->lisymbol = $symbol;
19906 return;
19907 }
19908 $symbol = strtolower($symbol);
19909 switch ($symbol) {
19910 case '!' :
19911 case '#' :
19912 case 'disc' :
19913 case 'circle' :
19914 case 'square' :
19915 case '1':
19916 case 'decimal':
19917 case 'decimal-leading-zero':
19918 case 'i':
19919 case 'lower-roman':
19920 case 'I':
19921 case 'upper-roman':
19922 case 'a':
19923 case 'lower-alpha':
19924 case 'lower-latin':
19925 case 'A':
19926 case 'upper-alpha':
19927 case 'upper-latin':
19928 case 'lower-greek': {
19929 $this->lisymbol = $symbol;
19930 break;
19931 }
19932 default : {
19933 $this->lisymbol = '';
19934 }
19935 }
19936 }
19937
19946 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
19947 $this->booklet = $booklet;
19948 if ($inner >= 0) {
19949 $this->lMargin = $inner;
19950 }
19951 if ($outer >= 0) {
19952 $this->rMargin = $outer;
19953 }
19954 }
19955
19962 protected function swapMargins($reverse=true) {
19963 if ($reverse) {
19964 // swap left and right margins
19965 $mtemp = $this->original_lMargin;
19966 $this->original_lMargin = $this->original_rMargin;
19967 $this->original_rMargin = $mtemp;
19968 $deltam = $this->original_lMargin - $this->original_rMargin;
19969 $this->lMargin += $deltam;
19970 $this->rMargin -= $deltam;
19971 }
19972 }
19973
19986 public function setHtmlVSpace($tagvs) {
19987 $this->tagvspaces = $tagvs;
19988 }
19989
19996 public function setListIndentWidth($width) {
19997 return $this->customlistindent = floatval($width);
19998 }
19999
20006 public function setOpenCell($isopen) {
20007 $this->opencell = $isopen;
20008 }
20009
20017 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
20018 $this->htmlLinkColorArray = $color;
20019 $this->htmlLinkFontStyle = $fontstyle;
20020 }
20021
20032 public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
20033 $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
20034 $retval = 0;
20035 $value = 0;
20036 $unit = 'px';
20037 if ($points) {
20038 $k = 1;
20039 } else {
20040 $k = $this->k;
20041 }
20042 if (in_array($defaultunit, $supportedunits)) {
20043 $unit = $defaultunit;
20044 }
20045 if (is_numeric($htmlval)) {
20046 $value = floatval($htmlval);
20047 } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
20048 $value = floatval($mnum[1]);
20049 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
20050 if (in_array($munit[1], $supportedunits)) {
20051 $unit = $munit[1];
20052 }
20053 }
20054 }
20055 switch ($unit) {
20056 // percentage
20057 case '%': {
20058 $retval = (($value * $refsize) / 100);
20059 break;
20060 }
20061 // relative-size
20062 case 'em': {
20063 $retval = ($value * $refsize);
20064 break;
20065 }
20066 // height of lower case 'x' (about half the font-size)
20067 case 'ex': {
20068 $retval = ($value * ($refsize / 2));
20069 break;
20070 }
20071 // absolute-size
20072 case 'in': {
20073 $retval = (($value * $this->dpi) / $k);
20074 break;
20075 }
20076 // centimeters
20077 case 'cm': {
20078 $retval = (($value / 2.54 * $this->dpi) / $k);
20079 break;
20080 }
20081 // millimeters
20082 case 'mm': {
20083 $retval = (($value / 25.4 * $this->dpi) / $k);
20084 break;
20085 }
20086 // one pica is 12 points
20087 case 'pc': {
20088 $retval = (($value * 12) / $k);
20089 break;
20090 }
20091 // points
20092 case 'pt': {
20093 $retval = ($value / $k);
20094 break;
20095 }
20096 // pixels
20097 case 'px': {
20098 $retval = $this->pixelsToUnits($value);
20099 if ($points) {
20100 $retval *= $this->k;
20101 }
20102 break;
20103 }
20104 }
20105 return $retval;
20106 }
20107
20116 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
20117 if ($this->state != 2) {
20118 return;
20119 }
20120 $size /= $this->k;
20121 $fill = '';
20122 $bgcolor = $this->bgcolor;
20123 $color = $this->fgcolor;
20124 $strokecolor = $this->strokecolor;
20125 $width = 0;
20126 $textitem = '';
20127 $tmpx = $this->x;
20128 $lspace = $this->GetStringWidth(' ');
20129 if ($listtype == '^') {
20130 // special symbol used for avoid justification of rect bullet
20131 $this->lispacer = '';
20132 return;
20133 } elseif ($listtype == '!') {
20134 // set default list type for unordered list
20135 $deftypes = array('disc', 'circle', 'square');
20136 $listtype = $deftypes[($listdepth - 1) % 3];
20137 } elseif ($listtype == '#') {
20138 // set default list type for ordered list
20139 $listtype = 'decimal';
20140 } elseif (substr($listtype, 0, 4) == 'img|') {
20141 // custom image type ('img|type|width|height|image.ext')
20142 $img = explode('|', $listtype);
20143 $listtype = 'img';
20144 }
20145 switch ($listtype) {
20146 // unordered types
20147 case 'none': {
20148 break;
20149 }
20150 case 'disc': {
20151 $r = $size / 6;
20152 $lspace += (2 * $r);
20153 if ($this->rtl) {
20154 $this->x += $lspace;
20155 } else {
20156 $this->x -= $lspace;
20157 }
20158 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8);
20159 break;
20160 }
20161 case 'circle': {
20162 $r = $size / 6;
20163 $lspace += (2 * $r);
20164 if ($this->rtl) {
20165 $this->x += $lspace;
20166 } else {
20167 $this->x -= $lspace;
20168 }
20169 $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor;
20170 $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
20171 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
20172 $this->_out($prev_line_style); // restore line settings
20173 break;
20174 }
20175 case 'square': {
20176 $l = $size / 3;
20177 $lspace += $l;
20178 if ($this->rtl) {;
20179 $this->x += $lspace;
20180 } else {
20181 $this->x -= $lspace;
20182 }
20183 $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color);
20184 break;
20185 }
20186 case 'img': {
20187 // 1=>type, 2=>width, 3=>height, 4=>image.ext
20188 $lspace += $img[2];
20189 if ($this->rtl) {;
20190 $this->x += $lspace;
20191 } else {
20192 $this->x -= $lspace;
20193 }
20194 $imgtype = strtolower($img[1]);
20195 $prev_y = $this->y;
20196 switch ($imgtype) {
20197 case 'svg': {
20198 $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
20199 break;
20200 }
20201 case 'ai':
20202 case 'eps': {
20203 $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
20204 break;
20205 }
20206 default: {
20207 $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);
20208 break;
20209 }
20210 }
20211 $this->y = $prev_y;
20212 break;
20213 }
20214 // ordered types
20215 // $this->listcount[$this->listnum];
20216 // $textitem
20217 case '1':
20218 case 'decimal': {
20219 $textitem = $this->listcount[$this->listnum];
20220 break;
20221 }
20222 case 'decimal-leading-zero': {
20223 $textitem = sprintf('%02d', $this->listcount[$this->listnum]);
20224 break;
20225 }
20226 case 'i':
20227 case 'lower-roman': {
20228 $textitem = strtolower(TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]));
20229 break;
20230 }
20231 case 'I':
20232 case 'upper-roman': {
20233 $textitem = TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]);
20234 break;
20235 }
20236 case 'a':
20237 case 'lower-alpha':
20238 case 'lower-latin': {
20239 $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
20240 break;
20241 }
20242 case 'A':
20243 case 'upper-alpha':
20244 case 'upper-latin': {
20245 $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
20246 break;
20247 }
20248 case 'lower-greek': {
20249 $textitem = TCPDF_FONTS::unichr((945 + $this->listcount[$this->listnum] - 1), $this->isunicode);
20250 break;
20251 }
20252 /*
20253 // Types to be implemented (special handling)
20254 case 'hebrew': {
20255 break;
20256 }
20257 case 'armenian': {
20258 break;
20259 }
20260 case 'georgian': {
20261 break;
20262 }
20263 case 'cjk-ideographic': {
20264 break;
20265 }
20266 case 'hiragana': {
20267 break;
20268 }
20269 case 'katakana': {
20270 break;
20271 }
20272 case 'hiragana-iroha': {
20273 break;
20274 }
20275 case 'katakana-iroha': {
20276 break;
20277 }
20278 */
20279 default: {
20280 $textitem = $this->listcount[$this->listnum];
20281 }
20282 }
20283 if (!TCPDF_STATIC::empty_string($textitem)) {
20284 // Check whether we need a new page or new column
20285 $prev_y = $this->y;
20286 $h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
20287 if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) {
20288 $tmpx = $this->x;
20289 }
20290 // print ordered item
20291 if ($this->rtl) {
20292 $textitem = '.'.$textitem;
20293 } else {
20294 $textitem = $textitem.'.';
20295 }
20296 $lspace += $this->GetStringWidth($textitem);
20297 if ($this->rtl) {
20298 $this->x += $lspace;
20299 } else {
20300 $this->x -= $lspace;
20301 }
20302 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
20303 }
20304 $this->x = $tmpx;
20305 $this->lispacer = '^';
20306 // restore colors
20307 $this->SetFillColorArray($bgcolor);
20308 $this->SetDrawColorArray($strokecolor);
20309 $this->SettextColorArray($color);
20310 }
20311
20318 protected function getGraphicVars() {
20319 $grapvars = array(
20320 'FontFamily' => $this->FontFamily,
20321 'FontStyle' => $this->FontStyle,
20322 'FontSizePt' => $this->FontSizePt,
20323 'rMargin' => $this->rMargin,
20324 'lMargin' => $this->lMargin,
20325 'cell_padding' => $this->cell_padding,
20326 'cell_margin' => $this->cell_margin,
20327 'LineWidth' => $this->LineWidth,
20328 'linestyleWidth' => $this->linestyleWidth,
20329 'linestyleCap' => $this->linestyleCap,
20330 'linestyleJoin' => $this->linestyleJoin,
20331 'linestyleDash' => $this->linestyleDash,
20332 'textrendermode' => $this->textrendermode,
20333 'textstrokewidth' => $this->textstrokewidth,
20334 'DrawColor' => $this->DrawColor,
20335 'FillColor' => $this->FillColor,
20336 'TextColor' => $this->TextColor,
20337 'ColorFlag' => $this->ColorFlag,
20338 'bgcolor' => $this->bgcolor,
20339 'fgcolor' => $this->fgcolor,
20340 'htmlvspace' => $this->htmlvspace,
20341 'listindent' => $this->listindent,
20342 'listindentlevel' => $this->listindentlevel,
20343 'listnum' => $this->listnum,
20344 'listordered' => $this->listordered,
20345 'listcount' => $this->listcount,
20346 'lispacer' => $this->lispacer,
20347 'cell_height_ratio' => $this->cell_height_ratio,
20348 'font_stretching' => $this->font_stretching,
20349 'font_spacing' => $this->font_spacing,
20350 'alpha' => $this->alpha,
20351 // extended
20352 'lasth' => $this->lasth,
20353 'tMargin' => $this->tMargin,
20354 'bMargin' => $this->bMargin,
20355 'AutoPageBreak' => $this->AutoPageBreak,
20356 'PageBreakTrigger' => $this->PageBreakTrigger,
20357 'x' => $this->x,
20358 'y' => $this->y,
20359 'w' => $this->w,
20360 'h' => $this->h,
20361 'wPt' => $this->wPt,
20362 'hPt' => $this->hPt,
20363 'fwPt' => $this->fwPt,
20364 'fhPt' => $this->fhPt,
20365 'page' => $this->page,
20366 'current_column' => $this->current_column,
20367 'num_columns' => $this->num_columns
20368 );
20369 return $grapvars;
20370 }
20371
20379 protected function setGraphicVars($gvars, $extended=false) {
20380 if ($this->state != 2) {
20381 return;
20382 }
20383 $this->FontFamily = $gvars['FontFamily'];
20384 $this->FontStyle = $gvars['FontStyle'];
20385 $this->FontSizePt = $gvars['FontSizePt'];
20386 $this->rMargin = $gvars['rMargin'];
20387 $this->lMargin = $gvars['lMargin'];
20388 $this->cell_padding = $gvars['cell_padding'];
20389 $this->cell_margin = $gvars['cell_margin'];
20390 $this->LineWidth = $gvars['LineWidth'];
20391 $this->linestyleWidth = $gvars['linestyleWidth'];
20392 $this->linestyleCap = $gvars['linestyleCap'];
20393 $this->linestyleJoin = $gvars['linestyleJoin'];
20394 $this->linestyleDash = $gvars['linestyleDash'];
20395 $this->textrendermode = $gvars['textrendermode'];
20396 $this->textstrokewidth = $gvars['textstrokewidth'];
20397 $this->DrawColor = $gvars['DrawColor'];
20398 $this->FillColor = $gvars['FillColor'];
20399 $this->TextColor = $gvars['TextColor'];
20400 $this->ColorFlag = $gvars['ColorFlag'];
20401 $this->bgcolor = $gvars['bgcolor'];
20402 $this->fgcolor = $gvars['fgcolor'];
20403 $this->htmlvspace = $gvars['htmlvspace'];
20404 $this->listindent = $gvars['listindent'];
20405 $this->listindentlevel = $gvars['listindentlevel'];
20406 $this->listnum = $gvars['listnum'];
20407 $this->listordered = $gvars['listordered'];
20408 $this->listcount = $gvars['listcount'];
20409 $this->lispacer = $gvars['lispacer'];
20410 $this->cell_height_ratio = $gvars['cell_height_ratio'];
20411 $this->font_stretching = $gvars['font_stretching'];
20412 $this->font_spacing = $gvars['font_spacing'];
20413 $this->alpha = $gvars['alpha'];
20414 if ($extended) {
20415 // restore extended values
20416 $this->lasth = $gvars['lasth'];
20417 $this->tMargin = $gvars['tMargin'];
20418 $this->bMargin = $gvars['bMargin'];
20419 $this->AutoPageBreak = $gvars['AutoPageBreak'];
20420 $this->PageBreakTrigger = $gvars['PageBreakTrigger'];
20421 $this->x = $gvars['x'];
20422 $this->y = $gvars['y'];
20423 $this->w = $gvars['w'];
20424 $this->h = $gvars['h'];
20425 $this->wPt = $gvars['wPt'];
20426 $this->hPt = $gvars['hPt'];
20427 $this->fwPt = $gvars['fwPt'];
20428 $this->fhPt = $gvars['fhPt'];
20429 $this->page = $gvars['page'];
20430 $this->current_column = $gvars['current_column'];
20431 $this->num_columns = $gvars['num_columns'];
20432 }
20433 $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
20434 if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
20435 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
20436 }
20437 }
20438
20447 protected function writeDiskCache($filename, $data, $append=false) {
20448 if ($append) {
20449 $fmode = 'ab+';
20450 } else {
20451 $fmode = 'wb+';
20452 }
20453 $f = @fopen($filename, $fmode);
20454 if (!$f) {
20455 $this->Error('Unable to write cache file: '.$filename);
20456 } else {
20457 fwrite($f, $data);
20458 fclose($f);
20459 }
20460 // update file length (needed for transactions)
20461 if (!isset($this->cache_file_length['_'.$filename])) {
20462 $this->cache_file_length['_'.$filename] = strlen($data);
20463 } else {
20464 $this->cache_file_length['_'.$filename] += strlen($data);
20465 }
20466 }
20467
20475 protected function readDiskCache($filename) {
20476 return file_get_contents($filename);
20477 }
20478
20485 protected function setBuffer($data) {
20486 $this->bufferlen += strlen($data);
20487 if ($this->diskcache) {
20488 if (!isset($this->buffer) OR TCPDF_STATIC::empty_string($this->buffer)) {
20489 $this->buffer = TCPDF_STATIC::getObjFilename('buffer');
20490 }
20491 $this->writeDiskCache($this->buffer, $data, true);
20492 } else {
20493 $this->buffer .= $data;
20494 }
20495 }
20496
20503 protected function replaceBuffer($data) {
20504 $this->bufferlen = strlen($data);
20505 if ($this->diskcache) {
20506 if (!isset($this->buffer) OR TCPDF_STATIC::empty_string($this->buffer)) {
20507 $this->buffer = TCPDF_STATIC::getObjFilename('buffer');
20508 }
20509 $this->writeDiskCache($this->buffer, $data, false);
20510 } else {
20511 $this->buffer = $data;
20512 }
20513 }
20514
20521 protected function getBuffer() {
20522 if ($this->diskcache) {
20523 return $this->readDiskCache($this->buffer);
20524 } else {
20525 return $this->buffer;
20526 }
20527 }
20528
20537 protected function setPageBuffer($page, $data, $append=false) {
20538 if ($this->diskcache) {
20539 if (!isset($this->pages[$page])) {
20540 $this->pages[$page] = TCPDF_STATIC::getObjFilename('page'.$page);
20541 }
20542 $this->writeDiskCache($this->pages[$page], $data, $append);
20543 } else {
20544 if ($append) {
20545 $this->pages[$page] .= $data;
20546 } else {
20547 $this->pages[$page] = $data;
20548 }
20549 }
20550 if ($append AND isset($this->pagelen[$page])) {
20551 $this->pagelen[$page] += strlen($data);
20552 } else {
20553 $this->pagelen[$page] = strlen($data);
20554 }
20555 }
20556
20564 protected function getPageBuffer($page) {
20565 if ($this->diskcache) {
20566 return $this->readDiskCache($this->pages[$page]);
20567 } elseif (isset($this->pages[$page])) {
20568 return $this->pages[$page];
20569 }
20570 return false;
20571 }
20572
20581 protected function setImageBuffer($image, $data) {
20582 if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) {
20583 $this->imagekeys[$this->numimages] = $image;
20584 $data['i'] = $this->numimages;
20585 ++$this->numimages;
20586 }
20587 if ($this->diskcache) {
20588 if (!isset($this->images[$image])) {
20589 $this->images[$image] = TCPDF_STATIC::getObjFilename('image'.$image);
20590 }
20591 $this->writeDiskCache($this->images[$image], serialize($data));
20592 } else {
20593 $this->images[$image] = $data;
20594 }
20595 return $data['i'];
20596 }
20597
20606 protected function setImageSubBuffer($image, $key, $data) {
20607 if (!isset($this->images[$image])) {
20608 $this->setImageBuffer($image, array());
20609 }
20610 if ($this->diskcache) {
20611 $tmpimg = $this->getImageBuffer($image);
20612 $tmpimg[$key] = $data;
20613 $this->writeDiskCache($this->images[$image], serialize($tmpimg));
20614 } else {
20615 $this->images[$image][$key] = $data;
20616 }
20617 }
20618
20626 protected function getImageBuffer($image) {
20627 if ($this->diskcache AND isset($this->images[$image])) {
20628 return unserialize($this->readDiskCache($this->images[$image]));
20629 } elseif (isset($this->images[$image])) {
20630 return $this->images[$image];
20631 }
20632 return false;
20633 }
20634
20642 protected function setFontBuffer($font, $data) {
20643 if ($this->diskcache) {
20644 if (!isset($this->fonts[$font])) {
20645 $this->fonts[$font] = TCPDF_STATIC::getObjFilename('font');
20646 }
20647 $this->writeDiskCache($this->fonts[$font], serialize($data));
20648 } else {
20649 $this->fonts[$font] = $data;
20650 }
20651 if (!in_array($font, $this->fontkeys)) {
20652 $this->fontkeys[] = $font;
20653 // store object ID for current font
20654 ++$this->n;
20655 $this->font_obj_ids[$font] = $this->n;
20656 $this->setFontSubBuffer($font, 'n', $this->n);
20657 }
20658 }
20659
20668 protected function setFontSubBuffer($font, $key, $data) {
20669 if (!isset($this->fonts[$font])) {
20670 $this->setFontBuffer($font, array());
20671 }
20672 if ($this->diskcache) {
20673 $tmpfont = $this->getFontBuffer($font);
20674 $tmpfont[$key] = $data;
20675 $this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
20676 } else {
20677 $this->fonts[$font][$key] = $data;
20678 }
20679 }
20680
20688 protected function getFontBuffer($font) {
20689 if ($this->diskcache AND isset($this->fonts[$font])) {
20690 return unserialize($this->readDiskCache($this->fonts[$font]));
20691 } elseif (isset($this->fonts[$font])) {
20692 return $this->fonts[$font];
20693 }
20694 return false;
20695 }
20696
20705 public function movePage($frompage, $topage) {
20706 if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
20707 return false;
20708 }
20709 if ($frompage == $this->page) {
20710 // close the page before moving it
20711 $this->endPage();
20712 }
20713 // move all page-related states
20714 $tmppage = $this->getPageBuffer($frompage);
20715 $tmppagedim = $this->pagedim[$frompage];
20716 $tmppagelen = $this->pagelen[$frompage];
20717 $tmpintmrk = $this->intmrk[$frompage];
20718 $tmpbordermrk = $this->bordermrk[$frompage];
20719 $tmpcntmrk = $this->cntmrk[$frompage];
20720 $tmppageobjects = $this->pageobjects[$frompage];
20721 if (isset($this->footerpos[$frompage])) {
20722 $tmpfooterpos = $this->footerpos[$frompage];
20723 }
20724 if (isset($this->footerlen[$frompage])) {
20725 $tmpfooterlen = $this->footerlen[$frompage];
20726 }
20727 if (isset($this->transfmrk[$frompage])) {
20728 $tmptransfmrk = $this->transfmrk[$frompage];
20729 }
20730 if (isset($this->PageAnnots[$frompage])) {
20731 $tmpannots = $this->PageAnnots[$frompage];
20732 }
20733 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
20734 for ($i = $frompage; $i > $topage; --$i) {
20735 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) {
20736 --$this->pagegroups[$this->newpagegroup[$i]];
20737 break;
20738 }
20739 }
20740 for ($i = $topage; $i > 0; --$i) {
20741 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) {
20742 ++$this->pagegroups[$this->newpagegroup[$i]];
20743 break;
20744 }
20745 }
20746 }
20747 for ($i = $frompage; $i > $topage; --$i) {
20748 $j = $i - 1;
20749 // shift pages down
20750 $this->setPageBuffer($i, $this->getPageBuffer($j));
20751 $this->pagedim[$i] = $this->pagedim[$j];
20752 $this->pagelen[$i] = $this->pagelen[$j];
20753 $this->intmrk[$i] = $this->intmrk[$j];
20754 $this->bordermrk[$i] = $this->bordermrk[$j];
20755 $this->cntmrk[$i] = $this->cntmrk[$j];
20756 $this->pageobjects[$i] = $this->pageobjects[$j];
20757 if (isset($this->footerpos[$j])) {
20758 $this->footerpos[$i] = $this->footerpos[$j];
20759 } elseif (isset($this->footerpos[$i])) {
20760 unset($this->footerpos[$i]);
20761 }
20762 if (isset($this->footerlen[$j])) {
20763 $this->footerlen[$i] = $this->footerlen[$j];
20764 } elseif (isset($this->footerlen[$i])) {
20765 unset($this->footerlen[$i]);
20766 }
20767 if (isset($this->transfmrk[$j])) {
20768 $this->transfmrk[$i] = $this->transfmrk[$j];
20769 } elseif (isset($this->transfmrk[$i])) {
20770 unset($this->transfmrk[$i]);
20771 }
20772 if (isset($this->PageAnnots[$j])) {
20773 $this->PageAnnots[$i] = $this->PageAnnots[$j];
20774 } elseif (isset($this->PageAnnots[$i])) {
20775 unset($this->PageAnnots[$i]);
20776 }
20777 if (isset($this->newpagegroup[$j])) {
20778 $this->newpagegroup[$i] = $this->newpagegroup[$j];
20779 unset($this->newpagegroup[$j]);
20780 }
20781 if ($this->currpagegroup == $j) {
20782 $this->currpagegroup = $i;
20783 }
20784 }
20785 $this->setPageBuffer($topage, $tmppage);
20786 $this->pagedim[$topage] = $tmppagedim;
20787 $this->pagelen[$topage] = $tmppagelen;
20788 $this->intmrk[$topage] = $tmpintmrk;
20789 $this->bordermrk[$topage] = $tmpbordermrk;
20790 $this->cntmrk[$topage] = $tmpcntmrk;
20791 $this->pageobjects[$topage] = $tmppageobjects;
20792 if (isset($tmpfooterpos)) {
20793 $this->footerpos[$topage] = $tmpfooterpos;
20794 } elseif (isset($this->footerpos[$topage])) {
20795 unset($this->footerpos[$topage]);
20796 }
20797 if (isset($tmpfooterlen)) {
20798 $this->footerlen[$topage] = $tmpfooterlen;
20799 } elseif (isset($this->footerlen[$topage])) {
20800 unset($this->footerlen[$topage]);
20801 }
20802 if (isset($tmptransfmrk)) {
20803 $this->transfmrk[$topage] = $tmptransfmrk;
20804 } elseif (isset($this->transfmrk[$topage])) {
20805 unset($this->transfmrk[$topage]);
20806 }
20807 if (isset($tmpannots)) {
20808 $this->PageAnnots[$topage] = $tmpannots;
20809 } elseif (isset($this->PageAnnots[$topage])) {
20810 unset($this->PageAnnots[$topage]);
20811 }
20812 // adjust outlines
20813 $tmpoutlines = $this->outlines;
20814 foreach ($tmpoutlines as $key => $outline) {
20815 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
20816 $this->outlines[$key]['p'] = ($outline['p'] + 1);
20817 } elseif ($outline['p'] == $frompage) {
20818 $this->outlines[$key]['p'] = $topage;
20819 }
20820 }
20821 // adjust dests
20822 $tmpdests = $this->dests;
20823 foreach ($tmpdests as $key => $dest) {
20824 if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
20825 $this->dests[$key]['p'] = ($dest['p'] + 1);
20826 } elseif ($dest['p'] == $frompage) {
20827 $this->dests[$key]['p'] = $topage;
20828 }
20829 }
20830 // adjust links
20831 $tmplinks = $this->links;
20832 foreach ($tmplinks as $key => $link) {
20833 if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
20834 $this->links[$key][0] = ($link[0] + 1);
20835 } elseif ($link[0] == $frompage) {
20836 $this->links[$key][0] = $topage;
20837 }
20838 }
20839 // adjust javascript
20840 $tmpjavascript = $this->javascript;
20841 global $jfrompage, $jtopage;
20842 $jfrompage = $frompage;
20843 $jtopage = $topage;
20844 $this->javascript = preg_replace_callback('/this\.addField\‍(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
20845 create_function('$matches', 'global $jfrompage, $jtopage;
20846 $pagenum = intval($matches[3]) + 1;
20847 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
20848 $newpage = ($pagenum + 1);
20849 } elseif ($pagenum == $jfrompage) {
20850 $newpage = $jtopage;
20851 } else {
20852 $newpage = $pagenum;
20853 }
20854 --$newpage;
20855 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
20856 // return to last page
20857 $this->lastPage(true);
20858 return true;
20859 }
20860
20868 public function deletePage($page) {
20869 if (($page < 1) OR ($page > $this->numpages)) {
20870 return false;
20871 }
20872 // delete current page
20873 unset($this->pages[$page]);
20874 unset($this->pagedim[$page]);
20875 unset($this->pagelen[$page]);
20876 unset($this->intmrk[$page]);
20877 unset($this->bordermrk[$page]);
20878 unset($this->cntmrk[$page]);
20879 foreach ($this->pageobjects[$page] as $oid) {
20880 if (isset($this->offsets[$oid])){
20881 unset($this->offsets[$oid]);
20882 }
20883 }
20884 unset($this->pageobjects[$page]);
20885 if (isset($this->footerpos[$page])) {
20886 unset($this->footerpos[$page]);
20887 }
20888 if (isset($this->footerlen[$page])) {
20889 unset($this->footerlen[$page]);
20890 }
20891 if (isset($this->transfmrk[$page])) {
20892 unset($this->transfmrk[$page]);
20893 }
20894 if (isset($this->PageAnnots[$page])) {
20895 unset($this->PageAnnots[$page]);
20896 }
20897 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
20898 for ($i = $page; $i > 0; --$i) {
20899 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) {
20900 --$this->pagegroups[$this->newpagegroup[$i]];
20901 break;
20902 }
20903 }
20904 }
20905 if (isset($this->pageopen[$page])) {
20906 unset($this->pageopen[$page]);
20907 }
20908 if ($page < $this->numpages) {
20909 // update remaining pages
20910 for ($i = $page; $i < $this->numpages; ++$i) {
20911 $j = $i + 1;
20912 // shift pages
20913 $this->setPageBuffer($i, $this->getPageBuffer($j));
20914 $this->pagedim[$i] = $this->pagedim[$j];
20915 $this->pagelen[$i] = $this->pagelen[$j];
20916 $this->intmrk[$i] = $this->intmrk[$j];
20917 $this->bordermrk[$i] = $this->bordermrk[$j];
20918 $this->cntmrk[$i] = $this->cntmrk[$j];
20919 $this->pageobjects[$i] = $this->pageobjects[$j];
20920 if (isset($this->footerpos[$j])) {
20921 $this->footerpos[$i] = $this->footerpos[$j];
20922 } elseif (isset($this->footerpos[$i])) {
20923 unset($this->footerpos[$i]);
20924 }
20925 if (isset($this->footerlen[$j])) {
20926 $this->footerlen[$i] = $this->footerlen[$j];
20927 } elseif (isset($this->footerlen[$i])) {
20928 unset($this->footerlen[$i]);
20929 }
20930 if (isset($this->transfmrk[$j])) {
20931 $this->transfmrk[$i] = $this->transfmrk[$j];
20932 } elseif (isset($this->transfmrk[$i])) {
20933 unset($this->transfmrk[$i]);
20934 }
20935 if (isset($this->PageAnnots[$j])) {
20936 $this->PageAnnots[$i] = $this->PageAnnots[$j];
20937 } elseif (isset($this->PageAnnots[$i])) {
20938 unset($this->PageAnnots[$i]);
20939 }
20940 if (isset($this->newpagegroup[$j])) {
20941 $this->newpagegroup[$i] = $this->newpagegroup[$j];
20942 unset($this->newpagegroup[$j]);
20943 }
20944 if ($this->currpagegroup == $j) {
20945 $this->currpagegroup = $i;
20946 }
20947 if (isset($this->pageopen[$j])) {
20948 $this->pageopen[$i] = $this->pageopen[$j];
20949 } elseif (isset($this->pageopen[$i])) {
20950 unset($this->pageopen[$i]);
20951 }
20952 }
20953 // remove last page
20954 unset($this->pages[$this->numpages]);
20955 unset($this->pagedim[$this->numpages]);
20956 unset($this->pagelen[$this->numpages]);
20957 unset($this->intmrk[$this->numpages]);
20958 unset($this->bordermrk[$this->numpages]);
20959 unset($this->cntmrk[$this->numpages]);
20960 foreach ($this->pageobjects[$this->numpages] as $oid) {
20961 if (isset($this->offsets[$oid])){
20962 unset($this->offsets[$oid]);
20963 }
20964 }
20965 unset($this->pageobjects[$this->numpages]);
20966 if (isset($this->footerpos[$this->numpages])) {
20967 unset($this->footerpos[$this->numpages]);
20968 }
20969 if (isset($this->footerlen[$this->numpages])) {
20970 unset($this->footerlen[$this->numpages]);
20971 }
20972 if (isset($this->transfmrk[$this->numpages])) {
20973 unset($this->transfmrk[$this->numpages]);
20974 }
20975 if (isset($this->PageAnnots[$this->numpages])) {
20976 unset($this->PageAnnots[$this->numpages]);
20977 }
20978 if (isset($this->newpagegroup[$this->numpages])) {
20979 unset($this->newpagegroup[$this->numpages]);
20980 }
20981 if ($this->currpagegroup == $this->numpages) {
20982 $this->currpagegroup = ($this->numpages - 1);
20983 }
20984 if (isset($this->pagegroups[$this->numpages])) {
20985 unset($this->pagegroups[$this->numpages]);
20986 }
20987 if (isset($this->pageopen[$this->numpages])) {
20988 unset($this->pageopen[$this->numpages]);
20989 }
20990 }
20991 --$this->numpages;
20992 $this->page = $this->numpages;
20993 // adjust outlines
20994 $tmpoutlines = $this->outlines;
20995 foreach ($tmpoutlines as $key => $outline) {
20996 if ($outline['p'] > $page) {
20997 $this->outlines[$key]['p'] = $outline['p'] - 1;
20998 } elseif ($outline['p'] == $page) {
20999 unset($this->outlines[$key]);
21000 }
21001 }
21002 // adjust dests
21003 $tmpdests = $this->dests;
21004 foreach ($tmpdests as $key => $dest) {
21005 if ($dest['p'] > $page) {
21006 $this->dests[$key]['p'] = $dest['p'] - 1;
21007 } elseif ($dest['p'] == $page) {
21008 unset($this->dests[$key]);
21009 }
21010 }
21011 // adjust links
21012 $tmplinks = $this->links;
21013 foreach ($tmplinks as $key => $link) {
21014 if ($link[0] > $page) {
21015 $this->links[$key][0] = $link[0] - 1;
21016 } elseif ($link[0] == $page) {
21017 unset($this->links[$key]);
21018 }
21019 }
21020 // adjust javascript
21021 $tmpjavascript = $this->javascript;
21022 global $jpage;
21023 $jpage = $page;
21024 $this->javascript = preg_replace_callback('/this\.addField\‍(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
21025 create_function('$matches', 'global $jpage;
21026 $pagenum = intval($matches[3]) + 1;
21027 if ($pagenum >= $jpage) {
21028 $newpage = ($pagenum - 1);
21029 } elseif ($pagenum == $jpage) {
21030 $newpage = 1;
21031 } else {
21032 $newpage = $pagenum;
21033 }
21034 --$newpage;
21035 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
21036 // return to last page
21037 if ($this->numpages > 0) {
21038 $this->lastPage(true);
21039 }
21040 return true;
21041 }
21042
21050 public function copyPage($page=0) {
21051 if ($page == 0) {
21052 // default value
21053 $page = $this->page;
21054 }
21055 if (($page < 1) OR ($page > $this->numpages)) {
21056 return false;
21057 }
21058 // close the last page
21059 $this->endPage();
21060 // copy all page-related states
21061 ++$this->numpages;
21062 $this->page = $this->numpages;
21063 $this->setPageBuffer($this->page, $this->getPageBuffer($page));
21064 $this->pagedim[$this->page] = $this->pagedim[$page];
21065 $this->pagelen[$this->page] = $this->pagelen[$page];
21066 $this->intmrk[$this->page] = $this->intmrk[$page];
21067 $this->bordermrk[$this->page] = $this->bordermrk[$page];
21068 $this->cntmrk[$this->page] = $this->cntmrk[$page];
21069 $this->pageobjects[$this->page] = $this->pageobjects[$page];
21070 $this->pageopen[$this->page] = false;
21071 if (isset($this->footerpos[$page])) {
21072 $this->footerpos[$this->page] = $this->footerpos[$page];
21073 }
21074 if (isset($this->footerlen[$page])) {
21075 $this->footerlen[$this->page] = $this->footerlen[$page];
21076 }
21077 if (isset($this->transfmrk[$page])) {
21078 $this->transfmrk[$this->page] = $this->transfmrk[$page];
21079 }
21080 if (isset($this->PageAnnots[$page])) {
21081 $this->PageAnnots[$this->page] = $this->PageAnnots[$page];
21082 }
21083 if (isset($this->newpagegroup[$page])) {
21084 // start a new group
21085 $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1;
21086 $this->currpagegroup = $this->newpagegroup[$this->page];
21087 $this->pagegroups[$this->currpagegroup] = 1;
21088 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
21089 ++$this->pagegroups[$this->currpagegroup];
21090 }
21091 // copy outlines
21092 $tmpoutlines = $this->outlines;
21093 foreach ($tmpoutlines as $key => $outline) {
21094 if ($outline['p'] == $page) {
21095 $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'x' => $outline['x'], 'y' => $outline['y'], 'p' => $this->page, 's' => $outline['s'], 'c' => $outline['c']);
21096 }
21097 }
21098 // copy links
21099 $tmplinks = $this->links;
21100 foreach ($tmplinks as $key => $link) {
21101 if ($link[0] == $page) {
21102 $this->links[] = array($this->page, $link[1]);
21103 }
21104 }
21105 // return to last page
21106 $this->lastPage(true);
21107 return true;
21108 }
21109
21127 public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
21128 $fontsize = $this->FontSizePt;
21129 $fontfamily = $this->FontFamily;
21130 $fontstyle = $this->FontStyle;
21131 $w = $this->w - $this->lMargin - $this->rMargin;
21132 $spacer = $this->GetStringWidth(chr(32)) * 4;
21133 $lmargin = $this->lMargin;
21134 $rmargin = $this->rMargin;
21135 $x_start = $this->GetX();
21136 $page_first = $this->page;
21137 $current_page = $this->page;
21138 $page_fill_start = false;
21139 $page_fill_end = false;
21140 $current_column = $this->current_column;
21141 if (TCPDF_STATIC::empty_string($numbersfont)) {
21142 $numbersfont = $this->default_monospaced_font;
21143 }
21144 if (TCPDF_STATIC::empty_string($filler)) {
21145 $filler = ' ';
21146 }
21147 if (TCPDF_STATIC::empty_string($page)) {
21148 $gap = ' ';
21149 } else {
21150 $gap = '';
21151 if ($page < 1) {
21152 $page = 1;
21153 }
21154 }
21155 $this->SetFont($numbersfont, $fontstyle, $fontsize);
21156 $numwidth = $this->GetStringWidth('00000');
21157 $maxpage = 0; //used for pages on attached documents
21158 foreach ($this->outlines as $key => $outline) {
21159 // check for extra pages (used for attachments)
21160 if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) {
21161 $outline['p'] += ($this->page - $page_first);
21162 }
21163 if ($this->rtl) {
21164 $aligntext = 'R';
21165 $alignnum = 'L';
21166 } else {
21167 $aligntext = 'L';
21168 $alignnum = 'R';
21169 }
21170 if ($outline['l'] == 0) {
21171 $this->SetFont($fontfamily, $outline['s'].'B', $fontsize);
21172 } else {
21173 $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']);
21174 }
21175 $this->SetTextColorArray($outline['c']);
21176 // check for page break
21177 $this->checkPageBreak((2 * $this->FontSize * $this->cell_height_ratio));
21178 // set margins and X position
21179 if (($this->page == $current_page) AND ($this->current_column == $current_column)) {
21180 $this->lMargin = $lmargin;
21181 $this->rMargin = $rmargin;
21182 } else {
21183 if ($this->current_column != $current_column) {
21184 if ($this->rtl) {
21185 $x_start = $this->w - $this->columns[$this->current_column]['x'];
21186 } else {
21187 $x_start = $this->columns[$this->current_column]['x'];
21188 }
21189 }
21190 $lmargin = $this->lMargin;
21191 $rmargin = $this->rMargin;
21192 $current_page = $this->page;
21193 $current_column = $this->current_column;
21194 }
21195 $this->SetX($x_start);
21196 $indent = ($spacer * $outline['l']);
21197 if ($this->rtl) {
21198 $this->x -= $indent;
21199 $this->rMargin = $this->w - $this->x;
21200 } else {
21201 $this->x += $indent;
21202 $this->lMargin = $this->x;
21203 }
21204 $link = $this->AddLink();
21205 $this->SetLink($link, $outline['y'], $outline['p']);
21206 // write the text
21207 if ($this->rtl) {
21208 $txt = ' '.$outline['t'];
21209 } else {
21210 $txt = $outline['t'].' ';
21211 }
21212 $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
21213 if ($this->rtl) {
21214 $tw = $this->x - $this->lMargin;
21215 } else {
21216 $tw = $this->w - $this->rMargin - $this->x;
21217 }
21218 $this->SetFont($numbersfont, $fontstyle, $fontsize);
21219 if (TCPDF_STATIC::empty_string($page)) {
21220 $pagenum = $outline['p'];
21221 } else {
21222 // placemark to be replaced with the correct number
21223 $pagenum = '{#'.($outline['p']).'}';
21224 if ($this->isUnicodeFont()) {
21225 $pagenum = '{'.$pagenum.'}';
21226 }
21227 $maxpage = max($maxpage, $outline['p']);
21228 }
21229 $fw = ($tw - $this->GetStringWidth($pagenum.$filler));
21230 $numfills = floor($fw / $this->GetStringWidth($filler));
21231 if ($numfills > 0) {
21232 $rowfill = str_repeat($filler, $numfills);
21233 } else {
21234 $rowfill = '';
21235 }
21236 if ($this->rtl) {
21237 $pagenum = $pagenum.$gap.$rowfill;
21238 } else {
21239 $pagenum = $rowfill.$gap.$pagenum;
21240 }
21241 // write the number
21242 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
21243 }
21244 $page_last = $this->getPage();
21245 $numpages = ($page_last - $page_first + 1);
21246 // account for booklet mode
21247 if ($this->booklet) {
21248 // check if a blank page is required before TOC
21249 $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21250 $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21251 if ($page_fill_start) {
21252 // add a page at the end (to be moved before TOC)
21253 $this->addPage();
21254 ++$page_last;
21255 ++$numpages;
21256 }
21257 if ($page_fill_end) {
21258 // add a page at the end
21259 $this->addPage();
21260 ++$page_last;
21261 ++$numpages;
21262 }
21263 }
21264 $maxpage = max($maxpage, $page_last);
21265 if (!TCPDF_STATIC::empty_string($page)) {
21266 for ($p = $page_first; $p <= $page_last; ++$p) {
21267 // get page data
21268 $temppage = $this->getPageBuffer($p);
21269 for ($n = 1; $n <= $maxpage; ++$n) {
21270 // update page numbers
21271 $a = '{#'.$n.'}';
21272 // get page number aliases
21273 $pnalias = $this->getInternalPageNumberAliases($a);
21274 // calculate replacement number
21275 if (($n >= $page) AND ($n <= $this->numpages)) {
21276 $np = $n + $numpages;
21277 } else {
21278 $np = $n;
21279 }
21280 $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21281 $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21282 // replace aliases with numbers
21283 foreach ($pnalias['u'] as $u) {
21284 $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
21285 if ($this->rtl) {
21286 $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21287 } else {
21288 $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21289 }
21290 $temppage = str_replace($u, $nr, $temppage);
21291 }
21292 foreach ($pnalias['a'] as $a) {
21293 $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
21294 if ($this->rtl) {
21295 $nr = $na.' '.$sfill;
21296 } else {
21297 $nr = $sfill.' '.$na;
21298 }
21299 $temppage = str_replace($a, $nr, $temppage);
21300 }
21301 }
21302 // save changes
21303 $this->setPageBuffer($p, $temppage);
21304 }
21305 // move pages
21306 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21307 if ($page_fill_start) {
21308 $this->movePage($page_last, $page_first);
21309 }
21310 for ($i = 0; $i < $numpages; ++$i) {
21311 $this->movePage($page_last, $page);
21312 }
21313 }
21314 }
21315
21332 public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
21333 $filler = ' ';
21334 $prev_htmlLinkColorArray = $this->htmlLinkColorArray;
21335 $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle;
21336 // set new style for link
21337 $this->htmlLinkColorArray = array();
21338 $this->htmlLinkFontStyle = '';
21339 $page_first = $this->getPage();
21340 $page_fill_start = false;
21341 $page_fill_end = false;
21342 // get the font type used for numbers in each template
21343 $current_font = $this->FontFamily;
21344 foreach ($templates as $level => $html) {
21345 $dom = $this->getHtmlDomArray($html);
21346 foreach ($dom as $key => $value) {
21347 if ($value['value'] == '#TOC_PAGE_NUMBER#') {
21348 $this->SetFont($dom[($key - 1)]['fontname']);
21349 $templates['F'.$level] = $this->isUnicodeFont();
21350 }
21351 }
21352 }
21353 $this->SetFont($current_font);
21354 $maxpage = 0; //used for pages on attached documents
21355 foreach ($this->outlines as $key => $outline) {
21356 // get HTML template
21357 $row = $templates[$outline['l']];
21358 if (TCPDF_STATIC::empty_string($page)) {
21359 $pagenum = $outline['p'];
21360 } else {
21361 // placemark to be replaced with the correct number
21362 $pagenum = '{#'.($outline['p']).'}';
21363 if ($templates['F'.$outline['l']]) {
21364 $pagenum = '{'.$pagenum.'}';
21365 }
21366 $maxpage = max($maxpage, $outline['p']);
21367 }
21368 // replace templates with current values
21369 $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
21370 $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
21371 // add link to page
21372 $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
21373 // write bookmark entry
21374 $this->writeHTML($row, false, false, true, false, '');
21375 }
21376 // restore link styles
21377 $this->htmlLinkColorArray = $prev_htmlLinkColorArray;
21378 $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle;
21379 // move TOC page and replace numbers
21380 $page_last = $this->getPage();
21381 $numpages = ($page_last - $page_first + 1);
21382 // account for booklet mode
21383 if ($this->booklet) {
21384 // check if a blank page is required before TOC
21385 $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21386 $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21387 if ($page_fill_start) {
21388 // add a page at the end (to be moved before TOC)
21389 $this->addPage();
21390 ++$page_last;
21391 ++$numpages;
21392 }
21393 if ($page_fill_end) {
21394 // add a page at the end
21395 $this->addPage();
21396 ++$page_last;
21397 ++$numpages;
21398 }
21399 }
21400 $maxpage = max($maxpage, $page_last);
21401 if (!TCPDF_STATIC::empty_string($page)) {
21402 for ($p = $page_first; $p <= $page_last; ++$p) {
21403 // get page data
21404 $temppage = $this->getPageBuffer($p);
21405 for ($n = 1; $n <= $maxpage; ++$n) {
21406 // update page numbers
21407 $a = '{#'.$n.'}';
21408 // get page number aliases
21409 $pnalias = $this->getInternalPageNumberAliases($a);
21410 // calculate replacement number
21411 if ($n >= $page) {
21412 $np = $n + $numpages;
21413 } else {
21414 $np = $n;
21415 }
21416 $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21417 $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21418 // replace aliases with numbers
21419 foreach ($pnalias['u'] as $u) {
21420 if ($correct_align) {
21421 $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
21422 if ($this->rtl) {
21423 $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21424 } else {
21425 $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21426 }
21427 } else {
21428 $nr = $nu;
21429 }
21430 $temppage = str_replace($u, $nr, $temppage);
21431 }
21432 foreach ($pnalias['a'] as $a) {
21433 if ($correct_align) {
21434 $sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
21435 if ($this->rtl) {
21436 $nr = $na.' '.$sfill;
21437 } else {
21438 $nr = $sfill.' '.$na;
21439 }
21440 } else {
21441 $nr = $na;
21442 }
21443 $temppage = str_replace($a, $nr, $temppage);
21444 }
21445 }
21446 // save changes
21447 $this->setPageBuffer($p, $temppage);
21448 }
21449 // move pages
21450 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21451 if ($page_fill_start) {
21452 $this->movePage($page_last, $page_first);
21453 }
21454 for ($i = 0; $i < $numpages; ++$i) {
21455 $this->movePage($page_last, $page);
21456 }
21457 }
21458 }
21459
21465 public function startTransaction() {
21466 if (isset($this->objcopy)) {
21467 // remove previous copy
21468 $this->commitTransaction();
21469 }
21470 // record current page number and Y position
21471 $this->start_transaction_page = $this->page;
21472 $this->start_transaction_y = $this->y;
21473 // clone current object
21474 $this->objcopy = TCPDF_STATIC::objclone($this);
21475 }
21476
21482 public function commitTransaction() {
21483 if (isset($this->objcopy)) {
21484 $this->objcopy->_destroy(true, true);
21485 unset($this->objcopy);
21486 }
21487 }
21488
21496 public function rollbackTransaction($self=false) {
21497 if (isset($this->objcopy)) {
21498 if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
21499 // truncate files to previous values
21500 foreach ($this->objcopy->cache_file_length as $file => $length) {
21501 $file = substr($file, 1);
21502 $handle = fopen($file, 'r+');
21503 ftruncate($handle, $length);
21504 }
21505 }
21506 $this->_destroy(true, true);
21507 if ($self) {
21508 $objvars = get_object_vars($this->objcopy);
21509 foreach ($objvars as $key => $value) {
21510 $this->$key = $value;
21511 }
21512 }
21513 return $this->objcopy;
21514 }
21515 return $this;
21516 }
21517
21518 // --- MULTI COLUMNS METHODS -----------------------
21519
21528 public function setEqualColumns($numcols=0, $width=0, $y='') {
21529 $this->columns = array();
21530 if ($numcols < 2) {
21531 $numcols = 0;
21532 $this->columns = array();
21533 } else {
21534 // maximum column width
21535 $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
21536 if (($width == 0) OR ($width > $maxwidth)) {
21537 $width = $maxwidth;
21538 }
21540 $y = $this->y;
21541 }
21542 // space between columns
21543 $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
21544 // fill the columns array (with, space, starting Y position)
21545 for ($i = 0; $i < $numcols; ++$i) {
21546 $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
21547 }
21548 }
21549 $this->num_columns = $numcols;
21550 $this->current_column = 0;
21551 $this->column_start_page = $this->page;
21552 $this->selectColumn(0);
21553 }
21554
21560 public function resetColumns() {
21561 $this->lMargin = $this->original_lMargin;
21562 $this->rMargin = $this->original_rMargin;
21563 $this->setEqualColumns();
21564 }
21565
21573 public function setColumnsArray($columns) {
21574 $this->columns = $columns;
21575 $this->num_columns = count($columns);
21576 $this->current_column = 0;
21577 $this->column_start_page = $this->page;
21578 $this->selectColumn(0);
21579 }
21580
21587 public function selectColumn($col='') {
21588 if (is_string($col)) {
21589 $col = $this->current_column;
21590 } elseif ($col >= $this->num_columns) {
21591 $col = 0;
21592 }
21593 $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
21594 $enable_thead = false;
21595 if ($this->num_columns > 1) {
21596 if ($col != $this->current_column) {
21597 // move Y pointer at the top of the column
21598 if ($this->column_start_page == $this->page) {
21599 $this->y = $this->columns[$col]['y'];
21600 } else {
21601 $this->y = $this->tMargin;
21602 }
21603 // Avoid to write table headers more than once
21604 if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) {
21605 $enable_thead = true;
21606 $this->maxselcol['page'] = $this->page;
21607 $this->maxselcol['column'] = $col;
21608 }
21609 }
21610 $xshift = $this->colxshift;
21611 // set X position of the current column by case
21612 $listindent = ($this->listindentlevel * $this->listindent);
21613 // calculate column X position
21614 $colpos = 0;
21615 for ($i = 0; $i < $col; ++$i) {
21616 $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']);
21617 }
21618 if ($this->rtl) {
21619 $x = $this->w - $this->original_rMargin - $colpos;
21620 $this->rMargin = ($this->w - $x + $listindent);
21621 $this->lMargin = ($x - $this->columns[$col]['w']);
21622 $this->x = $x - $listindent;
21623 } else {
21624 $x = $this->original_lMargin + $colpos;
21625 $this->lMargin = ($x + $listindent);
21626 $this->rMargin = ($this->w - $x - $this->columns[$col]['w']);
21627 $this->x = $x + $listindent;
21628 }
21629 $this->columns[$col]['x'] = $x;
21630 }
21631 $this->current_column = $col;
21632 // fix for HTML mode
21633 $this->newline = true;
21634 // print HTML table header (if any)
21635 if ((!TCPDF_STATIC::empty_string($this->thead)) AND (!$this->inthead)) {
21636 if ($enable_thead) {
21637 // print table header
21638 $this->writeHTML($this->thead, false, false, false, false, '');
21639 $this->y += $xshift['s']['V'];
21640 // store end of header position
21641 if (!isset($this->columns[$col]['th'])) {
21642 $this->columns[$col]['th'] = array();
21643 }
21644 $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y;
21645 $this->lasth = 0;
21646 } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) {
21647 $this->y = $this->columns[$col]['th']['\''.$this->page.'\''];
21648 }
21649 }
21650 // account for an html table cell over multiple columns
21651 if ($this->rtl) {
21652 $this->rMargin += $xshift['x'];
21653 $this->x -= ($xshift['x'] + $xshift['p']['R']);
21654 } else {
21655 $this->lMargin += $xshift['x'];
21656 $this->x += $xshift['x'] + $xshift['p']['L'];
21657 }
21658 }
21659
21666 public function getColumn() {
21667 return $this->current_column;
21668 }
21669
21676 public function getNumberOfColumns() {
21677 return $this->num_columns;
21678 }
21679
21688 public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
21689 // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
21690 // convert text rendering parameters
21691 if ($stroke < 0) {
21692 $stroke = 0;
21693 }
21694 if ($fill === true) {
21695 if ($stroke > 0) {
21696 if ($clip === true) {
21697 // Fill, then stroke text and add to path for clipping
21698 $textrendermode = 6;
21699 } else {
21700 // Fill, then stroke text
21701 $textrendermode = 2;
21702 }
21703 $textstrokewidth = $stroke;
21704 } else {
21705 if ($clip === true) {
21706 // Fill text and add to path for clipping
21707 $textrendermode = 4;
21708 } else {
21709 // Fill text
21710 $textrendermode = 0;
21711 }
21712 }
21713 } else {
21714 if ($stroke > 0) {
21715 if ($clip === true) {
21716 // Stroke text and add to path for clipping
21717 $textrendermode = 5;
21718 } else {
21719 // Stroke text
21720 $textrendermode = 1;
21721 }
21722 $textstrokewidth = $stroke;
21723 } else {
21724 if ($clip === true) {
21725 // Add text to path for clipping
21726 $textrendermode = 7;
21727 } else {
21728 // Neither fill nor stroke text (invisible)
21729 $textrendermode = 3;
21730 }
21731 }
21732 }
21733 $this->textrendermode = $textrendermode;
21734 $this->textstrokewidth = $stroke;
21735 }
21736
21743 public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) {
21744 if (isset($params['enabled'])) {
21745 $this->txtshadow['enabled'] = $params['enabled']?true:false;
21746 } else {
21747 $this->txtshadow['enabled'] = false;
21748 }
21749 if (isset($params['depth_w'])) {
21750 $this->txtshadow['depth_w'] = floatval($params['depth_w']);
21751 } else {
21752 $this->txtshadow['depth_w'] = 0;
21753 }
21754 if (isset($params['depth_h'])) {
21755 $this->txtshadow['depth_h'] = floatval($params['depth_h']);
21756 } else {
21757 $this->txtshadow['depth_h'] = 0;
21758 }
21759 if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) {
21760 $this->txtshadow['color'] = $params['color'];
21761 } else {
21762 $this->txtshadow['color'] = $this->strokecolor;
21763 }
21764 if (isset($params['opacity'])) {
21765 $this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity'])));
21766 } else {
21767 $this->txtshadow['opacity'] = 1;
21768 }
21769 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'))) {
21770 $this->txtshadow['blend_mode'] = $params['blend_mode'];
21771 } else {
21772 $this->txtshadow['blend_mode'] = 'Normal';
21773 }
21774 if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) {
21775 $this->txtshadow['enabled'] = false;
21776 }
21777 }
21778
21785 public function getTextShadow() {
21786 return $this->txtshadow;
21787 }
21788
21803 protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
21804 $hyphenword = array(); // hyphens positions
21805 $numchars = count($word);
21806 if ($numchars <= $charmin) {
21807 return $word;
21808 }
21809 $word_string = TCPDF_FONTS::UTF8ArrSubString($word, '', '', $this->isunicode);
21810 // some words will be returned as-is
21811 $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})(\]?)$/';
21812 if (preg_match($pattern, $word_string) > 0) {
21813 // email
21814 return $word;
21815 }
21816 $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})(\]?)$/';
21817 if (preg_match($pattern, $word_string) > 0) {
21818 // URL
21819 return $word;
21820 }
21821 if (isset($dictionary[$word_string])) {
21822 return TCPDF_FONTS::UTF8StringToArray($dictionary[$word_string], $this->isunicode, $this->CurrentFont);
21823 }
21824 // suround word with '_' characters
21825 $tmpword = array_merge(array(95), $word, array(95));
21826 $tmpnumchars = $numchars + 2;
21827 $maxpos = $tmpnumchars - $charmin;
21828 for ($pos = 0; $pos < $maxpos; ++$pos) {
21829 $imax = min(($tmpnumchars - $pos), $charmax);
21830 for ($i = $charmin; $i <= $imax; ++$i) {
21831 $subword = strtolower(TCPDF_FONTS::UTF8ArrSubString($tmpword, $pos, ($pos + $i), $this->isunicode));
21832 if (isset($patterns[$subword])) {
21833 $pattern = TCPDF_FONTS::UTF8StringToArray($patterns[$subword], $this->isunicode, $this->CurrentFont);
21834 $pattern_length = count($pattern);
21835 $digits = 1;
21836 for ($j = 0; $j < $pattern_length; ++$j) {
21837 // check if $pattern[$j] is a number
21838 if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
21839 if ($j == 0) {
21840 $zero = $pos - 1;
21841 } else {
21842 $zero = $pos + $j - $digits;
21843 }
21844 if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] != $pattern[$j])) {
21845 $hyphenword[$zero] = TCPDF_FONTS::unichr($pattern[$j], $this->isunicode);
21846 }
21847 ++$digits;
21848 }
21849 }
21850 }
21851 }
21852 }
21853 $inserted = 0;
21854 $maxpos = $numchars - $rightmin;
21855 for ($i = $leftmin; $i <= $maxpos; ++$i) {
21856 if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
21857 // 173 = soft hyphen character
21858 array_splice($word, $i + $inserted, 0, 173);
21859 ++$inserted;
21860 }
21861 }
21862 return $word;
21863 }
21864
21879 public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
21880 $text = $this->unhtmlentities($text);
21881 $word = array(); // last word
21882 $txtarr = array(); // text to be returned
21883 $intag = false; // true if we are inside an HTML tag
21884 if (!is_array($patterns)) {
21885 $patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patterns);
21886 }
21887 // get array of characters
21888 $unichars = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
21889 // for each char
21890 foreach ($unichars as $char) {
21891 if ((!$intag) AND TCPDF_FONT_DATA::$uni_type[$char] == 'L') {
21892 // letter character
21893 $word[] = $char;
21894 } else {
21895 // other type of character
21896 if (!TCPDF_STATIC::empty_string($word)) {
21897 // hypenate the word
21898 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
21899 $word = array();
21900 }
21901 $txtarr[] = $char;
21902 if (chr($char) == '<') {
21903 // we are inside an HTML tag
21904 $intag = true;
21905 } elseif ($intag AND (chr($char) == '>')) {
21906 // end of HTML tag
21907 $intag = false;
21908 }
21909 }
21910 }
21911 if (!TCPDF_STATIC::empty_string($word)) {
21912 // hypenate the word
21913 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
21914 }
21915 // convert char array to string and return
21916 return TCPDF_FONTS::UTF8ArrSubString($txtarr, '', '', $this->isunicode);
21917 }
21918
21925 public function setRasterizeVectorImages($mode) {
21926 $this->rasterize_vector_images = $mode;
21927 }
21928
21936 public function setFontSubsetting($enable=true) {
21937 if ($this->pdfa_mode) {
21938 $this->font_subsetting = false;
21939 } else {
21940 $this->font_subsetting = $enable ? true : false;
21941 }
21942 }
21943
21951 public function getFontSubsetting() {
21952 return $this->font_subsetting;
21953 }
21954
21964 public function stringLeftTrim($str, $replace='') {
21965 return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str);
21966 }
21967
21977 public function stringRightTrim($str, $replace='') {
21978 return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str);
21979 }
21980
21990 public function stringTrim($str, $replace='') {
21991 $str = $this->stringLeftTrim($str, $replace);
21992 $str = $this->stringRightTrim($str, $replace);
21993 return $str;
21994 }
21995
22003 public function isUnicodeFont() {
22004 return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0'));
22005 }
22006
22015 public function getFontFamilyName($fontfamily) {
22016 // remove spaces and symbols
22017 $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily));
22018 // extract all font names
22019 $fontslist = preg_split('/[,]/', $fontfamily);
22020 // find first valid font name
22021 foreach ($fontslist as $font) {
22022 // replace font variations
22023 $font = preg_replace('/italic$/', 'I', $font);
22024 $font = preg_replace('/oblique$/', 'I', $font);
22025 $font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
22026 // replace common family names and core fonts
22027 $pattern = array();
22028 $replacement = array();
22029 $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
22030 $replacement[] = 'times';
22031 $pattern[] = '/^sansserif/';
22032 $replacement[] = 'helvetica';
22033 $pattern[] = '/^monospace/';
22034 $replacement[] = 'courier';
22035 $font = preg_replace($pattern, $replacement, $font);
22036 if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) {
22037 return $font;
22038 }
22039 }
22040 // return current font as default
22041 return $this->CurrentFont['fontkey'];
22042 }
22043
22058 public function startTemplate($w=0, $h=0, $group=false) {
22059 if ($this->inxobj) {
22060 // we are already inside an XObject template
22061 return false;
22062 }
22063 $this->inxobj = true;
22064 ++$this->n;
22065 // XObject ID
22066 $this->xobjid = 'XT'.$this->n;
22067 // object ID
22068 $this->xobjects[$this->xobjid] = array('n' => $this->n);
22069 // store current graphic state
22070 $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars();
22071 // initialize data
22072 $this->xobjects[$this->xobjid]['intmrk'] = 0;
22073 $this->xobjects[$this->xobjid]['transfmrk'] = array();
22074 $this->xobjects[$this->xobjid]['outdata'] = '';
22075 $this->xobjects[$this->xobjid]['xobjects'] = array();
22076 $this->xobjects[$this->xobjid]['images'] = array();
22077 $this->xobjects[$this->xobjid]['fonts'] = array();
22078 $this->xobjects[$this->xobjid]['annotations'] = array();
22079 $this->xobjects[$this->xobjid]['extgstates'] = array();
22080 $this->xobjects[$this->xobjid]['gradients'] = array();
22081 $this->xobjects[$this->xobjid]['spot_colors'] = array();
22082 // set new environment
22083 $this->num_columns = 1;
22084 $this->current_column = 0;
22085 $this->SetAutoPageBreak(false);
22086 if (($w === '') OR ($w <= 0)) {
22087 $w = $this->w - $this->lMargin - $this->rMargin;
22088 }
22089 if (($h === '') OR ($h <= 0)) {
22090 $h = $this->h - $this->tMargin - $this->bMargin;
22091 }
22092 $this->xobjects[$this->xobjid]['x'] = 0;
22093 $this->xobjects[$this->xobjid]['y'] = 0;
22094 $this->xobjects[$this->xobjid]['w'] = $w;
22095 $this->xobjects[$this->xobjid]['h'] = $h;
22096 $this->w = $w;
22097 $this->h = $h;
22098 $this->wPt = $this->w * $this->k;
22099 $this->hPt = $this->h * $this->k;
22100 $this->fwPt = $this->wPt;
22101 $this->fhPt = $this->hPt;
22102 $this->x = 0;
22103 $this->y = 0;
22104 $this->lMargin = 0;
22105 $this->rMargin = 0;
22106 $this->tMargin = 0;
22107 $this->bMargin = 0;
22108 // set group mode
22109 $this->xobjects[$this->xobjid]['group'] = $group;
22110 return $this->xobjid;
22111 }
22112
22123 public function endTemplate() {
22124 if (!$this->inxobj) {
22125 // we are not inside a template
22126 return false;
22127 }
22128 $this->inxobj = false;
22129 // restore previous graphic state
22130 $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true);
22131 return $this->xobjid;
22132 }
22133
22152 public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
22153 if ($this->state != 2) {
22154 return;
22155 }
22156 if (!isset($this->xobjects[$id])) {
22157 $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
22158 }
22159 if ($this->inxobj) {
22160 if ($id == $this->xobjid) {
22161 // close current template
22162 $this->endTemplate();
22163 } else {
22164 // use the template as resource for the template currently opened
22165 $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id];
22166 }
22167 }
22168 // set default values
22169 if ($x === '') {
22170 $x = $this->x;
22171 }
22172 if ($y === '') {
22173 $y = $this->y;
22174 }
22175 // check page for no-write regions and adapt page margins if necessary
22176 list($x, $y) = $this->checkPageRegions($h, $x, $y);
22177 $ow = $this->xobjects[$id]['w'];
22178 $oh = $this->xobjects[$id]['h'];
22179 // calculate template width and height on document
22180 if (($w <= 0) AND ($h <= 0)) {
22181 $w = $ow;
22182 $h = $oh;
22183 } elseif ($w <= 0) {
22184 $w = $h * $ow / $oh;
22185 } elseif ($h <= 0) {
22186 $h = $w * $oh / $ow;
22187 }
22188 // fit the template on available space
22189 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22190 // set page alignment
22191 $rb_y = $y + $h;
22192 // set alignment
22193 if ($this->rtl) {
22194 if ($palign == 'L') {
22195 $xt = $this->lMargin;
22196 } elseif ($palign == 'C') {
22197 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22198 } elseif ($palign == 'R') {
22199 $xt = $this->w - $this->rMargin - $w;
22200 } else {
22201 $xt = $x - $w;
22202 }
22203 $rb_x = $xt;
22204 } else {
22205 if ($palign == 'L') {
22206 $xt = $this->lMargin;
22207 } elseif ($palign == 'C') {
22208 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22209 } elseif ($palign == 'R') {
22210 $xt = $this->w - $this->rMargin - $w;
22211 } else {
22212 $xt = $x;
22213 }
22214 $rb_x = $xt + $w;
22215 }
22216 // print XObject Template + Transformation matrix
22217 $this->StartTransform();
22218 // translate and scale
22219 $sx = ($w / $this->xobjects[$id]['w']);
22220 $sy = ($h / $this->xobjects[$id]['h']);
22221 $tm = array();
22222 $tm[0] = $sx;
22223 $tm[1] = 0;
22224 $tm[2] = 0;
22225 $tm[3] = $sy;
22226 $tm[4] = $xt * $this->k;
22227 $tm[5] = ($this->h - $h - $y) * $this->k;
22228 $this->Transform($tm);
22229 // set object
22230 $this->_out('/'.$id.' Do');
22231 $this->StopTransform();
22232 // add annotations
22233 if (!empty($this->xobjects[$id]['annotations'])) {
22234 foreach ($this->xobjects[$id]['annotations'] as $annot) {
22235 // transform original coordinates
22236 $coordlt = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k)));
22237 $ax = ($coordlt[4] / $this->k);
22238 $ay = ($this->h - $h - ($coordlt[5] / $this->k));
22239 $coordrb = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k)));
22240 $aw = ($coordrb[4] / $this->k) - $ax;
22241 $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay;
22242 $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
22243 }
22244 }
22245 // set pointer to align the next text/objects
22246 switch($align) {
22247 case 'T': {
22248 $this->y = $y;
22249 $this->x = $rb_x;
22250 break;
22251 }
22252 case 'M': {
22253 $this->y = $y + round($h/2);
22254 $this->x = $rb_x;
22255 break;
22256 }
22257 case 'B': {
22258 $this->y = $rb_y;
22259 $this->x = $rb_x;
22260 break;
22261 }
22262 case 'N': {
22263 $this->SetY($rb_y);
22264 break;
22265 }
22266 default:{
22267 break;
22268 }
22269 }
22270 }
22271
22279 public function setFontStretching($perc=100) {
22280 $this->font_stretching = $perc;
22281 }
22282
22290 public function getFontStretching() {
22291 return $this->font_stretching;
22292 }
22293
22301 public function setFontSpacing($spacing=0) {
22302 $this->font_spacing = $spacing;
22303 }
22304
22312 public function getFontSpacing() {
22313 return $this->font_spacing;
22314 }
22315
22324 public function getPageRegions() {
22325 return $this->page_regions;
22326 }
22327
22339 public function setPageRegions($regions=array()) {
22340 // empty current regions array
22341 $this->page_regions = array();
22342 // add regions
22343 foreach ($regions as $data) {
22344 $this->addPageRegion($data);
22345 }
22346 }
22347
22359 public function addPageRegion($region) {
22360 if (!isset($region['page']) OR empty($region['page'])) {
22361 $region['page'] = $this->page;
22362 }
22363 if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
22364 AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
22365 AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
22366 $this->page_regions[] = $region;
22367 }
22368 }
22369
22378 public function removePageRegion($key) {
22379 if (isset($this->page_regions[$key])) {
22380 unset($this->page_regions[$key]);
22381 }
22382 }
22383
22396 protected function checkPageRegions($h, $x, $y) {
22397 // set default values
22398 if ($x === '') {
22399 $x = $this->x;
22400 }
22401 if ($y === '') {
22402 $y = $this->y;
22403 }
22404 if (!$this->check_page_regions OR empty($this->page_regions)) {
22405 // no page regions defined
22406 return array($x, $y);
22407 }
22408 if (empty($h)) {
22409 $h = ($this->FontSize * $this->cell_height_ratio) + $this->cell_padding['T'] + $this->cell_padding['B'];
22410 }
22411 // check for page break
22412 if ($this->checkPageBreak($h, $y)) {
22413 // the content will be printed on a new page
22414 $x = $this->x;
22415 $y = $this->y;
22416 }
22417 if ($this->num_columns > 1) {
22418 if ($this->rtl) {
22419 $this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22420 } else {
22421 $this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22422 }
22423 } else {
22424 if ($this->rtl) {
22425 $this->lMargin = max($this->clMargin, $this->original_lMargin);
22426 } else {
22427 $this->rMargin = max($this->crMargin, $this->original_rMargin);
22428 }
22429 }
22430 // adjust coordinates and page margins
22431 foreach ($this->page_regions as $regid => $regdata) {
22432 if ($regdata['page'] == $this->page) {
22433 // check region boundaries
22434 if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
22435 // Y is inside the region
22436 $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
22437 $yt = max($y, $regdata['yt']);
22438 $yb = min(($yt + $h), $regdata['yb']);
22439 $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt'];
22440 $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt'];
22441 if ($regdata['side'] == 'L') { // left side
22442 $new_margin = max($xt, $xb);
22443 if ($this->lMargin < $new_margin) {
22444 if ($this->rtl) {
22445 // adjust left page margin
22446 $this->lMargin = max(0, $new_margin);
22447 }
22448 if ($x < $new_margin) {
22449 // adjust x position
22450 $x = $new_margin;
22451 if ($new_margin > ($this->w - $this->rMargin)) {
22452 // adjust y position
22453 $y = $regdata['yb'] - $h;
22454 }
22455 }
22456 }
22457 } elseif ($regdata['side'] == 'R') { // right side
22458 $new_margin = min($xt, $xb);
22459 if (($this->w - $this->rMargin) > $new_margin) {
22460 if (!$this->rtl) {
22461 // adjust right page margin
22462 $this->rMargin = max(0, ($this->w - $new_margin));
22463 }
22464 if ($x > $new_margin) {
22465 // adjust x position
22466 $x = $new_margin;
22467 if ($new_margin > $this->lMargin) {
22468 // adjust y position
22469 $y = $regdata['yb'] - $h;
22470 }
22471 }
22472 }
22473 }
22474 }
22475 }
22476 }
22477 return array($x, $y);
22478 }
22479
22480 // --- SVG METHODS ---------------------------------------------------------
22481
22499 public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
22500 if ($this->state != 2) {
22501 return;
22502 }
22503 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
22504 // convert SVG to raster image using GD or ImageMagick libraries
22505 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22506 }
22507 if ($file{0} === '@') { // image from string
22508 $this->svgdir = '';
22509 $svgdata = substr($file, 1);
22510 } else { // SVG file
22511 $this->svgdir = dirname($file);
22512 $svgdata = file_get_contents($file);
22513 }
22514 if ($svgdata === false) {
22515 $this->Error('SVG file not found: '.$file);
22516 }
22517 if ($x === '') {
22518 $x = $this->x;
22519 }
22520 if ($y === '') {
22521 $y = $this->y;
22522 }
22523 // check page for no-write regions and adapt page margins if necessary
22524 list($x, $y) = $this->checkPageRegions($h, $x, $y);
22525 $k = $this->k;
22526 $ox = 0;
22527 $oy = 0;
22528 $ow = $w;
22529 $oh = $h;
22530 $aspect_ratio_align = 'xMidYMid';
22531 $aspect_ratio_ms = 'meet';
22532 $regs = array();
22533 // get original image width and height
22534 preg_match('/<svg([^>]*)>/si', $svgdata, $regs);
22535 if (isset($regs[1]) AND !empty($regs[1])) {
22536 $tmp = array();
22537 if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22538 $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22539 }
22540 $tmp = array();
22541 if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22542 $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22543 }
22544 $tmp = array();
22545 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22546 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22547 }
22548 $tmp = array();
22549 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22550 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22551 }
22552 $tmp = array();
22553 $view_box = array();
22554 if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
22555 if (count($tmp) == 5) {
22556 array_shift($tmp);
22557 foreach ($tmp as $key => $val) {
22558 $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
22559 }
22560 $ox = $view_box[0];
22561 $oy = $view_box[1];
22562 }
22563 // get aspect ratio
22564 $tmp = array();
22565 if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22566 $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
22567 switch (count($aspect_ratio)) {
22568 case 3: {
22569 $aspect_ratio_align = $aspect_ratio[1];
22570 $aspect_ratio_ms = $aspect_ratio[2];
22571 break;
22572 }
22573 case 2: {
22574 $aspect_ratio_align = $aspect_ratio[0];
22575 $aspect_ratio_ms = $aspect_ratio[1];
22576 break;
22577 }
22578 case 1: {
22579 $aspect_ratio_align = $aspect_ratio[0];
22580 $aspect_ratio_ms = 'meet';
22581 break;
22582 }
22583 }
22584 }
22585 }
22586 }
22587 // calculate image width and height on document
22588 if (($w <= 0) AND ($h <= 0)) {
22589 // convert image size to document unit
22590 $w = $ow;
22591 $h = $oh;
22592 } elseif ($w <= 0) {
22593 $w = $h * $ow / $oh;
22594 } elseif ($h <= 0) {
22595 $h = $w * $oh / $ow;
22596 }
22597 // fit the image on available space
22598 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22599 if ($this->rasterize_vector_images) {
22600 // convert SVG to raster image using GD or ImageMagick libraries
22601 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22602 }
22603 // set alignment
22604 $this->img_rb_y = $y + $h;
22605 // set alignment
22606 if ($this->rtl) {
22607 if ($palign == 'L') {
22608 $ximg = $this->lMargin;
22609 } elseif ($palign == 'C') {
22610 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22611 } elseif ($palign == 'R') {
22612 $ximg = $this->w - $this->rMargin - $w;
22613 } else {
22614 $ximg = $x - $w;
22615 }
22616 $this->img_rb_x = $ximg;
22617 } else {
22618 if ($palign == 'L') {
22619 $ximg = $this->lMargin;
22620 } elseif ($palign == 'C') {
22621 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22622 } elseif ($palign == 'R') {
22623 $ximg = $this->w - $this->rMargin - $w;
22624 } else {
22625 $ximg = $x;
22626 }
22627 $this->img_rb_x = $ximg + $w;
22628 }
22629 // store current graphic vars
22630 $gvars = $this->getGraphicVars();
22631 // store SVG position and scale factors
22632 $svgoffset_x = ($ximg - $ox) * $this->k;
22633 $svgoffset_y = -($y - $oy) * $this->k;
22634 if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
22635 $ow = $view_box[2];
22636 $oh = $view_box[3];
22637 } else {
22638 if ($ow <= 0) {
22639 $ow = $w;
22640 }
22641 if ($oh <= 0) {
22642 $oh = $h;
22643 }
22644 }
22645 $svgscale_x = $w / $ow;
22646 $svgscale_y = $h / $oh;
22647 // scaling and alignment
22648 if ($aspect_ratio_align != 'none') {
22649 // store current scaling values
22650 $svgscale_old_x = $svgscale_x;
22651 $svgscale_old_y = $svgscale_y;
22652 // force uniform scaling
22653 if ($aspect_ratio_ms == 'slice') {
22654 // the entire viewport is covered by the viewBox
22655 if ($svgscale_x > $svgscale_y) {
22656 $svgscale_y = $svgscale_x;
22657 } elseif ($svgscale_x < $svgscale_y) {
22658 $svgscale_x = $svgscale_y;
22659 }
22660 } else { // meet
22661 // the entire viewBox is visible within the viewport
22662 if ($svgscale_x < $svgscale_y) {
22663 $svgscale_y = $svgscale_x;
22664 } elseif ($svgscale_x > $svgscale_y) {
22665 $svgscale_x = $svgscale_y;
22666 }
22667 }
22668 // correct X alignment
22669 switch (substr($aspect_ratio_align, 1, 3)) {
22670 case 'Min': {
22671 // do nothing
22672 break;
22673 }
22674 case 'Max': {
22675 $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
22676 break;
22677 }
22678 default:
22679 case 'Mid': {
22680 $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
22681 break;
22682 }
22683 }
22684 // correct Y alignment
22685 switch (substr($aspect_ratio_align, 5)) {
22686 case 'Min': {
22687 // do nothing
22688 break;
22689 }
22690 case 'Max': {
22691 $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
22692 break;
22693 }
22694 default:
22695 case 'Mid': {
22696 $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
22697 break;
22698 }
22699 }
22700 }
22701 // store current page break mode
22702 $page_break_mode = $this->AutoPageBreak;
22703 $page_break_margin = $this->getBreakMargin();
22704 $cell_padding = $this->cell_padding;
22705 $this->SetCellPadding(0);
22706 $this->SetAutoPageBreak(false);
22707 // save the current graphic state
22708 $this->_out('q'.$this->epsmarker);
22709 // set initial clipping mask
22710 $this->Rect($x, $y, $w, $h, 'CNZ', array(), array());
22711 // scale and translate
22712 $e = $ox * $this->k * (1 - $svgscale_x);
22713 $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
22714 $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y)));
22715 // creates a new XML parser to be used by the other XML functions
22716 $this->parser = xml_parser_create('UTF-8');
22717 // the following function allows to use parser inside object
22718 xml_set_object($this->parser, $this);
22719 // disable case-folding for this XML parser
22720 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
22721 // sets the element handler functions for the XML parser
22722 xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler');
22723 // sets the character data handler function for the XML parser
22724 xml_set_character_data_handler($this->parser, 'segSVGContentHandler');
22725 // start parsing an XML document
22726 if (!xml_parse($this->parser, $svgdata)) {
22727 $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));
22728 $this->Error($error_message);
22729 }
22730 // free this XML parser
22731 xml_parser_free($this->parser);
22732 // restore previous graphic state
22733 $this->_out($this->epsmarker.'Q');
22734 // restore graphic vars
22735 $this->setGraphicVars($gvars);
22736 $this->lasth = $gvars['lasth'];
22737 if (!empty($border)) {
22738 $bx = $this->x;
22739 $by = $this->y;
22740 $this->x = $ximg;
22741 if ($this->rtl) {
22742 $this->x += $w;
22743 }
22744 $this->y = $y;
22745 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
22746 $this->x = $bx;
22747 $this->y = $by;
22748 }
22749 if ($link) {
22750 $this->Link($ximg, $y, $w, $h, $link, 0);
22751 }
22752 // set pointer to align the next text/objects
22753 switch($align) {
22754 case 'T':{
22755 $this->y = $y;
22756 $this->x = $this->img_rb_x;
22757 break;
22758 }
22759 case 'M':{
22760 $this->y = $y + round($h/2);
22761 $this->x = $this->img_rb_x;
22762 break;
22763 }
22764 case 'B':{
22765 $this->y = $this->img_rb_y;
22766 $this->x = $this->img_rb_x;
22767 break;
22768 }
22769 case 'N':{
22770 $this->SetY($this->img_rb_y);
22771 break;
22772 }
22773 default:{
22774 // restore pointer to starting position
22775 $this->x = $gvars['x'];
22776 $this->y = $gvars['y'];
22777 $this->page = $gvars['page'];
22778 $this->current_column = $gvars['current_column'];
22779 $this->tMargin = $gvars['tMargin'];
22780 $this->bMargin = $gvars['bMargin'];
22781 $this->w = $gvars['w'];
22782 $this->h = $gvars['h'];
22783 $this->wPt = $gvars['wPt'];
22784 $this->hPt = $gvars['hPt'];
22785 $this->fwPt = $gvars['fwPt'];
22786 $this->fhPt = $gvars['fhPt'];
22787 break;
22788 }
22789 }
22790 $this->endlinex = $this->img_rb_x;
22791 // restore page break
22792 $this->SetAutoPageBreak($page_break_mode, $page_break_margin);
22793 $this->cell_padding = $cell_padding;
22794 }
22795
22803 protected function convertSVGtMatrix($tm) {
22804 $a = $tm[0];
22805 $b = -$tm[1];
22806 $c = -$tm[2];
22807 $d = $tm[3];
22808 $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
22809 $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
22810 $x = 0;
22811 $y = $this->h * $this->k;
22812 $e = ($x * (1 - $a)) - ($y * $c) + $e;
22813 $f = ($y * (1 - $d)) - ($x * $b) + $f;
22814 return array($a, $b, $c, $d, $e, $f);
22815 }
22816
22823 protected function SVGTransform($tm) {
22824 $this->Transform($this->convertSVGtMatrix($tm));
22825 }
22826
22842 protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
22843 if ($this->state != 2) {
22844 return;
22845 }
22846 $objstyle = '';
22847 $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
22848 if (!isset($svgstyle['opacity'])) {
22849 return $objstyle;
22850 }
22851 // clip-path
22852 $regs = array();
22853 if (preg_match('/url\‍([\s]*\#([^\‍)]*)\‍)/si', $svgstyle['clip-path'], $regs)) {
22854 $clip_path = $this->svgclippaths[$regs[1]];
22855 foreach ($clip_path as $cp) {
22856 $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
22857 }
22858 }
22859 // opacity
22860 if ($svgstyle['opacity'] != 1) {
22861 $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false);
22862 }
22863 // color
22864 $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors);
22865 $this->SetFillColorArray($fill_color);
22866 // text color
22867 $text_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors);
22868 $this->SetTextColorArray($text_color);
22869 // clip
22870 if (preg_match('/rect\‍(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\‍)/si', $svgstyle['clip'], $regs)) {
22871 $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
22872 $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
22873 $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
22874 $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
22875 $cx = $x + $left;
22876 $cy = $y + $top;
22877 $cw = $w - $left - $right;
22878 $ch = $h - $top - $bottom;
22879 if ($svgstyle['clip-rule'] == 'evenodd') {
22880 $clip_rule = 'CNZ';
22881 } else {
22882 $clip_rule = 'CEO';
22883 }
22884 $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
22885 }
22886 // fill
22887 $regs = array();
22888 if (preg_match('/url\‍([\s]*\#([^\‍)]*)\‍)/si', $svgstyle['fill'], $regs)) {
22889 // gradient
22890 $gradient = $this->svggradients[$regs[1]];
22891 if (isset($gradient['xref'])) {
22892 // reference to another gradient definition
22893 $newgradient = $this->svggradients[$gradient['xref']];
22894 $newgradient['coords'] = $gradient['coords'];
22895 $newgradient['mode'] = $gradient['mode'];
22896 $newgradient['gradientUnits'] = $gradient['gradientUnits'];
22897 if (isset($gradient['gradientTransform'])) {
22898 $newgradient['gradientTransform'] = $gradient['gradientTransform'];
22899 }
22900 $gradient = $newgradient;
22901 }
22902 //save current Graphic State
22903 $this->_out('q');
22904 //set clipping area
22905 if (!empty($clip_function) AND method_exists($this, $clip_function)) {
22906 $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
22907 if (is_array($bbox) AND (count($bbox) == 4)) {
22908 list($x, $y, $w, $h) = $bbox;
22909 }
22910 }
22911 if ($gradient['mode'] == 'measure') {
22912 if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
22913 $gtm = $gradient['gradientTransform'];
22914 // apply transformation matrix
22915 $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
22916 $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
22917 $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
22918 $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
22919 if (isset($gradient['coords'][4])) {
22920 $gradient['coords'][4] = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
22921 }
22922 $gradient['coords'][0] = $xa;
22923 $gradient['coords'][1] = $ya;
22924 $gradient['coords'][2] = $xb;
22925 $gradient['coords'][3] = $yb;
22926 }
22927 // convert SVG coordinates to user units
22928 $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
22929 $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
22930 $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
22931 $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
22932 if (isset($gradient['coords'][4])) {
22933 $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
22934 }
22935 if ($w <= $minlen) {
22936 $w = $minlen;
22937 }
22938 if ($h <= $minlen) {
22939 $h = $minlen;
22940 }
22941 // shift units
22942 if ($gradient['gradientUnits'] == 'objectBoundingBox') {
22943 // convert to SVG coordinate system
22944 $gradient['coords'][0] += $x;
22945 $gradient['coords'][1] += $y;
22946 $gradient['coords'][2] += $x;
22947 $gradient['coords'][3] += $y;
22948 }
22949 // calculate percentages
22950 $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w);
22951 $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h);
22952 $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w);
22953 $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h);
22954 if (isset($gradient['coords'][4])) {
22955 $gradient['coords'][4] /= $w;
22956 }
22957 } elseif ($gradient['mode'] == 'percentage') {
22958 foreach($gradient['coords'] as $key => $val) {
22959 $gradient['coords'][$key] = (intval($val) / 100);
22960 if ($val < 0) {
22961 $gradient['coords'][$key] = 0;
22962 } elseif ($val > 1) {
22963 $gradient['coords'][$key] = 1;
22964 }
22965 }
22966 }
22967 if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
22968 // single color (no shading)
22969 $gradient['coords'][0] = 1;
22970 $gradient['coords'][1] = 0;
22971 $gradient['coords'][2] = 0.999;
22972 $gradient['coords'][3] = 0;
22973 }
22974 // swap Y coordinates
22975 $tmp = $gradient['coords'][1];
22976 $gradient['coords'][1] = $gradient['coords'][3];
22977 $gradient['coords'][3] = $tmp;
22978 // set transformation map for gradient
22979 if ($gradient['type'] == 3) {
22980 // circular gradient
22981 $cy = $this->h - $y - ($gradient['coords'][1] * ($w + $h));
22982 $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($w * $this->k), ($x * $this->k), ($cy * $this->k)));
22983 } else {
22984 $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), (($this->h - ($y + $h)) * $this->k)));
22985 }
22986 if (count($gradient['stops']) > 1) {
22987 $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
22988 }
22989 } elseif ($svgstyle['fill'] != 'none') {
22990 $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors);
22991 if ($svgstyle['fill-opacity'] != 1) {
22992 $this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false);
22993 }
22994 $this->SetFillColorArray($fill_color);
22995 if ($svgstyle['fill-rule'] == 'evenodd') {
22996 $objstyle .= 'F*';
22997 } else {
22998 $objstyle .= 'F';
22999 }
23000 }
23001 // stroke
23002 if ($svgstyle['stroke'] != 'none') {
23003 if ($svgstyle['stroke-opacity'] != 1) {
23004 $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false);
23005 }
23006 $stroke_style = array(
23007 'color' => TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors),
23008 'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
23009 'cap' => $svgstyle['stroke-linecap'],
23010 'join' => $svgstyle['stroke-linejoin']
23011 );
23012 if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
23013 $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
23014 }
23015 $this->SetLineStyle($stroke_style);
23016 $objstyle .= 'D';
23017 }
23018 // font
23019 $regs = array();
23020 if (!empty($svgstyle['font'])) {
23021 if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
23022 $font_family = $this->getFontFamilyName($regs[1]);
23023 } else {
23024 $font_family = $svgstyle['font-family'];
23025 }
23026 if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23027 $font_size = trim($regs[1]);
23028 } else {
23029 $font_size = $svgstyle['font-size'];
23030 }
23031 if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23032 $font_style = trim($regs[1]);
23033 } else {
23034 $font_style = $svgstyle['font-style'];
23035 }
23036 if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23037 $font_weight = trim($regs[1]);
23038 } else {
23039 $font_weight = $svgstyle['font-weight'];
23040 }
23041 if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23042 $font_stretch = trim($regs[1]);
23043 } else {
23044 $font_stretch = $svgstyle['font-stretch'];
23045 }
23046 if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23047 $font_spacing = trim($regs[1]);
23048 } else {
23049 $font_spacing = $svgstyle['letter-spacing'];
23050 }
23051 } else {
23052 $font_family = $this->getFontFamilyName($svgstyle['font-family']);
23053 $font_size = $svgstyle['font-size'];
23054 $font_style = $svgstyle['font-style'];
23055 $font_weight = $svgstyle['font-weight'];
23056 $font_stretch = $svgstyle['font-stretch'];
23057 $font_spacing = $svgstyle['letter-spacing'];
23058 }
23059 $font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit);
23060 $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
23061 $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
23062 switch ($font_style) {
23063 case 'italic': {
23064 $font_style = 'I';
23065 break;
23066 }
23067 case 'oblique': {
23068 $font_style = 'I';
23069 break;
23070 }
23071 default:
23072 case 'normal': {
23073 $font_style = '';
23074 break;
23075 }
23076 }
23077 switch ($font_weight) {
23078 case 'bold':
23079 case 'bolder': {
23080 $font_style .= 'B';
23081 break;
23082 }
23083 }
23084 switch ($svgstyle['text-decoration']) {
23085 case 'underline': {
23086 $font_style .= 'U';
23087 break;
23088 }
23089 case 'overline': {
23090 $font_style .= 'O';
23091 break;
23092 }
23093 case 'line-through': {
23094 $font_style .= 'D';
23095 break;
23096 }
23097 default:
23098 case 'none': {
23099 break;
23100 }
23101 }
23102 $this->SetFont($font_family, $font_style, $font_size);
23103 $this->setFontStretching($font_stretch);
23104 $this->setFontSpacing($font_spacing);
23105 return $objstyle;
23106 }
23107
23126 protected function SVGPath($d, $style='') {
23127 if ($this->state != 2) {
23128 return;
23129 }
23130 // set fill/stroke style
23131 $op = TCPDF_STATIC::getPathPaintOperator($style, '');
23132 if (empty($op)) {
23133 return;
23134 }
23135 $paths = array();
23136 $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
23137 preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER);
23138 $x = 0;
23139 $y = 0;
23140 $x1 = 0;
23141 $y1 = 0;
23142 $x2 = 0;
23143 $y2 = 0;
23144 $xmin = 2147483647;
23145 $xmax = 0;
23146 $ymin = 2147483647;
23147 $ymax = 0;
23148 $relcoord = false;
23149 $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
23150 $firstcmd = true; // used to print first point
23151 // draw curve pieces
23152 foreach ($paths as $key => $val) {
23153 // get curve type
23154 $cmd = trim($val[1]);
23155 if (strtolower($cmd) == $cmd) {
23156 // use relative coordinated instead of absolute
23157 $relcoord = true;
23158 $xoffset = $x;
23159 $yoffset = $y;
23160 } else {
23161 $relcoord = false;
23162 $xoffset = 0;
23163 $yoffset = 0;
23164 }
23165 $params = array();
23166 if (isset($val[2])) {
23167 // get curve parameters
23168 $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
23169 $params = array();
23170 foreach ($rawparams as $ck => $cp) {
23171 $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
23172 if (abs($params[$ck]) < $minlen) {
23173 // aproximate little values to zero
23174 $params[$ck] = 0;
23175 }
23176 }
23177 }
23178 // store current origin point
23179 $x0 = $x;
23180 $y0 = $y;
23181 switch (strtoupper($cmd)) {
23182 case 'M': { // moveto
23183 foreach ($params as $ck => $cp) {
23184 if (($ck % 2) == 0) {
23185 $x = $cp + $xoffset;
23186 } else {
23187 $y = $cp + $yoffset;
23188 if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23189 if ($ck == 1) {
23190 $this->_outPoint($x, $y);
23191 $firstcmd = false;
23192 } else {
23193 $this->_outLine($x, $y);
23194 }
23195 $x0 = $x;
23196 $y0 = $y;
23197 }
23198 $xmin = min($xmin, $x);
23199 $ymin = min($ymin, $y);
23200 $xmax = max($xmax, $x);
23201 $ymax = max($ymax, $y);
23202 if ($relcoord) {
23203 $xoffset = $x;
23204 $yoffset = $y;
23205 }
23206 }
23207 }
23208 break;
23209 }
23210 case 'L': { // lineto
23211 foreach ($params as $ck => $cp) {
23212 if (($ck % 2) == 0) {
23213 $x = $cp + $xoffset;
23214 } else {
23215 $y = $cp + $yoffset;
23216 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23217 $this->_outLine($x, $y);
23218 $x0 = $x;
23219 $y0 = $y;
23220 }
23221 $xmin = min($xmin, $x);
23222 $ymin = min($ymin, $y);
23223 $xmax = max($xmax, $x);
23224 $ymax = max($ymax, $y);
23225 if ($relcoord) {
23226 $xoffset = $x;
23227 $yoffset = $y;
23228 }
23229 }
23230 }
23231 break;
23232 }
23233 case 'H': { // horizontal lineto
23234 foreach ($params as $ck => $cp) {
23235 $x = $cp + $xoffset;
23236 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23237 $this->_outLine($x, $y);
23238 $x0 = $x;
23239 $y0 = $y;
23240 }
23241 $xmin = min($xmin, $x);
23242 $xmax = max($xmax, $x);
23243 if ($relcoord) {
23244 $xoffset = $x;
23245 }
23246 }
23247 break;
23248 }
23249 case 'V': { // vertical lineto
23250 foreach ($params as $ck => $cp) {
23251 $y = $cp + $yoffset;
23252 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23253 $this->_outLine($x, $y);
23254 $x0 = $x;
23255 $y0 = $y;
23256 }
23257 $ymin = min($ymin, $y);
23258 $ymax = max($ymax, $y);
23259 if ($relcoord) {
23260 $yoffset = $y;
23261 }
23262 }
23263 break;
23264 }
23265 case 'C': { // curveto
23266 foreach ($params as $ck => $cp) {
23267 $params[$ck] = $cp;
23268 if ((($ck + 1) % 6) == 0) {
23269 $x1 = $params[($ck - 5)] + $xoffset;
23270 $y1 = $params[($ck - 4)] + $yoffset;
23271 $x2 = $params[($ck - 3)] + $xoffset;
23272 $y2 = $params[($ck - 2)] + $yoffset;
23273 $x = $params[($ck - 1)] + $xoffset;
23274 $y = $params[($ck)] + $yoffset;
23275 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23276 $xmin = min($xmin, $x, $x1, $x2);
23277 $ymin = min($ymin, $y, $y1, $y2);
23278 $xmax = max($xmax, $x, $x1, $x2);
23279 $ymax = max($ymax, $y, $y1, $y2);
23280 if ($relcoord) {
23281 $xoffset = $x;
23282 $yoffset = $y;
23283 }
23284 }
23285 }
23286 break;
23287 }
23288 case 'S': { // shorthand/smooth curveto
23289 foreach ($params as $ck => $cp) {
23290 $params[$ck] = $cp;
23291 if ((($ck + 1) % 4) == 0) {
23292 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
23293 $x1 = (2 * $x) - $x2;
23294 $y1 = (2 * $y) - $y2;
23295 } else {
23296 $x1 = $x;
23297 $y1 = $y;
23298 }
23299 $x2 = $params[($ck - 3)] + $xoffset;
23300 $y2 = $params[($ck - 2)] + $yoffset;
23301 $x = $params[($ck - 1)] + $xoffset;
23302 $y = $params[($ck)] + $yoffset;
23303 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23304 $xmin = min($xmin, $x, $x1, $x2);
23305 $ymin = min($ymin, $y, $y1, $y2);
23306 $xmax = max($xmax, $x, $x1, $x2);
23307 $ymax = max($ymax, $y, $y1, $y2);
23308 if ($relcoord) {
23309 $xoffset = $x;
23310 $yoffset = $y;
23311 }
23312 }
23313 }
23314 break;
23315 }
23316 case 'Q': { // quadratic B�zier curveto
23317 foreach ($params as $ck => $cp) {
23318 $params[$ck] = $cp;
23319 if ((($ck + 1) % 4) == 0) {
23320 // convert quadratic points to cubic points
23321 $x1 = $params[($ck - 3)] + $xoffset;
23322 $y1 = $params[($ck - 2)] + $yoffset;
23323 $xa = ($x + (2 * $x1)) / 3;
23324 $ya = ($y + (2 * $y1)) / 3;
23325 $x = $params[($ck - 1)] + $xoffset;
23326 $y = $params[($ck)] + $yoffset;
23327 $xb = ($x + (2 * $x1)) / 3;
23328 $yb = ($y + (2 * $y1)) / 3;
23329 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23330 $xmin = min($xmin, $x, $xa, $xb);
23331 $ymin = min($ymin, $y, $ya, $yb);
23332 $xmax = max($xmax, $x, $xa, $xb);
23333 $ymax = max($ymax, $y, $ya, $yb);
23334 if ($relcoord) {
23335 $xoffset = $x;
23336 $yoffset = $y;
23337 }
23338 }
23339 }
23340 break;
23341 }
23342 case 'T': { // shorthand/smooth quadratic B�zier curveto
23343 foreach ($params as $ck => $cp) {
23344 $params[$ck] = $cp;
23345 if (($ck % 2) != 0) {
23346 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
23347 $x1 = (2 * $x) - $x1;
23348 $y1 = (2 * $y) - $y1;
23349 } else {
23350 $x1 = $x;
23351 $y1 = $y;
23352 }
23353 // convert quadratic points to cubic points
23354 $xa = ($x + (2 * $x1)) / 3;
23355 $ya = ($y + (2 * $y1)) / 3;
23356 $x = $params[($ck - 1)] + $xoffset;
23357 $y = $params[($ck)] + $yoffset;
23358 $xb = ($x + (2 * $x1)) / 3;
23359 $yb = ($y + (2 * $y1)) / 3;
23360 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23361 $xmin = min($xmin, $x, $xa, $xb);
23362 $ymin = min($ymin, $y, $ya, $yb);
23363 $xmax = max($xmax, $x, $xa, $xb);
23364 $ymax = max($ymax, $y, $ya, $yb);
23365 if ($relcoord) {
23366 $xoffset = $x;
23367 $yoffset = $y;
23368 }
23369 }
23370 }
23371 break;
23372 }
23373 case 'A': { // elliptical arc
23374 foreach ($params as $ck => $cp) {
23375 $params[$ck] = $cp;
23376 if ((($ck + 1) % 7) == 0) {
23377 $x0 = $x;
23378 $y0 = $y;
23379 $rx = abs($params[($ck - 6)]);
23380 $ry = abs($params[($ck - 5)]);
23381 $ang = -$rawparams[($ck - 4)];
23382 $angle = deg2rad($ang);
23383 $fa = $rawparams[($ck - 3)]; // large-arc-flag
23384 $fs = $rawparams[($ck - 2)]; // sweep-flag
23385 $x = $params[($ck - 1)] + $xoffset;
23386 $y = $params[$ck] + $yoffset;
23387 if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
23388 // endpoints are almost identical
23389 $xmin = min($xmin, $x);
23390 $ymin = min($ymin, $y);
23391 $xmax = max($xmax, $x);
23392 $ymax = max($ymax, $y);
23393 } else {
23394 $cos_ang = cos($angle);
23395 $sin_ang = sin($angle);
23396 $a = (($x0 - $x) / 2);
23397 $b = (($y0 - $y) / 2);
23398 $xa = ($a * $cos_ang) - ($b * $sin_ang);
23399 $ya = ($a * $sin_ang) + ($b * $cos_ang);
23400 $rx2 = $rx * $rx;
23401 $ry2 = $ry * $ry;
23402 $xa2 = $xa * $xa;
23403 $ya2 = $ya * $ya;
23404 $delta = ($xa2 / $rx2) + ($ya2 / $ry2);
23405 if ($delta > 1) {
23406 $rx *= sqrt($delta);
23407 $ry *= sqrt($delta);
23408 $rx2 = $rx * $rx;
23409 $ry2 = $ry * $ry;
23410 }
23411 $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
23412 if ($numerator < 0) {
23413 $root = 0;
23414 } else {
23415 $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
23416 }
23417 if ($fa == $fs){
23418 $root *= -1;
23419 }
23420 $cax = $root * (($rx * $ya) / $ry);
23421 $cay = -$root * (($ry * $xa) / $rx);
23422 // coordinates of ellipse center
23423 $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
23424 $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
23425 // get angles
23426 $angs = TCPDF_STATIC::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
23427 $dang = TCPDF_STATIC::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
23428 if (($fs == 0) AND ($dang > 0)) {
23429 $dang -= (2 * M_PI);
23430 } elseif (($fs == 1) AND ($dang < 0)) {
23431 $dang += (2 * M_PI);
23432 }
23433 $angf = $angs - $dang;
23434 if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
23435 // reverse angles
23436 $tmp = $angs;
23437 $angs = $angf;
23438 $angf = $tmp;
23439 }
23440 $angs = round(rad2deg($angs), 6);
23441 $angf = round(rad2deg($angf), 6);
23442 // covent angles to positive values
23443 if (($angs < 0) AND ($angf < 0)) {
23444 $angs += 360;
23445 $angf += 360;
23446 }
23447 $pie = false;
23448 if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
23449 $pie = true;
23450 }
23451 list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
23452 $xmin = min($xmin, $x, $axmin);
23453 $ymin = min($ymin, $y, $aymin);
23454 $xmax = max($xmax, $x, $axmax);
23455 $ymax = max($ymax, $y, $aymax);
23456 }
23457 if ($relcoord) {
23458 $xoffset = $x;
23459 $yoffset = $y;
23460 }
23461 }
23462 }
23463 break;
23464 }
23465 case 'Z': {
23466 $this->_out('h');
23467 break;
23468 }
23469 }
23470 $firstcmd = false;
23471 } // end foreach
23472 if (!empty($op)) {
23473 $this->_out($op);
23474 }
23475 return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
23476 }
23477
23488 protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
23489 // check if we are in clip mode
23490 if ($this->svgclipmode) {
23491 $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]);
23492 return;
23493 }
23494 if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
23495 if (!isset($attribs['id'])) {
23496 $attribs['id'] = 'DF_'.(count($this->svgdefs) + 1);
23497 }
23498 $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23499 return;
23500 }
23501 $clipping = false;
23502 if ($parser == 'clip-path') {
23503 // set clipping mode
23504 $clipping = true;
23505 }
23506 // get styling properties
23507 $prev_svgstyle = $this->svgstyles[(count($this->svgstyles) - 1)]; // previous style
23508 $svgstyle = $this->svgstyles[0]; // set default style
23509 if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) {
23510 // default fill attribute for clipping
23511 $attribs['fill'] = 'none';
23512 }
23513 if (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) {
23514 // fix style for regular expression
23515 $attribs['style'] = ';'.$attribs['style'];
23516 }
23517 foreach ($prev_svgstyle as $key => $val) {
23518 if (in_array($key, TCPDF_IMAGES::$svginheritprop)) {
23519 // inherit previous value
23520 $svgstyle[$key] = $val;
23521 }
23522 if (isset($attribs[$key]) AND !TCPDF_STATIC::empty_string($attribs[$key])) {
23523 // specific attribute settings
23524 if ($attribs[$key] == 'inherit') {
23525 $svgstyle[$key] = $val;
23526 } else {
23527 $svgstyle[$key] = $attribs[$key];
23528 }
23529 } elseif (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) {
23530 // CSS style syntax
23531 $attrval = array();
23532 if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
23533 if ($attrval[1] == 'inherit') {
23534 $svgstyle[$key] = $val;
23535 } else {
23536 $svgstyle[$key] = $attrval[1];
23537 }
23538 }
23539 }
23540 }
23541 // transformation matrix
23542 if (!empty($ctm)) {
23543 $tm = $ctm;
23544 } else {
23545 //$tm = $this->svgstyles[(count($this->svgstyles) - 1)]['transfmatrix'];
23546 $tm = array(1,0,0,1,0,0);
23547 }
23548 if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
23550 }
23551 $svgstyle['transfmatrix'] = $tm;
23552 $invisible = false;
23553 if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
23554 // the current graphics element is invisible (nothing is painted)
23555 $invisible = true;
23556 }
23557 // process tag
23558 switch($name) {
23559 case 'defs': {
23560 $this->svgdefsmode = true;
23561 break;
23562 }
23563 // clipPath
23564 case 'clipPath': {
23565 if ($invisible) {
23566 break;
23567 }
23568 $this->svgclipmode = true;
23569 if (!isset($attribs['id'])) {
23570 $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1);
23571 }
23572 $this->svgclipid = $attribs['id'];
23573 $this->svgclippaths[$this->svgclipid] = array();
23574 $this->svgcliptm[$this->svgclipid] = $tm;
23575 break;
23576 }
23577 case 'svg': {
23578 // start of SVG object
23579 break;
23580 }
23581 case 'g': {
23582 // group together related graphics elements
23583 array_push($this->svgstyles, $svgstyle);
23584 $this->StartTransform();
23585 $this->SVGTransform($tm);
23586 $this->setSVGStyles($svgstyle, $prev_svgstyle);
23587 break;
23588 }
23589 case 'linearGradient': {
23590 if ($this->pdfa_mode) {
23591 break;
23592 }
23593 if (!isset($attribs['id'])) {
23594 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
23595 }
23596 $this->svggradientid = $attribs['id'];
23597 $this->svggradients[$this->svggradientid] = array();
23598 $this->svggradients[$this->svggradientid]['type'] = 2;
23599 $this->svggradients[$this->svggradientid]['stops'] = array();
23600 if (isset($attribs['gradientUnits'])) {
23601 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
23602 } else {
23603 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
23604 }
23605 //$attribs['spreadMethod']
23606 if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2'])))
23607 OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%'))
23608 OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%'))
23609 OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%'))
23610 OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) {
23611 $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
23612 } else {
23613 $this->svggradients[$this->svggradientid]['mode'] = 'measure';
23614 }
23615 $x1 = (isset($attribs['x1'])?$attribs['x1']:'0');
23616 $y1 = (isset($attribs['y1'])?$attribs['y1']:'0');
23617 $x2 = (isset($attribs['x2'])?$attribs['x2']:'100');
23618 $y2 = (isset($attribs['y2'])?$attribs['y2']:'0');
23619 if (isset($attribs['gradientTransform'])) {
23620 $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
23621 }
23622 $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
23623 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23624 // gradient is defined on another place
23625 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
23626 }
23627 break;
23628 }
23629 case 'radialGradient': {
23630 if ($this->pdfa_mode) {
23631 break;
23632 }
23633 if (!isset($attribs['id'])) {
23634 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
23635 }
23636 $this->svggradientid = $attribs['id'];
23637 $this->svggradients[$this->svggradientid] = array();
23638 $this->svggradients[$this->svggradientid]['type'] = 3;
23639 $this->svggradients[$this->svggradientid]['stops'] = array();
23640 if (isset($attribs['gradientUnits'])) {
23641 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
23642 } else {
23643 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
23644 }
23645 //$attribs['spreadMethod']
23646 if (((!isset($attribs['cx'])) AND (!isset($attribs['cy'])))
23647 OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%'))
23648 OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')) )) {
23649 $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
23650 } else {
23651 $this->svggradients[$this->svggradientid]['mode'] = 'measure';
23652 }
23653 $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5);
23654 $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5);
23655 $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx);
23656 $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy);
23657 $r = (isset($attribs['r']) ? $attribs['r'] : 0.5);
23658 if (isset($attribs['gradientTransform'])) {
23659 $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
23660 }
23661 $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
23662 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23663 // gradient is defined on another place
23664 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
23665 }
23666 break;
23667 }
23668 case 'stop': {
23669 // gradient stops
23670 if (substr($attribs['offset'], -1) == '%') {
23671 $offset = floatval(substr($attribs['offset'], -1)) / 100;
23672 } else {
23673 $offset = floatval($attribs['offset']);
23674 if ($offset > 1) {
23675 $offset /= 100;
23676 }
23677 }
23678 $stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors):'black';
23679 $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
23680 $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
23681 break;
23682 }
23683 // paths
23684 case 'path': {
23685 if ($invisible) {
23686 break;
23687 }
23688 if (isset($attribs['d'])) {
23689 $d = trim($attribs['d']);
23690 if (!empty($d)) {
23691 if ($clipping) {
23692 $this->SVGTransform($tm);
23693 $this->SVGPath($d, 'CNZ');
23694 } else {
23695 $this->StartTransform();
23696 $this->SVGTransform($tm);
23697 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, 0, 0, 1, 1, 'SVGPath', array($d, 'CNZ'));
23698 if (!empty($obstyle)) {
23699 $this->SVGPath($d, $obstyle);
23700 }
23701 $this->StopTransform();
23702 }
23703 }
23704 }
23705 break;
23706 }
23707 // shapes
23708 case 'rect': {
23709 if ($invisible) {
23710 break;
23711 }
23712 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
23713 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
23714 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
23715 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
23716 $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
23717 $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
23718 if ($clipping) {
23719 $this->SVGTransform($tm);
23720 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
23721 } else {
23722 $this->StartTransform();
23723 $this->SVGTransform($tm);
23724 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
23725 if (!empty($obstyle)) {
23726 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
23727 }
23728 $this->StopTransform();
23729 }
23730 break;
23731 }
23732 case 'circle': {
23733 if ($invisible) {
23734 break;
23735 }
23736 $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0);
23737 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
23738 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
23739 $x = ($cx - $r);
23740 $y = ($cy - $r);
23741 $w = (2 * $r);
23742 $h = $w;
23743 if ($clipping) {
23744 $this->SVGTransform($tm);
23745 $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
23746 } else {
23747 $this->StartTransform();
23748 $this->SVGTransform($tm);
23749 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
23750 if (!empty($obstyle)) {
23751 $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
23752 }
23753 $this->StopTransform();
23754 }
23755 break;
23756 }
23757 case 'ellipse': {
23758 if ($invisible) {
23759 break;
23760 }
23761 $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0);
23762 $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0);
23763 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
23764 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
23765 $x = ($cx - $rx);
23766 $y = ($cy - $ry);
23767 $w = (2 * $rx);
23768 $h = (2 * $ry);
23769 if ($clipping) {
23770 $this->SVGTransform($tm);
23771 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
23772 } else {
23773 $this->StartTransform();
23774 $this->SVGTransform($tm);
23775 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
23776 if (!empty($obstyle)) {
23777 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
23778 }
23779 $this->StopTransform();
23780 }
23781 break;
23782 }
23783 case 'line': {
23784 if ($invisible) {
23785 break;
23786 }
23787 $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
23788 $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
23789 $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
23790 $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
23791 $x = $x1;
23792 $y = $y1;
23793 $w = abs($x2 - $x1);
23794 $h = abs($y2 - $y1);
23795 if (!$clipping) {
23796 $this->StartTransform();
23797 $this->SVGTransform($tm);
23798 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
23799 $this->Line($x1, $y1, $x2, $y2);
23800 $this->StopTransform();
23801 }
23802 break;
23803 }
23804 case 'polyline':
23805 case 'polygon': {
23806 if ($invisible) {
23807 break;
23808 }
23809 $points = (isset($attribs['points'])?$attribs['points']:'0 0');
23810 $points = trim($points);
23811 // note that point may use a complex syntax not covered here
23812 $points = preg_split('/[\,\s]+/si', $points);
23813 if (count($points) < 4) {
23814 break;
23815 }
23816 $p = array();
23817 $xmin = 2147483647;
23818 $xmax = 0;
23819 $ymin = 2147483647;
23820 $ymax = 0;
23821 foreach ($points as $key => $val) {
23822 $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
23823 if (($key % 2) == 0) {
23824 // X coordinate
23825 $xmin = min($xmin, $p[$key]);
23826 $xmax = max($xmax, $p[$key]);
23827 } else {
23828 // Y coordinate
23829 $ymin = min($ymin, $p[$key]);
23830 $ymax = max($ymax, $p[$key]);
23831 }
23832 }
23833 $x = $xmin;
23834 $y = $ymin;
23835 $w = ($xmax - $xmin);
23836 $h = ($ymax - $ymin);
23837 if ($name == 'polyline') {
23838 $this->StartTransform();
23839 $this->SVGTransform($tm);
23840 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
23841 if (!empty($obstyle)) {
23842 $this->PolyLine($p, $obstyle, array(), array());
23843 }
23844 $this->StopTransform();
23845 } else { // polygon
23846 if ($clipping) {
23847 $this->SVGTransform($tm);
23848 $this->Polygon($p, 'CNZ', array(), array(), true);
23849 } else {
23850 $this->StartTransform();
23851 $this->SVGTransform($tm);
23852 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
23853 if (!empty($obstyle)) {
23854 $this->Polygon($p, $obstyle, array(), array(), true);
23855 }
23856 $this->StopTransform();
23857 }
23858 }
23859 break;
23860 }
23861 // image
23862 case 'image': {
23863 if ($invisible) {
23864 break;
23865 }
23866 if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
23867 break;
23868 }
23869 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
23870 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
23871 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
23872 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
23873 $img = $attribs['xlink:href'];
23874 if (!$clipping) {
23875 $this->StartTransform();
23876 $this->SVGTransform($tm);
23877 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
23878 if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
23879 // embedded image encoded as base64
23880 $img = '@'.base64_decode(substr($img, strlen($m[0])));
23881 } else {
23882 // fix image path
23883 if (!TCPDF_STATIC::empty_string($this->svgdir) AND (($img{0} == '.') OR (basename($img) == $img))) {
23884 // replace relative path with full server path
23885 $img = $this->svgdir.'/'.$img;
23886 }
23887 if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
23888 $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
23889 if (($findroot === false) OR ($findroot > 1)) {
23890 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
23891 $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
23892 } else {
23893 $img = $_SERVER['DOCUMENT_ROOT'].$img;
23894 }
23895 }
23896 }
23897 $img = urldecode($img);
23898 $testscrtype = @parse_url($img);
23899 if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
23900 // convert URL to server path
23901 $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
23902 }
23903 }
23904 // get image type
23906 if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
23907 $this->ImageEps($img, $x, $y, $w, $h);
23908 } elseif ($imgtype == 'svg') {
23909 $this->ImageSVG($img, $x, $y, $w, $h);
23910 } else {
23911 $this->Image($img, $x, $y, $w, $h);
23912 }
23913 $this->StopTransform();
23914 }
23915 break;
23916 }
23917 // text
23918 case 'text':
23919 case 'tspan': {
23920 // only basic support - advanced features must be implemented
23921 $this->svgtextmode['invisible'] = $invisible;
23922 if ($invisible) {
23923 break;
23924 }
23925 array_push($this->svgstyles, $svgstyle);
23926 if (isset($attribs['x'])) {
23927 $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false);
23928 } elseif ($name == 'tspan') {
23929 $x = $this->x;
23930 } else {
23931 $x = 0;
23932 }
23933 if (isset($attribs['dx'])) {
23934 $x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false);
23935 }
23936 if (isset($attribs['y'])) {
23937 $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false);
23938 } elseif ($name == 'tspan') {
23939 $y = $this->y;
23940 } else {
23941 $y = 0;
23942 }
23943 if (isset($attribs['dy'])) {
23944 $y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false);
23945 }
23946 $svgstyle['text-color'] = $svgstyle['fill'];
23947 $this->svgtext = '';
23948 if (isset($svgstyle['text-anchor'])) {
23949 $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor'];
23950 } else {
23951 $this->svgtextmode['text-anchor'] = 'start';
23952 }
23953 if (isset($svgstyle['direction'])) {
23954 if ($svgstyle['direction'] == 'rtl') {
23955 $this->svgtextmode['rtl'] = true;
23956 } else {
23957 $this->svgtextmode['rtl'] = false;
23958 }
23959 } else {
23960 $this->svgtextmode['rtl'] = false;
23961 }
23962 if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
23963 $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false);
23964 } else {
23965 $this->svgtextmode['stroke'] = false;
23966 }
23967 $this->StartTransform();
23968 $this->SVGTransform($tm);
23969 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
23970 $this->x = $x;
23971 $this->y = $y;
23972 break;
23973 }
23974 // use
23975 case 'use': {
23976 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23977 $svgdefid = substr($attribs['xlink:href'], 1);
23978 if (isset($this->svgdefs[$svgdefid])) {
23979 $use = $this->svgdefs[$svgdefid];
23980 if (isset($attribs['xlink:href'])) {
23981 unset($attribs['xlink:href']);
23982 }
23983 if (isset($attribs['id'])) {
23984 unset($attribs['id']);
23985 }
23986 $attribs = array_merge($attribs, $use['attribs']);
23987 $this->startSVGElementHandler($parser, $use['name'], $attribs);
23988 }
23989 }
23990 break;
23991 }
23992 default: {
23993 break;
23994 }
23995 } // end of switch
23996 }
23997
24006 protected function endSVGElementHandler($parser, $name) {
24007 switch($name) {
24008 case 'defs': {
24009 $this->svgdefsmode = false;
24010 break;
24011 }
24012 // clipPath
24013 case 'clipPath': {
24014 $this->svgclipmode = false;
24015 break;
24016 }
24017 case 'g': {
24018 // ungroup: remove last style from array
24019 array_pop($this->svgstyles);
24020 $this->StopTransform();
24021 break;
24022 }
24023 case 'text':
24024 case 'tspan': {
24025 if ($this->svgtextmode['invisible']) {
24026 // This implementation must be fixed to following the rule:
24027 // 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.
24028 break;
24029 }
24030 // print text
24031 $text = $this->svgtext;
24032 //$text = $this->stringTrim($text);
24033 $textlen = $this->GetStringWidth($text);
24034 if ($this->svgtextmode['text-anchor'] != 'start') {
24035 // check if string is RTL text
24036 if ($this->svgtextmode['text-anchor'] == 'end') {
24037 if ($this->svgtextmode['rtl']) {
24038 $this->x += $textlen;
24039 } else {
24040 $this->x -= $textlen;
24041 }
24042 } elseif ($this->svgtextmode['text-anchor'] == 'middle') {
24043 if ($this->svgtextmode['rtl']) {
24044 $this->x += ($textlen / 2);
24045 } else {
24046 $this->x -= ($textlen / 2);
24047 }
24048 }
24049 }
24050 $textrendermode = $this->textrendermode;
24051 $textstrokewidth = $this->textstrokewidth;
24052 $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false);
24053 if ($name == 'text') {
24054 // store current coordinates
24055 $tmpx = $this->x;
24056 $tmpy = $this->y;
24057 }
24058 $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
24059 if ($name == 'text') {
24060 // restore coordinates
24061 $this->x = $tmpx;
24062 $this->y = $tmpy;
24063 }
24064 // restore previous rendering mode
24065 $this->textrendermode = $textrendermode;
24066 $this->textstrokewidth = $textstrokewidth;
24067 $this->svgtext = '';
24068 $this->StopTransform();
24069 array_pop($this->svgstyles);
24070 break;
24071 }
24072 default: {
24073 break;
24074 }
24075 }
24076 }
24077
24086 protected function segSVGContentHandler($parser, $data) {
24087 $this->svgtext .= $data;
24088 }
24089
24090 // --- END SVG METHODS -----------------------------------------------------
24091
24092} // END OF TCPDF CLASS
24093
24094//============================================================+
24095// END OF FILE
24096//============================================================+
print $file
$n
Definition: RandomTest.php:80
$size
Definition: RandomTest.php:79
if(! $in) $columns
Definition: Utf8Test.php:46
$space
Definition: Sanitizer.php:42
$filename
Definition: buildRTE.php:89
foreach($mandatory_scripts as $file) $timestamp
Definition: buildRTE.php:81
PHP class to creates array representations for 2D barcodes to be used with TCPDF (http://www....
PHP class to creates array representations for common 1D barcodes to be used with TCPDF (http://www....
static getSpotColor($name, &$spotc)
Return the Spot color array.
static convertHTMLColorToDec($hcolor, &$spotc, $defcol=array('R'=>128, 'G'=>128, 'B'=>128))
Returns an array (RGB or CMYK) from an html color name, or a six-digit (i.e.
static _JScolor($color)
Convert color to javascript color.
static utf8StrRev($str, $setbom=false, $forcertl=false, $isunicode=true, &$currentfont)
Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
static UTF8ArrSubString($strarr, $start='', $end='', $unicode=true)
Extract a slice of the $strarr array and return it as string.
static UniArrSubString($uniarr, $start='', $end='')
Extract a slice of the $uniarr array and return it as string.
static UTF8ArrToLatin1Arr($unicode)
Converts UTF-8 characters array to array of Latin1 characters array
static utf8Bidi($ta, $str='', $forcertl=false, $isunicode=true, &$currentfont)
Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
static UTF8ToLatin1($str, $isunicode=true, &$currentfont)
Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.
static unichr($c, $unicode=true)
Returns the unicode caracter specified by the value.
static _getfontpath()
Return fonts path.
static UTF8StringToArray($str, $isunicode=true, &$currentfont)
Converts UTF-8 strings to codepoints array.
static addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false, $link=false)
Convert and add the selected TrueType or Type1 font to the fonts folder (that must be writeable).
Definition: tcpdf_fonts.php:66
static arrUTF8ToUTF16BE($unicode, $setbom=false)
Converts array of UTF-8 characters to UTF16-BE string.
static getFontRefSize($size, $refsize=12)
Get a reference font size.
static UTF8ToUTF16BE($str, $setbom=false, $isunicode=true, &$currentfont)
Converts UTF-8 strings to UTF16-BE.
static UTF8ArrayToUniArray($ta, $isunicode=true)
Convert an array of UTF8 values to array of unicode characters.
static _getTrueTypeFontSubset($font, $subsetchars)
Returns a subset of the TrueType font data without the unused glyphs.
static $uni_type
Array of Unicode types.
static $uni_identity_h
ToUnicode map for Identity-H stream static.
static $uni_RE_PATTERN_ARABIC
Pattern to test Arabic strings using regular expressions.
static $uni_RE_PATTERN_RTL
Pattern to test RTL (Righ-To-Left) strings using regular expressions.
static setGDImageTransparency($new_image, $image)
Set the transparency for the given GD image.
static getImageFileType($imgfile, $iminfo=array())
Return the image type given the file name or array returned by getimagesize() function.
static _toPNG($image)
Convert the loaded image to a PNG and then return a structure for the PDF creator.
static _parsejpeg($file)
Extract info from a JPEG file without using the GD library.
static $svginheritprop
Array of hinheritable SVG properties.
static _toJPEG($image, $quality)
Convert the loaded image to a JPEG and then return a structure for the PDF creator.
Static methods used by the TCPDF class.
static setPageBoxes($page, $type, $llx, $lly, $urx, $ury, $points=false, $k, $pagedim=array())
Set page boundaries.
static getPathPaintOperator($style, $default='S')
Get the Path-Painting Operators.
static getPageMode($mode='UseNone')
Get the canonical page layout mode.
static getCSSdataArray($dom, $key, $css)
Returns the styles array that apply for the selected HTML tag.
static getUserPermissionCode($permissions, $mode=0)
Return the premission code used on encryption (P value).
static getFormattedDate($time)
Returns a formatted date-time.
static extractCSSproperties($cssdata)
Extracts the CSS properties from a CSS string.
static $alias_group_tot_pages
String alias for total number of pages in a single group.
static getAnnotOptFromJSProp($prop, &$spot_colors, $rtl=false)
Convert JavaScript form fields properties array to Annotation Properties array.
static getEncPermissionsString($protection)
Convert encryption P value to a string of bytes, low-order byte first.
static getPageLayoutMode($layout='SinglePage')
Get the canonical page layout mode.
static _escape($s)
Add "\" before "", "(" and ")".
static getPageSizeFromFormat($format)
Get page dimensions from format name.
static $enc_padding
Encryption padding string.
static $pageboxes
Array page boxes names static.
static $alias_tot_pages
String alias for total number of pages.
static getTagStyleFromCSSarray($css)
Compact CSS data array into single string.
static intToRoman($number)
Returns the Roman representation of an integer number.
static sendOutputData($data, $length)
Ouput input data and compress it if possible.
static replacePageNumAliases($page, $replace, $diff=0)
Replace page number aliases with number.
static fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='', &$tagvspaces)
Cleanup HTML code (requires HTML Tidy library).
static getTCPDFProducer()
Return the current TCPDF producer.
static getVectorsAngle($x1, $y1, $x2, $y2)
Returns the angle in radiants between two vectors.
static $alias_group_num_page
String alias for group page number.
static $byterange_string
ByteRange placemark used during digital signature process.
static objclone($object)
Creates a copy of a class object.
static encodeNameObject($name)
Encode a name object.
static formatPageNumber($num)
Format the page numbers.
static removeSHY($txt='', $unicode=true)
Removes SHY characters from text.
static convertHexStringToString($bs)
Convert hexadecimal string to string.
static formatTOCPageNumber($num)
Format the page numbers on the Table Of Content.
static revstrpos($haystack, $needle, $offset=0)
Find position of last occurrence of a substring in a string.
static swapPageBoxCoordinates($page, $pagedim)
Swap X and Y coordinates of page boxes (change page boxes orientation).
static empty_string($str)
Determine whether a string is empty.
static _escapeXML($str)
Escape some special characters (< > &) for XML output.
static set_mqr($mqr)
Sets the current active configuration setting of magic_quotes_runtime (if the set_magic_quotes_runtim...
static getHyphenPatternsFromTEX($file)
Returns an array of hyphenation patterns.
static getRandomSeed($seed='')
Returns a string containing random data to be used as a seed for encryption methods.
static _AES($key, $text)
Returns the input text exrypted using AES algorithm and the specified key.
static _md5_16($str)
Encrypts a string using MD5 and returns it's value as a binary string.
static getObjFilename($name)
Returns a temporary filename for caching object on filesystem.
static getBorderMode($brd, $position='start', $opencell=true)
Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages)
static $alias_num_page
String alias for page number.
static serializeTCPDFtagParameters($pararray)
Serialize an array of parameters to be used with TCPDF tag in HTML code.
static _RC4($key, $text, &$last_enc_key, &$last_enc_key_c)
Returns the input text encrypted using RC4 algorithm and the specified key.
static getSVGTransformMatrix($attribute)
Get the tranformation matrix from SVG transform attribute.
static $alias_right_shift
String alias for right shift compensation used to correctly align page numbers on the right.
static getTransformationMatrixProduct($ta, $tb)
Get the product of two SVG tranformation matrices.
static isValidURL($url)
Check if the URL exist.
static getTimestamp($date)
Returns timestamp in seconds from formatted date-time.
static get_mqr()
Gets the current active configuration setting of magic_quotes_runtime (if the get_magic_quotes_runtim...
PHP class for generating PDF documents without requiring external extensions.
Definition: tcpdf.php:184
Ln($h='', $cell=false)
Performs a line break.
Definition: tcpdf.php:7327
SetXY($x, $y, $rtloff=false)
Defines the abscissa and ordinate of the current position.
Definition: tcpdf.php:7468
getPageNumGroupAlias()
Return the alias for the page number on the current page group.
Definition: tcpdf.php:13610
$colxshift
Array of: X difference between table cell x start and starting page margin, cellspacing,...
Definition: tcpdf.php:1500
endTOCPage()
Terminate the current TOC (Table Of Content) page.
Definition: tcpdf.php:3108
startPage($orientation='', $format='', $tocpage=false)
Starts a new page to the document.
Definition: tcpdf.php:3173
$DrawColor
Commands for drawing color.
Definition: tcpdf.php:472
getImageRBY()
Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image.
Definition: tcpdf.php:3387
$doc_modification_timestamp
Document modification date-time.
Definition: tcpdf.php:1815
$crMargin
Cell right margin (used by regions).
Definition: tcpdf.php:312
IncludeJS($script)
Adds a javascript.
Definition: tcpdf.php:12505
MirrorV($y='')
Verical Mirroring.
Definition: tcpdf.php:11104
$pdfunit
Default unit of measure for document.
Definition: tcpdf.php:1528
setJPEGQuality($quality)
Set the default JPEG compression quality (1-100)
Definition: tcpdf.php:13903
AddFont($family, $style='', $fontfile='', $subset='default')
Imports a TrueType, Type1, core, or CID0 font and makes it available.
Definition: tcpdf.php:4205
$encryptdata
Array containing encryption settings.
Definition: tcpdf.php:858
setGraphicVars($gvars, $extended=false)
Set graphic variables.
Definition: tcpdf.php:20379
$feps
Epsilon value used for float calculations.
Definition: tcpdf.php:1142
endSVGElementHandler($parser, $name)
Sets the closing SVG element handler function for the XML parser.
Definition: tcpdf.php:24006
_putimages()
Output images.
Definition: tcpdf.php:9095
AddSpotColor($name, $c, $m, $y, $k)
Defines a new spot color.
Definition: tcpdf.php:3718
setFontSubsetting($enable=true)
Enable or disable default option for font subsetting.
Definition: tcpdf.php:21936
$custom_xmp
Custom XMP data.
Definition: tcpdf.php:1822
stringLeftTrim($str, $replace='')
Left trim the input string.
Definition: tcpdf.php:21964
_putsignature()
Add certification signature (DocMDP or UR3) You can set only one signature type.
Definition: tcpdf.php:13301
endTemplate()
End the current XObject Template started with startTemplate() and restore the previous graphic state.
Definition: tcpdf.php:22123
$CoreFonts
Array of standard font names.
Definition: tcpdf.php:368
lastPage($resetmargins=false)
Reset pointer to the last document page.
Definition: tcpdf.php:3063
getFontDescent($font, $style='', $size=0)
Return the font descent value.
Definition: tcpdf.php:4606
getOriginalMargins()
Returns an array containing original margins:
Definition: tcpdf.php:15634
getCSSFontStretching($stretch, $parent=100)
Returns the percentage of font stretching from CSS value.
Definition: tcpdf.php:15985
getFontBBox()
Returns the bounding box of the current font in user units.
Definition: tcpdf.php:4540
$listcount
HTML PARSER: array count list items on nested lists.
Definition: tcpdf.php:778
$lisymbol
Symbol used for HTML unordered list items.
Definition: tcpdf.php:1107
SetLineWidth($width)
Defines the line width.
Definition: tcpdf.php:11306
$InFooter
Flag set when processing page footer.
Definition: tcpdf.php:514
resetLastH()
Reset the last cell height.
Definition: tcpdf.php:2458
$page_obj_id
ID of page objects.
Definition: tcpdf.php:1367
$OutlineRoot
Outline root for bookmark.
Definition: tcpdf.php:895
SetTopMargin($margin)
Defines the top margin.
Definition: tcpdf.php:2616
$sign
Boolean flag to enable document digital signature.
Definition: tcpdf.php:1311
setFooterData($tc=array(0, 0, 0), $lc=array(0, 0, 0))
Set footer data.
Definition: tcpdf.php:3293
$svgclippaths
Array of SVG clipPath commands.
Definition: tcpdf.php:1688
$file_id
File ID (used on document trailer).
Definition: tcpdf.php:879
$sig_obj_id
Digital signature object ID.
Definition: tcpdf.php:1360
getCellBorder($x, $y, $w, $h, $brd)
Returns the code to draw the cell border.
Definition: tcpdf.php:5583
$inthead
True when we are printing the thead section on a new page.
Definition: tcpdf.php:1458
$transfmrk
Array used to store positions of graphics transformation blocks inside the page buffer.
Definition: tcpdf.php:1185
$form_action
Current form action (used during XHTML rendering).
Definition: tcpdf.php:1395
openHTMLTagHandler($dom, $key, $cell)
Process opening tags.
Definition: tcpdf.php:18453
getFontStyle()
Returns the current font style.
Definition: tcpdf.php:15678
__construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false)
This is the class constructor.
Definition: tcpdf.php:1878
addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0, 0, 0))
Output a Table Of Content Index (TOC) using HTML templates.
Definition: tcpdf.php:21332
inPageBody()
Check if we are on the page body (excluding page header and footer).
Definition: tcpdf.php:3633
$re_spaces
Regular expression used to find blank characters (required for word-wrapping).
Definition: tcpdf.php:1346
getFontFamilyName($fontfamily)
Return normalized font name.
Definition: tcpdf.php:22015
$isunicode
Boolean flag set to true when the input text is unicode (require unicode fonts).
Definition: tcpdf.php:600
$theadMargins
Margins used for table header.
Definition: tcpdf.php:1304
setPageMark()
Set start-writing mark on current page stream used to put borders and fills.
Definition: tcpdf.php:3244
setFormDefaultProp($prop=array())
Set default properties for form fields.
Definition: tcpdf.php:12628
_dooverlinew($x, $y, $w)
Overline for rectangular text area.
Definition: tcpdf.php:10135
_putviewerpreferences()
Output viewer preferences.
Definition: tcpdf.php:9806
LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0, 0, 1, 0))
Paints a linear colour gradient.
Definition: tcpdf.php:14180
setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array())
Enable document signature (requires the OpenSSL Library).
Definition: tcpdf.php:13423
_dochecks()
Check for locale-related bug.
Definition: tcpdf.php:7767
$page
Current page number.
Definition: tcpdf.php:192
$tagvspaces
Array used for custom vertical spaces for HTML tags.
Definition: tcpdf.php:1149
$print_footer
Boolean flag to print/hide page footer.
Definition: tcpdf.php:681
setLastH($h)
Set the last cell height.
Definition: tcpdf.php:2449
$clMargin
Cell left margin (used by regions).
Definition: tcpdf.php:306
getImageBuffer($image)
Get image buffer content.
Definition: tcpdf.php:20626
getPDFData()
Returns the PDF data.
Definition: tcpdf.php:10377
setListIndentWidth($width)
Set custom width for list indentation.
Definition: tcpdf.php:19996
colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A, R, G, B, C, M, Y, K')
Paints color transition registration bars.
Definition: tcpdf.php:13980
checkPageRegions($h, $x, $y)
Check page for no-write regions and adapt current coordinates and page margins if necessary.
Definition: tcpdf.php:22396
$txtshadow
Text shadow data array.
Definition: tcpdf.php:740
_putocg()
Put pdf layers.
Definition: tcpdf.php:13652
SetCreator($creator)
Defines the creator of the document.
Definition: tcpdf.php:2929
cropMark($x, $y, $w, $h, $type='T, R, B, L', $color=array(0, 0, 0))
Paints crop marks.
Definition: tcpdf.php:14078
getGDgamma($c)
Get the GD-corrected PNG gamma value from alpha color.
Definition: tcpdf.php:7300
_getobj($objid='')
Return the starting object string for the selected object ID.
Definition: tcpdf.php:10054
getAlpha()
Get the alpha mode array (CA, ca, BM, AIS).
Definition: tcpdf.php:13893
$listindentlevel
HTML PARSER: current list indententation level.
Definition: tcpdf.php:796
$form_mode
Current method to submit forms.
Definition: tcpdf.php:1409
_addfield($type, $name, $x, $y, $w, $h, $prop)
Adds a javascript form field.
Definition: tcpdf.php:12592
_putspotcolors()
Output Spot Colors Resources.
Definition: tcpdf.php:9307
_putfonts()
Output fonts.
Definition: tcpdf.php:8751
$pagelen
Array containing page lengths in bytes.
Definition: tcpdf.php:1213
TranslateX($t_x)
Translate graphic object horizontally.
Definition: tcpdf.php:11141
ScaleY($s_y, $x='', $y='')
Vertical Scaling.
Definition: tcpdf.php:11033
$dpi
DPI (Dot Per Inch) Document Resolution (do not change).
Definition: tcpdf.php:932
$internal_encoding
PHP internal encoding.
Definition: tcpdf.php:828
$ZoomMode
Zoom display mode.
Definition: tcpdf.php:520
Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array())
Draws a Bezier curve.
Definition: tcpdf.php:11609
getHeaderFont()
Get header font.
Definition: tcpdf.php:10334
getBuffer()
Get buffer content.
Definition: tcpdf.php:20521
getGraphicVars()
Returns current graphic variables as array.
Definition: tcpdf.php:20318
$default_monospaced_font
Default monospace font.
Definition: tcpdf.php:1276
$author
Document author.
Definition: tcpdf.php:550
$spot_colors
Array of Spot colors.
Definition: tcpdf.php:1100
StopTransform()
Stops a 2D tranformation restoring previous graphic state.
Definition: tcpdf.php:10995
$page_regions
Array of no-write regions.
Definition: tcpdf.php:1598
getOverprint()
Get the overprint mode array (OP, op, OPM).
Definition: tcpdf.php:13848
PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2)
Draw the sector of an ellipse.
Definition: tcpdf.php:14658
$overprint
Overprint mode array.
Definition: tcpdf.php:1830
$encoding
Default encoding.
Definition: tcpdf.php:821
$svgcliptm
Array of SVG clipPath tranformation matrix.
Definition: tcpdf.php:1695
setContentMark($page=0)
Set start-writing mark on selected page.
Definition: tcpdf.php:3257
$ColorFlag
Indicates whether fill and text colors are different.
Definition: tcpdf.php:490
Error($msg)
This method is automatically called in case of fatal error; it simply outputs the message and halts t...
Definition: tcpdf.php:2940
setPDFVersion($version='1.7')
Set the PDF version (check PDF reference for valid values).
Definition: tcpdf.php:13945
GetCharWidth($char, $notlast=true)
Returns the length of the char in user unit for the current font considering current stretching and s...
Definition: tcpdf.php:4089
SetLeftMargin($margin)
Defines the left margin.
Definition: tcpdf.php:2601
$pageopen
Store the fage status (true when opened, false when closed).
Definition: tcpdf.php:1269
$svggradientid
ID of last SVG gradient.
Definition: tcpdf.php:1660
setStartingPageNumber($num=1)
Set the starting page number.
Definition: tcpdf.php:13533
$page_boxes
Define the page boundaries boxes to be set on document.
Definition: tcpdf.php:1845
Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0, 0, 0), $x=-1, $link='')
Adds a bookmark.
Definition: tcpdf.php:12320
$images
Array of used images.
Definition: tcpdf.php:392
removePageRegion($key)
Remove a single no-write region.
Definition: tcpdf.php:22378
setLanguageArray($language)
Set language array.
Definition: tcpdf.php:10364
Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true)
Draws a polygon.
Definition: tcpdf.php:11885
getAbsFontMeasure($s)
Convert a relative font measure into absolute value.
Definition: tcpdf.php:4579
setPageBuffer($page, $data, $append=false)
Set page buffer content.
Definition: tcpdf.php:20537
$fwPt
Width of page format in points.
Definition: tcpdf.php:258
SetAbsX($x)
Set the absolute X coordinate of the current pointer.
Definition: tcpdf.php:7480
copyPage($page=0)
Clone the specified page to a new page.
Definition: tcpdf.php:21050
putHtmlListBullet($listdepth, $listtype='', $size=10)
Output an HTML list bullet or ordered item symbol.
Definition: tcpdf.php:20116
getCSSBorderStyle($cssborder)
Returns the border style array from CSS border properties.
Definition: tcpdf.php:15761
Close()
Terminates the PDF document.
Definition: tcpdf.php:2972
startPageGroup($page='')
Create a new page group.
Definition: tcpdf.php:13520
setSRGBmode($mode=false)
Set flag to force sRGB_IEC61966-2.1 black scaled ICC color profile for the whole document.
Definition: tcpdf.php:2863
$annotation_fonts
List of fonts used on form fields (fontname => fontkey).
Definition: tcpdf.php:1416
$linestyleJoin
PDF string for join value of the last line.
Definition: tcpdf.php:1072
getNumberOfColumns()
Return the current number of columns.
Definition: tcpdf.php:21676
rollbackTransaction($self=false)
This method allows to undo the latest transaction by returning the latest saved TCPDF object with sta...
Definition: tcpdf.php:21496
Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M')
Prints a cell (rectangular area) with optional borders, background color and character string.
Definition: tcpdf.php:5023
deletePage($page)
Remove the specified page.
Definition: tcpdf.php:20868
segSVGContentHandler($parser, $data)
Sets the character data handler function for the XML parser.
Definition: tcpdf.php:24086
$objcopy
Cloned copy of the current class object.
Definition: tcpdf.php:1283
SetCellPadding($pad)
Set the same internal Cell padding for top, right, bottom, left-.
Definition: tcpdf.php:2645
$footer_text_color
Color for footer text (RGB array).
Definition: tcpdf.php:726
hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8)
Returns an array of chars containing soft hyphens.
Definition: tcpdf.php:21803
$imagekeys
Store the image keys.
Definition: tcpdf.php:1227
printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false)
Print an XObject Template.
Definition: tcpdf.php:22152
getAliasNbPages()
Returns the string alias used for the total number of pages.
Definition: tcpdf.php:13565
_getstream($s, $n=0)
Format output stream (DEPRECATED).
Definition: tcpdf.php:10275
movePage($frompage, $topage)
Move a page to a previous position.
Definition: tcpdf.php:20705
SetRightMargin($margin)
Defines the right margin.
Definition: tcpdf.php:2631
getImageScale()
Returns the adjusting factor to convert pixels to user units.
Definition: tcpdf.php:2490
SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='')
Defines the color used for all drawing operations (lines, rectangles and cell borders).
Definition: tcpdf.php:3984
$pagegroups
Array that contains the number of pages in each page group.
Definition: tcpdf.php:946
Output($name='doc.pdf', $dest='I')
Send the document to a given destination: string, local file or browser.
Definition: tcpdf.php:7518
SetLineStyle($style, $ret=false)
Set line style.
Definition: tcpdf.php:11349
_putjavascript()
Create a javascript PDF string.
Definition: tcpdf.php:12534
$svgdefs
Array of SVG defs.
Definition: tcpdf.php:1674
_putresources()
Output Resources.
Definition: tcpdf.php:9403
$font_spacing
Increases or decreases the space between characters in a text by the specified amount (tracking).
Definition: tcpdf.php:1590
getFooterFont()
Get Footer font.
Definition: tcpdf.php:10354
$PDFVersion
PDF version.
Definition: tcpdf.php:607
sortBookmarks()
Sort bookmarks for page and key.
Definition: tcpdf.php:12361
GetX()
Returns the relative X value of current position.
Definition: tcpdf.php:7362
$lasth
Height of last cell printed.
Definition: tcpdf.php:356
$font_stretching
Percentage of character stretching.
Definition: tcpdf.php:1583
_putAPXObject($w=0, $h=0, $stream='')
Put appearance streams XObject used to define annotation's appearance states.
Definition: tcpdf.php:8721
_putbookmarks()
Create a bookmark PDF string.
Definition: tcpdf.php:12379
_getannotsrefs($n)
Get references to page annotations.
Definition: tcpdf.php:8046
_dounderlinew($x, $y, $w)
Underline for rectangular text area.
Definition: tcpdf.php:10084
PageNoFormatted()
Returns the current page number formatted as a string.
Definition: tcpdf.php:13643
SetAutoPageBreak($auto, $margin=0)
Enables or disables the automatic page breaking mode.
Definition: tcpdf.php:2808
$rMargin
Right margin.
Definition: tcpdf.php:300
$textrendermode
Text rendering mode: 0 = Fill text; 1 = Stroke text; 2 = Fill, then stroke text; 3 = Neither fill nor...
Definition: tcpdf.php:1507
writeDiskCache($filename, $data, $append=false)
Writes data to a temporary file on filesystem.
Definition: tcpdf.php:20447
isCharDefined($char, $font='', $style='')
Return true in the character is present in the specified font.
Definition: tcpdf.php:4647
Translate($t_x, $t_y)
Translate graphic object horizontally and vertically.
Definition: tcpdf.php:11164
setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false)
Set alpha for stroking (CA) and non-stroking (ca) operations.
Definition: tcpdf.php:13861
$signature_appearance
Data for digital signature appearance.
Definition: tcpdf.php:1332
$footerlen
Array used to store footer length of each page.
Definition: tcpdf.php:1037
$force_srgb
If true force sRGB color profile for all document.
Definition: tcpdf.php:1794
addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false)
Convert and add the selected TrueType or Type1 font to the fonts folder (that must be writeable).
Definition: tcpdf.php:4188
_UEvalue()
Compute UE value (used for encryption)
Definition: tcpdf.php:10668
setColumnsArray($columns)
Set columns array.
Definition: tcpdf.php:21573
$header_string
String to pring on page header after title.
Definition: tcpdf.php:705
write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='')
Print a Linear Barcode.
Definition: tcpdf.php:15065
setOverprint($stroking=true, $nonstroking='', $mode=0)
Set overprint mode for stroking (OP) and non-stroking (op) painting operations.
Definition: tcpdf.php:13822
$fhPt
Height of page format in points.
Definition: tcpdf.php:264
getDestination()
Return the Named Destination array.
Definition: tcpdf.php:12267
PolyLine($p, $style='', $line_style=array(), $fill_color=array())
Draws a polygonal line.
Definition: tcpdf.php:11866
MirrorH($x='')
Horizontal Mirroring.
Definition: tcpdf.php:11093
$rtl
Boolean flag to indicate if the document language is Right-To-Left.
Definition: tcpdf.php:835
addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='')
Add an empty digital signature appearance (a cliccable rectangle area to get signature properties)
Definition: tcpdf.php:13475
getPageBuffer($page)
Get page buffer content.
Definition: tcpdf.php:20564
_newobj()
Begin a new object and return the object number.
Definition: tcpdf.php:10042
startSVGElementHandler($parser, $name, $attribs, $ctm=array())
Sets the opening SVG element handler function for the XML parser.
Definition: tcpdf.php:23488
addExtGState($parms)
Add transparency parameters to the current extgstate.
Definition: tcpdf.php:13749
selectColumn($col='')
Set position at a given column.
Definition: tcpdf.php:21587
getCSSFontSpacing($spacing, $parent=0)
Returns the letter-spacing value from CSS value.
Definition: tcpdf.php:15954
$pagedim
Page dimensions.
Definition: tcpdf.php:246
$newline
Boolean flag to indicate if a new line is created.
Definition: tcpdf.php:1044
getPageWidth($pagenum='')
Returns the page width in units.
Definition: tcpdf.php:2519
$FontFiles
Array of font files.
Definition: tcpdf.php:380
AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false)
Adds a new page to the document.
Definition: tcpdf.php:3123
setTableHeader()
This method is used to render the table header on new page (if any).
Definition: tcpdf.php:3642
closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0)
Process closing tags.
Definition: tcpdf.php:19123
$lMargin
Left margin.
Definition: tcpdf.php:294
$svgunit
Deafult unit of measure for SVG.
Definition: tcpdf.php:1646
$linethrough
line through state
Definition: tcpdf.php:918
replaceMissingChars($text, $font='', $style='', $subs=array())
Replace missing font characters on selected font with specified substitutions.
Definition: tcpdf.php:4674
_putstream($s, $n=0)
Output a stream (DEPRECATED).
Definition: tcpdf.php:10286
startLayer($name='', $print=true, $view=true)
Start a new pdf layer.
Definition: tcpdf.php:13677
Header()
This method is used to render the page header.
Definition: tcpdf.php:3413
$underline
Underlining flag.
Definition: tcpdf.php:442
_enddoc()
Output end of document (EOF).
Definition: tcpdf.php:9887
getRemainingWidth()
Returns the remaining width between the current position and margins.
Definition: tcpdf.php:6699
ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false)
Embedd a Scalable Vector Graphics (SVG) image.
Definition: tcpdf.php:22499
getCSSMargin($cssmargin, $width=0)
Get the internal Cell margin from CSS attribute.
Definition: tcpdf.php:15866
setRasterizeVectorImages($mode)
Enable/disable rasterization of vector images using ImageMagick library.
Definition: tcpdf.php:21925
$svgclipmode
Boolean value true when in SVG clipPath tag.
Definition: tcpdf.php:1681
_putXMP()
Put XMP data object and return ID.
Definition: tcpdf.php:9487
adjustCellPadding($brd=0)
Adjust the internal Cell padding array to take account of the line width.
Definition: tcpdf.php:2733
$default_table_columns
Default number of columns for html table.
Definition: tcpdf.php:746
Open()
This method begins the generation of the PDF document.
Definition: tcpdf.php:2960
getHtmlDomArray($html)
Returns the HTML DOM array.
Definition: tcpdf.php:16111
$LineWidth
Line width in user unit.
Definition: tcpdf.php:362
$efnames
Embedded Files Names.
Definition: tcpdf.php:1632
Scale($s_x, $s_y, $x='', $y='')
Vertical and horizontal non-proportional Scaling.
Definition: tcpdf.php:11060
__destruct()
Default destructor.
Definition: tcpdf.php:2029
_putcidfont0($font)
Output CID-0 fonts.
Definition: tcpdf.php:9025
_putextgstates()
Put extgstates for object transparency.
Definition: tcpdf.php:13793
$header_logo_width
Width of header image logo in user units.
Definition: tcpdf.php:693
GetAbsX()
Returns the absolute X value of current position.
Definition: tcpdf.php:7378
$tempfontsize
Temporary font size in points.
Definition: tcpdf.php:808
$jpeg_quality
Set the default JPEG compression quality (1-100).
Definition: tcpdf.php:967
$htmlLinkFontStyle
Default font style to add to html links.
Definition: tcpdf.php:1199
getDocModificationTimestamp()
Returns document modification timestamp in seconds.
Definition: tcpdf.php:10198
$js_objects
Javascript objects array.
Definition: tcpdf.php:1388
_outCurveV($x2, $y2, $x3, $y3)
Append a cubic B�zier curve to the current path.
Definition: tcpdf.php:11475
RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array())
Draws a rounded rectangle.
Definition: tcpdf.php:12076
getBorderStartPosition()
Return the starting coordinates to draw an html border.
Definition: tcpdf.php:19684
$CurrentFont
Current font info.
Definition: tcpdf.php:454
SetCompression($compress=true)
Activates or deactivates page compression.
Definition: tcpdf.php:2849
resetHeaderTemplate()
Reset the xobject template used by Header() method.
Definition: tcpdf.php:3395
$buffer
Buffer holding in-memory PDF.
Definition: tcpdf.php:216
getSpaceString()
Returns the string used to find spaces.
Definition: tcpdf.php:16854
$svgdefsmode
Boolean value true when in SVG defs group.
Definition: tcpdf.php:1667
$font_obj_ids
Store the font object IDs.
Definition: tcpdf.php:1262
$fontkeys
Store the font keys.
Definition: tcpdf.php:1255
$FontAscent
Current font ascent (distance between font top and baseline).
Definition: tcpdf.php:429
getPageDimensions($pagenum='')
Returns an array of page dimensions:
Definition: tcpdf.php:2503
ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='')
Extract info from a PNG image with alpha channel using the GD library.
Definition: tcpdf.php:7228
$title
Document title.
Definition: tcpdf.php:538
$footer_font
Default font used on page footer.
Definition: tcpdf.php:657
$default_graphic_vars
Array of default graphic settings.
Definition: tcpdf.php:1555
$FontFamily
Current font family.
Definition: tcpdf.php:416
$endlinex
End position of the latest inserted line.
Definition: tcpdf.php:1051
Transform($tm)
Apply graphic transformations.
Definition: tcpdf.php:11274
getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false)
Convert HTML string containing value and unit of measure to user's units or points.
Definition: tcpdf.php:20032
SkewY($angle_y, $x='', $y='')
Skew vertically.
Definition: tcpdf.php:11229
_putpages()
Output pages (and replace page number aliases).
Definition: tcpdf.php:7871
Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='')
This method prints text from the current position.
Definition: tcpdf.php:6277
$listindent
HTML PARSER: indent amount for lists.
Definition: tcpdf.php:790
getFontStretching()
Get the percentage of character stretching.
Definition: tcpdf.php:22290
getImageRBX()
Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image.
Definition: tcpdf.php:3378
$n_js
Javascript counter.
Definition: tcpdf.php:911
setImageBuffer($image, $data)
Set image buffer content.
Definition: tcpdf.php:20581
$FillColor
Commands for filling color.
Definition: tcpdf.php:478
$PageMode
A name object specifying how the document should be displayed when opened.
Definition: tcpdf.php:988
$svgclipid
ID of last SVG clipPath.
Definition: tcpdf.php:1702
$n
Current object number.
Definition: tcpdf.php:198
$javascript
Javascript code.
Definition: tcpdf.php:904
setTempRTL($mode)
Force temporary RTL language direction.
Definition: tcpdf.php:2406
readDiskCache($filename)
Read data from a temporary file on filesystem.
Definition: tcpdf.php:20475
$cell_margin
Array of cell margins ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
Definition: tcpdf.php:338
SetFontSize($size, $out=true)
Defines the size of the current font.
Definition: tcpdf.php:4500
getCellHeightRatio()
return the height of cell repect font height.
Definition: tcpdf.php:13935
$cache_file_length
Array used to store the lengths of cache files.
Definition: tcpdf.php:1290
registrationMark($x, $y, $r, $double=false, $cola=array(0, 0, 0), $colb=array(255, 255, 255))
Paints a registration mark.
Definition: tcpdf.php:14149
$FontStyle
Current font style.
Definition: tcpdf.php:422
getHeaderData()
Returns header data:
Definition: tcpdf.php:3305
$empty_signature_appearance
Array of empty digital signature appearances.
Definition: tcpdf.php:1339
$header_text_color
Color for header text (RGB array).
Definition: tcpdf.php:712
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())
Puts an image in the page.
Definition: tcpdf.php:6808
$footer_line_color
Color for footer line (RGB array).
Definition: tcpdf.php:733
setOpenCell($isopen)
Set the top/bottom cell sides to be open or closed when the cell cross the page.
Definition: tcpdf.php:20006
setHtmlVSpace($tagvs)
Set the vertical spaces for HTML tags.
Definition: tcpdf.php:19986
TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false)
Creates a text field.
Definition: tcpdf.php:12657
$linestyleWidth
PDF string for width value of the last line.
Definition: tcpdf.php:1058
ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false)
Creates a List-box field.
Definition: tcpdf.php:12881
$linestyleDash
PDF string for dash value of the last line.
Definition: tcpdf.php:1079
_getxobjectdict()
Return XObjects Dictionary.
Definition: tcpdf.php:9327
_putinfo()
Adds some Metadata information (Document Information Dictionary) (see Chapter 14.3....
Definition: tcpdf.php:9426
_fixAES256Password($password)
Convert password for AES-256 encryption mode.
Definition: tcpdf.php:10733
$diffs
Array of encoding differences.
Definition: tcpdf.php:386
getAutoPageBreak()
Return the auto-page-break mode (true or false).
Definition: tcpdf.php:2820
addPageRegion($region)
Add a single no-write region on selected page.
Definition: tcpdf.php:22359
AddLink()
Creates a new internal link and returns its identifier.
Definition: tcpdf.php:4722
$keywords
Document keywords.
Definition: tcpdf.php:556
$cell_padding
Array of cell internal paddings ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
Definition: tcpdf.php:331
$extgstates
Array of transparency objects and parameters.
Definition: tcpdf.php:960
$dests
A dictionary of names and corresponding destinations (Dests key on document Catalog).
Definition: tcpdf.php:1618
ScaleXY($s, $x='', $y='')
Vertical and horizontal proportional Scaling.
Definition: tcpdf.php:11046
$original_rMargin
Original right margin value.
Definition: tcpdf.php:645
setDefaultTableColumns($cols=4)
Set the default number of columns in a row for HTML tables.
Definition: tcpdf.php:13916
SetFillSpotColor($name, $tint=100)
Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
Definition: tcpdf.php:3792
$form_enctype
Current form encryption type (used during XHTML rendering).
Definition: tcpdf.php:1402
_putcatalog()
Output Catalog.
Definition: tcpdf.php:9625
$hPt
Current height of page in points.
Definition: tcpdf.php:276
getCSSBorderWidth($width)
Returns the border width from CSS property.
Definition: tcpdf.php:15705
startTransaction()
Stores a copy of the current TCPDF object used for undo operation.
Definition: tcpdf.php:21465
endPage($tocpage=false)
Terminate the current page.
Definition: tcpdf.php:3147
$creator
Document creator.
Definition: tcpdf.php:562
$w
Current width of page in user unit.
Definition: tcpdf.php:282
_putannotsrefs($n)
Output references to page annotations.
Definition: tcpdf.php:8034
setBuffer($data)
Set buffer content (always append data).
Definition: tcpdf.php:20485
$openMarkedContent
Boolean flag to indicate if marked-content sequence is open.
Definition: tcpdf.php:1086
$l
Language templates.
Definition: tcpdf.php:663
$header_title
Title to be printed on default page header.
Definition: tcpdf.php:699
setFooter()
This method is used to render the page footer.
Definition: tcpdf.php:3577
setUserRights( $enable=true, $document='/FullSave', $annots='/Create/Delete/Modify/Copy/Import/Export', $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate', $signature='/Modify', $ef='/Create/Delete/Modify/Import', $formex='')
Set User's Rights for PDF Reader WARNING: This is experimental and currently do not work.
Definition: tcpdf.php:13387
_getrawstream($s, $n=0)
get raw output stream.
Definition: tcpdf.php:10260
getCSSBorderDashStyle($style)
Returns the border dash style from CSS property.
Definition: tcpdf.php:15725
$intmrk
Array used to store positions inside the pages buffer (keys are the page numbers).
Definition: tcpdf.php:1002
unichr($c)
Returns the unicode caracter specified by the value.
Definition: tcpdf.php:4168
setRTL($enable, $resetx=true)
Enable or disable Right-To-Left language mode.
Definition: tcpdf.php:2380
getColumn()
Return the current column number.
Definition: tcpdf.php:21666
_datestring($n=0, $timestamp=0)
Returns a formatted date for meta information.
Definition: tcpdf.php:10210
$premode
Boolean flag to indicate if we are inside a PRE tag.
Definition: tcpdf.php:1177
Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2)
Draws a circle.
Definition: tcpdf.php:11848
SetProtection($permissions=array('print', 'modify', 'copy', 'annot-forms', 'fill-forms', 'extract', 'assemble', 'print-high'), $user_pass='', $owner_pass=null, $mode=0, $pubkeys=null)
Set document protection Remark: the protection against modification is for people who have the full A...
Definition: tcpdf.php:10869
startTemplate($w=0, $h=0, $group=false)
Start a new XObject Template.
Definition: tcpdf.php:22058
getCSSBorderMargin($cssbspace, $width=0)
Get the border-spacing from CSS attribute.
Definition: tcpdf.php:15920
getFooterMargin()
Returns footer margin in user units.
Definition: tcpdf.php:3352
$FontDescent
Current font descent (distance between font bottom and baseline).
Definition: tcpdf.php:436
setImageSubBuffer($image, $key, $data)
Set image buffer content for a specified sub-key.
Definition: tcpdf.php:20606
$header_line_color
Color for header line (RGB array).
Definition: tcpdf.php:719
_dooverline($x, $y, $txt)
Overline text.
Definition: tcpdf.php:10122
GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false)
Returns the length of a string in user unit.
Definition: tcpdf.php:4034
setCellMargins($left='', $top='', $right='', $bottom='')
Set the internal Cell margins.
Definition: tcpdf.php:2700
isUnicodeFont()
Return true if the current font is unicode type.
Definition: tcpdf.php:22003
$tmprtl
Boolean flag used to force RTL or LTR string direction.
Definition: tcpdf.php:842
getBreakMargin($pagenum='')
Returns the page break margin.
Definition: tcpdf.php:2551
setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0, 0, 0), $lc=array(0, 0, 0))
Set header data.
Definition: tcpdf.php:3278
setFooterMargin($fm=10)
Set footer margin.
Definition: tcpdf.php:3342
$tcpdflink
If true print TCPDF meta link.
Definition: tcpdf.php:1852
SetAbsY($y)
Set the absolute Y coordinate of the current pointer.
Definition: tcpdf.php:7491
writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true)
Definition: tcpdf.php:16896
setPageBoxTypes($boxes)
Set page boxes to be included on page descriptions.
Definition: tcpdf.php:7858
SetFillColorArray($color, $ret=false)
Defines the color used for all filling operations (filled rectangles and cell backgrounds).
Definition: tcpdf.php:3864
$columns
Array of column measures (width, space, starting Y position).
Definition: tcpdf.php:1465
_objectkey($n)
Compute encryption key depending on object number where the encrypted data is stored.
Definition: tcpdf.php:10467
setHtmlLinksStyle($color=array(0, 0, 255), $fontstyle='U')
Set the color and font style for HTML links.
Definition: tcpdf.php:20017
_dounderline($x, $y, $txt)
Underline text.
Definition: tcpdf.php:10071
drawHTMLTagBorder($tag, $xmax)
Draw an HTML block border and fill.
Definition: tcpdf.php:19700
addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false)
Add vertical spaces if needed.
Definition: tcpdf.php:19658
Line($x1, $y1, $x2, $y2, $style=array())
Draws a line between two points.
Definition: tcpdf.php:11508
$y
Current vertical position in user unit for cell positioning.
Definition: tcpdf.php:350
_dolinethrough($x, $y, $txt)
Line through text.
Definition: tcpdf.php:10096
RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false)
Creates a RadioButton field.
Definition: tcpdf.php:12778
setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0, 0, 0), $x=-1, $link='')
Adds a bookmark - alias for Bookmark().
Definition: tcpdf.php:12303
$ur
Array with additional document-wide usage rights for the document.
Definition: tcpdf.php:925
setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='')
Defines the color used by the specified type ('draw', 'fill', 'text').
Definition: tcpdf.php:3894
getPageHeight($pagenum='')
Returns the page height in units.
Definition: tcpdf.php:2535
getFontSizePt()
Returns the current font size in points unit.
Definition: tcpdf.php:15658
$last_enc_key
Last RC4 key encrypted (cached for optimisation).
Definition: tcpdf.php:865
SetX($x, $rtloff=false)
Defines the abscissa of the current position.
Definition: tcpdf.php:7402
setPrintFooter($val=true)
Set a flag to print page footer.
Definition: tcpdf.php:3369
$opencell
Boolean flag to indicate if the border of the cell sides that cross the page should be removed.
Definition: tcpdf.php:1163
SetDrawColorArray($color, $ret=false)
Defines the color used for all drawing operations (lines, rectangles and cell borders).
Definition: tcpdf.php:3850
commitTransaction()
Delete the copy of the current TCPDF object used for undo operation.
Definition: tcpdf.php:21482
hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8)
Returns text with soft hyphens.
Definition: tcpdf.php:21879
Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array())
Draws a rectangle.
Definition: tcpdf.php:11538
$embeddedfiles
Array of files to embedd.
Definition: tcpdf.php:1170
setCellHeightRatio($h)
Set the height of the cell (line height) respect the font height.
Definition: tcpdf.php:13926
$original_lMargin
Original left margin value.
Definition: tcpdf.php:638
_outCurve($x1, $y1, $x2, $y2, $x3, $y3)
Append a cubic B�zier curve to the current path.
Definition: tcpdf.php:11459
getFontSpacing()
Get the amount to increase or decrease the space between characters in a text.
Definition: tcpdf.php:22312
$lispacer
Spacer string for LI tags.
Definition: tcpdf.php:814
CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false)
Creates a CheckBox field.
Definition: tcpdf.php:13054
setPrintHeader($val=true)
Set a flag to print page header.
Definition: tcpdf.php:3360
$listnum
HTML PARSER: current list nesting level.
Definition: tcpdf.php:784
setSpacesRE($re='/[^\S\xa0]/')
Set regular expression to detect withespaces or word separators.
Definition: tcpdf.php:2355
Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false)
Creates a button field.
Definition: tcpdf.php:13134
$print_header
Boolean flag to print/hide page header.
Definition: tcpdf.php:675
setDestination($name, $y=-1, $page='', $x=-1)
Add a Named Destination.
Definition: tcpdf.php:12230
$svgdir
Directory used for the last SVG image.
Definition: tcpdf.php:1639
setHeaderTemplateAutoreset($val=true)
Set a flag to automatically reset the xobject template used by Header() method at each page.
Definition: tcpdf.php:3404
setExtraXMP($xmp)
Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag.
Definition: tcpdf.php:9477
getNumPages()
Get the total number of insered pages.
Definition: tcpdf.php:3085
$htmlvspace
Count the latest inserted vertical spaces on HTML.
Definition: tcpdf.php:1093
replaceChar($oldchar, $newchar)
Replace a char if is defined on the current font.
Definition: tcpdf.php:5560
Skew($angle_x, $angle_y, $x='', $y='')
Skew.
Definition: tcpdf.php:11243
SetAuthor($author)
Defines the author of the document.
Definition: tcpdf.php:2907
$fgcolor
Current foreground color.
Definition: tcpdf.php:766
setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=> 'Normal'))
Set parameters for drop shadow effect for text.
Definition: tcpdf.php:21743
setPage($pnum, $resetmargins=false)
Move pointer at the specified document page and update page dimensions.
Definition: tcpdf.php:3016
serializeTCPDFtagParameters($pararray)
Serialize an array of parameters to be used with TCPDF tag in HTML code.
Definition: tcpdf.php:16870
endLayer()
End the current PDF layer.
Definition: tcpdf.php:13697
setPageRegions($regions=array())
Set no-write regions on page.
Definition: tcpdf.php:22339
pixelsToUnits($px)
Converts pixels to User's Units.
Definition: tcpdf.php:10441
$FontSizePt
Current font size in points.
Definition: tcpdf.php:460
$header_xobjid
ID of the stored default header template (-1 = not set).
Definition: tcpdf.php:613
PageNo()
Returns the current page number.
Definition: tcpdf.php:3701
Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=> 'Text'), $spaces=0)
Puts a markup annotation on a rectangular area of the page.
Definition: tcpdf.php:4778
$start_transaction_page
Store page number when startTransaction() is called.
Definition: tcpdf.php:1444
$PageAnnots
Array of Annotations in pages.
Definition: tcpdf.php:404
_OEvalue()
Compute OE value (used for encryption)
Definition: tcpdf.php:10719
$customlistindent
HTML PARSER: custom indent amount for lists.
Definition: tcpdf.php:1156
_putannotsobjs()
Output annotations objects for all pages.
Definition: tcpdf.php:8090
GetLineWidth()
Returns the current the line width.
Definition: tcpdf.php:11322
_putheader()
Output PDF File Header (7.5.2).
Definition: tcpdf.php:9878
setHeader()
This method is used to render the page header.
Definition: tcpdf.php:3537
$compress
Compression flag.
Definition: tcpdf.php:234
checkPageBreak($h=0, $y='', $addpage=true)
Add page if needed.
Definition: tcpdf.php:4969
$footerpos
Array used to store footer positions of each page.
Definition: tcpdf.php:1030
$emptypagemrk
Array used to store page positions to track empty pages (keys are the page numbers).
Definition: tcpdf.php:1016
$barcode
Barcode to print on page footer (only if set).
Definition: tcpdf.php:669
getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M')
Returns the PDF string code to print a cell (rectangular area) with optional borders,...
Definition: tcpdf.php:5092
StartTransform()
Starts a 2D tranformation saving current graphic state.
Definition: tcpdf.php:10972
setColorArray($type, $color, $ret=false)
Set the color array for the specified type ('draw', 'fill', 'text').
Definition: tcpdf.php:3819
$check_page_regions
Boolean value true when page region check is active.
Definition: tcpdf.php:1604
_encrypt_data($n, $s)
Encrypt the input string.
Definition: tcpdf.php:10487
setTextRenderingMode($stroke=0, $fill=true, $clip=false)
Set Text rendering mode.
Definition: tcpdf.php:21688
stringRightTrim($str, $replace='')
Right trim the input string.
Definition: tcpdf.php:21977
AcceptPageBreak()
Whenever a page break condition is met, the method is called, and the break is issued or not dependin...
Definition: tcpdf.php:4942
$strokecolor
Current stroke color.
Definition: tcpdf.php:1521
$header_font
Default font used on page header.
Definition: tcpdf.php:651
$tocpage
Boolean flag true when we are on TOC (Table Of Content) page.
Definition: tcpdf.php:1534
setDocCreationTimestamp($time)
Set the document creation timestamp.
Definition: tcpdf.php:10162
_outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false)
Append an elliptical arc to the current path.
Definition: tcpdf.php:11720
setPageFormat($format, $orientation='P')
Change the format of the current page.
Definition: tcpdf.php:2136
getBarcode()
Get current barcode.
Definition: tcpdf.php:15031
MirrorP($x='', $y='')
Point reflection mirroring.
Definition: tcpdf.php:11116
$FontSize
Current font size in user unit.
Definition: tcpdf.php:466
_Uvalue()
Compute U value (used for encryption)
Definition: tcpdf.php:10635
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())
Draws a star polygon.
Definition: tcpdf.php:12034
SetDocInfoUnicode($unicode=true)
Turn on/off Unicode mode for document information dictionary (meta tags).
Definition: tcpdf.php:2874
getPage()
Get current document page number.
Definition: tcpdf.php:3074
$radio_groups
List of radio group objects IDs.
Definition: tcpdf.php:1430
$starting_page_number
Starting page number.
Definition: tcpdf.php:568
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)
This method allows printing text with line breaks.
Definition: tcpdf.php:5786
TranslateY($t_y)
Translate graphic object vertically.
Definition: tcpdf.php:11152
setFontSpacing($spacing=0)
Set the amount to increase or decrease the space between characters in a text.
Definition: tcpdf.php:22301
setFontBuffer($font, $data)
Set font buffer content.
Definition: tcpdf.php:20642
$last_enc_key_c
Last RC4 computed key.
Definition: tcpdf.php:872
getTextShadow()
Return the text shadow parameters array.
Definition: tcpdf.php:21785
$current_column
Current column number.
Definition: tcpdf.php:1479
$pdfa_mode
If true set the document to PDF/A mode.
Definition: tcpdf.php:1801
$default_form_prop
Deafult Javascript field properties.
Definition: tcpdf.php:1381
_outRect($x, $y, $w, $h, $op)
Append a rectangle to the current path as a complete subpath, with lower-left corner (x,...
Definition: tcpdf.php:11441
setPageUnit($unit)
Set the units of measure for the document.
Definition: tcpdf.php:2044
swapMargins($reverse=true)
Swap the left and right margins.
Definition: tcpdf.php:19962
RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array())
Draws a rounded rectangle.
Definition: tcpdf.php:12095
RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5, 0.5, 0.5, 0.5, 1))
Paints a radial colour gradient.
Definition: tcpdf.php:14198
getScaleFactor()
Returns the scale factor (number of points in user unit).
Definition: tcpdf.php:2565
Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array())
Draws a poly-Bezier curve.
Definition: tcpdf.php:11639
$pdflayers
Array of PDF layers data.
Definition: tcpdf.php:1611
$links
Array of internal links.
Definition: tcpdf.php:410
_outPoint($x, $y)
Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line s...
Definition: tcpdf.php:11411
setVisibility($v)
Set the visibility of the successive elements.
Definition: tcpdf.php:13716
SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='')
Defines the color used for text.
Definition: tcpdf.php:4018
$bufferlen
Length of the buffer in bytes.
Definition: tcpdf.php:1234
$form_obj_id
List of form annotations IDs.
Definition: tcpdf.php:1374
SetMargins($left, $top, $right=-1, $keepmargins=false)
Defines the left, top and right margins.
Definition: tcpdf.php:2579
$HREF
HTML PARSER: array to store current link and rendering styles.
Definition: tcpdf.php:754
$xobjects
Array of XObjects.
Definition: tcpdf.php:1562
Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2)
Draws an ellipse.
Definition: tcpdf.php:11679
Gradient($type, $coords, $stops, $background=array(), $antialias=false)
Output gradient.
Definition: tcpdf.php:14340
$gradients
Array for storing gradient information.
Definition: tcpdf.php:995
$wPt
Current width of page in points.
Definition: tcpdf.php:270
$docinfounicode
If true set the document information dictionary in Unicode.
Definition: tcpdf.php:532
_putencryption()
Put encryption on PDF document.
Definition: tcpdf.php:10515
$header_xobj_autoreset
If true reset the Header Xobject template at each page.
Definition: tcpdf.php:619
_puttruetypeunicode($font)
Adds unicode fonts.
Definition: tcpdf.php:8913
SVGTransform($tm)
Apply SVG graphic transformation matrix.
Definition: tcpdf.php:22823
Link($x, $y, $w, $h, $link, $spaces=0)
Puts a link on a rectangular area of the page.
Definition: tcpdf.php:4761
$InHeader
Flag set when processing page header.
Definition: tcpdf.php:508
$transfmatrix
Array of transformation matrix.
Definition: tcpdf.php:1121
$cached_files
Array of cached files.
Definition: tcpdf.php:398
setCellPaddings($left='', $top='', $right='', $bottom='')
Set the internal Cell paddings.
Definition: tcpdf.php:2664
SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='')
Defines the color used for all filling operations (filled rectangles and cell backgrounds).
Definition: tcpdf.php:4001
getGroupPageNo()
Return the current page in the group.
Definition: tcpdf.php:13623
SkewX($angle_x, $x='', $y='')
Skew horizontally.
Definition: tcpdf.php:11216
$subject
Document subject.
Definition: tcpdf.php:544
$pages
Array containing pages.
Definition: tcpdf.php:222
setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array())
Apply the requested SVG styles (*** TO BE COMPLETED ***)
Definition: tcpdf.php:22842
replaceBuffer($data)
Replace the buffer content.
Definition: tcpdf.php:20503
$img_rb_y
The right-bottom corner Y coordinate of last inserted image.
Definition: tcpdf.php:584
_putdests()
Insert Named Destinations.
Definition: tcpdf.php:12277
getCharBBox($char)
Returns the glyph bounding box of the specified character in the current font in user units.
Definition: tcpdf.php:4589
$pageobjects
Array of object IDs for each page.
Definition: tcpdf.php:210
stringTrim($str, $replace='')
Trim the input string.
Definition: tcpdf.php:21990
$header_logo
Header image logo.
Definition: tcpdf.php:687
$CurOrientation
Current page orientation (P = Portrait, L = Landscape).
Definition: tcpdf.php:240
$xobjid
Current XObject ID.
Definition: tcpdf.php:1576
_Ovalue()
Compute O value (used for encryption)
Definition: tcpdf.php:10681
$numfonts
Counts the number of fonts.
Definition: tcpdf.php:1248
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)
Prints a text cell at the specified position.
Definition: tcpdf.php:4922
PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90)
Draw the sector of a circle.
Definition: tcpdf.php:14637
$start_transaction_y
Store Y position when startTransaction() is called.
Definition: tcpdf.php:1451
$booklet
Booklet mode for double-sided pages.
Definition: tcpdf.php:1135
$radiobutton_groups
List of radio buttons parent objects.
Definition: tcpdf.php:1423
$n_dests
Object ID for Named Destinations.
Definition: tcpdf.php:1625
writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='')
Allows to preserve some HTML formatting (limited support).
Definition: tcpdf.php:16913
SetTitle($title)
Defines the title of the document.
Definition: tcpdf.php:2885
_putshaders()
Output gradient shaders.
Definition: tcpdf.php:14445
_out($s)
Output a string to the document.
Definition: tcpdf.php:10295
setFontSubBuffer($font, $key, $data)
Set font buffer content.
Definition: tcpdf.php:20668
getFontBuffer($font)
Get font buffer content.
Definition: tcpdf.php:20688
_textstring($s, $n=0)
Format a text string for meta information.
Definition: tcpdf.php:10224
$currpagegroup
Current page group number.
Definition: tcpdf.php:953
getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt')
Convert HTML string containing font size value to points.
Definition: tcpdf.php:16057
_putxobjects()
Output Form XObjects Templates.
Definition: tcpdf.php:9204
$signature_max_length
Digital signature max length.
Definition: tcpdf.php:1325
getGroupPageNoFormatted()
Returns the current group page number formatted as a string.
Definition: tcpdf.php:13633
getDocCreationTimestamp()
Returns document creation timestamp in seconds.
Definition: tcpdf.php:10188
addJavascriptObject($script, $onload=false)
Adds a javascript object and return object ID.
Definition: tcpdf.php:12518
GetNumChars($s)
Returns the numbero of characters in a string.
Definition: tcpdf.php:4137
getPageRegions()
Return an array of no-write page regions.
Definition: tcpdf.php:22324
getFormDefaultProp()
Return the default properties for form fields.
Definition: tcpdf.php:12639
SetSubject($subject)
Defines the subject of the document.
Definition: tcpdf.php:2896
$gdgammacache
Cache array for computed GD gamma values.
Definition: tcpdf.php:1859
$viewer_preferences
PDF viewer preferences.
Definition: tcpdf.php:981
$fonts
Array of used fonts.
Definition: tcpdf.php:374
isRTLTextDir()
Return the current temporary RTL status.
Definition: tcpdf.php:2438
getFontSize()
Returns the current font size.
Definition: tcpdf.php:15648
getCSSPadding($csspadding, $width=0)
Get the internal Cell padding from CSS attribute.
Definition: tcpdf.php:15812
ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false)
Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
Definition: tcpdf.php:14702
$k
Scale factor (number of points in user unit).
Definition: tcpdf.php:252
SetLink($link, $y=0, $page=-1)
Defines the page and position a link points to.
Definition: tcpdf.php:4738
$tMargin
Top margin.
Definition: tcpdf.php:318
$img_rb_x
The right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image.
Definition: tcpdf.php:576
$textstrokewidth
Text stroke width in doc units.
Definition: tcpdf.php:1514
getFontAscent($font, $style='', $size=0)
Return the font ascent value.
Definition: tcpdf.php:4627
getFontFamily()
Returns the current font family name.
Definition: tcpdf.php:15668
$bMargin
Page break margin.
Definition: tcpdf.php:324
getFontSubsetting()
Return the default option for font subsetting.
Definition: tcpdf.php:21951
getAliasRightShift()
Returns the string alias used right align page numbers.
Definition: tcpdf.php:13544
setViewerPreferences($preferences)
Set the viewer preferences dictionary controlling the way the document is to be presented on the scre...
Definition: tcpdf.php:13963
$cntmrk
Array used to store content positions inside the pages buffer (keys are the page numbers).
Definition: tcpdf.php:1023
$font_subsetting
Boolean flag: if true enables font subsetting by default.
Definition: tcpdf.php:1548
MirrorL($angle=0, $x='', $y='')
Reflection against a straight line through point (x, y) with the gradient angle (angle).
Definition: tcpdf.php:11129
$svgstyles
Array of SVG properties.
Definition: tcpdf.php:1723
$AutoPageBreak
Automatic page breaking.
Definition: tcpdf.php:496
$htmlLinkColorArray
Default color for html links.
Definition: tcpdf.php:1192
convertSVGtMatrix($tm)
Convert SVG transformation matrix to PDF.
Definition: tcpdf.php:22803
SetY($y, $resetx=true, $rtloff=false)
Moves the current abscissa back to the left margin and sets the ordinate.
Definition: tcpdf.php:7435
$svgtextmode
SVG text properties.
Definition: tcpdf.php:1716
_putEmbeddedFiles()
Embedd the attached files.
Definition: tcpdf.php:4868
_datastring($s, $n=0)
Format a data string for meta information.
Definition: tcpdf.php:10148
setDocModificationTimestamp($time)
Set the document modification timestamp.
Definition: tcpdf.php:10175
addTOCPage($orientation='', $format='', $keepmargins=false)
Adds a new TOC (Table Of Content) page to the document.
Definition: tcpdf.php:3098
$linestyleCap
PDF string for CAP value of the last line.
Definition: tcpdf.php:1065
$footer_margin
Minimum distance between footer and bottom page margin.
Definition: tcpdf.php:631
setBarcode($bc='')
Set document barcode.
Definition: tcpdf.php:15021
getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0)
This method return the estimated height needed for printing a simple text string using the Multicell(...
Definition: tcpdf.php:6240
setFontStretching($perc=100)
Set the percentage of character stretching.
Definition: tcpdf.php:22279
setPageOrientation($orientation, $autopagebreak='', $bottommargin='')
Set page orientation.
Definition: tcpdf.php:2250
ScaleX($s_x, $x='', $y='')
Horizontal Scaling.
Definition: tcpdf.php:11020
$h
Current height of page in user unit.
Definition: tcpdf.php:288
SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone')
Defines the way the document is to be displayed by the viewer.
Definition: tcpdf.php:2832
Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15)
Draws a grahic arrow.
Definition: tcpdf.php:12165
$rasterize_vector_images
Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick librar...
Definition: tcpdf.php:1541
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())
Draws a regular polygon.
Definition: tcpdf.php:11986
$listordered
HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
Definition: tcpdf.php:772
$TextColor
Commands for text color.
Definition: tcpdf.php:484
$textindent
Text indentation value (used for text-indent CSS attribute).
Definition: tcpdf.php:1437
setHeaderMargin($hm=10)
Set header margin.
Definition: tcpdf.php:3322
setExtGState($gs)
Add an extgstate.
Definition: tcpdf.php:13780
resetColumns()
Remove columns and reset page margins.
Definition: tcpdf.php:21560
fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='')
Cleanup HTML code (requires HTML Tidy library).
Definition: tcpdf.php:15694
getAliasNumPage()
Returns the string alias used for the page number.
Definition: tcpdf.php:13580
_escapetext($s)
THIS METHOD IS DEPRECATED Format a text string.
Definition: tcpdf.php:10240
getCellPaddings()
Get the internal Cell padding array.
Definition: tcpdf.php:2686
$diskcache
If true enables disk caching.
Definition: tcpdf.php:1241
GetY()
Returns the ordinate of the current position.
Definition: tcpdf.php:7389
Footer()
This method is used to render the page footer.
Definition: tcpdf.php:3490
$bgcolor
Current background color.
Definition: tcpdf.php:802
$imgscale
Adjusting factor to convert pixels to user units.
Definition: tcpdf.php:592
$overline
Overlining flag.
Definition: tcpdf.php:448
$alpha
Alpha mode array.
Definition: tcpdf.php:1838
unhtmlentities($text_to_convert)
Reverse function for htmlentities.
Definition: tcpdf.php:10452
$bordermrk
Array used to store positions inside the pages buffer (keys are the page numbers).
Definition: tcpdf.php:1009
$numpages
Counts the number of pages.
Definition: tcpdf.php:1206
addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0, 0, 0))
Output a Table of Content Index (TOC).
Definition: tcpdf.php:21127
SetTextColorArray($color, $ret=false)
Defines the color used for text.
Definition: tcpdf.php:3877
$maxselcol
Maximum page and column selected.
Definition: tcpdf.php:1493
$thead
Table header content to be repeated on each new page.
Definition: tcpdf.php:1297
getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='')
Get the array that defines the signature appearance (page and rectangle coordinates).
Definition: tcpdf.php:13493
getFontsList()
Fill the list of available fonts ($this->fontlist).
Definition: tcpdf.php:4149
$PageBreakTrigger
Threshold used to trigger page breaks.
Definition: tcpdf.php:502
_destroy($destroyall=false, $preserve_objcopy=false)
Unset all class variables except the following critical variables.
Definition: tcpdf.php:7729
$state
Current document state.
Definition: tcpdf.php:228
SetDefaultMonospacedFont($font)
Defines the default monospaced font.
Definition: tcpdf.php:4711
getMargins()
Returns an array containing current margins:
Definition: tcpdf.php:15607
$num_columns
Number of colums.
Definition: tcpdf.php:1472
setEqualColumns($numcols=0, $width=0, $y='')
Set multiple columns of the same size.
Definition: tcpdf.php:21528
_beginpage($orientation='', $format='')
Initialize a new page.
Definition: tcpdf.php:9989
getCellMargins()
Get the internal Cell margin array.
Definition: tcpdf.php:2722
_generateencryptionkey()
Compute encryption key.
Definition: tcpdf.php:10748
$inxobj
Boolean value true when we are inside an XObject.
Definition: tcpdf.php:1569
$svgtext
SVG text.
Definition: tcpdf.php:1709
setHeaderFont($font)
Set header font.
Definition: tcpdf.php:10324
SetAbsXY($x, $y)
Set the absolute X and Y coordinates of the current pointer.
Definition: tcpdf.php:7503
Rotate($angle, $x='', $y='')
Rotate object.
Definition: tcpdf.php:11186
$re_space
Array of $re_spaces parts.
Definition: tcpdf.php:1353
_putresourcedict()
Output Resources Dictionary.
Definition: tcpdf.php:9339
getRTL()
Return the RTL status.
Definition: tcpdf.php:2396
_endpage()
Mark end of page.
Definition: tcpdf.php:10032
$transfmatrix_key
Current key for transformation matrix.
Definition: tcpdf.php:1128
SetDrawSpotColor($name, $tint=100)
Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
Definition: tcpdf.php:3780
SetTextSpotColor($name, $tint=100)
Defines the spot color used for text.
Definition: tcpdf.php:3804
setFooterFont($font)
Set footer font.
Definition: tcpdf.php:10344
fitBlock($w, $h, $x, $y, $fitonpage=false)
Set the block dimensions accounting for page breaks and page/column fitting.
Definition: tcpdf.php:6719
addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false)
Output anchor link.
Definition: tcpdf.php:10396
_dolinethroughw($x, $y, $w)
Line through for rectangular text area.
Definition: tcpdf.php:10109
getInternalPageNumberAliases($a='')
Return an array containing variations for the basic page number alias.
Definition: tcpdf.php:7784
getHeaderMargin()
Returns header margin in user units.
Definition: tcpdf.php:3332
$offsets
Array of object offsets.
Definition: tcpdf.php:204
Clip($x, $y, $w, $h)
Set a rectangular clipping area.
Definition: tcpdf.php:14313
$doc_creation_timestamp
Document creation date-time.
Definition: tcpdf.php:1808
setLIsymbol($symbol='!')
Set the default bullet to be used as LI bullet symbol.
Definition: tcpdf.php:19902
replaceRightShiftPageNumAliases($page, $aliases, $diff)
Replace right shift page number aliases with spaces to correct right alignment.
Definition: tcpdf.php:7823
getRawCharWidth($char)
Returns the length of the char in user unit for the current font.
Definition: tcpdf.php:4111
$svggradients
Array of SVG gradients.
Definition: tcpdf.php:1653
setImageScale($scale)
Set the adjusting factor to convert pixels to user units.
Definition: tcpdf.php:2479
SetBooklet($booklet=true, $inner=-1, $outer=-1)
Set the booklet mode for double-sided pages.
Definition: tcpdf.php:19946
getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0)
This method return the estimated number of lines for print a simple text string using Multicell() met...
Definition: tcpdf.php:6134
_outLine($x, $y)
Append a straight line segment from the current point to the point (x, y).
Definition: tcpdf.php:11425
$x
Current horizontal position in user unit for cell positioning.
Definition: tcpdf.php:344
getPageGroupAlias()
Return the alias for the total number of pages in the current page group.
Definition: tcpdf.php:13595
$fontlist
List of available fonts on filesystem.
Definition: tcpdf.php:760
$column_start_page
Starting page for columns.
Definition: tcpdf.php:1486
setSpotColor($type, $name, $tint=100)
Set the spot color for the specified type ('draw', 'fill', 'text').
Definition: tcpdf.php:3734
$cell_height_ratio
Default cell height ratio.
Definition: tcpdf.php:974
getAllInternalPageNumberAliases()
Return an array containing all internal page aliases.
Definition: tcpdf.php:7805
SVGPath($d, $style='')
Draws an SVG path.
Definition: tcpdf.php:23126
$numimages
Counts the number of pages.
Definition: tcpdf.php:1220
$epsmarker
String used to mark the beginning and end of EPS image blocks.
Definition: tcpdf.php:1114
$signature_data
Digital signature data.
Definition: tcpdf.php:1318
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)
Paints a coons patch mesh.
Definition: tcpdf.php:14221
$newpagegroup
Array of page numbers were a new page group was started (the page numbers are the keys of the array).
Definition: tcpdf.php:939
SetKeywords($keywords)
Associates keywords with the document, generally in the form 'keyword1 keyword2 .....
Definition: tcpdf.php:2918
_outCurveY($x1, $y1, $x3, $y3)
Append a cubic B�zier curve to the current path.
Definition: tcpdf.php:11491
$LayoutMode
Layout display mode.
Definition: tcpdf.php:526
SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true)
Sets the font used to print character strings.
Definition: tcpdf.php:4472
$header_margin
Minimum distance between header and top page margin.
Definition: tcpdf.php:625
write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false)
Print 2D Barcode.
Definition: tcpdf.php:15381
setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='')
Set the digital signature appearance (a cliccable rectangle area to get signature properties)
Definition: tcpdf.php:13459
$outlines
Outlines for bookmark.
Definition: tcpdf.php:888
GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false)
Returns the string length of an array of chars in user unit or an array of characters widths.
Definition: tcpdf.php:4050
getLastH()
Get the last cell height.
Definition: tcpdf.php:2468
$encrypted
IBoolean flag indicating whether document is protected.
Definition: tcpdf.php:851
$txt
Definition: error.php:10
v($data, $pos)
$cmd
Definition: sahs_server.php:35
echo;exit;}function LogoutNotification($SessionID){ global $ilDB;$q="SELECT session_id, data FROM usr_session WHERE expires > (\w+)\|/" PREG_SPLIT_NO_EMPTY PREG_SPLIT_DELIM_CAPTURE
if(!defined( 'K_TCPDF_EXTERNAL_CONFIG')) if(!defined( 'K_PATH_MAIN'))
Definition: tcpdf.php:156