ILIAS  Release_4_4_x_branch Revision 61816
 All Data Structures Namespaces Files Functions Variables Groups Pages
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.
146 if (!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 }
156 if (!defined('K_PATH_MAIN')) {
157  $this->Error('Unable to include configuration file.');
158 }
159 
160 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
161 
162 // TCPDF static font methods and data
163 require_once(dirname( __FILE__ ) . '/include/tcpdf_font_data.php');
164 // TCPDF static font methods and data
165 require_once(dirname( __FILE__ ) . '/include/tcpdf_fonts.php');
166 // TCPDF static color methods and data
167 require_once(dirname( __FILE__ ) . '/include/tcpdf_colors.php');
168 // TCPDF static image methods and data
169 require_once(dirname( __FILE__ ) . '/include/tcpdf_images.php');
170 // TCPDF static methods and data
171 require_once(dirname( __FILE__ ) . '/include/tcpdf_static.php');
172 
173 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
174 
184 class 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 
240  protected $CurOrientation;
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 
502  protected $PageBreakTrigger;
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 
568  protected $starting_page_number = 1;
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 
638  protected $original_lMargin;
639 
645  protected $original_rMargin;
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 
746  protected $default_table_columns = 4;
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 
872  protected $last_enc_key_c;
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 
1451  protected $start_transaction_y = 0;
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
2143  $pf = TCPDF_STATIC::getPageSizeFromFormat($format);
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  }
3212  ++$this->numpages;
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) {
3259  $page = $this->page;
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;
3545  $lasth = $this->lasth;
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;
3593  $lasth = $this->lasth;
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  }
4362  ++$this->numfonts;
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) {
4743  $page = $this->page;
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 {
4837  $page = $this->page;
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;
5042  $alpha = $this->alpha;
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;
5638  $w += $this->LineWidth;
5639  $h += $this->LineWidth;
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;
5652  $w -= $this->LineWidth;
5653  $h -= $this->LineWidth;
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('open_basedir') == '') AND (!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  }
7108  TCPDF_STATIC::set_mqr($mqr);
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
7241  $imga = TCPDF_STATIC::objclone($img);
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  }
8823  TCPDF_STATIC::set_mqr($mqr);
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 
10198  public function getDocModificationTimestamp() {
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)) {
10403  $page = $this->page;
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)) {
11563  $op = TCPDF_STATIC::getPathPaintOperator($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  }
11616  $op = TCPDF_STATIC::getPathPaintOperator($style);
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  }
11646  $op = TCPDF_STATIC::getPathPaintOperator($style);
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  }
11689  $op = TCPDF_STATIC::getPathPaintOperator($style);
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  }
11905  $op = TCPDF_STATIC::getPathPaintOperator($style);
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  }
12108  $op = TCPDF_STATIC::getPathPaintOperator($style);
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() {
12640  return $this->default_form_prop;
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() {
13936  return $this->cell_height_ratio;
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  }
14665  $op = TCPDF_STATIC::getPathPaintOperator($style);
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)) {
18647  $page = $this->page;
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': {
18697  ++$this->listnum;
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': {
18721  ++$this->listnum;
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': {
19561  --$this->listnum;
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': {
19589  --$this->listnum;
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  }
21539  if (TCPDF_STATIC::empty_string($y)) {
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 //============================================================+