ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
tcpdf.php
Go to the documentation of this file.
1 <?php
2 //============================================================+
3 // File name : tcpdf.php
4 // Version : 6.2.13
5 // Begin : 2002-08-03
6 // Last Update : 2015-06-18
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-2015 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 Extension, 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 
110 // TCPDF configuration
111 require_once(dirname(__FILE__).'/tcpdf_autoconfig.php');
112 // TCPDF static font methods and data
113 require_once(dirname(__FILE__).'/include/tcpdf_font_data.php');
114 // TCPDF static font methods and data
115 require_once(dirname(__FILE__).'/include/tcpdf_fonts.php');
116 // TCPDF static color methods and data
117 require_once(dirname(__FILE__).'/include/tcpdf_colors.php');
118 // TCPDF static image methods and data
119 require_once(dirname(__FILE__).'/include/tcpdf_images.php');
120 // TCPDF static methods and data
121 require_once(dirname(__FILE__).'/include/tcpdf_static.php');
122 
123 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
124 
134 class TCPDF {
135 
136  // Protected properties
137 
142  protected $page;
143 
148  protected $n;
149 
154  protected $offsets = array();
155 
160  protected $pageobjects = array();
161 
166  protected $buffer;
167 
172  protected $pages = array();
173 
178  protected $state;
179 
184  protected $compress;
185 
190  protected $CurOrientation;
191 
196  protected $pagedim = array();
197 
202  protected $k;
203 
208  protected $fwPt;
209 
214  protected $fhPt;
215 
220  protected $wPt;
221 
226  protected $hPt;
227 
232  protected $w;
233 
238  protected $h;
239 
244  protected $lMargin;
245 
250  protected $rMargin;
251 
256  protected $clMargin;
257 
262  protected $crMargin;
263 
268  protected $tMargin;
269 
274  protected $bMargin;
275 
281  protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
282 
288  protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
289 
294  protected $x;
295 
300  protected $y;
301 
306  protected $lasth;
307 
312  protected $LineWidth;
313 
318  protected $CoreFonts;
319 
324  protected $fonts = array();
325 
330  protected $FontFiles = array();
331 
336  protected $diffs = array();
337 
342  protected $images = array();
343 
348  protected $svg_tag_depth = 0;
349 
354  protected $PageAnnots = array();
355 
360  protected $links = array();
361 
366  protected $FontFamily;
367 
372  protected $FontStyle;
373 
379  protected $FontAscent;
380 
386  protected $FontDescent;
387 
392  protected $underline;
393 
398  protected $overline;
399 
404  protected $CurrentFont;
405 
410  protected $FontSizePt;
411 
416  protected $FontSize;
417 
422  protected $DrawColor;
423 
428  protected $FillColor;
429 
434  protected $TextColor;
435 
440  protected $ColorFlag;
441 
446  protected $AutoPageBreak;
447 
452  protected $PageBreakTrigger;
453 
458  protected $InHeader = false;
459 
464  protected $InFooter = false;
465 
470  protected $ZoomMode;
471 
476  protected $LayoutMode;
477 
482  protected $docinfounicode = true;
483 
488  protected $title = '';
489 
494  protected $subject = '';
495 
500  protected $author = '';
501 
506  protected $keywords = '';
507 
512  protected $creator = '';
513 
518  protected $starting_page_number = 1;
519 
526  protected $img_rb_x;
527 
534  protected $img_rb_y;
535 
542  protected $imgscale = 1;
543 
550  protected $isunicode = false;
551 
557  protected $PDFVersion = '1.7';
558 
563  protected $header_xobjid = false;
564 
569  protected $header_xobj_autoreset = false;
570 
575  protected $header_margin;
576 
581  protected $footer_margin;
582 
588  protected $original_lMargin;
589 
595  protected $original_rMargin;
596 
601  protected $header_font;
602 
607  protected $footer_font;
608 
613  protected $l;
614 
619  protected $barcode = false;
620 
625  protected $print_header = true;
626 
631  protected $print_footer = true;
632 
637  protected $header_logo = '';
638 
643  protected $header_logo_width = 30;
644 
649  protected $header_title = '';
650 
655  protected $header_string = '';
656 
662  protected $header_text_color = array(0,0,0);
663 
669  protected $header_line_color = array(0,0,0);
670 
676  protected $footer_text_color = array(0,0,0);
677 
683  protected $footer_line_color = array(0,0,0);
684 
690  protected $txtshadow = array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal');
691 
696  protected $default_table_columns = 4;
697 
698  // variables for html parser
699 
704  protected $HREF = array();
705 
710  protected $fontlist = array();
711 
716  protected $fgcolor;
717 
722  protected $listordered = array();
723 
728  protected $listcount = array();
729 
734  protected $listnum = 0;
735 
740  protected $listindent = 0;
741 
746  protected $listindentlevel = 0;
747 
752  protected $bgcolor;
753 
758  protected $tempfontsize = 10;
759 
764  protected $lispacer = '';
765 
771  protected $encoding = 'UTF-8';
772 
779 
785  protected $rtl = false;
786 
792  protected $tmprtl = false;
793 
794  // --- Variables used for document encryption:
795 
801  protected $encrypted;
802 
808  protected $encryptdata = array();
809 
815  protected $last_enc_key;
816 
822  protected $last_enc_key_c;
823 
829  protected $file_id;
830 
831  // --- bookmark ---
832 
838  protected $outlines = array();
839 
845  protected $OutlineRoot;
846 
847  // --- javascript and form ---
848 
854  protected $javascript = '';
855 
861  protected $n_js;
862 
868  protected $linethrough;
869 
875  protected $ur = array();
876 
882  protected $dpi = 72;
883 
889  protected $newpagegroup = array();
890 
896  protected $pagegroups = array();
897 
903  protected $currpagegroup = 0;
904 
910  protected $extgstates;
911 
917  protected $jpeg_quality;
918 
925 
932 
938  protected $PageMode;
939 
945  protected $gradients = array();
946 
952  protected $intmrk = array();
953 
959  protected $bordermrk = array();
960 
966  protected $emptypagemrk = array();
967 
973  protected $cntmrk = array();
974 
980  protected $footerpos = array();
981 
987  protected $footerlen = array();
988 
994  protected $newline = true;
995 
1001  protected $endlinex = 0;
1002 
1008  protected $linestyleWidth = '';
1009 
1015  protected $linestyleCap = '0 J';
1016 
1022  protected $linestyleJoin = '0 j';
1023 
1029  protected $linestyleDash = '[] 0 d';
1030 
1036  protected $openMarkedContent = false;
1037 
1043  protected $htmlvspace = 0;
1044 
1050  protected $spot_colors = array();
1051 
1057  protected $lisymbol = '';
1058 
1064  protected $epsmarker = 'x#!#EPS#!#x';
1065 
1071  protected $transfmatrix = array();
1072 
1078  protected $transfmatrix_key = 0;
1079 
1085  protected $booklet = false;
1086 
1092  protected $feps = 0.005;
1093 
1099  protected $tagvspaces = array();
1100 
1106  protected $customlistindent = -1;
1107 
1113  protected $opencell = true;
1114 
1120  protected $embeddedfiles = array();
1121 
1127  protected $premode = false;
1128 
1135  protected $transfmrk = array();
1136 
1142  protected $htmlLinkColorArray = array(0, 0, 255);
1143 
1149  protected $htmlLinkFontStyle = 'U';
1150 
1156  protected $numpages = 0;
1157 
1163  protected $pagelen = array();
1164 
1170  protected $numimages = 0;
1171 
1177  protected $imagekeys = array();
1178 
1184  protected $bufferlen = 0;
1185 
1191  protected $numfonts = 0;
1192 
1198  protected $fontkeys = array();
1199 
1205  protected $font_obj_ids = array();
1206 
1212  protected $pageopen = array();
1213 
1219  protected $default_monospaced_font = 'courier';
1220 
1226  protected $objcopy;
1227 
1233  protected $cache_file_length = array();
1234 
1240  protected $thead = '';
1241 
1247  protected $theadMargins = array();
1248 
1254  protected $sign = false;
1255 
1261  protected $signature_data = array();
1262 
1268  protected $signature_max_length = 11742;
1269 
1275  protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
1276 
1282  protected $empty_signature_appearance = array();
1283 
1289  protected $tsa_timestamp = false;
1290 
1296  protected $tsa_data = array();
1297 
1303  protected $re_spaces = '/[^\S\xa0]/';
1304 
1310  protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
1311 
1317  protected $sig_obj_id = 0;
1318 
1324  protected $page_obj_id = array();
1325 
1331  protected $form_obj_id = array();
1332 
1338  protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1339 
1345  protected $js_objects = array();
1346 
1352  protected $form_action = '';
1353 
1359  protected $form_enctype = 'application/x-www-form-urlencoded';
1360 
1366  protected $form_mode = 'post';
1367 
1373  protected $annotation_fonts = array();
1374 
1380  protected $radiobutton_groups = array();
1381 
1387  protected $radio_groups = array();
1388 
1394  protected $textindent = 0;
1395 
1402 
1408  protected $start_transaction_y = 0;
1409 
1415  protected $inthead = false;
1416 
1422  protected $columns = array();
1423 
1429  protected $num_columns = 1;
1430 
1436  protected $current_column = 0;
1437 
1443  protected $column_start_page = 0;
1444 
1450  protected $maxselcol = array('page' => 0, 'column' => 0);
1451 
1457  protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
1458 
1464  protected $textrendermode = 0;
1465 
1471  protected $textstrokewidth = 0;
1472 
1478  protected $strokecolor;
1479 
1485  protected $pdfunit = 'mm';
1486 
1491  protected $tocpage = false;
1492 
1498  protected $rasterize_vector_images = false;
1499 
1505  protected $font_subsetting = true;
1506 
1512  protected $default_graphic_vars = array();
1513 
1519  protected $xobjects = array();
1520 
1526  protected $inxobj = false;
1527 
1533  protected $xobjid = '';
1534 
1540  protected $font_stretching = 100;
1541 
1547  protected $font_spacing = 0;
1548 
1555  protected $page_regions = array();
1556 
1561  protected $check_page_regions = true;
1562 
1568  protected $pdflayers = array();
1569 
1575  protected $dests = array();
1576 
1582  protected $n_dests;
1583 
1589  protected $efnames = array();
1590 
1596  protected $svgdir = '';
1597 
1603  protected $svgunit = 'px';
1604 
1610  protected $svggradients = array();
1611 
1617  protected $svggradientid = 0;
1618 
1624  protected $svgdefsmode = false;
1625 
1631  protected $svgdefs = array();
1632 
1638  protected $svgclipmode = false;
1639 
1645  protected $svgclippaths = array();
1646 
1652  protected $svgcliptm = array();
1653 
1659  protected $svgclipid = 0;
1660 
1666  protected $svgtext = '';
1667 
1673  protected $svgtextmode = array();
1674 
1680  protected $svgstyles = array(array(
1681  'alignment-baseline' => 'auto',
1682  'baseline-shift' => 'baseline',
1683  'clip' => 'auto',
1684  'clip-path' => 'none',
1685  'clip-rule' => 'nonzero',
1686  'color' => 'black',
1687  'color-interpolation' => 'sRGB',
1688  'color-interpolation-filters' => 'linearRGB',
1689  'color-profile' => 'auto',
1690  'color-rendering' => 'auto',
1691  'cursor' => 'auto',
1692  'direction' => 'ltr',
1693  'display' => 'inline',
1694  'dominant-baseline' => 'auto',
1695  'enable-background' => 'accumulate',
1696  'fill' => 'black',
1697  'fill-opacity' => 1,
1698  'fill-rule' => 'nonzero',
1699  'filter' => 'none',
1700  'flood-color' => 'black',
1701  'flood-opacity' => 1,
1702  'font' => '',
1703  'font-family' => 'helvetica',
1704  'font-size' => 'medium',
1705  'font-size-adjust' => 'none',
1706  'font-stretch' => 'normal',
1707  'font-style' => 'normal',
1708  'font-variant' => 'normal',
1709  'font-weight' => 'normal',
1710  'glyph-orientation-horizontal' => '0deg',
1711  'glyph-orientation-vertical' => 'auto',
1712  'image-rendering' => 'auto',
1713  'kerning' => 'auto',
1714  'letter-spacing' => 'normal',
1715  'lighting-color' => 'white',
1716  'marker' => '',
1717  'marker-end' => 'none',
1718  'marker-mid' => 'none',
1719  'marker-start' => 'none',
1720  'mask' => 'none',
1721  'opacity' => 1,
1722  'overflow' => 'auto',
1723  'pointer-events' => 'visiblePainted',
1724  'shape-rendering' => 'auto',
1725  'stop-color' => 'black',
1726  'stop-opacity' => 1,
1727  'stroke' => 'none',
1728  'stroke-dasharray' => 'none',
1729  'stroke-dashoffset' => 0,
1730  'stroke-linecap' => 'butt',
1731  'stroke-linejoin' => 'miter',
1732  'stroke-miterlimit' => 4,
1733  'stroke-opacity' => 1,
1734  'stroke-width' => 1,
1735  'text-anchor' => 'start',
1736  'text-decoration' => 'none',
1737  'text-rendering' => 'auto',
1738  'unicode-bidi' => 'normal',
1739  'visibility' => 'visible',
1740  'word-spacing' => 'normal',
1741  'writing-mode' => 'lr-tb',
1742  'text-color' => 'black',
1743  'transfmatrix' => array(1, 0, 0, 1, 0, 0)
1744  ));
1745 
1751  protected $force_srgb = false;
1752 
1758  protected $pdfa_mode = false;
1759 
1766 
1773 
1779  protected $custom_xmp = '';
1780 
1787  protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0);
1788 
1795  protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false);
1796 
1802  protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
1803 
1809  protected $tcpdflink = true;
1810 
1816  protected $gdgammacache = array();
1817 
1818  //------------------------------------------------------------
1819  // METHODS
1820  //------------------------------------------------------------
1821 
1838  public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) {
1839  /* Set internal character encoding to ASCII */
1840  if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1841  $this->internal_encoding = mb_internal_encoding();
1842  mb_internal_encoding('ASCII');
1843  }
1844  // set file ID for trailer
1845  $serformat = (is_array($format) ? json_encode($format) : $format);
1846  $this->file_id = md5(TCPDF_STATIC::getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding));
1847  $this->font_obj_ids = array();
1848  $this->page_obj_id = array();
1849  $this->form_obj_id = array();
1850  // set pdf/a mode
1851  $this->pdfa_mode = $pdfa;
1852  $this->force_srgb = false;
1853  // set language direction
1854  $this->rtl = false;
1855  $this->tmprtl = false;
1856  // some checks
1857  $this->_dochecks();
1858  // initialization of properties
1859  $this->isunicode = $unicode;
1860  $this->page = 0;
1861  $this->transfmrk[0] = array();
1862  $this->pagedim = array();
1863  $this->n = 2;
1864  $this->buffer = '';
1865  $this->pages = array();
1866  $this->state = 0;
1867  $this->fonts = array();
1868  $this->FontFiles = array();
1869  $this->diffs = array();
1870  $this->images = array();
1871  $this->links = array();
1872  $this->gradients = array();
1873  $this->InFooter = false;
1874  $this->lasth = 0;
1875  $this->FontFamily = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
1876  $this->FontStyle = '';
1877  $this->FontSizePt = 12;
1878  $this->underline = false;
1879  $this->overline = false;
1880  $this->linethrough = false;
1881  $this->DrawColor = '0 G';
1882  $this->FillColor = '0 g';
1883  $this->TextColor = '0 g';
1884  $this->ColorFlag = false;
1885  $this->pdflayers = array();
1886  // encryption values
1887  $this->encrypted = false;
1888  $this->last_enc_key = '';
1889  // standard Unicode fonts
1890  $this->CoreFonts = array(
1891  'courier'=>'Courier',
1892  'courierB'=>'Courier-Bold',
1893  'courierI'=>'Courier-Oblique',
1894  'courierBI'=>'Courier-BoldOblique',
1895  'helvetica'=>'Helvetica',
1896  'helveticaB'=>'Helvetica-Bold',
1897  'helveticaI'=>'Helvetica-Oblique',
1898  'helveticaBI'=>'Helvetica-BoldOblique',
1899  'times'=>'Times-Roman',
1900  'timesB'=>'Times-Bold',
1901  'timesI'=>'Times-Italic',
1902  'timesBI'=>'Times-BoldItalic',
1903  'symbol'=>'Symbol',
1904  'zapfdingbats'=>'ZapfDingbats'
1905  );
1906  // set scale factor
1907  $this->setPageUnit($unit);
1908  // set page format and orientation
1909  $this->setPageFormat($format, $orientation);
1910  // page margins (1 cm)
1911  $margin = 28.35 / $this->k;
1912  $this->SetMargins($margin, $margin);
1913  $this->clMargin = $this->lMargin;
1914  $this->crMargin = $this->rMargin;
1915  // internal cell padding
1916  $cpadding = $margin / 10;
1917  $this->setCellPaddings($cpadding, 0, $cpadding, 0);
1918  // cell margins
1919  $this->setCellMargins(0, 0, 0, 0);
1920  // line width (0.2 mm)
1921  $this->LineWidth = 0.57 / $this->k;
1922  $this->linestyleWidth = sprintf('%F w', ($this->LineWidth * $this->k));
1923  $this->linestyleCap = '0 J';
1924  $this->linestyleJoin = '0 j';
1925  $this->linestyleDash = '[] 0 d';
1926  // automatic page break
1927  $this->SetAutoPageBreak(true, (2 * $margin));
1928  // full width display mode
1929  $this->SetDisplayMode('fullwidth');
1930  // compression
1931  $this->SetCompression();
1932  // set default PDF version number
1933  $this->setPDFVersion();
1934  $this->tcpdflink = true;
1935  $this->encoding = $encoding;
1936  $this->HREF = array();
1937  $this->getFontsList();
1938  $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1939  $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0);
1940  $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1941  $this->extgstates = array();
1942  $this->setTextShadow();
1943  // signature
1944  $this->sign = false;
1945  $this->tsa_timestamp = false;
1946  $this->tsa_data = array();
1947  $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0', 'name' => 'Signature');
1948  $this->empty_signature_appearance = array();
1949  // user's rights
1950  $this->ur['enabled'] = false;
1951  $this->ur['document'] = '/FullSave';
1952  $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
1953  $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1954  $this->ur['signature'] = '/Modify';
1955  $this->ur['ef'] = '/Create/Delete/Modify/Import';
1956  $this->ur['formex'] = '';
1957  // set default JPEG quality
1958  $this->jpeg_quality = 75;
1959  // initialize some settings
1960  TCPDF_FONTS::utf8Bidi(array(''), '', false, $this->isunicode, $this->CurrentFont);
1961  // set default font
1962  $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
1963  $this->setHeaderFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt));
1964  $this->setFooterFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt));
1965  // check if PCRE Unicode support is enabled
1966  if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
1967  // PCRE unicode support is turned ON
1968  // \s : any whitespace character
1969  // \p{Z} : any separator
1970  // \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words.
1971  // \xa0 : Unicode Character 'NO-BREAK SPACE' (U+00A0)
1972  //$this->setSpacesRE('/(?!\xa0)[\s\p{Z}\p{Lo}]/u');
1973  $this->setSpacesRE('/(?!\xa0)[\s\p{Z}]/u');
1974  } else {
1975  // PCRE unicode support is turned OFF
1976  $this->setSpacesRE('/[^\S\xa0]/');
1977  }
1978  $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1979  // set document creation and modification timestamp
1980  $this->doc_creation_timestamp = time();
1981  $this->doc_modification_timestamp = $this->doc_creation_timestamp;
1982  // get default graphic vars
1983  $this->default_graphic_vars = $this->getGraphicVars();
1984  $this->header_xobj_autoreset = false;
1985  $this->custom_xmp = '';
1986  // Call cleanup method after script execution finishes or exit() is called.
1987  // NOTE: This will not be executed if the process is killed with a SIGTERM or SIGKILL signal.
1988  register_shutdown_function(array($this, '_destroy'), true);
1989  }
1990 
1996  public function __destruct() {
1997  // restore internal encoding
1998  if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1999  mb_internal_encoding($this->internal_encoding);
2000  }
2001  // cleanup
2002  $this->_destroy(true);
2003  }
2004 
2011  public function setPageUnit($unit) {
2012  $unit = strtolower($unit);
2013  //Set scale factor
2014  switch ($unit) {
2015  // points
2016  case 'px':
2017  case 'pt': {
2018  $this->k = 1;
2019  break;
2020  }
2021  // millimeters
2022  case 'mm': {
2023  $this->k = $this->dpi / 25.4;
2024  break;
2025  }
2026  // centimeters
2027  case 'cm': {
2028  $this->k = $this->dpi / 2.54;
2029  break;
2030  }
2031  // inches
2032  case 'in': {
2033  $this->k = $this->dpi;
2034  break;
2035  }
2036  // unsupported unit
2037  default : {
2038  $this->Error('Incorrect unit: '.$unit);
2039  break;
2040  }
2041  }
2042  $this->pdfunit = $unit;
2043  if (isset($this->CurOrientation)) {
2044  $this->setPageOrientation($this->CurOrientation);
2045  }
2046  }
2047 
2103  protected function setPageFormat($format, $orientation='P') {
2104  if (!empty($format) AND isset($this->pagedim[$this->page])) {
2105  // remove inherited values
2106  unset($this->pagedim[$this->page]);
2107  }
2108  if (is_string($format)) {
2109  // get page measures from format name
2110  $pf = TCPDF_STATIC::getPageSizeFromFormat($format);
2111  $this->fwPt = $pf[0];
2112  $this->fhPt = $pf[1];
2113  } else {
2114  // the boundaries of the physical medium on which the page shall be displayed or printed
2115  if (isset($format['MediaBox'])) {
2116  $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);
2117  $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k);
2118  $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k);
2119  } else {
2120  if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) {
2121  $pf = array(($format[0] * $this->k), ($format[1] * $this->k));
2122  } else {
2123  if (!isset($format['format'])) {
2124  // default value
2125  $format['format'] = 'A4';
2126  }
2127  $pf = TCPDF_STATIC::getPageSizeFromFormat($format['format']);
2128  }
2129  $this->fwPt = $pf[0];
2130  $this->fhPt = $pf[1];
2131  $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2132  }
2133  // the visible region of default user space
2134  if (isset($format['CropBox'])) {
2135  $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);
2136  }
2137  // the region to which the contents of the page shall be clipped when output in a production environment
2138  if (isset($format['BleedBox'])) {
2139  $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);
2140  }
2141  // the intended dimensions of the finished page after trimming
2142  if (isset($format['TrimBox'])) {
2143  $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);
2144  }
2145  // the page's meaningful content (including potential white space)
2146  if (isset($format['ArtBox'])) {
2147  $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);
2148  }
2149  // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
2150  if (isset($format['BoxColorInfo'])) {
2151  $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo'];
2152  }
2153  if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) {
2154  // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2155  $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']);
2156  }
2157  if (isset($format['PZ'])) {
2158  // The page's preferred zoom (magnification) factor
2159  $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']);
2160  }
2161  if (isset($format['trans'])) {
2162  // The style and duration of the visual transition to use when moving from another page to the given page during a presentation
2163  if (isset($format['trans']['Dur'])) {
2164  // The page's display duration
2165  $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']);
2166  }
2167  $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
2168  if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
2169  // The transition style that shall be used when moving to this page from another during a presentation
2170  $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S'];
2171  $valid_effect = array('Split', 'Blinds');
2172  $valid_vals = array('H', 'V');
2173  if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
2174  $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm'];
2175  }
2176  $valid_effect = array('Split', 'Box', 'Fly');
2177  $valid_vals = array('I', 'O');
2178  if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
2179  $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M'];
2180  }
2181  $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
2182  if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
2183  if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
2184  OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
2185  OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
2186  $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']);
2187  }
2188  }
2189  if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
2190  $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']);
2191  }
2192  if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
2193  $this->pagedim[$this->page]['trans']['B'] = 'true';
2194  }
2195  } else {
2196  $this->pagedim[$this->page]['trans']['S'] = 'R';
2197  }
2198  if (isset($format['trans']['D'])) {
2199  // The duration of the transition effect, in seconds
2200  $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']);
2201  } else {
2202  $this->pagedim[$this->page]['trans']['D'] = 1;
2203  }
2204  }
2205  }
2206  $this->setPageOrientation($orientation);
2207  }
2208 
2217  public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
2218  if (!isset($this->pagedim[$this->page]['MediaBox'])) {
2219  // the boundaries of the physical medium on which the page shall be displayed or printed
2220  $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2221  }
2222  if (!isset($this->pagedim[$this->page]['CropBox'])) {
2223  // the visible region of default user space
2224  $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);
2225  }
2226  if (!isset($this->pagedim[$this->page]['BleedBox'])) {
2227  // the region to which the contents of the page shall be clipped when output in a production environment
2228  $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);
2229  }
2230  if (!isset($this->pagedim[$this->page]['TrimBox'])) {
2231  // the intended dimensions of the finished page after trimming
2232  $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);
2233  }
2234  if (!isset($this->pagedim[$this->page]['ArtBox'])) {
2235  // the page's meaningful content (including potential white space)
2236  $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);
2237  }
2238  if (!isset($this->pagedim[$this->page]['Rotate'])) {
2239  // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2240  $this->pagedim[$this->page]['Rotate'] = 0;
2241  }
2242  if (!isset($this->pagedim[$this->page]['PZ'])) {
2243  // The page's preferred zoom (magnification) factor
2244  $this->pagedim[$this->page]['PZ'] = 1;
2245  }
2246  if ($this->fwPt > $this->fhPt) {
2247  // landscape
2248  $default_orientation = 'L';
2249  } else {
2250  // portrait
2251  $default_orientation = 'P';
2252  }
2253  $valid_orientations = array('P', 'L');
2254  if (empty($orientation)) {
2255  $orientation = $default_orientation;
2256  } else {
2257  $orientation = strtoupper($orientation[0]);
2258  }
2259  if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
2260  $this->CurOrientation = $orientation;
2261  $this->wPt = $this->fhPt;
2262  $this->hPt = $this->fwPt;
2263  } else {
2264  $this->CurOrientation = $default_orientation;
2265  $this->wPt = $this->fwPt;
2266  $this->hPt = $this->fhPt;
2267  }
2268  if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
2269  // swap X and Y coordinates (change page orientation)
2270  $this->pagedim = TCPDF_STATIC::swapPageBoxCoordinates($this->page, $this->pagedim);
2271  }
2272  $this->w = ($this->wPt / $this->k);
2273  $this->h = ($this->hPt / $this->k);
2274  if (TCPDF_STATIC::empty_string($autopagebreak)) {
2275  if (isset($this->AutoPageBreak)) {
2276  $autopagebreak = $this->AutoPageBreak;
2277  } else {
2278  $autopagebreak = true;
2279  }
2280  }
2281  if (TCPDF_STATIC::empty_string($bottommargin)) {
2282  if (isset($this->bMargin)) {
2283  $bottommargin = $this->bMargin;
2284  } else {
2285  // default value = 2 cm
2286  $bottommargin = 2 * 28.35 / $this->k;
2287  }
2288  }
2289  $this->SetAutoPageBreak($autopagebreak, $bottommargin);
2290  // store page dimensions
2291  $this->pagedim[$this->page]['w'] = $this->wPt;
2292  $this->pagedim[$this->page]['h'] = $this->hPt;
2293  $this->pagedim[$this->page]['wk'] = $this->w;
2294  $this->pagedim[$this->page]['hk'] = $this->h;
2295  $this->pagedim[$this->page]['tm'] = $this->tMargin;
2296  $this->pagedim[$this->page]['bm'] = $bottommargin;
2297  $this->pagedim[$this->page]['lm'] = $this->lMargin;
2298  $this->pagedim[$this->page]['rm'] = $this->rMargin;
2299  $this->pagedim[$this->page]['pb'] = $autopagebreak;
2300  $this->pagedim[$this->page]['or'] = $this->CurOrientation;
2301  $this->pagedim[$this->page]['olm'] = $this->original_lMargin;
2302  $this->pagedim[$this->page]['orm'] = $this->original_rMargin;
2303  }
2304 
2323  public function setSpacesRE($re='/[^\S\xa0]/') {
2324  $this->re_spaces = $re;
2325  $re_parts = explode('/', $re);
2326  // get pattern parts
2327  $this->re_space = array();
2328  if (isset($re_parts[1]) AND !empty($re_parts[1])) {
2329  $this->re_space['p'] = $re_parts[1];
2330  } else {
2331  $this->re_space['p'] = '[\s]';
2332  }
2333  // set pattern modifiers
2334  if (isset($re_parts[2]) AND !empty($re_parts[2])) {
2335  $this->re_space['m'] = $re_parts[2];
2336  } else {
2337  $this->re_space['m'] = '';
2338  }
2339  }
2340 
2348  public function setRTL($enable, $resetx=true) {
2349  $enable = $enable ? true : false;
2350  $resetx = ($resetx AND ($enable != $this->rtl));
2351  $this->rtl = $enable;
2352  $this->tmprtl = false;
2353  if ($resetx) {
2354  $this->Ln(0);
2355  }
2356  }
2357 
2364  public function getRTL() {
2365  return $this->rtl;
2366  }
2367 
2374  public function setTempRTL($mode) {
2375  $newmode = false;
2376  switch (strtoupper($mode)) {
2377  case 'LTR':
2378  case 'L': {
2379  if ($this->rtl) {
2380  $newmode = 'L';
2381  }
2382  break;
2383  }
2384  case 'RTL':
2385  case 'R': {
2386  if (!$this->rtl) {
2387  $newmode = 'R';
2388  }
2389  break;
2390  }
2391  case false:
2392  default: {
2393  $newmode = false;
2394  break;
2395  }
2396  }
2397  $this->tmprtl = $newmode;
2398  }
2399 
2406  public function isRTLTextDir() {
2407  return ($this->rtl OR ($this->tmprtl == 'R'));
2408  }
2409 
2417  public function setLastH($h) {
2418  $this->lasth = $h;
2419  }
2420 
2427  public function getCellHeight($fontsize, $padding=TRUE) {
2428  $height = ($fontsize * $this->cell_height_ratio);
2429  if ($padding) {
2430  $height += ($this->cell_padding['T'] + $this->cell_padding['B']);
2431  }
2432  return round($height, 6);
2433  }
2434 
2440  public function resetLastH() {
2441  $this->lasth = $this->getCellHeight($this->FontSize);
2442  }
2443 
2450  public function getLastH() {
2451  return $this->lasth;
2452  }
2453 
2461  public function setImageScale($scale) {
2462  $this->imgscale = $scale;
2463  }
2464 
2472  public function getImageScale() {
2473  return $this->imgscale;
2474  }
2475 
2485  public function getPageDimensions($pagenum='') {
2486  if (empty($pagenum)) {
2487  $pagenum = $this->page;
2488  }
2489  return $this->pagedim[$pagenum];
2490  }
2491 
2501  public function getPageWidth($pagenum='') {
2502  if (empty($pagenum)) {
2503  return $this->w;
2504  }
2505  return $this->pagedim[$pagenum]['w'];
2506  }
2507 
2517  public function getPageHeight($pagenum='') {
2518  if (empty($pagenum)) {
2519  return $this->h;
2520  }
2521  return $this->pagedim[$pagenum]['h'];
2522  }
2523 
2533  public function getBreakMargin($pagenum='') {
2534  if (empty($pagenum)) {
2535  return $this->bMargin;
2536  }
2537  return $this->pagedim[$pagenum]['bm'];
2538  }
2539 
2547  public function getScaleFactor() {
2548  return $this->k;
2549  }
2550 
2561  public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
2562  //Set left, top and right margins
2563  $this->lMargin = $left;
2564  $this->tMargin = $top;
2565  if ($right == -1) {
2566  $right = $left;
2567  }
2568  $this->rMargin = $right;
2569  if ($keepmargins) {
2570  // overwrite original values
2571  $this->original_lMargin = $this->lMargin;
2572  $this->original_rMargin = $this->rMargin;
2573  }
2574  }
2575 
2583  public function SetLeftMargin($margin) {
2584  //Set left margin
2585  $this->lMargin = $margin;
2586  if (($this->page > 0) AND ($this->x < $margin)) {
2587  $this->x = $margin;
2588  }
2589  }
2590 
2598  public function SetTopMargin($margin) {
2599  //Set top margin
2600  $this->tMargin = $margin;
2601  if (($this->page > 0) AND ($this->y < $margin)) {
2602  $this->y = $margin;
2603  }
2604  }
2605 
2613  public function SetRightMargin($margin) {
2614  $this->rMargin = $margin;
2615  if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
2616  $this->x = $this->w - $margin;
2617  }
2618  }
2619 
2627  public function SetCellPadding($pad) {
2628  if ($pad >= 0) {
2629  $this->cell_padding['L'] = $pad;
2630  $this->cell_padding['T'] = $pad;
2631  $this->cell_padding['R'] = $pad;
2632  $this->cell_padding['B'] = $pad;
2633  }
2634  }
2635 
2646  public function setCellPaddings($left='', $top='', $right='', $bottom='') {
2647  if (($left !== '') AND ($left >= 0)) {
2648  $this->cell_padding['L'] = $left;
2649  }
2650  if (($top !== '') AND ($top >= 0)) {
2651  $this->cell_padding['T'] = $top;
2652  }
2653  if (($right !== '') AND ($right >= 0)) {
2654  $this->cell_padding['R'] = $right;
2655  }
2656  if (($bottom !== '') AND ($bottom >= 0)) {
2657  $this->cell_padding['B'] = $bottom;
2658  }
2659  }
2660 
2668  public function getCellPaddings() {
2669  return $this->cell_padding;
2670  }
2671 
2682  public function setCellMargins($left='', $top='', $right='', $bottom='') {
2683  if (($left !== '') AND ($left >= 0)) {
2684  $this->cell_margin['L'] = $left;
2685  }
2686  if (($top !== '') AND ($top >= 0)) {
2687  $this->cell_margin['T'] = $top;
2688  }
2689  if (($right !== '') AND ($right >= 0)) {
2690  $this->cell_margin['R'] = $right;
2691  }
2692  if (($bottom !== '') AND ($bottom >= 0)) {
2693  $this->cell_margin['B'] = $bottom;
2694  }
2695  }
2696 
2704  public function getCellMargins() {
2705  return $this->cell_margin;
2706  }
2707 
2715  protected function adjustCellPadding($brd=0) {
2716  if (empty($brd)) {
2717  return;
2718  }
2719  if (is_string($brd)) {
2720  // convert string to array
2721  $slen = strlen($brd);
2722  $newbrd = array();
2723  for ($i = 0; $i < $slen; ++$i) {
2724  $newbrd[$brd[$i]] = true;
2725  }
2726  $brd = $newbrd;
2727  } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) {
2728  $brd = array('LRTB' => true);
2729  }
2730  if (!is_array($brd)) {
2731  return;
2732  }
2733  // store current cell padding
2734  $cp = $this->cell_padding;
2735  // select border mode
2736  if (isset($brd['mode'])) {
2737  $mode = $brd['mode'];
2738  unset($brd['mode']);
2739  } else {
2740  $mode = 'normal';
2741  }
2742  // process borders
2743  foreach ($brd as $border => $style) {
2744  $line_width = $this->LineWidth;
2745  if (is_array($style) AND isset($style['width'])) {
2746  // get border width
2747  $line_width = $style['width'];
2748  }
2749  $adj = 0; // line width inside the cell
2750  switch ($mode) {
2751  case 'ext': {
2752  $adj = 0;
2753  break;
2754  }
2755  case 'int': {
2756  $adj = $line_width;
2757  break;
2758  }
2759  case 'normal':
2760  default: {
2761  $adj = ($line_width / 2);
2762  break;
2763  }
2764  }
2765  // correct internal cell padding if required to avoid overlap between text and lines
2766  if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) {
2767  $this->cell_padding['T'] = $adj;
2768  }
2769  if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) {
2770  $this->cell_padding['R'] = $adj;
2771  }
2772  if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) {
2773  $this->cell_padding['B'] = $adj;
2774  }
2775  if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) {
2776  $this->cell_padding['L'] = $adj;
2777  }
2778  }
2779  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']));
2780  }
2781 
2790  public function SetAutoPageBreak($auto, $margin=0) {
2791  $this->AutoPageBreak = $auto ? true : false;
2792  $this->bMargin = $margin;
2793  $this->PageBreakTrigger = $this->h - $margin;
2794  }
2795 
2802  public function getAutoPageBreak() {
2803  return $this->AutoPageBreak;
2804  }
2805 
2814  public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
2815  if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
2816  $this->ZoomMode = $zoom;
2817  } else {
2818  $this->Error('Incorrect zoom display mode: '.$zoom);
2819  }
2820  $this->LayoutMode = TCPDF_STATIC::getPageLayoutMode($layout);
2821  $this->PageMode = TCPDF_STATIC::getPageMode($mode);
2822  }
2823 
2831  public function SetCompression($compress=true) {
2832  if (function_exists('gzcompress')) {
2833  $this->compress = $compress ? true : false;
2834  } else {
2835  $this->compress = false;
2836  }
2837  }
2838 
2845  public function setSRGBmode($mode=false) {
2846  $this->force_srgb = $mode ? true : false;
2847  }
2848 
2856  public function SetDocInfoUnicode($unicode=true) {
2857  $this->docinfounicode = $unicode ? true : false;
2858  }
2859 
2867  public function SetTitle($title) {
2868  $this->title = $title;
2869  }
2870 
2878  public function SetSubject($subject) {
2879  $this->subject = $subject;
2880  }
2881 
2889  public function SetAuthor($author) {
2890  $this->author = $author;
2891  }
2892 
2900  public function SetKeywords($keywords) {
2901  $this->keywords = $keywords;
2902  }
2903 
2911  public function SetCreator($creator) {
2912  $this->creator = $creator;
2913  }
2914 
2921  public function Error($msg) {
2922  // unset all class variables
2923  $this->_destroy(true);
2924  if (defined('K_TCPDF_THROW_EXCEPTION_ERROR') AND !K_TCPDF_THROW_EXCEPTION_ERROR) {
2925  die('<strong>TCPDF ERROR: </strong>'.$msg);
2926  } else {
2927  throw new Exception('TCPDF ERROR: '.$msg);
2928  }
2929  }
2930 
2939  public function Open() {
2940  $this->state = 1;
2941  }
2942 
2951  public function Close() {
2952  if ($this->state == 3) {
2953  return;
2954  }
2955  if ($this->page == 0) {
2956  $this->AddPage();
2957  }
2958  $this->endLayer();
2959  if ($this->tcpdflink) {
2960  // save current graphic settings
2961  $gvars = $this->getGraphicVars();
2962  $this->setEqualColumns();
2963  $this->lastpage(true);
2964  $this->SetAutoPageBreak(false);
2965  $this->x = 0;
2966  $this->y = $this->h - (1 / $this->k);
2967  $this->lMargin = 0;
2968  $this->_outSaveGraphicsState();
2969  $font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
2970  $this->SetFont($font, '', 1);
2971  $this->setTextRenderingMode(0, false, false);
2972  $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";
2973  $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
2974  $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
2975  $this->_outRestoreGraphicsState();
2976  // restore graphic settings
2977  $this->setGraphicVars($gvars);
2978  }
2979  // close page
2980  $this->endPage();
2981  // close document
2982  $this->_enddoc();
2983  // unset all class variables (except critical ones)
2984  $this->_destroy(false);
2985  }
2986 
2995  public function setPage($pnum, $resetmargins=false) {
2996  if (($pnum == $this->page) AND ($this->state == 2)) {
2997  return;
2998  }
2999  if (($pnum > 0) AND ($pnum <= $this->numpages)) {
3000  $this->state = 2;
3001  // save current graphic settings
3002  //$gvars = $this->getGraphicVars();
3003  $oldpage = $this->page;
3004  $this->page = $pnum;
3005  $this->wPt = $this->pagedim[$this->page]['w'];
3006  $this->hPt = $this->pagedim[$this->page]['h'];
3007  $this->w = $this->pagedim[$this->page]['wk'];
3008  $this->h = $this->pagedim[$this->page]['hk'];
3009  $this->tMargin = $this->pagedim[$this->page]['tm'];
3010  $this->bMargin = $this->pagedim[$this->page]['bm'];
3011  $this->original_lMargin = $this->pagedim[$this->page]['olm'];
3012  $this->original_rMargin = $this->pagedim[$this->page]['orm'];
3013  $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
3014  $this->CurOrientation = $this->pagedim[$this->page]['or'];
3015  $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
3016  // restore graphic settings
3017  //$this->setGraphicVars($gvars);
3018  if ($resetmargins) {
3019  $this->lMargin = $this->pagedim[$this->page]['olm'];
3020  $this->rMargin = $this->pagedim[$this->page]['orm'];
3021  $this->SetY($this->tMargin);
3022  } else {
3023  // account for booklet mode
3024  if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3025  $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
3026  $this->lMargin += $deltam;
3027  $this->rMargin -= $deltam;
3028  }
3029  }
3030  } else {
3031  $this->Error('Wrong page number on setPage() function: '.$pnum);
3032  }
3033  }
3034 
3042  public function lastPage($resetmargins=false) {
3043  $this->setPage($this->getNumPages(), $resetmargins);
3044  }
3045 
3053  public function getPage() {
3054  return $this->page;
3055  }
3056 
3064  public function getNumPages() {
3065  return $this->numpages;
3066  }
3067 
3077  public function addTOCPage($orientation='', $format='', $keepmargins=false) {
3078  $this->AddPage($orientation, $format, $keepmargins, true);
3079  }
3080 
3087  public function endTOCPage() {
3088  $this->endPage(true);
3089  }
3090 
3102  public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
3103  if ($this->inxobj) {
3104  // we are inside an XObject template
3105  return;
3106  }
3107  if (!isset($this->original_lMargin) OR $keepmargins) {
3108  $this->original_lMargin = $this->lMargin;
3109  }
3110  if (!isset($this->original_rMargin) OR $keepmargins) {
3111  $this->original_rMargin = $this->rMargin;
3112  }
3113  // terminate previous page
3114  $this->endPage();
3115  // start new page
3116  $this->startPage($orientation, $format, $tocpage);
3117  }
3118 
3126  public function endPage($tocpage=false) {
3127  // check if page is already closed
3128  if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
3129  return;
3130  }
3131  // print page footer
3132  $this->setFooter();
3133  // close page
3134  $this->_endpage();
3135  // mark page as closed
3136  $this->pageopen[$this->page] = false;
3137  if ($tocpage) {
3138  $this->tocpage = false;
3139  }
3140  }
3141 
3152  public function startPage($orientation='', $format='', $tocpage=false) {
3153  if ($tocpage) {
3154  $this->tocpage = true;
3155  }
3156  // move page numbers of documents to be attached
3157  if ($this->tocpage) {
3158  // move reference to unexistent pages (used for page attachments)
3159  // adjust outlines
3160  $tmpoutlines = $this->outlines;
3161  foreach ($tmpoutlines as $key => $outline) {
3162  if (!$outline['f'] AND ($outline['p'] > $this->numpages)) {
3163  $this->outlines[$key]['p'] = ($outline['p'] + 1);
3164  }
3165  }
3166  // adjust dests
3167  $tmpdests = $this->dests;
3168  foreach ($tmpdests as $key => $dest) {
3169  if (!$dest['f'] AND ($dest['p'] > $this->numpages)) {
3170  $this->dests[$key]['p'] = ($dest['p'] + 1);
3171  }
3172  }
3173  // adjust links
3174  $tmplinks = $this->links;
3175  foreach ($tmplinks as $key => $link) {
3176  if (!$link['f'] AND ($link['p'] > $this->numpages)) {
3177  $this->links[$key]['p'] = ($link['p'] + 1);
3178  }
3179  }
3180  }
3181  if ($this->numpages > $this->page) {
3182  // this page has been already added
3183  $this->setPage($this->page + 1);
3184  $this->SetY($this->tMargin);
3185  return;
3186  }
3187  // start a new page
3188  if ($this->state == 0) {
3189  $this->Open();
3190  }
3191  ++$this->numpages;
3192  $this->swapMargins($this->booklet);
3193  // save current graphic settings
3194  $gvars = $this->getGraphicVars();
3195  // start new page
3196  $this->_beginpage($orientation, $format);
3197  // mark page as open
3198  $this->pageopen[$this->page] = true;
3199  // restore graphic settings
3200  $this->setGraphicVars($gvars);
3201  // mark this point
3202  $this->setPageMark();
3203  // print page header
3204  $this->setHeader();
3205  // restore graphic settings
3206  $this->setGraphicVars($gvars);
3207  // mark this point
3208  $this->setPageMark();
3209  // print table header (if any)
3210  $this->setTableHeader();
3211  // set mark for empty page check
3212  $this->emptypagemrk[$this->page]= $this->pagelen[$this->page];
3213  }
3214 
3223  public function setPageMark() {
3224  $this->intmrk[$this->page] = $this->pagelen[$this->page];
3225  $this->bordermrk[$this->page] = $this->intmrk[$this->page];
3226  $this->setContentMark();
3227  }
3228 
3236  protected function setContentMark($page=0) {
3237  if ($page <= 0) {
3238  $page = $this->page;
3239  }
3240  if (isset($this->footerlen[$page])) {
3241  $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
3242  } else {
3243  $this->cntmrk[$page] = $this->pagelen[$page];
3244  }
3245  }
3246 
3257  public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) {
3258  $this->header_logo = $ln;
3259  $this->header_logo_width = $lw;
3260  $this->header_title = $ht;
3261  $this->header_string = $hs;
3262  $this->header_text_color = $tc;
3263  $this->header_line_color = $lc;
3264  }
3265 
3272  public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) {
3273  $this->footer_text_color = $tc;
3274  $this->footer_line_color = $lc;
3275  }
3276 
3284  public function getHeaderData() {
3285  $ret = array();
3286  $ret['logo'] = $this->header_logo;
3287  $ret['logo_width'] = $this->header_logo_width;
3288  $ret['title'] = $this->header_title;
3289  $ret['string'] = $this->header_string;
3290  $ret['text_color'] = $this->header_text_color;
3291  $ret['line_color'] = $this->header_line_color;
3292  return $ret;
3293  }
3294 
3301  public function setHeaderMargin($hm=10) {
3302  $this->header_margin = $hm;
3303  }
3304 
3311  public function getHeaderMargin() {
3312  return $this->header_margin;
3313  }
3314 
3321  public function setFooterMargin($fm=10) {
3322  $this->footer_margin = $fm;
3323  }
3324 
3331  public function getFooterMargin() {
3332  return $this->footer_margin;
3333  }
3339  public function setPrintHeader($val=true) {
3340  $this->print_header = $val ? true : false;
3341  }
3342 
3348  public function setPrintFooter($val=true) {
3349  $this->print_footer = $val ? true : false;
3350  }
3351 
3357  public function getImageRBX() {
3358  return $this->img_rb_x;
3359  }
3360 
3366  public function getImageRBY() {
3367  return $this->img_rb_y;
3368  }
3369 
3374  public function resetHeaderTemplate() {
3375  $this->header_xobjid = false;
3376  }
3377 
3383  public function setHeaderTemplateAutoreset($val=true) {
3384  $this->header_xobj_autoreset = $val ? true : false;
3385  }
3386 
3392  public function Header() {
3393  if ($this->header_xobjid === false) {
3394  // start a new XObject Template
3395  $this->header_xobjid = $this->startTemplate($this->w, $this->tMargin);
3396  $headerfont = $this->getHeaderFont();
3397  $headerdata = $this->getHeaderData();
3398  $this->y = $this->header_margin;
3399  if ($this->rtl) {
3400  $this->x = $this->w - $this->original_rMargin;
3401  } else {
3402  $this->x = $this->original_lMargin;
3403  }
3404  if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
3405  $imgtype = TCPDF_IMAGES::getImageFileType(K_PATH_IMAGES.$headerdata['logo']);
3406  if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
3407  $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3408  } elseif ($imgtype == 'svg') {
3409  $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3410  } else {
3411  $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3412  }
3413  $imgy = $this->getImageRBY();
3414  } else {
3415  $imgy = $this->y;
3416  }
3417  $cell_height = $this->getCellHeight($headerfont[2] / $this->k);
3418  // set starting margin for text data cell
3419  if ($this->getRTL()) {
3420  $header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1);
3421  } else {
3422  $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1);
3423  }
3424  $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1);
3425  $this->SetTextColorArray($this->header_text_color);
3426  // header title
3427  $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
3428  $this->SetX($header_x);
3429  $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
3430  // header string
3431  $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
3432  $this->SetX($header_x);
3433  $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false);
3434  // print an ending header line
3435  $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color']));
3436  $this->SetY((2.835 / $this->k) + max($imgy, $this->y));
3437  if ($this->rtl) {
3438  $this->SetX($this->original_rMargin);
3439  } else {
3440  $this->SetX($this->original_lMargin);
3441  }
3442  $this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C');
3443  $this->endTemplate();
3444  }
3445  // print header template
3446  $x = 0;
3447  $dx = 0;
3448  if (!$this->header_xobj_autoreset AND $this->booklet AND (($this->page % 2) == 0)) {
3449  // adjust margins for booklet mode
3450  $dx = ($this->original_lMargin - $this->original_rMargin);
3451  }
3452  if ($this->rtl) {
3453  $x = $this->w + $dx;
3454  } else {
3455  $x = 0 + $dx;
3456  }
3457  $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false);
3458  if ($this->header_xobj_autoreset) {
3459  // reset header xobject template at each page
3460  $this->header_xobjid = false;
3461  }
3462  }
3463 
3469  public function Footer() {
3470  $cur_y = $this->y;
3471  $this->SetTextColorArray($this->footer_text_color);
3472  //set style for cell border
3473  $line_width = (0.85 / $this->k);
3474  $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color));
3475  //print document barcode
3476  $barcode = $this->getBarcode();
3477  if (!empty($barcode)) {
3478  $this->Ln($line_width);
3479  $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3);
3480  $style = array(
3481  'position' => $this->rtl?'R':'L',
3482  'align' => $this->rtl?'R':'L',
3483  'stretch' => false,
3484  'fitwidth' => true,
3485  'cellfitalign' => '',
3486  'border' => false,
3487  'padding' => 0,
3488  'fgcolor' => array(0,0,0),
3489  'bgcolor' => false,
3490  'text' => false
3491  );
3492  $this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, '');
3493  }
3494  $w_page = isset($this->l['w_page']) ? $this->l['w_page'].' ' : '';
3495  if (empty($this->pagegroups)) {
3496  $pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
3497  } else {
3498  $pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
3499  }
3500  $this->SetY($cur_y);
3501  //Print page number
3502  if ($this->getRTL()) {
3503  $this->SetX($this->original_rMargin);
3504  $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
3505  } else {
3506  $this->SetX($this->original_lMargin);
3507  $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R');
3508  }
3509  }
3510 
3516  protected function setHeader() {
3517  if (!$this->print_header OR ($this->state != 2)) {
3518  return;
3519  }
3520  $this->InHeader = true;
3521  $this->setGraphicVars($this->default_graphic_vars);
3522  $temp_thead = $this->thead;
3523  $temp_theadMargins = $this->theadMargins;
3524  $lasth = $this->lasth;
3526  $this->_outSaveGraphicsState();
3527  $this->rMargin = $this->original_rMargin;
3528  $this->lMargin = $this->original_lMargin;
3529  $this->SetCellPadding(0);
3530  //set current position
3531  if ($this->rtl) {
3532  $this->SetXY($this->original_rMargin, $this->header_margin);
3533  } else {
3534  $this->SetXY($this->original_lMargin, $this->header_margin);
3535  }
3536  $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
3537  $this->Header();
3538  //restore position
3539  if ($this->rtl) {
3540  $this->SetXY($this->original_rMargin, $this->tMargin);
3541  } else {
3542  $this->SetXY($this->original_lMargin, $this->tMargin);
3543  }
3544  $this->_outRestoreGraphicsState();
3545  $this->lasth = $lasth;
3546  $this->thead = $temp_thead;
3547  $this->theadMargins = $temp_theadMargins;
3548  $this->newline = $newline;
3549  $this->InHeader = false;
3550  }
3551 
3557  protected function setFooter() {
3558  if ($this->state != 2) {
3559  return;
3560  }
3561  $this->InFooter = true;
3562  // save current graphic settings
3563  $gvars = $this->getGraphicVars();
3564  // mark this point
3565  $this->footerpos[$this->page] = $this->pagelen[$this->page];
3566  $this->_out("\n");
3567  if ($this->print_footer) {
3568  $this->setGraphicVars($this->default_graphic_vars);
3569  $this->current_column = 0;
3570  $this->num_columns = 1;
3571  $temp_thead = $this->thead;
3572  $temp_theadMargins = $this->theadMargins;
3573  $lasth = $this->lasth;
3574  $this->_outSaveGraphicsState();
3575  $this->rMargin = $this->original_rMargin;
3576  $this->lMargin = $this->original_lMargin;
3577  $this->SetCellPadding(0);
3578  //set current position
3579  $footer_y = $this->h - $this->footer_margin;
3580  if ($this->rtl) {
3581  $this->SetXY($this->original_rMargin, $footer_y);
3582  } else {
3583  $this->SetXY($this->original_lMargin, $footer_y);
3584  }
3585  $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
3586  $this->Footer();
3587  //restore position
3588  if ($this->rtl) {
3589  $this->SetXY($this->original_rMargin, $this->tMargin);
3590  } else {
3591  $this->SetXY($this->original_lMargin, $this->tMargin);
3592  }
3593  $this->_outRestoreGraphicsState();
3594  $this->lasth = $lasth;
3595  $this->thead = $temp_thead;
3596  $this->theadMargins = $temp_theadMargins;
3597  }
3598  // restore graphic settings
3599  $this->setGraphicVars($gvars);
3600  $this->current_column = $gvars['current_column'];
3601  $this->num_columns = $gvars['num_columns'];
3602  // calculate footer length
3603  $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
3604  $this->InFooter = false;
3605  }
3606 
3613  protected function inPageBody() {
3614  return (($this->InHeader === false) AND ($this->InFooter === false));
3615  }
3616 
3622  protected function setTableHeader() {
3623  if ($this->num_columns > 1) {
3624  // multi column mode
3625  return;
3626  }
3627  if (isset($this->theadMargins['top'])) {
3628  // restore the original top-margin
3629  $this->tMargin = $this->theadMargins['top'];
3630  $this->pagedim[$this->page]['tm'] = $this->tMargin;
3631  $this->y = $this->tMargin;
3632  }
3633  if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
3634  // set margins
3635  $prev_lMargin = $this->lMargin;
3636  $prev_rMargin = $this->rMargin;
3637  $prev_cell_padding = $this->cell_padding;
3638  $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']);
3639  $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']);
3640  $this->cell_padding = $this->theadMargins['cell_padding'];
3641  if ($this->rtl) {
3642  $this->x = $this->w - $this->rMargin;
3643  } else {
3644  $this->x = $this->lMargin;
3645  }
3646  // account for special "cell" mode
3647  if ($this->theadMargins['cell']) {
3648  if ($this->rtl) {
3649  $this->x -= $this->cell_padding['R'];
3650  } else {
3651  $this->x += $this->cell_padding['L'];
3652  }
3653  }
3654  $gvars = $this->getGraphicVars();
3655  if (!empty($this->theadMargins['gvars'])) {
3656  // set the correct graphic style
3657  $this->setGraphicVars($this->theadMargins['gvars']);
3658  $this->rMargin = $gvars['rMargin'];
3659  $this->lMargin = $gvars['lMargin'];
3660  }
3661  // print table header
3662  $this->writeHTML($this->thead, false, false, false, false, '');
3663  $this->setGraphicVars($gvars);
3664  // set new top margin to skip the table headers
3665  if (!isset($this->theadMargins['top'])) {
3666  $this->theadMargins['top'] = $this->tMargin;
3667  }
3668  // store end of header position
3669  if (!isset($this->columns[0]['th'])) {
3670  $this->columns[0]['th'] = array();
3671  }
3672  $this->columns[0]['th']['\''.$this->page.'\''] = $this->y;
3673  $this->tMargin = $this->y;
3674  $this->pagedim[$this->page]['tm'] = $this->tMargin;
3675  $this->lasth = 0;
3676  $this->lMargin = $prev_lMargin;
3677  $this->rMargin = $prev_rMargin;
3678  $this->cell_padding = $prev_cell_padding;
3679  }
3680  }
3681 
3689  public function PageNo() {
3690  return $this->page;
3691  }
3692 
3699  public function getAllSpotColors() {
3700  return $this->spot_colors;
3701  }
3702 
3716  public function AddSpotColor($name, $c, $m, $y, $k) {
3717  if (!isset($this->spot_colors[$name])) {
3718  $i = (1 + count($this->spot_colors));
3719  $this->spot_colors[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i);
3720  }
3721  }
3722 
3732  public function setSpotColor($type, $name, $tint=100) {
3733  $spotcolor = TCPDF_COLORS::getSpotColor($name, $this->spot_colors);
3734  if ($spotcolor === false) {
3735  $this->Error('Undefined spot color: '.$name.', you must add it using the AddSpotColor() method.');
3736  }
3737  $tint = (max(0, min(100, $tint)) / 100);
3738  $pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']);
3739  switch ($type) {
3740  case 'draw': {
3741  $pdfcolor .= sprintf('CS %F SCN', $tint);
3742  $this->DrawColor = $pdfcolor;
3743  $this->strokecolor = $spotcolor;
3744  break;
3745  }
3746  case 'fill': {
3747  $pdfcolor .= sprintf('cs %F scn', $tint);
3748  $this->FillColor = $pdfcolor;
3749  $this->bgcolor = $spotcolor;
3750  break;
3751  }
3752  case 'text': {
3753  $pdfcolor .= sprintf('cs %F scn', $tint);
3754  $this->TextColor = $pdfcolor;
3755  $this->fgcolor = $spotcolor;
3756  break;
3757  }
3758  }
3759  $this->ColorFlag = ($this->FillColor != $this->TextColor);
3760  if ($this->state == 2) {
3761  $this->_out($pdfcolor);
3762  }
3763  if ($this->inxobj) {
3764  // we are inside an XObject template
3765  $this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name];
3766  }
3767  return $pdfcolor;
3768  }
3769 
3778  public function SetDrawSpotColor($name, $tint=100) {
3779  $this->setSpotColor('draw', $name, $tint);
3780  }
3781 
3790  public function SetFillSpotColor($name, $tint=100) {
3791  $this->setSpotColor('fill', $name, $tint);
3792  }
3793 
3802  public function SetTextSpotColor($name, $tint=100) {
3803  $this->setSpotColor('text', $name, $tint);
3804  }
3805 
3817  public function setColorArray($type, $color, $ret=false) {
3818  if (is_array($color)) {
3819  $color = array_values($color);
3820  // component: grey, RGB red or CMYK cyan
3821  $c = isset($color[0]) ? $color[0] : -1;
3822  // component: RGB green or CMYK magenta
3823  $m = isset($color[1]) ? $color[1] : -1;
3824  // component: RGB blue or CMYK yellow
3825  $y = isset($color[2]) ? $color[2] : -1;
3826  // component: CMYK black
3827  $k = isset($color[3]) ? $color[3] : -1;
3828  // color name
3829  $name = isset($color[4]) ? $color[4] : '';
3830  if ($c >= 0) {
3831  return $this->setColor($type, $c, $m, $y, $k, $ret, $name);
3832  }
3833  }
3834  return '';
3835  }
3836 
3848  public function SetDrawColorArray($color, $ret=false) {
3849  return $this->setColorArray('draw', $color, $ret);
3850  }
3851 
3862  public function SetFillColorArray($color, $ret=false) {
3863  return $this->setColorArray('fill', $color, $ret);
3864  }
3865 
3875  public function SetTextColorArray($color, $ret=false) {
3876  return $this->setColorArray('text', $color, $ret);
3877  }
3878 
3892  public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3893  // set default values
3894  if (!is_numeric($col1)) {
3895  $col1 = 0;
3896  }
3897  if (!is_numeric($col2)) {
3898  $col2 = -1;
3899  }
3900  if (!is_numeric($col3)) {
3901  $col3 = -1;
3902  }
3903  if (!is_numeric($col4)) {
3904  $col4 = -1;
3905  }
3906  // set color by case
3907  $suffix = '';
3908  if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
3909  // Grey scale
3910  $col1 = max(0, min(255, $col1));
3911  $intcolor = array('G' => $col1);
3912  $pdfcolor = sprintf('%F ', ($col1 / 255));
3913  $suffix = 'g';
3914  } elseif ($col4 == -1) {
3915  // RGB
3916  $col1 = max(0, min(255, $col1));
3917  $col2 = max(0, min(255, $col2));
3918  $col3 = max(0, min(255, $col3));
3919  $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
3920  $pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255));
3921  $suffix = 'rg';
3922  } else {
3923  $col1 = max(0, min(100, $col1));
3924  $col2 = max(0, min(100, $col2));
3925  $col3 = max(0, min(100, $col3));
3926  $col4 = max(0, min(100, $col4));
3927  if (empty($name)) {
3928  // CMYK
3929  $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
3930  $pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
3931  $suffix = 'k';
3932  } else {
3933  // SPOT COLOR
3934  $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
3935  $this->AddSpotColor($name, $col1, $col2, $col3, $col4);
3936  $pdfcolor = $this->setSpotColor($type, $name, 100);
3937  }
3938  }
3939  switch ($type) {
3940  case 'draw': {
3941  $pdfcolor .= strtoupper($suffix);
3942  $this->DrawColor = $pdfcolor;
3943  $this->strokecolor = $intcolor;
3944  break;
3945  }
3946  case 'fill': {
3947  $pdfcolor .= $suffix;
3948  $this->FillColor = $pdfcolor;
3949  $this->bgcolor = $intcolor;
3950  break;
3951  }
3952  case 'text': {
3953  $pdfcolor .= $suffix;
3954  $this->TextColor = $pdfcolor;
3955  $this->fgcolor = $intcolor;
3956  break;
3957  }
3958  }
3959  $this->ColorFlag = ($this->FillColor != $this->TextColor);
3960  if (($type != 'text') AND ($this->state == 2)) {
3961  if (!$ret) {
3962  $this->_out($pdfcolor);
3963  }
3964  return $pdfcolor;
3965  }
3966  return '';
3967  }
3968 
3982  public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3983  return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name);
3984  }
3985 
3999  public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4000  return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name);
4001  }
4002 
4016  public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4017  return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name);
4018  }
4019 
4032  public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4033  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);
4034  }
4035 
4048  public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4049  // store current values
4050  if (!TCPDF_STATIC::empty_string($fontname)) {
4051  $prev_FontFamily = $this->FontFamily;
4052  $prev_FontStyle = $this->FontStyle;
4053  $prev_FontSizePt = $this->FontSizePt;
4054  $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false);
4055  }
4056  // convert UTF-8 array to Latin1 if required
4057  if ($this->isunicode AND (!$this->isUnicodeFont())) {
4059  }
4060  $w = 0; // total width
4061  $wa = array(); // array of characters widths
4062  foreach ($sa as $ck => $char) {
4063  // character width
4064  $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)]));
4065  $wa[] = $cw;
4066  $w += $cw;
4067  }
4068  // restore previous values
4069  if (!TCPDF_STATIC::empty_string($fontname)) {
4070  $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
4071  }
4072  if ($getarray) {
4073  return $wa;
4074  }
4075  return $w;
4076  }
4077 
4087  public function GetCharWidth($char, $notlast=true) {
4088  // get raw width
4089  $chw = $this->getRawCharWidth($char);
4090  if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) {
4091  // increase/decrease font spacing
4092  $chw += $this->font_spacing;
4093  }
4094  if ($this->font_stretching != 100) {
4095  // fixed stretching mode
4096  $chw *= ($this->font_stretching / 100);
4097  }
4098  return $chw;
4099  }
4100 
4109  public function getRawCharWidth($char) {
4110  if ($char == 173) {
4111  // SHY character will not be printed
4112  return (0);
4113  }
4114  if (isset($this->CurrentFont['cw'][$char])) {
4115  $w = $this->CurrentFont['cw'][$char];
4116  } elseif (isset($this->CurrentFont['dw'])) {
4117  // default width
4118  $w = $this->CurrentFont['dw'];
4119  } elseif (isset($this->CurrentFont['cw'][32])) {
4120  // default width
4121  $w = $this->CurrentFont['cw'][32];
4122  } else {
4123  $w = 600;
4124  }
4125  return $this->getAbsFontMeasure($w);
4126  }
4127 
4135  public function GetNumChars($s) {
4136  if ($this->isUnicodeFont()) {
4137  return count(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont));
4138  }
4139  return strlen($s);
4140  }
4141 
4147  protected function getFontsList() {
4148  if (($fontsdir = opendir(TCPDF_FONTS::_getfontpath())) !== false) {
4149  while (($file = readdir($fontsdir)) !== false) {
4150  if (substr($file, -4) == '.php') {
4151  array_push($this->fontlist, strtolower(basename($file, '.php')));
4152  }
4153  }
4154  closedir($fontsdir);
4155  }
4156  }
4157 
4171  public function AddFont($family, $style='', $fontfile='', $subset='default') {
4172  if ($subset === 'default') {
4173  $subset = $this->font_subsetting;
4174  }
4175  if ($this->pdfa_mode) {
4176  $subset = false;
4177  }
4178  if (TCPDF_STATIC::empty_string($family)) {
4179  if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
4180  $family = $this->FontFamily;
4181  } else {
4182  $this->Error('Empty font family');
4183  }
4184  }
4185  // move embedded styles on $style
4186  if (substr($family, -1) == 'I') {
4187  $style .= 'I';
4188  $family = substr($family, 0, -1);
4189  }
4190  if (substr($family, -1) == 'B') {
4191  $style .= 'B';
4192  $family = substr($family, 0, -1);
4193  }
4194  // normalize family name
4195  $family = strtolower($family);
4196  if ((!$this->isunicode) AND ($family == 'arial')) {
4197  $family = 'helvetica';
4198  }
4199  if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
4200  $style = '';
4201  }
4202  if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) {
4203  // all fonts must be embedded
4204  $family = 'pdfa'.$family;
4205  }
4206  $tempstyle = strtoupper($style);
4207  $style = '';
4208  // underline
4209  if (strpos($tempstyle, 'U') !== false) {
4210  $this->underline = true;
4211  } else {
4212  $this->underline = false;
4213  }
4214  // line-through (deleted)
4215  if (strpos($tempstyle, 'D') !== false) {
4216  $this->linethrough = true;
4217  } else {
4218  $this->linethrough = false;
4219  }
4220  // overline
4221  if (strpos($tempstyle, 'O') !== false) {
4222  $this->overline = true;
4223  } else {
4224  $this->overline = false;
4225  }
4226  // bold
4227  if (strpos($tempstyle, 'B') !== false) {
4228  $style .= 'B';
4229  }
4230  // oblique
4231  if (strpos($tempstyle, 'I') !== false) {
4232  $style .= 'I';
4233  }
4234  $bistyle = $style;
4235  $fontkey = $family.$style;
4236  $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : '');
4237  $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
4238  // check if the font has been already added
4239  $fb = $this->getFontBuffer($fontkey);
4240  if ($fb !== false) {
4241  if ($this->inxobj) {
4242  // we are inside an XObject template
4243  $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i'];
4244  }
4245  return $fontdata;
4246  }
4247  // get specified font directory (if any)
4248  $fontdir = false;
4249  if (!TCPDF_STATIC::empty_string($fontfile)) {
4250  $fontdir = dirname($fontfile);
4251  if (TCPDF_STATIC::empty_string($fontdir) OR ($fontdir == '.')) {
4252  $fontdir = '';
4253  } else {
4254  $fontdir .= '/';
4255  }
4256  }
4257  // true when the font style variation is missing
4258  $missing_style = false;
4259  // search and include font file
4260  if (TCPDF_STATIC::empty_string($fontfile) OR (!@file_exists($fontfile))) {
4261  // build a standard filenames for specified font
4262  $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
4263  $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);
4264  if (TCPDF_STATIC::empty_string($fontfile)) {
4265  $missing_style = true;
4266  // try to remove the style part
4267  $tmp_fontfile = str_replace(' ', '', $family).'.php';
4268  $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);
4269  }
4270  }
4271  // include font file
4272  if (!TCPDF_STATIC::empty_string($fontfile) AND (@file_exists($fontfile))) {
4273  include($fontfile);
4274  } else {
4275  $this->Error('Could not include font definition file: '.$family.'');
4276  }
4277  // check font parameters
4278  if ((!isset($type)) OR (!isset($cw))) {
4279  $this->Error('The font definition file has a bad format: '.$fontfile.'');
4280  }
4281  // SET default parameters
4282  if (!isset($file) OR TCPDF_STATIC::empty_string($file)) {
4283  $file = '';
4284  }
4285  if (!isset($enc) OR TCPDF_STATIC::empty_string($enc)) {
4286  $enc = '';
4287  }
4288  if (!isset($cidinfo) OR TCPDF_STATIC::empty_string($cidinfo)) {
4289  $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0);
4290  $cidinfo['uni2cid'] = array();
4291  }
4292  if (!isset($ctg) OR TCPDF_STATIC::empty_string($ctg)) {
4293  $ctg = '';
4294  }
4295  if (!isset($desc) OR TCPDF_STATIC::empty_string($desc)) {
4296  $desc = array();
4297  }
4298  if (!isset($up) OR TCPDF_STATIC::empty_string($up)) {
4299  $up = -100;
4300  }
4301  if (!isset($ut) OR TCPDF_STATIC::empty_string($ut)) {
4302  $ut = 50;
4303  }
4304  if (!isset($cw) OR TCPDF_STATIC::empty_string($cw)) {
4305  $cw = array();
4306  }
4307  if (!isset($dw) OR TCPDF_STATIC::empty_string($dw)) {
4308  // set default width
4309  if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
4310  $dw = $desc['MissingWidth'];
4311  } elseif (isset($cw[32])) {
4312  $dw = $cw[32];
4313  } else {
4314  $dw = 600;
4315  }
4316  }
4317  ++$this->numfonts;
4318  if ($type == 'core') {
4319  $name = $this->CoreFonts[$fontkey];
4320  $subset = false;
4321  } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
4322  $subset = false;
4323  } elseif ($type == 'TrueTypeUnicode') {
4324  $enc = 'Identity-H';
4325  } elseif ($type == 'cidfont0') {
4326  if ($this->pdfa_mode) {
4327  $this->Error('All fonts must be embedded in PDF/A mode!');
4328  }
4329  } else {
4330  $this->Error('Unknow font type: '.$type.'');
4331  }
4332  // set name if unset
4333  if (!isset($name) OR empty($name)) {
4334  $name = $fontkey;
4335  }
4336  // create artificial font style variations if missing (only works with non-embedded fonts)
4337  if (($type != 'core') AND $missing_style) {
4338  // style variations
4339  $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
4340  $name .= $styles[$bistyle];
4341  // artificial bold
4342  if (strpos($bistyle, 'B') !== false) {
4343  if (isset($desc['StemV'])) {
4344  // from normal to bold
4345  $desc['StemV'] = round($desc['StemV'] * 1.75);
4346  } else {
4347  // bold
4348  $desc['StemV'] = 123;
4349  }
4350  }
4351  // artificial italic
4352  if (strpos($bistyle, 'I') !== false) {
4353  if (isset($desc['ItalicAngle'])) {
4354  $desc['ItalicAngle'] -= 11;
4355  } else {
4356  $desc['ItalicAngle'] = -11;
4357  }
4358  if (isset($desc['Flags'])) {
4359  $desc['Flags'] |= 64; //bit 7
4360  } else {
4361  $desc['Flags'] = 64;
4362  }
4363  }
4364  }
4365  // check if the array of characters bounding boxes is defined
4366  if (!isset($cbbox)) {
4367  $cbbox = array();
4368  }
4369  // initialize subsetchars
4370  $subsetchars = array_fill(0, 255, true);
4371  $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));
4372  if ($this->inxobj) {
4373  // we are inside an XObject template
4374  $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts;
4375  }
4376  if (isset($diff) AND (!empty($diff))) {
4377  //Search existing encodings
4378  $d = 0;
4379  $nb = count($this->diffs);
4380  for ($i=1; $i <= $nb; ++$i) {
4381  if ($this->diffs[$i] == $diff) {
4382  $d = $i;
4383  break;
4384  }
4385  }
4386  if ($d == 0) {
4387  $d = $nb + 1;
4388  $this->diffs[$d] = $diff;
4389  }
4390  $this->setFontSubBuffer($fontkey, 'diff', $d);
4391  }
4393  if (!isset($this->FontFiles[$file])) {
4394  if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
4395  $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4396  } elseif ($type != 'core') {
4397  $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4398  }
4399  } else {
4400  // update fontkeys that are sharing this font file
4401  $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset);
4402  if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) {
4403  $this->FontFiles[$file]['fontkeys'][] = $fontkey;
4404  }
4405  }
4406  }
4407  return $fontdata;
4408  }
4409 
4427  public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) {
4428  //Select a font; size given in points
4429  if ($size === null) {
4431  }
4432  if ($size < 0) {
4433  $size = 0;
4434  }
4435  // try to add font (if not already added)
4436  $fontdata = $this->AddFont($family, $style, $fontfile, $subset);
4437  $this->FontFamily = $fontdata['family'];
4438  $this->FontStyle = $fontdata['style'];
4439  if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
4440  // save subset chars of the previous font
4441  $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
4442  }
4443  $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
4444  $this->SetFontSize($size, $out);
4445  }
4446 
4455  public function SetFontSize($size, $out=true) {
4456  // font size in points
4457  $this->FontSizePt = $size;
4458  // font size in user units
4459  $this->FontSize = $size / $this->k;
4460  // calculate some font metrics
4461  if (isset($this->CurrentFont['desc']['FontBBox'])) {
4462  $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4463  $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
4464  } else {
4465  $font_height = $size * 1.219;
4466  }
4467  if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
4468  $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000);
4469  }
4470  if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) {
4471  $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000);
4472  }
4473  if (!isset($font_ascent) AND !isset($font_descent)) {
4474  // core font
4475  $font_ascent = 0.76 * $font_height;
4476  $font_descent = $font_height - $font_ascent;
4477  } elseif (!isset($font_descent)) {
4478  $font_descent = $font_height - $font_ascent;
4479  } elseif (!isset($font_ascent)) {
4480  $font_ascent = $font_height - $font_descent;
4481  }
4482  $this->FontAscent = ($font_ascent / $this->k);
4483  $this->FontDescent = ($font_descent / $this->k);
4484  if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i'])) AND ($this->state == 2)) {
4485  $this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4486  }
4487  }
4488 
4495  public function getFontBBox() {
4496  $fbbox = array();
4497  if (isset($this->CurrentFont['desc']['FontBBox'])) {
4498  $tmpbbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4499  $fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox);
4500  } else {
4501  // Find max width
4502  if (isset($this->CurrentFont['desc']['MaxWidth'])) {
4503  $maxw = $this->getAbsFontMeasure(intval($this->CurrentFont['desc']['MaxWidth']));
4504  } else {
4505  $maxw = 0;
4506  if (isset($this->CurrentFont['desc']['MissingWidth'])) {
4507  $maxw = max($maxw, $this->CurrentFont['desc']['MissingWidth']);
4508  }
4509  if (isset($this->CurrentFont['desc']['AvgWidth'])) {
4510  $maxw = max($maxw, $this->CurrentFont['desc']['AvgWidth']);
4511  }
4512  if (isset($this->CurrentFont['dw'])) {
4513  $maxw = max($maxw, $this->CurrentFont['dw']);
4514  }
4515  foreach ($this->CurrentFont['cw'] as $char => $w) {
4516  $maxw = max($maxw, $w);
4517  }
4518  if ($maxw == 0) {
4519  $maxw = 600;
4520  }
4521  $maxw = $this->getAbsFontMeasure($maxw);
4522  }
4523  $fbbox = array(0, (0 - $this->FontDescent), $maxw, $this->FontAscent);
4524  }
4525  return $fbbox;
4526  }
4527 
4534  public function getAbsFontMeasure($s) {
4535  return ($s * $this->FontSize / 1000);
4536  }
4537 
4544  public function getCharBBox($char) {
4545  $c = intval($char);
4546  if (isset($this->CurrentFont['cw'][$c])) {
4547  // glyph is defined ... use zero width & height for glyphs without outlines
4548  $result = array(0,0,0,0);
4549  if (isset($this->CurrentFont['cbbox'][$c])) {
4550  $result = $this->CurrentFont['cbbox'][$c];
4551  }
4552  return array_map(array($this,'getAbsFontMeasure'), $result);
4553  }
4554  return false;
4555  }
4556 
4567  public function getFontDescent($font, $style='', $size=0) {
4568  $fontdata = $this->AddFont($font, $style);
4569  $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4570  if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
4571  $descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
4572  } else {
4573  $descent = (1.219 * 0.24 * $size);
4574  }
4575  return ($descent / $this->k);
4576  }
4577 
4588  public function getFontAscent($font, $style='', $size=0) {
4589  $fontdata = $this->AddFont($font, $style);
4590  $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4591  if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
4592  $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
4593  } else {
4594  $ascent = 1.219 * 0.76 * $size;
4595  }
4596  return ($ascent / $this->k);
4597  }
4598 
4608  public function isCharDefined($char, $font='', $style='') {
4609  if (is_string($char)) {
4610  // get character code
4611  $char = TCPDF_FONTS::UTF8StringToArray($char, $this->isunicode, $this->CurrentFont);
4612  $char = $char[0];
4613  }
4614  if (TCPDF_STATIC::empty_string($font)) {
4616  return (isset($this->CurrentFont['cw'][intval($char)]));
4617  }
4618  $font = $this->FontFamily;
4619  }
4620  $fontdata = $this->AddFont($font, $style);
4621  $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4622  return (isset($fontinfo['cw'][intval($char)]));
4623  }
4624 
4635  public function replaceMissingChars($text, $font='', $style='', $subs=array()) {
4636  if (empty($subs)) {
4637  return $text;
4638  }
4639  if (TCPDF_STATIC::empty_string($font)) {
4640  $font = $this->FontFamily;
4641  }
4642  $fontdata = $this->AddFont($font, $style);
4643  $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4644  $uniarr = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
4645  foreach ($uniarr as $k => $chr) {
4646  if (!isset($fontinfo['cw'][$chr])) {
4647  // this character is missing on the selected font
4648  if (isset($subs[$chr])) {
4649  // we have available substitutions
4650  if (is_array($subs[$chr])) {
4651  foreach($subs[$chr] as $s) {
4652  if (isset($fontinfo['cw'][$s])) {
4653  $uniarr[$k] = $s;
4654  break;
4655  }
4656  }
4657  } elseif (isset($fontinfo['cw'][$subs[$chr]])) {
4658  $uniarr[$k] = $subs[$chr];
4659  }
4660  }
4661  }
4662  }
4663  return TCPDF_FONTS::UniArrSubString(TCPDF_FONTS::UTF8ArrayToUniArray($uniarr, $this->isunicode));
4664  }
4665 
4672  public function SetDefaultMonospacedFont($font) {
4673  $this->default_monospaced_font = $font;
4674  }
4675 
4683  public function AddLink() {
4684  // create a new internal link
4685  $n = count($this->links) + 1;
4686  $this->links[$n] = array('p' => 0, 'y' => 0, 'f' => false);
4687  return $n;
4688  }
4689 
4699  public function SetLink($link, $y=0, $page=-1) {
4700  $fixed = false;
4701  if (!empty($page) AND ($page[0] == '*')) {
4702  $page = intval(substr($page, 1));
4703  // this page number will not be changed when moving/add/deleting pages
4704  $fixed = true;
4705  }
4706  if ($page < 0) {
4707  $page = $this->page;
4708  }
4709  if ($y == -1) {
4710  $y = $this->y;
4711  }
4712  $this->links[$link] = array('p' => $page, 'y' => $y, 'f' => $fixed);
4713  }
4714 
4728  public function Link($x, $y, $w, $h, $link, $spaces=0) {
4729  $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
4730  }
4731 
4745  public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
4746  if ($this->inxobj) {
4747  // store parameters for later use on template
4748  $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
4749  return;
4750  }
4751  if ($x === '') {
4752  $x = $this->x;
4753  }
4754  if ($y === '') {
4755  $y = $this->y;
4756  }
4757  // check page for no-write regions and adapt page margins if necessary
4758  list($x, $y) = $this->checkPageRegions($h, $x, $y);
4759  // recalculate coordinates to account for graphic transformations
4760  if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) {
4761  for ($i=$this->transfmatrix_key; $i > 0; --$i) {
4762  $maxid = count($this->transfmatrix[$i]) - 1;
4763  for ($j=$maxid; $j >= 0; --$j) {
4764  $ctm = $this->transfmatrix[$i][$j];
4765  if (isset($ctm['a'])) {
4766  $x = $x * $this->k;
4767  $y = ($this->h - $y) * $this->k;
4768  $w = $w * $this->k;
4769  $h = $h * $this->k;
4770  // top left
4771  $xt = $x;
4772  $yt = $y;
4773  $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4774  $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4775  // top right
4776  $xt = $x + $w;
4777  $yt = $y;
4778  $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4779  $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4780  // bottom left
4781  $xt = $x;
4782  $yt = $y - $h;
4783  $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4784  $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4785  // bottom right
4786  $xt = $x + $w;
4787  $yt = $y - $h;
4788  $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4789  $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4790  // new coordinates (rectangle area)
4791  $x = min($x1, $x2, $x3, $x4);
4792  $y = max($y1, $y2, $y3, $y4);
4793  $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
4794  $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
4795  $x = $x / $this->k;
4796  $y = $this->h - ($y / $this->k);
4797  }
4798  }
4799  }
4800  }
4801  if ($this->page <= 0) {
4802  $page = 1;
4803  } else {
4804  $page = $this->page;
4805  }
4806  if (!isset($this->PageAnnots[$page])) {
4807  $this->PageAnnots[$page] = array();
4808  }
4809  $this->PageAnnots[$page][] = array('n' => ++$this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
4810  if (!$this->pdfa_mode) {
4811  if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!TCPDF_STATIC::empty_string($opt['FS']))
4812  AND (@file_exists($opt['FS']) OR TCPDF_STATIC::isValidURL($opt['FS']))
4813  AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
4814  $this->embeddedfiles[basename($opt['FS'])] = array('f' => ++$this->n, 'n' => ++$this->n, 'file' => $opt['FS']);
4815  }
4816  }
4817  // Add widgets annotation's icons
4818  if (isset($opt['mk']['i']) AND @file_exists($opt['mk']['i'])) {
4819  $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
4820  }
4821  if (isset($opt['mk']['ri']) AND @file_exists($opt['mk']['ri'])) {
4822  $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4823  }
4824  if (isset($opt['mk']['ix']) AND @file_exists($opt['mk']['ix'])) {
4825  $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4826  }
4827  }
4828 
4835  protected function _putEmbeddedFiles() {
4836  if ($this->pdfa_mode) {
4837  // embedded files are not allowed in PDF/A mode
4838  return;
4839  }
4840  reset($this->embeddedfiles);
4841  foreach ($this->embeddedfiles as $filename => $filedata) {
4842  $data = TCPDF_STATIC::fileGetContents($filedata['file']);
4843  if ($data !== FALSE) {
4844  $rawsize = strlen($data);
4845  if ($rawsize > 0) {
4846  // update name tree
4847  $this->efnames[$filename] = $filedata['f'].' 0 R';
4848  // embedded file specification object
4849  $out = $this->_getobj($filedata['f'])."\n";
4850  $out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']).' /EF <</F '.$filedata['n'].' 0 R>> >>';
4851  $out .= "\n".'endobj';
4852  $this->_out($out);
4853  // embedded file object
4854  $filter = '';
4855  if ($this->compress) {
4856  $data = gzcompress($data);
4857  $filter = ' /Filter /FlateDecode';
4858  }
4859  $stream = $this->_getrawstream($data, $filedata['n']);
4860  $out = $this->_getobj($filedata['n'])."\n";
4861  $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>';
4862  $out .= ' stream'."\n".$stream."\n".'endstream';
4863  $out .= "\n".'endobj';
4864  $this->_out($out);
4865  }
4866  }
4867  }
4868  }
4869 
4893  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) {
4896  $this->setTextRenderingMode($fstroke, $ffill, $fclip);
4897  $this->SetXY($x, $y, $rtloff);
4898  $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
4899  // restore previous rendering mode
4900  $this->textrendermode = $textrendermode;
4901  $this->textstrokewidth = $textstrokewidth;
4902  }
4903 
4913  public function AcceptPageBreak() {
4914  if ($this->num_columns > 1) {
4915  // multi column mode
4916  if ($this->current_column < ($this->num_columns - 1)) {
4917  // go to next column
4918  $this->selectColumn($this->current_column + 1);
4919  } elseif ($this->AutoPageBreak) {
4920  // add a new page
4921  $this->AddPage();
4922  // set first column
4923  $this->selectColumn(0);
4924  }
4925  // avoid page breaking from checkPageBreak()
4926  return false;
4927  }
4928  return $this->AutoPageBreak;
4929  }
4930 
4940  protected function checkPageBreak($h=0, $y='', $addpage=true) {
4942  $y = $this->y;
4943  }
4944  $current_page = $this->page;
4945  if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) {
4946  if ($addpage) {
4947  //Automatic page break
4948  $x = $this->x;
4949  $this->AddPage($this->CurOrientation);
4950  $this->y = $this->tMargin;
4951  $oldpage = $this->page - 1;
4952  if ($this->rtl) {
4953  if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
4954  $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
4955  } else {
4956  $this->x = $x;
4957  }
4958  } else {
4959  if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
4960  $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
4961  } else {
4962  $this->x = $x;
4963  }
4964  }
4965  }
4966  return true;
4967  }
4968  if ($current_page != $this->page) {
4969  // account for columns mode
4970  return true;
4971  }
4972  return false;
4973  }
4974 
4994  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') {
4995  $prev_cell_margin = $this->cell_margin;
4996  $prev_cell_padding = $this->cell_padding;
4997  $this->adjustCellPadding($border);
4998  if (!$ignore_min_height) {
4999  $min_cell_height = $this->getCellHeight($this->FontSize);
5000  if ($h < $min_cell_height) {
5001  $h = $min_cell_height;
5002  }
5003  }
5004  $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']);
5005  // apply text shadow if enabled
5006  if ($this->txtshadow['enabled']) {
5007  // save data
5008  $x = $this->x;
5009  $y = $this->y;
5010  $bc = $this->bgcolor;
5011  $fc = $this->fgcolor;
5012  $sc = $this->strokecolor;
5013  $alpha = $this->alpha;
5014  // print shadow
5015  $this->x += $this->txtshadow['depth_w'];
5016  $this->y += $this->txtshadow['depth_h'];
5017  $this->SetFillColorArray($this->txtshadow['color']);
5018  $this->SetTextColorArray($this->txtshadow['color']);
5019  $this->SetDrawColorArray($this->txtshadow['color']);
5020  if ($this->txtshadow['opacity'] != $alpha['CA']) {
5021  $this->setAlpha($this->txtshadow['opacity'], $this->txtshadow['blend_mode']);
5022  }
5023  if ($this->state == 2) {
5024  $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5025  }
5026  //restore data
5027  $this->x = $x;
5028  $this->y = $y;
5029  $this->SetFillColorArray($bc);
5030  $this->SetTextColorArray($fc);
5031  $this->SetDrawColorArray($sc);
5032  if ($this->txtshadow['opacity'] != $alpha['CA']) {
5033  $this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']);
5034  }
5035  }
5036  if ($this->state == 2) {
5037  $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5038  }
5039  $this->cell_padding = $prev_cell_padding;
5040  $this->cell_margin = $prev_cell_margin;
5041  }
5042 
5063  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') {
5064  // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space
5065  $txt = str_replace(TCPDF_FONTS::unichr(160, $this->isunicode), ' ', $txt);
5066  $prev_cell_margin = $this->cell_margin;
5067  $prev_cell_padding = $this->cell_padding;
5068  $txt = TCPDF_STATIC::removeSHY($txt, $this->isunicode);
5069  $rs = ''; //string to be returned
5070  $this->adjustCellPadding($border);
5071  if (!$ignore_min_height) {
5072  $min_cell_height = $this->getCellHeight($this->FontSize);
5073  if ($h < $min_cell_height) {
5074  $h = $min_cell_height;
5075  }
5076  }
5077  $k = $this->k;
5078  // check page for no-write regions and adapt page margins if necessary
5079  list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
5080  if ($this->rtl) {
5081  $x = $this->x - $this->cell_margin['R'];
5082  } else {
5083  $x = $this->x + $this->cell_margin['L'];
5084  }
5085  $y = $this->y + $this->cell_margin['T'];
5086  $prev_font_stretching = $this->font_stretching;
5087  $prev_font_spacing = $this->font_spacing;
5088  // cell vertical alignment
5089  switch ($calign) {
5090  case 'A': {
5091  // font top
5092  switch ($valign) {
5093  case 'T': {
5094  // top
5095  $y -= $this->cell_padding['T'];
5096  break;
5097  }
5098  case 'B': {
5099  // bottom
5100  $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent);
5101  break;
5102  }
5103  default:
5104  case 'C':
5105  case 'M': {
5106  // center
5107  $y -= (($h - $this->FontAscent - $this->FontDescent) / 2);
5108  break;
5109  }
5110  }
5111  break;
5112  }
5113  case 'L': {
5114  // font baseline
5115  switch ($valign) {
5116  case 'T': {
5117  // top
5118  $y -= ($this->cell_padding['T'] + $this->FontAscent);
5119  break;
5120  }
5121  case 'B': {
5122  // bottom
5123  $y -= ($h - $this->cell_padding['B'] - $this->FontDescent);
5124  break;
5125  }
5126  default:
5127  case 'C':
5128  case 'M': {
5129  // center
5130  $y -= (($h + $this->FontAscent - $this->FontDescent) / 2);
5131  break;
5132  }
5133  }
5134  break;
5135  }
5136  case 'D': {
5137  // font bottom
5138  switch ($valign) {
5139  case 'T': {
5140  // top
5141  $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent);
5142  break;
5143  }
5144  case 'B': {
5145  // bottom
5146  $y -= ($h - $this->cell_padding['B']);
5147  break;
5148  }
5149  default:
5150  case 'C':
5151  case 'M': {
5152  // center
5153  $y -= (($h + $this->FontAscent + $this->FontDescent) / 2);
5154  break;
5155  }
5156  }
5157  break;
5158  }
5159  case 'B': {
5160  // cell bottom
5161  $y -= $h;
5162  break;
5163  }
5164  case 'C':
5165  case 'M': {
5166  // cell center
5167  $y -= ($h / 2);
5168  break;
5169  }
5170  default:
5171  case 'T': {
5172  // cell top
5173  break;
5174  }
5175  }
5176  // text vertical alignment
5177  switch ($valign) {
5178  case 'T': {
5179  // top
5180  $yt = $y + $this->cell_padding['T'];
5181  break;
5182  }
5183  case 'B': {
5184  // bottom
5185  $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent;
5186  break;
5187  }
5188  default:
5189  case 'C':
5190  case 'M': {
5191  // center
5192  $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2);
5193  break;
5194  }
5195  }
5196  $basefonty = $yt + $this->FontAscent;
5197  if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5198  if ($this->rtl) {
5199  $w = $x - $this->lMargin;
5200  } else {
5201  $w = $this->w - $this->rMargin - $x;
5202  }
5203  }
5204  $s = '';
5205  // fill and borders
5206  if (is_string($border) AND (strlen($border) == 4)) {
5207  // full border
5208  $border = 1;
5209  }
5210  if ($fill OR ($border == 1)) {
5211  if ($fill) {
5212  $op = ($border == 1) ? 'B' : 'f';
5213  } else {
5214  $op = 'S';
5215  }
5216  if ($this->rtl) {
5217  $xk = (($x - $w) * $k);
5218  } else {
5219  $xk = ($x * $k);
5220  }
5221  $s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op);
5222  }
5223  // draw borders
5224  $s .= $this->getCellBorder($x, $y, $w, $h, $border);
5225  if ($txt != '') {
5226  $txt2 = $txt;
5227  if ($this->isunicode) {
5228  if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5229  $txt2 = TCPDF_FONTS::UTF8ToLatin1($txt2, $this->isunicode, $this->CurrentFont);
5230  } else {
5231  $unicode = TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont); // array of UTF-8 unicode values
5232  $unicode = TCPDF_FONTS::utf8Bidi($unicode, '', $this->tmprtl, $this->isunicode, $this->CurrentFont);
5233  // replace thai chars (if any)
5234  if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) {
5235  // number of chars
5236  $numchars = count($unicode);
5237  // po pla, for far, for fan
5238  $longtail = array(0x0e1b, 0x0e1d, 0x0e1f);
5239  // do chada, to patak
5240  $lowtail = array(0x0e0e, 0x0e0f);
5241  // mai hun arkad, sara i, sara ii, sara ue, sara uee
5242  $upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37);
5243  // mai ek, mai tho, mai tri, mai chattawa, karan
5244  $tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c);
5245  // sara u, sara uu, pinthu
5246  $lowvowel = array(0x0e38, 0x0e39, 0x0e3a);
5247  $output = array();
5248  for ($i = 0; $i < $numchars; $i++) {
5249  if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) {
5250  $ch0 = $unicode[$i];
5251  $ch1 = ($i > 0) ? $unicode[($i - 1)] : 0;
5252  $ch2 = ($i > 1) ? $unicode[($i - 2)] : 0;
5253  $chn = ($i < ($numchars - 1)) ? $unicode[($i + 1)] : 0;
5254  if (in_array($ch0, $tonemark)) {
5255  if ($chn == 0x0e33) {
5256  // sara um
5257  if (in_array($ch1, $longtail)) {
5258  // tonemark at upper left
5259  $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5260  } else {
5261  // tonemark at upper right (normal position)
5262  $output[] = $ch0;
5263  }
5264  } elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) {
5265  // tonemark at lower left
5266  $output[] = $this->replaceChar($ch0, (0xf705 + $ch0 - 0x0e48));
5267  } elseif (in_array($ch1, $upvowel)) {
5268  if (in_array($ch2, $longtail)) {
5269  // tonemark at upper left
5270  $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5271  } else {
5272  // tonemark at upper right (normal position)
5273  $output[] = $ch0;
5274  }
5275  } else {
5276  // tonemark at lower right
5277  $output[] = $this->replaceChar($ch0, (0xf70a + $ch0 - 0x0e48));
5278  }
5279  } elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) {
5280  // add lower left nikhahit and sara aa
5281  if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) {
5282  $output[] = 0xf711;
5283  $this->CurrentFont['subsetchars'][0xf711] = true;
5284  $output[] = 0x0e32;
5285  $this->CurrentFont['subsetchars'][0x0e32] = true;
5286  } else {
5287  $output[] = $ch0;
5288  }
5289  } elseif (in_array($ch1, $longtail)) {
5290  if ($ch0 == 0x0e31) {
5291  // lower left mai hun arkad
5292  $output[] = $this->replaceChar($ch0, 0xf710);
5293  } elseif (in_array($ch0, $upvowel)) {
5294  // lower left
5295  $output[] = $this->replaceChar($ch0, (0xf701 + $ch0 - 0x0e34));
5296  } elseif ($ch0 == 0x0e47) {
5297  // lower left mai tai koo
5298  $output[] = $this->replaceChar($ch0, 0xf712);
5299  } else {
5300  // normal character
5301  $output[] = $ch0;
5302  }
5303  } elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) {
5304  // lower vowel
5305  $output[] = $this->replaceChar($ch0, (0xf718 + $ch0 - 0x0e38));
5306  } elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) {
5307  // yo ying without lower part
5308  $output[] = $this->replaceChar($ch0, 0xf70f);
5309  } elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) {
5310  // tho santan without lower part
5311  $output[] = $this->replaceChar($ch0, 0xf700);
5312  } else {
5313  $output[] = $ch0;
5314  }
5315  } else {
5316  // non-thai character
5317  $output[] = $unicode[$i];
5318  }
5319  }
5320  $unicode = $output;
5321  // update font subsetchars
5322  $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
5323  } // end of K_THAI_TOPCHARS
5324  $txt2 = TCPDF_FONTS::arrUTF8ToUTF16BE($unicode, false);
5325  }
5326  }
5327  $txt2 = TCPDF_STATIC::_escape($txt2);
5328  // get current text width (considering general font stretching and spacing)
5329  $txwidth = $this->GetStringWidth($txt);
5330  $width = $txwidth;
5331  // check for stretch mode
5332  if ($stretch > 0) {
5333  // calculate ratio between cell width and text width
5334  if ($width <= 0) {
5335  $ratio = 1;
5336  } else {
5337  $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width);
5338  }
5339  // check if stretching is required
5340  if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) {
5341  // the text will be stretched to fit cell width
5342  if ($stretch > 2) {
5343  // set new character spacing
5344  $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100));
5345  } else {
5346  // set new horizontal stretching
5347  $this->font_stretching *= $ratio;
5348  }
5349  // recalculate text width (the text fills the entire cell)
5350  $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5351  // reset alignment
5352  $align = '';
5353  }
5354  }
5355  if ($this->font_stretching != 100) {
5356  // apply font stretching
5357  $rs .= sprintf('BT %F Tz ET ', $this->font_stretching);
5358  }
5359  if ($this->font_spacing != 0) {
5360  // increase/decrease font spacing
5361  $rs .= sprintf('BT %F Tc ET ', ($this->font_spacing * $this->k));
5362  }
5363  if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5364  $s .= 'q '.$this->TextColor.' ';
5365  }
5366  // rendering mode
5367  $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, ($this->textstrokewidth * $this->k));
5368  // count number of spaces
5369  $ns = substr_count($txt, chr(32));
5370  // Justification
5371  $spacewidth = 0;
5372  if (($align == 'J') AND ($ns > 0)) {
5373  if ($this->isUnicodeFont()) {
5374  // get string width without spaces
5375  $width = $this->GetStringWidth(str_replace(' ', '', $txt));
5376  // calculate average space width
5377  $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / ($this->FontSize?$this->FontSize:1);
5378  if ($this->font_stretching != 100) {
5379  // word spacing is affected by stretching
5380  $spacewidth /= ($this->font_stretching / 100);
5381  }
5382  // set word position to be used with TJ operator
5383  $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2);
5384  $unicode_justification = true;
5385  } else {
5386  // get string width
5387  $width = $txwidth;
5388  // new space width
5389  $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k;
5390  if ($this->font_stretching != 100) {
5391  // word spacing (Tw) is affected by stretching
5392  $spacewidth /= ($this->font_stretching / 100);
5393  }
5394  // set word spacing
5395  $rs .= sprintf('BT %F Tw ET ', $spacewidth);
5396  }
5397  $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5398  }
5399  // replace carriage return characters
5400  $txt2 = str_replace("\r", ' ', $txt2);
5401  switch ($align) {
5402  case 'C': {
5403  $dx = ($w - $width) / 2;
5404  break;
5405  }
5406  case 'R': {
5407  if ($this->rtl) {
5408  $dx = $this->cell_padding['R'];
5409  } else {
5410  $dx = $w - $width - $this->cell_padding['R'];
5411  }
5412  break;
5413  }
5414  case 'L': {
5415  if ($this->rtl) {
5416  $dx = $w - $width - $this->cell_padding['L'];
5417  } else {
5418  $dx = $this->cell_padding['L'];
5419  }
5420  break;
5421  }
5422  case 'J':
5423  default: {
5424  if ($this->rtl) {
5425  $dx = $this->cell_padding['R'];
5426  } else {
5427  $dx = $this->cell_padding['L'];
5428  }
5429  break;
5430  }
5431  }
5432  if ($this->rtl) {
5433  $xdx = $x - $dx - $width;
5434  } else {
5435  $xdx = $x + $dx;
5436  }
5437  $xdk = $xdx * $k;
5438  // print text
5439  $s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
5440  if (isset($uniblock)) {
5441  // print overlapping characters as separate string
5442  $xshift = 0; // horizontal shift
5443  $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k);
5444  $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1));
5445  foreach ($uniblock as $uk => $uniarr) {
5446  if (($uk % 2) == 0) {
5447  // x space to skip
5448  if ($spacewidth != 0) {
5449  // justification shift
5450  $xshift += (count(array_keys($uniarr, 32)) * $spw);
5451  }
5452  $xshift += $this->GetArrStringWidth($uniarr); // + shift justification
5453  } else {
5454  // character to print
5455  $topchr = TCPDF_FONTS::arrUTF8ToUTF16BE($uniarr, false);
5456  $topchr = TCPDF_STATIC::_escape($topchr);
5457  $s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr);
5458  }
5459  }
5460  }
5461  if ($this->underline) {
5462  $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
5463  }
5464  if ($this->linethrough) {
5465  $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
5466  }
5467  if ($this->overline) {
5468  $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
5469  }
5470  if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5471  $s .= ' Q';
5472  }
5473  if ($link) {
5474  $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns);
5475  }
5476  }
5477  // output cell
5478  if ($s) {
5479  // output cell
5480  $rs .= $s;
5481  if ($this->font_spacing != 0) {
5482  // reset font spacing mode
5483  $rs .= ' BT 0 Tc ET';
5484  }
5485  if ($this->font_stretching != 100) {
5486  // reset font stretching mode
5487  $rs .= ' BT 100 Tz ET';
5488  }
5489  }
5490  // reset word spacing
5491  if (!$this->isUnicodeFont() AND ($align == 'J')) {
5492  $rs .= ' BT 0 Tw ET';
5493  }
5494  // reset stretching and spacing
5495  $this->font_stretching = $prev_font_stretching;
5496  $this->font_spacing = $prev_font_spacing;
5497  $this->lasth = $h;
5498  if ($ln > 0) {
5499  //Go to the beginning of the next line
5500  $this->y = $y + $h + $this->cell_margin['B'];
5501  if ($ln == 1) {
5502  if ($this->rtl) {
5503  $this->x = $this->w - $this->rMargin;
5504  } else {
5505  $this->x = $this->lMargin;
5506  }
5507  }
5508  } else {
5509  // go left or right by case
5510  if ($this->rtl) {
5511  $this->x = $x - $w - $this->cell_margin['L'];
5512  } else {
5513  $this->x = $x + $w + $this->cell_margin['R'];
5514  }
5515  }
5516  $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
5517  $rs = $gstyles.$rs;
5518  $this->cell_padding = $prev_cell_padding;
5519  $this->cell_margin = $prev_cell_margin;
5520  return $rs;
5521  }
5522 
5531  protected function replaceChar($oldchar, $newchar) {
5532  if ($this->isCharDefined($newchar)) {
5533  // add the new char on the subset list
5534  $this->CurrentFont['subsetchars'][$newchar] = true;
5535  // return the new character
5536  return $newchar;
5537  }
5538  // return the old char
5539  return $oldchar;
5540  }
5541 
5554  protected function getCellBorder($x, $y, $w, $h, $brd) {
5555  $s = ''; // string to be returned
5556  if (empty($brd)) {
5557  return $s;
5558  }
5559  if ($brd == 1) {
5560  $brd = array('LRTB' => true);
5561  }
5562  // calculate coordinates for border
5563  $k = $this->k;
5564  if ($this->rtl) {
5565  $xeL = ($x - $w) * $k;
5566  $xeR = $x * $k;
5567  } else {
5568  $xeL = $x * $k;
5569  $xeR = ($x + $w) * $k;
5570  }
5571  $yeL = (($this->h - ($y + $h)) * $k);
5572  $yeT = (($this->h - $y) * $k);
5573  $xeT = $xeL;
5574  $xeB = $xeR;
5575  $yeR = $yeT;
5576  $yeB = $yeL;
5577  if (is_string($brd)) {
5578  // convert string to array
5579  $slen = strlen($brd);
5580  $newbrd = array();
5581  for ($i = 0; $i < $slen; ++$i) {
5582  $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
5583  }
5584  $brd = $newbrd;
5585  }
5586  if (isset($brd['mode'])) {
5587  $mode = $brd['mode'];
5588  unset($brd['mode']);
5589  } else {
5590  $mode = 'normal';
5591  }
5592  foreach ($brd as $border => $style) {
5593  if (is_array($style) AND !empty($style)) {
5594  // apply border style
5595  $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' ';
5596  $s .= $this->SetLineStyle($style, true)."\n";
5597  }
5598  switch ($mode) {
5599  case 'ext': {
5600  $off = (($this->LineWidth / 2) * $k);
5601  $xL = $xeL - $off;
5602  $xR = $xeR + $off;
5603  $yT = $yeT + $off;
5604  $yL = $yeL - $off;
5605  $xT = $xL;
5606  $xB = $xR;
5607  $yR = $yT;
5608  $yB = $yL;
5609  $w += $this->LineWidth;
5610  $h += $this->LineWidth;
5611  break;
5612  }
5613  case 'int': {
5614  $off = ($this->LineWidth / 2) * $k;
5615  $xL = $xeL + $off;
5616  $xR = $xeR - $off;
5617  $yT = $yeT - $off;
5618  $yL = $yeL + $off;
5619  $xT = $xL;
5620  $xB = $xR;
5621  $yR = $yT;
5622  $yB = $yL;
5623  $w -= $this->LineWidth;
5624  $h -= $this->LineWidth;
5625  break;
5626  }
5627  case 'normal':
5628  default: {
5629  $xL = $xeL;
5630  $xT = $xeT;
5631  $xB = $xeB;
5632  $xR = $xeR;
5633  $yL = $yeL;
5634  $yT = $yeT;
5635  $yB = $yeB;
5636  $yR = $yeR;
5637  break;
5638  }
5639  }
5640  // draw borders by case
5641  if (strlen($border) == 4) {
5642  $s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k));
5643  } elseif (strlen($border) == 3) {
5644  if (strpos($border,'B') === false) { // LTR
5645  $s .= sprintf('%F %F m ', $xL, $yL);
5646  $s .= sprintf('%F %F l ', $xT, $yT);
5647  $s .= sprintf('%F %F l ', $xR, $yR);
5648  $s .= sprintf('%F %F l ', $xB, $yB);
5649  $s .= 'S ';
5650  } elseif (strpos($border,'L') === false) { // TRB
5651  $s .= sprintf('%F %F m ', $xT, $yT);
5652  $s .= sprintf('%F %F l ', $xR, $yR);
5653  $s .= sprintf('%F %F l ', $xB, $yB);
5654  $s .= sprintf('%F %F l ', $xL, $yL);
5655  $s .= 'S ';
5656  } elseif (strpos($border,'T') === false) { // RBL
5657  $s .= sprintf('%F %F m ', $xR, $yR);
5658  $s .= sprintf('%F %F l ', $xB, $yB);
5659  $s .= sprintf('%F %F l ', $xL, $yL);
5660  $s .= sprintf('%F %F l ', $xT, $yT);
5661  $s .= 'S ';
5662  } elseif (strpos($border,'R') === false) { // BLT
5663  $s .= sprintf('%F %F m ', $xB, $yB);
5664  $s .= sprintf('%F %F l ', $xL, $yL);
5665  $s .= sprintf('%F %F l ', $xT, $yT);
5666  $s .= sprintf('%F %F l ', $xR, $yR);
5667  $s .= 'S ';
5668  }
5669  } elseif (strlen($border) == 2) {
5670  if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
5671  $s .= sprintf('%F %F m ', $xL, $yL);
5672  $s .= sprintf('%F %F l ', $xT, $yT);
5673  $s .= sprintf('%F %F l ', $xR, $yR);
5674  $s .= 'S ';
5675  } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
5676  $s .= sprintf('%F %F m ', $xT, $yT);
5677  $s .= sprintf('%F %F l ', $xR, $yR);
5678  $s .= sprintf('%F %F l ', $xB, $yB);
5679  $s .= 'S ';
5680  } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
5681  $s .= sprintf('%F %F m ', $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,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
5686  $s .= sprintf('%F %F m ', $xB, $yB);
5687  $s .= sprintf('%F %F l ', $xL, $yL);
5688  $s .= sprintf('%F %F l ', $xT, $yT);
5689  $s .= 'S ';
5690  } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
5691  $s .= sprintf('%F %F m ', $xL, $yL);
5692  $s .= sprintf('%F %F l ', $xT, $yT);
5693  $s .= 'S ';
5694  $s .= sprintf('%F %F m ', $xR, $yR);
5695  $s .= sprintf('%F %F l ', $xB, $yB);
5696  $s .= 'S ';
5697  } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
5698  $s .= sprintf('%F %F m ', $xT, $yT);
5699  $s .= sprintf('%F %F l ', $xR, $yR);
5700  $s .= 'S ';
5701  $s .= sprintf('%F %F m ', $xB, $yB);
5702  $s .= sprintf('%F %F l ', $xL, $yL);
5703  $s .= 'S ';
5704  }
5705  } else { // strlen($border) == 1
5706  if (strpos($border,'L') !== false) { // L
5707  $s .= sprintf('%F %F m ', $xL, $yL);
5708  $s .= sprintf('%F %F l ', $xT, $yT);
5709  $s .= 'S ';
5710  } elseif (strpos($border,'T') !== false) { // T
5711  $s .= sprintf('%F %F m ', $xT, $yT);
5712  $s .= sprintf('%F %F l ', $xR, $yR);
5713  $s .= 'S ';
5714  } elseif (strpos($border,'R') !== false) { // R
5715  $s .= sprintf('%F %F m ', $xR, $yR);
5716  $s .= sprintf('%F %F l ', $xB, $yB);
5717  $s .= 'S ';
5718  } elseif (strpos($border,'B') !== false) { // B
5719  $s .= sprintf('%F %F m ', $xB, $yB);
5720  $s .= sprintf('%F %F l ', $xL, $yL);
5721  $s .= 'S ';
5722  }
5723  }
5724  if (is_array($style) AND !empty($style)) {
5725  // reset border style to previous value
5726  $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n";
5727  }
5728  }
5729  return $s;
5730  }
5731 
5757  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) {
5758  $prev_cell_margin = $this->cell_margin;
5759  $prev_cell_padding = $this->cell_padding;
5760  // adjust internal padding
5761  $this->adjustCellPadding($border);
5762  $mc_padding = $this->cell_padding;
5763  $mc_margin = $this->cell_margin;
5764  $this->cell_padding['T'] = 0;
5765  $this->cell_padding['B'] = 0;
5766  $this->setCellMargins(0, 0, 0, 0);
5767  if (TCPDF_STATIC::empty_string($this->lasth) OR $reseth) {
5768  // reset row height
5769  $this->resetLastH();
5770  }
5772  $this->SetY($y);
5773  } else {
5774  $y = $this->GetY();
5775  }
5776  $resth = 0;
5777  if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) {
5778  // spit cell in more pages/columns
5779  $newh = ($this->PageBreakTrigger - $y);
5780  $resth = ($h - $newh); // cell to be printed on the next page/column
5781  $h = $newh;
5782  }
5783  // get current page number
5784  $startpage = $this->page;
5785  // get current column
5786  $startcolumn = $this->current_column;
5788  $this->SetX($x);
5789  } else {
5790  $x = $this->GetX();
5791  }
5792  // check page for no-write regions and adapt page margins if necessary
5793  list($x, $y) = $this->checkPageRegions(0, $x, $y);
5794  // apply margins
5795  $oy = $y + $mc_margin['T'];
5796  if ($this->rtl) {
5797  $ox = ($this->w - $x - $mc_margin['R']);
5798  } else {
5799  $ox = ($x + $mc_margin['L']);
5800  }
5801  $this->x = $ox;
5802  $this->y = $oy;
5803  // set width
5804  if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5805  if ($this->rtl) {
5806  $w = ($this->x - $this->lMargin - $mc_margin['L']);
5807  } else {
5808  $w = ($this->w - $this->x - $this->rMargin - $mc_margin['R']);
5809  }
5810  }
5811  // store original margin values
5814  if ($this->rtl) {
5815  $this->rMargin = ($this->w - $this->x);
5816  $this->lMargin = ($this->x - $w);
5817  } else {
5818  $this->lMargin = ($this->x);
5819  $this->rMargin = ($this->w - $this->x - $w);
5820  }
5821  $this->clMargin = $this->lMargin;
5822  $this->crMargin = $this->rMargin;
5823  if ($autopadding) {
5824  // add top padding
5825  $this->y += $mc_padding['T'];
5826  }
5827  if ($ishtml) { // ******* Write HTML text
5828  $this->writeHTML($txt, true, false, $reseth, true, $align);
5829  $nl = 1;
5830  } else { // ******* Write simple text
5831  $prev_FontSizePt = $this->FontSizePt;
5832  if ($fitcell) {
5833  // ajust height values
5834  $tobottom = ($this->h - $this->y - $this->bMargin - $this->cell_padding['T'] - $this->cell_padding['B']);
5835  $h = $maxh = max(min($h, $tobottom), min($maxh, $tobottom));
5836  }
5837  // vertical alignment
5838  if ($maxh > 0) {
5839  // get text height
5840  $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5841  if ($fitcell AND ($text_height > $maxh) AND ($this->FontSizePt > 1)) {
5842  // try to reduce font size to fit text on cell (use a quick search algorithm)
5843  $fmin = 1;
5844  $fmax = $this->FontSizePt;
5845  $diff_epsilon = (1 / $this->k); // one point (min resolution)
5846  $maxit = (2 * min(100, max(10, intval($fmax)))); // max number of iterations
5847  while ($maxit >= 0) {
5848  $fmid = (($fmax + $fmin) / 2);
5849  $this->SetFontSize($fmid, false);
5850  $this->resetLastH();
5851  $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5852  $diff = ($maxh - $text_height);
5853  if ($diff >= 0) {
5854  if ($diff <= $diff_epsilon) {
5855  break;
5856  }
5857  $fmin = $fmid;
5858  } else {
5859  $fmax = $fmid;
5860  }
5861  --$maxit;
5862  }
5863  if ($maxit < 0) {
5864  // premature exit, we get the minimum font value to fit the cell
5865  $this->SetFontSize($fmin);
5866  $this->resetLastH();
5867  $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5868  } else {
5869  $this->SetFontSize($fmid);
5870  $this->resetLastH();
5871  }
5872  }
5873  if ($text_height < $maxh) {
5874  if ($valign == 'M') {
5875  // text vertically centered
5876  $this->y += (($maxh - $text_height) / 2);
5877  } elseif ($valign == 'B') {
5878  // text vertically aligned on bottom
5879  $this->y += ($maxh - $text_height);
5880  }
5881  }
5882  }
5883  $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
5884  if ($fitcell) {
5885  // restore font size
5886  $this->SetFontSize($prev_FontSizePt);
5887  }
5888  }
5889  if ($autopadding) {
5890  // add bottom padding
5891  $this->y += $mc_padding['B'];
5892  }
5893  // Get end-of-text Y position
5894  $currentY = $this->y;
5895  // get latest page number
5896  $endpage = $this->page;
5897  if ($resth > 0) {
5898  $skip = ($endpage - $startpage);
5899  $tmpresth = $resth;
5900  while ($tmpresth > 0) {
5901  if ($skip <= 0) {
5902  // add a page (or trig AcceptPageBreak() for multicolumn mode)
5903  $this->checkPageBreak($this->PageBreakTrigger + 1);
5904  }
5905  if ($this->num_columns > 1) {
5906  $tmpresth -= ($this->h - $this->y - $this->bMargin);
5907  } else {
5908  $tmpresth -= ($this->h - $this->tMargin - $this->bMargin);
5909  }
5910  --$skip;
5911  }
5912  $currentY = $this->y;
5913  $endpage = $this->page;
5914  }
5915  // get latest column
5916  $endcolumn = $this->current_column;
5917  if ($this->num_columns == 0) {
5918  $this->num_columns = 1;
5919  }
5920  // disable page regions check
5922  $this->check_page_regions = false;
5923  // get border modes
5924  $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
5925  $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
5926  $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
5927  // design borders around HTML cells.
5928  for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
5929  $ccode = '';
5930  $this->setPage($page);
5931  if ($this->num_columns < 2) {
5932  // single-column mode
5933  $this->SetX($x);
5934  $this->y = $this->tMargin;
5935  }
5936  // account for margin changes
5937  if ($page > $startpage) {
5938  if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
5939  $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
5940  } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
5941  $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
5942  }
5943  }
5944  if ($startpage == $endpage) {
5945  // single page
5946  for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
5947  $this->selectColumn($column);
5948  if ($this->rtl) {
5949  $this->x -= $mc_margin['R'];
5950  } else {
5951  $this->x += $mc_margin['L'];
5952  }
5953  if ($startcolumn == $endcolumn) { // single column
5954  $cborder = $border;
5955  $h = max($h, ($currentY - $oy));
5956  $this->y = $oy;
5957  } elseif ($column == $startcolumn) { // first column
5958  $cborder = $border_start;
5959  $this->y = $oy;
5960  $h = $this->h - $this->y - $this->bMargin;
5961  } elseif ($column == $endcolumn) { // end column
5962  $cborder = $border_end;
5963  $h = $currentY - $this->y;
5964  if ($resth > $h) {
5965  $h = $resth;
5966  }
5967  } else { // middle column
5968  $cborder = $border_middle;
5969  $h = $this->h - $this->y - $this->bMargin;
5970  $resth -= $h;
5971  }
5972  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
5973  } // end for each column
5974  } elseif ($page == $startpage) { // first page
5975  for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
5976  $this->selectColumn($column);
5977  if ($this->rtl) {
5978  $this->x -= $mc_margin['R'];
5979  } else {
5980  $this->x += $mc_margin['L'];
5981  }
5982  if ($column == $startcolumn) { // first column
5983  $cborder = $border_start;
5984  $this->y = $oy;
5985  $h = $this->h - $this->y - $this->bMargin;
5986  } else { // middle column
5987  $cborder = $border_middle;
5988  $h = $this->h - $this->y - $this->bMargin;
5989  $resth -= $h;
5990  }
5991  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
5992  } // end for each column
5993  } elseif ($page == $endpage) { // last page
5994  for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
5995  $this->selectColumn($column);
5996  if ($this->rtl) {
5997  $this->x -= $mc_margin['R'];
5998  } else {
5999  $this->x += $mc_margin['L'];
6000  }
6001  if ($column == $endcolumn) {
6002  // end column
6003  $cborder = $border_end;
6004  $h = $currentY - $this->y;
6005  if ($resth > $h) {
6006  $h = $resth;
6007  }
6008  } else {
6009  // middle column
6010  $cborder = $border_middle;
6011  $h = $this->h - $this->y - $this->bMargin;
6012  $resth -= $h;
6013  }
6014  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6015  } // end for each column
6016  } else { // middle page
6017  for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
6018  $this->selectColumn($column);
6019  if ($this->rtl) {
6020  $this->x -= $mc_margin['R'];
6021  } else {
6022  $this->x += $mc_margin['L'];
6023  }
6024  $cborder = $border_middle;
6025  $h = $this->h - $this->y - $this->bMargin;
6026  $resth -= $h;
6027  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6028  } // end for each column
6029  }
6030  if ($cborder OR $fill) {
6031  $offsetlen = strlen($ccode);
6032  // draw border and fill
6033  if ($this->inxobj) {
6034  // we are inside an XObject template
6035  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
6036  $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
6037  $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
6038  $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
6039  } else {
6040  $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
6041  $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
6042  }
6043  $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
6044  $pstart = substr($pagebuff, 0, $pagemark);
6045  $pend = substr($pagebuff, $pagemark);
6046  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
6047  } else {
6048  if (end($this->transfmrk[$this->page]) !== false) {
6049  $pagemarkkey = key($this->transfmrk[$this->page]);
6050  $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
6051  $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
6052  } elseif ($this->InFooter) {
6053  $pagemark = $this->footerpos[$this->page];
6054  $this->footerpos[$this->page] += $offsetlen;
6055  } else {
6056  $pagemark = $this->intmrk[$this->page];
6057  $this->intmrk[$this->page] += $offsetlen;
6058  }
6059  $pagebuff = $this->getPageBuffer($this->page);
6060  $pstart = substr($pagebuff, 0, $pagemark);
6061  $pend = substr($pagebuff, $pagemark);
6062  $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
6063  }
6064  }
6065  } // end for each page
6066  // restore page regions check
6067  $this->check_page_regions = $check_page_regions;
6068  // Get end-of-cell Y position
6069  $currentY = $this->GetY();
6070  // restore previous values
6071  if ($this->num_columns > 1) {
6072  $this->selectColumn();
6073  } else {
6074  // restore original margins
6075  $this->lMargin = $lMargin;
6076  $this->rMargin = $rMargin;
6077  if ($this->page > $startpage) {
6078  // check for margin variations between pages (i.e. booklet mode)
6079  $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']);
6080  $dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']);
6081  if (($dl != 0) OR ($dr != 0)) {
6082  $this->lMargin += $dl;
6083  $this->rMargin += $dr;
6084  }
6085  }
6086  }
6087  if ($ln > 0) {
6088  //Go to the beginning of the next line
6089  $this->SetY($currentY + $mc_margin['B']);
6090  if ($ln == 2) {
6091  $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6092  }
6093  } else {
6094  // go left or right by case
6095  $this->setPage($startpage);
6096  $this->y = $y;
6097  $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6098  }
6099  $this->setContentMark();
6100  $this->cell_padding = $prev_cell_padding;
6101  $this->cell_margin = $prev_cell_margin;
6102  $this->clMargin = $this->lMargin;
6103  $this->crMargin = $this->rMargin;
6104  return $nl;
6105  }
6106 
6120  public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6121  if ($txt === NULL) {
6122  return 0;
6123  }
6124  if ($txt === '') {
6125  // empty string
6126  return 1;
6127  }
6128  // adjust internal padding
6129  $prev_cell_padding = $this->cell_padding;
6130  $prev_lasth = $this->lasth;
6131  if (is_array($cellpadding)) {
6132  $this->cell_padding = $cellpadding;
6133  }
6134  $this->adjustCellPadding($border);
6135  if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
6136  if ($this->rtl) {
6137  $w = $this->x - $this->lMargin;
6138  } else {
6139  $w = $this->w - $this->rMargin - $this->x;
6140  }
6141  }
6142  $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6143  if ($reseth) {
6144  // reset row height
6145  $this->resetLastH();
6146  }
6147  $lines = 1;
6148  $sum = 0;
6149  $chars = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont), $txt, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6150  $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
6151  $length = count($chars);
6152  $lastSeparator = -1;
6153  for ($i = 0; $i < $length; ++$i) {
6154  $c = $chars[$i];
6155  $charWidth = $charsWidth[$i];
6156  if (($c != 160)
6157  AND (($c == 173)
6158  OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6159  OR (($c == 45)
6160  AND ($i > 0) AND ($i < ($length - 1))
6161  AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i - 1)], $this->isunicode))
6162  AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6163  )
6164  )
6165  ) {
6166  $lastSeparator = $i;
6167  }
6168  if ((($sum + $charWidth) > $wmax) OR ($c == 10)) {
6169  ++$lines;
6170  if ($c == 10) {
6171  $lastSeparator = -1;
6172  $sum = 0;
6173  } elseif ($lastSeparator != -1) {
6174  $i = $lastSeparator;
6175  $lastSeparator = -1;
6176  $sum = 0;
6177  } else {
6178  $sum = $charWidth;
6179  }
6180  } else {
6181  $sum += $charWidth;
6182  }
6183  }
6184  if ($chars[($length - 1)] == 10) {
6185  --$lines;
6186  }
6187  $this->cell_padding = $prev_cell_padding;
6188  $this->lasth = $prev_lasth;
6189  return $lines;
6190  }
6191 
6239  public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6240  // adjust internal padding
6241  $prev_cell_padding = $this->cell_padding;
6242  $prev_lasth = $this->lasth;
6243  if (is_array($cellpadding)) {
6244  $this->cell_padding = $cellpadding;
6245  }
6246  $this->adjustCellPadding($border);
6247  $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
6248  $height = $this->getCellHeight(($lines * $this->FontSize), $autopadding);
6249  $this->cell_padding = $prev_cell_padding;
6250  $this->lasth = $prev_lasth;
6251  return $height;
6252  }
6253 
6272  public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
6273  // check page for no-write regions and adapt page margins if necessary
6274  list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
6275  if (strlen($txt) == 0) {
6276  // fix empty text
6277  $txt = ' ';
6278  }
6279  if ($margin === '') {
6280  // set default margins
6281  $margin = $this->cell_margin;
6282  }
6283  // remove carriage returns
6284  $s = str_replace("\r", '', $txt);
6285  // check if string contains arabic text
6286  if (preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_ARABIC, $s)) {
6287  $arabic = true;
6288  } else {
6289  $arabic = false;
6290  }
6291  // check if string contains RTL text
6292  if ($arabic OR ($this->tmprtl == 'R') OR preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $s)) {
6293  $rtlmode = true;
6294  } else {
6295  $rtlmode = false;
6296  }
6297  // get a char width
6298  $chrwidth = $this->GetCharWidth(46); // dot character
6299  // get array of unicode values
6300  $chars = TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont);
6301  // calculate maximum width for a single character on string
6302  $chrw = $this->GetArrStringWidth($chars, '', '', 0, true);
6303  array_walk($chrw, array($this, 'getRawCharWidth'));
6304  $maxchwidth = max($chrw);
6305  // get array of chars
6306  $uchars = TCPDF_FONTS::UTF8ArrayToUniArray($chars, $this->isunicode);
6307  // get the number of characters
6308  $nb = count($chars);
6309  // replacement for SHY character (minus symbol)
6310  $shy_replacement = 45;
6311  $shy_replacement_char = TCPDF_FONTS::unichr($shy_replacement, $this->isunicode);
6312  // widht for SHY replacement
6313  $shy_replacement_width = $this->GetCharWidth($shy_replacement);
6314  // page width
6315  $pw = $w = $this->w - $this->lMargin - $this->rMargin;
6316  // calculate remaining line width ($w)
6317  if ($this->rtl) {
6318  $w = $this->x - $this->lMargin;
6319  } else {
6320  $w = $this->w - $this->rMargin - $this->x;
6321  }
6322  // max column width
6323  $wmax = ($w - $wadj);
6324  if (!$firstline) {
6325  $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']);
6326  }
6327  if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) {
6328  // the maximum width character do not fit on column
6329  return '';
6330  }
6331  // minimum row height
6332  $row_height = max($h, $this->getCellHeight($this->FontSize));
6333  // max Y
6334  $maxy = $this->y + $maxh - max($row_height, $h);
6335  $start_page = $this->page;
6336  $i = 0; // character position
6337  $j = 0; // current starting position
6338  $sep = -1; // position of the last blank space
6339  $prevsep = $sep; // previous separator
6340  $shy = false; // true if the last blank is a soft hypen (SHY)
6341  $prevshy = $shy; // previous shy mode
6342  $l = 0; // current string length
6343  $nl = 0; //number of lines
6344  $linebreak = false;
6345  $pc = 0; // previous character
6346  // for each character
6347  while ($i < $nb) {
6348  if (($maxh > 0) AND ($this->y > $maxy) ) {
6349  break;
6350  }
6351  //Get the current character
6352  $c = $chars[$i];
6353  if ($c == 10) { // 10 = "\n" = new line
6354  //Explicit line break
6355  if ($align == 'J') {
6356  if ($this->rtl) {
6357  $talign = 'R';
6358  } else {
6359  $talign = 'L';
6360  }
6361  } else {
6362  $talign = $align;
6363  }
6364  $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6365  if ($firstline) {
6366  $startx = $this->x;
6367  $tmparr = array_slice($chars, $j, ($i - $j));
6368  if ($rtlmode) {
6369  $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6370  }
6371  $linew = $this->GetArrStringWidth($tmparr);
6372  unset($tmparr);
6373  if ($this->rtl) {
6374  $this->endlinex = $startx - $linew;
6375  } else {
6376  $this->endlinex = $startx + $linew;
6377  }
6378  $w = $linew;
6379  $tmpcellpadding = $this->cell_padding;
6380  if ($maxh == 0) {
6381  $this->SetCellPadding(0);
6382  }
6383  }
6384  if ($firstblock AND $this->isRTLTextDir()) {
6385  $tmpstr = $this->stringRightTrim($tmpstr);
6386  }
6387  // Skip newlines at the beginning of a page or column
6388  if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) {
6389  $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
6390  }
6391  unset($tmpstr);
6392  if ($firstline) {
6393  $this->cell_padding = $tmpcellpadding;
6394  return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6395  }
6396  ++$nl;
6397  $j = $i + 1;
6398  $l = 0;
6399  $sep = -1;
6400  $prevsep = $sep;
6401  $shy = false;
6402  // account for margin changes
6403  if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6404  $this->AcceptPageBreak();
6405  if ($this->rtl) {
6406  $this->x -= $margin['R'];
6407  } else {
6408  $this->x += $margin['L'];
6409  }
6410  $this->lMargin += $margin['L'];
6411  $this->rMargin += $margin['R'];
6412  }
6413  $w = $this->getRemainingWidth();
6414  $wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']);
6415  } else {
6416  // 160 is the non-breaking space.
6417  // 173 is SHY (Soft Hypen).
6418  // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
6419  // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
6420  // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
6421  if (($c != 160)
6422  AND (($c == 173)
6423  OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6424  OR (($c == 45)
6425  AND ($i < ($nb - 1))
6426  AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($pc, $this->isunicode))
6427  AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6428  )
6429  )
6430  ) {
6431  // update last blank space position
6432  $prevsep = $sep;
6433  $sep = $i;
6434  // check if is a SHY
6435  if (($c == 173) OR ($c == 45)) {
6436  $prevshy = $shy;
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  if (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) {
6459  $sep = $prevsep;
6460  $shy = $prevshy;
6461  }
6462  // we have reached the end of column
6463  if ($sep == -1) {
6464  // check if the line was already started
6465  if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth)))
6466  OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) {
6467  // print a void cell and go to next line
6468  $this->Cell($w, $h, '', 0, 1);
6469  $linebreak = true;
6470  if ($firstline) {
6471  return (TCPDF_FONTS::UniArrSubString($uchars, $j));
6472  }
6473  } else {
6474  // truncate the word because do not fit on column
6475  $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6476  if ($firstline) {
6477  $startx = $this->x;
6478  $tmparr = array_slice($chars, $j, ($i - $j));
6479  if ($rtlmode) {
6480  $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6481  }
6482  $linew = $this->GetArrStringWidth($tmparr);
6483  unset($tmparr);
6484  if ($this->rtl) {
6485  $this->endlinex = $startx - $linew;
6486  } else {
6487  $this->endlinex = $startx + $linew;
6488  }
6489  $w = $linew;
6490  $tmpcellpadding = $this->cell_padding;
6491  if ($maxh == 0) {
6492  $this->SetCellPadding(0);
6493  }
6494  }
6495  if ($firstblock AND $this->isRTLTextDir()) {
6496  $tmpstr = $this->stringRightTrim($tmpstr);
6497  }
6498  $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6499  unset($tmpstr);
6500  if ($firstline) {
6501  $this->cell_padding = $tmpcellpadding;
6502  return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6503  }
6504  $j = $i;
6505  --$i;
6506  }
6507  } else {
6508  // word wrapping
6509  if ($this->rtl AND (!$firstblock) AND ($sep < $i)) {
6510  $endspace = 1;
6511  } else {
6512  $endspace = 0;
6513  }
6514  // check the length of the next string
6515  $strrest = TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace));
6516  $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $this->stringTrim($strrest));
6517  if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
6518  // truncate the word because do not fit on a full page width
6519  $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6520  if ($firstline) {
6521  $startx = $this->x;
6522  $tmparr = array_slice($chars, $j, ($i - $j));
6523  if ($rtlmode) {
6524  $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6525  }
6526  $linew = $this->GetArrStringWidth($tmparr);
6527  unset($tmparr);
6528  if ($this->rtl) {
6529  $this->endlinex = ($startx - $linew);
6530  } else {
6531  $this->endlinex = ($startx + $linew);
6532  }
6533  $w = $linew;
6534  $tmpcellpadding = $this->cell_padding;
6535  if ($maxh == 0) {
6536  $this->SetCellPadding(0);
6537  }
6538  }
6539  if ($firstblock AND $this->isRTLTextDir()) {
6540  $tmpstr = $this->stringRightTrim($tmpstr);
6541  }
6542  $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6543  unset($tmpstr);
6544  if ($firstline) {
6545  $this->cell_padding = $tmpcellpadding;
6546  return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6547  }
6548  $j = $i;
6549  --$i;
6550  } else {
6551  // word wrapping
6552  if ($shy) {
6553  // add hypen (minus symbol) at the end of the line
6554  $shy_width = $tmp_shy_replacement_width;
6555  if ($this->rtl) {
6556  $shy_char_left = $tmp_shy_replacement_char;
6557  $shy_char_right = '';
6558  } else {
6559  $shy_char_left = '';
6560  $shy_char_right = $tmp_shy_replacement_char;
6561  }
6562  } else {
6563  $shy_width = 0;
6564  $shy_char_left = '';
6565  $shy_char_right = '';
6566  }
6567  $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, ($sep + $endspace));
6568  if ($firstline) {
6569  $startx = $this->x;
6570  $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
6571  if ($rtlmode) {
6572  $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6573  }
6574  $linew = $this->GetArrStringWidth($tmparr);
6575  unset($tmparr);
6576  if ($this->rtl) {
6577  $this->endlinex = $startx - $linew - $shy_width;
6578  } else {
6579  $this->endlinex = $startx + $linew + $shy_width;
6580  }
6581  $w = $linew;
6582  $tmpcellpadding = $this->cell_padding;
6583  if ($maxh == 0) {
6584  $this->SetCellPadding(0);
6585  }
6586  }
6587  // print the line
6588  if ($firstblock AND $this->isRTLTextDir()) {
6589  $tmpstr = $this->stringRightTrim($tmpstr);
6590  }
6591  $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
6592  unset($tmpstr);
6593  if ($firstline) {
6594  if ($chars[$sep] == 45) {
6595  $endspace += 1;
6596  }
6597  // return the remaining text
6598  $this->cell_padding = $tmpcellpadding;
6599  return (TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace)));
6600  }
6601  $i = $sep;
6602  $sep = -1;
6603  $shy = false;
6604  $j = ($i + 1);
6605  }
6606  }
6607  // account for margin changes
6608  if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6609  $this->AcceptPageBreak();
6610  if ($this->rtl) {
6611  $this->x -= $margin['R'];
6612  } else {
6613  $this->x += $margin['L'];
6614  }
6615  $this->lMargin += $margin['L'];
6616  $this->rMargin += $margin['R'];
6617  }
6618  $w = $this->getRemainingWidth();
6619  $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6620  if ($linebreak) {
6621  $linebreak = false;
6622  } else {
6623  ++$nl;
6624  $l = 0;
6625  }
6626  }
6627  }
6628  // save last character
6629  $pc = $c;
6630  ++$i;
6631  } // end while i < nb
6632  // print last substring (if any)
6633  if ($l > 0) {
6634  switch ($align) {
6635  case 'J':
6636  case 'C': {
6637  $w = $w;
6638  break;
6639  }
6640  case 'L': {
6641  if ($this->rtl) {
6642  $w = $w;
6643  } else {
6644  $w = $l;
6645  }
6646  break;
6647  }
6648  case 'R': {
6649  if ($this->rtl) {
6650  $w = $l;
6651  } else {
6652  $w = $w;
6653  }
6654  break;
6655  }
6656  default: {
6657  $w = $l;
6658  break;
6659  }
6660  }
6661  $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $nb);
6662  if ($firstline) {
6663  $startx = $this->x;
6664  $tmparr = array_slice($chars, $j, ($nb - $j));
6665  if ($rtlmode) {
6666  $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6667  }
6668  $linew = $this->GetArrStringWidth($tmparr);
6669  unset($tmparr);
6670  if ($this->rtl) {
6671  $this->endlinex = $startx - $linew;
6672  } else {
6673  $this->endlinex = $startx + $linew;
6674  }
6675  $w = $linew;
6676  $tmpcellpadding = $this->cell_padding;
6677  if ($maxh == 0) {
6678  $this->SetCellPadding(0);
6679  }
6680  }
6681  if ($firstblock AND $this->isRTLTextDir()) {
6682  $tmpstr = $this->stringRightTrim($tmpstr);
6683  }
6684  $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
6685  unset($tmpstr);
6686  if ($firstline) {
6687  $this->cell_padding = $tmpcellpadding;
6688  return (TCPDF_FONTS::UniArrSubString($uchars, $nb));
6689  }
6690  ++$nl;
6691  }
6692  if ($firstline) {
6693  return '';
6694  }
6695  return $nl;
6696  }
6697 
6703  protected function getRemainingWidth() {
6704  list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y);
6705  if ($this->rtl) {
6706  return ($this->x - $this->lMargin);
6707  } else {
6708  return ($this->w - $this->rMargin - $this->x);
6709  }
6710  }
6711 
6723  protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
6724  if ($w <= 0) {
6725  // set maximum width
6726  $w = ($this->w - $this->lMargin - $this->rMargin);
6727  if ($w <= 0) {
6728  $w = 1;
6729  }
6730  }
6731  if ($h <= 0) {
6732  // set maximum height
6733  $h = ($this->PageBreakTrigger - $this->tMargin);
6734  if ($h <= 0) {
6735  $h = 1;
6736  }
6737  }
6738  // resize the block to be vertically contained on a single page or single column
6739  if ($fitonpage OR $this->AutoPageBreak) {
6740  $ratio_wh = ($w / $h);
6741  if ($h > ($this->PageBreakTrigger - $this->tMargin)) {
6742  $h = $this->PageBreakTrigger - $this->tMargin;
6743  $w = ($h * $ratio_wh);
6744  }
6745  // resize the block to be horizontally contained on a single page or single column
6746  if ($fitonpage) {
6747  $maxw = ($this->w - $this->lMargin - $this->rMargin);
6748  if ($w > $maxw) {
6749  $w = $maxw;
6750  $h = ($w / $ratio_wh);
6751  }
6752  }
6753  }
6754  // Check whether we need a new page or new column first as this does not fit
6755  $prev_x = $this->x;
6756  $prev_y = $this->y;
6757  if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) {
6758  $y = $this->y;
6759  if ($this->rtl) {
6760  $x += ($prev_x - $this->x);
6761  } else {
6762  $x += ($this->x - $prev_x);
6763  }
6764  $this->newline = true;
6765  }
6766  // resize the block to be contained on the remaining available page or column space
6767  if ($fitonpage) {
6768  //$ratio_wh = ($w / $h);
6769  // PATCH BEGIN
6770  if($h)
6771  {
6772  $ratio_wh = ($w / $h);
6773  }
6774  else
6775  {
6776  $ratio_wh = 1;
6777  }
6778  // PATCH END
6779 
6780  if (($y + $h) > $this->PageBreakTrigger) {
6781  $h = $this->PageBreakTrigger - $y;
6782  $w = ($h * $ratio_wh);
6783  }
6784  if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
6785  $w = $this->w - $this->rMargin - $x;
6786  $h = ($w / $ratio_wh);
6787  } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
6788  $w = $x - $this->lMargin;
6789  $h = ($w / $ratio_wh);
6790  }
6791  }
6792  return array($w, $h, $x, $y);
6793  }
6794 
6829  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()) {
6830  if ($this->state != 2) {
6831  return;
6832  }
6833  if (strcmp($x, '') === 0) {
6834  $x = $this->x;
6835  }
6836  if (strcmp($y, '') === 0) {
6837  $y = $this->y;
6838  }
6839  // check page for no-write regions and adapt page margins if necessary
6840  list($x, $y) = $this->checkPageRegions($h, $x, $y);
6841  $exurl = ''; // external streams
6842  $imsize = FALSE;
6843  // check if we are passing an image as file or string
6844  if ($file[0] === '@') {
6845  // image from string
6846  $imgdata = substr($file, 1);
6847  } else { // image file
6848  if ($file[0] === '*') {
6849  // image as external stream
6850  $file = substr($file, 1);
6851  $exurl = $file;
6852  }
6853  // check if is a local file
6854  if (!@file_exists($file)) {
6855  // try to encode spaces on filename
6856  $tfile = str_replace(' ', '%20', $file);
6857  if (@file_exists($tfile)) {
6858  $file = $tfile;
6859  }
6860  }
6861  if (($imsize = @getimagesize($file)) === FALSE) {
6862  if (in_array($file, $this->imagekeys)) {
6863  // get existing image data
6864  $info = $this->getImageBuffer($file);
6865  $imsize = array($info['w'], $info['h']);
6866  } elseif (strpos($file, '__tcpdf_'.$this->file_id.'_img') === FALSE) {
6868  }
6869  }
6870  }
6871  if (!empty($imgdata)) {
6872  // copy image to cache
6873  $original_file = $file;
6874  $file = TCPDF_STATIC::getObjFilename('img', $this->file_id);
6875  $fp = TCPDF_STATIC::fopenLocal($file, 'w');
6876  if (!$fp) {
6877  $this->Error('Unable to write file: '.$file);
6878  }
6879  fwrite($fp, $imgdata);
6880  fclose($fp);
6881  unset($imgdata);
6882  $imsize = @getimagesize($file);
6883  if ($imsize === FALSE) {
6884  unlink($file);
6885  $file = $original_file;
6886  }
6887  }
6888  if ($imsize === FALSE) {
6889  if (($w > 0) AND ($h > 0)) {
6890  // get measures from specified data
6891  $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6892  $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6893  $imsize = array($pw, $ph);
6894  } else {
6895  $this->Error('[Image] Unable to get the size of the image: '.$file);
6896  }
6897  }
6898  // file hash
6899  $filehash = md5($file);
6900  // get original image width and height in pixels
6901  list($pixw, $pixh) = $imsize;
6902  // calculate image width and height on document
6903  if (($w <= 0) AND ($h <= 0)) {
6904  // convert image size to document unit
6905  $w = $this->pixelsToUnits($pixw);
6906  $h = $this->pixelsToUnits($pixh);
6907  } elseif ($w <= 0) {
6908  $w = $h * $pixw / $pixh;
6909  } elseif ($h <= 0) {
6910  $h = $w * $pixh / $pixw;
6911  } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
6912  if (strlen($fitbox) !== 2) {
6913  // set default alignment
6914  $fitbox = '--';
6915  }
6916  // scale image dimensions proportionally to fit within the ($w, $h) box
6917  if ((($w * $pixh) / ($h * $pixw)) < 1) {
6918  // store current height
6919  $oldh = $h;
6920  // calculate new height
6921  $h = $w * $pixh / $pixw;
6922  // height difference
6923  $hdiff = ($oldh - $h);
6924  // vertical alignment
6925  switch (strtoupper($fitbox[1])) {
6926  case 'T': {
6927  break;
6928  }
6929  case 'M': {
6930  $y += ($hdiff / 2);
6931  break;
6932  }
6933  case 'B': {
6934  $y += $hdiff;
6935  break;
6936  }
6937  }
6938  } else {
6939  // store current width
6940  $oldw = $w;
6941  // calculate new width
6942  $w = $h * $pixw / $pixh;
6943  // width difference
6944  $wdiff = ($oldw - $w);
6945  // horizontal alignment
6946  switch (strtoupper($fitbox[0])) {
6947  case 'L': {
6948  if ($this->rtl) {
6949  $x -= $wdiff;
6950  }
6951  break;
6952  }
6953  case 'C': {
6954  if ($this->rtl) {
6955  $x -= ($wdiff / 2);
6956  } else {
6957  $x += ($wdiff / 2);
6958  }
6959  break;
6960  }
6961  case 'R': {
6962  if (!$this->rtl) {
6963  $x += $wdiff;
6964  }
6965  break;
6966  }
6967  }
6968  }
6969  }
6970  // fit the image on available space
6971  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
6972  // calculate new minimum dimensions in pixels
6973  $neww = round($w * $this->k * $dpi / $this->dpi);
6974  $newh = round($h * $this->k * $dpi / $this->dpi);
6975  // check if resize is necessary (resize is used only to reduce the image)
6976  $newsize = ($neww * $newh);
6977  $pixsize = ($pixw * $pixh);
6978  if (intval($resize) == 2) {
6979  $resize = true;
6980  } elseif ($newsize >= $pixsize) {
6981  $resize = false;
6982  }
6983  // check if image has been already added on document
6984  $newimage = true;
6985  if (in_array($file, $this->imagekeys)) {
6986  $newimage = false;
6987  // get existing image data
6988  $info = $this->getImageBuffer($file);
6989  if (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE) {
6990  // check if the newer image is larger
6991  $oldsize = ($info['w'] * $info['h']);
6992  if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
6993  $newimage = true;
6994  }
6995  }
6996  } elseif (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE)) {
6997  // create temp image file (without alpha channel)
6998  $tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash;
6999  // create temp alpha file
7000  $tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash;
7001  // check for cached images
7002  if (in_array($tempfile_plain, $this->imagekeys)) {
7003  // get existing image data
7004  $info = $this->getImageBuffer($tempfile_plain);
7005  // check if the newer image is larger
7006  $oldsize = ($info['w'] * $info['h']);
7007  if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7008  $newimage = true;
7009  } else {
7010  $newimage = false;
7011  // embed mask image
7012  $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7013  // embed image, masked with previously embedded mask
7014  return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7015  }
7016  }
7017  }
7018  if ($newimage) {
7019  //First use of image, get info
7020  $type = strtolower($type);
7021  if ($type == '') {
7022  $type = TCPDF_IMAGES::getImageFileType($file, $imsize);
7023  } elseif ($type == 'jpg') {
7024  $type = 'jpeg';
7025  }
7026  $mqr = TCPDF_STATIC::get_mqr();
7027  TCPDF_STATIC::set_mqr(false);
7028  // Specific image handlers (defined on TCPDF_IMAGES CLASS)
7029  $mtd = '_parse'.$type;
7030  // GD image handler function
7031  $gdfunction = 'imagecreatefrom'.$type;
7032  $info = false;
7033  if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
7034  // TCPDF image functions
7035  $info = TCPDF_IMAGES::$mtd($file);
7036  if (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE)
7037  AND (($info === 'pngalpha') OR (isset($info['trns']) AND !empty($info['trns'])))) {
7038  return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
7039  }
7040  }
7041  if (($info === false) AND function_exists($gdfunction)) {
7042  try {
7043  // GD library
7044  $img = $gdfunction($file);
7045  if ($img !== false) {
7046  if ($resize) {
7047  $imgr = imagecreatetruecolor($neww, $newh);
7048  if (($type == 'gif') OR ($type == 'png')) {
7050  }
7051  imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
7052  $img = $imgr;
7053  }
7054  if (($type == 'gif') OR ($type == 'png')) {
7055  $info = TCPDF_IMAGES::_toPNG($img, TCPDF_STATIC::getObjFilename('img', $this->file_id));
7056  } else {
7057  $info = TCPDF_IMAGES::_toJPEG($img, $this->jpeg_quality, TCPDF_STATIC::getObjFilename('img', $this->file_id));
7058  }
7059  }
7060  } catch(Exception $e) {
7061  $info = false;
7062  }
7063  }
7064  if (($info === false) AND extension_loaded('imagick')) {
7065  try {
7066  // ImageMagick library
7067  $img = new Imagick();
7068  if ($type == 'svg') {
7069  if ($file[0] === '@') {
7070  // image from string
7071  $svgimg = substr($file, 1);
7072  } else {
7073  // get SVG file content
7075  }
7076  if ($svgimg !== FALSE) {
7077  // get width and height
7078  $regs = array();
7079  if (preg_match('/<svg([^>]*)>/si', $svgimg, $regs)) {
7080  $svgtag = $regs[1];
7081  $tmp = array();
7082  if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7083  $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7084  $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit;
7085  $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
7086  } else {
7087  $ow = $w;
7088  }
7089  $tmp = array();
7090  if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7091  $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7092  $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit;
7093  $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
7094  } else {
7095  $oh = $h;
7096  }
7097  $tmp = array();
7098  if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
7099  $vbw = ($ow * $this->imgscale * $this->k);
7100  $vbh = ($oh * $this->imgscale * $this->k);
7101  $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh);
7102  $svgtag = $vbox.$svgtag;
7103  }
7104  $svgimg = preg_replace('/<svg([^>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
7105  }
7106  $img->readImageBlob($svgimg);
7107  }
7108  } else {
7109  $img->readImage($file);
7110  }
7111  if ($resize) {
7112  $img->resizeImage($neww, $newh, 10, 1, false);
7113  }
7114  $img->setCompressionQuality($this->jpeg_quality);
7115  $img->setImageFormat('jpeg');
7116  $tempname = TCPDF_STATIC::getObjFilename('img', $this->file_id);
7117  $img->writeImage($tempname);
7118  $info = TCPDF_IMAGES::_parsejpeg($tempname);
7119  unlink($tempname);
7120  $img->destroy();
7121  } catch(Exception $e) {
7122  $info = false;
7123  }
7124  }
7125  if ($info === false) {
7126  // unable to process image
7127  return;
7128  }
7129  TCPDF_STATIC::set_mqr($mqr);
7130  if ($ismask) {
7131  // force grayscale
7132  $info['cs'] = 'DeviceGray';
7133  }
7134  if ($imgmask !== false) {
7135  $info['masked'] = $imgmask;
7136  }
7137  if (!empty($exurl)) {
7138  $info['exurl'] = $exurl;
7139  }
7140  // array of alternative images
7141  $info['altimgs'] = $altimgs;
7142  // add image to document
7143  $info['i'] = $this->setImageBuffer($file, $info);
7144  }
7145  // set alignment
7146  $this->img_rb_y = $y + $h;
7147  // set alignment
7148  if ($this->rtl) {
7149  if ($palign == 'L') {
7150  $ximg = $this->lMargin;
7151  } elseif ($palign == 'C') {
7152  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7153  } elseif ($palign == 'R') {
7154  $ximg = $this->w - $this->rMargin - $w;
7155  } else {
7156  $ximg = $x - $w;
7157  }
7158  $this->img_rb_x = $ximg;
7159  } else {
7160  if ($palign == 'L') {
7161  $ximg = $this->lMargin;
7162  } elseif ($palign == 'C') {
7163  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7164  } elseif ($palign == 'R') {
7165  $ximg = $this->w - $this->rMargin - $w;
7166  } else {
7167  $ximg = $x;
7168  }
7169  $this->img_rb_x = $ximg + $w;
7170  }
7171  if ($ismask OR $hidden) {
7172  // image is not displayed
7173  return $info['i'];
7174  }
7175  $xkimg = $ximg * $this->k;
7176  if (!$alt) {
7177  // only non-alternative immages will be set
7178  $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']));
7179  }
7180  if (!empty($border)) {
7181  $bx = $this->x;
7182  $by = $this->y;
7183  $this->x = $ximg;
7184  if ($this->rtl) {
7185  $this->x += $w;
7186  }
7187  $this->y = $y;
7188  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
7189  $this->x = $bx;
7190  $this->y = $by;
7191  }
7192  if ($link) {
7193  $this->Link($ximg, $y, $w, $h, $link, 0);
7194  }
7195  // set pointer to align the next text/objects
7196  switch($align) {
7197  case 'T': {
7198  $this->y = $y;
7199  $this->x = $this->img_rb_x;
7200  break;
7201  }
7202  case 'M': {
7203  $this->y = $y + round($h/2);
7204  $this->x = $this->img_rb_x;
7205  break;
7206  }
7207  case 'B': {
7208  $this->y = $this->img_rb_y;
7209  $this->x = $this->img_rb_x;
7210  break;
7211  }
7212  case 'N': {
7213  $this->SetY($this->img_rb_y);
7214  break;
7215  }
7216  default:{
7217  break;
7218  }
7219  }
7220  $this->endlinex = $this->img_rb_x;
7221  if ($this->inxobj) {
7222  // we are inside an XObject template
7223  $this->xobjects[$this->xobjid]['images'][] = $info['i'];
7224  }
7225  return $info['i'];
7226  }
7227 
7249  protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
7250  // create temp images
7251  if (empty($filehash)) {
7252  $filehash = md5($file);
7253  }
7254  // create temp image file (without alpha channel)
7255  $tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash;
7256  // create temp alpha file
7257  $tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash;
7258  $parsed = false;
7259  $parse_error = '';
7260  // ImageMagick extension
7261  if (($parsed === false) AND extension_loaded('imagick')) {
7262  try {
7263  // ImageMagick library
7264  $img = new Imagick();
7265  $img->readImage($file);
7266  // clone image object
7267  $imga = TCPDF_STATIC::objclone($img);
7268  // extract alpha channel
7269  if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) {
7270  $img->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT);
7271  } else {
7272  $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
7273  $img->negateImage(true);
7274  }
7275  $img->setImageFormat('png');
7276  $img->writeImage($tempfile_alpha);
7277  // remove alpha channel
7278  if (method_exists($imga, 'setImageMatte')) {
7279  $imga->setImageMatte(false);
7280  } else {
7281  $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
7282  }
7283  $imga->setImageFormat('png');
7284  $imga->writeImage($tempfile_plain);
7285  $parsed = true;
7286  } catch (Exception $e) {
7287  // Imagemagick fails, try with GD
7288  $parse_error = 'Imagick library error: '.$e->getMessage();
7289  }
7290  }
7291  // GD extension
7292  if (($parsed === false) AND function_exists('imagecreatefrompng')) {
7293  try {
7294  // generate images
7295  $img = imagecreatefrompng($file);
7296  $imgalpha = imagecreate($wpx, $hpx);
7297  // generate gray scale palette (0 -> 255)
7298  for ($c = 0; $c < 256; ++$c) {
7299  ImageColorAllocate($imgalpha, $c, $c, $c);
7300  }
7301  // extract alpha channel
7302  for ($xpx = 0; $xpx < $wpx; ++$xpx) {
7303  for ($ypx = 0; $ypx < $hpx; ++$ypx) {
7304  $color = imagecolorat($img, $xpx, $ypx);
7305  // get and correct gamma color
7306  $alpha = $this->getGDgamma($img, $color);
7307  imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
7308  }
7309  }
7310  imagepng($imgalpha, $tempfile_alpha);
7311  imagedestroy($imgalpha);
7312  // extract image without alpha channel
7313  $imgplain = imagecreatetruecolor($wpx, $hpx);
7314  imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
7315  imagepng($imgplain, $tempfile_plain);
7316  imagedestroy($imgplain);
7317  $parsed = true;
7318  } catch (Exception $e) {
7319  // GD fails
7320  $parse_error = 'GD library error: '.$e->getMessage();
7321  }
7322  }
7323  if ($parsed === false) {
7324  if (empty($parse_error)) {
7325  $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
7326  } else {
7327  $this->Error($parse_error);
7328  }
7329  }
7330  // embed mask image
7331  $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7332  // embed image, masked with previously embedded mask
7333  $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7334  }
7335 
7343  protected function getGDgamma($img, $c) {
7344  if (!isset($this->gdgammacache['#'.$c])) {
7345  $colors = imagecolorsforindex($img, $c);
7346  // GD alpha is only 7 bit (0 -> 127)
7347  $this->gdgammacache['#'.$c] = (((127 - $colors['alpha']) / 127) * 255);
7348  // correct gamma
7349  $this->gdgammacache['#'.$c] = (pow(($this->gdgammacache['#'.$c] / 255), 2.2) * 255);
7350  // store the latest values on cache to improve performances
7351  if (count($this->gdgammacache) > 8) {
7352  // remove one element from the cache array
7353  array_shift($this->gdgammacache);
7354  }
7355  }
7356  return $this->gdgammacache['#'.$c];
7357  }
7358 
7368  public function Ln($h='', $cell=false) {
7369  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'])) {
7370  // revove vertical space from the top of the column
7371  return;
7372  }
7373  if ($cell) {
7374  if ($this->rtl) {
7375  $cellpadding = $this->cell_padding['R'];
7376  } else {
7377  $cellpadding = $this->cell_padding['L'];
7378  }
7379  } else {
7380  $cellpadding = 0;
7381  }
7382  if ($this->rtl) {
7383  $this->x = $this->w - $this->rMargin - $cellpadding;
7384  } else {
7385  $this->x = $this->lMargin + $cellpadding;
7386  }
7387  if (is_string($h)) {
7388  $h = $this->lasth;
7389  }
7390  $this->y += $h;
7391  $this->newline = true;
7392  }
7393 
7402  public function GetX() {
7403  //Get x position
7404  if ($this->rtl) {
7405  return ($this->w - $this->x);
7406  } else {
7407  return $this->x;
7408  }
7409  }
7410 
7418  public function GetAbsX() {
7419  return $this->x;
7420  }
7421 
7429  public function GetY() {
7430  return $this->y;
7431  }
7432 
7442  public function SetX($x, $rtloff=false) {
7443  $x = floatval($x);
7444  if (!$rtloff AND $this->rtl) {
7445  if ($x >= 0) {
7446  $this->x = $this->w - $x;
7447  } else {
7448  $this->x = abs($x);
7449  }
7450  } else {
7451  if ($x >= 0) {
7452  $this->x = $x;
7453  } else {
7454  $this->x = $this->w + $x;
7455  }
7456  }
7457  if ($this->x < 0) {
7458  $this->x = 0;
7459  }
7460  if ($this->x > $this->w) {
7461  $this->x = $this->w;
7462  }
7463  }
7464 
7475  public function SetY($y, $resetx=true, $rtloff=false) {
7476  $y = floatval($y);
7477  if ($resetx) {
7478  //reset x
7479  if (!$rtloff AND $this->rtl) {
7480  $this->x = $this->w - $this->rMargin;
7481  } else {
7482  $this->x = $this->lMargin;
7483  }
7484  }
7485  if ($y >= 0) {
7486  $this->y = $y;
7487  } else {
7488  $this->y = $this->h + $y;
7489  }
7490  if ($this->y < 0) {
7491  $this->y = 0;
7492  }
7493  if ($this->y > $this->h) {
7494  $this->y = $this->h;
7495  }
7496  }
7497 
7508  public function SetXY($x, $y, $rtloff=false) {
7509  $this->SetY($y, false, $rtloff);
7510  $this->SetX($x, $rtloff);
7511  }
7512 
7520  public function SetAbsX($x) {
7521  $this->x = floatval($x);
7522  }
7523 
7531  public function SetAbsY($y) {
7532  $this->y = floatval($y);
7533  }
7534 
7543  public function SetAbsXY($x, $y) {
7544  $this->SetAbsX($x);
7545  $this->SetAbsY($y);
7546  }
7547 
7559  public function Output($name='doc.pdf', $dest='I') {
7560  //Output PDF to some destination
7561  //Finish document if necessary
7562  if ($this->state < 3) {
7563  $this->Close();
7564  }
7565  //Normalize parameters
7566  if (is_bool($dest)) {
7567  $dest = $dest ? 'D' : 'F';
7568  }
7569  $dest = strtoupper($dest);
7570  if ($dest[0] != 'F') {
7571  $name = preg_replace('/[\s]+/', '_', $name);
7572  $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
7573  }
7574  if ($this->sign) {
7575  // *** apply digital signature to the document ***
7576  // get the document content
7577  $pdfdoc = $this->getBuffer();
7578  // remove last newline
7579  $pdfdoc = substr($pdfdoc, 0, -1);
7580  // remove filler space
7581  $byterange_string_len = strlen(TCPDF_STATIC::$byterange_string);
7582  // define the ByteRange
7583  $byte_range = array();
7584  $byte_range[0] = 0;
7585  $byte_range[1] = strpos($pdfdoc, TCPDF_STATIC::$byterange_string) + $byterange_string_len + 10;
7586  $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
7587  $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
7588  $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
7589  // replace the ByteRange
7590  $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
7591  $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
7592  $pdfdoc = str_replace(TCPDF_STATIC::$byterange_string, $byterange, $pdfdoc);
7593  // write the document to a temporary folder
7594  $tempdoc = TCPDF_STATIC::getObjFilename('doc', $this->file_id);
7595  $f = TCPDF_STATIC::fopenLocal($tempdoc, 'wb');
7596  if (!$f) {
7597  $this->Error('Unable to create temporary file: '.$tempdoc);
7598  }
7599  $pdfdoc_length = strlen($pdfdoc);
7600  fwrite($f, $pdfdoc, $pdfdoc_length);
7601  fclose($f);
7602  // get digital signature via openssl library
7603  $tempsign = TCPDF_STATIC::getObjFilename('sig', $this->file_id);
7604  if (empty($this->signature_data['extracerts'])) {
7605  openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
7606  } else {
7607  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']);
7608  }
7609  // read signature
7610  $signature = file_get_contents($tempsign);
7611  // extract signature
7612  $signature = substr($signature, $pdfdoc_length);
7613  $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
7614  $tmparr = explode("\n\n", $signature);
7615  $signature = $tmparr[1];
7616  // decode signature
7617  $signature = base64_decode(trim($signature));
7618  // add TSA timestamp to signature
7619  $signature = $this->applyTSA($signature);
7620  // convert signature to hex
7621  $signature = current(unpack('H*', $signature));
7622  $signature = str_pad($signature, $this->signature_max_length, '0');
7623  // Add signature to the document
7624  $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
7625  $this->bufferlen = strlen($this->buffer);
7626  }
7627  switch($dest) {
7628  case 'I': {
7629  // Send PDF to the standard output
7630  if (ob_get_contents()) {
7631  $this->Error('Some data has already been output, can\'t send PDF file');
7632  }
7633  if (php_sapi_name() != 'cli') {
7634  // send output to a browser
7635  header('Content-Type: application/pdf');
7636  if (headers_sent()) {
7637  $this->Error('Some data has already been output to browser, can\'t send PDF file');
7638  }
7639  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7640  //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7641  header('Pragma: public');
7642  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7643  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7644  header('Content-Disposition: inline; filename="'.basename($name).'"');
7645  TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7646  } else {
7647  echo $this->getBuffer();
7648  }
7649  break;
7650  }
7651  case 'D': {
7652  // download PDF as file
7653  if (ob_get_contents()) {
7654  $this->Error('Some data has already been output, can\'t send PDF file');
7655  }
7656  header('Content-Description: File Transfer');
7657  if (headers_sent()) {
7658  $this->Error('Some data has already been output to browser, can\'t send PDF file');
7659  }
7660  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7661  //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7662  header('Pragma: public');
7663  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7664  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7665  // force download dialog
7666  if (strpos(php_sapi_name(), 'cgi') === false) {
7667  header('Content-Type: application/force-download');
7668  header('Content-Type: application/octet-stream', false);
7669  header('Content-Type: application/download', false);
7670  header('Content-Type: application/pdf', false);
7671  } else {
7672  header('Content-Type: application/pdf');
7673  }
7674  // use the Content-Disposition header to supply a recommended filename
7675  header('Content-Disposition: attachment; filename="'.basename($name).'"');
7676  header('Content-Transfer-Encoding: binary');
7677  TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7678  break;
7679  }
7680  case 'F':
7681  case 'FI':
7682  case 'FD': {
7683  // save PDF to a local file
7684  $f = TCPDF_STATIC::fopenLocal($name, 'wb');
7685  if (!$f) {
7686  $this->Error('Unable to create output file: '.$name);
7687  }
7688  fwrite($f, $this->getBuffer(), $this->bufferlen);
7689  fclose($f);
7690  if ($dest == 'FI') {
7691  // send headers to browser
7692  header('Content-Type: application/pdf');
7693  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7694  //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7695  header('Pragma: public');
7696  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7697  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7698  header('Content-Disposition: inline; filename="'.basename($name).'"');
7699  TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7700  } elseif ($dest == 'FD') {
7701  // send headers to browser
7702  if (ob_get_contents()) {
7703  $this->Error('Some data has already been output, can\'t send PDF file');
7704  }
7705  header('Content-Description: File Transfer');
7706  if (headers_sent()) {
7707  $this->Error('Some data has already been output to browser, can\'t send PDF file');
7708  }
7709  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7710  header('Pragma: public');
7711  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7712  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7713  // force download dialog
7714  if (strpos(php_sapi_name(), 'cgi') === false) {
7715  header('Content-Type: application/force-download');
7716  header('Content-Type: application/octet-stream', false);
7717  header('Content-Type: application/download', false);
7718  header('Content-Type: application/pdf', false);
7719  } else {
7720  header('Content-Type: application/pdf');
7721  }
7722  // use the Content-Disposition header to supply a recommended filename
7723  header('Content-Disposition: attachment; filename="'.basename($name).'"');
7724  header('Content-Transfer-Encoding: binary');
7725  TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7726  }
7727  break;
7728  }
7729  case 'E': {
7730  // return PDF as base64 mime multi-part email attachment (RFC 2045)
7731  $retval = 'Content-Type: application/pdf;'."\r\n";
7732  $retval .= ' name="'.$name.'"'."\r\n";
7733  $retval .= 'Content-Transfer-Encoding: base64'."\r\n";
7734  $retval .= 'Content-Disposition: attachment;'."\r\n";
7735  $retval .= ' filename="'.$name.'"'."\r\n\r\n";
7736  $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
7737  return $retval;
7738  }
7739  case 'S': {
7740  // returns PDF as a string
7741  return $this->getBuffer();
7742  }
7743  default: {
7744  $this->Error('Incorrect output destination: '.$dest);
7745  }
7746  }
7747  return '';
7748  }
7749 
7757  public function _destroy($destroyall=false, $preserve_objcopy=false) {
7758  if ($destroyall AND !$preserve_objcopy) {
7759  // remove all temporary files
7760  $tmpfiles = glob(K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_*');
7761  if (!empty($tmpfiles)) {
7762  array_map('unlink', $tmpfiles);
7763  }
7764  }
7765  $preserve = array(
7766  'file_id',
7767  'internal_encoding',
7768  'state',
7769  'bufferlen',
7770  'buffer',
7771  'cached_files',
7772  'sign',
7773  'signature_data',
7774  'signature_max_length',
7775  'byterange_string',
7776  'tsa_timestamp',
7777  'tsa_data'
7778  );
7779  foreach (array_keys(get_object_vars($this)) as $val) {
7780  if ($destroyall OR !in_array($val, $preserve)) {
7781  if ((!$preserve_objcopy OR ($val != 'objcopy')) AND ($val != 'file_id') AND isset($this->$val)) {
7782  unset($this->$val);
7783  }
7784  }
7785  }
7786  }
7787 
7792  protected function _dochecks() {
7793  //Check for locale-related bug
7794  if (1.1 == 1) {
7795  $this->Error('Don\'t alter the locale before including class file');
7796  }
7797  //Check for decimal separator
7798  if (sprintf('%.1F', 1.0) != '1.0') {
7799  setlocale(LC_NUMERIC, 'C');
7800  }
7801  }
7802 
7809  protected function getInternalPageNumberAliases($a= '') {
7810  $alias = array();
7811  // build array of Unicode + ASCII variants (the order is important)
7812  $alias = array('u' => array(), 'a' => array());
7813  $u = '{'.$a.'}';
7814  $alias['u'][] = TCPDF_STATIC::_escape($u);
7815  if ($this->isunicode) {
7816  $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($u, $this->isunicode, $this->CurrentFont));
7817  $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($u, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7818  $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($a, $this->isunicode, $this->CurrentFont));
7819  $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($a, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7820  }
7821  $alias['a'][] = TCPDF_STATIC::_escape($a);
7822  return $alias;
7823  }
7824 
7830  protected function getAllInternalPageNumberAliases() {
7832  $pnalias = array();
7833  foreach($basic_alias as $k => $a) {
7834  $pnalias[$k] = $this->getInternalPageNumberAliases($a);
7835  }
7836  return $pnalias;
7837  }
7838 
7848  protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
7849  foreach ($aliases as $type => $alias) {
7850  foreach ($alias as $a) {
7851  // find position of compensation factor
7852  $startnum = (strpos($a, ':') + 1);
7853  $a = substr($a, 0, $startnum);
7854  if (($pos = strpos($page, $a)) !== false) {
7855  // end of alias
7856  $endnum = strpos($page, '}', $pos);
7857  // string to be replaced
7858  $aa = substr($page, $pos, ($endnum - $pos + 1));
7859  // get compensation factor
7860  $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum));
7861  $ratio = preg_replace('/[^0-9\.]/', '', $ratio);
7862  $ratio = floatval($ratio);
7863  if ($type == 'u') {
7864  $chrdiff = floor(($diff + 12) * $ratio);
7865  $shift = str_repeat(' ', $chrdiff);
7866  $shift = TCPDF_FONTS::UTF8ToUTF16BE($shift, false, $this->isunicode, $this->CurrentFont);
7867  } else {
7868  $chrdiff = floor(($diff + 11) * $ratio);
7869  $shift = str_repeat(' ', $chrdiff);
7870  }
7871  $page = str_replace($aa, $shift, $page);
7872  }
7873  }
7874  }
7875  return $page;
7876  }
7877 
7883  protected function setPageBoxTypes($boxes) {
7884  $this->page_boxes = array();
7885  foreach ($boxes as $box) {
7886  if (in_array($box, TCPDF_STATIC::$pageboxes)) {
7887  $this->page_boxes[] = $box;
7888  }
7889  }
7890  }
7891 
7896  protected function _putpages() {
7897  $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
7898  // get internal aliases for page numbers
7899  $pnalias = $this->getAllInternalPageNumberAliases();
7900  $num_pages = $this->numpages;
7901  $ptpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $num_pages - 1));
7902  $ptpu = TCPDF_FONTS::UTF8ToUTF16BE($ptpa, false, $this->isunicode, $this->CurrentFont);
7903  $ptp_num_chars = $this->GetNumChars($ptpa);
7904  $pagegroupnum = 0;
7905  $groupnum = 0;
7906  $ptgu = 1;
7907  $ptga = 1;
7908  $ptg_num_chars = 1;
7909  for ($n = 1; $n <= $num_pages; ++$n) {
7910  // get current page
7911  $temppage = $this->getPageBuffer($n);
7912  $pagelen = strlen($temppage);
7913  // set replacements for total pages number
7914  $pnpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $n - 1));
7915  $pnpu = TCPDF_FONTS::UTF8ToUTF16BE($pnpa, false, $this->isunicode, $this->CurrentFont);
7916  $pnp_num_chars = $this->GetNumChars($pnpa);
7917  $pdiff = 0; // difference used for right shift alignment of page numbers
7918  $gdiff = 0; // difference used for right shift alignment of page group numbers
7919  if (!empty($this->pagegroups)) {
7920  if (isset($this->newpagegroup[$n])) {
7921  $pagegroupnum = 0;
7922  ++$groupnum;
7923  $ptga = TCPDF_STATIC::formatPageNumber($this->pagegroups[$groupnum]);
7924  $ptgu = TCPDF_FONTS::UTF8ToUTF16BE($ptga, false, $this->isunicode, $this->CurrentFont);
7925  $ptg_num_chars = $this->GetNumChars($ptga);
7926  }
7927  ++$pagegroupnum;
7928  $pnga = TCPDF_STATIC::formatPageNumber($pagegroupnum);
7929  $pngu = TCPDF_FONTS::UTF8ToUTF16BE($pnga, false, $this->isunicode, $this->CurrentFont);
7930  $png_num_chars = $this->GetNumChars($pnga);
7931  // replace page numbers
7932  $replace = array();
7933  $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
7934  $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
7935  $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
7936  $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
7937  list($temppage, $gdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $gdiff);
7938  }
7939  // replace page numbers
7940  $replace = array();
7941  $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
7942  $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
7943  $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
7944  $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
7945  list($temppage, $pdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $pdiff);
7946  // replace right shift alias
7947  $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
7948  // replace EPS marker
7949  $temppage = str_replace($this->epsmarker, '', $temppage);
7950  //Page
7951  $this->page_obj_id[$n] = $this->_newobj();
7952  $out = '<<';
7953  $out .= ' /Type /Page';
7954  $out .= ' /Parent 1 0 R';
7955  if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
7956  $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp);
7957  }
7958  $out .= ' /Resources 2 0 R';
7959  foreach ($this->page_boxes as $box) {
7960  $out .= ' /'.$box;
7961  $out .= sprintf(' [%F %F %F %F]', $this->pagedim[$n][$box]['llx'], $this->pagedim[$n][$box]['lly'], $this->pagedim[$n][$box]['urx'], $this->pagedim[$n][$box]['ury']);
7962  }
7963  if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
7964  $out .= ' /BoxColorInfo <<';
7965  foreach ($this->page_boxes as $box) {
7966  if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
7967  $out .= ' /'.$box.' <<';
7968  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
7969  $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
7970  $out .= ' /C [';
7971  $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
7972  $out .= ' ]';
7973  }
7974  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
7975  $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
7976  }
7977  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
7978  $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
7979  }
7980  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
7981  $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
7982  $out .= ' /D [';
7983  foreach ($dashes as $dash) {
7984  $out .= sprintf(' %F', ($dash * $this->k));
7985  }
7986  $out .= ' ]';
7987  }
7988  $out .= ' >>';
7989  }
7990  }
7991  $out .= ' >>';
7992  }
7993  $out .= ' /Contents '.($this->n + 1).' 0 R';
7994  $out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
7995  if (!$this->pdfa_mode) {
7996  $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
7997  }
7998  if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
7999  // page transitions
8000  if (isset($this->pagedim[$n]['trans']['Dur'])) {
8001  $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
8002  }
8003  $out .= ' /Trans <<';
8004  $out .= ' /Type /Trans';
8005  if (isset($this->pagedim[$n]['trans']['S'])) {
8006  $out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
8007  }
8008  if (isset($this->pagedim[$n]['trans']['D'])) {
8009  $out .= ' /D '.$this->pagedim[$n]['trans']['D'];
8010  }
8011  if (isset($this->pagedim[$n]['trans']['Dm'])) {
8012  $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
8013  }
8014  if (isset($this->pagedim[$n]['trans']['M'])) {
8015  $out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
8016  }
8017  if (isset($this->pagedim[$n]['trans']['Di'])) {
8018  $out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
8019  }
8020  if (isset($this->pagedim[$n]['trans']['SS'])) {
8021  $out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
8022  }
8023  if (isset($this->pagedim[$n]['trans']['B'])) {
8024  $out .= ' /B '.$this->pagedim[$n]['trans']['B'];
8025  }
8026  $out .= ' >>';
8027  }
8028  $out .= $this->_getannotsrefs($n);
8029  $out .= ' /PZ '.$this->pagedim[$n]['PZ'];
8030  $out .= ' >>';
8031  $out .= "\n".'endobj';
8032  $this->_out($out);
8033  //Page content
8034  $p = ($this->compress) ? gzcompress($temppage) : $temppage;
8035  $this->_newobj();
8036  $p = $this->_getrawstream($p);
8037  $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
8038  }
8039  //Pages root
8040  $out = $this->_getobj(1)."\n";
8041  $out .= '<< /Type /Pages /Kids [';
8042  foreach($this->page_obj_id as $page_obj) {
8043  $out .= ' '.$page_obj.' 0 R';
8044  }
8045  $out .= ' ] /Count '.$num_pages.' >>';
8046  $out .= "\n".'endobj';
8047  $this->_out($out);
8048  }
8049 
8058  protected function _getannotsrefs($n) {
8059  if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
8060  return '';
8061  }
8062  $out = ' /Annots [';
8063  if (isset($this->PageAnnots[$n])) {
8064  foreach ($this->PageAnnots[$n] as $key => $val) {
8065  if (!in_array($val['n'], $this->radio_groups)) {
8066  $out .= ' '.$val['n'].' 0 R';
8067  }
8068  }
8069  // add radiobutton groups
8070  if (isset($this->radiobutton_groups[$n])) {
8071  foreach ($this->radiobutton_groups[$n] as $key => $data) {
8072  if (isset($data['n'])) {
8073  $out .= ' '.$data['n'].' 0 R';
8074  }
8075  }
8076  }
8077  }
8078  if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
8079  // set reference for signature object
8080  $out .= ' '.$this->sig_obj_id.' 0 R';
8081  }
8082  if (!empty($this->empty_signature_appearance)) {
8083  foreach ($this->empty_signature_appearance as $esa) {
8084  if ($esa['page'] == $n) {
8085  // set reference for empty signature objects
8086  $out .= ' '.$esa['objid'].' 0 R';
8087  }
8088  }
8089  }
8090  $out .= ' ]';
8091  return $out;
8092  }
8093 
8102  protected function _putannotsobjs() {
8103  // reset object counter
8104  for ($n=1; $n <= $this->numpages; ++$n) {
8105  if (isset($this->PageAnnots[$n])) {
8106  // set page annotations
8107  foreach ($this->PageAnnots[$n] as $key => $pl) {
8108  $annot_obj_id = $this->PageAnnots[$n][$key]['n'];
8109  // create annotation object for grouping radiobuttons
8110  if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
8111  $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n'];
8112  $annots = '<<';
8113  $annots .= ' /Type /Annot';
8114  $annots .= ' /Subtype /Widget';
8115  $annots .= ' /Rect [0 0 0 0]';
8116  if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) {
8117  // read only
8118  $annots .= ' /F 68';
8119  $annots .= ' /Ff 49153';
8120  } else {
8121  $annots .= ' /F 4'; // default print for PDF/A
8122  $annots .= ' /Ff 49152';
8123  }
8124  $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
8125  if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8126  $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id);
8127  }
8128  $annots .= ' /FT /Btn';
8129  $annots .= ' /Kids [';
8130  $defval = '';
8131  foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) {
8132  if (isset($data['kid'])) {
8133  $annots .= ' '.$data['kid'].' 0 R';
8134  if ($data['def'] !== 'Off') {
8135  $defval = $data['def'];
8136  }
8137  }
8138  }
8139  $annots .= ' ]';
8140  if (!empty($defval)) {
8141  $annots .= ' /V /'.$defval;
8142  }
8143  $annots .= ' >>';
8144  $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
8145  $this->form_obj_id[] = $radio_button_obj_id;
8146  // store object id to be used on Parent entry of Kids
8147  $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id;
8148  }
8149  $formfield = false;
8150  $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
8151  $a = $pl['x'] * $this->k;
8152  $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k);
8153  $c = $pl['w'] * $this->k;
8154  $d = $pl['h'] * $this->k;
8155  $rect = sprintf('%F %F %F %F', $a, $b, $a+$c, $b+$d);
8156  // create new annotation object
8157  $annots = '<</Type /Annot';
8158  $annots .= ' /Subtype /'.$pl['opt']['subtype'];
8159  $annots .= ' /Rect ['.$rect.']';
8160  $ft = array('Btn', 'Tx', 'Ch', 'Sig');
8161  if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
8162  $annots .= ' /FT /'.$pl['opt']['ft'];
8163  $formfield = true;
8164  }
8165  $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
8166  $annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
8167  $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
8168  $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp);
8169  if (isset($pl['opt']['f'])) {
8170  $fval = 0;
8171  if (is_array($pl['opt']['f'])) {
8172  foreach ($pl['opt']['f'] as $f) {
8173  switch (strtolower($f)) {
8174  case 'invisible': {
8175  $fval += 1 << 0;
8176  break;
8177  }
8178  case 'hidden': {
8179  $fval += 1 << 1;
8180  break;
8181  }
8182  case 'print': {
8183  $fval += 1 << 2;
8184  break;
8185  }
8186  case 'nozoom': {
8187  $fval += 1 << 3;
8188  break;
8189  }
8190  case 'norotate': {
8191  $fval += 1 << 4;
8192  break;
8193  }
8194  case 'noview': {
8195  $fval += 1 << 5;
8196  break;
8197  }
8198  case 'readonly': {
8199  $fval += 1 << 6;
8200  break;
8201  }
8202  case 'locked': {
8203  $fval += 1 << 8;
8204  break;
8205  }
8206  case 'togglenoview': {
8207  $fval += 1 << 9;
8208  break;
8209  }
8210  case 'lockedcontents': {
8211  $fval += 1 << 10;
8212  break;
8213  }
8214  default: {
8215  break;
8216  }
8217  }
8218  }
8219  } else {
8220  $fval = intval($pl['opt']['f']);
8221  }
8222  } else {
8223  $fval = 4;
8224  }
8225  if ($this->pdfa_mode) {
8226  // force print flag for PDF/A mode
8227  $fval |= 4;
8228  }
8229  $annots .= ' /F '.intval($fval);
8230  if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
8231  $annots .= ' /AS /'.$pl['opt']['as'];
8232  }
8233  if (isset($pl['opt']['ap'])) {
8234  // appearance stream
8235  $annots .= ' /AP <<';
8236  if (is_array($pl['opt']['ap'])) {
8237  foreach ($pl['opt']['ap'] as $apmode => $apdef) {
8238  // $apmode can be: n = normal; r = rollover; d = down;
8239  $annots .= ' /'.strtoupper($apmode);
8240  if (is_array($apdef)) {
8241  $annots .= ' <<';
8242  foreach ($apdef as $apstate => $stream) {
8243  // reference to XObject that define the appearance for this mode-state
8244  $apsobjid = $this->_putAPXObject($c, $d, $stream);
8245  $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
8246  }
8247  $annots .= ' >>';
8248  } else {
8249  // reference to XObject that define the appearance for this mode
8250  $apsobjid = $this->_putAPXObject($c, $d, $apdef);
8251  $annots .= ' '.$apsobjid.' 0 R';
8252  }
8253  }
8254  } else {
8255  $annots .= $pl['opt']['ap'];
8256  }
8257  $annots .= ' >>';
8258  }
8259  if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
8260  $annots .= ' /BS <<';
8261  $annots .= ' /Type /Border';
8262  if (isset($pl['opt']['bs']['w'])) {
8263  $annots .= ' /W '.intval($pl['opt']['bs']['w']);
8264  }
8265  $bstyles = array('S', 'D', 'B', 'I', 'U');
8266  if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
8267  $annots .= ' /S /'.$pl['opt']['bs']['s'];
8268  }
8269  if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
8270  $annots .= ' /D [';
8271  foreach ($pl['opt']['bs']['d'] as $cord) {
8272  $annots .= ' '.intval($cord);
8273  }
8274  $annots .= ']';
8275  }
8276  $annots .= ' >>';
8277  } else {
8278  $annots .= ' /Border [';
8279  if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
8280  $annots .= intval($pl['opt']['border'][0]).' ';
8281  $annots .= intval($pl['opt']['border'][1]).' ';
8282  $annots .= intval($pl['opt']['border'][2]);
8283  if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
8284  $annots .= ' [';
8285  foreach ($pl['opt']['border'][3] as $dash) {
8286  $annots .= intval($dash).' ';
8287  }
8288  $annots .= ']';
8289  }
8290  } else {
8291  $annots .= '0 0 0';
8292  }
8293  $annots .= ']';
8294  }
8295  if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
8296  $annots .= ' /BE <<';
8297  $bstyles = array('S', 'C');
8298  if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) {
8299  $annots .= ' /S /'.$pl['opt']['bs']['s'];
8300  } else {
8301  $annots .= ' /S /S';
8302  }
8303  if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
8304  $annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']);
8305  }
8306  $annots .= '>>';
8307  }
8308  if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
8309  $annots .= ' /C '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['c']);
8310  }
8311  //$annots .= ' /StructParent ';
8312  //$annots .= ' /OC ';
8313  $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
8314  if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
8315  // this is a markup type
8316  if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8317  $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
8318  }
8319  //$annots .= ' /Popup ';
8320  if (isset($pl['opt']['ca'])) {
8321  $annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca']));
8322  }
8323  if (isset($pl['opt']['rc'])) {
8324  $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8325  }
8326  $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp);
8327  //$annots .= ' /IRT ';
8328  if (isset($pl['opt']['subj'])) {
8329  $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
8330  }
8331  //$annots .= ' /RT ';
8332  //$annots .= ' /IT ';
8333  //$annots .= ' /ExData ';
8334  }
8335  $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
8336  // Annotation types
8337  switch (strtolower($pl['opt']['subtype'])) {
8338  case 'text': {
8339  if (isset($pl['opt']['open'])) {
8340  $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
8341  }
8342  $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
8343  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8344  $annots .= ' /Name /'.$pl['opt']['name'];
8345  } else {
8346  $annots .= ' /Name /Note';
8347  }
8348  $statemodels = array('Marked', 'Review');
8349  if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
8350  $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8351  } else {
8352  $pl['opt']['statemodel'] = 'Marked';
8353  $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8354  }
8355  if ($pl['opt']['statemodel'] == 'Marked') {
8356  $states = array('Accepted', 'Unmarked');
8357  } else {
8358  $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
8359  }
8360  if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
8361  $annots .= ' /State /'.$pl['opt']['state'];
8362  } else {
8363  if ($pl['opt']['statemodel'] == 'Marked') {
8364  $annots .= ' /State /Unmarked';
8365  } else {
8366  $annots .= ' /State /None';
8367  }
8368  }
8369  break;
8370  }
8371  case 'link': {
8372  if (is_string($pl['txt'])) {
8373  if ($pl['txt'][0] == '#') {
8374  // internal destination
8375  $annots .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($pl['txt'], 1));
8376  } elseif ($pl['txt'][0] == '%') {
8377  // embedded PDF file
8378  $filename = basename(substr($pl['txt'], 1));
8379  $annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
8380  } elseif ($pl['txt'][0] == '*') {
8381  // embedded generic file
8382  $filename = basename(substr($pl['txt'], 1));
8383  $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});';
8384  $annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>';
8385  } else {
8386  $parsedUrl = parse_url($pl['txt']);
8387  if (empty($parsedUrl['scheme']) AND (strtolower(substr($parsedUrl['path'], -4)) == '.pdf')) {
8388  // relative link to a PDF file
8389  $dest = '[0 /Fit]'; // default page 0
8390  if (!empty($parsedUrl['fragment'])) {
8391  // check for named destination
8392  $tmp = explode('=', $parsedUrl['fragment']);
8393  $dest = '('.((count($tmp) == 2) ? $tmp[1] : $tmp[0]).')';
8394  }
8395  $annots .= ' /A <</S /GoToR /D '.$dest.' /F '.$this->_datastring($this->unhtmlentities($parsedUrl['path']), $annot_obj_id).' /NewWindow true>>';
8396  } else {
8397  // external URI link
8398  $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
8399  }
8400  }
8401  } elseif (isset($this->links[$pl['txt']])) {
8402  // internal link ID
8403  $l = $this->links[$pl['txt']];
8404  if (isset($this->page_obj_id[($l['p'])])) {
8405  $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l['p'])], ($this->pagedim[$l['p']]['h'] - ($l['y'] * $this->k)));
8406  }
8407  }
8408  $hmodes = array('N', 'I', 'O', 'P');
8409  if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
8410  $annots .= ' /H /'.$pl['opt']['h'];
8411  } else {
8412  $annots .= ' /H /I';
8413  }
8414  //$annots .= ' /PA ';
8415  //$annots .= ' /Quadpoints ';
8416  break;
8417  }
8418  case 'freetext': {
8419  if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8420  $annots .= ' /DA ('.$pl['opt']['da'].')';
8421  }
8422  if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8423  $annots .= ' /Q '.intval($pl['opt']['q']);
8424  }
8425  if (isset($pl['opt']['rc'])) {
8426  $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8427  }
8428  if (isset($pl['opt']['ds'])) {
8429  $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
8430  }
8431  if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
8432  $annots .= ' /CL [';
8433  foreach ($pl['opt']['cl'] as $cl) {
8434  $annots .= sprintf('%F ', $cl * $this->k);
8435  }
8436  $annots .= ']';
8437  }
8438  $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
8439  if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
8440  $annots .= ' /IT /'.$pl['opt']['it'];
8441  }
8442  if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
8443  $l = $pl['opt']['rd'][0] * $this->k;
8444  $r = $pl['opt']['rd'][1] * $this->k;
8445  $t = $pl['opt']['rd'][2] * $this->k;
8446  $b = $pl['opt']['rd'][3] * $this->k;
8447  $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']';
8448  }
8449  if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
8450  $annots .= ' /LE /'.$pl['opt']['le'];
8451  }
8452  break;
8453  }
8454  case 'line': {
8455  break;
8456  }
8457  case 'square': {
8458  break;
8459  }
8460  case 'circle': {
8461  break;
8462  }
8463  case 'polygon': {
8464  break;
8465  }
8466  case 'polyline': {
8467  break;
8468  }
8469  case 'highlight': {
8470  break;
8471  }
8472  case 'underline': {
8473  break;
8474  }
8475  case 'squiggly': {
8476  break;
8477  }
8478  case 'strikeout': {
8479  break;
8480  }
8481  case 'stamp': {
8482  break;
8483  }
8484  case 'caret': {
8485  break;
8486  }
8487  case 'ink': {
8488  break;
8489  }
8490  case 'popup': {
8491  break;
8492  }
8493  case 'fileattachment': {
8494  if ($this->pdfa_mode) {
8495  // embedded files are not allowed in PDF/A mode
8496  break;
8497  }
8498  if (!isset($pl['opt']['fs'])) {
8499  break;
8500  }
8501  $filename = basename($pl['opt']['fs']);
8502  if (isset($this->embeddedfiles[$filename]['f'])) {
8503  $annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R';
8504  $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
8505  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8506  $annots .= ' /Name /'.$pl['opt']['name'];
8507  } else {
8508  $annots .= ' /Name /PushPin';
8509  }
8510  // index (zero-based) of the annotation in the Annots array of this page
8511  $this->embeddedfiles[$filename]['a'] = $key;
8512  }
8513  break;
8514  }
8515  case 'sound': {
8516  if (!isset($pl['opt']['fs'])) {
8517  break;
8518  }
8519  $filename = basename($pl['opt']['fs']);
8520  if (isset($this->embeddedfiles[$filename]['f'])) {
8521  // ... TO BE COMPLETED ...
8522  // /R /C /B /E /CO /CP
8523  $annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R';
8524  $iconsapp = array('Speaker', 'Mic');
8525  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8526  $annots .= ' /Name /'.$pl['opt']['name'];
8527  } else {
8528  $annots .= ' /Name /Speaker';
8529  }
8530  }
8531  break;
8532  }
8533  case 'movie': {
8534  break;
8535  }
8536  case 'widget': {
8537  $hmode = array('N', 'I', 'O', 'P', 'T');
8538  if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
8539  $annots .= ' /H /'.$pl['opt']['h'];
8540  }
8541  if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
8542  $annots .= ' /MK <<';
8543  if (isset($pl['opt']['mk']['r'])) {
8544  $annots .= ' /R '.$pl['opt']['mk']['r'];
8545  }
8546  if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
8547  $annots .= ' /BC '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bc']);
8548  }
8549  if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
8550  $annots .= ' /BG '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bg']);
8551  }
8552  if (isset($pl['opt']['mk']['ca'])) {
8553  $annots .= ' /CA '.$pl['opt']['mk']['ca'];
8554  }
8555  if (isset($pl['opt']['mk']['rc'])) {
8556  $annots .= ' /RC '.$pl['opt']['mk']['rc'];
8557  }
8558  if (isset($pl['opt']['mk']['ac'])) {
8559  $annots .= ' /AC '.$pl['opt']['mk']['ac'];
8560  }
8561  if (isset($pl['opt']['mk']['i'])) {
8562  $info = $this->getImageBuffer($pl['opt']['mk']['i']);
8563  if ($info !== false) {
8564  $annots .= ' /I '.$info['n'].' 0 R';
8565  }
8566  }
8567  if (isset($pl['opt']['mk']['ri'])) {
8568  $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
8569  if ($info !== false) {
8570  $annots .= ' /RI '.$info['n'].' 0 R';
8571  }
8572  }
8573  if (isset($pl['opt']['mk']['ix'])) {
8574  $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
8575  if ($info !== false) {
8576  $annots .= ' /IX '.$info['n'].' 0 R';
8577  }
8578  }
8579  if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
8580  $annots .= ' /IF <<';
8581  $if_sw = array('A', 'B', 'S', 'N');
8582  if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
8583  $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
8584  }
8585  $if_s = array('A', 'P');
8586  if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
8587  $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
8588  }
8589  if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
8590  $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
8591  }
8592  if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
8593  $annots .= ' /FB true';
8594  }
8595  $annots .= '>>';
8596  }
8597  if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
8598  $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
8599  }
8600  $annots .= '>>';
8601  } // end MK
8602  // --- Entries for field dictionaries ---
8603  if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
8604  // set parent
8605  $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
8606  }
8607  if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8608  $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
8609  }
8610  if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8611  $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
8612  }
8613  if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
8614  $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
8615  }
8616  if (isset($pl['opt']['ff'])) {
8617  if (is_array($pl['opt']['ff'])) {
8618  // array of bit settings
8619  $flag = 0;
8620  foreach($pl['opt']['ff'] as $val) {
8621  $flag += 1 << ($val - 1);
8622  }
8623  } else {
8624  $flag = intval($pl['opt']['ff']);
8625  }
8626  $annots .= ' /Ff '.$flag;
8627  }
8628  if (isset($pl['opt']['maxlen'])) {
8629  $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
8630  }
8631  if (isset($pl['opt']['v'])) {
8632  $annots .= ' /V';
8633  if (is_array($pl['opt']['v'])) {
8634  foreach ($pl['opt']['v'] AS $optval) {
8635  if (is_float($optval)) {
8636  $optval = sprintf('%F', $optval);
8637  }
8638  $annots .= ' '.$optval;
8639  }
8640  } else {
8641  $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
8642  }
8643  }
8644  if (isset($pl['opt']['dv'])) {
8645  $annots .= ' /DV';
8646  if (is_array($pl['opt']['dv'])) {
8647  foreach ($pl['opt']['dv'] AS $optval) {
8648  if (is_float($optval)) {
8649  $optval = sprintf('%F', $optval);
8650  }
8651  $annots .= ' '.$optval;
8652  }
8653  } else {
8654  $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
8655  }
8656  }
8657  if (isset($pl['opt']['rv'])) {
8658  $annots .= ' /RV';
8659  if (is_array($pl['opt']['rv'])) {
8660  foreach ($pl['opt']['rv'] AS $optval) {
8661  if (is_float($optval)) {
8662  $optval = sprintf('%F', $optval);
8663  }
8664  $annots .= ' '.$optval;
8665  }
8666  } else {
8667  $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
8668  }
8669  }
8670  if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
8671  $annots .= ' /A << '.$pl['opt']['a'].' >>';
8672  }
8673  if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
8674  $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
8675  }
8676  if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8677  $annots .= ' /DA ('.$pl['opt']['da'].')';
8678  }
8679  if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8680  $annots .= ' /Q '.intval($pl['opt']['q']);
8681  }
8682  if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
8683  $annots .= ' /Opt [';
8684  foreach($pl['opt']['opt'] AS $copt) {
8685  if (is_array($copt)) {
8686  $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
8687  } else {
8688  $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
8689  }
8690  }
8691  $annots .= ']';
8692  }
8693  if (isset($pl['opt']['ti'])) {
8694  $annots .= ' /TI '.intval($pl['opt']['ti']);
8695  }
8696  if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
8697  $annots .= ' /I [';
8698  foreach($pl['opt']['i'] AS $copt) {
8699  $annots .= intval($copt).' ';
8700  }
8701  $annots .= ']';
8702  }
8703  break;
8704  }
8705  case 'screen': {
8706  break;
8707  }
8708  case 'printermark': {
8709  break;
8710  }
8711  case 'trapnet': {
8712  break;
8713  }
8714  case 'watermark': {
8715  break;
8716  }
8717  case '3d': {
8718  break;
8719  }
8720  default: {
8721  break;
8722  }
8723  }
8724  $annots .= '>>';
8725  // create new annotation object
8726  $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
8727  if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
8728  // store reference of form object
8729  $this->form_obj_id[] = $annot_obj_id;
8730  }
8731  }
8732  }
8733  } // end for each page
8734  }
8735 
8745  protected function _putAPXObject($w=0, $h=0, $stream='') {
8746  $stream = trim($stream);
8747  $out = $this->_getobj()."\n";
8748  $this->xobjects['AX'.$this->n] = array('n' => $this->n);
8749  $out .= '<<';
8750  $out .= ' /Type /XObject';
8751  $out .= ' /Subtype /Form';
8752  $out .= ' /FormType 1';
8753  if ($this->compress) {
8754  $stream = gzcompress($stream);
8755  $out .= ' /Filter /FlateDecode';
8756  }
8757  $rect = sprintf('%F %F', $w, $h);
8758  $out .= ' /BBox [0 0 '.$rect.']';
8759  $out .= ' /Matrix [1 0 0 1 0 0]';
8760  $out .= ' /Resources 2 0 R';
8761  $stream = $this->_getrawstream($stream);
8762  $out .= ' /Length '.strlen($stream);
8763  $out .= ' >>';
8764  $out .= ' stream'."\n".$stream."\n".'endstream';
8765  $out .= "\n".'endobj';
8766  $this->_out($out);
8767  return $this->n;
8768  }
8769 
8775  protected function _putfonts() {
8776  $nf = $this->n;
8777  foreach ($this->diffs as $diff) {
8778  //Encodings
8779  $this->_newobj();
8780  $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
8781  }
8782  $mqr = TCPDF_STATIC::get_mqr();
8783  TCPDF_STATIC::set_mqr(false);
8784  foreach ($this->FontFiles as $file => $info) {
8785  // search and get font file to embedd
8786  $fontfile = TCPDF_FONTS::getFontFullPath($file, $info['fontdir']);
8787  if (!TCPDF_STATIC::empty_string($fontfile)) {
8788  $font = file_get_contents($fontfile);
8789  $compressed = (substr($file, -2) == '.z');
8790  if ((!$compressed) AND (isset($info['length2']))) {
8791  $header = (ord($font[0]) == 128);
8792  if ($header) {
8793  // strip first binary header
8794  $font = substr($font, 6);
8795  }
8796  if ($header AND (ord($font[$info['length1']]) == 128)) {
8797  // strip second binary header
8798  $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
8799  }
8800  } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
8801  if ($compressed) {
8802  // uncompress font
8803  $font = gzuncompress($font);
8804  }
8805  // merge subset characters
8806  $subsetchars = array(); // used chars
8807  foreach ($info['fontkeys'] as $fontkey) {
8808  $fontinfo = $this->getFontBuffer($fontkey);
8809  $subsetchars += $fontinfo['subsetchars'];
8810  }
8811  // rebuild a font subset
8812  $font = TCPDF_FONTS::_getTrueTypeFontSubset($font, $subsetchars);
8813  // calculate new font length
8814  $info['length1'] = strlen($font);
8815  if ($compressed) {
8816  // recompress font
8817  $font = gzcompress($font);
8818  }
8819  }
8820  $this->_newobj();
8821  $this->FontFiles[$file]['n'] = $this->n;
8822  $stream = $this->_getrawstream($font);
8823  $out = '<< /Length '.strlen($stream);
8824  if ($compressed) {
8825  $out .= ' /Filter /FlateDecode';
8826  }
8827  $out .= ' /Length1 '.$info['length1'];
8828  if (isset($info['length2'])) {
8829  $out .= ' /Length2 '.$info['length2'].' /Length3 0';
8830  }
8831  $out .= ' >>';
8832  $out .= ' stream'."\n".$stream."\n".'endstream';
8833  $out .= "\n".'endobj';
8834  $this->_out($out);
8835  }
8836  }
8837  TCPDF_STATIC::set_mqr($mqr);
8838  foreach ($this->fontkeys as $k) {
8839  //Font objects
8840  $font = $this->getFontBuffer($k);
8841  $type = $font['type'];
8842  $name = $font['name'];
8843  if ($type == 'core') {
8844  // standard core font
8845  $out = $this->_getobj($this->font_obj_ids[$k])."\n";
8846  $out .= '<</Type /Font';
8847  $out .= ' /Subtype /Type1';
8848  $out .= ' /BaseFont /'.$name;
8849  $out .= ' /Name /F'.$font['i'];
8850  if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
8851  $out .= ' /Encoding /WinAnsiEncoding';
8852  }
8853  if ($k == 'helvetica') {
8854  // add default font for annotations
8855  $this->annotation_fonts[$k] = $font['i'];
8856  }
8857  $out .= ' >>';
8858  $out .= "\n".'endobj';
8859  $this->_out($out);
8860  } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
8861  // additional Type1 or TrueType font
8862  $out = $this->_getobj($this->font_obj_ids[$k])."\n";
8863  $out .= '<</Type /Font';
8864  $out .= ' /Subtype /'.$type;
8865  $out .= ' /BaseFont /'.$name;
8866  $out .= ' /Name /F'.$font['i'];
8867  $out .= ' /FirstChar 32 /LastChar 255';
8868  $out .= ' /Widths '.($this->n + 1).' 0 R';
8869  $out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
8870  if ($font['enc']) {
8871  if (isset($font['diff'])) {
8872  $out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
8873  } else {
8874  $out .= ' /Encoding /WinAnsiEncoding';
8875  }
8876  }
8877  $out .= ' >>';
8878  $out .= "\n".'endobj';
8879  $this->_out($out);
8880  // Widths
8881  $this->_newobj();
8882  $s = '[';
8883  for ($i = 32; $i < 256; ++$i) {
8884  if (isset($font['cw'][$i])) {
8885  $s .= $font['cw'][$i].' ';
8886  } else {
8887  $s .= $font['dw'].' ';
8888  }
8889  }
8890  $s .= ']';
8891  $s .= "\n".'endobj';
8892  $this->_out($s);
8893  //Descriptor
8894  $this->_newobj();
8895  $s = '<</Type /FontDescriptor /FontName /'.$name;
8896  foreach ($font['desc'] as $fdk => $fdv) {
8897  if (is_float($fdv)) {
8898  $fdv = sprintf('%F', $fdv);
8899  }
8900  $s .= ' /'.$fdk.' '.$fdv.'';
8901  }
8902  if (!TCPDF_STATIC::empty_string($font['file'])) {
8903  $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
8904  }
8905  $s .= '>>';
8906  $s .= "\n".'endobj';
8907  $this->_out($s);
8908  } else {
8909  // additional types
8910  $mtd = '_put'.strtolower($type);
8911  if (!method_exists($this, $mtd)) {
8912  $this->Error('Unsupported font type: '.$type);
8913  }
8914  $this->$mtd($font);
8915  }
8916  }
8917  }
8918 
8927  protected function _puttruetypeunicode($font) {
8928  $fontname = '';
8929  if ($font['subset']) {
8930  // change name for font subsetting
8931  $subtag = sprintf('%06u', $font['i']);
8932  $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
8933  $fontname .= $subtag.'+';
8934  }
8935  $fontname .= $font['name'];
8936  // Type0 Font
8937  // A composite font composed of other fonts, organized hierarchically
8938  $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
8939  $out .= '<< /Type /Font';
8940  $out .= ' /Subtype /Type0';
8941  $out .= ' /BaseFont /'.$fontname;
8942  $out .= ' /Name /F'.$font['i'];
8943  $out .= ' /Encoding /'.$font['enc'];
8944  $out .= ' /ToUnicode '.($this->n + 1).' 0 R';
8945  $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
8946  $out .= ' >>';
8947  $out .= "\n".'endobj';
8948  $this->_out($out);
8949  // ToUnicode map for Identity-H
8951  // ToUnicode Object
8952  $this->_newobj();
8953  $stream = ($this->compress) ? gzcompress($stream) : $stream;
8954  $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
8955  $stream = $this->_getrawstream($stream);
8956  $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
8957  // CIDFontType2
8958  // A CIDFont whose glyph descriptions are based on TrueType font technology
8959  $oid = $this->_newobj();
8960  $out = '<< /Type /Font';
8961  $out .= ' /Subtype /CIDFontType2';
8962  $out .= ' /BaseFont /'.$fontname;
8963  // A dictionary containing entries that define the character collection of the CIDFont.
8964  $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
8965  $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
8966  $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
8967  $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
8968  $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
8969  $out .= ' /DW '.$font['dw']; // default width
8970  $out .= "\n".TCPDF_FONTS::_putfontwidths($font, 0);
8971  if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
8972  $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
8973  }
8974  $out .= ' >>';
8975  $out .= "\n".'endobj';
8976  $this->_out($out);
8977  // Font descriptor
8978  // A font descriptor describing the CIDFont default metrics other than its glyph widths
8979  $this->_newobj();
8980  $out = '<< /Type /FontDescriptor';
8981  $out .= ' /FontName /'.$fontname;
8982  foreach ($font['desc'] as $key => $value) {
8983  if (is_float($value)) {
8984  $value = sprintf('%F', $value);
8985  }
8986  $out .= ' /'.$key.' '.$value;
8987  }
8988  $fontdir = false;
8989  if (!TCPDF_STATIC::empty_string($font['file'])) {
8990  // A stream containing a TrueType font
8991  $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
8992  $fontdir = $this->FontFiles[$font['file']]['fontdir'];
8993  }
8994  $out .= ' >>';
8995  $out .= "\n".'endobj';
8996  $this->_out($out);
8997  if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
8998  $this->_newobj();
8999  // Embed CIDToGIDMap
9000  // A specification of the mapping from CIDs to glyph indices
9001  // search and get CTG font file to embedd
9002  $ctgfile = strtolower($font['ctg']);
9003  // search and get ctg font file to embedd
9004  $fontfile = TCPDF_FONTS::getFontFullPath($ctgfile, $fontdir);
9005  if (TCPDF_STATIC::empty_string($fontfile)) {
9006  $this->Error('Font file not found: '.$ctgfile);
9007  }
9008  $stream = $this->_getrawstream(file_get_contents($fontfile));
9009  $out = '<< /Length '.strlen($stream).'';
9010  if (substr($fontfile, -2) == '.z') { // check file extension
9011  // Decompresses data encoded using the public-domain
9012  // zlib/deflate compression method, reproducing the
9013  // original text or binary data
9014  $out .= ' /Filter /FlateDecode';
9015  }
9016  $out .= ' >>';
9017  $out .= ' stream'."\n".$stream."\n".'endstream';
9018  $out .= "\n".'endobj';
9019  $this->_out($out);
9020  }
9021  }
9022 
9031  protected function _putcidfont0($font) {
9032  $cidoffset = 0;
9033  if (!isset($font['cw'][1])) {
9034  $cidoffset = 31;
9035  }
9036  if (isset($font['cidinfo']['uni2cid'])) {
9037  // convert unicode to cid.
9038  $uni2cid = $font['cidinfo']['uni2cid'];
9039  $cw = array();
9040  foreach ($font['cw'] as $uni => $width) {
9041  if (isset($uni2cid[$uni])) {
9042  $cw[($uni2cid[$uni] + $cidoffset)] = $width;
9043  } elseif ($uni < 256) {
9044  $cw[$uni] = $width;
9045  } // else unknown character
9046  }
9047  $font = array_merge($font, array('cw' => $cw));
9048  }
9049  $name = $font['name'];
9050  $enc = $font['enc'];
9051  if ($enc) {
9052  $longname = $name.'-'.$enc;
9053  } else {
9054  $longname = $name;
9055  }
9056  $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
9057  $out .= '<</Type /Font';
9058  $out .= ' /Subtype /Type0';
9059  $out .= ' /BaseFont /'.$longname;
9060  $out .= ' /Name /F'.$font['i'];
9061  if ($enc) {
9062  $out .= ' /Encoding /'.$enc;
9063  }
9064  $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
9065  $out .= ' >>';
9066  $out .= "\n".'endobj';
9067  $this->_out($out);
9068  $oid = $this->_newobj();
9069  $out = '<</Type /Font';
9070  $out .= ' /Subtype /CIDFontType0';
9071  $out .= ' /BaseFont /'.$name;
9072  $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9073  $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9074  $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9075  $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
9076  $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
9077  $out .= ' /DW '.$font['dw'];
9078  $out .= "\n".TCPDF_FONTS::_putfontwidths($font, $cidoffset);
9079  $out .= ' >>';
9080  $out .= "\n".'endobj';
9081  $this->_out($out);
9082  $this->_newobj();
9083  $s = '<</Type /FontDescriptor /FontName /'.$name;
9084  foreach ($font['desc'] as $k => $v) {
9085  if ($k != 'Style') {
9086  if (is_float($v)) {
9087  $v = sprintf('%F', $v);
9088  }
9089  $s .= ' /'.$k.' '.$v.'';
9090  }
9091  }
9092  $s .= '>>';
9093  $s .= "\n".'endobj';
9094  $this->_out($s);
9095  }
9096 
9101  protected function _putimages() {
9102  $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9103  foreach ($this->imagekeys as $file) {
9104  $info = $this->getImageBuffer($file);
9105  // set object for alternate images array
9106  if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
9107  $altoid = $this->_newobj();
9108  $out = '[';
9109  foreach ($info['altimgs'] as $altimage) {
9110  if (isset($this->xobjects['I'.$altimage[0]]['n'])) {
9111  $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R';
9112  $out .= ' /DefaultForPrinting';
9113  if ($altimage[1] === true) {
9114  $out .= ' true';
9115  } else {
9116  $out .= ' false';
9117  }
9118  $out .= ' >>';
9119  }
9120  }
9121  $out .= ' ]';
9122  $out .= "\n".'endobj';
9123  $this->_out($out);
9124  }
9125  // set image object
9126  $oid = $this->_newobj();
9127  $this->xobjects['I'.$info['i']] = array('n' => $oid);
9128  $this->setImageSubBuffer($file, 'n', $this->n);
9129  $out = '<</Type /XObject';
9130  $out .= ' /Subtype /Image';
9131  $out .= ' /Width '.$info['w'];
9132  $out .= ' /Height '.$info['h'];
9133  if (array_key_exists('masked', $info)) {
9134  $out .= ' /SMask '.($this->n - 1).' 0 R';
9135  }
9136  // set color space
9137  $icc = false;
9138  if (isset($info['icc']) AND ($info['icc'] !== false)) {
9139  // ICC Colour Space
9140  $icc = true;
9141  $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]';
9142  } elseif ($info['cs'] == 'Indexed') {
9143  // Indexed Colour Space
9144  $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
9145  } else {
9146  // Device Colour Space
9147  $out .= ' /ColorSpace /'.$info['cs'];
9148  }
9149  if ($info['cs'] == 'DeviceCMYK') {
9150  $out .= ' /Decode [1 0 1 0 1 0 1 0]';
9151  }
9152  $out .= ' /BitsPerComponent '.$info['bpc'];
9153  if (isset($altoid) AND ($altoid > 0)) {
9154  // reference to alternate images dictionary
9155  $out .= ' /Alternates '.$altoid.' 0 R';
9156  }
9157  if (isset($info['exurl']) AND !empty($info['exurl'])) {
9158  // external stream
9159  $out .= ' /Length 0';
9160  $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
9161  if (isset($info['f'])) {
9162  $out .= ' /FFilter /'.$info['f'];
9163  }
9164  $out .= ' >>';
9165  $out .= ' stream'."\n".'endstream';
9166  } else {
9167  if (isset($info['f'])) {
9168  $out .= ' /Filter /'.$info['f'];
9169  }
9170  if (isset($info['parms'])) {
9171  $out .= ' '.$info['parms'];
9172  }
9173  if (isset($info['trns']) AND is_array($info['trns'])) {
9174  $trns = '';
9175  $count_info = count($info['trns']);
9176  if ($info['cs'] == 'Indexed') {
9177  $maxval =(pow(2, $info['bpc']) - 1);
9178  for ($i = 0; $i < $count_info; ++$i) {
9179  if (($info['trns'][$i] != 0) AND ($info['trns'][$i] != $maxval)) {
9180  // this is not a binary type mask @TODO: create a SMask
9181  $trns = '';
9182  break;
9183  } elseif (empty($trns) AND ($info['trns'][$i] == 0)) {
9184  // store the first fully transparent value
9185  $trns .= $i.' '.$i.' ';
9186  }
9187  }
9188  } else {
9189  // grayscale or RGB
9190  for ($i = 0; $i < $count_info; ++$i) {
9191  if ($info['trns'][$i] == 0) {
9192  $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
9193  }
9194  }
9195  }
9196  // Colour Key Masking
9197  if (!empty($trns)) {
9198  $out .= ' /Mask ['.$trns.']';
9199  }
9200  }
9201  $stream = $this->_getrawstream($info['data']);
9202  $out .= ' /Length '.strlen($stream).' >>';
9203  $out .= ' stream'."\n".$stream."\n".'endstream';
9204  }
9205  $out .= "\n".'endobj';
9206  $this->_out($out);
9207  if ($icc) {
9208  // ICC colour profile
9209  $this->_newobj();
9210  $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
9211  $icc = $this->_getrawstream($icc);
9212  $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9213  } elseif ($info['cs'] == 'Indexed') {
9214  // colour palette
9215  $this->_newobj();
9216  $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
9217  $pal = $this->_getrawstream($pal);
9218  $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
9219  }
9220  }
9221  }
9222 
9230  protected function _putxobjects() {
9231  foreach ($this->xobjects as $key => $data) {
9232  if (isset($data['outdata'])) {
9233  $stream = str_replace($this->epsmarker, '', trim($data['outdata']));
9234  $out = $this->_getobj($data['n'])."\n";
9235  $out .= '<<';
9236  $out .= ' /Type /XObject';
9237  $out .= ' /Subtype /Form';
9238  $out .= ' /FormType 1';
9239  if ($this->compress) {
9240  $stream = gzcompress($stream);
9241  $out .= ' /Filter /FlateDecode';
9242  }
9243  $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));
9244  $out .= ' /Matrix [1 0 0 1 0 0]';
9245  $out .= ' /Resources <<';
9246  $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9247  if (!$this->pdfa_mode) {
9248  // transparency
9249  if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
9250  $out .= ' /ExtGState <<';
9251  foreach ($data['extgstates'] as $k => $extgstate) {
9252  if (isset($this->extgstates[$k]['name'])) {
9253  $out .= ' /'.$this->extgstates[$k]['name'];
9254  } else {
9255  $out .= ' /GS'.$k;
9256  }
9257  $out .= ' '.$this->extgstates[$k]['n'].' 0 R';
9258  }
9259  $out .= ' >>';
9260  }
9261  if (isset($data['gradients']) AND !empty($data['gradients'])) {
9262  $gp = '';
9263  $gs = '';
9264  foreach ($data['gradients'] as $id => $grad) {
9265  // gradient patterns
9266  $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R';
9267  // gradient shadings
9268  $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R';
9269  }
9270  $out .= ' /Pattern <<'.$gp.' >>';
9271  $out .= ' /Shading <<'.$gs.' >>';
9272  }
9273  }
9274  // spot colors
9275  if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
9276  $out .= ' /ColorSpace <<';
9277  foreach ($data['spot_colors'] as $name => $color) {
9278  $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R';
9279  }
9280  $out .= ' >>';
9281  }
9282  // fonts
9283  if (!empty($data['fonts'])) {
9284  $out .= ' /Font <<';
9285  foreach ($data['fonts'] as $fontkey => $fontid) {
9286  $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9287  }
9288  $out .= ' >>';
9289  }
9290  // images or nested xobjects
9291  if (!empty($data['images']) OR !empty($data['xobjects'])) {
9292  $out .= ' /XObject <<';
9293  foreach ($data['images'] as $imgid) {
9294  $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
9295  }
9296  foreach ($data['xobjects'] as $sub_id => $sub_objid) {
9297  $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
9298  }
9299  $out .= ' >>';
9300  }
9301  $out .= ' >>'; //end resources
9302  if (isset($data['group']) AND ($data['group'] !== false)) {
9303  // set transparency group
9304  $out .= ' /Group << /Type /Group /S /Transparency';
9305  if (is_array($data['group'])) {
9306  if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
9307  $out .= ' /CS /'.$data['group']['CS'];
9308  }
9309  if (isset($data['group']['I'])) {
9310  $out .= ' /I /'.($data['group']['I']===true?'true':'false');
9311  }
9312  if (isset($data['group']['K'])) {
9313  $out .= ' /K /'.($data['group']['K']===true?'true':'false');
9314  }
9315  }
9316  $out .= ' >>';
9317  }
9318  $stream = $this->_getrawstream($stream, $data['n']);
9319  $out .= ' /Length '.strlen($stream);
9320  $out .= ' >>';
9321  $out .= ' stream'."\n".$stream."\n".'endstream';
9322  $out .= "\n".'endobj';
9323  $this->_out($out);
9324  }
9325  }
9326  }
9327 
9333  protected function _putspotcolors() {
9334  foreach ($this->spot_colors as $name => $color) {
9335  $this->_newobj();
9336  $this->spot_colors[$name]['n'] = $this->n;
9337  $out = '[/Separation /'.str_replace(' ', '#20', $name);
9338  $out .= ' /DeviceCMYK <<';
9339  $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
9340  $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
9341  $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
9342  $out .= "\n".'endobj';
9343  $this->_out($out);
9344  }
9345  }
9346 
9353  protected function _getxobjectdict() {
9354  $out = '';
9355  foreach ($this->xobjects as $id => $objid) {
9356  $out .= ' /'.$id.' '.$objid['n'].' 0 R';
9357  }
9358  return $out;
9359  }
9360 
9365  protected function _putresourcedict() {
9366  $out = $this->_getobj(2)."\n";
9367  $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9368  $out .= ' /Font <<';
9369  foreach ($this->fontkeys as $fontkey) {
9370  $font = $this->getFontBuffer($fontkey);
9371  $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
9372  }
9373  $out .= ' >>';
9374  $out .= ' /XObject <<';
9375  $out .= $this->_getxobjectdict();
9376  $out .= ' >>';
9377  // layers
9378  if (!empty($this->pdflayers)) {
9379  $out .= ' /Properties <<';
9380  foreach ($this->pdflayers as $layer) {
9381  $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
9382  }
9383  $out .= ' >>';
9384  }
9385  if (!$this->pdfa_mode) {
9386  // transparency
9387  if (isset($this->extgstates) AND !empty($this->extgstates)) {
9388  $out .= ' /ExtGState <<';
9389  foreach ($this->extgstates as $k => $extgstate) {
9390  if (isset($extgstate['name'])) {
9391  $out .= ' /'.$extgstate['name'];
9392  } else {
9393  $out .= ' /GS'.$k;
9394  }
9395  $out .= ' '.$extgstate['n'].' 0 R';
9396  }
9397  $out .= ' >>';
9398  }
9399  if (isset($this->gradients) AND !empty($this->gradients)) {
9400  $gp = '';
9401  $gs = '';
9402  foreach ($this->gradients as $id => $grad) {
9403  // gradient patterns
9404  $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
9405  // gradient shadings
9406  $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
9407  }
9408  $out .= ' /Pattern <<'.$gp.' >>';
9409  $out .= ' /Shading <<'.$gs.' >>';
9410  }
9411  }
9412  // spot colors
9413  if (isset($this->spot_colors) AND !empty($this->spot_colors)) {
9414  $out .= ' /ColorSpace <<';
9415  foreach ($this->spot_colors as $color) {
9416  $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
9417  }
9418  $out .= ' >>';
9419  }
9420  $out .= ' >>';
9421  $out .= "\n".'endobj';
9422  $this->_out($out);
9423  }
9424 
9429  protected function _putresources() {
9430  $this->_putextgstates();
9431  $this->_putocg();
9432  $this->_putfonts();
9433  $this->_putimages();
9434  $this->_putspotcolors();
9435  $this->_putshaders();
9436  $this->_putxobjects();
9437  $this->_putresourcedict();
9438  $this->_putdests();
9439  $this->_putEmbeddedFiles();
9440  $this->_putannotsobjs();
9441  $this->_putjavascript();
9442  $this->_putbookmarks();
9443  $this->_putencryption();
9444  }
9445 
9452  protected function _putinfo() {
9453  $oid = $this->_newobj();
9454  $out = '<<';
9455  // store current isunicode value
9456  $prev_isunicode = $this->isunicode;
9457  if ($this->docinfounicode) {
9458  $this->isunicode = true;
9459  }
9460  if (!TCPDF_STATIC::empty_string($this->title)) {
9461  // The document's title.
9462  $out .= ' /Title '.$this->_textstring($this->title, $oid);
9463  }
9464  if (!TCPDF_STATIC::empty_string($this->author)) {
9465  // The name of the person who created the document.
9466  $out .= ' /Author '.$this->_textstring($this->author, $oid);
9467  }
9468  if (!TCPDF_STATIC::empty_string($this->subject)) {
9469  // The subject of the document.
9470  $out .= ' /Subject '.$this->_textstring($this->subject, $oid);
9471  }
9472  if (!TCPDF_STATIC::empty_string($this->keywords)) {
9473  // Keywords associated with the document.
9474  $out .= ' /Keywords '.$this->_textstring($this->keywords, $oid);
9475  }
9476  if (!TCPDF_STATIC::empty_string($this->creator)) {
9477  // 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.
9478  $out .= ' /Creator '.$this->_textstring($this->creator, $oid);
9479  }
9480  // restore previous isunicode value
9481  $this->isunicode = $prev_isunicode;
9482  // default producer
9483  $out .= ' /Producer '.$this->_textstring(TCPDF_STATIC::getTCPDFProducer(), $oid);
9484  // The date and time the document was created, in human-readable form
9485  $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp);
9486  // The date and time the document was most recently modified, in human-readable form
9487  $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp);
9488  // A name object indicating whether the document has been modified to include trapping information
9489  $out .= ' /Trapped /False';
9490  $out .= ' >>';
9491  $out .= "\n".'endobj';
9492  $this->_out($out);
9493  return $oid;
9494  }
9495 
9503  public function setExtraXMP($xmp) {
9504  $this->custom_xmp = $xmp;
9505  }
9506 
9513  protected function _putXMP() {
9514  $oid = $this->_newobj();
9515  // store current isunicode value
9516  $prev_isunicode = $this->isunicode;
9517  $this->isunicode = true;
9518  $prev_encrypted = $this->encrypted;
9519  $this->encrypted = false;
9520  // set XMP data
9521  $xmp = '<?xpacket begin="'.TCPDF_FONTS::unichr(0xfeff, $this->isunicode).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
9522  $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";
9523  $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
9524  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
9525  $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
9526  $xmp .= "\t\t\t".'<dc:title>'."\n";
9527  $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9528  $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->title).'</rdf:li>'."\n";
9529  $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9530  $xmp .= "\t\t\t".'</dc:title>'."\n";
9531  $xmp .= "\t\t\t".'<dc:creator>'."\n";
9532  $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
9533  $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->author).'</rdf:li>'."\n";
9534  $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
9535  $xmp .= "\t\t\t".'</dc:creator>'."\n";
9536  $xmp .= "\t\t\t".'<dc:description>'."\n";
9537  $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9538  $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->subject).'</rdf:li>'."\n";
9539  $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9540  $xmp .= "\t\t\t".'</dc:description>'."\n";
9541  $xmp .= "\t\t\t".'<dc:subject>'."\n";
9542  $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9543  $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->keywords).'</rdf:li>'."\n";
9544  $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9545  $xmp .= "\t\t\t".'</dc:subject>'."\n";
9546  $xmp .= "\t\t".'</rdf:Description>'."\n";
9547  // convert doc creation date format
9548  $dcdate = TCPDF_STATIC::getFormattedDate($this->doc_creation_timestamp);
9549  $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2);
9550  $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2);
9551  $doccreationdate .= substr($dcdate, 14, 3).':'.substr($dcdate, 18, 2);
9552  $doccreationdate = TCPDF_STATIC::_escapeXML($doccreationdate);
9553  // convert doc modification date format
9554  $dmdate = TCPDF_STATIC::getFormattedDate($this->doc_modification_timestamp);
9555  $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2);
9556  $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2);
9557  $docmoddate .= substr($dmdate, 14, 3).':'.substr($dmdate, 18, 2);
9558  $docmoddate = TCPDF_STATIC::_escapeXML($docmoddate);
9559  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
9560  $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n";
9561  $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n";
9562  $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n";
9563  $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n";
9564  $xmp .= "\t\t".'</rdf:Description>'."\n";
9565  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
9566  $xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC::_escapeXML($this->keywords).'</pdf:Keywords>'."\n";
9567  $xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC::_escapeXML(TCPDF_STATIC::getTCPDFProducer()).'</pdf:Producer>'."\n";
9568  $xmp .= "\t\t".'</rdf:Description>'."\n";
9569  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
9570  $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);
9571  $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
9572  $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
9573  $xmp .= "\t\t".'</rdf:Description>'."\n";
9574  if ($this->pdfa_mode) {
9575  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
9576  $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n";
9577  $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
9578  $xmp .= "\t\t".'</rdf:Description>'."\n";
9579  }
9580  // XMP extension schemas
9581  $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";
9582  $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
9583  $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9584  $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9585  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
9586  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
9587  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
9588  $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9589  $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9590  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
9591  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
9592  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
9593  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9594  $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9595  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9596  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9597  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
9598  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9599  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9600  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9601  $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9602  $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9603  $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9604  $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9605  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
9606  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
9607  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
9608  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9609  $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9610  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9611  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9612  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
9613  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
9614  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
9615  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9616  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9617  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9618  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
9619  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
9620  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9621  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9622  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9623  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9624  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
9625  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
9626  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9627  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9628  $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9629  $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9630  $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9631  $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9632  $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
9633  $xmp .= "\t\t".'</rdf:Description>'."\n";
9634  $xmp .= "\t".'</rdf:RDF>'."\n";
9635  $xmp .= $this->custom_xmp;
9636  $xmp .= '</x:xmpmeta>'."\n";
9637  $xmp .= '<?xpacket end="w"?>';
9638  $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
9639  // restore previous isunicode value
9640  $this->isunicode = $prev_isunicode;
9641  $this->encrypted = $prev_encrypted;
9642  $this->_out($out);
9643  return $oid;
9644  }
9645 
9651  protected function _putcatalog() {
9652  // put XMP
9653  $xmpobj = $this->_putXMP();
9654  // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile
9655  if ($this->pdfa_mode OR $this->force_srgb) {
9656  $iccobj = $this->_newobj();
9657  $icc = file_get_contents(dirname(__FILE__).'/include/sRGB.icc');
9658  $filter = '';
9659  if ($this->compress) {
9660  $filter = ' /Filter /FlateDecode';
9661  $icc = gzcompress($icc);
9662  }
9663  $icc = $this->_getrawstream($icc);
9664  $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9665  }
9666  // start catalog
9667  $oid = $this->_newobj();
9668  $out = '<< /Type /Catalog';
9669  $out .= ' /Version /'.$this->PDFVersion;
9670  //$out .= ' /Extensions <<>>';
9671  $out .= ' /Pages 1 0 R';
9672  //$out .= ' /PageLabels ' //...;
9673  $out .= ' /Names <<';
9674  if ((!$this->pdfa_mode) AND !empty($this->n_js)) {
9675  $out .= ' /JavaScript '.$this->n_js;
9676  }
9677  if (!empty($this->efnames)) {
9678  $out .= ' /EmbeddedFiles <</Names [';
9679  foreach ($this->efnames AS $fn => $fref) {
9680  $out .= ' '.$this->_datastring($fn).' '.$fref;
9681  }
9682  $out .= ' ]>>';
9683  }
9684  $out .= ' >>';
9685  if (!empty($this->dests)) {
9686  $out .= ' /Dests '.($this->n_dests).' 0 R';
9687  }
9688  $out .= $this->_putviewerpreferences();
9689  if (isset($this->LayoutMode) AND (!TCPDF_STATIC::empty_string($this->LayoutMode))) {
9690  $out .= ' /PageLayout /'.$this->LayoutMode;
9691  }
9692  if (isset($this->PageMode) AND (!TCPDF_STATIC::empty_string($this->PageMode))) {
9693  $out .= ' /PageMode /'.$this->PageMode;
9694  }
9695  if (count($this->outlines) > 0) {
9696  $out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
9697  $out .= ' /PageMode /UseOutlines';
9698  }
9699  //$out .= ' /Threads []';
9700  if ($this->ZoomMode == 'fullpage') {
9701  $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
9702  } elseif ($this->ZoomMode == 'fullwidth') {
9703  $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
9704  } elseif ($this->ZoomMode == 'real') {
9705  $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
9706  } elseif (!is_string($this->ZoomMode)) {
9707  $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100));
9708  }
9709  //$out .= ' /AA <<>>';
9710  //$out .= ' /URI <<>>';
9711  $out .= ' /Metadata '.$xmpobj.' 0 R';
9712  //$out .= ' /StructTreeRoot <<>>';
9713  //$out .= ' /MarkInfo <<>>';
9714  if (isset($this->l['a_meta_language'])) {
9715  $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
9716  }
9717  //$out .= ' /SpiderInfo <<>>';
9718  // set OutputIntent to sRGB IEC61966-2.1 if required
9719  if ($this->pdfa_mode OR $this->force_srgb) {
9720  $out .= ' /OutputIntents [<<';
9721  $out .= ' /Type /OutputIntent';
9722  $out .= ' /S /GTS_PDFA1';
9723  $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9724  $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9725  $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
9726  $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9727  $out .= ' /DestOutputProfile '.$iccobj.' 0 R';
9728  $out .= ' >>]';
9729  }
9730  //$out .= ' /PieceInfo <<>>';
9731  if (!empty($this->pdflayers)) {
9732  $lyrobjs = '';
9733  $lyrobjs_off = '';
9734  $lyrobjs_lock = '';
9735  foreach ($this->pdflayers as $layer) {
9736  $layer_obj_ref = ' '.$layer['objid'].' 0 R';
9737  $lyrobjs .= $layer_obj_ref;
9738  if ($layer['view'] === false) {
9739  $lyrobjs_off .= $layer_obj_ref;
9740  }
9741  if ($layer['lock']) {
9742  $lyrobjs_lock .= $layer_obj_ref;
9743  }
9744  }
9745  $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
9746  $out .= ' /D <<';
9747  $out .= ' /Name '.$this->_textstring('Layers', $oid);
9748  $out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
9749  $out .= ' /BaseState /ON';
9750  $out .= ' /OFF ['.$lyrobjs_off.']';
9751  $out .= ' /Locked ['.$lyrobjs_lock.']';
9752  $out .= ' /Intent /View';
9753  $out .= ' /AS [';
9754  $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
9755  $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
9756  $out .= ' ]';
9757  $out .= ' /Order ['.$lyrobjs.']';
9758  $out .= ' /ListMode /AllPages';
9759  //$out .= ' /RBGroups ['..']';
9760  //$out .= ' /Locked ['..']';
9761  $out .= ' >>';
9762  $out .= ' >>';
9763  }
9764  // AcroForm
9765  if (!empty($this->form_obj_id)
9766  OR ($this->sign AND isset($this->signature_data['cert_type']))
9767  OR !empty($this->empty_signature_appearance)) {
9768  $out .= ' /AcroForm <<';
9769  $objrefs = '';
9770  if ($this->sign AND isset($this->signature_data['cert_type'])) {
9771  // set reference for signature object
9772  $objrefs .= $this->sig_obj_id.' 0 R';
9773  }
9774  if (!empty($this->empty_signature_appearance)) {
9775  foreach ($this->empty_signature_appearance as $esa) {
9776  // set reference for empty signature objects
9777  $objrefs .= ' '.$esa['objid'].' 0 R';
9778  }
9779  }
9780  if (!empty($this->form_obj_id)) {
9781  foreach($this->form_obj_id as $objid) {
9782  $objrefs .= ' '.$objid.' 0 R';
9783  }
9784  }
9785  $out .= ' /Fields ['.$objrefs.']';
9786  // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
9787  if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
9788  $out .= ' /NeedAppearances false';
9789  }
9790  if ($this->sign AND isset($this->signature_data['cert_type'])) {
9791  if ($this->signature_data['cert_type'] > 0) {
9792  $out .= ' /SigFlags 3';
9793  } else {
9794  $out .= ' /SigFlags 1';
9795  }
9796  }
9797  //$out .= ' /CO ';
9798  if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
9799  $out .= ' /DR <<';
9800  $out .= ' /Font <<';
9801  foreach ($this->annotation_fonts as $fontkey => $fontid) {
9802  $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9803  }
9804  $out .= ' >> >>';
9805  }
9806  $font = $this->getFontBuffer('helvetica');
9807  $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
9808  $out .= ' /Q '.(($this->rtl)?'2':'0');
9809  //$out .= ' /XFA ';
9810  $out .= ' >>';
9811  // signatures
9812  if ($this->sign AND isset($this->signature_data['cert_type'])
9813  AND (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A'))) {
9814  if ($this->signature_data['cert_type'] > 0) {
9815  $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
9816  } else {
9817  $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
9818  }
9819  }
9820  }
9821  //$out .= ' /Legal <<>>';
9822  //$out .= ' /Requirements []';
9823  //$out .= ' /Collection <<>>';
9824  //$out .= ' /NeedsRendering true';
9825  $out .= ' >>';
9826  $out .= "\n".'endobj';
9827  $this->_out($out);
9828  return $oid;
9829  }
9830 
9838  protected function _putviewerpreferences() {
9840  $out = ' /ViewerPreferences <<';
9841  if ($this->rtl) {
9842  $out .= ' /Direction /R2L';
9843  } else {
9844  $out .= ' /Direction /L2R';
9845  }
9846  if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) {
9847  $out .= ' /HideToolbar true';
9848  }
9849  if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) {
9850  $out .= ' /HideMenubar true';
9851  }
9852  if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) {
9853  $out .= ' /HideWindowUI true';
9854  }
9855  if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) {
9856  $out .= ' /FitWindow true';
9857  }
9858  if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) {
9859  $out .= ' /CenterWindow true';
9860  }
9861  if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) {
9862  $out .= ' /DisplayDocTitle true';
9863  }
9864  if (isset($vp['NonFullScreenPageMode'])) {
9865  $out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode'];
9866  }
9867  if (isset($vp['ViewArea'])) {
9868  $out .= ' /ViewArea /'.$vp['ViewArea'];
9869  }
9870  if (isset($vp['ViewClip'])) {
9871  $out .= ' /ViewClip /'.$vp['ViewClip'];
9872  }
9873  if (isset($vp['PrintArea'])) {
9874  $out .= ' /PrintArea /'.$vp['PrintArea'];
9875  }
9876  if (isset($vp['PrintClip'])) {
9877  $out .= ' /PrintClip /'.$vp['PrintClip'];
9878  }
9879  if (isset($vp['PrintScaling'])) {
9880  $out .= ' /PrintScaling /'.$vp['PrintScaling'];
9881  }
9882  if (isset($vp['Duplex']) AND (!TCPDF_STATIC::empty_string($vp['Duplex']))) {
9883  $out .= ' /Duplex /'.$vp['Duplex'];
9884  }
9885  if (isset($vp['PickTrayByPDFSize'])) {
9886  if ($vp['PickTrayByPDFSize']) {
9887  $out .= ' /PickTrayByPDFSize true';
9888  } else {
9889  $out .= ' /PickTrayByPDFSize false';
9890  }
9891  }
9892  if (isset($vp['PrintPageRange'])) {
9893  $PrintPageRangeNum = '';
9894  foreach ($vp['PrintPageRange'] as $k => $v) {
9895  $PrintPageRangeNum .= ' '.($v - 1).'';
9896  }
9897  $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
9898  }
9899  if (isset($vp['NumCopies'])) {
9900  $out .= ' /NumCopies '.intval($vp['NumCopies']);
9901  }
9902  $out .= ' >>';
9903  return $out;
9904  }
9905 
9910  protected function _putheader() {
9911  $this->_out('%PDF-'.$this->PDFVersion);
9912  $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
9913  }
9914 
9919  protected function _enddoc() {
9920  if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
9921  // save subset chars of the previous font
9922  $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
9923  }
9924  $this->state = 1;
9925  $this->_putheader();
9926  $this->_putpages();
9927  $this->_putresources();
9928  // empty signature fields
9929  if (!empty($this->empty_signature_appearance)) {
9930  foreach ($this->empty_signature_appearance as $key => $esa) {
9931  // widget annotation for empty signature
9932  $out = $this->_getobj($esa['objid'])."\n";
9933  $out .= '<< /Type /Annot';
9934  $out .= ' /Subtype /Widget';
9935  $out .= ' /Rect ['.$esa['rect'].']';
9936  $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page
9937  $out .= ' /F 4';
9938  $out .= ' /FT /Sig';
9939  $signame = $esa['name'].sprintf(' [%03d]', ($key + 1));
9940  $out .= ' /T '.$this->_textstring($signame, $esa['objid']);
9941  $out .= ' /Ff 0';
9942  $out .= ' >>';
9943  $out .= "\n".'endobj';
9944  $this->_out($out);
9945  }
9946  }
9947  // Signature
9948  if ($this->sign AND isset($this->signature_data['cert_type'])) {
9949  // widget annotation for signature
9950  $out = $this->_getobj($this->sig_obj_id)."\n";
9951  $out .= '<< /Type /Annot';
9952  $out .= ' /Subtype /Widget';
9953  $out .= ' /Rect ['.$this->signature_appearance['rect'].']';
9954  $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
9955  $out .= ' /F 4';
9956  $out .= ' /FT /Sig';
9957  $out .= ' /T '.$this->_textstring($this->signature_appearance['name'], $this->sig_obj_id);
9958  $out .= ' /Ff 0';
9959  $out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
9960  $out .= ' >>';
9961  $out .= "\n".'endobj';
9962  $this->_out($out);
9963  // signature
9964  $this->_putsignature();
9965  }
9966  // Info
9967  $objid_info = $this->_putinfo();
9968  // Catalog
9969  $objid_catalog = $this->_putcatalog();
9970  // Cross-ref
9971  $o = $this->bufferlen;
9972  // XREF section
9973  $this->_out('xref');
9974  $this->_out('0 '.($this->n + 1));
9975  $this->_out('0000000000 65535 f ');
9976  $freegen = ($this->n + 2);
9977  for ($i=1; $i <= $this->n; ++$i) {
9978  if (!isset($this->offsets[$i]) AND ($i > 1)) {
9979  $this->_out(sprintf('0000000000 %05d f ', $freegen));
9980  ++$freegen;
9981  } else {
9982  $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
9983  }
9984  }
9985  // TRAILER
9986  $out = 'trailer'."\n";
9987  $out .= '<<';
9988  $out .= ' /Size '.($this->n + 1);
9989  $out .= ' /Root '.$objid_catalog.' 0 R';
9990  $out .= ' /Info '.$objid_info.' 0 R';
9991  if ($this->encrypted) {
9992  $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
9993  }
9994  $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
9995  $out .= ' >>';
9996  $this->_out($out);
9997  $this->_out('startxref');
9998  $this->_out($o);
9999  $this->_out('%%EOF');
10000  $this->state = 3; // end-of-doc
10001  }
10002 
10010  protected function _beginpage($orientation='', $format='') {
10011  ++$this->page;
10012  $this->pageobjects[$this->page] = array();
10013  $this->setPageBuffer($this->page, '');
10014  // initialize array for graphics tranformation positions inside a page buffer
10015  $this->transfmrk[$this->page] = array();
10016  $this->state = 2;
10017  if (TCPDF_STATIC::empty_string($orientation)) {
10018  if (isset($this->CurOrientation)) {
10019  $orientation = $this->CurOrientation;
10020  } elseif ($this->fwPt > $this->fhPt) {
10021  // landscape
10022  $orientation = 'L';
10023  } else {
10024  // portrait
10025  $orientation = 'P';
10026  }
10027  }
10028  if (TCPDF_STATIC::empty_string($format)) {
10029  $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
10030  $this->setPageOrientation($orientation);
10031  } else {
10032  $this->setPageFormat($format, $orientation);
10033  }
10034  if ($this->rtl) {
10035  $this->x = $this->w - $this->rMargin;
10036  } else {
10037  $this->x = $this->lMargin;
10038  }
10039  $this->y = $this->tMargin;
10040  if (isset($this->newpagegroup[$this->page])) {
10041  // start a new group
10042  $this->currpagegroup = $this->newpagegroup[$this->page];
10043  $this->pagegroups[$this->currpagegroup] = 1;
10044  } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
10045  ++$this->pagegroups[$this->currpagegroup];
10046  }
10047  }
10048 
10053  protected function _endpage() {
10054  $this->setVisibility('all');
10055  $this->state = 1;
10056  }
10057 
10063  protected function _newobj() {
10064  $this->_out($this->_getobj());
10065  return $this->n;
10066  }
10067 
10075  protected function _getobj($objid='') {
10076  if ($objid === '') {
10077  ++$this->n;
10078  $objid = $this->n;
10079  }
10080  $this->offsets[$objid] = $this->bufferlen;
10081  $this->pageobjects[$this->page][] = $objid;
10082  return $objid.' 0 obj';
10083  }
10084 
10092  protected function _dounderline($x, $y, $txt) {
10093  $w = $this->GetStringWidth($txt);
10094  return $this->_dounderlinew($x, $y, $w);
10095  }
10096 
10105  protected function _dounderlinew($x, $y, $w) {
10106  $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10107  return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
10108  }
10109 
10117  protected function _dolinethrough($x, $y, $txt) {
10118  $w = $this->GetStringWidth($txt);
10119  return $this->_dolinethroughw($x, $y, $w);
10120  }
10121 
10130  protected function _dolinethroughw($x, $y, $w) {
10131  $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10132  return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew);
10133  }
10134 
10143  protected function _dooverline($x, $y, $txt) {
10144  $w = $this->GetStringWidth($txt);
10145  return $this->_dooverlinew($x, $y, $w);
10146  }
10147 
10156  protected function _dooverlinew($x, $y, $w) {
10157  $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10158  return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
10159 
10160  }
10161 
10169  protected function _datastring($s, $n=0) {
10170  if ($n == 0) {
10171  $n = $this->n;
10172  }
10173  $s = $this->_encrypt_data($n, $s);
10174  return '('. TCPDF_STATIC::_escape($s).')';
10175  }
10176 
10183  public function setDocCreationTimestamp($time) {
10184  if (is_string($time)) {
10185  $time = TCPDF_STATIC::getTimestamp($time);
10186  }
10187  $this->doc_creation_timestamp = intval($time);
10188  }
10189 
10196  public function setDocModificationTimestamp($time) {
10197  if (is_string($time)) {
10198  $time = TCPDF_STATIC::getTimestamp($time);
10199  }
10200  $this->doc_modification_timestamp = intval($time);
10201  }
10202 
10209  public function getDocCreationTimestamp() {
10211  }
10212 
10219  public function getDocModificationTimestamp() {
10221  }
10222 
10231  protected function _datestring($n=0, $timestamp=0) {
10232  if ((empty($timestamp)) OR ($timestamp < 0)) {
10234  }
10236  }
10237 
10245  protected function _textstring($s, $n=0) {
10246  if ($this->isunicode) {
10247  //Convert string to UTF-16BE
10248  $s = TCPDF_FONTS::UTF8ToUTF16BE($s, true, $this->isunicode, $this->CurrentFont);
10249  }
10250  return $this->_datastring($s, $n);
10251  }
10252 
10261  protected function _getrawstream($s, $n=0) {
10262  if ($n <= 0) {
10263  // default to current object
10264  $n = $this->n;
10265  }
10266  return $this->_encrypt_data($n, $s);
10267  }
10268 
10274  protected function _out($s) {
10275  if ($this->state == 2) {
10276  if ($this->inxobj) {
10277  // we are inside an XObject template
10278  $this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
10279  } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
10280  // puts data before page footer
10281  $pagebuff = $this->getPageBuffer($this->page);
10282  $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
10283  $footer = substr($pagebuff, -$this->footerlen[$this->page]);
10284  $this->setPageBuffer($this->page, $page.$s."\n".$footer);
10285  // update footer position
10286  $this->footerpos[$this->page] += strlen($s."\n");
10287  } else {
10288  // set page data
10289  $this->setPageBuffer($this->page, $s."\n", true);
10290  }
10291  } elseif ($this->state > 0) {
10292  // set general data
10293  $this->setBuffer($s."\n");
10294  }
10295  }
10296 
10303  public function setHeaderFont($font) {
10304  $this->header_font = $font;
10305  }
10306 
10313  public function getHeaderFont() {
10314  return $this->header_font;
10315  }
10316 
10323  public function setFooterFont($font) {
10324  $this->footer_font = $font;
10325  }
10326 
10333  public function getFooterFont() {
10334  return $this->footer_font;
10335  }
10336 
10343  public function setLanguageArray($language) {
10344  $this->l = $language;
10345  if (isset($this->l['a_meta_dir'])) {
10346  $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
10347  } else {
10348  $this->rtl = false;
10349  }
10350  }
10351 
10356  public function getPDFData() {
10357  if ($this->state < 3) {
10358  $this->Close();
10359  }
10360  return $this->buffer;
10361  }
10362 
10375  public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
10376  if (isset($url[1]) AND ($url[0] == '#') AND is_numeric($url[1])) {
10377  // convert url to internal link
10378  $lnkdata = explode(',', $url);
10379  if (isset($lnkdata[0]) ) {
10380  $page = substr($lnkdata[0], 1);
10381  if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
10382  $lnky = floatval($lnkdata[1]);
10383  } else {
10384  $lnky = 0;
10385  }
10386  $url = $this->AddLink();
10387  $this->SetLink($url, $lnky, $page);
10388  }
10389  }
10390  // store current settings
10391  $prevcolor = $this->fgcolor;
10392  $prevstyle = $this->FontStyle;
10393  if (empty($color)) {
10394  $this->SetTextColorArray($this->htmlLinkColorArray);
10395  } else {
10396  $this->SetTextColorArray($color);
10397  }
10398  if ($style == -1) {
10399  $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
10400  } else {
10401  $this->SetFont('', $this->FontStyle.$style);
10402  }
10403  $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
10404  // restore settings
10405  $this->SetFont('', $prevstyle);
10406  $this->SetTextColorArray($prevcolor);
10407  return $ret;
10408  }
10409 
10417  public function pixelsToUnits($px) {
10418  return ($px / ($this->imgscale * $this->k));
10419  }
10420 
10428  public function unhtmlentities($text_to_convert) {
10429  return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
10430  }
10431 
10432  // ENCRYPTION METHODS ----------------------------------
10433 
10443  protected function _objectkey($n) {
10444  $objkey = $this->encryptdata['key'].pack('VXxx', $n);
10445  if ($this->encryptdata['mode'] == 2) { // AES-128
10446  // AES padding
10447  $objkey .= "\x73\x41\x6C\x54"; // sAlT
10448  }
10449  $objkey = substr(TCPDF_STATIC::_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
10450  $objkey = substr($objkey, 0, 16);
10451  return $objkey;
10452  }
10453 
10463  protected function _encrypt_data($n, $s) {
10464  if (!$this->encrypted) {
10465  return $s;
10466  }
10467  switch ($this->encryptdata['mode']) {
10468  case 0: // RC4-40
10469  case 1: { // RC4-128
10470  $s = TCPDF_STATIC::_RC4($this->_objectkey($n), $s, $this->last_enc_key, $this->last_enc_key_c);
10471  break;
10472  }
10473  case 2: { // AES-128
10474  $s = TCPDF_STATIC::_AES($this->_objectkey($n), $s);
10475  break;
10476  }
10477  case 3: { // AES-256
10478  $s = TCPDF_STATIC::_AES($this->encryptdata['key'], $s);
10479  break;
10480  }
10481  }
10482  return $s;
10483  }
10484 
10491  protected function _putencryption() {
10492  if (!$this->encrypted) {
10493  return;
10494  }
10495  $this->encryptdata['objid'] = $this->_newobj();
10496  $out = '<<';
10497  if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
10498  $this->encryptdata['Filter'] = 'Standard';
10499  }
10500  $out .= ' /Filter /'.$this->encryptdata['Filter'];
10501  if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
10502  $out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
10503  }
10504  if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
10505  $this->encryptdata['V'] = 1;
10506  }
10507  // V is a code specifying the algorithm to be used in encrypting and decrypting the document
10508  $out .= ' /V '.$this->encryptdata['V'];
10509  if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
10510  // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
10511  $out .= ' /Length '.$this->encryptdata['Length'];
10512  } else {
10513  $out .= ' /Length 40';
10514  }
10515  if ($this->encryptdata['V'] >= 4) {
10516  if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
10517  $this->encryptdata['StmF'] = 'Identity';
10518  }
10519  if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
10520  // The name of the crypt filter that shall be used when decrypting all strings in the document.
10521  $this->encryptdata['StrF'] = 'Identity';
10522  }
10523  // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
10524  if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
10525  $out .= ' /CF <<';
10526  $out .= ' /'.$this->encryptdata['StmF'].' <<';
10527  $out .= ' /Type /CryptFilter';
10528  if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
10529  // The method used
10530  $out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
10531  if ($this->encryptdata['pubkey']) {
10532  $out .= ' /Recipients [';
10533  foreach ($this->encryptdata['Recipients'] as $rec) {
10534  $out .= ' <'.$rec.'>';
10535  }
10536  $out .= ' ]';
10537  if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
10538  $out .= ' /EncryptMetadata false';
10539  } else {
10540  $out .= ' /EncryptMetadata true';
10541  }
10542  }
10543  } else {
10544  $out .= ' /CFM /None';
10545  }
10546  if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
10547  // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
10548  $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
10549  } else {
10550  $out .= ' /AuthEvent /DocOpen';
10551  }
10552  if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
10553  // The bit length of the encryption key.
10554  $out .= ' /Length '.$this->encryptdata['CF']['Length'];
10555  }
10556  $out .= ' >> >>';
10557  }
10558  // The name of the crypt filter that shall be used by default when decrypting streams.
10559  $out .= ' /StmF /'.$this->encryptdata['StmF'];
10560  // The name of the crypt filter that shall be used when decrypting all strings in the document.
10561  $out .= ' /StrF /'.$this->encryptdata['StrF'];
10562  if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
10563  // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
10564  $out .= ' /EFF /'.$this->encryptdata[''];
10565  }
10566  }
10567  // Additional encryption dictionary entries for the standard security handler
10568  if ($this->encryptdata['pubkey']) {
10569  if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
10570  $out .= ' /Recipients [';
10571  foreach ($this->encryptdata['Recipients'] as $rec) {
10572  $out .= ' <'.$rec.'>';
10573  }
10574  $out .= ' ]';
10575  }
10576  } else {
10577  $out .= ' /R';
10578  if ($this->encryptdata['V'] == 5) { // AES-256
10579  $out .= ' 5';
10580  $out .= ' /OE ('.TCPDF_STATIC::_escape($this->encryptdata['OE']).')';
10581  $out .= ' /UE ('.TCPDF_STATIC::_escape($this->encryptdata['UE']).')';
10582  $out .= ' /Perms ('.TCPDF_STATIC::_escape($this->encryptdata['perms']).')';
10583  } elseif ($this->encryptdata['V'] == 4) { // AES-128
10584  $out .= ' 4';
10585  } elseif ($this->encryptdata['V'] < 2) { // RC-40
10586  $out .= ' 2';
10587  } else { // RC-128
10588  $out .= ' 3';
10589  }
10590  $out .= ' /O ('.TCPDF_STATIC::_escape($this->encryptdata['O']).')';
10591  $out .= ' /U ('.TCPDF_STATIC::_escape($this->encryptdata['U']).')';
10592  $out .= ' /P '.$this->encryptdata['P'];
10593  if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
10594  $out .= ' /EncryptMetadata false';
10595  } else {
10596  $out .= ' /EncryptMetadata true';
10597  }
10598  }
10599  $out .= ' >>';
10600  $out .= "\n".'endobj';
10601  $this->_out($out);
10602  }
10603 
10611  protected function _Uvalue() {
10612  if ($this->encryptdata['mode'] == 0) { // RC4-40
10613  return TCPDF_STATIC::_RC4($this->encryptdata['key'], TCPDF_STATIC::$enc_padding, $this->last_enc_key, $this->last_enc_key_c);
10614  } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
10615  $tmp = TCPDF_STATIC::_md5_16(TCPDF_STATIC::$enc_padding.$this->encryptdata['fileid']);
10616  $enc = TCPDF_STATIC::_RC4($this->encryptdata['key'], $tmp, $this->last_enc_key, $this->last_enc_key_c);
10617  $len = strlen($tmp);
10618  for ($i = 1; $i <= 19; ++$i) {
10619  $ek = '';
10620  for ($j = 0; $j < $len; ++$j) {
10621  $ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i);
10622  }
10623  $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10624  }
10625  $enc .= str_repeat("\x00", 16);
10626  return substr($enc, 0, 32);
10627  } elseif ($this->encryptdata['mode'] == 3) { // AES-256
10629  // User Validation Salt
10630  $this->encryptdata['UVS'] = substr($seed, 0, 8);
10631  // User Key Salt
10632  $this->encryptdata['UKS'] = substr($seed, 8, 16);
10633  return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
10634  }
10635  }
10636 
10644  protected function _UEvalue() {
10645  $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
10646  return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']);
10647  }
10648 
10656  protected function _Ovalue() {
10657  if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
10658  $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['owner_password']);
10659  if ($this->encryptdata['mode'] > 0) {
10660  for ($i = 0; $i < 50; ++$i) {
10661  $tmp = TCPDF_STATIC::_md5_16($tmp);
10662  }
10663  }
10664  $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
10665  $enc = TCPDF_STATIC::_RC4($owner_key, $this->encryptdata['user_password'], $this->last_enc_key, $this->last_enc_key_c);
10666  if ($this->encryptdata['mode'] > 0) {
10667  $len = strlen($owner_key);
10668  for ($i = 1; $i <= 19; ++$i) {
10669  $ek = '';
10670  for ($j = 0; $j < $len; ++$j) {
10671  $ek .= chr(ord($owner_key[$j]) ^ $i);
10672  }
10673  $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10674  }
10675  }
10676  return $enc;
10677  } elseif ($this->encryptdata['mode'] == 3) { // AES-256
10679  // Owner Validation Salt
10680  $this->encryptdata['OVS'] = substr($seed, 0, 8);
10681  // Owner Key Salt
10682  $this->encryptdata['OKS'] = substr($seed, 8, 16);
10683  return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
10684  }
10685  }
10686 
10694  protected function _OEvalue() {
10695  $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
10696  return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']);
10697  }
10698 
10707  protected function _fixAES256Password($password) {
10708  $psw = ''; // password to be returned
10709  $psw_array = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($password, $this->isunicode, $this->CurrentFont), $password, $this->rtl, $this->isunicode, $this->CurrentFont);
10710  foreach ($psw_array as $c) {
10711  $psw .= TCPDF_FONTS::unichr($c, $this->isunicode);
10712  }
10713  return substr($psw, 0, 127);
10714  }
10715 
10722  protected function _generateencryptionkey() {
10723  $keybytelen = ($this->encryptdata['Length'] / 8);
10724  if (!$this->encryptdata['pubkey']) { // standard mode
10725  if ($this->encryptdata['mode'] == 3) { // AES-256
10726  // generate 256 bit random key
10727  $this->encryptdata['key'] = substr(hash('sha256', TCPDF_STATIC::getRandomSeed(), true), 0, $keybytelen);
10728  // truncate passwords
10729  $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
10730  $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
10731  // Compute U value
10732  $this->encryptdata['U'] = $this->_Uvalue();
10733  // Compute UE value
10734  $this->encryptdata['UE'] = $this->_UEvalue();
10735  // Compute O value
10736  $this->encryptdata['O'] = $this->_Ovalue();
10737  // Compute OE value
10738  $this->encryptdata['OE'] = $this->_OEvalue();
10739  // Compute P value
10740  $this->encryptdata['P'] = $this->encryptdata['protection'];
10741  // Computing the encryption dictionary's Perms (permissions) value
10742  $perms = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
10743  $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
10744  if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
10745  $perms .= 'F';
10746  } else {
10747  $perms .= 'T';
10748  }
10749  $perms .= 'adb'; // bytes 9-11
10750  $perms .= 'nick'; // bytes 12-15
10751  $this->encryptdata['perms'] = TCPDF_STATIC::_AESnopad($this->encryptdata['key'], $perms);
10752  } else { // RC4-40, RC4-128, AES-128
10753  // Pad passwords
10754  $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10755  $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10756  // Compute O value
10757  $this->encryptdata['O'] = $this->_Ovalue();
10758  // get default permissions (reverse byte order)
10759  $permissions = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']);
10760  // Compute encryption key
10761  $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
10762  if ($this->encryptdata['mode'] > 0) {
10763  for ($i = 0; $i < 50; ++$i) {
10764  $tmp = TCPDF_STATIC::_md5_16(substr($tmp, 0, $keybytelen));
10765  }
10766  }
10767  $this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
10768  // Compute U value
10769  $this->encryptdata['U'] = $this->_Uvalue();
10770  // Compute P value
10771  $this->encryptdata['P'] = $this->encryptdata['protection'];
10772  }
10773  } else { // Public-Key mode
10774  // random 20-byte seed
10775  $seed = sha1(TCPDF_STATIC::getRandomSeed(), true);
10776  $recipient_bytes = '';
10777  foreach ($this->encryptdata['pubkeys'] as $pubkey) {
10778  // for each public certificate
10779  if (isset($pubkey['p'])) {
10780  $pkprotection = TCPDF_STATIC::getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
10781  } else {
10782  $pkprotection = $this->encryptdata['protection'];
10783  }
10784  // get default permissions (reverse byte order)
10785  $pkpermissions = TCPDF_STATIC::getEncPermissionsString($pkprotection);
10786  // envelope data
10787  $envelope = $seed.$pkpermissions;
10788  // write the envelope data to a temporary file
10789  $tempkeyfile = TCPDF_STATIC::getObjFilename('key', $this->file_id);
10790  $f = TCPDF_STATIC::fopenLocal($tempkeyfile, 'wb');
10791  if (!$f) {
10792  $this->Error('Unable to create temporary key file: '.$tempkeyfile);
10793  }
10794  $envelope_length = strlen($envelope);
10795  fwrite($f, $envelope, $envelope_length);
10796  fclose($f);
10797  $tempencfile = TCPDF_STATIC::getObjFilename('enc', $this->file_id);
10798  if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) {
10799  $this->Error('Unable to encrypt the file: '.$tempkeyfile);
10800  }
10801  // read encryption signature
10802  $signature = file_get_contents($tempencfile, false, null, $envelope_length);
10803  // extract signature
10804  $signature = substr($signature, strpos($signature, 'Content-Disposition'));
10805  $tmparr = explode("\n\n", $signature);
10806  $signature = trim($tmparr[1]);
10807  unset($tmparr);
10808  // decode signature
10809  $signature = base64_decode($signature);
10810  // convert signature to hex
10811  $hexsignature = current(unpack('H*', $signature));
10812  // store signature on recipients array
10813  $this->encryptdata['Recipients'][] = $hexsignature;
10814  // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
10815  $recipient_bytes .= $signature;
10816  }
10817  // calculate encryption key
10818  if ($this->encryptdata['mode'] == 3) { // AES-256
10819  $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
10820  } else { // RC4-40, RC4-128, AES-128
10821  $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
10822  }
10823  }
10824  }
10825 
10840  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) {
10841  if ($this->pdfa_mode) {
10842  // encryption is not allowed in PDF/A mode
10843  return;
10844  }
10845  $this->encryptdata['protection'] = TCPDF_STATIC::getUserPermissionCode($permissions, $mode);
10846  if (($pubkeys !== null) AND (is_array($pubkeys))) {
10847  // public-key mode
10848  $this->encryptdata['pubkeys'] = $pubkeys;
10849  if ($mode == 0) {
10850  // public-Key Security requires at least 128 bit
10851  $mode = 1;
10852  }
10853  if (!function_exists('openssl_pkcs7_encrypt')) {
10854  $this->Error('Public-Key Security requires openssl library.');
10855  }
10856  // Set Public-Key filter (available are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
10857  $this->encryptdata['pubkey'] = true;
10858  $this->encryptdata['Filter'] = 'Adobe.PubSec';
10859  $this->encryptdata['StmF'] = 'DefaultCryptFilter';
10860  $this->encryptdata['StrF'] = 'DefaultCryptFilter';
10861  } else {
10862  // standard mode (password mode)
10863  $this->encryptdata['pubkey'] = false;
10864  $this->encryptdata['Filter'] = 'Standard';
10865  $this->encryptdata['StmF'] = 'StdCF';
10866  $this->encryptdata['StrF'] = 'StdCF';
10867  }
10868  if ($mode > 1) { // AES
10869  if (!extension_loaded('openssl') && !extension_loaded('mcrypt')) {
10870  $this->Error('AES encryption requires openssl or mcrypt extension (http://www.php.net/manual/en/mcrypt.requirements.php).');
10871  }
10872  if (extension_loaded('openssl') && !in_array('aes-256-cbc', openssl_get_cipher_methods())) {
10873  $this->Error('AES encryption requires openssl/aes-256-cbc cypher.');
10874  }
10875  if (extension_loaded('mcrypt') && mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
10876  $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
10877  }
10878  if (($mode == 3) AND !function_exists('hash')) {
10879  // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
10880  $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
10881  }
10882  }
10883  if ($owner_pass === null) {
10884  $owner_pass = md5(TCPDF_STATIC::getRandomSeed());
10885  }
10886  $this->encryptdata['user_password'] = $user_pass;
10887  $this->encryptdata['owner_password'] = $owner_pass;
10888  $this->encryptdata['mode'] = $mode;
10889  switch ($mode) {
10890  case 0: { // RC4 40 bit
10891  $this->encryptdata['V'] = 1;
10892  $this->encryptdata['Length'] = 40;
10893  $this->encryptdata['CF']['CFM'] = 'V2';
10894  break;
10895  }
10896  case 1: { // RC4 128 bit
10897  $this->encryptdata['V'] = 2;
10898  $this->encryptdata['Length'] = 128;
10899  $this->encryptdata['CF']['CFM'] = 'V2';
10900  if ($this->encryptdata['pubkey']) {
10901  $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
10902  $this->encryptdata['Recipients'] = array();
10903  }
10904  break;
10905  }
10906  case 2: { // AES 128 bit
10907  $this->encryptdata['V'] = 4;
10908  $this->encryptdata['Length'] = 128;
10909  $this->encryptdata['CF']['CFM'] = 'AESV2';
10910  $this->encryptdata['CF']['Length'] = 128;
10911  if ($this->encryptdata['pubkey']) {
10912  $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
10913  $this->encryptdata['Recipients'] = array();
10914  }
10915  break;
10916  }
10917  case 3: { // AES 256 bit
10918  $this->encryptdata['V'] = 5;
10919  $this->encryptdata['Length'] = 256;
10920  $this->encryptdata['CF']['CFM'] = 'AESV3';
10921  $this->encryptdata['CF']['Length'] = 256;
10922  if ($this->encryptdata['pubkey']) {
10923  $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
10924  $this->encryptdata['Recipients'] = array();
10925  }
10926  break;
10927  }
10928  }
10929  $this->encrypted = true;
10930  $this->encryptdata['fileid'] = TCPDF_STATIC::convertHexStringToString($this->file_id);
10931  $this->_generateencryptionkey();
10932  }
10933 
10934  // END OF ENCRYPTION FUNCTIONS -------------------------
10935 
10936  // START TRANSFORMATIONS SECTION -----------------------
10937 
10946  public function StartTransform() {
10947  if ($this->state != 2) {
10948  return;
10949  }
10950  $this->_outSaveGraphicsState();
10951  if ($this->inxobj) {
10952  // we are inside an XObject template
10953  $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
10954  } else {
10955  $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
10956  }
10958  $this->transfmatrix[$this->transfmatrix_key] = array();
10959  }
10960 
10969  public function StopTransform() {
10970  if ($this->state != 2) {
10971  return;
10972  }
10973  $this->_outRestoreGraphicsState();
10974  if (isset($this->transfmatrix[$this->transfmatrix_key])) {
10975  array_pop($this->transfmatrix[$this->transfmatrix_key]);
10977  }
10978  if ($this->inxobj) {
10979  // we are inside an XObject template
10980  array_pop($this->xobjects[$this->xobjid]['transfmrk']);
10981  } else {
10982  array_pop($this->transfmrk[$this->page]);
10983  }
10984  }
10994  public function ScaleX($s_x, $x='', $y='') {
10995  $this->Scale($s_x, 100, $x, $y);
10996  }
10997 
11007  public function ScaleY($s_y, $x='', $y='') {
11008  $this->Scale(100, $s_y, $x, $y);
11009  }
11010 
11020  public function ScaleXY($s, $x='', $y='') {
11021  $this->Scale($s, $s, $x, $y);
11022  }
11023 
11034  public function Scale($s_x, $s_y, $x='', $y='') {
11035  if ($x === '') {
11036  $x = $this->x;
11037  }
11038  if ($y === '') {
11039  $y = $this->y;
11040  }
11041  if (($s_x == 0) OR ($s_y == 0)) {
11042  $this->Error('Please do not use values equal to zero for scaling');
11043  }
11044  $y = ($this->h - $y) * $this->k;
11045  $x *= $this->k;
11046  //calculate elements of transformation matrix
11047  $s_x /= 100;
11048  $s_y /= 100;
11049  $tm = array();
11050  $tm[0] = $s_x;
11051  $tm[1] = 0;
11052  $tm[2] = 0;
11053  $tm[3] = $s_y;
11054  $tm[4] = $x * (1 - $s_x);
11055  $tm[5] = $y * (1 - $s_y);
11056  //scale the coordinate system
11057  $this->Transform($tm);
11058  }
11059 
11067  public function MirrorH($x='') {
11068  $this->Scale(-100, 100, $x);
11069  }
11070 
11078  public function MirrorV($y='') {
11079  $this->Scale(100, -100, '', $y);
11080  }
11081 
11090  public function MirrorP($x='',$y='') {
11091  $this->Scale(-100, -100, $x, $y);
11092  }
11093 
11103  public function MirrorL($angle=0, $x='',$y='') {
11104  $this->Scale(-100, 100, $x, $y);
11105  $this->Rotate(-2*($angle-90), $x, $y);
11106  }
11107 
11115  public function TranslateX($t_x) {
11116  $this->Translate($t_x, 0);
11117  }
11118 
11126  public function TranslateY($t_y) {
11127  $this->Translate(0, $t_y);
11128  }
11129 
11138  public function Translate($t_x, $t_y) {
11139  //calculate elements of transformation matrix
11140  $tm = array();
11141  $tm[0] = 1;
11142  $tm[1] = 0;
11143  $tm[2] = 0;
11144  $tm[3] = 1;
11145  $tm[4] = $t_x * $this->k;
11146  $tm[5] = -$t_y * $this->k;
11147  //translate the coordinate system
11148  $this->Transform($tm);
11149  }
11150 
11160  public function Rotate($angle, $x='', $y='') {
11161  if ($x === '') {
11162  $x = $this->x;
11163  }
11164  if ($y === '') {
11165  $y = $this->y;
11166  }
11167  $y = ($this->h - $y) * $this->k;
11168  $x *= $this->k;
11169  //calculate elements of transformation matrix
11170  $tm = array();
11171  $tm[0] = cos(deg2rad($angle));
11172  $tm[1] = sin(deg2rad($angle));
11173  $tm[2] = -$tm[1];
11174  $tm[3] = $tm[0];
11175  $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
11176  $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
11177  //rotate the coordinate system around ($x,$y)
11178  $this->Transform($tm);
11179  }
11180 
11190  public function SkewX($angle_x, $x='', $y='') {
11191  $this->Skew($angle_x, 0, $x, $y);
11192  }
11193 
11203  public function SkewY($angle_y, $x='', $y='') {
11204  $this->Skew(0, $angle_y, $x, $y);
11205  }
11206 
11217  public function Skew($angle_x, $angle_y, $x='', $y='') {
11218  if ($x === '') {
11219  $x = $this->x;
11220  }
11221  if ($y === '') {
11222  $y = $this->y;
11223  }
11224  if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
11225  $this->Error('Please use values between -90 and +90 degrees for Skewing.');
11226  }
11227  $x *= $this->k;
11228  $y = ($this->h - $y) * $this->k;
11229  //calculate elements of transformation matrix
11230  $tm = array();
11231  $tm[0] = 1;
11232  $tm[1] = tan(deg2rad($angle_y));
11233  $tm[2] = tan(deg2rad($angle_x));
11234  $tm[3] = 1;
11235  $tm[4] = -$tm[2] * $y;
11236  $tm[5] = -$tm[1] * $x;
11237  //skew the coordinate system
11238  $this->Transform($tm);
11239  }
11240 
11248  protected function Transform($tm) {
11249  if ($this->state != 2) {
11250  return;
11251  }
11252  $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
11253  // add tranformation matrix
11254  $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
11255  // update transformation mark
11256  if ($this->inxobj) {
11257  // we are inside an XObject template
11258  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
11259  $key = key($this->xobjects[$this->xobjid]['transfmrk']);
11260  $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
11261  }
11262  } elseif (end($this->transfmrk[$this->page]) !== false) {
11263  $key = key($this->transfmrk[$this->page]);
11264  $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
11265  }
11266  }
11267 
11268  // END TRANSFORMATIONS SECTION -------------------------
11269 
11270  // START GRAPHIC FUNCTIONS SECTION ---------------------
11271  // The following section is based on the code provided by David Hernandez Sanz
11272 
11280  public function SetLineWidth($width) {
11281  //Set line width
11282  $this->LineWidth = $width;
11283  $this->linestyleWidth = sprintf('%F w', ($width * $this->k));
11284  if ($this->state == 2) {
11285  $this->_out($this->linestyleWidth);
11286  }
11287  }
11288 
11296  public function GetLineWidth() {
11297  return $this->LineWidth;
11298  }
11299 
11323  public function SetLineStyle($style, $ret=false) {
11324  $s = ''; // string to be returned
11325  if (!is_array($style)) {
11326  return;
11327  }
11328  if (isset($style['width'])) {
11329  $this->LineWidth = $style['width'];
11330  $this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k));
11331  $s .= $this->linestyleWidth.' ';
11332  }
11333  if (isset($style['cap'])) {
11334  $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
11335  if (isset($ca[$style['cap']])) {
11336  $this->linestyleCap = $ca[$style['cap']].' J';
11337  $s .= $this->linestyleCap.' ';
11338  }
11339  }
11340  if (isset($style['join'])) {
11341  $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
11342  if (isset($ja[$style['join']])) {
11343  $this->linestyleJoin = $ja[$style['join']].' j';
11344  $s .= $this->linestyleJoin.' ';
11345  }
11346  }
11347  if (isset($style['dash'])) {
11348  $dash_string = '';
11349  if ($style['dash']) {
11350  if (preg_match('/^.+,/', $style['dash']) > 0) {
11351  $tab = explode(',', $style['dash']);
11352  } else {
11353  $tab = array($style['dash']);
11354  }
11355  $dash_string = '';
11356  foreach ($tab as $i => $v) {
11357  if ($i) {
11358  $dash_string .= ' ';
11359  }
11360  $dash_string .= sprintf('%F', $v);
11361  }
11362  }
11363  if (!isset($style['phase']) OR !$style['dash']) {
11364  $style['phase'] = 0;
11365  }
11366  $this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']);
11367  $s .= $this->linestyleDash.' ';
11368  }
11369  if (isset($style['color'])) {
11370  $s .= $this->SetDrawColorArray($style['color'], true).' ';
11371  }
11372  if (!$ret AND ($this->state == 2)) {
11373  $this->_out($s);
11374  }
11375  return $s;
11376  }
11377 
11385  protected function _outPoint($x, $y) {
11386  if ($this->state == 2) {
11387  $this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k)));
11388  }
11389  }
11390 
11399  protected function _outLine($x, $y) {
11400  if ($this->state == 2) {
11401  $this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k)));
11402  }
11403  }
11404 
11415  protected function _outRect($x, $y, $w, $h, $op) {
11416  if ($this->state == 2) {
11417  $this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k), (($this->h - $y) * $this->k), ($w * $this->k), (-$h * $this->k), $op));
11418  }
11419  }
11420 
11433  protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
11434  if ($this->state == 2) {
11435  $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)));
11436  }
11437  }
11438 
11449  protected function _outCurveV($x2, $y2, $x3, $y3) {
11450  if ($this->state == 2) {
11451  $this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11452  }
11453  }
11454 
11465  protected function _outCurveY($x1, $y1, $x3, $y3) {
11466  if ($this->state == 2) {
11467  $this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11468  }
11469  }
11470 
11482  public function Line($x1, $y1, $x2, $y2, $style=array()) {
11483  if ($this->state != 2) {
11484  return;
11485  }
11486  if (is_array($style)) {
11487  $this->SetLineStyle($style);
11488  }
11489  $this->_outPoint($x1, $y1);
11490  $this->_outLine($x2, $y2);
11491  $this->_out('S');
11492  }
11493 
11512  public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
11513  if ($this->state != 2) {
11514  return;
11515  }
11516  if (empty($style)) {
11517  $style = 'S';
11518  }
11519  if (!(strpos($style, 'F') === false) AND !empty($fill_color)) {
11520  // set background color
11521  $this->SetFillColorArray($fill_color);
11522  }
11523  if (!empty($border_style)) {
11524  if (isset($border_style['all']) AND !empty($border_style['all'])) {
11525  //set global style for border
11526  $this->SetLineStyle($border_style['all']);
11527  $border_style = array();
11528  } else {
11529  // remove stroke operator from style
11530  $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*' );
11531  if (isset($opnostroke[$style])) {
11532  $style = $opnostroke[$style];
11533  }
11534  }
11535  }
11536  if (!empty($style)) {
11538  $this->_outRect($x, $y, $w, $h, $op);
11539  }
11540  if (!empty($border_style)) {
11541  $border_style2 = array();
11542  foreach ($border_style as $line => $value) {
11543  $length = strlen($line);
11544  for ($i = 0; $i < $length; ++$i) {
11545  $border_style2[$line[$i]] = $value;
11546  }
11547  }
11548  $border_style = $border_style2;
11549  if (isset($border_style['L']) AND $border_style['L']) {
11550  $this->Line($x, $y, $x, $y + $h, $border_style['L']);
11551  }
11552  if (isset($border_style['T']) AND $border_style['T']) {
11553  $this->Line($x, $y, $x + $w, $y, $border_style['T']);
11554  }
11555  if (isset($border_style['R']) AND $border_style['R']) {
11556  $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
11557  }
11558  if (isset($border_style['B']) AND $border_style['B']) {
11559  $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
11560  }
11561  }
11562  }
11563 
11583  public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
11584  if ($this->state != 2) {
11585  return;
11586  }
11587  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11588  $this->SetFillColorArray($fill_color);
11589  }
11591  if ($line_style) {
11592  $this->SetLineStyle($line_style);
11593  }
11594  $this->_outPoint($x0, $y0);
11595  $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11596  $this->_out($op);
11597  }
11598 
11613  public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
11614  if ($this->state != 2) {
11615  return;
11616  }
11617  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11618  $this->SetFillColorArray($fill_color);
11619  }
11621  if ($op == 'f') {
11622  $line_style = array();
11623  }
11624  if ($line_style) {
11625  $this->SetLineStyle($line_style);
11626  }
11627  $this->_outPoint($x0, $y0);
11628  foreach ($segments as $segment) {
11629  list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
11630  $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11631  }
11632  $this->_out($op);
11633  }
11634 
11653  public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11654  if ($this->state != 2) {
11655  return;
11656  }
11657  if (TCPDF_STATIC::empty_string($ry) OR ($ry == 0)) {
11658  $ry = $rx;
11659  }
11660  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11661  $this->SetFillColorArray($fill_color);
11662  }
11664  if ($op == 'f') {
11665  $line_style = array();
11666  }
11667  if ($line_style) {
11668  $this->SetLineStyle($line_style);
11669  }
11670  $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
11671  $this->_out($op);
11672  }
11673 
11694  protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
11695  if (($rx <= 0) OR ($ry < 0)) {
11696  return;
11697  }
11698  $k = $this->k;
11699  if ($nc < 2) {
11700  $nc = 2;
11701  }
11702  $xmin = 2147483647;
11703  $ymin = 2147483647;
11704  $xmax = 0;
11705  $ymax = 0;
11706  if ($pie) {
11707  // center of the arc
11708  $this->_outPoint($xc, $yc);
11709  }
11710  $xang = deg2rad((float) $xang);
11711  $angs = deg2rad((float) $angs);
11712  $angf = deg2rad((float) $angf);
11713  if ($svg) {
11714  $as = $angs;
11715  $af = $angf;
11716  } else {
11717  $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
11718  $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
11719  }
11720  if ($as < 0) {
11721  $as += (2 * M_PI);
11722  }
11723  if ($af < 0) {
11724  $af += (2 * M_PI);
11725  }
11726  if ($ccw AND ($as > $af)) {
11727  // reverse rotation
11728  $as -= (2 * M_PI);
11729  } elseif (!$ccw AND ($as < $af)) {
11730  // reverse rotation
11731  $af -= (2 * M_PI);
11732  }
11733  $total_angle = ($af - $as);
11734  if ($nc < 2) {
11735  $nc = 2;
11736  }
11737  // total arcs to draw
11738  $nc *= (2 * abs($total_angle) / M_PI);
11739  $nc = round($nc) + 1;
11740  // angle of each arc
11741  $arcang = ($total_angle / $nc);
11742  // center point in PDF coordinates
11743  $x0 = $xc;
11744  $y0 = ($this->h - $yc);
11745  // starting angle
11746  $ang = $as;
11747  $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
11748  $cos_xang = cos($xang);
11749  $sin_xang = sin($xang);
11750  $cos_ang = cos($ang);
11751  $sin_ang = sin($ang);
11752  // first arc point
11753  $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11754  $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11755  // first Bezier control point
11756  $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11757  $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11758  if ($pie) {
11759  // line from center to arc starting point
11760  $this->_outLine($px1, $this->h - $py1);
11761  } elseif ($startpoint) {
11762  // arc starting point
11763  $this->_outPoint($px1, $this->h - $py1);
11764  }
11765  // draw arcs
11766  for ($i = 1; $i <= $nc; ++$i) {
11767  // starting angle
11768  $ang = $as + ($i * $arcang);
11769  if ($i == $nc) {
11770  $ang = $af;
11771  }
11772  $cos_ang = cos($ang);
11773  $sin_ang = sin($ang);
11774  // second arc point
11775  $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11776  $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11777  // second Bezier control point
11778  $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11779  $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11780  // draw arc
11781  $cx1 = ($px1 + $qx1);
11782  $cy1 = ($this->h - ($py1 + $qy1));
11783  $cx2 = ($px2 - $qx2);
11784  $cy2 = ($this->h - ($py2 - $qy2));
11785  $cx3 = $px2;
11786  $cy3 = ($this->h - $py2);
11787  $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
11788  // get bounding box coordinates
11789  $xmin = min($xmin, $cx1, $cx2, $cx3);
11790  $ymin = min($ymin, $cy1, $cy2, $cy3);
11791  $xmax = max($xmax, $cx1, $cx2, $cx3);
11792  $ymax = max($ymax, $cy1, $cy2, $cy3);
11793  // move to next point
11794  $px1 = $px2;
11795  $py1 = $py2;
11796  $qx1 = $qx2;
11797  $qy1 = $qy2;
11798  }
11799  if ($pie) {
11800  $this->_outLine($xc, $yc);
11801  // get bounding box coordinates
11802  $xmin = min($xmin, $xc);
11803  $ymin = min($ymin, $yc);
11804  $xmax = max($xmax, $xc);
11805  $ymax = max($ymax, $yc);
11806  }
11807  return array($xmin, $ymin, $xmax, $ymax);
11808  }
11809 
11825  public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11826  $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
11827  }
11828 
11843  public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
11844  $this->Polygon($p, $style, $line_style, $fill_color, false);
11845  }
11846 
11862  public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
11863  if ($this->state != 2) {
11864  return;
11865  }
11866  $nc = count($p); // number of coordinates
11867  $np = $nc / 2; // number of points
11868  if ($closed) {
11869  // close polygon by adding the first 2 points at the end (one line)
11870  for ($i = 0; $i < 4; ++$i) {
11871  $p[$nc + $i] = $p[$i];
11872  }
11873  // copy style for the last added line
11874  if (isset($line_style[0])) {
11875  $line_style[$np] = $line_style[0];
11876  }
11877  $nc += 4;
11878  }
11879  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11880  $this->SetFillColorArray($fill_color);
11881  }
11883  if ($op == 'f') {
11884  $line_style = array();
11885  }
11886  $draw = true;
11887  if ($line_style) {
11888  if (isset($line_style['all'])) {
11889  $this->SetLineStyle($line_style['all']);
11890  } else {
11891  $draw = false;
11892  if ($op == 'B') {
11893  // draw fill
11894  $op = 'f';
11895  $this->_outPoint($p[0], $p[1]);
11896  for ($i = 2; $i < $nc; $i = $i + 2) {
11897  $this->_outLine($p[$i], $p[$i + 1]);
11898  }
11899  $this->_out($op);
11900  }
11901  // draw outline
11902  $this->_outPoint($p[0], $p[1]);
11903  for ($i = 2; $i < $nc; $i = $i + 2) {
11904  $line_num = ($i / 2) - 1;
11905  if (isset($line_style[$line_num])) {
11906  if ($line_style[$line_num] != 0) {
11907  if (is_array($line_style[$line_num])) {
11908  $this->_out('S');
11909  $this->SetLineStyle($line_style[$line_num]);
11910  $this->_outPoint($p[$i - 2], $p[$i - 1]);
11911  $this->_outLine($p[$i], $p[$i + 1]);
11912  $this->_out('S');
11913  $this->_outPoint($p[$i], $p[$i + 1]);
11914  } else {
11915  $this->_outLine($p[$i], $p[$i + 1]);
11916  }
11917  }
11918  } else {
11919  $this->_outLine($p[$i], $p[$i + 1]);
11920  }
11921  }
11922  $this->_out($op);
11923  }
11924  }
11925  if ($draw) {
11926  $this->_outPoint($p[0], $p[1]);
11927  for ($i = 2; $i < $nc; $i = $i + 2) {
11928  $this->_outLine($p[$i], $p[$i + 1]);
11929  }
11930  $this->_out($op);
11931  }
11932  }
11933 
11963  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()) {
11964  if (3 > $ns) {
11965  $ns = 3;
11966  }
11967  if ($draw_circle) {
11968  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
11969  }
11970  $p = array();
11971  for ($i = 0; $i < $ns; ++$i) {
11972  $a = $angle + ($i * 360 / $ns);
11973  $a_rad = deg2rad((float) $a);
11974  $p[] = $x0 + ($r * sin($a_rad));
11975  $p[] = $y0 + ($r * cos($a_rad));
11976  }
11977  $this->Polygon($p, $style, $line_style, $fill_color);
11978  }
11979 
12011  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()) {
12012  if ($nv < 2) {
12013  $nv = 2;
12014  }
12015  if ($draw_circle) {
12016  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12017  }
12018  $p2 = array();
12019  $visited = array();
12020  for ($i = 0; $i < $nv; ++$i) {
12021  $a = $angle + ($i * 360 / $nv);
12022  $a_rad = deg2rad((float) $a);
12023  $p2[] = $x0 + ($r * sin($a_rad));
12024  $p2[] = $y0 + ($r * cos($a_rad));
12025  $visited[] = false;
12026  }
12027  $p = array();
12028  $i = 0;
12029  do {
12030  $p[] = $p2[$i * 2];
12031  $p[] = $p2[($i * 2) + 1];
12032  $visited[$i] = true;
12033  $i += $ng;
12034  $i %= $nv;
12035  } while (!$visited[$i]);
12036  $this->Polygon($p, $style, $line_style, $fill_color);
12037  }
12038 
12053  public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12054  $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
12055  }
12056 
12072  public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12073  if ($this->state != 2) {
12074  return;
12075  }
12076  if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
12077  // Not rounded
12078  $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
12079  return;
12080  }
12081  // Rounded
12082  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12083  $this->SetFillColorArray($fill_color);
12084  }
12086  if ($op == 'f') {
12087  $border_style = array();
12088  }
12089  if ($border_style) {
12090  $this->SetLineStyle($border_style);
12091  }
12092  $MyArc = 4 / 3 * (sqrt(2) - 1);
12093  $this->_outPoint($x + $rx, $y);
12094  $xc = $x + $w - $rx;
12095  $yc = $y + $ry;
12096  $this->_outLine($xc, $y);
12097  if ($round_corner[0]) {
12098  $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
12099  } else {
12100  $this->_outLine($x + $w, $y);
12101  }
12102  $xc = $x + $w - $rx;
12103  $yc = $y + $h - $ry;
12104  $this->_outLine($x + $w, $yc);
12105  if ($round_corner[1]) {
12106  $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
12107  } else {
12108  $this->_outLine($x + $w, $y + $h);
12109  }
12110  $xc = $x + $rx;
12111  $yc = $y + $h - $ry;
12112  $this->_outLine($xc, $y + $h);
12113  if ($round_corner[2]) {
12114  $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
12115  } else {
12116  $this->_outLine($x, $y + $h);
12117  }
12118  $xc = $x + $rx;
12119  $yc = $y + $ry;
12120  $this->_outLine($x, $yc);
12121  if ($round_corner[3]) {
12122  $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
12123  } else {
12124  $this->_outLine($x, $y);
12125  $this->_outLine($x + $rx, $y);
12126  }
12127  $this->_out($op);
12128  }
12129 
12142  public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
12143  // getting arrow direction angle
12144  // 0 deg angle is when both arms go along X axis. angle grows clockwise.
12145  $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
12146  if ($dir_angle < 0) {
12147  $dir_angle += (2 * M_PI);
12148  }
12149  $arm_angle = deg2rad($arm_angle);
12150  $sx1 = $x1;
12151  $sy1 = $y1;
12152  if ($head_style > 0) {
12153  // calculate the stopping point for the arrow shaft
12154  $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
12155  $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
12156  }
12157  // main arrow line / shaft
12158  $this->Line($x0, $y0, $sx1, $sy1);
12159  // left arrowhead arm tip
12160  $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
12161  $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
12162  // right arrowhead arm tip
12163  $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
12164  $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
12165  $mode = 'D';
12166  $style = array();
12167  switch ($head_style) {
12168  case 0: {
12169  // draw only arrowhead arms
12170  $mode = 'D';
12171  $style = array(1, 1, 0);
12172  break;
12173  }
12174  case 1: {
12175  // draw closed arrowhead, but no fill
12176  $mode = 'D';
12177  break;
12178  }
12179  case 2: {
12180  // closed and filled arrowhead
12181  $mode = 'DF';
12182  break;
12183  }
12184  case 3: {
12185  // filled arrowhead
12186  $mode = 'F';
12187  break;
12188  }
12189  }
12190  $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
12191  }
12192 
12193  // END GRAPHIC FUNCTIONS SECTION -----------------------
12194 
12207  public function setDestination($name, $y=-1, $page='', $x=-1) {
12208  // remove unsupported characters
12209  $name = TCPDF_STATIC::encodeNameObject($name);
12210  if (TCPDF_STATIC::empty_string($name)) {
12211  return false;
12212  }
12213  if ($y == -1) {
12214  $y = $this->GetY();
12215  } elseif ($y < 0) {
12216  $y = 0;
12217  } elseif ($y > $this->h) {
12218  $y = $this->h;
12219  }
12220  if ($x == -1) {
12221  $x = $this->GetX();
12222  } elseif ($x < 0) {
12223  $x = 0;
12224  } elseif ($x > $this->w) {
12225  $x = $this->w;
12226  }
12227  $fixed = false;
12228  if (!empty($page) AND ($page[0] == '*')) {
12229  $page = intval(substr($page, 1));
12230  // this page number will not be changed when moving/add/deleting pages
12231  $fixed = true;
12232  }
12233  if (empty($page)) {
12234  $page = $this->PageNo();
12235  if (empty($page)) {
12236  return;
12237  }
12238  }
12239  $this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed);
12240  return $name;
12241  }
12242 
12250  public function getDestination() {
12251  return $this->dests;
12252  }
12253 
12260  protected function _putdests() {
12261  if (empty($this->dests)) {
12262  return;
12263  }
12264  $this->n_dests = $this->_newobj();
12265  $out = ' <<';
12266  foreach($this->dests as $name => $o) {
12267  $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)));
12268  }
12269  $out .= ' >>';
12270  $out .= "\n".'endobj';
12271  $this->_out($out);
12272  }
12273 
12286  public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12287  $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link);
12288  }
12289 
12303  public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12304  if ($level < 0) {
12305  $level = 0;
12306  }
12307  if (isset($this->outlines[0])) {
12308  $lastoutline = end($this->outlines);
12309  $maxlevel = $lastoutline['l'] + 1;
12310  } else {
12311  $maxlevel = 0;
12312  }
12313  if ($level > $maxlevel) {
12314  $level = $maxlevel;
12315  }
12316  if ($y == -1) {
12317  $y = $this->GetY();
12318  } elseif ($y < 0) {
12319  $y = 0;
12320  } elseif ($y > $this->h) {
12321  $y = $this->h;
12322  }
12323  if ($x == -1) {
12324  $x = $this->GetX();
12325  } elseif ($x < 0) {
12326  $x = 0;
12327  } elseif ($x > $this->w) {
12328  $x = $this->w;
12329  }
12330  $fixed = false;
12331  if (!empty($page) AND ($page[0] == '*')) {
12332  $page = intval(substr($page, 1));
12333  // this page number will not be changed when moving/add/deleting pages
12334  $fixed = true;
12335  }
12336  if (empty($page)) {
12337  $page = $this->PageNo();
12338  if (empty($page)) {
12339  return;
12340  }
12341  }
12342  $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed, 's' => strtoupper($style), 'c' => $color, 'u' => $link);
12343  }
12344 
12350  protected function sortBookmarks() {
12351  // get sorting columns
12352  $outline_p = array();
12353  $outline_y = array();
12354  foreach ($this->outlines as $key => $row) {
12355  $outline_p[$key] = $row['p'];
12356  $outline_k[$key] = $key;
12357  }
12358  // sort outlines by page and original position
12359  array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
12360  }
12361 
12368  protected function _putbookmarks() {
12369  $nb = count($this->outlines);
12370  if ($nb == 0) {
12371  return;
12372  }
12373  // sort bookmarks
12374  $this->sortBookmarks();
12375  $lru = array();
12376  $level = 0;
12377  foreach ($this->outlines as $i => $o) {
12378  if ($o['l'] > 0) {
12379  $parent = $lru[($o['l'] - 1)];
12380  //Set parent and last pointers
12381  $this->outlines[$i]['parent'] = $parent;
12382  $this->outlines[$parent]['last'] = $i;
12383  if ($o['l'] > $level) {
12384  //Level increasing: set first pointer
12385  $this->outlines[$parent]['first'] = $i;
12386  }
12387  } else {
12388  $this->outlines[$i]['parent'] = $nb;
12389  }
12390  if (($o['l'] <= $level) AND ($i > 0)) {
12391  //Set prev and next pointers
12392  $prev = $lru[$o['l']];
12393  $this->outlines[$prev]['next'] = $i;
12394  $this->outlines[$i]['prev'] = $prev;
12395  }
12396  $lru[$o['l']] = $i;
12397  $level = $o['l'];
12398  }
12399  //Outline items
12400  $n = $this->n + 1;
12401  $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';
12402  foreach ($this->outlines as $i => $o) {
12403  $oid = $this->_newobj();
12404  // covert HTML title to string
12405  $title = preg_replace($nltags, "\n", $o['t']);
12406  $title = preg_replace("/[\r]+/si", '', $title);
12407  $title = preg_replace("/[\n]+/si", "\n", $title);
12408  $title = strip_tags($title);
12409  $title = $this->stringTrim($title);
12410  $out = '<</Title '.$this->_textstring($title, $oid);
12411  $out .= ' /Parent '.($n + $o['parent']).' 0 R';
12412  if (isset($o['prev'])) {
12413  $out .= ' /Prev '.($n + $o['prev']).' 0 R';
12414  }
12415  if (isset($o['next'])) {
12416  $out .= ' /Next '.($n + $o['next']).' 0 R';
12417  }
12418  if (isset($o['first'])) {
12419  $out .= ' /First '.($n + $o['first']).' 0 R';
12420  }
12421  if (isset($o['last'])) {
12422  $out .= ' /Last '.($n + $o['last']).' 0 R';
12423  }
12424  if (isset($o['u']) AND !empty($o['u'])) {
12425  // link
12426  if (is_string($o['u'])) {
12427  if ($o['u'][0] == '#') {
12428  // internal destination
12429  $out .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($o['u'], 1));
12430  } elseif ($o['u'][0] == '%') {
12431  // embedded PDF file
12432  $filename = basename(substr($o['u'], 1));
12433  $out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
12434  } elseif ($o['u'][0] == '*') {
12435  // embedded generic file
12436  $filename = basename(substr($o['u'], 1));
12437  $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});';
12438  $out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>';
12439  } else {
12440  // external URI link
12441  $out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>';
12442  }
12443  } elseif (isset($this->links[$o['u']])) {
12444  // internal link ID
12445  $l = $this->links[$o['u']];
12446  if (isset($this->page_obj_id[($l['p'])])) {
12447  $out .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l['p'])], ($this->pagedim[$l['p']]['h'] - ($l['y'] * $this->k)));
12448  }
12449  }
12450  } elseif (isset($this->page_obj_id[($o['p'])])) {
12451  // link to a page
12452  $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)));
12453  }
12454  // set font style
12455  $style = 0;
12456  if (!empty($o['s'])) {
12457  // bold
12458  if (strpos($o['s'], 'B') !== false) {
12459  $style |= 2;
12460  }
12461  // oblique
12462  if (strpos($o['s'], 'I') !== false) {
12463  $style |= 1;
12464  }
12465  }
12466  $out .= sprintf(' /F %d', $style);
12467  // set bookmark color
12468  if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
12469  $color = array_values($o['c']);
12470  $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
12471  } else {
12472  // black
12473  $out .= ' /C [0.0 0.0 0.0]';
12474  }
12475  $out .= ' /Count 0'; // normally closed item
12476  $out .= ' >>';
12477  $out .= "\n".'endobj';
12478  $this->_out($out);
12479  }
12480  //Outline root
12481  $this->OutlineRoot = $this->_newobj();
12482  $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj');
12483  }
12484 
12485  // --- JAVASCRIPT ------------------------------------------------------
12486 
12494  public function IncludeJS($script) {
12495  $this->javascript .= $script;
12496  }
12497 
12507  public function addJavascriptObject($script, $onload=false) {
12508  if ($this->pdfa_mode) {
12509  // javascript is not allowed in PDF/A mode
12510  return false;
12511  }
12512  ++$this->n;
12513  $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload);
12514  return $this->n;
12515  }
12516 
12523  protected function _putjavascript() {
12524  if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) {
12525  return;
12526  }
12527  if (strpos($this->javascript, 'this.addField') > 0) {
12528  if (!$this->ur['enabled']) {
12529  //$this->setUserRights();
12530  }
12531  // the following two lines are used to avoid form fields duplication after saving
12532  // The addField method only works when releasing user rights (UR3)
12533  $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
12534  $jsb = "getField('tcpdfdocsaved').value='saved';";
12535  $this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
12536  }
12537  // name tree for javascript
12538  $this->n_js = '<< /Names [';
12539  if (!empty($this->javascript)) {
12540  $this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
12541  }
12542  if (!empty($this->js_objects)) {
12543  foreach ($this->js_objects as $key => $val) {
12544  if ($val['onload']) {
12545  $this->n_js .= ' (JS'.$key.') '.$key.' 0 R';
12546  }
12547  }
12548  }
12549  $this->n_js .= ' ] >>';
12550  // default Javascript object
12551  if (!empty($this->javascript)) {
12552  $obj_id = $this->_newobj();
12553  $out = '<< /S /JavaScript';
12554  $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id);
12555  $out .= ' >>';
12556  $out .= "\n".'endobj';
12557  $this->_out($out);
12558  }
12559  // additional Javascript objects
12560  if (!empty($this->js_objects)) {
12561  foreach ($this->js_objects as $key => $val) {
12562  $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
12563  $this->_out($out);
12564  }
12565  }
12566  }
12567 
12581  protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
12582  if ($this->rtl) {
12583  $x = $x - $w;
12584  }
12585  // the followind avoid fields duplication after saving the document
12586  $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {";
12587  $k = $this->k;
12588  $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";
12589  $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
12590  while (list($key, $val) = each($prop)) {
12591  if (strcmp(substr($key, -5), 'Color') == 0) {
12592  $val = TCPDF_COLORS::_JScolor($val);
12593  } else {
12594  $val = "'".$val."'";
12595  }
12596  $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
12597  }
12598  if ($this->rtl) {
12599  $this->x -= $w;
12600  } else {
12601  $this->x += $w;
12602  }
12603  $this->javascript .= '}';
12604  }
12605 
12606  // --- FORM FIELDS -----------------------------------------------------
12607 
12608 
12609 
12617  public function setFormDefaultProp($prop=array()) {
12618  $this->default_form_prop = $prop;
12619  }
12620 
12628  public function getFormDefaultProp() {
12629  return $this->default_form_prop;
12630  }
12631 
12646  public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12647  if ($x === '') {
12648  $x = $this->x;
12649  }
12650  if ($y === '') {
12651  $y = $this->y;
12652  }
12653  // check page for no-write regions and adapt page margins if necessary
12654  list($x, $y) = $this->checkPageRegions($h, $x, $y);
12655  if ($js) {
12656  $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
12657  return;
12658  }
12659  // get default style
12660  $prop = array_merge($this->getFormDefaultProp(), $prop);
12661  // get annotation data
12662  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12663  // set default appearance stream
12664  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12665  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12666  $popt['da'] = $fontstyle;
12667  // build appearance stream
12668  $popt['ap'] = array();
12669  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12670  $text = '';
12671  if (isset($prop['value']) AND !empty($prop['value'])) {
12672  $text = $prop['value'];
12673  } elseif (isset($opt['v']) AND !empty($opt['v'])) {
12674  $text = $opt['v'];
12675  }
12676  $tmpid = $this->startTemplate($w, $h, false);
12677  $align = '';
12678  if (isset($popt['q'])) {
12679  switch ($popt['q']) {
12680  case 0: {
12681  $align = 'L';
12682  break;
12683  }
12684  case 1: {
12685  $align = 'C';
12686  break;
12687  }
12688  case 2: {
12689  $align = 'R';
12690  break;
12691  }
12692  default: {
12693  $align = '';
12694  break;
12695  }
12696  }
12697  }
12698  $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12699  $this->endTemplate();
12700  --$this->n;
12701  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12702  unset($this->xobjects[$tmpid]);
12703  $popt['ap']['n'] .= 'Q EMC';
12704  // merge options
12705  $opt = array_merge($popt, $opt);
12706  // remove some conflicting options
12707  unset($opt['bs']);
12708  // set remaining annotation data
12709  $opt['Subtype'] = 'Widget';
12710  $opt['ft'] = 'Tx';
12711  $opt['t'] = $name;
12712  // Additional annotation's parameters (check _putannotsobj() method):
12713  //$opt['f']
12714  //$opt['as']
12715  //$opt['bs']
12716  //$opt['be']
12717  //$opt['c']
12718  //$opt['border']
12719  //$opt['h']
12720  //$opt['mk'];
12721  //$opt['mk']['r']
12722  //$opt['mk']['bc'];
12723  //$opt['mk']['bg'];
12724  unset($opt['mk']['ca']);
12725  unset($opt['mk']['rc']);
12726  unset($opt['mk']['ac']);
12727  unset($opt['mk']['i']);
12728  unset($opt['mk']['ri']);
12729  unset($opt['mk']['ix']);
12730  unset($opt['mk']['if']);
12731  //$opt['mk']['if']['sw'];
12732  //$opt['mk']['if']['s'];
12733  //$opt['mk']['if']['a'];
12734  //$opt['mk']['if']['fb'];
12735  unset($opt['mk']['tp']);
12736  //$opt['tu']
12737  //$opt['tm']
12738  //$opt['ff']
12739  //$opt['v']
12740  //$opt['dv']
12741  //$opt['a']
12742  //$opt['aa']
12743  //$opt['q']
12744  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12745  if ($this->rtl) {
12746  $this->x -= $w;
12747  } else {
12748  $this->x += $w;
12749  }
12750  }
12751 
12767  public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
12768  if ($x === '') {
12769  $x = $this->x;
12770  }
12771  if ($y === '') {
12772  $y = $this->y;
12773  }
12774  // check page for no-write regions and adapt page margins if necessary
12775  list($x, $y) = $this->checkPageRegions($w, $x, $y);
12776  if ($js) {
12777  $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
12778  return;
12779  }
12780  if (TCPDF_STATIC::empty_string($onvalue)) {
12781  $onvalue = 'On';
12782  }
12783  if ($checked) {
12784  $defval = $onvalue;
12785  } else {
12786  $defval = 'Off';
12787  }
12788  // set font
12789  $font = 'zapfdingbats';
12790  if ($this->pdfa_mode) {
12791  // all fonts must be embedded
12792  $font = 'pdfa'.$font;
12793  }
12794  $this->AddFont($font);
12795  $tmpfont = $this->getFontBuffer($font);
12796  // set data for parent group
12797  if (!isset($this->radiobutton_groups[$this->page])) {
12798  $this->radiobutton_groups[$this->page] = array();
12799  }
12800  if (!isset($this->radiobutton_groups[$this->page][$name])) {
12801  $this->radiobutton_groups[$this->page][$name] = array();
12802  ++$this->n;
12803  $this->radiobutton_groups[$this->page][$name]['n'] = $this->n;
12804  $this->radio_groups[] = $this->n;
12805  }
12806  $kid = ($this->n + 1);
12807  // save object ID to be added on Kids entry on parent object
12808  $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval);
12809  // get default style
12810  $prop = array_merge($this->getFormDefaultProp(), $prop);
12811  $prop['NoToggleToOff'] = 'true';
12812  $prop['Radio'] = 'true';
12813  $prop['borderStyle'] = 'inset';
12814  // get annotation data
12815  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12816  // set additional default options
12817  $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
12818  $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
12819  $popt['da'] = $fontstyle;
12820  // build appearance stream
12821  $popt['ap'] = array();
12822  $popt['ap']['n'] = array();
12823  $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k);
12824  $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
12825  $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);
12826  $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);
12827  if (!isset($popt['mk'])) {
12828  $popt['mk'] = array();
12829  }
12830  $popt['mk']['ca'] = '(l)';
12831  // merge options
12832  $opt = array_merge($popt, $opt);
12833  // set remaining annotation data
12834  $opt['Subtype'] = 'Widget';
12835  $opt['ft'] = 'Btn';
12836  if ($checked) {
12837  $opt['v'] = array('/'.$onvalue);
12838  $opt['as'] = $onvalue;
12839  } else {
12840  $opt['as'] = 'Off';
12841  }
12842  // store readonly flag
12843  if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) {
12844  $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false;
12845  }
12846  $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64);
12847  $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
12848  if ($this->rtl) {
12849  $this->x -= $w;
12850  } else {
12851  $this->x += $w;
12852  }
12853  }
12854 
12870  public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12871  if ($x === '') {
12872  $x = $this->x;
12873  }
12874  if ($y === '') {
12875  $y = $this->y;
12876  }
12877  // check page for no-write regions and adapt page margins if necessary
12878  list($x, $y) = $this->checkPageRegions($h, $x, $y);
12879  if ($js) {
12880  $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
12881  $s = '';
12882  foreach ($values as $value) {
12883  if (is_array($value)) {
12884  $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12885  } else {
12886  $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12887  }
12888  }
12889  $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12890  return;
12891  }
12892  // get default style
12893  $prop = array_merge($this->getFormDefaultProp(), $prop);
12894  // get annotation data
12895  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12896  // set additional default values
12897  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12898  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12899  $popt['da'] = $fontstyle;
12900  // build appearance stream
12901  $popt['ap'] = array();
12902  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12903  $text = '';
12904  foreach($values as $item) {
12905  if (is_array($item)) {
12906  $text .= $item[1]."\n";
12907  } else {
12908  $text .= $item."\n";
12909  }
12910  }
12911  $tmpid = $this->startTemplate($w, $h, false);
12912  $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12913  $this->endTemplate();
12914  --$this->n;
12915  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12916  unset($this->xobjects[$tmpid]);
12917  $popt['ap']['n'] .= 'Q EMC';
12918  // merge options
12919  $opt = array_merge($popt, $opt);
12920  // set remaining annotation data
12921  $opt['Subtype'] = 'Widget';
12922  $opt['ft'] = 'Ch';
12923  $opt['t'] = $name;
12924  $opt['opt'] = $values;
12925  unset($opt['mk']['ca']);
12926  unset($opt['mk']['rc']);
12927  unset($opt['mk']['ac']);
12928  unset($opt['mk']['i']);
12929  unset($opt['mk']['ri']);
12930  unset($opt['mk']['ix']);
12931  unset($opt['mk']['if']);
12932  unset($opt['mk']['tp']);
12933  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12934  if ($this->rtl) {
12935  $this->x -= $w;
12936  } else {
12937  $this->x += $w;
12938  }
12939  }
12940 
12956  public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12957  if ($x === '') {
12958  $x = $this->x;
12959  }
12960  if ($y === '') {
12961  $y = $this->y;
12962  }
12963  // check page for no-write regions and adapt page margins if necessary
12964  list($x, $y) = $this->checkPageRegions($h, $x, $y);
12965  if ($js) {
12966  $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
12967  $s = '';
12968  foreach ($values as $value) {
12969  if (is_array($value)) {
12970  $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12971  } else {
12972  $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12973  }
12974  }
12975  $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12976  return;
12977  }
12978  // get default style
12979  $prop = array_merge($this->getFormDefaultProp(), $prop);
12980  $prop['Combo'] = true;
12981  // get annotation data
12982  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12983  // set additional default options
12984  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12985  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12986  $popt['da'] = $fontstyle;
12987  // build appearance stream
12988  $popt['ap'] = array();
12989  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12990  $text = '';
12991  foreach($values as $item) {
12992  if (is_array($item)) {
12993  $text .= $item[1]."\n";
12994  } else {
12995  $text .= $item."\n";
12996  }
12997  }
12998  $tmpid = $this->startTemplate($w, $h, false);
12999  $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13000  $this->endTemplate();
13001  --$this->n;
13002  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13003  unset($this->xobjects[$tmpid]);
13004  $popt['ap']['n'] .= 'Q EMC';
13005  // merge options
13006  $opt = array_merge($popt, $opt);
13007  // set remaining annotation data
13008  $opt['Subtype'] = 'Widget';
13009  $opt['ft'] = 'Ch';
13010  $opt['t'] = $name;
13011  $opt['opt'] = $values;
13012  unset($opt['mk']['ca']);
13013  unset($opt['mk']['rc']);
13014  unset($opt['mk']['ac']);
13015  unset($opt['mk']['i']);
13016  unset($opt['mk']['ri']);
13017  unset($opt['mk']['ix']);
13018  unset($opt['mk']['if']);
13019  unset($opt['mk']['tp']);
13020  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13021  if ($this->rtl) {
13022  $this->x -= $w;
13023  } else {
13024  $this->x += $w;
13025  }
13026  }
13027 
13043  public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
13044  if ($x === '') {
13045  $x = $this->x;
13046  }
13047  if ($y === '') {
13048  $y = $this->y;
13049  }
13050  // check page for no-write regions and adapt page margins if necessary
13051  list($x, $y) = $this->checkPageRegions($w, $x, $y);
13052  if ($js) {
13053  $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
13054  return;
13055  }
13056  if (!isset($prop['value'])) {
13057  $prop['value'] = array('Yes');
13058  }
13059  // get default style
13060  $prop = array_merge($this->getFormDefaultProp(), $prop);
13061  $prop['borderStyle'] = 'inset';
13062  // get annotation data
13063  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13064  // set additional default options
13065  $font = 'zapfdingbats';
13066  if ($this->pdfa_mode) {
13067  // all fonts must be embedded
13068  $font = 'pdfa'.$font;
13069  }
13070  $this->AddFont($font);
13071  $tmpfont = $this->getFontBuffer($font);
13072  $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
13073  $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
13074  $popt['da'] = $fontstyle;
13075  // build appearance stream
13076  $popt['ap'] = array();
13077  $popt['ap']['n'] = array();
13078  $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k);
13079  $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
13080  $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);
13081  $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);
13082  // merge options
13083  $opt = array_merge($popt, $opt);
13084  // set remaining annotation data
13085  $opt['Subtype'] = 'Widget';
13086  $opt['ft'] = 'Btn';
13087  $opt['t'] = $name;
13088  if (TCPDF_STATIC::empty_string($onvalue)) {
13089  $onvalue = 'Yes';
13090  }
13091  $opt['opt'] = array($onvalue);
13092  if ($checked) {
13093  $opt['v'] = array('/Yes');
13094  $opt['as'] = 'Yes';
13095  } else {
13096  $opt['v'] = array('/Off');
13097  $opt['as'] = 'Off';
13098  }
13099  $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
13100  if ($this->rtl) {
13101  $this->x -= $w;
13102  } else {
13103  $this->x += $w;
13104  }
13105  }
13106 
13123  public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13124  if ($x === '') {
13125  $x = $this->x;
13126  }
13127  if ($y === '') {
13128  $y = $this->y;
13129  }
13130  // check page for no-write regions and adapt page margins if necessary
13131  list($x, $y) = $this->checkPageRegions($h, $x, $y);
13132  if ($js) {
13133  $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
13134  $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
13135  $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
13136  $this->javascript .= 'f'.$name.".highlight='push';\n";
13137  $this->javascript .= 'f'.$name.".print=false;\n";
13138  return;
13139  }
13140  // get default style
13141  $prop = array_merge($this->getFormDefaultProp(), $prop);
13142  $prop['Pushbutton'] = 'true';
13143  $prop['highlight'] = 'push';
13144  $prop['display'] = 'display.noPrint';
13145  // get annotation data
13146  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13147  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13148  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13149  $popt['da'] = $fontstyle;
13150  // build appearance stream
13151  $popt['ap'] = array();
13152  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13153  $tmpid = $this->startTemplate($w, $h, false);
13154  $bw = (2 / $this->k); // border width
13155  $border = array(
13156  'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13157  'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
13158  'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13159  'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
13160  $this->SetFillColor(204);
13161  $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
13162  $this->endTemplate();
13163  --$this->n;
13164  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13165  unset($this->xobjects[$tmpid]);
13166  $popt['ap']['n'] .= 'Q EMC';
13167  // set additional default options
13168  if (!isset($popt['mk'])) {
13169  $popt['mk'] = array();
13170  }
13171  $ann_obj_id = ($this->n + 1);
13172  if (!empty($action) AND !is_array($action)) {
13173  $ann_obj_id = ($this->n + 2);
13174  }
13175  $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
13176  $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
13177  $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
13178  // merge options
13179  $opt = array_merge($popt, $opt);
13180  // set remaining annotation data
13181  $opt['Subtype'] = 'Widget';
13182  $opt['ft'] = 'Btn';
13183  $opt['t'] = $caption;
13184  $opt['v'] = $name;
13185  if (!empty($action)) {
13186  if (is_array($action)) {
13187  // form action options as on section 12.7.5 of PDF32000_2008.
13188  $opt['aa'] = '/D <<';
13189  $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
13190  foreach ($action AS $key => $val) {
13191  if (($key == 'S') AND in_array($val, $bmode)) {
13192  $opt['aa'] .= ' /S /'.$val;
13193  } elseif (($key == 'F') AND (!empty($val))) {
13194  $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
13195  } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
13196  $opt['aa'] .= ' /Fields [';
13197  foreach ($val AS $field) {
13198  $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
13199  }
13200  $opt['aa'] .= ']';
13201  } elseif (($key == 'Flags')) {
13202  $ff = 0;
13203  if (is_array($val)) {
13204  foreach ($val AS $flag) {
13205  switch ($flag) {
13206  case 'Include/Exclude': {
13207  $ff += 1 << 0;
13208  break;
13209  }
13210  case 'IncludeNoValueFields': {
13211  $ff += 1 << 1;
13212  break;
13213  }
13214  case 'ExportFormat': {
13215  $ff += 1 << 2;
13216  break;
13217  }
13218  case 'GetMethod': {
13219  $ff += 1 << 3;
13220  break;
13221  }
13222  case 'SubmitCoordinates': {
13223  $ff += 1 << 4;
13224  break;
13225  }
13226  case 'XFDF': {
13227  $ff += 1 << 5;
13228  break;
13229  }
13230  case 'IncludeAppendSaves': {
13231  $ff += 1 << 6;
13232  break;
13233  }
13234  case 'IncludeAnnotations': {
13235  $ff += 1 << 7;
13236  break;
13237  }
13238  case 'SubmitPDF': {
13239  $ff += 1 << 8;
13240  break;
13241  }
13242  case 'CanonicalFormat': {
13243  $ff += 1 << 9;
13244  break;
13245  }
13246  case 'ExclNonUserAnnots': {
13247  $ff += 1 << 10;
13248  break;
13249  }
13250  case 'ExclFKey': {
13251  $ff += 1 << 11;
13252  break;
13253  }
13254  case 'EmbedForm': {
13255  $ff += 1 << 13;
13256  break;
13257  }
13258  }
13259  }
13260  } else {
13261  $ff = intval($val);
13262  }
13263  $opt['aa'] .= ' /Flags '.$ff;
13264  }
13265  }
13266  $opt['aa'] .= ' >>';
13267  } else {
13268  // Javascript action or raw action command
13269  $js_obj_id = $this->addJavascriptObject($action);
13270  $opt['aa'] = '/D '.$js_obj_id.' 0 R';
13271  }
13272  }
13273  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13274  if ($this->rtl) {
13275  $this->x -= $w;
13276  } else {
13277  $this->x += $w;
13278  }
13279  }
13280 
13281  // --- END FORMS FIELDS ------------------------------------------------
13282 
13290  protected function _putsignature() {
13291  if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
13292  return;
13293  }
13294  $sigobjid = ($this->sig_obj_id + 1);
13295  $out = $this->_getobj($sigobjid)."\n";
13296  $out .= '<< /Type /Sig';
13297  $out .= ' /Filter /Adobe.PPKLite';
13298  $out .= ' /SubFilter /adbe.pkcs7.detached';
13299  $out .= ' '.TCPDF_STATIC::$byterange_string;
13300  $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>';
13301  if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
13302  $out .= ' /Reference ['; // array of signature reference dictionaries
13303  $out .= ' << /Type /SigRef';
13304  if ($this->signature_data['cert_type'] > 0) {
13305  $out .= ' /TransformMethod /DocMDP';
13306  $out .= ' /TransformParams <<';
13307  $out .= ' /Type /TransformParams';
13308  $out .= ' /P '.$this->signature_data['cert_type'];
13309  $out .= ' /V /1.2';
13310  } else {
13311  $out .= ' /TransformMethod /UR3';
13312  $out .= ' /TransformParams <<';
13313  $out .= ' /Type /TransformParams';
13314  $out .= ' /V /2.2';
13315  if (!TCPDF_STATIC::empty_string($this->ur['document'])) {
13316  $out .= ' /Document['.$this->ur['document'].']';
13317  }
13318  if (!TCPDF_STATIC::empty_string($this->ur['form'])) {
13319  $out .= ' /Form['.$this->ur['form'].']';
13320  }
13321  if (!TCPDF_STATIC::empty_string($this->ur['signature'])) {
13322  $out .= ' /Signature['.$this->ur['signature'].']';
13323  }
13324  if (!TCPDF_STATIC::empty_string($this->ur['annots'])) {
13325  $out .= ' /Annots['.$this->ur['annots'].']';
13326  }
13327  if (!TCPDF_STATIC::empty_string($this->ur['ef'])) {
13328  $out .= ' /EF['.$this->ur['ef'].']';
13329  }
13330  if (!TCPDF_STATIC::empty_string($this->ur['formex'])) {
13331  $out .= ' /FormEX['.$this->ur['formex'].']';
13332  }
13333  }
13334  $out .= ' >>'; // close TransformParams
13335  // optional digest data (values must be calculated and replaced later)
13336  //$out .= ' /Data ********** 0 R';
13337  //$out .= ' /DigestMethod/MD5';
13338  //$out .= ' /DigestLocation[********** 34]';
13339  //$out .= ' /DigestValue<********************************>';
13340  $out .= ' >>';
13341  $out .= ' ]'; // end of reference
13342  }
13343  if (isset($this->signature_data['info']['Name']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Name'])) {
13344  $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid);
13345  }
13346  if (isset($this->signature_data['info']['Location']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Location'])) {
13347  $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid);
13348  }
13349  if (isset($this->signature_data['info']['Reason']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Reason'])) {
13350  $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid);
13351  }
13352  if (isset($this->signature_data['info']['ContactInfo']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['ContactInfo'])) {
13353  $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid);
13354  }
13355  $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp);
13356  $out .= ' >>';
13357  $out .= "\n".'endobj';
13358  $this->_out($out);
13359  }
13360 
13378  public function setUserRights(
13379  $enable=true,
13380  $document='/FullSave',
13381  $annots='/Create/Delete/Modify/Copy/Import/Export',
13382  $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
13383  $signature='/Modify',
13384  $ef='/Create/Delete/Modify/Import',
13385  $formex='') {
13386  $this->ur['enabled'] = $enable;
13387  $this->ur['document'] = $document;
13388  $this->ur['annots'] = $annots;
13389  $this->ur['form'] = $form;
13390  $this->ur['signature'] = $signature;
13391  $this->ur['ef'] = $ef;
13392  $this->ur['formex'] = $formex;
13393  if (!$this->sign) {
13394  $this->setSignature('', '', '', '', 0, array());
13395  }
13396  }
13397 
13415  public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='') {
13416  // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13417  // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13418  // to convert pfx certificate to pem: openssl
13419  // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
13420  $this->sign = true;
13421  ++$this->n;
13422  $this->sig_obj_id = $this->n; // signature widget
13423  ++$this->n; // signature object ($this->sig_obj_id + 1)
13424  $this->signature_data = array();
13425  if (strlen($signing_cert) == 0) {
13426  $this->Error('Please provide a certificate file and password!');
13427  }
13428  if (strlen($private_key) == 0) {
13429  $private_key = $signing_cert;
13430  }
13431  $this->signature_data['signcert'] = $signing_cert;
13432  $this->signature_data['privkey'] = $private_key;
13433  $this->signature_data['password'] = $private_key_password;
13434  $this->signature_data['extracerts'] = $extracerts;
13435  $this->signature_data['cert_type'] = $cert_type;
13436  $this->signature_data['info'] = $info;
13437  $this->signature_data['approval'] = $approval;
13438  }
13439 
13452  public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13453  $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13454  }
13455 
13468  public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13469  ++$this->n;
13470  $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13471  }
13472 
13486  protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13487  $sigapp = array();
13488  if (($page < 1) OR ($page > $this->numpages)) {
13489  $sigapp['page'] = $this->page;
13490  } else {
13491  $sigapp['page'] = intval($page);
13492  }
13493  if (empty($name)) {
13494  $sigapp['name'] = 'Signature';
13495  } else {
13496  $sigapp['name'] = $name;
13497  }
13498  $a = $x * $this->k;
13499  $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k);
13500  $c = $w * $this->k;
13501  $d = $h * $this->k;
13502  $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d));
13503  return $sigapp;
13504  }
13505 
13518  public function setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='') {
13519  $this->tsa_data = array();
13520  if (!function_exists('curl_init')) {
13521  $this->Error('Please enable cURL PHP extension!');
13522  }
13523  if (strlen($tsa_host) == 0) {
13524  $this->Error('Please specify the host of Time Stamping Authority (TSA)!');
13525  }
13526  $this->tsa_data['tsa_host'] = $tsa_host;
13527  if (is_file($tsa_username)) {
13528  $this->tsa_data['tsa_auth'] = $tsa_username;
13529  } else {
13530  $this->tsa_data['tsa_username'] = $tsa_username;
13531  }
13532  $this->tsa_data['tsa_password'] = $tsa_password;
13533  $this->tsa_data['tsa_cert'] = $tsa_cert;
13534  $this->tsa_timestamp = true;
13535  }
13536 
13546  protected function applyTSA($signature) {
13547  if (!$this->tsa_timestamp) {
13548  return $signature;
13549  }
13550  //@TODO: implement this feature
13551  return $signature;
13552  }
13553 
13561  public function startPageGroup($page='') {
13562  if (empty($page)) {
13563  $page = $this->page + 1;
13564  }
13565  $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1;
13566  }
13567 
13574  public function setStartingPageNumber($num=1) {
13575  $this->starting_page_number = max(0, intval($num));
13576  }
13577 
13585  public function getAliasRightShift() {
13586  // calculate aproximatively the ratio between widths of aliases and replacements.
13587  $ref = '{'.TCPDF_STATIC::$alias_right_shift.'}{'.TCPDF_STATIC::$alias_tot_pages.'}{'.TCPDF_STATIC::$alias_num_page.'}';
13588  $rep = str_repeat(' ', $this->GetNumChars($ref));
13589  $wrep = $this->GetStringWidth($rep);
13590  if ($wrep > 0) {
13591  $wdiff = max(1, ($this->GetStringWidth($ref) / $wrep));
13592  } else {
13593  $wdiff = 1;
13594  }
13595  $sdiff = sprintf('%F', $wdiff);
13596  $alias = TCPDF_STATIC::$alias_right_shift.$sdiff.'}';
13597  if ($this->isUnicodeFont()) {
13598  $alias = '{'.$alias;
13599  }
13600  return $alias;
13601  }
13602 
13611  public function getAliasNbPages() {
13612  if ($this->isUnicodeFont()) {
13613  return '{'.TCPDF_STATIC::$alias_tot_pages.'}';
13614  }
13616  }
13617 
13626  public function getAliasNumPage() {
13627  if ($this->isUnicodeFont()) {
13628  return '{'.TCPDF_STATIC::$alias_num_page.'}';
13629  }
13631  }
13632 
13641  public function getPageGroupAlias() {
13642  if ($this->isUnicodeFont()) {
13643  return '{'.TCPDF_STATIC::$alias_group_tot_pages.'}';
13644  }
13646  }
13647 
13656  public function getPageNumGroupAlias() {
13657  if ($this->isUnicodeFont()) {
13658  return '{'.TCPDF_STATIC::$alias_group_num_page.'}';
13659  }
13661  }
13662 
13669  public function getGroupPageNo() {
13670  return $this->pagegroups[$this->currpagegroup];
13671  }
13672 
13679  public function getGroupPageNoFormatted() {
13681  }
13682 
13689  public function PageNoFormatted() {
13690  return TCPDF_STATIC::formatPageNumber($this->PageNo());
13691  }
13692 
13698  protected function _putocg() {
13699  if (empty($this->pdflayers)) {
13700  return;
13701  }
13702  foreach ($this->pdflayers as $key => $layer) {
13703  $this->pdflayers[$key]['objid'] = $this->_newobj();
13704  $out = '<< /Type /OCG';
13705  $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']);
13706  $out .= ' /Usage <<';
13707  if (isset($layer['print']) AND ($layer['print'] !== NULL)) {
13708  $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>';
13709  }
13710  $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>';
13711  $out .= ' >> >>';
13712  $out .= "\n".'endobj';
13713  $this->_out($out);
13714  }
13715  }
13716 
13726  public function startLayer($name='', $print=true, $view=true, $lock=true) {
13727  if ($this->state != 2) {
13728  return;
13729  }
13730  $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1));
13731  if (empty($name)) {
13732  $name = $layer;
13733  } else {
13734  $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
13735  }
13736  $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view, 'lock' => $lock);
13737  $this->openMarkedContent = true;
13738  $this->_out('/OC /'.$layer.' BDC');
13739  }
13740 
13746  public function endLayer() {
13747  if ($this->state != 2) {
13748  return;
13749  }
13750  if ($this->openMarkedContent) {
13751  // close existing open marked-content layer
13752  $this->_out('EMC');
13753  $this->openMarkedContent = false;
13754  }
13755  }
13756 
13765  public function setVisibility($v) {
13766  if ($this->state != 2) {
13767  return;
13768  }
13769  $this->endLayer();
13770  switch($v) {
13771  case 'print': {
13772  $this->startLayer('Print', true, false);
13773  break;
13774  }
13775  case 'view':
13776  case 'screen': {
13777  $this->startLayer('View', false, true);
13778  break;
13779  }
13780  case 'all': {
13781  $this->_out('');
13782  break;
13783  }
13784  default: {
13785  $this->Error('Incorrect visibility: '.$v);
13786  break;
13787  }
13788  }
13789  }
13790 
13798  protected function addExtGState($parms) {
13799  if ($this->pdfa_mode) {
13800  // transparencies are not allowed in PDF/A mode
13801  return;
13802  }
13803  // check if this ExtGState already exist
13804  foreach ($this->extgstates as $i => $ext) {
13805  if ($ext['parms'] == $parms) {
13806  if ($this->inxobj) {
13807  // we are inside an XObject template
13808  $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext;
13809  }
13810  // return reference to existing ExtGState
13811  return $i;
13812  }
13813  }
13814  $n = (count($this->extgstates) + 1);
13815  $this->extgstates[$n] = array('parms' => $parms);
13816  if ($this->inxobj) {
13817  // we are inside an XObject template
13818  $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n];
13819  }
13820  return $n;
13821  }
13822 
13829  protected function setExtGState($gs) {
13830  if ($this->pdfa_mode OR ($this->state != 2)) {
13831  // transparency is not allowed in PDF/A mode
13832  return;
13833  }
13834  $this->_out(sprintf('/GS%d gs', $gs));
13835  }
13836 
13842  protected function _putextgstates() {
13843  foreach ($this->extgstates as $i => $ext) {
13844  $this->extgstates[$i]['n'] = $this->_newobj();
13845  $out = '<< /Type /ExtGState';
13846  foreach ($ext['parms'] as $k => $v) {
13847  if (is_float($v)) {
13848  $v = sprintf('%F', $v);
13849  } elseif ($v === true) {
13850  $v = 'true';
13851  } elseif ($v === false) {
13852  $v = 'false';
13853  }
13854  $out .= ' /'.$k.' '.$v;
13855  }
13856  $out .= ' >>';
13857  $out .= "\n".'endobj';
13858  $this->_out($out);
13859  }
13860  }
13861 
13871  public function setOverprint($stroking=true, $nonstroking='', $mode=0) {
13872  if ($this->state != 2) {
13873  return;
13874  }
13875  $stroking = $stroking ? true : false;
13876  if (TCPDF_STATIC::empty_string($nonstroking)) {
13877  // default value if not set
13878  $nonstroking = $stroking;
13879  } else {
13880  $nonstroking = $nonstroking ? true : false;
13881  }
13882  if (($mode != 0) AND ($mode != 1)) {
13883  $mode = 0;
13884  }
13885  $this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode);
13886  $gs = $this->addExtGState($this->overprint);
13887  $this->setExtGState($gs);
13888  }
13889 
13897  public function getOverprint() {
13898  return $this->overprint;
13899  }
13900 
13910  public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) {
13911  if ($this->pdfa_mode) {
13912  // transparency is not allowed in PDF/A mode
13913  return;
13914  }
13915  $stroking = floatval($stroking);
13916  if (TCPDF_STATIC::empty_string($nonstroking)) {
13917  // default value if not set
13918  $nonstroking = $stroking;
13919  } else {
13920  $nonstroking = floatval($nonstroking);
13921  }
13922  if ($bm[0] == '/') {
13923  // remove trailing slash
13924  $bm = substr($bm, 1);
13925  }
13926  if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
13927  $bm = 'Normal';
13928  }
13929  $ais = $ais ? true : false;
13930  $this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais);
13931  $gs = $this->addExtGState($this->alpha);
13932  $this->setExtGState($gs);
13933  }
13934 
13942  public function getAlpha() {
13943  return $this->alpha;
13944  }
13945 
13952  public function setJPEGQuality($quality) {
13953  if (($quality < 1) OR ($quality > 100)) {
13954  $quality = 75;
13955  }
13956  $this->jpeg_quality = intval($quality);
13957  }
13958 
13965  public function setDefaultTableColumns($cols=4) {
13966  $this->default_table_columns = intval($cols);
13967  }
13968 
13975  public function setCellHeightRatio($h) {
13976  $this->cell_height_ratio = $h;
13977  }
13978 
13984  public function getCellHeightRatio() {
13985  return $this->cell_height_ratio;
13986  }
13987 
13994  public function setPDFVersion($version='1.7') {
13995  if ($this->pdfa_mode) {
13996  // PDF/A mode
13997  $this->PDFVersion = '1.4';
13998  } else {
13999  $this->PDFVersion = $version;
14000  }
14001  }
14002 
14013  $this->viewer_preferences = $preferences;
14014  }
14015 
14029  public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
14030  if (strpos($colors, 'ALLSPOT') !== false) {
14031  // expand spot colors
14032  $spot_colors = '';
14033  foreach ($this->spot_colors as $spot_color_name => $v) {
14034  $spot_colors .= ','.$spot_color_name;
14035  }
14036  if (!empty($spot_colors)) {
14037  $spot_colors = substr($spot_colors, 1);
14038  $colors = str_replace('ALLSPOT', $spot_colors, $colors);
14039  } else {
14040  $colors = str_replace('ALLSPOT', 'NONE', $colors);
14041  }
14042  }
14043  $bars = explode(',', $colors);
14044  $numbars = count($bars); // number of bars to print
14045  if ($numbars <= 0) {
14046  return;
14047  }
14048  // set bar measures
14049  if ($vertical) {
14050  $coords = array(0, 0, 0, 1);
14051  $wb = $w / $numbars; // bar width
14052  $hb = $h; // bar height
14053  $xd = $wb; // delta x
14054  $yd = 0; // delta y
14055  } else {
14056  $coords = array(1, 0, 0, 0);
14057  $wb = $w; // bar width
14058  $hb = $h / $numbars; // bar height
14059  $xd = 0; // delta x
14060  $yd = $hb; // delta y
14061  }
14062  $xb = $x;
14063  $yb = $y;
14064  foreach ($bars as $col) {
14065  switch ($col) {
14066  // set transition colors
14067  case 'A': { // BLACK (GRAYSCALE)
14068  $col_a = array(255);
14069  $col_b = array(0);
14070  break;
14071  }
14072  case 'W': { // WHITE (GRAYSCALE)
14073  $col_a = array(0);
14074  $col_b = array(255);
14075  break;
14076  }
14077  case 'R': { // RED (RGB)
14078  $col_a = array(255,255,255);
14079  $col_b = array(255,0,0);
14080  break;
14081  }
14082  case 'G': { // GREEN (RGB)
14083  $col_a = array(255,255,255);
14084  $col_b = array(0,255,0);
14085  break;
14086  }
14087  case 'B': { // BLUE (RGB)
14088  $col_a = array(255,255,255);
14089  $col_b = array(0,0,255);
14090  break;
14091  }
14092  case 'C': { // CYAN (CMYK)
14093  $col_a = array(0,0,0,0);
14094  $col_b = array(100,0,0,0);
14095  break;
14096  }
14097  case 'M': { // MAGENTA (CMYK)
14098  $col_a = array(0,0,0,0);
14099  $col_b = array(0,100,0,0);
14100  break;
14101  }
14102  case 'Y': { // YELLOW (CMYK)
14103  $col_a = array(0,0,0,0);
14104  $col_b = array(0,0,100,0);
14105  break;
14106  }
14107  case 'K': { // KEY - BLACK (CMYK)
14108  $col_a = array(0,0,0,0);
14109  $col_b = array(0,0,0,100);
14110  break;
14111  }
14112  case 'RGB': { // BLACK REGISTRATION (RGB)
14113  $col_a = array(255,255,255);
14114  $col_b = array(0,0,0);
14115  break;
14116  }
14117  case 'CMYK': { // BLACK REGISTRATION (CMYK)
14118  $col_a = array(0,0,0,0);
14119  $col_b = array(100,100,100,100);
14120  break;
14121  }
14122  case 'ALL': { // SPOT COLOR REGISTRATION
14123  $col_a = array(0,0,0,0,'None');
14124  $col_b = array(100,100,100,100,'All');
14125  break;
14126  }
14127  case 'NONE': { // SKIP THIS COLOR
14128  $col_a = array(0,0,0,0,'None');
14129  $col_b = array(0,0,0,0,'None');
14130  break;
14131  }
14132  default: { // SPECIFIC SPOT COLOR NAME
14133  $col_a = array(0,0,0,0,'None');
14134  $col_b = TCPDF_COLORS::getSpotColor($col, $this->spot_colors);
14135  if ($col_b === false) {
14136  // in case of error defaults to the registration color
14137  $col_b = array(100,100,100,100,'All');
14138  }
14139  break;
14140  }
14141  }
14142  if ($col != 'NONE') {
14143  if ($transition) {
14144  // color gradient
14145  $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
14146  } else {
14147  $this->SetFillColorArray($col_b);
14148  // colored rectangle
14149  $this->Rect($xb, $yb, $wb, $hb, 'F', array());
14150  }
14151  $xb += $xd;
14152  $yb += $yd;
14153  }
14154  }
14155  }
14156 
14169  public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(100,100,100,100,'All')) {
14170  $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
14171  $type = strtoupper($type);
14172  $type = preg_replace('/[^A-Z\-\,]*/', '', $type);
14173  // split type in single components
14174  $type = str_replace('-', ',', $type);
14175  $type = str_replace('TL', 'T,L', $type);
14176  $type = str_replace('TR', 'T,R', $type);
14177  $type = str_replace('BL', 'F,L', $type);
14178  $type = str_replace('BR', 'F,R', $type);
14179  $type = str_replace('A', 'T,L', $type);
14180  $type = str_replace('B', 'T,R', $type);
14181  $type = str_replace('T,RO', 'BO', $type);
14182  $type = str_replace('C', 'F,L', $type);
14183  $type = str_replace('D', 'F,R', $type);
14184  $crops = explode(',', strtoupper($type));
14185  // remove duplicates
14186  $crops = array_unique($crops);
14187  $dw = ($w / 4); // horizontal space to leave before the intersection point
14188  $dh = ($h / 4); // vertical space to leave before the intersection point
14189  foreach ($crops as $crop) {
14190  switch ($crop) {
14191  case 'T':
14192  case 'TOP': {
14193  $x1 = $x;
14194  $y1 = ($y - $h);
14195  $x2 = $x;
14196  $y2 = ($y - $dh);
14197  break;
14198  }
14199  case 'F':
14200  case 'BOTTOM': {
14201  $x1 = $x;
14202  $y1 = ($y + $dh);
14203  $x2 = $x;
14204  $y2 = ($y + $h);
14205  break;
14206  }
14207  case 'L':
14208  case 'LEFT': {
14209  $x1 = ($x - $w);
14210  $y1 = $y;
14211  $x2 = ($x - $dw);
14212  $y2 = $y;
14213  break;
14214  }
14215  case 'R':
14216  case 'RIGHT': {
14217  $x1 = ($x + $dw);
14218  $y1 = $y;
14219  $x2 = ($x + $w);
14220  $y2 = $y;
14221  break;
14222  }
14223  }
14224  $this->Line($x1, $y1, $x2, $y2);
14225  }
14226  }
14227 
14240  public function registrationMark($x, $y, $r, $double=false, $cola=array(100,100,100,100,'All'), $colb=array(0,0,0,0,'None')) {
14241  $line_style = array('width' => max((0.5 / $this->k),($r / 30)), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
14242  $this->SetFillColorArray($cola);
14243  $this->PieSector($x, $y, $r, 90, 180, 'F');
14244  $this->PieSector($x, $y, $r, 270, 360, 'F');
14245  $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14246  if ($double) {
14247  $ri = $r * 0.5;
14248  $this->SetFillColorArray($colb);
14249  $this->PieSector($x, $y, $ri, 90, 180, 'F');
14250  $this->PieSector($x, $y, $ri, 270, 360, 'F');
14251  $this->SetFillColorArray($cola);
14252  $this->PieSector($x, $y, $ri, 0, 90, 'F');
14253  $this->PieSector($x, $y, $ri, 180, 270, 'F');
14254  $this->Circle($x, $y, $ri, 0, 360, 'C', $line_style, array(), 8);
14255  }
14256  }
14257 
14267  public function registrationMarkCMYK($x, $y, $r) {
14268  // line width
14269  $lw = max((0.5 / $this->k),($r / 8));
14270  // internal radius
14271  $ri = ($r * 0.6);
14272  // external radius
14273  $re = ($r * 1.3);
14274  // Cyan
14275  $this->SetFillColorArray(array(100,0,0,0));
14276  $this->PieSector($x, $y, $ri, 270, 360, 'F');
14277  // Magenta
14278  $this->SetFillColorArray(array(0,100,0,0));
14279  $this->PieSector($x, $y, $ri, 0, 90, 'F');
14280  // Yellow
14281  $this->SetFillColorArray(array(0,0,100,0));
14282  $this->PieSector($x, $y, $ri, 90, 180, 'F');
14283  // Key - black
14284  $this->SetFillColorArray(array(0,0,0,100));
14285  $this->PieSector($x, $y, $ri, 180, 270, 'F');
14286  // registration color
14287  $line_style = array('width' => $lw, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(100,100,100,100,'All'));
14288  $this->SetFillColorArray(array(100,100,100,100,'All'));
14289  // external circle
14290  $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14291  // cross lines
14292  $this->Line($x, ($y - $re), $x, ($y - $ri));
14293  $this->Line($x, ($y + $ri), $x, ($y + $re));
14294  $this->Line(($x - $re), $y, ($x - $ri), $y);
14295  $this->Line(($x + $ri), $y, ($x + $re), $y);
14296  }
14297 
14311  public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
14312  $this->Clip($x, $y, $w, $h);
14313  $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14314  }
14315 
14329  public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
14330  $this->Clip($x, $y, $w, $h);
14331  $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14332  }
14333 
14352  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) {
14353  if ($this->pdfa_mode OR ($this->state != 2)) {
14354  return;
14355  }
14356  $this->Clip($x, $y, $w, $h);
14357  $n = count($this->gradients) + 1;
14358  $this->gradients[$n] = array();
14359  $this->gradients[$n]['type'] = 6; //coons patch mesh
14360  $this->gradients[$n]['coords'] = array();
14361  $this->gradients[$n]['antialias'] = $antialias;
14362  $this->gradients[$n]['colors'] = array();
14363  $this->gradients[$n]['transparency'] = false;
14364  //check the coords array if it is the simple array or the multi patch array
14365  if (!isset($coords[0]['f'])) {
14366  //simple array -> convert to multi patch array
14367  if (!isset($col1[1])) {
14368  $col1[1] = $col1[2] = $col1[0];
14369  }
14370  if (!isset($col2[1])) {
14371  $col2[1] = $col2[2] = $col2[0];
14372  }
14373  if (!isset($col3[1])) {
14374  $col3[1] = $col3[2] = $col3[0];
14375  }
14376  if (!isset($col4[1])) {
14377  $col4[1] = $col4[2] = $col4[0];
14378  }
14379  $patch_array[0]['f'] = 0;
14380  $patch_array[0]['points'] = $coords;
14381  $patch_array[0]['colors'][0]['r'] = $col1[0];
14382  $patch_array[0]['colors'][0]['g'] = $col1[1];
14383  $patch_array[0]['colors'][0]['b'] = $col1[2];
14384  $patch_array[0]['colors'][1]['r'] = $col2[0];
14385  $patch_array[0]['colors'][1]['g'] = $col2[1];
14386  $patch_array[0]['colors'][1]['b'] = $col2[2];
14387  $patch_array[0]['colors'][2]['r'] = $col3[0];
14388  $patch_array[0]['colors'][2]['g'] = $col3[1];
14389  $patch_array[0]['colors'][2]['b'] = $col3[2];
14390  $patch_array[0]['colors'][3]['r'] = $col4[0];
14391  $patch_array[0]['colors'][3]['g'] = $col4[1];
14392  $patch_array[0]['colors'][3]['b'] = $col4[2];
14393  } else {
14394  //multi patch array
14396  }
14397  $bpcd = 65535; //16 bits per coordinate
14398  //build the data stream
14399  $this->gradients[$n]['stream'] = '';
14400  $count_patch = count($patch_array);
14401  for ($i=0; $i < $count_patch; ++$i) {
14402  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
14403  $count_points = count($patch_array[$i]['points']);
14404  for ($j=0; $j < $count_points; ++$j) {
14405  //each point as 16 bit
14406  $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
14407  if ($patch_array[$i]['points'][$j] < 0) {
14408  $patch_array[$i]['points'][$j] = 0;
14409  }
14410  if ($patch_array[$i]['points'][$j] > $bpcd) {
14411  $patch_array[$i]['points'][$j] = $bpcd;
14412  }
14413  $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
14414  $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
14415  }
14416  $count_cols = count($patch_array[$i]['colors']);
14417  for ($j=0; $j < $count_cols; ++$j) {
14418  //each color component as 8 bit
14419  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
14420  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
14421  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
14422  }
14423  }
14424  //paint the gradient
14425  $this->_out('/Sh'.$n.' sh');
14426  //restore previous Graphic State
14427  $this->_outRestoreGraphicsState();
14428  if ($this->inxobj) {
14429  // we are inside an XObject template
14430  $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14431  }
14432  }
14433 
14444  protected function Clip($x, $y, $w, $h) {
14445  if ($this->state != 2) {
14446  return;
14447  }
14448  if ($this->rtl) {
14449  $x = $this->w - $x - $w;
14450  }
14451  //save current Graphic State
14452  $s = 'q';
14453  //set clipping area
14454  $s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
14455  //set up transformation matrix for gradient
14456  $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
14457  $this->_out($s);
14458  }
14459 
14471  public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
14472  if ($this->pdfa_mode OR ($this->state != 2)) {
14473  return;
14474  }
14475  $n = count($this->gradients) + 1;
14476  $this->gradients[$n] = array();
14477  $this->gradients[$n]['type'] = $type;
14478  $this->gradients[$n]['coords'] = $coords;
14479  $this->gradients[$n]['antialias'] = $antialias;
14480  $this->gradients[$n]['colors'] = array();
14481  $this->gradients[$n]['transparency'] = false;
14482  // color space
14483  $numcolspace = count($stops[0]['color']);
14484  $bcolor = array_values($background);
14485  switch($numcolspace) {
14486  case 5: // SPOT
14487  case 4: { // CMYK
14488  $this->gradients[$n]['colspace'] = 'DeviceCMYK';
14489  if (!empty($background)) {
14490  $this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
14491  }
14492  break;
14493  }
14494  case 3: { // RGB
14495  $this->gradients[$n]['colspace'] = 'DeviceRGB';
14496  if (!empty($background)) {
14497  $this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
14498  }
14499  break;
14500  }
14501  case 1: { // GRAY SCALE
14502  $this->gradients[$n]['colspace'] = 'DeviceGray';
14503  if (!empty($background)) {
14504  $this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255);
14505  }
14506  break;
14507  }
14508  }
14509  $num_stops = count($stops);
14510  $last_stop_id = $num_stops - 1;
14511  foreach ($stops as $key => $stop) {
14512  $this->gradients[$n]['colors'][$key] = array();
14513  // offset represents a location along the gradient vector
14514  if (isset($stop['offset'])) {
14515  $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
14516  } else {
14517  if ($key == 0) {
14518  $this->gradients[$n]['colors'][$key]['offset'] = 0;
14519  } elseif ($key == $last_stop_id) {
14520  $this->gradients[$n]['colors'][$key]['offset'] = 1;
14521  } else {
14522  $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
14523  $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
14524  }
14525  }
14526  if (isset($stop['opacity'])) {
14527  $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
14528  if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) {
14529  $this->gradients[$n]['transparency'] = true;
14530  }
14531  } else {
14532  $this->gradients[$n]['colors'][$key]['opacity'] = 1;
14533  }
14534  // exponent for the exponential interpolation function
14535  if (isset($stop['exponent'])) {
14536  $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
14537  } else {
14538  $this->gradients[$n]['colors'][$key]['exponent'] = 1;
14539  }
14540  // set colors
14541  $color = array_values($stop['color']);
14542  switch($numcolspace) {
14543  case 5: // SPOT
14544  case 4: { // CMYK
14545  $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
14546  break;
14547  }
14548  case 3: { // RGB
14549  $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255);
14550  break;
14551  }
14552  case 1: { // GRAY SCALE
14553  $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255);
14554  break;
14555  }
14556  }
14557  }
14558  if ($this->gradients[$n]['transparency']) {
14559  // paint luminosity gradient
14560  $this->_out('/TGS'.$n.' gs');
14561  }
14562  //paint the gradient
14563  $this->_out('/Sh'.$n.' sh');
14564  //restore previous Graphic State
14565  $this->_outRestoreGraphicsState();
14566  if ($this->inxobj) {
14567  // we are inside an XObject template
14568  $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14569  }
14570  }
14571 
14578  function _putshaders() {
14579  if ($this->pdfa_mode) {
14580  return;
14581  }
14582  $idt = count($this->gradients); //index for transparency gradients
14583  foreach ($this->gradients as $id => $grad) {
14584  if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
14585  $fc = $this->_newobj();
14586  $out = '<<';
14587  $out .= ' /FunctionType 3';
14588  $out .= ' /Domain [0 1]';
14589  $functions = '';
14590  $bounds = '';
14591  $encode = '';
14592  $i = 1;
14593  $num_cols = count($grad['colors']);
14594  $lastcols = $num_cols - 1;
14595  for ($i = 1; $i < $num_cols; ++$i) {
14596  $functions .= ($fc + $i).' 0 R ';
14597  if ($i < $lastcols) {
14598  $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']);
14599  }
14600  $encode .= '0 1 ';
14601  }
14602  $out .= ' /Functions ['.trim($functions).']';
14603  $out .= ' /Bounds ['.trim($bounds).']';
14604  $out .= ' /Encode ['.trim($encode).']';
14605  $out .= ' >>';
14606  $out .= "\n".'endobj';
14607  $this->_out($out);
14608  for ($i = 1; $i < $num_cols; ++$i) {
14609  $this->_newobj();
14610  $out = '<<';
14611  $out .= ' /FunctionType 2';
14612  $out .= ' /Domain [0 1]';
14613  $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
14614  $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
14615  $out .= ' /N '.$grad['colors'][$i]['exponent'];
14616  $out .= ' >>';
14617  $out .= "\n".'endobj';
14618  $this->_out($out);
14619  }
14620  // set transparency functions
14621  if ($grad['transparency']) {
14622  $ft = $this->_newobj();
14623  $out = '<<';
14624  $out .= ' /FunctionType 3';
14625  $out .= ' /Domain [0 1]';
14626  $functions = '';
14627  $i = 1;
14628  $num_cols = count($grad['colors']);
14629  for ($i = 1; $i < $num_cols; ++$i) {
14630  $functions .= ($ft + $i).' 0 R ';
14631  }
14632  $out .= ' /Functions ['.trim($functions).']';
14633  $out .= ' /Bounds ['.trim($bounds).']';
14634  $out .= ' /Encode ['.trim($encode).']';
14635  $out .= ' >>';
14636  $out .= "\n".'endobj';
14637  $this->_out($out);
14638  for ($i = 1; $i < $num_cols; ++$i) {
14639  $this->_newobj();
14640  $out = '<<';
14641  $out .= ' /FunctionType 2';
14642  $out .= ' /Domain [0 1]';
14643  $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
14644  $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
14645  $out .= ' /N '.$grad['colors'][$i]['exponent'];
14646  $out .= ' >>';
14647  $out .= "\n".'endobj';
14648  $this->_out($out);
14649  }
14650  }
14651  }
14652  // set shading object
14653  $this->_newobj();
14654  $out = '<< /ShadingType '.$grad['type'];
14655  if (isset($grad['colspace'])) {
14656  $out .= ' /ColorSpace /'.$grad['colspace'];
14657  } else {
14658  $out .= ' /ColorSpace /DeviceRGB';
14659  }
14660  if (isset($grad['background']) AND !empty($grad['background'])) {
14661  $out .= ' /Background ['.$grad['background'].']';
14662  }
14663  if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
14664  $out .= ' /AntiAlias true';
14665  }
14666  if ($grad['type'] == 2) {
14667  $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
14668  $out .= ' /Domain [0 1]';
14669  $out .= ' /Function '.$fc.' 0 R';
14670  $out .= ' /Extend [true true]';
14671  $out .= ' >>';
14672  } elseif ($grad['type'] == 3) {
14673  //x0, y0, r0, x1, y1, r1
14674  //at this this time radius of inner circle is 0
14675  $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
14676  $out .= ' /Domain [0 1]';
14677  $out .= ' /Function '.$fc.' 0 R';
14678  $out .= ' /Extend [true true]';
14679  $out .= ' >>';
14680  } elseif ($grad['type'] == 6) {
14681  $out .= ' /BitsPerCoordinate 16';
14682  $out .= ' /BitsPerComponent 8';
14683  $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
14684  $out .= ' /BitsPerFlag 8';
14685  $stream = $this->_getrawstream($grad['stream']);
14686  $out .= ' /Length '.strlen($stream);
14687  $out .= ' >>';
14688  $out .= ' stream'."\n".$stream."\n".'endstream';
14689  }
14690  $out .= "\n".'endobj';
14691  $this->_out($out);
14692  if ($grad['transparency']) {
14693  $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
14694  $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
14695  }
14696  $this->gradients[$id]['id'] = $this->n;
14697  // set pattern object
14698  $this->_newobj();
14699  $out = '<< /Type /Pattern /PatternType 2';
14700  $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
14701  $out .= ' >>';
14702  $out .= "\n".'endobj';
14703  $this->_out($out);
14704  $this->gradients[$id]['pattern'] = $this->n;
14705  // set shading and pattern for transparency mask
14706  if ($grad['transparency']) {
14707  // luminosity pattern
14708  $idgs = $id + $idt;
14709  $this->_newobj();
14710  $this->_out($shading_transparency);
14711  $this->gradients[$idgs]['id'] = $this->n;
14712  $this->_newobj();
14713  $out = '<< /Type /Pattern /PatternType 2';
14714  $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
14715  $out .= ' >>';
14716  $out .= "\n".'endobj';
14717  $this->_out($out);
14718  $this->gradients[$idgs]['pattern'] = $this->n;
14719  // luminosity XObject
14720  $oid = $this->_newobj();
14721  $this->xobjects['LX'.$oid] = array('n' => $oid);
14722  $filter = '';
14723  $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
14724  if ($this->compress) {
14725  $filter = ' /Filter /FlateDecode';
14726  $stream = gzcompress($stream);
14727  }
14728  $stream = $this->_getrawstream($stream);
14729  $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
14730  $out .= ' /Length '.strlen($stream);
14731  $rect = sprintf('%F %F', $this->wPt, $this->hPt);
14732  $out .= ' /BBox [0 0 '.$rect.']';
14733  $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
14734  $out .= ' /Resources <<';
14735  $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
14736  $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
14737  $out .= ' >>';
14738  $out .= ' >> ';
14739  $out .= ' stream'."\n".$stream."\n".'endstream';
14740  $out .= "\n".'endobj';
14741  $this->_out($out);
14742  // SMask
14743  $this->_newobj();
14744  $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj';
14745  $this->_out($out);
14746  // ExtGState
14747  $this->_newobj();
14748  $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj';
14749  $this->_out($out);
14750  $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
14751  }
14752  }
14753  }
14754 
14770  public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
14771  $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
14772  }
14773 
14791  public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
14792  if ($this->state != 2) {
14793  return;
14794  }
14795  if ($this->rtl) {
14796  $xc = ($this->w - $xc);
14797  }
14799  if ($op == 'f') {
14800  $line_style = array();
14801  }
14802  if ($cw) {
14803  $d = $b;
14804  $b = (360 - $a + $o);
14805  $a = (360 - $d + $o);
14806  } else {
14807  $b += $o;
14808  $a += $o;
14809  }
14810  $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
14811  $this->_out($op);
14812  }
14813 
14835  public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
14836  if ($this->state != 2) {
14837  return;
14838  }
14839  if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
14840  // convert EPS to raster image using GD or ImageMagick libraries
14841  return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14842  }
14843  if ($x === '') {
14844  $x = $this->x;
14845  }
14846  if ($y === '') {
14847  $y = $this->y;
14848  }
14849  // check page for no-write regions and adapt page margins if necessary
14850  list($x, $y) = $this->checkPageRegions($h, $x, $y);
14851  $k = $this->k;
14852  if ($file[0] === '@') { // image from string
14853  $data = substr($file, 1);
14854  } else { // EPS/AI file
14856  }
14857  if ($data === FALSE) {
14858  $this->Error('EPS file not found: '.$file);
14859  }
14860  $regs = array();
14861  // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
14862  preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
14863  if (count($regs) > 1) {
14864  $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
14865  if (strpos($version_str, 'Adobe Illustrator') !== false) {
14866  $versexp = explode(' ', $version_str);
14867  $version = (float)array_pop($versexp);
14868  if ($version >= 9) {
14869  $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
14870  }
14871  }
14872  }
14873  // strip binary bytes in front of PS-header
14874  $start = strpos($data, '%!PS-Adobe');
14875  if ($start > 0) {
14876  $data = substr($data, $start);
14877  }
14878  // find BoundingBox params
14879  preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
14880  if (count($regs) > 1) {
14881  list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
14882  } else {
14883  $this->Error('No BoundingBox found in EPS/AI file: '.$file);
14884  }
14885  $start = strpos($data, '%%EndSetup');
14886  if ($start === false) {
14887  $start = strpos($data, '%%EndProlog');
14888  }
14889  if ($start === false) {
14890  $start = strpos($data, '%%BoundingBox');
14891  }
14892  $data = substr($data, $start);
14893  $end = strpos($data, '%%PageTrailer');
14894  if ($end===false) {
14895  $end = strpos($data, 'showpage');
14896  }
14897  if ($end) {
14898  $data = substr($data, 0, $end);
14899  }
14900  // calculate image width and height on document
14901  if (($w <= 0) AND ($h <= 0)) {
14902  $w = ($x2 - $x1) / $k;
14903  $h = ($y2 - $y1) / $k;
14904  } elseif ($w <= 0) {
14905  $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
14906  } elseif ($h <= 0) {
14907  $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
14908  }
14909  // fit the image on available space
14910  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
14911  if ($this->rasterize_vector_images) {
14912  // convert EPS to raster image using GD or ImageMagick libraries
14913  return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14914  }
14915  // set scaling factors
14916  $scale_x = $w / (($x2 - $x1) / $k);
14917  $scale_y = $h / (($y2 - $y1) / $k);
14918  // set alignment
14919  $this->img_rb_y = $y + $h;
14920  // set alignment
14921  if ($this->rtl) {
14922  if ($palign == 'L') {
14923  $ximg = $this->lMargin;
14924  } elseif ($palign == 'C') {
14925  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
14926  } elseif ($palign == 'R') {
14927  $ximg = $this->w - $this->rMargin - $w;
14928  } else {
14929  $ximg = $x - $w;
14930  }
14931  $this->img_rb_x = $ximg;
14932  } else {
14933  if ($palign == 'L') {
14934  $ximg = $this->lMargin;
14935  } elseif ($palign == 'C') {
14936  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
14937  } elseif ($palign == 'R') {
14938  $ximg = $this->w - $this->rMargin - $w;
14939  } else {
14940  $ximg = $x;
14941  }
14942  $this->img_rb_x = $ximg + $w;
14943  }
14944  if ($useBoundingBox) {
14945  $dx = $ximg * $k - $x1;
14946  $dy = $y * $k - $y1;
14947  } else {
14948  $dx = $ximg * $k;
14949  $dy = $y * $k;
14950  }
14951  // save the current graphic state
14952  $this->_out('q'.$this->epsmarker);
14953  // translate
14954  $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
14955  // scale
14956  if (isset($scale_x)) {
14957  $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
14958  }
14959  // handle pc/unix/mac line endings
14960  $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY);
14961  $u=0;
14962  $cnt = count($lines);
14963  for ($i=0; $i < $cnt; ++$i) {
14964  $line = $lines[$i];
14965  if (($line == '') OR ($line[0] == '%')) {
14966  continue;
14967  }
14968  $len = strlen($line);
14969  // check for spot color names
14970  $color_name = '';
14971  if (strcasecmp('x', substr(trim($line), -1)) == 0) {
14972  if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) {
14973  // extract spot color name
14974  $color_name = $matches[0];
14975  // remove color name from string
14976  $line = str_replace(' '.$color_name, '', $line);
14977  // remove pharentesis from color name
14978  $color_name = substr($color_name, 1, -1);
14979  }
14980  }
14981  $chunks = explode(' ', $line);
14982  $cmd = trim(array_pop($chunks));
14983  // RGB
14984  if (($cmd == 'Xa') OR ($cmd == 'XA')) {
14985  $b = array_pop($chunks);
14986  $g = array_pop($chunks);
14987  $r = array_pop($chunks);
14988  $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!
14989  continue;
14990  }
14991  $skip = false;
14992  if ($fixoutvals) {
14993  // check for values outside the bounding box
14994  switch ($cmd) {
14995  case 'm':
14996  case 'l':
14997  case 'L': {
14998  // skip values outside bounding box
14999  foreach ($chunks as $key => $val) {
15000  if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) {
15001  $skip = true;
15002  } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) {
15003  $skip = true;
15004  }
15005  }
15006  }
15007  }
15008  }
15009  switch ($cmd) {
15010  case 'm':
15011  case 'l':
15012  case 'v':
15013  case 'y':
15014  case 'c':
15015  case 'k':
15016  case 'K':
15017  case 'g':
15018  case 'G':
15019  case 's':
15020  case 'S':
15021  case 'J':
15022  case 'j':
15023  case 'w':
15024  case 'M':
15025  case 'd':
15026  case 'n': {
15027  if ($skip) {
15028  break;
15029  }
15030  $this->_out($line);
15031  break;
15032  }
15033  case 'x': {// custom fill color
15034  if (empty($color_name)) {
15035  // CMYK color
15036  list($col_c, $col_m, $col_y, $col_k) = $chunks;
15037  $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
15038  } else {
15039  // Spot Color (CMYK + tint)
15040  list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15041  $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15042  $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15043  $this->_out($color_cmd);
15044  }
15045  break;
15046  }
15047  case 'X': { // custom stroke color
15048  if (empty($color_name)) {
15049  // CMYK color
15050  list($col_c, $col_m, $col_y, $col_k) = $chunks;
15051  $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
15052  } else {
15053  // Spot Color (CMYK + tint)
15054  list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15055  $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15056  $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15057  $this->_out($color_cmd);
15058  }
15059  break;
15060  }
15061  case 'Y':
15062  case 'N':
15063  case 'V':
15064  case 'L':
15065  case 'C': {
15066  if ($skip) {
15067  break;
15068  }
15069  $line[($len - 1)] = strtolower($cmd);
15070  $this->_out($line);
15071  break;
15072  }
15073  case 'b':
15074  case 'B': {
15075  $this->_out($cmd . '*');
15076  break;
15077  }
15078  case 'f':
15079  case 'F': {
15080  if ($u > 0) {
15081  $isU = false;
15082  $max = min(($i + 5), $cnt);
15083  for ($j = ($i + 1); $j < $max; ++$j) {
15084  $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
15085  }
15086  if ($isU) {
15087  $this->_out('f*');
15088  }
15089  } else {
15090  $this->_out('f*');
15091  }
15092  break;
15093  }
15094  case '*u': {
15095  ++$u;
15096  break;
15097  }
15098  case '*U': {
15099  --$u;
15100  break;
15101  }
15102  }
15103  }
15104  // restore previous graphic state
15105  $this->_out($this->epsmarker.'Q');
15106  if (!empty($border)) {
15107  $bx = $this->x;
15108  $by = $this->y;
15109  $this->x = $ximg;
15110  if ($this->rtl) {
15111  $this->x += $w;
15112  }
15113  $this->y = $y;
15114  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
15115  $this->x = $bx;
15116  $this->y = $by;
15117  }
15118  if ($link) {
15119  $this->Link($ximg, $y, $w, $h, $link, 0);
15120  }
15121  // set pointer to align the next text/objects
15122  switch($align) {
15123  case 'T':{
15124  $this->y = $y;
15125  $this->x = $this->img_rb_x;
15126  break;
15127  }
15128  case 'M':{
15129  $this->y = $y + round($h/2);
15130  $this->x = $this->img_rb_x;
15131  break;
15132  }
15133  case 'B':{
15134  $this->y = $this->img_rb_y;
15135  $this->x = $this->img_rb_x;
15136  break;
15137  }
15138  case 'N':{
15139  $this->SetY($this->img_rb_y);
15140  break;
15141  }
15142  default:{
15143  break;
15144  }
15145  }
15146  $this->endlinex = $this->img_rb_x;
15147  }
15148 
15154  public function setBarcode($bc='') {
15155  $this->barcode = $bc;
15156  }
15157 
15164  public function getBarcode() {
15165  return $this->barcode;
15166  }
15167 
15198  public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
15199  if (TCPDF_STATIC::empty_string(trim($code))) {
15200  return;
15201  }
15202  require_once(dirname(__FILE__).'/tcpdf_barcodes_1d.php');
15203  // save current graphic settings
15204  $gvars = $this->getGraphicVars();
15205  // create new barcode object
15206  $barcodeobj = new TCPDFBarcode($code, $type);
15207  $arrcode = $barcodeobj->getBarcodeArray();
15208  if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] <= 0)) {
15209  $this->Error('Error in 1D barcode string');
15210  }
15211  if ($arrcode['maxh'] <= 0) {
15212  $arrcode['maxh'] = 1;
15213  }
15214  // set default values
15215  if (!isset($style['position'])) {
15216  $style['position'] = '';
15217  } elseif ($style['position'] == 'S') {
15218  // keep this for backward compatibility
15219  $style['position'] = '';
15220  $style['stretch'] = true;
15221  }
15222  if (!isset($style['fitwidth'])) {
15223  if (!isset($style['stretch'])) {
15224  $style['fitwidth'] = true;
15225  } else {
15226  $style['fitwidth'] = false;
15227  }
15228  }
15229  if ($style['fitwidth']) {
15230  // disable stretch
15231  $style['stretch'] = false;
15232  }
15233  if (!isset($style['stretch'])) {
15234  if (($w === '') OR ($w <= 0)) {
15235  $style['stretch'] = false;
15236  } else {
15237  $style['stretch'] = true;
15238  }
15239  }
15240  if (!isset($style['fgcolor'])) {
15241  $style['fgcolor'] = array(0,0,0); // default black
15242  }
15243  if (!isset($style['bgcolor'])) {
15244  $style['bgcolor'] = false; // default transparent
15245  }
15246  if (!isset($style['border'])) {
15247  $style['border'] = false;
15248  }
15249  $fontsize = 0;
15250  if (!isset($style['text'])) {
15251  $style['text'] = false;
15252  }
15253  if ($style['text'] AND isset($style['font'])) {
15254  if (isset($style['fontsize'])) {
15255  $fontsize = $style['fontsize'];
15256  }
15257  $this->SetFont($style['font'], '', $fontsize);
15258  }
15259  if (!isset($style['stretchtext'])) {
15260  $style['stretchtext'] = 4;
15261  }
15262  if ($x === '') {
15263  $x = $this->x;
15264  }
15265  if ($y === '') {
15266  $y = $this->y;
15267  }
15268  // check page for no-write regions and adapt page margins if necessary
15269  list($x, $y) = $this->checkPageRegions($h, $x, $y);
15270  if (($w === '') OR ($w <= 0)) {
15271  if ($this->rtl) {
15272  $w = $x - $this->lMargin;
15273  } else {
15274  $w = $this->w - $this->rMargin - $x;
15275  }
15276  }
15277  // padding
15278  if (!isset($style['padding'])) {
15279  $padding = 0;
15280  } elseif ($style['padding'] === 'auto') {
15281  $padding = 10 * ($w / ($arrcode['maxw'] + 20));
15282  } else {
15283  $padding = floatval($style['padding']);
15284  }
15285  // horizontal padding
15286  if (!isset($style['hpadding'])) {
15287  $hpadding = $padding;
15288  } elseif ($style['hpadding'] === 'auto') {
15289  $hpadding = 10 * ($w / ($arrcode['maxw'] + 20));
15290  } else {
15291  $hpadding = floatval($style['hpadding']);
15292  }
15293  // vertical padding
15294  if (!isset($style['vpadding'])) {
15295  $vpadding = $padding;
15296  } elseif ($style['vpadding'] === 'auto') {
15297  $vpadding = ($hpadding / 2);
15298  } else {
15299  $vpadding = floatval($style['vpadding']);
15300  }
15301  // calculate xres (single bar width)
15302  $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
15303  if ($style['stretch']) {
15304  $xres = $max_xres;
15305  } else {
15306  if (TCPDF_STATIC::empty_string($xres)) {
15307  $xres = (0.141 * $this->k); // default bar width = 0.4 mm
15308  }
15309  if ($xres > $max_xres) {
15310  // correct xres to fit on $w
15311  $xres = $max_xres;
15312  }
15313  if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
15314  OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
15315  $hpadding = 10 * $xres;
15316  if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
15317  $vpadding = ($hpadding / 2);
15318  }
15319  }
15320  }
15321  if ($style['fitwidth']) {
15322  $wold = $w;
15323  $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding));
15324  if (isset($style['cellfitalign'])) {
15325  switch ($style['cellfitalign']) {
15326  case 'L': {
15327  if ($this->rtl) {
15328  $x -= ($wold - $w);
15329  }
15330  break;
15331  }
15332  case 'R': {
15333  if (!$this->rtl) {
15334  $x += ($wold - $w);
15335  }
15336  break;
15337  }
15338  case 'C': {
15339  if ($this->rtl) {
15340  $x -= (($wold - $w) / 2);
15341  } else {
15342  $x += (($wold - $w) / 2);
15343  }
15344  break;
15345  }
15346  default : {
15347  break;
15348  }
15349  }
15350  }
15351  }
15352  $text_height = $this->getCellHeight($fontsize / $this->k);
15353  // height
15354  if (($h === '') OR ($h <= 0)) {
15355  // set default height
15356  $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height;
15357  }
15358  $barh = $h - $text_height - (2 * $vpadding);
15359  if ($barh <=0) {
15360  // try to reduce font or padding to fit barcode on available height
15361  if ($text_height > $h) {
15362  $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio));
15363  $text_height = $this->getCellHeight($fontsize / $this->k);
15364  $this->SetFont($style['font'], '', $fontsize);
15365  }
15366  if ($vpadding > 0) {
15367  $vpadding = (($h - $text_height) / 4);
15368  }
15369  $barh = $h - $text_height - (2 * $vpadding);
15370  }
15371  // fit the barcode on available space
15372  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15373  // set alignment
15374  $this->img_rb_y = $y + $h;
15375  // set alignment
15376  if ($this->rtl) {
15377  if ($style['position'] == 'L') {
15378  $xpos = $this->lMargin;
15379  } elseif ($style['position'] == 'C') {
15380  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15381  } elseif ($style['position'] == 'R') {
15382  $xpos = $this->w - $this->rMargin - $w;
15383  } else {
15384  $xpos = $x - $w;
15385  }
15386  $this->img_rb_x = $xpos;
15387  } else {
15388  if ($style['position'] == 'L') {
15389  $xpos = $this->lMargin;
15390  } elseif ($style['position'] == 'C') {
15391  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15392  } elseif ($style['position'] == 'R') {
15393  $xpos = $this->w - $this->rMargin - $w;
15394  } else {
15395  $xpos = $x;
15396  }
15397  $this->img_rb_x = $xpos + $w;
15398  }
15399  $xpos_rect = $xpos;
15400  if (!isset($style['align'])) {
15401  $style['align'] = 'C';
15402  }
15403  switch ($style['align']) {
15404  case 'L': {
15405  $xpos = $xpos_rect + $hpadding;
15406  break;
15407  }
15408  case 'R': {
15409  $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding;
15410  break;
15411  }
15412  case 'C':
15413  default : {
15414  $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2);
15415  break;
15416  }
15417  }
15418  $xpos_text = $xpos;
15419  // barcode is always printed in LTR direction
15420  $tempRTL = $this->rtl;
15421  $this->rtl = false;
15422  // print background color
15423  if ($style['bgcolor']) {
15424  $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15425  } elseif ($style['border']) {
15426  $this->Rect($xpos_rect, $y, $w, $h, 'D');
15427  }
15428  // set foreground color
15429  $this->SetDrawColorArray($style['fgcolor']);
15430  $this->SetTextColorArray($style['fgcolor']);
15431  // print bars
15432  foreach ($arrcode['bcode'] as $k => $v) {
15433  $bw = ($v['w'] * $xres);
15434  if ($v['t']) {
15435  // draw a vertical bar
15436  $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']);
15437  $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
15438  }
15439  $xpos += $bw;
15440  }
15441  // print text
15442  if ($style['text']) {
15443  if (isset($style['label']) AND !TCPDF_STATIC::empty_string($style['label'])) {
15444  $label = $style['label'];
15445  } else {
15446  $label = $code;
15447  }
15448  $txtwidth = ($arrcode['maxw'] * $xres);
15449  if ($this->GetStringWidth($label) > $txtwidth) {
15450  $style['stretchtext'] = 2;
15451  }
15452  // print text
15453  $this->x = $xpos_text;
15454  $this->y = $y + $vpadding + $barh;
15455  $cellpadding = $this->cell_padding;
15456  $this->SetCellPadding(0);
15457  $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
15458  $this->cell_padding = $cellpadding;
15459  }
15460  // restore original direction
15461  $this->rtl = $tempRTL;
15462  // restore previous settings
15463  $this->setGraphicVars($gvars);
15464  // set pointer to align the next text/objects
15465  switch($align) {
15466  case 'T':{
15467  $this->y = $y;
15468  $this->x = $this->img_rb_x;
15469  break;
15470  }
15471  case 'M':{
15472  $this->y = $y + round($h / 2);
15473  $this->x = $this->img_rb_x;
15474  break;
15475  }
15476  case 'B':{
15477  $this->y = $this->img_rb_y;
15478  $this->x = $this->img_rb_x;
15479  break;
15480  }
15481  case 'N':{
15482  $this->SetY($this->img_rb_y);
15483  break;
15484  }
15485  default:{
15486  break;
15487  }
15488  }
15489  $this->endlinex = $this->img_rb_x;
15490  }
15491 
15517  public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) {
15518  if (TCPDF_STATIC::empty_string(trim($code))) {
15519  return;
15520  }
15521  require_once(dirname(__FILE__).'/tcpdf_barcodes_2d.php');
15522  // save current graphic settings
15523  $gvars = $this->getGraphicVars();
15524  // create new barcode object
15525  $barcodeobj = new TCPDF2DBarcode($code, $type);
15526  $arrcode = $barcodeobj->getBarcodeArray();
15527  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)) {
15528  $this->Error('Error in 2D barcode string');
15529  }
15530  // set default values
15531  if (!isset($style['position'])) {
15532  $style['position'] = '';
15533  }
15534  if (!isset($style['fgcolor'])) {
15535  $style['fgcolor'] = array(0,0,0); // default black
15536  }
15537  if (!isset($style['bgcolor'])) {
15538  $style['bgcolor'] = false; // default transparent
15539  }
15540  if (!isset($style['border'])) {
15541  $style['border'] = false;
15542  }
15543  // padding
15544  if (!isset($style['padding'])) {
15545  $style['padding'] = 0;
15546  } elseif ($style['padding'] === 'auto') {
15547  $style['padding'] = 4;
15548  }
15549  if (!isset($style['hpadding'])) {
15550  $style['hpadding'] = $style['padding'];
15551  } elseif ($style['hpadding'] === 'auto') {
15552  $style['hpadding'] = 4;
15553  }
15554  if (!isset($style['vpadding'])) {
15555  $style['vpadding'] = $style['padding'];
15556  } elseif ($style['vpadding'] === 'auto') {
15557  $style['vpadding'] = 4;
15558  }
15559  $hpad = (2 * $style['hpadding']);
15560  $vpad = (2 * $style['vpadding']);
15561  // cell (module) dimension
15562  if (!isset($style['module_width'])) {
15563  $style['module_width'] = 1; // width of a single module in points
15564  }
15565  if (!isset($style['module_height'])) {
15566  $style['module_height'] = 1; // height of a single module in points
15567  }
15568  if ($x === '') {
15569  $x = $this->x;
15570  }
15571  if ($y === '') {
15572  $y = $this->y;
15573  }
15574  // check page for no-write regions and adapt page margins if necessary
15575  list($x, $y) = $this->checkPageRegions($h, $x, $y);
15576  // number of barcode columns and rows
15577  $rows = $arrcode['num_rows'];
15578  $cols = $arrcode['num_cols'];
15579  if (($rows <= 0) || ($cols <= 0)){
15580  $this->Error('Error in 2D barcode string');
15581  }
15582  // module width and height
15583  $mw = $style['module_width'];
15584  $mh = $style['module_height'];
15585  if (($mw <= 0) OR ($mh <= 0)) {
15586  $this->Error('Error in 2D barcode string');
15587  }
15588  // get max dimensions
15589  if ($this->rtl) {
15590  $maxw = $x - $this->lMargin;
15591  } else {
15592  $maxw = $this->w - $this->rMargin - $x;
15593  }
15594  $maxh = ($this->h - $this->tMargin - $this->bMargin);
15595  $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad));
15596  $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad));
15597  if (!$distort) {
15598  if (($maxw * $ratioHW) > $maxh) {
15599  $maxw = $maxh * $ratioWH;
15600  }
15601  if (($maxh * $ratioWH) > $maxw) {
15602  $maxh = $maxw * $ratioHW;
15603  }
15604  }
15605  // set maximum dimensions
15606  if ($w > $maxw) {
15607  $w = $maxw;
15608  }
15609  if ($h > $maxh) {
15610  $h = $maxh;
15611  }
15612  // set dimensions
15613  if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
15614  $w = ($cols + $hpad) * ($mw / $this->k);
15615  $h = ($rows + $vpad) * ($mh / $this->k);
15616  } elseif (($w === '') OR ($w <= 0)) {
15617  $w = $h * $ratioWH;
15618  } elseif (($h === '') OR ($h <= 0)) {
15619  $h = $w * $ratioHW;
15620  }
15621  // barcode size (excluding padding)
15622  $bw = ($w * $cols) / ($cols + $hpad);
15623  $bh = ($h * $rows) / ($rows + $vpad);
15624  // dimension of single barcode cell unit
15625  $cw = $bw / $cols;
15626  $ch = $bh / $rows;
15627  if (!$distort) {
15628  if (($cw / $ch) > ($mw / $mh)) {
15629  // correct horizontal distortion
15630  $cw = $ch * $mw / $mh;
15631  $bw = $cw * $cols;
15632  $style['hpadding'] = ($w - $bw) / (2 * $cw);
15633  } else {
15634  // correct vertical distortion
15635  $ch = $cw * $mh / $mw;
15636  $bh = $ch * $rows;
15637  $style['vpadding'] = ($h - $bh) / (2 * $ch);
15638  }
15639  }
15640  // fit the barcode on available space
15641  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15642  // set alignment
15643  $this->img_rb_y = $y + $h;
15644  // set alignment
15645  if ($this->rtl) {
15646  if ($style['position'] == 'L') {
15647  $xpos = $this->lMargin;
15648  } elseif ($style['position'] == 'C') {
15649  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15650  } elseif ($style['position'] == 'R') {
15651  $xpos = $this->w - $this->rMargin - $w;
15652  } else {
15653  $xpos = $x - $w;
15654  }
15655  $this->img_rb_x = $xpos;
15656  } else {
15657  if ($style['position'] == 'L') {
15658  $xpos = $this->lMargin;
15659  } elseif ($style['position'] == 'C') {
15660  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15661  } elseif ($style['position'] == 'R') {
15662  $xpos = $this->w - $this->rMargin - $w;
15663  } else {
15664  $xpos = $x;
15665  }
15666  $this->img_rb_x = $xpos + $w;
15667  }
15668  $xstart = $xpos + ($style['hpadding'] * $cw);
15669  $ystart = $y + ($style['vpadding'] * $ch);
15670  // barcode is always printed in LTR direction
15671  $tempRTL = $this->rtl;
15672  $this->rtl = false;
15673  // print background color
15674  if ($style['bgcolor']) {
15675  $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15676  } elseif ($style['border']) {
15677  $this->Rect($xpos, $y, $w, $h, 'D');
15678  }
15679  // set foreground color
15680  $this->SetDrawColorArray($style['fgcolor']);
15681  // print barcode cells
15682  // for each row
15683  for ($r = 0; $r < $rows; ++$r) {
15684  $xr = $xstart;
15685  // for each column
15686  for ($c = 0; $c < $cols; ++$c) {
15687  if ($arrcode['bcode'][$r][$c] == 1) {
15688  // draw a single barcode cell
15689  $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
15690  }
15691  $xr += $cw;
15692  }
15693  $ystart += $ch;
15694  }
15695  // restore original direction
15696  $this->rtl = $tempRTL;
15697  // restore previous settings
15698  $this->setGraphicVars($gvars);
15699  // set pointer to align the next text/objects
15700  switch($align) {
15701  case 'T':{
15702  $this->y = $y;
15703  $this->x = $this->img_rb_x;
15704  break;
15705  }
15706  case 'M':{
15707  $this->y = $y + round($h/2);
15708  $this->x = $this->img_rb_x;
15709  break;
15710  }
15711  case 'B':{
15712  $this->y = $this->img_rb_y;
15713  $this->x = $this->img_rb_x;
15714  break;
15715  }
15716  case 'N':{
15717  $this->SetY($this->img_rb_y);
15718  break;
15719  }
15720  default:{
15721  break;
15722  }
15723  }
15724  $this->endlinex = $this->img_rb_x;
15725  }
15726 
15746  public function getMargins() {
15747  $ret = array(
15748  'left' => $this->lMargin,
15749  'right' => $this->rMargin,
15750  'top' => $this->tMargin,
15751  'bottom' => $this->bMargin,
15752  'header' => $this->header_margin,
15753  'footer' => $this->footer_margin,
15754  'cell' => $this->cell_padding,
15755  'padding_left' => $this->cell_padding['L'],
15756  'padding_top' => $this->cell_padding['T'],
15757  'padding_right' => $this->cell_padding['R'],
15758  'padding_bottom' => $this->cell_padding['B']
15759  );
15760  return $ret;
15761  }
15762 
15773  public function getOriginalMargins() {
15774  $ret = array(
15775  'left' => $this->original_lMargin,
15776  'right' => $this->original_rMargin
15777  );
15778  return $ret;
15779  }
15780 
15787  public function getFontSize() {
15788  return $this->FontSize;
15789  }
15790 
15797  public function getFontSizePt() {
15798  return $this->FontSizePt;
15799  }
15800 
15807  public function getFontFamily() {
15808  return $this->FontFamily;
15809  }
15810 
15817  public function getFontStyle() {
15818  return $this->FontStyle;
15819  }
15820 
15833  public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
15834  return TCPDF_STATIC::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces);
15835  }
15836 
15844  protected function getCSSBorderWidth($width) {
15845  if ($width == 'thin') {
15846  $width = (2 / $this->k);
15847  } elseif ($width == 'medium') {
15848  $width = (4 / $this->k);
15849  } elseif ($width == 'thick') {
15850  $width = (6 / $this->k);
15851  } else {
15852  $width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
15853  }
15854  return $width;
15855  }
15856 
15864  protected function getCSSBorderDashStyle($style) {
15865  switch (strtolower($style)) {
15866  case 'none':
15867  case 'hidden': {
15868  $dash = -1;
15869  break;
15870  }
15871  case 'dotted': {
15872  $dash = 1;
15873  break;
15874  }
15875  case 'dashed': {
15876  $dash = 3;
15877  break;
15878  }
15879  case 'double':
15880  case 'groove':
15881  case 'ridge':
15882  case 'inset':
15883  case 'outset':
15884  case 'solid':
15885  default: {
15886  $dash = 0;
15887  break;
15888  }
15889  }
15890  return $dash;
15891  }
15892 
15900  protected function getCSSBorderStyle($cssborder) {
15901  $bprop = preg_split('/[\s]+/', trim($cssborder));
15902  $border = array(); // value to be returned
15903  switch (count($bprop)) {
15904  case 3: {
15905  $width = $bprop[0];
15906  $style = $bprop[1];
15907  $color = $bprop[2];
15908  break;
15909  }
15910  case 2: {
15911  $width = 'medium';
15912  $style = $bprop[0];
15913  $color = $bprop[1];
15914  break;
15915  }
15916  case 1: {
15917  $width = 'medium';
15918  $style = $bprop[0];
15919  $color = 'black';
15920  break;
15921  }
15922  default: {
15923  $width = 'medium';
15924  $style = 'solid';
15925  $color = 'black';
15926  break;
15927  }
15928  }
15929  if ($style == 'none') {
15930  return array();
15931  }
15932  $border['cap'] = 'square';
15933  $border['join'] = 'miter';
15934  $border['dash'] = $this->getCSSBorderDashStyle($style);
15935  if ($border['dash'] < 0) {
15936  return array();
15937  }
15938  $border['width'] = $this->getCSSBorderWidth($width);
15939  $border['color'] = TCPDF_COLORS::convertHTMLColorToDec($color, $this->spot_colors);
15940  return $border;
15941  }
15942 
15951  public function getCSSPadding($csspadding, $width=0) {
15952  $padding = preg_split('/[\s]+/', trim($csspadding));
15953  $cell_padding = array(); // value to be returned
15954  switch (count($padding)) {
15955  case 4: {
15956  $cell_padding['T'] = $padding[0];
15957  $cell_padding['R'] = $padding[1];
15958  $cell_padding['B'] = $padding[2];
15959  $cell_padding['L'] = $padding[3];
15960  break;
15961  }
15962  case 3: {
15963  $cell_padding['T'] = $padding[0];
15964  $cell_padding['R'] = $padding[1];
15965  $cell_padding['B'] = $padding[2];
15966  $cell_padding['L'] = $padding[1];
15967  break;
15968  }
15969  case 2: {
15970  $cell_padding['T'] = $padding[0];
15971  $cell_padding['R'] = $padding[1];
15972  $cell_padding['B'] = $padding[0];
15973  $cell_padding['L'] = $padding[1];
15974  break;
15975  }
15976  case 1: {
15977  $cell_padding['T'] = $padding[0];
15978  $cell_padding['R'] = $padding[0];
15979  $cell_padding['B'] = $padding[0];
15980  $cell_padding['L'] = $padding[0];
15981  break;
15982  }
15983  default: {
15984  return $this->cell_padding;
15985  }
15986  }
15987  if ($width == 0) {
15988  $width = $this->w - $this->lMargin - $this->rMargin;
15989  }
15990  $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
15991  $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
15992  $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
15993  $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
15994  return $cell_padding;
15995  }
15996 
16005  public function getCSSMargin($cssmargin, $width=0) {
16006  $margin = preg_split('/[\s]+/', trim($cssmargin));
16007  $cell_margin = array(); // value to be returned
16008  switch (count($margin)) {
16009  case 4: {
16010  $cell_margin['T'] = $margin[0];
16011  $cell_margin['R'] = $margin[1];
16012  $cell_margin['B'] = $margin[2];
16013  $cell_margin['L'] = $margin[3];
16014  break;
16015  }
16016  case 3: {
16017  $cell_margin['T'] = $margin[0];
16018  $cell_margin['R'] = $margin[1];
16019  $cell_margin['B'] = $margin[2];
16020  $cell_margin['L'] = $margin[1];
16021  break;
16022  }
16023  case 2: {
16024  $cell_margin['T'] = $margin[0];
16025  $cell_margin['R'] = $margin[1];
16026  $cell_margin['B'] = $margin[0];
16027  $cell_margin['L'] = $margin[1];
16028  break;
16029  }
16030  case 1: {
16031  $cell_margin['T'] = $margin[0];
16032  $cell_margin['R'] = $margin[0];
16033  $cell_margin['B'] = $margin[0];
16034  $cell_margin['L'] = $margin[0];
16035  break;
16036  }
16037  default: {
16038  return $this->cell_margin;
16039  }
16040  }
16041  if ($width == 0) {
16042  $width = $this->w - $this->lMargin - $this->rMargin;
16043  }
16044  $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
16045  $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
16046  $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
16047  $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
16048  return $cell_margin;
16049  }
16050 
16059  public function getCSSBorderMargin($cssbspace, $width=0) {
16060  $space = preg_split('/[\s]+/', trim($cssbspace));
16061  $border_spacing = array(); // value to be returned
16062  switch (count($space)) {
16063  case 2: {
16064  $border_spacing['H'] = $space[0];
16065  $border_spacing['V'] = $space[1];
16066  break;
16067  }
16068  case 1: {
16069  $border_spacing['H'] = $space[0];
16070  $border_spacing['V'] = $space[0];
16071  break;
16072  }
16073  default: {
16074  return array('H' => 0, 'V' => 0);
16075  }
16076  }
16077  if ($width == 0) {
16078  $width = $this->w - $this->lMargin - $this->rMargin;
16079  }
16080  $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
16081  $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
16082  return $border_spacing;
16083  }
16084 
16093  protected function getCSSFontSpacing($spacing, $parent=0) {
16094  $val = 0; // value to be returned
16095  $spacing = trim($spacing);
16096  switch ($spacing) {
16097  case 'normal': {
16098  $val = 0;
16099  break;
16100  }
16101  case 'inherit': {
16102  if ($parent == 'normal') {
16103  $val = 0;
16104  } else {
16105  $val = $parent;
16106  }
16107  break;
16108  }
16109  default: {
16110  $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
16111  }
16112  }
16113  return $val;
16114  }
16115 
16124  protected function getCSSFontStretching($stretch, $parent=100) {
16125  $val = 100; // value to be returned
16126  $stretch = trim($stretch);
16127  switch ($stretch) {
16128  case 'ultra-condensed': {
16129  $val = 40;
16130  break;
16131  }
16132  case 'extra-condensed': {
16133  $val = 55;
16134  break;
16135  }
16136  case 'condensed': {
16137  $val = 70;
16138  break;
16139  }
16140  case 'semi-condensed': {
16141  $val = 85;
16142  break;
16143  }
16144  case 'normal': {
16145  $val = 100;
16146  break;
16147  }
16148  case 'semi-expanded': {
16149  $val = 115;
16150  break;
16151  }
16152  case 'expanded': {
16153  $val = 130;
16154  break;
16155  }
16156  case 'extra-expanded': {
16157  $val = 145;
16158  break;
16159  }
16160  case 'ultra-expanded': {
16161  $val = 160;
16162  break;
16163  }
16164  case 'wider': {
16165  $val = ($parent + 10);
16166  break;
16167  }
16168  case 'narrower': {
16169  $val = ($parent - 10);
16170  break;
16171  }
16172  case 'inherit': {
16173  if ($parent == 'normal') {
16174  $val = 100;
16175  } else {
16176  $val = $parent;
16177  }
16178  break;
16179  }
16180  default: {
16181  $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
16182  }
16183  }
16184  return $val;
16185  }
16186 
16196  public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') {
16197  $refsize = TCPDF_FONTS::getFontRefSize($refsize);
16198  $parent_size = TCPDF_FONTS::getFontRefSize($parent_size, $refsize);
16199  switch ($val) {
16200  case 'xx-small': {
16201  $size = ($refsize - 4);
16202  break;
16203  }
16204  case 'x-small': {
16205  $size = ($refsize - 3);
16206  break;
16207  }
16208  case 'small': {
16209  $size = ($refsize - 2);
16210  break;
16211  }
16212  case 'medium': {
16213  $size = $refsize;
16214  break;
16215  }
16216  case 'large': {
16217  $size = ($refsize + 2);
16218  break;
16219  }
16220  case 'x-large': {
16221  $size = ($refsize + 4);
16222  break;
16223  }
16224  case 'xx-large': {
16225  $size = ($refsize + 6);
16226  break;
16227  }
16228  case 'smaller': {
16229  $size = ($parent_size - 3);
16230  break;
16231  }
16232  case 'larger': {
16233  $size = ($parent_size + 3);
16234  break;
16235  }
16236  default: {
16237  $size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true);
16238  }
16239  }
16240  return $size;
16241  }
16242 
16250  protected function getHtmlDomArray($html) {
16251  // array of CSS styles ( selector => properties).
16252  $css = array();
16253  // get CSS array defined at previous call
16254  $matches = array();
16255  if (preg_match_all('/<cssarray>([^<]*)<\/cssarray>/isU', $html, $matches) > 0) {
16256  if (isset($matches[1][0])) {
16257  $css = array_merge($css, json_decode($this->unhtmlentities($matches[1][0]), true));
16258  }
16259  $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
16260  }
16261  // extract external CSS files
16262  $matches = array();
16263  if (preg_match_all('/<link([^>]*)>/isU', $html, $matches) > 0) {
16264  foreach ($matches[1] as $key => $link) {
16265  $type = array();
16266  if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
16267  $type = array();
16268  preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
16269  // get 'all' and 'print' media, other media types are discarded
16270  // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16271  if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16272  $type = array();
16273  if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
16274  // read CSS data file
16275  $cssdata = TCPDF_STATIC::fileGetContents(trim($type[1]));
16276  if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) {
16277  $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16278  }
16279  }
16280  }
16281  }
16282  }
16283  }
16284  // extract style tags
16285  $matches = array();
16286  if (preg_match_all('/<style([^>]*)>([^<]*)<\/style>/isU', $html, $matches) > 0) {
16287  foreach ($matches[1] as $key => $media) {
16288  $type = array();
16289  preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
16290  // get 'all' and 'print' media, other media types are discarded
16291  // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16292  if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16293  $cssdata = $matches[2][$key];
16294  $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16295  }
16296  }
16297  }
16298  // create a special tag to contain the CSS array (used for table content)
16299  $csstagarray = '<cssarray>'.htmlentities(json_encode($css)).'</cssarray>';
16300  // remove head and style blocks
16301  $html = preg_replace('/<head([^>]*)>(.*?)<\/head>/siU', '', $html);
16302  $html = preg_replace('/<style([^>]*)>([^<]*)<\/style>/isU', '', $html);
16303  // define block tags
16304  $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
16305  // define self-closing tags
16306  $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
16307  // remove all unsupported tags (the line below lists all supported tags)
16308  $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>');
16309  //replace some blank characters
16310  $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
16311  $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);
16312  $html = preg_replace('@(\r\n|\r)@', "\n", $html);
16313  $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
16314  $html = strtr($html, $repTable);
16315  $offset = 0;
16316  while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
16317  $html_a = substr($html, 0, $offset);
16318  $html_b = substr($html, $offset, ($pos - $offset + 6));
16319  while (preg_match("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
16320  // preserve newlines on <pre> tag
16321  $html_b = preg_replace("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
16322  }
16323  while (preg_match("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) {
16324  // preserve spaces on <pre> tag
16325  $html_b = preg_replace("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2&nbsp;\\3</pre>", $html_b);
16326  }
16327  $html = $html_a.$html_b.substr($html, $pos + 6);
16328  $offset = strlen($html_a.$html_b);
16329  }
16330  $offset = 0;
16331  while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
16332  $html_a = substr($html, 0, $offset);
16333  $html_b = substr($html, $offset, ($pos - $offset + 11));
16334  while (preg_match("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
16335  // preserve newlines on <textarea> tag
16336  $html_b = preg_replace("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
16337  $html_b = preg_replace("'<textarea([^>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
16338  }
16339  $html = $html_a.$html_b.substr($html, $pos + 11);
16340  $offset = strlen($html_a.$html_b);
16341  }
16342  $html = preg_replace('/([\s]*)<option/si', '<option', $html);
16343  $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
16344  $offset = 0;
16345  while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
16346  $html_a = substr($html, 0, $offset);
16347  $html_b = substr($html, $offset, ($pos - $offset + 9));
16348  while (preg_match("'<option([^>]*)>(.*?)</option>'si", $html_b)) {
16349  $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
16350  $html_b = preg_replace("'<option([^>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
16351  }
16352  $html = $html_a.$html_b.substr($html, $pos + 9);
16353  $offset = strlen($html_a.$html_b);
16354  }
16355  if (preg_match("'</select'si", $html)) {
16356  $html = preg_replace("'<select([^>]*)>'si", "<select\\1 opt=\"", $html);
16357  $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
16358  }
16359  $html = str_replace("\n", ' ', $html);
16360  // restore textarea newlines
16361  $html = str_replace('<TBR>', "\n", $html);
16362  // remove extra spaces from code
16363  $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
16364  $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html);
16365  $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
16366  $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html);
16367  $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);
16368  $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
16369  $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
16370  $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html);
16371  $html = preg_replace('/<img([^>]*)>[\s]+([^<])/xi', '<img\\1>&nbsp;\\2', $html);
16372  $html = preg_replace('/<img([^>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
16373  $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
16374  $html = preg_replace('/<textarea([^>]*)>([^<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
16375  $html = preg_replace('/<li([^>]*)><\/li>/', '<li\\1>&nbsp;</li>', $html);
16376  $html = preg_replace('/<li([^>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1">&nbsp;</font><img', $html);
16377  $html = preg_replace('/<([^>\/]*)>[\s]/', '<\\1>&nbsp;', $html); // preserve some spaces
16378  $html = preg_replace('/[\s]<\/([^>]*)>/', '&nbsp;</\\1>', $html); // preserve some spaces
16379  $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment
16380  $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment
16381  $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space
16382  // trim string
16383  $html = $this->stringTrim($html);
16384  // fix br tag after li
16385  $html = preg_replace('/<li><br([^>]*)>/', '<li> <br\\1>', $html);
16386  // fix first image tag alignment
16387  $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
16388  // pattern for generic tag
16389  $tagpattern = '/(<[^>]+>)/';
16390  // explodes the string
16391  $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
16392  // count elements
16393  $maxel = count($a);
16394  $elkey = 0;
16395  $key = 0;
16396  // create an array of elements
16397  $dom = array();
16398  $dom[$key] = array();
16399  // set inheritable properties fot the first void element
16400  // 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
16401  $dom[$key]['tag'] = false;
16402  $dom[$key]['block'] = false;
16403  $dom[$key]['value'] = '';
16404  $dom[$key]['parent'] = 0;
16405  $dom[$key]['hide'] = false;
16406  $dom[$key]['fontname'] = $this->FontFamily;
16407  $dom[$key]['fontstyle'] = $this->FontStyle;
16408  $dom[$key]['fontsize'] = $this->FontSizePt;
16409  $dom[$key]['font-stretch'] = $this->font_stretching;
16410  $dom[$key]['letter-spacing'] = $this->font_spacing;
16411  $dom[$key]['stroke'] = $this->textstrokewidth;
16412  $dom[$key]['fill'] = (($this->textrendermode % 2) == 0);
16413  $dom[$key]['clip'] = ($this->textrendermode > 3);
16414  $dom[$key]['line-height'] = $this->cell_height_ratio;
16415  $dom[$key]['bgcolor'] = false;
16416  $dom[$key]['fgcolor'] = $this->fgcolor; // color
16417  $dom[$key]['strokecolor'] = $this->strokecolor;
16418  $dom[$key]['align'] = '';
16419  $dom[$key]['listtype'] = '';
16420  $dom[$key]['text-indent'] = 0;
16421  $dom[$key]['text-transform'] = '';
16422  $dom[$key]['border'] = array();
16423  $dom[$key]['dir'] = $this->rtl?'rtl':'ltr';
16424  $thead = false; // true when we are inside the THEAD tag
16425  ++$key;
16426  $level = array();
16427  array_push($level, 0); // root
16428  while ($elkey < $maxel) {
16429  $dom[$key] = array();
16430  $element = $a[$elkey];
16431  $dom[$key]['elkey'] = $elkey;
16432  if (preg_match($tagpattern, $element)) {
16433  // html tag
16434  $element = substr($element, 1, -1);
16435  // get tag name
16436  preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
16437  $tagname = strtolower($tag[1]);
16438  // check if we are inside a table header
16439  if ($tagname == 'thead') {
16440  if ($element[0] == '/') {
16441  $thead = false;
16442  } else {
16443  $thead = true;
16444  }
16445  ++$elkey;
16446  continue;
16447  }
16448  $dom[$key]['tag'] = true;
16449  $dom[$key]['value'] = $tagname;
16450  if (in_array($dom[$key]['value'], $blocktags)) {
16451  $dom[$key]['block'] = true;
16452  } else {
16453  $dom[$key]['block'] = false;
16454  }
16455  if ($element[0] == '/') {
16456  // *** closing html tag
16457  $dom[$key]['opening'] = false;
16458  $dom[$key]['parent'] = end($level);
16459  array_pop($level);
16460  $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
16461  $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
16462  $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
16463  $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
16464  $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
16465  $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
16466  $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
16467  $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
16468  $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
16469  $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
16470  $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
16471  $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
16472  $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
16473  $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
16474  $dom[$key]['text-transform'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['text-transform'];
16475  $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
16476  if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
16477  $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
16478  }
16479  // set the number of columns in table tag
16480  if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
16481  $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
16482  }
16483  if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
16484  $dom[($dom[$key]['parent'])]['content'] = $csstagarray;
16485  for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
16486  $dom[($dom[$key]['parent'])]['content'] .= stripslashes($a[$dom[$i]['elkey']]);
16487  }
16488  $key = $i;
16489  // mark nested tables
16490  $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
16491  // remove thead sections from nested tables
16492  $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
16493  $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
16494  }
16495  // store header rows on a new table
16496  if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
16497  if (TCPDF_STATIC::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
16498  $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
16499  }
16500  for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
16501  $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
16502  }
16503  if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
16504  $dom[($dom[$key]['parent'])]['attribute'] = array();
16505  }
16506  // header elements must be always contained in a single page
16507  $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
16508  }
16509  if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC::empty_string($dom[($dom[$key]['parent'])]['thead']))) {
16510  // remove the nobr attributes from the table header
16511  $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
16512  $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
16513  }
16514  } else {
16515  // *** opening or self-closing html tag
16516  $dom[$key]['opening'] = true;
16517  $dom[$key]['parent'] = end($level);
16518  if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
16519  // self-closing tag
16520  $dom[$key]['self'] = true;
16521  } else {
16522  // opening tag
16523  array_push($level, $key);
16524  $dom[$key]['self'] = false;
16525  }
16526  // copy some values from parent
16527  $parentkey = 0;
16528  if ($key > 0) {
16529  $parentkey = $dom[$key]['parent'];
16530  $dom[$key]['hide'] = $dom[$parentkey]['hide'];
16531  $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
16532  $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
16533  $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
16534  $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
16535  $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
16536  $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
16537  $dom[$key]['fill'] = $dom[$parentkey]['fill'];
16538  $dom[$key]['clip'] = $dom[$parentkey]['clip'];
16539  $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16540  $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
16541  $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
16542  $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
16543  $dom[$key]['align'] = $dom[$parentkey]['align'];
16544  $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16545  $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16546  $dom[$key]['text-transform'] = $dom[$parentkey]['text-transform'];
16547  $dom[$key]['border'] = array();
16548  $dom[$key]['dir'] = $dom[$parentkey]['dir'];
16549  }
16550  // get attributes
16551  preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER);
16552  $dom[$key]['attribute'] = array(); // reset attribute array
16553  while (list($id, $name) = each($attr_array[1])) {
16554  $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
16555  }
16556  if (!empty($css)) {
16557  // merge CSS style to current style
16558  list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC::getCSSdataArray($dom, $key, $css);
16559  $dom[$key]['attribute']['style'] = TCPDF_STATIC::getTagStyleFromCSSarray($dom[$key]['cssdata']);
16560  }
16561  // split style attributes
16562  if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
16563  // get style attributes
16564  preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
16565  $dom[$key]['style'] = array(); // reset style attribute array
16566  while (list($id, $name) = each($style_array[1])) {
16567  // in case of duplicate attribute the last replace the previous
16568  $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
16569  }
16570  // --- get some style attributes ---
16571  // text direction
16572  if (isset($dom[$key]['style']['direction'])) {
16573  $dom[$key]['dir'] = $dom[$key]['style']['direction'];
16574  }
16575  // display
16576  if (isset($dom[$key]['style']['display'])) {
16577  $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
16578  }
16579  // font family
16580  if (isset($dom[$key]['style']['font-family'])) {
16581  $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
16582  }
16583  // list-style-type
16584  if (isset($dom[$key]['style']['list-style-type'])) {
16585  $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
16586  if ($dom[$key]['listtype'] == 'inherit') {
16587  $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16588  }
16589  }
16590  // text-indent
16591  if (isset($dom[$key]['style']['text-indent'])) {
16592  $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
16593  if ($dom[$key]['text-indent'] == 'inherit') {
16594  $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16595  }
16596  }
16597  // text-transform
16598  if (isset($dom[$key]['style']['text-transform'])) {
16599  $dom[$key]['text-transform'] = $dom[$key]['style']['text-transform'];
16600  }
16601  // font size
16602  if (isset($dom[$key]['style']['font-size'])) {
16603  $fsize = trim($dom[$key]['style']['font-size']);
16604  $dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt');
16605  }
16606  // font-stretch
16607  if (isset($dom[$key]['style']['font-stretch'])) {
16608  $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
16609  }
16610  // letter-spacing
16611  if (isset($dom[$key]['style']['letter-spacing'])) {
16612  $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
16613  }
16614  // line-height (internally is the cell height ratio)
16615  if (isset($dom[$key]['style']['line-height'])) {
16616  $lineheight = trim($dom[$key]['style']['line-height']);
16617  switch ($lineheight) {
16618  // A normal line height. This is default
16619  case 'normal': {
16620  $dom[$key]['line-height'] = $dom[0]['line-height'];
16621  break;
16622  }
16623  case 'inherit': {
16624  $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16625  }
16626  default: {
16627  if (is_numeric($lineheight)) {
16628  // convert to percentage of font height
16629  $lineheight = ($lineheight * 100).'%';
16630  }
16631  $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
16632  if (substr($lineheight, -1) !== '%') {
16633  if ($dom[$key]['fontsize'] <= 0) {
16634  $dom[$key]['line-height'] = 1;
16635  } else {
16636  $dom[$key]['line-height'] = (($dom[$key]['line-height'] - $this->cell_padding['T'] - $this->cell_padding['B']) / $dom[$key]['fontsize']);
16637  }
16638  }
16639  }
16640  }
16641  }
16642  // font style
16643  if (isset($dom[$key]['style']['font-weight'])) {
16644  if (strtolower($dom[$key]['style']['font-weight'][0]) == 'n') {
16645  if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
16646  $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
16647  }
16648  } elseif (strtolower($dom[$key]['style']['font-weight'][0]) == 'b') {
16649  $dom[$key]['fontstyle'] .= 'B';
16650  }
16651  }
16652  if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style'][0]) == 'i')) {
16653  $dom[$key]['fontstyle'] .= 'I';
16654  }
16655  // font color
16656  if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['color']))) {
16657  $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors);
16658  } elseif ($dom[$key]['value'] == 'a') {
16659  $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16660  }
16661  // background color
16662  if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['background-color']))) {
16663  $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors);
16664  }
16665  // text-decoration
16666  if (isset($dom[$key]['style']['text-decoration'])) {
16667  $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
16668  foreach ($decors as $dec) {
16669  $dec = trim($dec);
16670  if (!TCPDF_STATIC::empty_string($dec)) {
16671  if ($dec[0] == 'u') {
16672  // underline
16673  $dom[$key]['fontstyle'] .= 'U';
16674  } elseif ($dec[0] == 'l') {
16675  // line-through
16676  $dom[$key]['fontstyle'] .= 'D';
16677  } elseif ($dec[0] == 'o') {
16678  // overline
16679  $dom[$key]['fontstyle'] .= 'O';
16680  }
16681  }
16682  }
16683  } elseif ($dom[$key]['value'] == 'a') {
16684  $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16685  }
16686  // check for width attribute
16687  if (isset($dom[$key]['style']['width'])) {
16688  $dom[$key]['width'] = $dom[$key]['style']['width'];
16689  }
16690  // check for height attribute
16691  if (isset($dom[$key]['style']['height'])) {
16692  $dom[$key]['height'] = $dom[$key]['style']['height'];
16693  }
16694  // check for text alignment
16695  if (isset($dom[$key]['style']['text-align'])) {
16696  $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align'][0]);
16697  }
16698  // check for CSS border properties
16699  if (isset($dom[$key]['style']['border'])) {
16700  $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
16701  if (!empty($borderstyle)) {
16702  $dom[$key]['border']['LTRB'] = $borderstyle;
16703  }
16704  }
16705  if (isset($dom[$key]['style']['border-color'])) {
16706  $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
16707  if (isset($brd_colors[3])) {
16708  $dom[$key]['border']['L']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[3], $this->spot_colors);
16709  }
16710  if (isset($brd_colors[1])) {
16711  $dom[$key]['border']['R']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[1], $this->spot_colors);
16712  }
16713  if (isset($brd_colors[0])) {
16714  $dom[$key]['border']['T']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[0], $this->spot_colors);
16715  }
16716  if (isset($brd_colors[2])) {
16717  $dom[$key]['border']['B']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[2], $this->spot_colors);
16718  }
16719  }
16720  if (isset($dom[$key]['style']['border-width'])) {
16721  $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
16722  if (isset($brd_widths[3])) {
16723  $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
16724  }
16725  if (isset($brd_widths[1])) {
16726  $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
16727  }
16728  if (isset($brd_widths[0])) {
16729  $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
16730  }
16731  if (isset($brd_widths[2])) {
16732  $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
16733  }
16734  }
16735  if (isset($dom[$key]['style']['border-style'])) {
16736  $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
16737  if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) {
16738  $dom[$key]['border']['L']['cap'] = 'square';
16739  $dom[$key]['border']['L']['join'] = 'miter';
16740  $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
16741  if ($dom[$key]['border']['L']['dash'] < 0) {
16742  $dom[$key]['border']['L'] = array();
16743  }
16744  }
16745  if (isset($brd_styles[1])) {
16746  $dom[$key]['border']['R']['cap'] = 'square';
16747  $dom[$key]['border']['R']['join'] = 'miter';
16748  $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
16749  if ($dom[$key]['border']['R']['dash'] < 0) {
16750  $dom[$key]['border']['R'] = array();
16751  }
16752  }
16753  if (isset($brd_styles[0])) {
16754  $dom[$key]['border']['T']['cap'] = 'square';
16755  $dom[$key]['border']['T']['join'] = 'miter';
16756  $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
16757  if ($dom[$key]['border']['T']['dash'] < 0) {
16758  $dom[$key]['border']['T'] = array();
16759  }
16760  }
16761  if (isset($brd_styles[2])) {
16762  $dom[$key]['border']['B']['cap'] = 'square';
16763  $dom[$key]['border']['B']['join'] = 'miter';
16764  $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
16765  if ($dom[$key]['border']['B']['dash'] < 0) {
16766  $dom[$key]['border']['B'] = array();
16767  }
16768  }
16769  }
16770  $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
16771  foreach ($cellside as $bsk => $bsv) {
16772  if (isset($dom[$key]['style']['border-'.$bsv])) {
16773  $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
16774  if (!empty($borderstyle)) {
16775  $dom[$key]['border'][$bsk] = $borderstyle;
16776  }
16777  }
16778  if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
16779  $dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors);
16780  }
16781  if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
16782  $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
16783  }
16784  if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
16785  $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
16786  if ($dom[$key]['border'][$bsk]['dash'] < 0) {
16787  $dom[$key]['border'][$bsk] = array();
16788  }
16789  }
16790  }
16791  // check for CSS padding properties
16792  if (isset($dom[$key]['style']['padding'])) {
16793  $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
16794  } else {
16795  $dom[$key]['padding'] = $this->cell_padding;
16796  }
16797  foreach ($cellside as $psk => $psv) {
16798  if (isset($dom[$key]['style']['padding-'.$psv])) {
16799  $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
16800  }
16801  }
16802  // check for CSS margin properties
16803  if (isset($dom[$key]['style']['margin'])) {
16804  $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
16805  } else {
16806  $dom[$key]['margin'] = $this->cell_margin;
16807  }
16808  foreach ($cellside as $psk => $psv) {
16809  if (isset($dom[$key]['style']['margin-'.$psv])) {
16810  $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
16811  }
16812  }
16813  // check for CSS border-spacing properties
16814  if (isset($dom[$key]['style']['border-spacing'])) {
16815  $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
16816  }
16817  // page-break-inside
16818  if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
16819  $dom[$key]['attribute']['nobr'] = 'true';
16820  }
16821  // page-break-before
16822  if (isset($dom[$key]['style']['page-break-before'])) {
16823  if ($dom[$key]['style']['page-break-before'] == 'always') {
16824  $dom[$key]['attribute']['pagebreak'] = 'true';
16825  } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
16826  $dom[$key]['attribute']['pagebreak'] = 'left';
16827  } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
16828  $dom[$key]['attribute']['pagebreak'] = 'right';
16829  }
16830  }
16831  // page-break-after
16832  if (isset($dom[$key]['style']['page-break-after'])) {
16833  if ($dom[$key]['style']['page-break-after'] == 'always') {
16834  $dom[$key]['attribute']['pagebreakafter'] = 'true';
16835  } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
16836  $dom[$key]['attribute']['pagebreakafter'] = 'left';
16837  } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
16838  $dom[$key]['attribute']['pagebreakafter'] = 'right';
16839  }
16840  }
16841  }
16842  if (isset($dom[$key]['attribute']['display'])) {
16843  $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
16844  }
16845  if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
16846  $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
16847  if (!empty($borderstyle)) {
16848  $dom[$key]['border']['LTRB'] = $borderstyle;
16849  }
16850  }
16851  // check for font tag
16852  if ($dom[$key]['value'] == 'font') {
16853  // font family
16854  if (isset($dom[$key]['attribute']['face'])) {
16855  $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
16856  }
16857  // font size
16858  if (isset($dom[$key]['attribute']['size'])) {
16859  if ($key > 0) {
16860  if ($dom[$key]['attribute']['size'][0] == '+') {
16861  $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
16862  } elseif ($dom[$key]['attribute']['size'][0] == '-') {
16863  $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
16864  } else {
16865  $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16866  }
16867  } else {
16868  $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16869  }
16870  }
16871  }
16872  // force natural alignment for lists
16873  if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
16874  AND (!isset($dom[$key]['align']) OR TCPDF_STATIC::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
16875  if ($this->rtl) {
16876  $dom[$key]['align'] = 'R';
16877  } else {
16878  $dom[$key]['align'] = 'L';
16879  }
16880  }
16881  if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
16882  if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16883  $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
16884  }
16885  }
16886  if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
16887  $dom[$key]['fontstyle'] .= 'B';
16888  }
16889  if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
16890  $dom[$key]['fontstyle'] .= 'I';
16891  }
16892  if ($dom[$key]['value'] == 'u') {
16893  $dom[$key]['fontstyle'] .= 'U';
16894  }
16895  if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
16896  $dom[$key]['fontstyle'] .= 'D';
16897  }
16898  if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
16899  $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16900  }
16901  if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
16902  $dom[$key]['fontname'] = $this->default_monospaced_font;
16903  }
16904  if (!empty($dom[$key]['value']) AND ($dom[$key]['value'][0] == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
16905  // headings h1, h2, h3, h4, h5, h6
16906  if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16907  $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
16908  $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
16909  }
16910  if (!isset($dom[$key]['style']['font-weight'])) {
16911  $dom[$key]['fontstyle'] .= 'B';
16912  }
16913  }
16914  if (($dom[$key]['value'] == 'table')) {
16915  $dom[$key]['rows'] = 0; // number of rows
16916  $dom[$key]['trids'] = array(); // IDs of TR elements
16917  $dom[$key]['thead'] = ''; // table header rows
16918  }
16919  if (($dom[$key]['value'] == 'tr')) {
16920  $dom[$key]['cols'] = 0;
16921  if ($thead) {
16922  $dom[$key]['thead'] = true;
16923  // rows on thead block are printed as a separate table
16924  } else {
16925  $dom[$key]['thead'] = false;
16926  // store the number of rows on table element
16927  ++$dom[($dom[$key]['parent'])]['rows'];
16928  // store the TR elements IDs on table element
16929  array_push($dom[($dom[$key]['parent'])]['trids'], $key);
16930  }
16931  }
16932  if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
16933  if (isset($dom[$key]['attribute']['colspan'])) {
16934  $colspan = intval($dom[$key]['attribute']['colspan']);
16935  } else {
16936  $colspan = 1;
16937  }
16938  $dom[$key]['attribute']['colspan'] = $colspan;
16939  $dom[($dom[$key]['parent'])]['cols'] += $colspan;
16940  }
16941  // text direction
16942  if (isset($dom[$key]['attribute']['dir'])) {
16943  $dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
16944  }
16945  // set foreground color attribute
16946  if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['color']))) {
16947  $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors);
16948  } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
16949  $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16950  }
16951  // set background color attribute
16952  if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['bgcolor']))) {
16953  $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors);
16954  }
16955  // set stroke color attribute
16956  if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['strokecolor']))) {
16957  $dom[$key]['strokecolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors);
16958  }
16959  // check for width attribute
16960  if (isset($dom[$key]['attribute']['width'])) {
16961  $dom[$key]['width'] = $dom[$key]['attribute']['width'];
16962  }
16963  // check for height attribute
16964  if (isset($dom[$key]['attribute']['height'])) {
16965  $dom[$key]['height'] = $dom[$key]['attribute']['height'];
16966  }
16967  // check for text alignment
16968  if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
16969  $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align'][0]);
16970  }
16971  // check for text rendering mode (the following attributes do not exist in HTML)
16972  if (isset($dom[$key]['attribute']['stroke'])) {
16973  // font stroke width
16974  $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
16975  }
16976  if (isset($dom[$key]['attribute']['fill'])) {
16977  // font fill
16978  if ($dom[$key]['attribute']['fill'] == 'true') {
16979  $dom[$key]['fill'] = true;
16980  } else {
16981  $dom[$key]['fill'] = false;
16982  }
16983  }
16984  if (isset($dom[$key]['attribute']['clip'])) {
16985  // clipping mode
16986  if ($dom[$key]['attribute']['clip'] == 'true') {
16987  $dom[$key]['clip'] = true;
16988  } else {
16989  $dom[$key]['clip'] = false;
16990  }
16991  }
16992  } // end opening tag
16993  } else {
16994  // text
16995  $dom[$key]['tag'] = false;
16996  $dom[$key]['block'] = false;
16997  $dom[$key]['parent'] = end($level);
16998  $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
16999  if (!empty($dom[$dom[$key]['parent']]['text-transform'])) {
17000  // text-transform for unicode requires mb_convert_case (Multibyte String Functions)
17001  if (function_exists('mb_convert_case')) {
17002  $ttm = array('capitalize' => MB_CASE_TITLE, 'uppercase' => MB_CASE_UPPER, 'lowercase' => MB_CASE_LOWER);
17003  if (isset($ttm[$dom[$dom[$key]['parent']]['text-transform']])) {
17004  $element = mb_convert_case($element, $ttm[$dom[$dom[$key]['parent']]['text-transform']], $this->encoding);
17005  }
17006  } elseif (!$this->isunicode) {
17007  switch ($dom[$dom[$key]['parent']]['text-transform']) {
17008  case 'capitalize': {
17009  $element = ucwords(strtolower($element));
17010  break;
17011  }
17012  case 'uppercase': {
17013  $element = strtoupper($element);
17014  break;
17015  }
17016  case 'lowercase': {
17017  $element = strtolower($element);
17018  break;
17019  }
17020  }
17021  }
17022  }
17023  $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
17024  }
17025  ++$elkey;
17026  ++$key;
17027  }
17028  return $dom;
17029  }
17030 
17038  protected function getSpaceString() {
17039  $spacestr = chr(32);
17040  if ($this->isUnicodeFont()) {
17041  $spacestr = chr(0).chr(32);
17042  }
17043  return $spacestr;
17044  }
17045 
17052  protected function getHashForTCPDFtagParams($data) {
17053  return md5(strlen($data).$this->file_id.$data);
17054  }
17055 
17063  $encoded = urlencode(json_encode($data));
17064  return $this->getHashForTCPDFtagParams($encoded).$encoded;
17065  }
17066 
17073  protected function unserializeTCPDFtagParameters($data) {
17074  $hash = substr($data, 0, 32);
17075  $encoded = substr($data, 32);
17076  if ($hash != $this->getHashForTCPDFtagParams($encoded)) {
17077  $this->Error('Invalid parameters');
17078  }
17079  return json_decode(urldecode($encoded), true);
17080  }
17081 
17104  public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
17105  return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
17106  }
17107 
17121  public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
17122  $gvars = $this->getGraphicVars();
17123  // store current values
17124  $prev_cell_margin = $this->cell_margin;
17125  $prev_cell_padding = $this->cell_padding;
17126  $prevPage = $this->page;
17127  $prevlMargin = $this->lMargin;
17128  $prevrMargin = $this->rMargin;
17129  $curfontname = $this->FontFamily;
17130  $curfontstyle = $this->FontStyle;
17131  $curfontsize = $this->FontSizePt;
17132  $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
17133  $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
17134  $curfontstretcing = $this->font_stretching;
17135  $curfonttracking = $this->font_spacing;
17136  $this->newline = true;
17137  $newline = true;
17138  $startlinepage = $this->page;
17139  $minstartliney = $this->y;
17140  $maxbottomliney = 0;
17141  $startlinex = $this->x;
17142  $startliney = $this->y;
17143  $yshift = 0;
17144  $loop = 0;
17145  $curpos = 0;
17146  $this_method_vars = array();
17147  $undo = false;
17148  $fontaligned = false;
17149  $reverse_dir = false; // true when the text direction is reversed
17150  $this->premode = false;
17151  if ($this->inxobj) {
17152  // we are inside an XObject template
17153  $pask = count($this->xobjects[$this->xobjid]['annotations']);
17154  } elseif (isset($this->PageAnnots[$this->page])) {
17155  $pask = count($this->PageAnnots[$this->page]);
17156  } else {
17157  $pask = 0;
17158  }
17159  if ($this->inxobj) {
17160  // we are inside an XObject template
17161  $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17162  } elseif (!$this->InFooter) {
17163  if (isset($this->footerlen[$this->page])) {
17164  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17165  } else {
17166  $this->footerpos[$this->page] = $this->pagelen[$this->page];
17167  }
17168  $startlinepos = $this->footerpos[$this->page];
17169  } else {
17170  // we are inside the footer
17171  $startlinepos = $this->pagelen[$this->page];
17172  }
17173  $lalign = $align;
17174  $plalign = $align;
17175  if ($this->rtl) {
17176  $w = $this->x - $this->lMargin;
17177  } else {
17178  $w = $this->w - $this->rMargin - $this->x;
17179  }
17180  $w -= ($this->cell_padding['L'] + $this->cell_padding['R']);
17181  if ($cell) {
17182  if ($this->rtl) {
17183  $this->x -= $this->cell_padding['R'];
17184  $this->lMargin += $this->cell_padding['R'];
17185  } else {
17186  $this->x += $this->cell_padding['L'];
17187  $this->rMargin += $this->cell_padding['L'];
17188  }
17189  }
17190  if ($this->customlistindent >= 0) {
17191  $this->listindent = $this->customlistindent;
17192  } else {
17193  $this->listindent = $this->GetStringWidth('000000');
17194  }
17195  $this->listindentlevel = 0;
17196  // save previous states
17197  $prev_cell_height_ratio = $this->cell_height_ratio;
17198  $prev_listnum = $this->listnum;
17199  $prev_listordered = $this->listordered;
17200  $prev_listcount = $this->listcount;
17201  $prev_lispacer = $this->lispacer;
17202  $this->listnum = 0;
17203  $this->listordered = array();
17204  $this->listcount = array();
17205  $this->lispacer = '';
17206  if ((TCPDF_STATIC::empty_string($this->lasth)) OR ($reseth)) {
17207  // reset row height
17208  $this->resetLastH();
17209  }
17210  $dom = $this->getHtmlDomArray($html);
17211  $maxel = count($dom);
17212  $key = 0;
17213  while ($key < $maxel) {
17214  if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND $dom[$key]['hide']) {
17215  // store the node key
17216  $hidden_node_key = $key;
17217  if ($dom[$key]['self']) {
17218  // skip just this self-closing tag
17219  ++$key;
17220  } else {
17221  // skip this and all children tags
17222  while (($key < $maxel) AND (!$dom[$key]['tag'] OR $dom[$key]['opening'] OR ($dom[$key]['parent'] != $hidden_node_key))) {
17223  // skip hidden objects
17224  ++$key;
17225  }
17226  ++$key;
17227  }
17228  }
17229  if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
17230  // check for pagebreak
17231  if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
17232  // add a page (or trig AcceptPageBreak() for multicolumn mode)
17233  $this->checkPageBreak($this->PageBreakTrigger + 1);
17234  $this->htmlvspace = ($this->PageBreakTrigger + 1);
17235  }
17236  if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
17237  OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
17238  // add a page (or trig AcceptPageBreak() for multicolumn mode)
17239  $this->checkPageBreak($this->PageBreakTrigger + 1);
17240  $this->htmlvspace = ($this->PageBreakTrigger + 1);
17241  }
17242  }
17243  if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
17244  if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
17245  $dom[$key]['attribute']['nobr'] = false;
17246  } else {
17247  // store current object
17248  $this->startTransaction();
17249  // save this method vars
17250  $this_method_vars['html'] = $html;
17251  $this_method_vars['ln'] = $ln;
17252  $this_method_vars['fill'] = $fill;
17253  $this_method_vars['reseth'] = $reseth;
17254  $this_method_vars['cell'] = $cell;
17255  $this_method_vars['align'] = $align;
17256  $this_method_vars['gvars'] = $gvars;
17257  $this_method_vars['prevPage'] = $prevPage;
17258  $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
17259  $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
17260  $this_method_vars['prevlMargin'] = $prevlMargin;
17261  $this_method_vars['prevrMargin'] = $prevrMargin;
17262  $this_method_vars['curfontname'] = $curfontname;
17263  $this_method_vars['curfontstyle'] = $curfontstyle;
17264  $this_method_vars['curfontsize'] = $curfontsize;
17265  $this_method_vars['curfontascent'] = $curfontascent;
17266  $this_method_vars['curfontdescent'] = $curfontdescent;
17267  $this_method_vars['curfontstretcing'] = $curfontstretcing;
17268  $this_method_vars['curfonttracking'] = $curfonttracking;
17269  $this_method_vars['minstartliney'] = $minstartliney;
17270  $this_method_vars['maxbottomliney'] = $maxbottomliney;
17271  $this_method_vars['yshift'] = $yshift;
17272  $this_method_vars['startlinepage'] = $startlinepage;
17273  $this_method_vars['startlinepos'] = $startlinepos;
17274  $this_method_vars['startlinex'] = $startlinex;
17275  $this_method_vars['startliney'] = $startliney;
17276  $this_method_vars['newline'] = $newline;
17277  $this_method_vars['loop'] = $loop;
17278  $this_method_vars['curpos'] = $curpos;
17279  $this_method_vars['pask'] = $pask;
17280  $this_method_vars['lalign'] = $lalign;
17281  $this_method_vars['plalign'] = $plalign;
17282  $this_method_vars['w'] = $w;
17283  $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
17284  $this_method_vars['prev_listnum'] = $prev_listnum;
17285  $this_method_vars['prev_listordered'] = $prev_listordered;
17286  $this_method_vars['prev_listcount'] = $prev_listcount;
17287  $this_method_vars['prev_lispacer'] = $prev_lispacer;
17288  $this_method_vars['fontaligned'] = $fontaligned;
17289  $this_method_vars['key'] = $key;
17290  $this_method_vars['dom'] = $dom;
17291  }
17292  }
17293  // print THEAD block
17294  if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
17295  if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC::empty_string($dom[$dom[$key]['parent']]['thead'])) {
17296  $this->inthead = true;
17297  // print table header (thead)
17298  $this->writeHTML($this->thead, false, false, false, false, '');
17299  // check if we are on a new page or on a new column
17300  if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
17301  // we are on a new page or on a new column and the total object height is less than the available vertical space.
17302  // restore previous object
17303  $this->rollbackTransaction(true);
17304  // restore previous values
17305  foreach ($this_method_vars as $vkey => $vval) {
17306  $$vkey = $vval;
17307  }
17308  // disable table header
17309  $tmp_thead = $this->thead;
17310  $this->thead = '';
17311  // add a page (or trig AcceptPageBreak() for multicolumn mode)
17312  $pre_y = $this->y;
17313  if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
17314  // fix for multicolumn mode
17315  $startliney = $this->y;
17316  }
17317  $this->start_transaction_page = $this->page;
17318  $this->start_transaction_y = $this->y;
17319  // restore table header
17320  $this->thead = $tmp_thead;
17321  // fix table border properties
17322  if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
17323  $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
17324  } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
17325  $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
17326  } else {
17327  $tmp_cellspacing = 0;
17328  }
17329  $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
17330  $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
17331  $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
17332  $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']);
17333  $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
17334  $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
17335  // print table header (thead)
17336  $this->writeHTML($this->thead, false, false, false, false, '');
17337  }
17338  }
17339  // move $key index forward to skip THEAD block
17340  while ( ($key < $maxel) AND (!(
17341  ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
17342  OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
17343  ++$key;
17344  }
17345  }
17346  if ($dom[$key]['tag'] OR ($key == 0)) {
17347  if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
17348  $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
17349  }
17350  // vertically align image in line
17351  if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
17352  // get image height
17353  $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], ($dom[$key]['fontsize'] / $this->k), 'px');
17354  $autolinebreak = false;
17355  if (!empty($dom[$key]['width'])) {
17356  $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], ($dom[$key]['fontsize'] / $this->k), 'px', false);
17357  if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R']))
17358  AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L'])))
17359  OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) {
17360  // add automatic line break
17361  $autolinebreak = true;
17362  $this->Ln('', $cell);
17363  if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
17364  // go back to evaluate this line break
17365  --$key;
17366  }
17367  }
17368  }
17369  if (!$autolinebreak) {
17370  if ($this->inPageBody()) {
17371  $pre_y = $this->y;
17372  // check for page break
17373  if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
17374  // fix for multicolumn mode
17375  $startliney = $this->y;
17376  }
17377  }
17378  if ($this->page > $startlinepage) {
17379  // fix line splitted over two pages
17380  if (isset($this->footerlen[$startlinepage])) {
17381  $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17382  }
17383  // line to be moved one page forward
17384  $pagebuff = $this->getPageBuffer($startlinepage);
17385  $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17386  $tstart = substr($pagebuff, 0, $startlinepos);
17387  $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17388  // remove line from previous page
17389  $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17390  $pagebuff = $this->getPageBuffer($this->page);
17391  $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17392  $tend = substr($pagebuff, $this->cntmrk[$this->page]);
17393  // add line start to current page
17394  $yshift = ($minstartliney - $this->y);
17395  if ($fontaligned) {
17396  $yshift += ($curfontsize / $this->k);
17397  }
17398  $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17399  $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17400  // shift the annotations and links
17401  if (isset($this->PageAnnots[$this->page])) {
17402  $next_pask = count($this->PageAnnots[$this->page]);
17403  } else {
17404  $next_pask = 0;
17405  }
17406  if (isset($this->PageAnnots[$startlinepage])) {
17407  foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17408  if ($pak >= $pask) {
17409  $this->PageAnnots[$this->page][] = $pac;
17410  unset($this->PageAnnots[$startlinepage][$pak]);
17411  $npak = count($this->PageAnnots[$this->page]) - 1;
17412  $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17413  }
17414  }
17415  }
17416  $pask = $next_pask;
17417  $startlinepos = $this->cntmrk[$this->page];
17418  $startlinepage = $this->page;
17419  $startliney = $this->y;
17420  $this->newline = false;
17421  }
17422  $this->y += ($this->getCellHeight($curfontsize / $this->k) - ($curfontdescent * $this->cell_height_ratio) - $imgh);
17423  $minstartliney = min($this->y, $minstartliney);
17424  $maxbottomliney = ($startliney + $this->getCellHeight($curfontsize / $this->k));
17425  }
17426  } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
17427  // account for different font size
17428  $pfontname = $curfontname;
17429  $pfontstyle = $curfontstyle;
17430  $pfontsize = $curfontsize;
17431  $fontname = (isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname);
17432  $fontstyle = (isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle);
17433  $fontsize = (isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize);
17434  $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
17435  $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
17436  if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
17437  OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17438  OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
17439  if (($key < ($maxel - 1)) AND (
17440  ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
17441  OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17442  OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize)
17443  AND ($fontsize >= 0) AND ($curfontsize >= 0)
17444  AND (($fontsize != $curfontsize) OR ($fontstyle != $curfontstyle) OR ($fontname != $curfontname)))
17445  )) {
17446  if ($this->page > $startlinepage) {
17447  // fix lines splitted over two pages
17448  if (isset($this->footerlen[$startlinepage])) {
17449  $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17450  }
17451  // line to be moved one page forward
17452  $pagebuff = $this->getPageBuffer($startlinepage);
17453  $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17454  $tstart = substr($pagebuff, 0, $startlinepos);
17455  $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17456  // remove line start from previous page
17457  $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17458  $pagebuff = $this->getPageBuffer($this->page);
17459  $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17460  $tend = substr($pagebuff, $this->cntmrk[$this->page]);
17461  // add line start to current page
17462  $yshift = ($minstartliney - $this->y);
17463  $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17464  $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17465  // shift the annotations and links
17466  if (isset($this->PageAnnots[$this->page])) {
17467  $next_pask = count($this->PageAnnots[$this->page]);
17468  } else {
17469  $next_pask = 0;
17470  }
17471  if (isset($this->PageAnnots[$startlinepage])) {
17472  foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17473  if ($pak >= $pask) {
17474  $this->PageAnnots[$this->page][] = $pac;
17475  unset($this->PageAnnots[$startlinepage][$pak]);
17476  $npak = count($this->PageAnnots[$this->page]) - 1;
17477  $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17478  }
17479  }
17480  }
17481  $pask = $next_pask;
17482  $startlinepos = $this->cntmrk[$this->page];
17483  $startlinepage = $this->page;
17484  $startliney = $this->y;
17485  }
17486  if (!isset($dom[$key]['line-height'])) {
17487  $dom[$key]['line-height'] = $this->cell_height_ratio;
17488  }
17489  if (!$dom[$key]['block']) {
17490  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']))) {
17491  $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
17492  }
17493  if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
17494  $current_line_align_data = array($key, $minstartliney, $maxbottomliney);
17495  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)))) {
17496  $minstartliney = min($this->y, $line_align_data[1]);
17497  $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $line_align_data[2]);
17498  } else {
17499  $minstartliney = min($this->y, $minstartliney);
17500  $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $maxbottomliney);
17501  }
17502  $line_align_data = $current_line_align_data;
17503  }
17504  }
17505  $this->cell_height_ratio = $dom[$key]['line-height'];
17506  $fontaligned = true;
17507  }
17508  $this->SetFont($fontname, $fontstyle, $fontsize);
17509  // reset row height
17510  $this->resetLastH();
17511  $curfontname = $fontname;
17512  $curfontstyle = $fontstyle;
17513  $curfontsize = $fontsize;
17514  $curfontascent = $fontascent;
17515  $curfontdescent = $fontdescent;
17516  }
17517  }
17518  // set text rendering mode
17519  $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
17520  $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
17521  $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
17522  $this->setTextRenderingMode($textstroke, $textfill, $textclip);
17523  if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
17524  $this->setFontStretching($dom[$key]['font-stretch']);
17525  }
17526  if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
17527  $this->setFontSpacing($dom[$key]['letter-spacing']);
17528  }
17529  if (($plalign == 'J') AND $dom[$key]['block']) {
17530  $plalign = '';
17531  }
17532  // get current position on page buffer
17533  $curpos = $this->pagelen[$startlinepage];
17534  if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
17535  $this->SetFillColorArray($dom[$key]['bgcolor']);
17536  $wfill = true;
17537  } else {
17538  $wfill = $fill | false;
17539  }
17540  if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
17541  $this->SetTextColorArray($dom[$key]['fgcolor']);
17542  }
17543  if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
17544  $this->SetDrawColorArray($dom[$key]['strokecolor']);
17545  }
17546  if (isset($dom[$key]['align'])) {
17547  $lalign = $dom[$key]['align'];
17548  }
17549  if (TCPDF_STATIC::empty_string($lalign)) {
17550  $lalign = $align;
17551  }
17552  }
17553  // align lines
17554  if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
17555  $newline = true;
17556  $fontaligned = false;
17557  // we are at the beginning of a new line
17558  if (isset($startlinex)) {
17559  $yshift = ($minstartliney - $startliney);
17560  if (($yshift > 0) OR ($this->page > $startlinepage)) {
17561  $yshift = 0;
17562  }
17563  $t_x = 0;
17564  // the last line must be shifted to be aligned as requested
17565  $linew = abs($this->endlinex - $startlinex);
17566  if ($this->inxobj) {
17567  // we are inside an XObject template
17568  $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
17569  if (isset($opentagpos)) {
17570  $midpos = $opentagpos;
17571  } else {
17572  $midpos = 0;
17573  }
17574  if ($midpos > 0) {
17575  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
17576  $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
17577  } else {
17578  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
17579  $pend = '';
17580  }
17581  } else {
17582  $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
17583  if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17584  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17585  $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
17586  } elseif (isset($opentagpos)) {
17587  $midpos = $opentagpos;
17588  } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17589  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17590  $midpos = $this->footerpos[$startlinepage];
17591  } else {
17592  $midpos = 0;
17593  }
17594  if ($midpos > 0) {
17595  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
17596  $pend = substr($this->getPageBuffer($startlinepage), $midpos);
17597  } else {
17598  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
17599  $pend = '';
17600  }
17601  }
17602  if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
17603  // calculate shifting amount
17604  $tw = $w;
17605  if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
17606  $tw += $this->cell_padding['R'];
17607  }
17608  if ($this->lMargin != $prevlMargin) {
17609  $tw += ($prevlMargin - $this->lMargin);
17610  }
17611  if ($this->rMargin != $prevrMargin) {
17612  $tw += ($prevrMargin - $this->rMargin);
17613  }
17614  $one_space_width = $this->GetStringWidth(chr(32));
17615  $no = 0; // number of spaces on a line contained on a single block
17616  if ($this->isRTLTextDir()) { // RTL
17617  // remove left space if exist
17618  $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
17619  if ($pos1 > 0) {
17620  $pos1 = intval($pos1);
17621  if ($this->isUnicodeFont()) {
17622  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
17623  $spacelen = 2;
17624  } else {
17625  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
17626  $spacelen = 1;
17627  }
17628  if ($pos1 == $pos2) {
17629  $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
17630  if (substr($pmid, $pos1, 4) == '[()]') {
17631  $linew -= $one_space_width;
17632  } elseif ($pos1 == strpos($pmid, '[(')) {
17633  $no = 1;
17634  }
17635  }
17636  }
17637  } else { // LTR
17638  // remove right space if exist
17639  $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
17640  if ($pos1 > 0) {
17641  $pos1 = intval($pos1);
17642  if ($this->isUnicodeFont()) {
17643  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
17644  $spacelen = 2;
17645  } else {
17646  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
17647  $spacelen = 1;
17648  }
17649  if ($pos1 == $pos2) {
17650  $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
17651  $linew -= $one_space_width;
17652  }
17653  }
17654  }
17655  $mdiff = ($tw - $linew);
17656  if ($plalign == 'C') {
17657  if ($this->rtl) {
17658  $t_x = -($mdiff / 2);
17659  } else {
17660  $t_x = ($mdiff / 2);
17661  }
17662  } elseif ($plalign == 'R') {
17663  // right alignment on LTR document
17664  $t_x = $mdiff;
17665  } elseif ($plalign == 'L') {
17666  // left alignment on RTL document
17667  $t_x = -$mdiff;
17668  } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
17669  // Justification
17670  if ($this->isRTLTextDir()) {
17671  // align text on the left
17672  $t_x = -$mdiff;
17673  }
17674  $ns = 0; // number of spaces
17675  $pmidtemp = $pmid;
17676  // escape special characters
17677  $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
17678  $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
17679  // search spaces
17680  if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
17681  $spacestr = $this->getSpaceString();
17682  $maxkk = count($lnstring[1]) - 1;
17683  for ($kk=0; $kk <= $maxkk; ++$kk) {
17684  // restore special characters
17685  $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
17686  $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
17687  // store number of spaces on the strings
17688  $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
17689  // count total spaces on line
17690  $ns += $lnstring[2][$kk];
17691  $lnstring[3][$kk] = $ns;
17692  }
17693  if ($ns == 0) {
17694  $ns = 1;
17695  }
17696  // calculate additional space to add to each existing space
17697  $spacewidth = ($mdiff / ($ns - $no)) * $this->k;
17698  if ($this->FontSize <= 0) {
17699  $this->FontSize = 1;
17700  }
17701  $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize;
17702  if ($this->font_spacing != 0) {
17703  // fixed spacing mode
17704  $osw = -1000 * $this->font_spacing / $this->FontSize;
17705  $spacewidthu += $osw;
17706  }
17707  $nsmax = $ns;
17708  $ns = 0;
17709  reset($lnstring);
17710  $offset = 0;
17711  $strcount = 0;
17712  $prev_epsposbeg = 0;
17713  $textpos = 0;
17714  if ($this->isRTLTextDir()) {
17715  $textpos = $this->wPt;
17716  }
17717  while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
17718  // check if we are inside a string section '[( ... )]'
17719  $stroffset = strpos($pmid, '[(', $offset);
17720  if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
17721  // set offset to the end of string section
17722  $offset = strpos($pmid, ')]', $stroffset);
17723  while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
17724  $offset = strpos($pmid, ')]', ($offset + 1));
17725  }
17726  if ($offset === false) {
17727  $this->Error('HTML Justification: malformed PDF code.');
17728  }
17729  continue;
17730  }
17731  if ($this->isRTLTextDir()) {
17732  $spacew = ($spacewidth * ($nsmax - $ns));
17733  } else {
17734  $spacew = ($spacewidth * $ns);
17735  }
17736  $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
17737  $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset);
17738  if ($epsposend !== null) {
17739  $epsposend += strlen($this->epsmarker.'Q');
17740  $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
17741  if ($epsposbeg === null) {
17742  $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
17743  $prev_epsposbeg = $epsposbeg;
17744  }
17745  if (($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) {
17746  // shift EPS images
17747  $trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
17748  $pmid_b = substr($pmid, 0, $epsposbeg);
17749  $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
17750  $pmid_e = substr($pmid, $epsposend);
17751  $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
17752  $offset = $epsposend;
17753  continue;
17754  }
17755  }
17756  $currentxpos = 0;
17757  // shift blocks of code
17758  switch ($strpiece[2][0]) {
17759  case 'Td':
17760  case 'cm':
17761  case 'm':
17762  case 'l': {
17763  // get current X position
17764  preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
17765  if (!isset($xmatches[1])) {
17766  break;
17767  }
17768  $currentxpos = $xmatches[1];
17769  $textpos = $currentxpos;
17770  if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
17771  $ns = $lnstring[3][$strcount];
17772  if ($this->isRTLTextDir()) {
17773  $spacew = ($spacewidth * ($nsmax - $ns));
17774  }
17775  ++$strcount;
17776  }
17777  // justify block
17778  if (preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $pmatch) == 1) {
17779  $newpmid = sprintf('%F',(floatval($pmatch[1]) + $spacew)).' '.$pmatch[2].' x*#!#*x'.$pmatch[3].$pmatch[4];
17780  $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17781  unset($pmatch, $newpmid);
17782  }
17783  break;
17784  }
17785  case 're': {
17786  // justify block
17787  if (!TCPDF_STATIC::empty_string($this->lispacer)) {
17788  $this->lispacer = '';
17789  continue;
17790  }
17791  preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
17792  if (!isset($xmatches[1])) {
17793  break;
17794  }
17795  $currentxpos = $xmatches[1];
17796  $x_diff = 0;
17797  $w_diff = 0;
17798  if ($this->isRTLTextDir()) { // RTL
17799  if ($currentxpos < $textpos) {
17800  $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
17801  $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17802  } else {
17803  if ($strcount > 0) {
17804  $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
17805  $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17806  }
17807  }
17808  } else { // LTR
17809  if ($currentxpos > $textpos) {
17810  if ($strcount > 0) {
17811  $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
17812  }
17813  $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17814  } else {
17815  if ($strcount > 1) {
17816  $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
17817  }
17818  if ($strcount > 0) {
17819  $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17820  }
17821  }
17822  }
17823  if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $pmatch) == 1) {
17824  $newx = sprintf('%F',(floatval($pmatch[1]) + $x_diff));
17825  $neww = sprintf('%F',(floatval($pmatch[3]) + $w_diff));
17826  $newpmid = $newx.' '.$pmatch[2].' '.$neww.' '.$pmatch[4].' x*#!#*x'.$pmatch[5].$pmatch[6];
17827  $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17828  unset($pmatch, $newpmid, $newx, $neww);
17829  }
17830  break;
17831  }
17832  case 'c': {
17833  // get current X position
17834  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);
17835  if (!isset($xmatches[1])) {
17836  break;
17837  }
17838  $currentxpos = $xmatches[1];
17839  // justify block
17840  if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$xmatches[4].')[\s]('.$xmatches[5].')[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', $pmid, $pmatch) == 1) {
17841  $newx1 = sprintf('%F',(floatval($pmatch[1]) + $spacew));
17842  $newx2 = sprintf('%F',(floatval($pmatch[3]) + $spacew));
17843  $newx3 = sprintf('%F',(floatval($pmatch[5]) + $spacew));
17844  $newpmid = $newx1.' '.$pmatch[2].' '.$newx2.' '.$pmatch[4].' '.$newx3.' '.$pmatch[6].' x*#!#*x'.$pmatch[7].$pmatch[8];
17845  $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17846  unset($pmatch, $newpmid, $newx1, $newx2, $newx3);
17847  }
17848  break;
17849  }
17850  }
17851  // shift the annotations and links
17852  $cxpos = ($currentxpos / $this->k);
17853  $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps);
17854  if ($this->inxobj) {
17855  // we are inside an XObject template
17856  foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17857  if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17858  if ($cxpos > $lmpos) {
17859  $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k);
17860  $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17861  } else {
17862  $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17863  }
17864  break;
17865  }
17866  }
17867  } elseif (isset($this->PageAnnots[$this->page])) {
17868  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17869  if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17870  if ($cxpos > $lmpos) {
17871  $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
17872  $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17873  } else {
17874  $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17875  }
17876  break;
17877  }
17878  }
17879  }
17880  } // end of while
17881  // remove markers
17882  $pmid = str_replace('x*#!#*x', '', $pmid);
17883  if ($this->isUnicodeFont()) {
17884  // multibyte characters
17885  $spacew = $spacewidthu;
17886  if ($this->font_stretching != 100) {
17887  // word spacing is affected by stretching
17888  $spacew /= ($this->font_stretching / 100);
17889  }
17890  // escape special characters
17891  $pos = 0;
17892  $pmid = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmid);
17893  $pmid = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmid);
17894  if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmid, $pamatch) > 0) {
17895  foreach($pamatch[0] as $pk => $pmatch) {
17896  $replace = $pamatch[1][$pk];
17897  $replace = str_replace('#!#OP#!#', '(', $replace);
17898  $replace = str_replace('#!#CP#!#', ')', $replace);
17899  $newpmid = '[('.str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacew).' (', $replace).')]';
17900  $pos = strpos($pmid, $pmatch, $pos);
17901  if ($pos !== FALSE) {
17902  $pmid = substr_replace($pmid, $newpmid, $pos, strlen($pmatch));
17903  }
17904  ++$pos;
17905  }
17906  unset($pamatch);
17907  }
17908  if ($this->inxobj) {
17909  // we are inside an XObject template
17910  $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend;
17911  } else {
17912  $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
17913  }
17914  $endlinepos = strlen($pstart."\n".$pmid."\n");
17915  } else {
17916  // non-unicode (single-byte characters)
17917  if ($this->font_stretching != 100) {
17918  // word spacing (Tw) is affected by stretching
17919  $spacewidth /= ($this->font_stretching / 100);
17920  }
17921  $rs = sprintf('%F Tw', $spacewidth);
17922  $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
17923  if ($this->inxobj) {
17924  // we are inside an XObject template
17925  $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
17926  } else {
17927  $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
17928  }
17929  $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
17930  }
17931  }
17932  } // end of J
17933  } // end if $startlinex
17934  if (($t_x != 0) OR ($yshift < 0)) {
17935  // shift the line
17936  $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
17937  $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
17938  $endlinepos = strlen($pstart);
17939  if ($this->inxobj) {
17940  // we are inside an XObject template
17941  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
17942  foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17943  if ($pak >= $pask) {
17944  $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
17945  $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
17946  }
17947  }
17948  } else {
17949  $this->setPageBuffer($startlinepage, $pstart.$pend);
17950  // shift the annotations and links
17951  if (isset($this->PageAnnots[$this->page])) {
17952  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17953  if ($pak >= $pask) {
17954  $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
17955  $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
17956  }
17957  }
17958  }
17959  }
17960  $this->y -= $yshift;
17961  }
17962  }
17963  $pbrk = $this->checkPageBreak($this->lasth);
17964  $this->newline = false;
17965  $startlinex = $this->x;
17966  $startliney = $this->y;
17967  if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
17968  $startliney -= ((0.3 * $this->FontSizePt) / $this->k);
17969  } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
17970  $startliney -= (($this->FontSizePt / 0.7) / $this->k);
17971  } else {
17972  $minstartliney = $startliney;
17973  $maxbottomliney = ($this->y + $this->getCellHeight($fontsize / $this->k));
17974  }
17975  $startlinepage = $this->page;
17976  if (isset($endlinepos) AND (!$pbrk)) {
17977  $startlinepos = $endlinepos;
17978  } else {
17979  if ($this->inxobj) {
17980  // we are inside an XObject template
17981  $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17982  } elseif (!$this->InFooter) {
17983  if (isset($this->footerlen[$this->page])) {
17984  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17985  } else {
17986  $this->footerpos[$this->page] = $this->pagelen[$this->page];
17987  }
17988  $startlinepos = $this->footerpos[$this->page];
17989  } else {
17990  $startlinepos = $this->pagelen[$this->page];
17991  }
17992  }
17993  unset($endlinepos);
17994  $plalign = $lalign;
17995  if (isset($this->PageAnnots[$this->page])) {
17996  $pask = count($this->PageAnnots[$this->page]);
17997  } else {
17998  $pask = 0;
17999  }
18000  if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
18001  AND (isset($this->emptypagemrk[$this->page]))
18002  AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) {
18003  $this->SetFont($fontname, $fontstyle, $fontsize);
18004  if ($wfill) {
18005  $this->SetFillColorArray($this->bgcolor);
18006  }
18007  }
18008  } // end newline
18009  if (isset($opentagpos)) {
18010  unset($opentagpos);
18011  }
18012  if ($dom[$key]['tag']) {
18013  if ($dom[$key]['opening']) {
18014  // get text indentation (if any)
18015  if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
18016  $this->textindent = $dom[$key]['text-indent'];
18017  $this->newline = true;
18018  }
18019  // table
18020  if (($dom[$key]['value'] == 'table') AND isset($dom[$key]['cols']) AND ($dom[$key]['cols'] > 0)) {
18021  // available page width
18022  if ($this->rtl) {
18023  $wtmp = $this->x - $this->lMargin;
18024  } else {
18025  $wtmp = $this->w - $this->rMargin - $this->x;
18026  }
18027  // get cell spacing
18028  if (isset($dom[$key]['attribute']['cellspacing'])) {
18029  $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
18030  $cellspacing = array('H' => $clsp, 'V' => $clsp);
18031  } elseif (isset($dom[$key]['border-spacing'])) {
18032  $cellspacing = $dom[$key]['border-spacing'];
18033  } else {
18034  $cellspacing = array('H' => 0, 'V' => 0);
18035  }
18036  // table width
18037  if (isset($dom[$key]['width'])) {
18038  $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
18039  } else {
18040  $table_width = $wtmp;
18041  }
18042  $table_width -= (2 * $cellspacing['H']);
18043  if (!$this->inthead) {
18044  $this->y += $cellspacing['V'];
18045  }
18046  if ($this->rtl) {
18047  $cellspacingx = -$cellspacing['H'];
18048  } else {
18049  $cellspacingx = $cellspacing['H'];
18050  }
18051  // total table width without cellspaces
18052  $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
18053  // minimum column width
18054  $table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
18055  // array of custom column widths
18056  $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
18057  }
18058  // table row
18059  if ($dom[$key]['value'] == 'tr') {
18060  // reset column counter
18061  $colid = 0;
18062  }
18063  // table cell
18064  if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
18065  $trid = $dom[$key]['parent'];
18066  $table_el = $dom[$trid]['parent'];
18067  if (!isset($dom[$table_el]['cols'])) {
18068  $dom[$table_el]['cols'] = $dom[$trid]['cols'];
18069  }
18070  // store border info
18071  $tdborder = 0;
18072  if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
18073  $tdborder = $dom[$key]['border'];
18074  }
18075  $colspan = intval($dom[$key]['attribute']['colspan']);
18076  if ($colspan <= 0) {
18077  $colspan = 1;
18078  }
18079  $old_cell_padding = $this->cell_padding;
18080  if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
18081  $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
18082  $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
18083  } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
18084  $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
18085  } else {
18086  $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
18087  }
18088  $this->cell_padding = $current_cell_padding;
18089  if (isset($dom[$key]['height'])) {
18090  // minimum cell height
18091  $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
18092  } else {
18093  $cellh = 0;
18094  }
18095  if (isset($dom[$key]['content'])) {
18096  $cell_content = $dom[$key]['content'];
18097  } else {
18098  $cell_content = '&nbsp;';
18099  }
18100  $tagtype = $dom[$key]['value'];
18101  $parentid = $key;
18102  while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
18103  // move $key index forward
18104  ++$key;
18105  }
18106  if (!isset($dom[$trid]['startpage'])) {
18107  $dom[$trid]['startpage'] = $this->page;
18108  } else {
18109  $this->setPage($dom[$trid]['startpage']);
18110  }
18111  if (!isset($dom[$trid]['startcolumn'])) {
18112  $dom[$trid]['startcolumn'] = $this->current_column;
18113  } elseif ($this->current_column != $dom[$trid]['startcolumn']) {
18114  $tmpx = $this->x;
18115  $this->selectColumn($dom[$trid]['startcolumn']);
18116  $this->x = $tmpx;
18117  }
18118  if (!isset($dom[$trid]['starty'])) {
18119  $dom[$trid]['starty'] = $this->y;
18120  } else {
18121  $this->y = $dom[$trid]['starty'];
18122  }
18123  if (!isset($dom[$trid]['startx'])) {
18124  $dom[$trid]['startx'] = $this->x;
18125  $this->x += $cellspacingx;
18126  } else {
18127  $this->x += ($cellspacingx / 2);
18128  }
18129  if (isset($dom[$parentid]['attribute']['rowspan'])) {
18130  $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
18131  } else {
18132  $rowspan = 1;
18133  }
18134  // skip row-spanned cells started on the previous rows
18135  if (isset($dom[$table_el]['rowspans'])) {
18136  $rsk = 0;
18137  $rskmax = count($dom[$table_el]['rowspans']);
18138  while ($rsk < $rskmax) {
18139  $trwsp = $dom[$table_el]['rowspans'][$rsk];
18140  $rsstartx = $trwsp['startx'];
18141  $rsendx = $trwsp['endx'];
18142  // account for margin changes
18143  if ($trwsp['startpage'] < $this->page) {
18144  if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
18145  $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
18146  $rsstartx -= $dl;
18147  $rsendx -= $dl;
18148  } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
18149  $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
18150  $rsstartx += $dl;
18151  $rsendx += $dl;
18152  }
18153  }
18154  if (($trwsp['rowspan'] > 0)
18155  AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps))
18156  AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps))
18157  AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) {
18158  // set the starting X position of the current cell
18159  $this->x = $rsendx + $cellspacingx;
18160  // increment column indicator
18161  $colid += $trwsp['colspan'];
18162  if (($trwsp['rowspan'] == 1)
18163  AND (isset($dom[$trid]['endy']))
18164  AND (isset($dom[$trid]['endpage']))
18165  AND (isset($dom[$trid]['endcolumn']))
18166  AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
18167  AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18168  // set ending Y position for row
18169  $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18170  $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
18171  }
18172  $rsk = 0;
18173  } else {
18174  ++$rsk;
18175  }
18176  }
18177  }
18178  if (isset($dom[$parentid]['width'])) {
18179  // user specified width
18180  $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
18181  $tmpcw = ($cellw / $colspan);
18182  for ($i = 0; $i < $colspan; ++$i) {
18183  $table_colwidths[($colid + $i)] = $tmpcw;
18184  }
18185  } else {
18186  // inherit column width
18187  $cellw = 0;
18188  for ($i = 0; $i < $colspan; ++$i) {
18189  $cellw += (isset($table_colwidths[($colid + $i)]) ? $table_colwidths[($colid + $i)] : 0);
18190  }
18191  }
18192  $cellw += (($colspan - 1) * $cellspacing['H']);
18193  // increment column indicator
18194  $colid += $colspan;
18195  // add rowspan information to table element
18196  if ($rowspan > 1) {
18197  $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));
18198  }
18199  $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
18200  if ($rowspan > 1) {
18201  $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
18202  }
18203  // push background colors
18204  if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
18205  $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
18206  }
18207  // store border info
18208  if (isset($tdborder) AND !empty($tdborder)) {
18209  $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
18210  }
18211  $prevLastH = $this->lasth;
18212  // store some info for multicolumn mode
18213  if ($this->rtl) {
18214  $this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
18215  } else {
18216  $this->colxshift['x'] = $this->x - $this->lMargin;
18217  }
18218  $this->colxshift['s'] = $cellspacing;
18219  $this->colxshift['p'] = $current_cell_padding;
18220  // ****** write the cell content ******
18221  $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
18222  // restore some values
18223  $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
18224  $this->lasth = $prevLastH;
18225  $this->cell_padding = $old_cell_padding;
18226  $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
18227  // update the end of row position
18228  if ($rowspan <= 1) {
18229  if (isset($dom[$trid]['endy'])) {
18230  if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) {
18231  $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
18232  } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) {
18233  $dom[$trid]['endy'] = $this->y;
18234  }
18235  } else {
18236  $dom[$trid]['endy'] = $this->y;
18237  }
18238  if (isset($dom[$trid]['endpage'])) {
18239  $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
18240  } else {
18241  $dom[$trid]['endpage'] = $this->page;
18242  }
18243  if (isset($dom[$trid]['endcolumn'])) {
18244  $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
18245  } else {
18246  $dom[$trid]['endcolumn'] = $this->current_column;
18247  }
18248  } else {
18249  // account for row-spanned cells
18250  $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
18251  $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
18252  $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
18253  $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column;
18254  }
18255  if (isset($dom[$table_el]['rowspans'])) {
18256  // update endy and endpage on rowspanned cells
18257  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
18258  if ($trwsp['rowspan'] > 0) {
18259  if (isset($dom[$trid]['endpage'])) {
18260  if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18261  $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18262  } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
18263  $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
18264  $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
18265  $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
18266  } else {
18267  $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
18268  }
18269  }
18270  }
18271  }
18272  }
18273  $this->x += ($cellspacingx / 2);
18274  } else {
18275  // opening tag (or self-closing tag)
18276  if (!isset($opentagpos)) {
18277  if ($this->inxobj) {
18278  // we are inside an XObject template
18279  $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
18280  } elseif (!$this->InFooter) {
18281  if (isset($this->footerlen[$this->page])) {
18282  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
18283  } else {
18284  $this->footerpos[$this->page] = $this->pagelen[$this->page];
18285  }
18286  $opentagpos = $this->footerpos[$this->page];
18287  }
18288  }
18289  $dom = $this->openHTMLTagHandler($dom, $key, $cell);
18290  }
18291  } else { // closing tag
18292  $prev_numpages = $this->numpages;
18293  $old_bordermrk = $this->bordermrk[$this->page];
18294  $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
18295  if ($this->bordermrk[$this->page] > $old_bordermrk) {
18296  $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk);
18297  }
18298  if ($prev_numpages > $this->numpages) {
18299  $startlinepage = $this->page;
18300  }
18301  }
18302  } elseif (strlen($dom[$key]['value']) > 0) {
18303  // print list-item
18304  if (!TCPDF_STATIC::empty_string($this->lispacer) AND ($this->lispacer != '^')) {
18305  $this->SetFont($pfontname, $pfontstyle, $pfontsize);
18306  $this->resetLastH();
18307  $minstartliney = $this->y;
18308  $maxbottomliney = ($startliney + $this->getCellHeight($this->FontSize));
18309  if (is_numeric($pfontsize) AND ($pfontsize > 0)) {
18310  $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
18311  }
18312  $this->SetFont($curfontname, $curfontstyle, $curfontsize);
18313  $this->resetLastH();
18314  if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
18315  $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
18316  $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
18317  $this->y += ($this->getCellHeight(($pfontsize - $curfontsize) / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
18318  $minstartliney = min($this->y, $minstartliney);
18319  $maxbottomliney = max(($this->y + $this->getCellHeight($pfontsize / $this->k)), $maxbottomliney);
18320  }
18321  }
18322  // text
18323  $this->htmlvspace = 0;
18324  if ((!$this->premode) AND $this->isRTLTextDir()) {
18325  // reverse spaces order
18326  $lsp = ''; // left spaces
18327  $rsp = ''; // right spaces
18328  if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18329  $lsp = $matches[1];
18330  }
18331  if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18332  $rsp = $matches[1];
18333  }
18334  $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
18335  }
18336  if ($newline) {
18337  if (!$this->premode) {
18338  $prelen = strlen($dom[$key]['value']);
18339  if ($this->isRTLTextDir()) {
18340  // right trim except non-breaking space
18341  $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
18342  } else {
18343  // left trim except non-breaking space
18344  $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
18345  }
18346  $postlen = strlen($dom[$key]['value']);
18347  if (($postlen == 0) AND ($prelen > 0)) {
18348  $dom[$key]['trimmed_space'] = true;
18349  }
18350  }
18351  $newline = false;
18352  $firstblock = true;
18353  } else {
18354  $firstblock = false;
18355  // replace empty multiple spaces string with a single space
18356  $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']);
18357  }
18358  $strrest = '';
18359  if ($this->rtl) {
18360  $this->x -= $this->textindent;
18361  } else {
18362  $this->x += $this->textindent;
18363  }
18364  if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
18365  $strlinelen = $this->GetStringWidth($dom[$key]['value']);
18366  if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
18367  // HTML <a> Link
18368  $hrefcolor = '';
18369  if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
18370  $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
18371  }
18372  $hrefstyle = -1;
18373  if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
18374  $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
18375  }
18376  $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
18377  } else {
18378  $wadj = 0; // space to leave for block continuity
18379  if ($this->rtl) {
18380  $cwa = ($this->x - $this->lMargin);
18381  } else {
18382  $cwa = ($this->w - $this->rMargin - $this->x);
18383  }
18384  if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) {
18385  // check the next text blocks for continuity
18386  $nkey = ($key + 1);
18387  $write_block = true;
18388  $same_textdir = true;
18389  $tmp_fontname = $this->FontFamily;
18390  $tmp_fontstyle = $this->FontStyle;
18391  $tmp_fontsize = $this->FontSizePt;
18392  while ($write_block AND isset($dom[$nkey])) {
18393  if ($dom[$nkey]['tag']) {
18394  if ($dom[$nkey]['block']) {
18395  // end of block
18396  $write_block = false;
18397  }
18398  $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
18399  $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
18400  $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
18401  $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
18402  } else {
18403  $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'+/', $this->re_space['m'], $dom[$nkey]['value']);
18404  if (isset($nextstr[0]) AND $same_textdir) {
18405  $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
18406  if (isset($nextstr[1])) {
18407  $write_block = false;
18408  }
18409  }
18410  }
18411  ++$nkey;
18412  }
18413  }
18414  if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) {
18415  $wadj = 0;
18416  $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $dom[$key]['value']);
18417  $numblks = count($nextstr);
18418  if ($numblks > 1) {
18419  // try to split on blank spaces
18420  $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)]));
18421  } else {
18422  // set the entire block on new line
18423  $wadj = $this->GetStringWidth($nextstr[0]);
18424  }
18425  }
18426  // check for reversed text direction
18427  if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) {
18428  // LTR text on RTL direction or RTL text on LTR direction
18429  $reverse_dir = true;
18430  $this->rtl = !$this->rtl;
18431  $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems
18432  if ($this->rtl) {
18433  $this->x += $revshift;
18434  } else {
18435  $this->x -= $revshift;
18436  }
18437  $xws = $this->x;
18438  }
18439  // ****** write only until the end of the line and get the rest ******
18440  $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
18441  // restore default direction
18442  if ($reverse_dir AND ($wadj == 0)) {
18443  $this->x = $xws;
18444  $this->rtl = !$this->rtl;
18445  $reverse_dir = false;
18446  }
18447  }
18448  }
18449  $this->textindent = 0;
18450  if (strlen($strrest) > 0) {
18451  // store the remaining string on the previous $key position
18452  $this->newline = true;
18453  if ($strrest == $dom[$key]['value']) {
18454  // used to avoid infinite loop
18455  ++$loop;
18456  } else {
18457  $loop = 0;
18458  }
18459  $dom[$key]['value'] = $strrest;
18460  if ($cell) {
18461  if ($this->rtl) {
18462  $this->x -= $this->cell_padding['R'];
18463  } else {
18464  $this->x += $this->cell_padding['L'];
18465  }
18466  }
18467  if ($loop < 3) {
18468  --$key;
18469  }
18470  } else {
18471  $loop = 0;
18472  // add the positive font spacing of the last character (if any)
18473  if ($this->font_spacing > 0) {
18474  if ($this->rtl) {
18475  $this->x -= $this->font_spacing;
18476  } else {
18477  $this->x += $this->font_spacing;
18478  }
18479  }
18480  }
18481  }
18482  ++$key;
18483  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')) {
18484  // check if we are on a new page or on a new column
18485  if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) {
18486  // we are on a new page or on a new column and the total object height is less than the available vertical space.
18487  // restore previous object
18488  $this->rollbackTransaction(true);
18489  // restore previous values
18490  foreach ($this_method_vars as $vkey => $vval) {
18491  $$vkey = $vval;
18492  }
18493  if (!empty($dom[$key]['thead'])) {
18494  $this->inthead = true;
18495  }
18496  // add a page (or trig AcceptPageBreak() for multicolumn mode)
18497  $pre_y = $this->y;
18498  if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
18499  $startliney = $this->y;
18500  }
18501  $undo = true; // avoid infinite loop
18502  } else {
18503  $undo = false;
18504  }
18505  }
18506  } // end for each $key
18507  // align the last line
18508  if (isset($startlinex)) {
18509  $yshift = ($minstartliney - $startliney);
18510  if (($yshift > 0) OR ($this->page > $startlinepage)) {
18511  $yshift = 0;
18512  }
18513  $t_x = 0;
18514  // the last line must be shifted to be aligned as requested
18515  $linew = abs($this->endlinex - $startlinex);
18516  if ($this->inxobj) {
18517  // we are inside an XObject template
18518  $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
18519  if (isset($opentagpos)) {
18520  $midpos = $opentagpos;
18521  } else {
18522  $midpos = 0;
18523  }
18524  if ($midpos > 0) {
18525  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
18526  $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
18527  } else {
18528  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
18529  $pend = '';
18530  }
18531  } else {
18532  $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
18533  if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18534  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18535  $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
18536  } elseif (isset($opentagpos)) {
18537  $midpos = $opentagpos;
18538  } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18539  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18540  $midpos = $this->footerpos[$startlinepage];
18541  } else {
18542  $midpos = 0;
18543  }
18544  if ($midpos > 0) {
18545  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
18546  $pend = substr($this->getPageBuffer($startlinepage), $midpos);
18547  } else {
18548  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
18549  $pend = '';
18550  }
18551  }
18552  if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
18553  // calculate shifting amount
18554  $tw = $w;
18555  if ($this->lMargin != $prevlMargin) {
18556  $tw += ($prevlMargin - $this->lMargin);
18557  }
18558  if ($this->rMargin != $prevrMargin) {
18559  $tw += ($prevrMargin - $this->rMargin);
18560  }
18561  $one_space_width = $this->GetStringWidth(chr(32));
18562  $no = 0; // number of spaces on a line contained on a single block
18563  if ($this->isRTLTextDir()) { // RTL
18564  // remove left space if exist
18565  $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
18566  if ($pos1 > 0) {
18567  $pos1 = intval($pos1);
18568  if ($this->isUnicodeFont()) {
18569  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
18570  $spacelen = 2;
18571  } else {
18572  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
18573  $spacelen = 1;
18574  }
18575  if ($pos1 == $pos2) {
18576  $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
18577  if (substr($pmid, $pos1, 4) == '[()]') {
18578  $linew -= $one_space_width;
18579  } elseif ($pos1 == strpos($pmid, '[(')) {
18580  $no = 1;
18581  }
18582  }
18583  }
18584  } else { // LTR
18585  // remove right space if exist
18586  $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
18587  if ($pos1 > 0) {
18588  $pos1 = intval($pos1);
18589  if ($this->isUnicodeFont()) {
18590  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
18591  $spacelen = 2;
18592  } else {
18593  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
18594  $spacelen = 1;
18595  }
18596  if ($pos1 == $pos2) {
18597  $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
18598  $linew -= $one_space_width;
18599  }
18600  }
18601  }
18602  $mdiff = ($tw - $linew);
18603  if ($plalign == 'C') {
18604  if ($this->rtl) {
18605  $t_x = -($mdiff / 2);
18606  } else {
18607  $t_x = ($mdiff / 2);
18608  }
18609  } elseif ($plalign == 'R') {
18610  // right alignment on LTR document
18611  $t_x = $mdiff;
18612  } elseif ($plalign == 'L') {
18613  // left alignment on RTL document
18614  $t_x = -$mdiff;
18615  }
18616  } // end if startlinex
18617  if (($t_x != 0) OR ($yshift < 0)) {
18618  // shift the line
18619  $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
18620  $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18621  $endlinepos = strlen($pstart);
18622  if ($this->inxobj) {
18623  // we are inside an XObject template
18624  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
18625  foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
18626  if ($pak >= $pask) {
18627  $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
18628  $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
18629  }
18630  }
18631  } else {
18632  $this->setPageBuffer($startlinepage, $pstart.$pend);
18633  // shift the annotations and links
18634  if (isset($this->PageAnnots[$this->page])) {
18635  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
18636  if ($pak >= $pask) {
18637  $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
18638  $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
18639  }
18640  }
18641  }
18642  }
18643  $this->y -= $yshift;
18644  $yshift = 0;
18645  }
18646  }
18647  // restore previous values
18648  $this->setGraphicVars($gvars);
18649  if ($this->num_columns > 1) {
18650  $this->selectColumn();
18651  } elseif ($this->page > $prevPage) {
18652  $this->lMargin = $this->pagedim[$this->page]['olm'];
18653  $this->rMargin = $this->pagedim[$this->page]['orm'];
18654  }
18655  // restore previous list state
18656  $this->cell_height_ratio = $prev_cell_height_ratio;
18657  $this->listnum = $prev_listnum;
18658  $this->listordered = $prev_listordered;
18659  $this->listcount = $prev_listcount;
18660  $this->lispacer = $prev_lispacer;
18661  if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
18662  $this->Ln($this->lasth);
18663  if (($this->y < $maxbottomliney) AND ($startlinepage == $this->page)) {
18664  $this->y = $maxbottomliney;
18665  }
18666  }
18667  unset($dom);
18668  }
18669 
18678  protected function openHTMLTagHandler($dom, $key, $cell) {
18679  $tag = $dom[$key];
18680  $parent = $dom[($dom[$key]['parent'])];
18681  $firsttag = ($key == 1);
18682  // check for text direction attribute
18683  if (isset($tag['dir'])) {
18684  $this->setTempRTL($tag['dir']);
18685  } else {
18686  $this->tmprtl = false;
18687  }
18688  if ($tag['block']) {
18689  $hbz = 0; // distance from y to line bottom
18690  $hb = 0; // vertical space between block tags
18691  // calculate vertical space for block tags
18692  if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
18693  $cur_h = $this->tagvspaces[$tag['value']][0]['h'];
18694  } elseif (isset($tag['fontsize'])) {
18695  $cur_h = $this->getCellHeight($tag['fontsize'] / $this->k);
18696  } else {
18697  $cur_h = $this->getCellHeight($this->FontSize);
18698  }
18699  if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
18700  $on = $this->tagvspaces[$tag['value']][0]['n'];
18701  } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18702  $on = 0.6;
18703  } else {
18704  $on = 1;
18705  }
18706  if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br', 'hr')))) {
18707  $hb = 0;
18708  } else {
18709  $hb = ($on * $cur_h);
18710  }
18711  if (($this->htmlvspace <= 0) AND ($on > 0)) {
18712  if (isset($parent['fontsize'])) {
18713  $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
18714  } else {
18715  $hbz = $this->getCellHeight($this->FontSize);
18716  }
18717  }
18718  if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) {
18719  // fix vertical space after table
18720  $hbz = 0;
18721  }
18722  // closing vertical space
18723  $hbc = 0;
18724  if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
18725  $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
18726  } elseif (isset($parent['fontsize'])) {
18727  $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
18728  } else {
18729  $pre_h = $this->getCellHeight($this->FontSize);
18730  }
18731  if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
18732  $cn = $this->tagvspaces[$tag['value']][1]['n'];
18733  } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18734  $cn = 0.6;
18735  } else {
18736  $cn = 1;
18737  }
18738  if (isset($this->tagvspaces[$tag['value']][1])) {
18739  $hbc = ($cn * $pre_h);
18740  }
18741  }
18742  // Opening tag
18743  switch($tag['value']) {
18744  case 'table': {
18745  $cp = 0;
18746  $cs = 0;
18747  $dom[$key]['rowspans'] = array();
18748  if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
18749  $this->htmlvspace = 0;
18750  // set table header
18751  if (!TCPDF_STATIC::empty_string($dom[$key]['thead'])) {
18752  // set table header
18753  $this->thead = $dom[$key]['thead'];
18754  if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
18755  $this->theadMargins = array();
18756  $this->theadMargins['cell_padding'] = $this->cell_padding;
18757  $this->theadMargins['lmargin'] = $this->lMargin;
18758  $this->theadMargins['rmargin'] = $this->rMargin;
18759  $this->theadMargins['page'] = $this->page;
18760  $this->theadMargins['cell'] = $cell;
18761  $this->theadMargins['gvars'] = $this->getGraphicVars();
18762  }
18763  }
18764  }
18765  // store current margins and page
18766  $dom[$key]['old_cell_padding'] = $this->cell_padding;
18767  if (isset($tag['attribute']['cellpadding'])) {
18768  $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
18769  $this->SetCellPadding($pad);
18770  } elseif (isset($tag['padding'])) {
18771  $this->cell_padding = $tag['padding'];
18772  }
18773  if (isset($tag['attribute']['cellspacing'])) {
18774  $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
18775  } elseif (isset($tag['border-spacing'])) {
18776  $cs = $tag['border-spacing']['V'];
18777  }
18778  $prev_y = $this->y;
18779  if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) {
18780  $this->inthead = true;
18781  // add a page (or trig AcceptPageBreak() for multicolumn mode)
18782  $this->checkPageBreak($this->PageBreakTrigger + 1);
18783  }
18784  break;
18785  }
18786  case 'tr': {
18787  // array of columns positions
18788  $dom[$key]['cellpos'] = array();
18789  break;
18790  }
18791  case 'hr': {
18792  if ((isset($tag['height'])) AND ($tag['height'] != '')) {
18793  $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
18794  } else {
18795  $hrHeight = $this->GetLineWidth();
18796  }
18797  $this->addHTMLVertSpace($hbz, max($hb, ($hrHeight / 2)), $cell, $firsttag);
18798  $x = $this->GetX();
18799  $y = $this->GetY();
18800  $wtmp = $this->w - $this->lMargin - $this->rMargin;
18801  if ($cell) {
18802  $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']);
18803  }
18804  if ((isset($tag['width'])) AND ($tag['width'] != '')) {
18805  $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
18806  } else {
18807  $hrWidth = $wtmp;
18808  }
18809  $prevlinewidth = $this->GetLineWidth();
18810  $this->SetLineWidth($hrHeight);
18811  $this->Line($x, $y, $x + $hrWidth, $y);
18812  $this->SetLineWidth($prevlinewidth);
18813  $this->addHTMLVertSpace(max($hbc, ($hrHeight / 2)), 0, $cell, !isset($dom[($key + 1)]));
18814  break;
18815  }
18816  case 'a': {
18817  if (array_key_exists('href', $tag['attribute'])) {
18818  $this->HREF['url'] = $tag['attribute']['href'];
18819  }
18820  break;
18821  }
18822  case 'img': {
18823  if (!empty($tag['attribute']['src'])) {
18824  if ($tag['attribute']['src'][0] === '@') {
18825  // data stream
18826  $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1));
18827  $type = '';
18828  } else {
18829  // get image type
18830  $type = TCPDF_IMAGES::getImageFileType($tag['attribute']['src']);
18831  }
18832  if (!isset($tag['width'])) {
18833  $tag['width'] = 0;
18834  }
18835  if (!isset($tag['height'])) {
18836  $tag['height'] = 0;
18837  }
18838  //if (!isset($tag['attribute']['align'])) {
18839  // the only alignment supported is "bottom"
18840  // further development is required for other modes.
18841  $tag['attribute']['align'] = 'bottom';
18842  //}
18843  switch($tag['attribute']['align']) {
18844  case 'top': {
18845  $align = 'T';
18846  break;
18847  }
18848  case 'middle': {
18849  $align = 'M';
18850  break;
18851  }
18852  case 'bottom': {
18853  $align = 'B';
18854  break;
18855  }
18856  default: {
18857  $align = 'B';
18858  break;
18859  }
18860  }
18861  $prevy = $this->y;
18862  $xpos = $this->x;
18863  $imglink = '';
18864  if (isset($this->HREF['url']) AND !TCPDF_STATIC::empty_string($this->HREF['url'])) {
18865  $imglink = $this->HREF['url'];
18866  if ($imglink[0] == '#') {
18867  // convert url to internal link
18868  $lnkdata = explode(',', $imglink);
18869  if (isset($lnkdata[0])) {
18870  $page = intval(substr($lnkdata[0], 1));
18871  if (empty($page) OR ($page <= 0)) {
18872  $page = $this->page;
18873  }
18874  if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
18875  $lnky = floatval($lnkdata[1]);
18876  } else {
18877  $lnky = 0;
18878  }
18879  $imglink = $this->AddLink();
18880  $this->SetLink($imglink, $lnky, $page);
18881  }
18882  }
18883  }
18884  $border = 0;
18885  if (isset($tag['border']) AND !empty($tag['border'])) {
18886  // currently only support 1 (frame) or a combination of 'LTRB'
18887  $border = $tag['border'];
18888  }
18889  $iw = '';
18890  if (isset($tag['width'])) {
18891  $iw = $this->getHTMLUnitToUnits($tag['width'], ($tag['fontsize'] / $this->k), 'px', false);
18892  }
18893  $ih = '';
18894  if (isset($tag['height'])) {
18895  $ih = $this->getHTMLUnitToUnits($tag['height'], ($tag['fontsize'] / $this->k), 'px', false);
18896  }
18897  if (($type == 'eps') OR ($type == 'ai')) {
18898  $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
18899  } elseif ($type == 'svg') {
18900  $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
18901  } else {
18902  $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
18903  }
18904  switch($align) {
18905  case 'T': {
18906  $this->y = $prevy;
18907  break;
18908  }
18909  case 'M': {
18910  $this->y = (($this->img_rb_y + $prevy - ($this->getCellHeight($tag['fontsize'] / $this->k))) / 2);
18911  break;
18912  }
18913  case 'B': {
18914  $this->y = $this->img_rb_y - ($this->getCellHeight($tag['fontsize'] / $this->k) - ($this->getFontDescent($tag['fontname'], $tag['fontstyle'], $tag['fontsize']) * $this->cell_height_ratio));
18915  break;
18916  }
18917  }
18918  }
18919  break;
18920  }
18921  case 'dl': {
18922  ++$this->listnum;
18923  if ($this->listnum == 1) {
18924  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18925  } else {
18926  $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18927  }
18928  break;
18929  }
18930  case 'dt': {
18931  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18932  break;
18933  }
18934  case 'dd': {
18935  if ($this->rtl) {
18936  $this->rMargin += $this->listindent;
18937  } else {
18938  $this->lMargin += $this->listindent;
18939  }
18941  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18942  break;
18943  }
18944  case 'ul':
18945  case 'ol': {
18946  ++$this->listnum;
18947  if ($tag['value'] == 'ol') {
18948  $this->listordered[$this->listnum] = true;
18949  } else {
18950  $this->listordered[$this->listnum] = false;
18951  }
18952  if (isset($tag['attribute']['start'])) {
18953  $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
18954  } else {
18955  $this->listcount[$this->listnum] = 0;
18956  }
18957  if ($this->rtl) {
18958  $this->rMargin += $this->listindent;
18959  $this->x -= $this->listindent;
18960  } else {
18961  $this->lMargin += $this->listindent;
18962  $this->x += $this->listindent;
18963  }
18965  if ($this->listnum == 1) {
18966  if ($key > 1) {
18967  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18968  }
18969  } else {
18970  $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18971  }
18972  break;
18973  }
18974  case 'li': {
18975  if ($key > 2) {
18976  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18977  }
18978  if ($this->listordered[$this->listnum]) {
18979  // ordered item
18980  if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
18981  $this->lispacer = $parent['attribute']['type'];
18982  } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
18983  $this->lispacer = $parent['listtype'];
18984  } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
18985  $this->lispacer = $this->lisymbol;
18986  } else {
18987  $this->lispacer = '#';
18988  }
18989  ++$this->listcount[$this->listnum];
18990  if (isset($tag['attribute']['value'])) {
18991  $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
18992  }
18993  } else {
18994  // unordered item
18995  if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
18996  $this->lispacer = $parent['attribute']['type'];
18997  } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
18998  $this->lispacer = $parent['listtype'];
18999  } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
19000  $this->lispacer = $this->lisymbol;
19001  } else {
19002  $this->lispacer = '!';
19003  }
19004  }
19005  break;
19006  }
19007  case 'blockquote': {
19008  if ($this->rtl) {
19009  $this->rMargin += $this->listindent;
19010  } else {
19011  $this->lMargin += $this->listindent;
19012  }
19014  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19015  break;
19016  }
19017  case 'br': {
19018  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19019  break;
19020  }
19021  case 'div': {
19022  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19023  break;
19024  }
19025  case 'p': {
19026  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19027  break;
19028  }
19029  case 'pre': {
19030  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19031  $this->premode = true;
19032  break;
19033  }
19034  case 'sup': {
19035  $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
19036  break;
19037  }
19038  case 'sub': {
19039  $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
19040  break;
19041  }
19042  case 'h1':
19043  case 'h2':
19044  case 'h3':
19045  case 'h4':
19046  case 'h5':
19047  case 'h6': {
19048  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19049  break;
19050  }
19051  // Form fields (since 4.8.000 - 2009-09-07)
19052  case 'form': {
19053  if (isset($tag['attribute']['action'])) {
19054  $this->form_action = $tag['attribute']['action'];
19055  } else {
19056  $this->Error('Please explicitly set action attribute path!');
19057  }
19058  if (isset($tag['attribute']['enctype'])) {
19059  $this->form_enctype = $tag['attribute']['enctype'];
19060  } else {
19061  $this->form_enctype = 'application/x-www-form-urlencoded';
19062  }
19063  if (isset($tag['attribute']['method'])) {
19064  $this->form_mode = $tag['attribute']['method'];
19065  } else {
19066  $this->form_mode = 'post';
19067  }
19068  break;
19069  }
19070  case 'input': {
19071  if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19072  $name = $tag['attribute']['name'];
19073  } else {
19074  break;
19075  }
19076  $prop = array();
19077  $opt = array();
19078  if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19079  $prop['readonly'] = true;
19080  }
19081  if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19082  $value = $tag['attribute']['value'];
19083  }
19084  if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC::empty_string($tag['attribute']['maxlength'])) {
19085  $opt['maxlen'] = intval($tag['attribute']['maxlength']);
19086  }
19087  $h = $this->getCellHeight($this->FontSize);
19088  if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19089  $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
19090  } else {
19091  $w = $h;
19092  }
19093  if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
19094  $checked = true;
19095  } else {
19096  $checked = false;
19097  }
19098  if (isset($tag['align'])) {
19099  switch ($tag['align']) {
19100  case 'C': {
19101  $opt['q'] = 1;
19102  break;
19103  }
19104  case 'R': {
19105  $opt['q'] = 2;
19106  break;
19107  }
19108  case 'L':
19109  default: {
19110  break;
19111  }
19112  }
19113  }
19114  switch ($tag['attribute']['type']) {
19115  case 'text': {
19116  if (isset($value)) {
19117  $opt['v'] = $value;
19118  }
19119  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19120  break;
19121  }
19122  case 'password': {
19123  if (isset($value)) {
19124  $opt['v'] = $value;
19125  }
19126  $prop['password'] = 'true';
19127  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19128  break;
19129  }
19130  case 'checkbox': {
19131  if (!isset($value)) {
19132  break;
19133  }
19134  $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
19135  break;
19136  }
19137  case 'radio': {
19138  if (!isset($value)) {
19139  break;
19140  }
19141  $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
19142  break;
19143  }
19144  case 'submit': {
19145  if (!isset($value)) {
19146  $value = 'submit';
19147  }
19148  $w = $this->GetStringWidth($value) * 1.5;
19149  $h *= 1.6;
19150  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19151  $action = array();
19152  $action['S'] = 'SubmitForm';
19153  $action['F'] = $this->form_action;
19154  if ($this->form_enctype != 'FDF') {
19155  $action['Flags'] = array('ExportFormat');
19156  }
19157  if ($this->form_mode == 'get') {
19158  $action['Flags'] = array('GetMethod');
19159  }
19160  $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
19161  break;
19162  }
19163  case 'reset': {
19164  if (!isset($value)) {
19165  $value = 'reset';
19166  }
19167  $w = $this->GetStringWidth($value) * 1.5;
19168  $h *= 1.6;
19169  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19170  $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
19171  break;
19172  }
19173  case 'file': {
19174  $prop['fileSelect'] = 'true';
19175  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19176  if (!isset($value)) {
19177  $value = '*';
19178  }
19179  $w = $this->GetStringWidth($value) * 2;
19180  $h *= 1.2;
19181  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19182  $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
19183  $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19184  break;
19185  }
19186  case 'hidden': {
19187  if (isset($value)) {
19188  $opt['v'] = $value;
19189  }
19190  $opt['f'] = array('invisible', 'hidden');
19191  $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
19192  break;
19193  }
19194  case 'image': {
19195  // THIS TYPE MUST BE FIXED
19196  if (isset($tag['attribute']['src']) AND !TCPDF_STATIC::empty_string($tag['attribute']['src'])) {
19197  $img = $tag['attribute']['src'];
19198  } else {
19199  break;
19200  }
19201  $value = 'img';
19202  //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
19203  if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19204  $jsaction = $tag['attribute']['onclick'];
19205  } else {
19206  $jsaction = '';
19207  }
19208  $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19209  break;
19210  }
19211  case 'button': {
19212  if (!isset($value)) {
19213  $value = ' ';
19214  }
19215  $w = $this->GetStringWidth($value) * 1.5;
19216  $h *= 1.6;
19217  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19218  if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19219  $jsaction = $tag['attribute']['onclick'];
19220  } else {
19221  $jsaction = '';
19222  }
19223  $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19224  break;
19225  }
19226  }
19227  break;
19228  }
19229  case 'textarea': {
19230  $prop = array();
19231  $opt = array();
19232  if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19233  $prop['readonly'] = true;
19234  }
19235  if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19236  $name = $tag['attribute']['name'];
19237  } else {
19238  break;
19239  }
19240  if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19241  $opt['v'] = $tag['attribute']['value'];
19242  }
19243  if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC::empty_string($tag['attribute']['cols'])) {
19244  $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
19245  } else {
19246  $w = 40;
19247  }
19248  if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC::empty_string($tag['attribute']['rows'])) {
19249  $h = intval($tag['attribute']['rows']) * $this->getCellHeight($this->FontSize);
19250  } else {
19251  $h = 10;
19252  }
19253  $prop['multiline'] = 'true';
19254  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19255  break;
19256  }
19257  case 'select': {
19258  $h = $this->getCellHeight($this->FontSize);
19259  if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19260  $h *= ($tag['attribute']['size'] + 1);
19261  }
19262  $prop = array();
19263  $opt = array();
19264  if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19265  $name = $tag['attribute']['name'];
19266  } else {
19267  break;
19268  }
19269  $w = 0;
19270  if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC::empty_string($tag['attribute']['opt'])) {
19271  $options = explode('#!NwL!#', $tag['attribute']['opt']);
19272  $values = array();
19273  foreach ($options as $val) {
19274  if (strpos($val, '#!TaB!#') !== false) {
19275  $opts = explode('#!TaB!#', $val);
19276  $values[] = $opts;
19277  $w = max($w, $this->GetStringWidth($opts[1]));
19278  } else {
19279  $values[] = $val;
19280  $w = max($w, $this->GetStringWidth($val));
19281  }
19282  }
19283  } else {
19284  break;
19285  }
19286  $w *= 2;
19287  if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
19288  $prop['multipleSelection'] = 'true';
19289  $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19290  } else {
19291  $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19292  }
19293  break;
19294  }
19295  case 'tcpdf': {
19296  if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
19297  // Special tag used to call TCPDF methods
19298  if (isset($tag['attribute']['method'])) {
19299  $tcpdf_method = $tag['attribute']['method'];
19300  if (method_exists($this, $tcpdf_method)) {
19301  if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
19302  $params = $this->unserializeTCPDFtagParameters($tag['attribute']['params']);
19303  call_user_func_array(array($this, $tcpdf_method), $params);
19304  } else {
19305  $this->$tcpdf_method();
19306  }
19307  $this->newline = true;
19308  }
19309  }
19310  }
19311  break;
19312  }
19313  default: {
19314  break;
19315  }
19316  }
19317  // define tags that support borders and background colors
19318  $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
19319  if (in_array($tag['value'], $bordertags)) {
19320  // set border
19321  $dom[$key]['borderposition'] = $this->getBorderStartPosition();
19322  }
19323  if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
19324  $pba = $dom[$key]['attribute']['pagebreakafter'];
19325  // check for pagebreak
19326  if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19327  // add a page (or trig AcceptPageBreak() for multicolumn mode)
19328  $this->checkPageBreak($this->PageBreakTrigger + 1);
19329  }
19330  if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19331  OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19332  // add a page (or trig AcceptPageBreak() for multicolumn mode)
19333  $this->checkPageBreak($this->PageBreakTrigger + 1);
19334  }
19335  }
19336  return $dom;
19337  }
19338 
19348  protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
19349  $tag = $dom[$key];
19350  $parent = $dom[($dom[$key]['parent'])];
19351  $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
19352  $in_table_head = false;
19353  // maximum x position (used to draw borders)
19354  if ($this->rtl) {
19355  $xmax = $this->w;
19356  } else {
19357  $xmax = 0;
19358  }
19359  if ($tag['block']) {
19360  $hbz = 0; // distance from y to line bottom
19361  $hb = 0; // vertical space between block tags
19362  // calculate vertical space for block tags
19363  if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
19364  $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
19365  } elseif (isset($parent['fontsize'])) {
19366  $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
19367  } else {
19368  $pre_h = $this->getCellHeight($this->FontSize);
19369  }
19370  if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
19371  $cn = $this->tagvspaces[$tag['value']][1]['n'];
19372  } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
19373  $cn = 0.6;
19374  } else {
19375  $cn = 1;
19376  }
19377  if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) {
19378  $hb = 0;
19379  } else {
19380  $hb = ($cn * $pre_h);
19381  }
19382  if ($maxbottomliney > $this->PageBreakTrigger) {
19383  $hbz = $this->getCellHeight($this->FontSize);
19384  } elseif ($this->y < $maxbottomliney) {
19385  $hbz = ($maxbottomliney - $this->y);
19386  }
19387  }
19388  // Closing tag
19389  switch($tag['value']) {
19390  case 'tr': {
19391  $table_el = $dom[($dom[$key]['parent'])]['parent'];
19392  if (!isset($parent['endy'])) {
19393  $dom[($dom[$key]['parent'])]['endy'] = $this->y;
19394  $parent['endy'] = $this->y;
19395  }
19396  if (!isset($parent['endpage'])) {
19397  $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
19398  $parent['endpage'] = $this->page;
19399  }
19400  if (!isset($parent['endcolumn'])) {
19401  $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column;
19402  $parent['endcolumn'] = $this->current_column;
19403  }
19404  // update row-spanned cells
19405  if (isset($dom[$table_el]['rowspans'])) {
19406  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19407  $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
19408  if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19409  if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
19410  $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
19411  } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
19412  $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19413  $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19414  $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19415  }
19416  }
19417  }
19418  // report new endy and endpage to the rowspanned cells
19419  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19420  if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19421  $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
19422  $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19423  $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
19424  $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19425  $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
19426  $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19427  }
19428  }
19429  // update remaining rowspanned cells
19430  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19431  if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19432  $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
19433  $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
19434  $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
19435  }
19436  }
19437  }
19438  $prev_page = $this->page;
19439  $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
19440  if ($this->num_columns > 1) {
19441  if (($prev_page < $this->page)
19442  AND ((($this->current_column == 0) AND ($dom[($dom[$key]['parent'])]['endcolumn'] == ($this->num_columns - 1)))
19443  OR ($this->current_column == $dom[($dom[$key]['parent'])]['endcolumn']))) {
19444  // page jump
19445  $this->selectColumn(0);
19446  $dom[($dom[$key]['parent'])]['endcolumn'] = 0;
19447  $dom[($dom[$key]['parent'])]['endy'] = $this->y;
19448  } else {
19449  $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
19450  $this->y = $dom[($dom[$key]['parent'])]['endy'];
19451  }
19452  } else {
19453  $this->y = $dom[($dom[$key]['parent'])]['endy'];
19454  }
19455  if (isset($dom[$table_el]['attribute']['cellspacing'])) {
19456  $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
19457  } elseif (isset($dom[$table_el]['border-spacing'])) {
19458  $this->y += $dom[$table_el]['border-spacing']['V'];
19459  }
19460  $this->Ln(0, $cell);
19461  if ($this->current_column == $parent['startcolumn']) {
19462  $this->x = $parent['startx'];
19463  }
19464  // account for booklet mode
19465  if ($this->page > $parent['startpage']) {
19466  if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
19467  $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
19468  } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
19469  $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
19470  }
19471  }
19472  break;
19473  }
19474  case 'tablehead':
19475  // closing tag used for the thead part
19476  $in_table_head = true;
19477  $this->inthead = false;
19478  case 'table': {
19479  $table_el = $parent;
19480  // set default border
19481  if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
19482  // set default border
19483  $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
19484  } else {
19485  $border = 0;
19486  }
19487  $default_border = $border;
19488  // fix bottom line alignment of last line before page break
19489  foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
19490  // update row-spanned cells
19491  if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19492  foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19493  if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) {
19494  $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
19495  }
19496  if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) {
19497  $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
19498  }
19499  }
19500  }
19501  if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
19502  $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
19503  $dom[$prevtrkey]['endy'] = $pgendy;
19504  // update row-spanned cells
19505  if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19506  foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19507  if (($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
19508  $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
19509  $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
19510  }
19511  }
19512  }
19513  }
19514  $prevtrkey = $trkey;
19515  $table_el = $dom[($dom[$key]['parent'])];
19516  }
19517  // for each row
19518  if (count($table_el['trids']) > 0) {
19519  unset($xmax);
19520  }
19521  foreach ($table_el['trids'] as $j => $trkey) {
19522  $parent = $dom[$trkey];
19523  if (!isset($xmax)) {
19524  $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
19525  }
19526  // for each cell on the row
19527  foreach ($parent['cellpos'] as $k => $cellpos) {
19528  if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
19529  $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
19530  $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
19531  $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
19532  $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
19533  $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
19534  $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
19535  $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
19536  } else {
19537  $endy = $parent['endy'];
19538  $startpage = $parent['startpage'];
19539  $endpage = $parent['endpage'];
19540  $startcolumn = $parent['startcolumn'];
19541  $endcolumn = $parent['endcolumn'];
19542  }
19543  if ($this->num_columns == 0) {
19544  $this->num_columns = 1;
19545  }
19546  if (isset($cellpos['border'])) {
19547  $border = $cellpos['border'];
19548  }
19549  if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
19550  $this->SetFillColorArray($cellpos['bgcolor']);
19551  $fill = true;
19552  } else {
19553  $fill = false;
19554  }
19555  $x = $cellpos['startx'];
19556  $y = $parent['starty'];
19557  $starty = $y;
19558  $w = abs($cellpos['endx'] - $cellpos['startx']);
19559  // get border modes
19560  $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
19561  $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
19562  $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19563  // design borders around HTML cells.
19564  for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
19565  $ccode = '';
19566  $this->setPage($page);
19567  if ($this->num_columns < 2) {
19568  // single-column mode
19569  $this->x = $x;
19570  $this->y = $this->tMargin;
19571  }
19572  // account for margin changes
19573  if ($page > $startpage) {
19574  if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
19575  $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
19576  } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
19577  $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
19578  }
19579  }
19580  if ($startpage == $endpage) { // single page
19581  $deltacol = 0;
19582  $deltath = 0;
19583  for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
19584  $this->selectColumn($column);
19585  if ($startcolumn == $endcolumn) { // single column
19586  $cborder = $border;
19587  $h = $endy - $parent['starty'];
19588  $this->y = $y;
19589  $this->x = $x;
19590  } elseif ($column == $startcolumn) { // first column
19591  $cborder = $border_start;
19592  $this->y = $starty;
19593  $this->x = $x;
19594  $h = $this->h - $this->y - $this->bMargin;
19595  if ($this->rtl) {
19596  $deltacol = $this->x + $this->rMargin - $this->w;
19597  } else {
19598  $deltacol = $this->x - $this->lMargin;
19599  }
19600  } elseif ($column == $endcolumn) { // end column
19601  $cborder = $border_end;
19602  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19603  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19604  }
19605  $this->x += $deltacol;
19606  $h = $endy - $this->y;
19607  } else { // middle column
19608  $cborder = $border_middle;
19609  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19610  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19611  }
19612  $this->x += $deltacol;
19613  $h = $this->h - $this->y - $this->bMargin;
19614  }
19615  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19616  } // end for each column
19617  } elseif ($page == $startpage) { // first page
19618  $deltacol = 0;
19619  $deltath = 0;
19620  for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
19621  $this->selectColumn($column);
19622  if ($column == $startcolumn) { // first column
19623  $cborder = $border_start;
19624  $this->y = $starty;
19625  $this->x = $x;
19626  $h = $this->h - $this->y - $this->bMargin;
19627  if ($this->rtl) {
19628  $deltacol = $this->x + $this->rMargin - $this->w;
19629  } else {
19630  $deltacol = $this->x - $this->lMargin;
19631  }
19632  } else { // middle column
19633  $cborder = $border_middle;
19634  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19635  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19636  }
19637  $this->x += $deltacol;
19638  $h = $this->h - $this->y - $this->bMargin;
19639  }
19640  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19641  } // end for each column
19642  } elseif ($page == $endpage) { // last page
19643  $deltacol = 0;
19644  $deltath = 0;
19645  for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
19646  $this->selectColumn($column);
19647  if ($column == $endcolumn) { // end column
19648  $cborder = $border_end;
19649  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19650  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19651  }
19652  $this->x += $deltacol;
19653  $h = $endy - $this->y;
19654  } else { // middle column
19655  $cborder = $border_middle;
19656  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19657  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19658  }
19659  $this->x += $deltacol;
19660  $h = $this->h - $this->y - $this->bMargin;
19661  }
19662  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19663  } // end for each column
19664  } else { // middle page
19665  $deltacol = 0;
19666  $deltath = 0;
19667  for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
19668  $this->selectColumn($column);
19669  $cborder = $border_middle;
19670  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19671  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19672  }
19673  $this->x += $deltacol;
19674  $h = $this->h - $this->y - $this->bMargin;
19675  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19676  } // end for each column
19677  }
19678  if (!empty($cborder) OR !empty($fill)) {
19679  $offsetlen = strlen($ccode);
19680  // draw border and fill
19681  if ($this->inxobj) {
19682  // we are inside an XObject template
19683  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
19684  $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
19685  $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
19686  $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
19687  } else {
19688  $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
19689  $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
19690  }
19691  $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
19692  $pstart = substr($pagebuff, 0, $pagemark);
19693  $pend = substr($pagebuff, $pagemark);
19694  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
19695  } else {
19696  // draw border and fill
19697  if (end($this->transfmrk[$this->page]) !== false) {
19698  $pagemarkkey = key($this->transfmrk[$this->page]);
19699  $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
19700  } elseif ($this->InFooter) {
19701  $pagemark = $this->footerpos[$this->page];
19702  } else {
19703  $pagemark = $this->intmrk[$this->page];
19704  }
19705  $pagebuff = $this->getPageBuffer($this->page);
19706  $pstart = substr($pagebuff, 0, $pagemark);
19707  $pend = substr($pagebuff, $pagemark);
19708  $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
19709  }
19710  }
19711  } // end for each page
19712  // restore default border
19713  $border = $default_border;
19714  } // end for each cell on the row
19715  if (isset($table_el['attribute']['cellspacing'])) {
19716  $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
19717  } elseif (isset($table_el['border-spacing'])) {
19718  $this->y += $table_el['border-spacing']['V'];
19719  }
19720  $this->Ln(0, $cell);
19721  $this->x = $parent['startx'];
19722  if ($endpage > $startpage) {
19723  if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
19724  $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
19725  } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
19726  $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
19727  }
19728  }
19729  }
19730  if (!$in_table_head) { // we are not inside a thead section
19731  $this->cell_padding = $table_el['old_cell_padding'];
19732  // reset row height
19733  $this->resetLastH();
19734  if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) {
19735  $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]);
19736  if (($plendiff > 0) AND ($plendiff < 60)) {
19737  $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff);
19738  if (substr($pagediff, 0, 5) == 'BT /F') {
19739  // the difference is only a font setting
19740  $plendiff = 0;
19741  }
19742  }
19743  if ($plendiff == 0) {
19744  // remove last blank page
19745  $this->deletePage($this->numpages);
19746  }
19747  }
19748  if (isset($this->theadMargins['top'])) {
19749  // restore top margin
19750  $this->tMargin = $this->theadMargins['top'];
19751  }
19752  if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
19753  // reset main table header
19754  $this->thead = '';
19755  $this->theadMargins = array();
19756  $this->pagedim[$this->page]['tm'] = $this->tMargin;
19757  }
19758  }
19759  $parent = $table_el;
19760  break;
19761  }
19762  case 'a': {
19763  $this->HREF = '';
19764  break;
19765  }
19766  case 'sup': {
19767  $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
19768  break;
19769  }
19770  case 'sub': {
19771  $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k));
19772  break;
19773  }
19774  case 'div': {
19775  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19776  break;
19777  }
19778  case 'blockquote': {
19779  if ($this->rtl) {
19780  $this->rMargin -= $this->listindent;
19781  } else {
19782  $this->lMargin -= $this->listindent;
19783  }
19785  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19786  break;
19787  }
19788  case 'p': {
19789  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19790  break;
19791  }
19792  case 'pre': {
19793  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19794  $this->premode = false;
19795  break;
19796  }
19797  case 'dl': {
19798  --$this->listnum;
19799  if ($this->listnum <= 0) {
19800  $this->listnum = 0;
19801  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19802  } else {
19803  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19804  }
19805  $this->resetLastH();
19806  break;
19807  }
19808  case 'dt': {
19809  $this->lispacer = '';
19810  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19811  break;
19812  }
19813  case 'dd': {
19814  $this->lispacer = '';
19815  if ($this->rtl) {
19816  $this->rMargin -= $this->listindent;
19817  } else {
19818  $this->lMargin -= $this->listindent;
19819  }
19821  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19822  break;
19823  }
19824  case 'ul':
19825  case 'ol': {
19826  --$this->listnum;
19827  $this->lispacer = '';
19828  if ($this->rtl) {
19829  $this->rMargin -= $this->listindent;
19830  } else {
19831  $this->lMargin -= $this->listindent;
19832  }
19834  if ($this->listnum <= 0) {
19835  $this->listnum = 0;
19836  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19837  } else {
19838  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19839  }
19840  $this->resetLastH();
19841  break;
19842  }
19843  case 'li': {
19844  $this->lispacer = '';
19845  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19846  break;
19847  }
19848  case 'h1':
19849  case 'h2':
19850  case 'h3':
19851  case 'h4':
19852  case 'h5':
19853  case 'h6': {
19854  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19855  break;
19856  }
19857  // Form fields (since 4.8.000 - 2009-09-07)
19858  case 'form': {
19859  $this->form_action = '';
19860  $this->form_enctype = 'application/x-www-form-urlencoded';
19861  break;
19862  }
19863  default : {
19864  break;
19865  }
19866  }
19867  // draw border and background (if any)
19868  $this->drawHTMLTagBorder($parent, $xmax);
19869  if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
19870  $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
19871  // check for pagebreak
19872  if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19873  // add a page (or trig AcceptPageBreak() for multicolumn mode)
19874  $this->checkPageBreak($this->PageBreakTrigger + 1);
19875  }
19876  if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19877  OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19878  // add a page (or trig AcceptPageBreak() for multicolumn mode)
19879  $this->checkPageBreak($this->PageBreakTrigger + 1);
19880  }
19881  }
19882  $this->tmprtl = false;
19883  return $dom;
19884  }
19885 
19895  protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
19896  if ($firsttag) {
19897  $this->Ln(0, $cell);
19898  $this->htmlvspace = 0;
19899  return;
19900  }
19901  if ($lasttag) {
19902  $this->Ln($hbz, $cell);
19903  $this->htmlvspace = 0;
19904  return;
19905  }
19906  if ($hb < $this->htmlvspace) {
19907  $hd = 0;
19908  } else {
19909  $hd = $hb - $this->htmlvspace;
19910  $this->htmlvspace = $hb;
19911  }
19912  $this->Ln(($hbz + $hd), $cell);
19913  }
19914 
19921  protected function getBorderStartPosition() {
19922  if ($this->rtl) {
19923  $xmax = $this->lMargin;
19924  } else {
19925  $xmax = $this->w - $this->rMargin;
19926  }
19927  return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax);
19928  }
19929 
19937  protected function drawHTMLTagBorder($tag, $xmax) {
19938  if (!isset($tag['borderposition'])) {
19939  // nothing to draw
19940  return;
19941  }
19942  $prev_x = $this->x;
19943  $prev_y = $this->y;
19944  $prev_lasth = $this->lasth;
19945  $border = 0;
19946  $fill = false;
19947  $this->lasth = 0;
19948  if (isset($tag['border']) AND !empty($tag['border'])) {
19949  // get border style
19950  $border = $tag['border'];
19951  if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
19952  // border for table header
19953  $border = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19954  }
19955  }
19956  if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
19957  // get background color
19958  $old_bgcolor = $this->bgcolor;
19959  $this->SetFillColorArray($tag['bgcolor']);
19960  $fill = true;
19961  }
19962  if (!$border AND !$fill) {
19963  // nothing to draw
19964  return;
19965  }
19966  if (isset($tag['attribute']['cellspacing'])) {
19967  $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
19968  $cellspacing = array('H' => $clsp, 'V' => $clsp);
19969  } elseif (isset($tag['border-spacing'])) {
19970  $cellspacing = $tag['border-spacing'];
19971  } else {
19972  $cellspacing = array('H' => 0, 'V' => 0);
19973  }
19974  if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
19975  // draw the border externally respect the sqare edge.
19976  $border['mode'] = 'ext';
19977  }
19978  if ($this->rtl) {
19979  if ($xmax >= $tag['borderposition']['x']) {
19980  $xmax = $tag['borderposition']['xmax'];
19981  }
19982  $w = ($tag['borderposition']['x'] - $xmax);
19983  } else {
19984  if ($xmax <= $tag['borderposition']['x']) {
19985  $xmax = $tag['borderposition']['xmax'];
19986  }
19987  $w = ($xmax - $tag['borderposition']['x']);
19988  }
19989  if ($w <= 0) {
19990  return;
19991  }
19992  $w += $cellspacing['H'];
19993  $startpage = $tag['borderposition']['page'];
19994  $startcolumn = $tag['borderposition']['column'];
19995  $x = $tag['borderposition']['x'];
19996  $y = $tag['borderposition']['y'];
19997  $endpage = $this->page;
19998  $starty = $tag['borderposition']['y'] - $cellspacing['V'];
19999  $currentY = $this->y;
20000  $this->x = $x;
20001  // get latest column
20002  $endcolumn = $this->current_column;
20003  if ($this->num_columns == 0) {
20004  $this->num_columns = 1;
20005  }
20006  // get border modes
20007  $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
20008  $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
20009  $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
20010  // temporary disable page regions
20011  $temp_page_regions = $this->page_regions;
20012  $this->page_regions = array();
20013  // design borders around HTML cells.
20014  for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
20015  $ccode = '';
20016  $this->setPage($page);
20017  if ($this->num_columns < 2) {
20018  // single-column mode
20019  $this->x = $x;
20020  $this->y = $this->tMargin;
20021  }
20022  // account for margin changes
20023  if ($page > $startpage) {
20024  if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
20025  $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
20026  } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
20027  $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
20028  }
20029  }
20030  if ($startpage == $endpage) {
20031  // single page
20032  for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
20033  $this->selectColumn($column);
20034  if ($startcolumn == $endcolumn) { // single column
20035  $cborder = $border;
20036  $h = ($currentY - $y) + $cellspacing['V'];
20037  $this->y = $starty;
20038  } elseif ($column == $startcolumn) { // first column
20039  $cborder = $border_start;
20040  $this->y = $starty;
20041  $h = $this->h - $this->y - $this->bMargin;
20042  } elseif ($column == $endcolumn) { // end column
20043  $cborder = $border_end;
20044  $h = $currentY - $this->y;
20045  } else { // middle column
20046  $cborder = $border_middle;
20047  $h = $this->h - $this->y - $this->bMargin;
20048  }
20049  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20050  } // end for each column
20051  } elseif ($page == $startpage) { // first page
20052  for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
20053  $this->selectColumn($column);
20054  if ($column == $startcolumn) { // first column
20055  $cborder = $border_start;
20056  $this->y = $starty;
20057  $h = $this->h - $this->y - $this->bMargin;
20058  } else { // middle column
20059  $cborder = $border_middle;
20060  $h = $this->h - $this->y - $this->bMargin;
20061  }
20062  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20063  } // end for each column
20064  } elseif ($page == $endpage) { // last page
20065  for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
20066  $this->selectColumn($column);
20067  if ($column == $endcolumn) {
20068  // end column
20069  $cborder = $border_end;
20070  $h = $currentY - $this->y;
20071  } else {
20072  // middle column
20073  $cborder = $border_middle;
20074  $h = $this->h - $this->y - $this->bMargin;
20075  }
20076  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20077  } // end for each column
20078  } else { // middle page
20079  for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
20080  $this->selectColumn($column);
20081  $cborder = $border_middle;
20082  $h = $this->h - $this->y - $this->bMargin;
20083  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20084  } // end for each column
20085  }
20086  if ($cborder OR $fill) {
20087  $offsetlen = strlen($ccode);
20088  // draw border and fill
20089  if ($this->inxobj) {
20090  // we are inside an XObject template
20091  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
20092  $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
20093  $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
20094  $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
20095  } else {
20096  $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
20097  $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
20098  }
20099  $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
20100  $pstart = substr($pagebuff, 0, $pagemark);
20101  $pend = substr($pagebuff, $pagemark);
20102  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
20103  } else {
20104  if (end($this->transfmrk[$this->page]) !== false) {
20105  $pagemarkkey = key($this->transfmrk[$this->page]);
20106  $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
20107  } elseif ($this->InFooter) {
20108  $pagemark = $this->footerpos[$this->page];
20109  } else {
20110  $pagemark = $this->intmrk[$this->page];
20111  }
20112  $pagebuff = $this->getPageBuffer($this->page);
20113  $pstart = substr($pagebuff, 0, $pagemark);
20114  $pend = substr($pagebuff, $pagemark);
20115  $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
20116  $this->bordermrk[$this->page] += $offsetlen;
20117  $this->cntmrk[$this->page] += $offsetlen;
20118  }
20119  }
20120  } // end for each page
20121  // restore page regions
20122  $this->page_regions = $temp_page_regions;
20123  if (isset($old_bgcolor)) {
20124  // restore background color
20125  $this->SetFillColorArray($old_bgcolor);
20126  }
20127  // restore pointer position
20128  $this->x = $prev_x;
20129  $this->y = $prev_y;
20130  $this->lasth = $prev_lasth;
20131  }
20132 
20139  public function setLIsymbol($symbol='!') {
20140  // check for custom image symbol
20141  if (substr($symbol, 0, 4) == 'img|') {
20142  $this->lisymbol = $symbol;
20143  return;
20144  }
20145  $symbol = strtolower($symbol);
20146  $valid_symbols = array('!', '#', 'disc', 'circle', 'square', '1', 'decimal', 'decimal-leading-zero', 'i', 'lower-roman', 'I', 'upper-roman', 'a', 'lower-alpha', 'lower-latin', 'A', 'upper-alpha', 'upper-latin', 'lower-greek');
20147  if (in_array($symbol, $valid_symbols)) {
20148  $this->lisymbol = $symbol;
20149  } else {
20150  $this->lisymbol = '';
20151  }
20152  }
20153 
20162  public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
20163  $this->booklet = $booklet;
20164  if ($inner >= 0) {
20165  $this->lMargin = $inner;
20166  }
20167  if ($outer >= 0) {
20168  $this->rMargin = $outer;
20169  }
20170  }
20171 
20178  protected function swapMargins($reverse=true) {
20179  if ($reverse) {
20180  // swap left and right margins
20181  $mtemp = $this->original_lMargin;
20182  $this->original_lMargin = $this->original_rMargin;
20183  $this->original_rMargin = $mtemp;
20184  $deltam = $this->original_lMargin - $this->original_rMargin;
20185  $this->lMargin += $deltam;
20186  $this->rMargin -= $deltam;
20187  }
20188  }
20189 
20202  public function setHtmlVSpace($tagvs) {
20203  $this->tagvspaces = $tagvs;
20204  }
20205 
20212  public function setListIndentWidth($width) {
20213  return $this->customlistindent = floatval($width);
20214  }
20215 
20222  public function setOpenCell($isopen) {
20223  $this->opencell = $isopen;
20224  }
20225 
20233  public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
20234  $this->htmlLinkColorArray = $color;
20235  $this->htmlLinkFontStyle = $fontstyle;
20236  }
20237 
20248  public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
20249  $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
20250  $retval = 0;
20251  $value = 0;
20252  $unit = 'px';
20253  if ($points) {
20254  $k = 1;
20255  } else {
20256  $k = $this->k;
20257  }
20258  if (in_array($defaultunit, $supportedunits)) {
20259  $unit = $defaultunit;
20260  }
20261  if (is_numeric($htmlval)) {
20262  $value = floatval($htmlval);
20263  } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
20264  $value = floatval($mnum[1]);
20265  if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
20266  if (in_array($munit[1], $supportedunits)) {
20267  $unit = $munit[1];
20268  }
20269  }
20270  }
20271  switch ($unit) {
20272  // percentage
20273  case '%': {
20274  $retval = (($value * $refsize) / 100);
20275  break;
20276  }
20277  // relative-size
20278  case 'em': {
20279  $retval = ($value * $refsize);
20280  break;
20281  }
20282  // height of lower case 'x' (about half the font-size)
20283  case 'ex': {
20284  $retval = ($value * ($refsize / 2));
20285  break;
20286  }
20287  // absolute-size
20288  case 'in': {
20289  $retval = (($value * $this->dpi) / $k);
20290  break;
20291  }
20292  // centimeters
20293  case 'cm': {
20294  $retval = (($value / 2.54 * $this->dpi) / $k);
20295  break;
20296  }
20297  // millimeters
20298  case 'mm': {
20299  $retval = (($value / 25.4 * $this->dpi) / $k);
20300  break;
20301  }
20302  // one pica is 12 points
20303  case 'pc': {
20304  $retval = (($value * 12) / $k);
20305  break;
20306  }
20307  // points
20308  case 'pt': {
20309  $retval = ($value / $k);
20310  break;
20311  }
20312  // pixels
20313  case 'px': {
20314  $retval = $this->pixelsToUnits($value);
20315  if ($points) {
20316  $retval *= $this->k;
20317  }
20318  break;
20319  }
20320  }
20321  return $retval;
20322  }
20323 
20332  protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
20333  if ($this->state != 2) {
20334  return;
20335  }
20336  $size /= $this->k;
20337  $fill = '';
20339  $color = $this->fgcolor;
20341  $width = 0;
20342  $textitem = '';
20343  $tmpx = $this->x;
20344  $lspace = $this->GetStringWidth(' ');
20345  if ($listtype == '^') {
20346  // special symbol used for avoid justification of rect bullet
20347  $this->lispacer = '';
20348  return;
20349  } elseif ($listtype == '!') {
20350  // set default list type for unordered list
20351  $deftypes = array('disc', 'circle', 'square');
20352  $listtype = $deftypes[($listdepth - 1) % 3];
20353  } elseif ($listtype == '#') {
20354  // set default list type for ordered list
20355  $listtype = 'decimal';
20356  } elseif (substr($listtype, 0, 4) == 'img|') {
20357  // custom image type ('img|type|width|height|image.ext')
20358  $img = explode('|', $listtype);
20359  $listtype = 'img';
20360  }
20361  switch ($listtype) {
20362  // unordered types
20363  case 'none': {
20364  break;
20365  }
20366  case 'disc': {
20367  $r = $size / 6;
20368  $lspace += (2 * $r);
20369  if ($this->rtl) {
20370  $this->x += $lspace;
20371  } else {
20372  $this->x -= $lspace;
20373  }
20374  $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8);
20375  break;
20376  }
20377  case 'circle': {
20378  $r = $size / 6;
20379  $lspace += (2 * $r);
20380  if ($this->rtl) {
20381  $this->x += $lspace;
20382  } else {
20383  $this->x -= $lspace;
20384  }
20385  $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor;
20386  $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
20387  $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
20388  $this->_out($prev_line_style); // restore line settings
20389  break;
20390  }
20391  case 'square': {
20392  $l = $size / 3;
20393  $lspace += $l;
20394  if ($this->rtl) {;
20395  $this->x += $lspace;
20396  } else {
20397  $this->x -= $lspace;
20398  }
20399  $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color);
20400  break;
20401  }
20402  case 'img': {
20403  // 1=>type, 2=>width, 3=>height, 4=>image.ext
20404  $lspace += $img[2];
20405  if ($this->rtl) {;
20406  $this->x += $lspace;
20407  } else {
20408  $this->x -= $lspace;
20409  }
20410  $imgtype = strtolower($img[1]);
20411  $prev_y = $this->y;
20412  switch ($imgtype) {
20413  case 'svg': {
20414  $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
20415  break;
20416  }
20417  case 'ai':
20418  case 'eps': {
20419  $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
20420  break;
20421  }
20422  default: {
20423  $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);
20424  break;
20425  }
20426  }
20427  $this->y = $prev_y;
20428  break;
20429  }
20430  // ordered types
20431  // $this->listcount[$this->listnum];
20432  // $textitem
20433  case '1':
20434  case 'decimal': {
20435  $textitem = $this->listcount[$this->listnum];
20436  break;
20437  }
20438  case 'decimal-leading-zero': {
20439  $textitem = sprintf('%02d', $this->listcount[$this->listnum]);
20440  break;
20441  }
20442  case 'i':
20443  case 'lower-roman': {
20444  $textitem = strtolower(TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]));
20445  break;
20446  }
20447  case 'I':
20448  case 'upper-roman': {
20449  $textitem = TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]);
20450  break;
20451  }
20452  case 'a':
20453  case 'lower-alpha':
20454  case 'lower-latin': {
20455  $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
20456  break;
20457  }
20458  case 'A':
20459  case 'upper-alpha':
20460  case 'upper-latin': {
20461  $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
20462  break;
20463  }
20464  case 'lower-greek': {
20465  $textitem = TCPDF_FONTS::unichr((945 + $this->listcount[$this->listnum] - 1), $this->isunicode);
20466  break;
20467  }
20468  /*
20469  // Types to be implemented (special handling)
20470  case 'hebrew': {
20471  break;
20472  }
20473  case 'armenian': {
20474  break;
20475  }
20476  case 'georgian': {
20477  break;
20478  }
20479  case 'cjk-ideographic': {
20480  break;
20481  }
20482  case 'hiragana': {
20483  break;
20484  }
20485  case 'katakana': {
20486  break;
20487  }
20488  case 'hiragana-iroha': {
20489  break;
20490  }
20491  case 'katakana-iroha': {
20492  break;
20493  }
20494  */
20495  default: {
20496  $textitem = $this->listcount[$this->listnum];
20497  }
20498  }
20499  if (!TCPDF_STATIC::empty_string($textitem)) {
20500  // Check whether we need a new page or new column
20501  $prev_y = $this->y;
20502  $h = $this->getCellHeight($this->FontSize);
20503  if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) {
20504  $tmpx = $this->x;
20505  }
20506  // print ordered item
20507  if ($this->rtl) {
20508  $textitem = '.'.$textitem;
20509  } else {
20510  $textitem = $textitem.'.';
20511  }
20512  $lspace += $this->GetStringWidth($textitem);
20513  if ($this->rtl) {
20514  $this->x += $lspace;
20515  } else {
20516  $this->x -= $lspace;
20517  }
20518  $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
20519  }
20520  $this->x = $tmpx;
20521  $this->lispacer = '^';
20522  // restore colors
20523  $this->SetFillColorArray($bgcolor);
20525  $this->SettextColorArray($color);
20526  }
20527 
20534  protected function getGraphicVars() {
20535  $grapvars = array(
20536  'FontFamily' => $this->FontFamily,
20537  'FontStyle' => $this->FontStyle,
20538  'FontSizePt' => $this->FontSizePt,
20539  'rMargin' => $this->rMargin,
20540  'lMargin' => $this->lMargin,
20541  'cell_padding' => $this->cell_padding,
20542  'cell_margin' => $this->cell_margin,
20543  'LineWidth' => $this->LineWidth,
20544  'linestyleWidth' => $this->linestyleWidth,
20545  'linestyleCap' => $this->linestyleCap,
20546  'linestyleJoin' => $this->linestyleJoin,
20547  'linestyleDash' => $this->linestyleDash,
20548  'textrendermode' => $this->textrendermode,
20549  'textstrokewidth' => $this->textstrokewidth,
20550  'DrawColor' => $this->DrawColor,
20551  'FillColor' => $this->FillColor,
20552  'TextColor' => $this->TextColor,
20553  'ColorFlag' => $this->ColorFlag,
20554  'bgcolor' => $this->bgcolor,
20555  'fgcolor' => $this->fgcolor,
20556  'htmlvspace' => $this->htmlvspace,
20557  'listindent' => $this->listindent,
20558  'listindentlevel' => $this->listindentlevel,
20559  'listnum' => $this->listnum,
20560  'listordered' => $this->listordered,
20561  'listcount' => $this->listcount,
20562  'lispacer' => $this->lispacer,
20563  'cell_height_ratio' => $this->cell_height_ratio,
20564  'font_stretching' => $this->font_stretching,
20565  'font_spacing' => $this->font_spacing,
20566  'alpha' => $this->alpha,
20567  // extended
20568  'lasth' => $this->lasth,
20569  'tMargin' => $this->tMargin,
20570  'bMargin' => $this->bMargin,
20571  'AutoPageBreak' => $this->AutoPageBreak,
20572  'PageBreakTrigger' => $this->PageBreakTrigger,
20573  'x' => $this->x,
20574  'y' => $this->y,
20575  'w' => $this->w,
20576  'h' => $this->h,
20577  'wPt' => $this->wPt,
20578  'hPt' => $this->hPt,
20579  'fwPt' => $this->fwPt,
20580  'fhPt' => $this->fhPt,
20581  'page' => $this->page,
20582  'current_column' => $this->current_column,
20583  'num_columns' => $this->num_columns
20584  );
20585  return $grapvars;
20586  }
20587 
20595  protected function setGraphicVars($gvars, $extended=false) {
20596  if ($this->state != 2) {
20597  return;
20598  }
20599  $this->FontFamily = $gvars['FontFamily'];
20600  $this->FontStyle = $gvars['FontStyle'];
20601  $this->FontSizePt = $gvars['FontSizePt'];
20602  $this->rMargin = $gvars['rMargin'];
20603  $this->lMargin = $gvars['lMargin'];
20604  $this->cell_padding = $gvars['cell_padding'];
20605  $this->cell_margin = $gvars['cell_margin'];
20606  $this->LineWidth = $gvars['LineWidth'];
20607  $this->linestyleWidth = $gvars['linestyleWidth'];
20608  $this->linestyleCap = $gvars['linestyleCap'];
20609  $this->linestyleJoin = $gvars['linestyleJoin'];
20610  $this->linestyleDash = $gvars['linestyleDash'];
20611  $this->textrendermode = $gvars['textrendermode'];
20612  $this->textstrokewidth = $gvars['textstrokewidth'];
20613  $this->DrawColor = $gvars['DrawColor'];
20614  $this->FillColor = $gvars['FillColor'];
20615  $this->TextColor = $gvars['TextColor'];
20616  $this->ColorFlag = $gvars['ColorFlag'];
20617  $this->bgcolor = $gvars['bgcolor'];
20618  $this->fgcolor = $gvars['fgcolor'];
20619  $this->htmlvspace = $gvars['htmlvspace'];
20620  $this->listindent = $gvars['listindent'];
20621  $this->listindentlevel = $gvars['listindentlevel'];
20622  $this->listnum = $gvars['listnum'];
20623  $this->listordered = $gvars['listordered'];
20624  $this->listcount = $gvars['listcount'];
20625  $this->lispacer = $gvars['lispacer'];
20626  $this->cell_height_ratio = $gvars['cell_height_ratio'];
20627  $this->font_stretching = $gvars['font_stretching'];
20628  $this->font_spacing = $gvars['font_spacing'];
20629  $this->alpha = $gvars['alpha'];
20630  if ($extended) {
20631  // restore extended values
20632  $this->lasth = $gvars['lasth'];
20633  $this->tMargin = $gvars['tMargin'];
20634  $this->bMargin = $gvars['bMargin'];
20635  $this->AutoPageBreak = $gvars['AutoPageBreak'];
20636  $this->PageBreakTrigger = $gvars['PageBreakTrigger'];
20637  $this->x = $gvars['x'];
20638  $this->y = $gvars['y'];
20639  $this->w = $gvars['w'];
20640  $this->h = $gvars['h'];
20641  $this->wPt = $gvars['wPt'];
20642  $this->hPt = $gvars['hPt'];
20643  $this->fwPt = $gvars['fwPt'];
20644  $this->fhPt = $gvars['fhPt'];
20645  $this->page = $gvars['page'];
20646  $this->current_column = $gvars['current_column'];
20647  $this->num_columns = $gvars['num_columns'];
20648  }
20649  $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
20650  if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
20651  $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
20652  }
20653  }
20654 
20659  protected function _outSaveGraphicsState() {
20660  $this->_out('q');
20661  }
20662 
20667  protected function _outRestoreGraphicsState() {
20668  $this->_out('Q');
20669  }
20670 
20677  protected function setBuffer($data) {
20678  $this->bufferlen += strlen($data);
20679  $this->buffer .= $data;
20680  }
20681 
20688  protected function replaceBuffer($data) {
20689  $this->bufferlen = strlen($data);
20690  $this->buffer = $data;
20691  }
20692 
20699  protected function getBuffer() {
20700  return $this->buffer;
20701  }
20702 
20711  protected function setPageBuffer($page, $data, $append=false) {
20712  if ($append) {
20713  $this->pages[$page] .= $data;
20714  } else {
20715  $this->pages[$page] = $data;
20716  }
20717  if ($append AND isset($this->pagelen[$page])) {
20718  $this->pagelen[$page] += strlen($data);
20719  } else {
20720  $this->pagelen[$page] = strlen($data);
20721  }
20722  }
20723 
20731  protected function getPageBuffer($page) {
20732  if (isset($this->pages[$page])) {
20733  return $this->pages[$page];
20734  }
20735  return false;
20736  }
20737 
20746  protected function setImageBuffer($image, $data) {
20747  if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) {
20748  $this->imagekeys[$this->numimages] = $image;
20749  $data['i'] = $this->numimages;
20750  ++$this->numimages;
20751  }
20752  $this->images[$image] = $data;
20753  return $data['i'];
20754  }
20755 
20764  protected function setImageSubBuffer($image, $key, $data) {
20765  if (!isset($this->images[$image])) {
20766  $this->setImageBuffer($image, array());
20767  }
20768  $this->images[$image][$key] = $data;
20769  }
20770 
20778  protected function getImageBuffer($image) {
20779  if (isset($this->images[$image])) {
20780  return $this->images[$image];
20781  }
20782  return false;
20783  }
20784 
20792  protected function setFontBuffer($font, $data) {
20793  $this->fonts[$font] = $data;
20794  if (!in_array($font, $this->fontkeys)) {
20795  $this->fontkeys[] = $font;
20796  // store object ID for current font
20797  ++$this->n;
20798  $this->font_obj_ids[$font] = $this->n;
20799  $this->setFontSubBuffer($font, 'n', $this->n);
20800  }
20801  }
20802 
20811  protected function setFontSubBuffer($font, $key, $data) {
20812  if (!isset($this->fonts[$font])) {
20813  $this->setFontBuffer($font, array());
20814  }
20815  $this->fonts[$font][$key] = $data;
20816  }
20817 
20825  protected function getFontBuffer($font) {
20826  if (isset($this->fonts[$font])) {
20827  return $this->fonts[$font];
20828  }
20829  return false;
20830  }
20831 
20840  public function movePage($frompage, $topage) {
20841  if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
20842  return false;
20843  }
20844  if ($frompage == $this->page) {
20845  // close the page before moving it
20846  $this->endPage();
20847  }
20848  // move all page-related states
20849  $tmppage = $this->getPageBuffer($frompage);
20850  $tmppagedim = $this->pagedim[$frompage];
20851  $tmppagelen = $this->pagelen[$frompage];
20852  $tmpintmrk = $this->intmrk[$frompage];
20853  $tmpbordermrk = $this->bordermrk[$frompage];
20854  $tmpcntmrk = $this->cntmrk[$frompage];
20855  $tmppageobjects = $this->pageobjects[$frompage];
20856  if (isset($this->footerpos[$frompage])) {
20857  $tmpfooterpos = $this->footerpos[$frompage];
20858  }
20859  if (isset($this->footerlen[$frompage])) {
20860  $tmpfooterlen = $this->footerlen[$frompage];
20861  }
20862  if (isset($this->transfmrk[$frompage])) {
20863  $tmptransfmrk = $this->transfmrk[$frompage];
20864  }
20865  if (isset($this->PageAnnots[$frompage])) {
20866  $tmpannots = $this->PageAnnots[$frompage];
20867  }
20868  if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
20869  for ($i = $frompage; $i > $topage; --$i) {
20870  if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) {
20871  --$this->pagegroups[$this->newpagegroup[$i]];
20872  break;
20873  }
20874  }
20875  for ($i = $topage; $i > 0; --$i) {
20876  if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) {
20877  ++$this->pagegroups[$this->newpagegroup[$i]];
20878  break;
20879  }
20880  }
20881  }
20882  for ($i = $frompage; $i > $topage; --$i) {
20883  $j = $i - 1;
20884  // shift pages down
20885  $this->setPageBuffer($i, $this->getPageBuffer($j));
20886  $this->pagedim[$i] = $this->pagedim[$j];
20887  $this->pagelen[$i] = $this->pagelen[$j];
20888  $this->intmrk[$i] = $this->intmrk[$j];
20889  $this->bordermrk[$i] = $this->bordermrk[$j];
20890  $this->cntmrk[$i] = $this->cntmrk[$j];
20891  $this->pageobjects[$i] = $this->pageobjects[$j];
20892  if (isset($this->footerpos[$j])) {
20893  $this->footerpos[$i] = $this->footerpos[$j];
20894  } elseif (isset($this->footerpos[$i])) {
20895  unset($this->footerpos[$i]);
20896  }
20897  if (isset($this->footerlen[$j])) {
20898  $this->footerlen[$i] = $this->footerlen[$j];
20899  } elseif (isset($this->footerlen[$i])) {
20900  unset($this->footerlen[$i]);
20901  }
20902  if (isset($this->transfmrk[$j])) {
20903  $this->transfmrk[$i] = $this->transfmrk[$j];
20904  } elseif (isset($this->transfmrk[$i])) {
20905  unset($this->transfmrk[$i]);
20906  }
20907  if (isset($this->PageAnnots[$j])) {
20908  $this->PageAnnots[$i] = $this->PageAnnots[$j];
20909  } elseif (isset($this->PageAnnots[$i])) {
20910  unset($this->PageAnnots[$i]);
20911  }
20912  if (isset($this->newpagegroup[$j])) {
20913  $this->newpagegroup[$i] = $this->newpagegroup[$j];
20914  unset($this->newpagegroup[$j]);
20915  }
20916  if ($this->currpagegroup == $j) {
20917  $this->currpagegroup = $i;
20918  }
20919  }
20920  $this->setPageBuffer($topage, $tmppage);
20921  $this->pagedim[$topage] = $tmppagedim;
20922  $this->pagelen[$topage] = $tmppagelen;
20923  $this->intmrk[$topage] = $tmpintmrk;
20924  $this->bordermrk[$topage] = $tmpbordermrk;
20925  $this->cntmrk[$topage] = $tmpcntmrk;
20926  $this->pageobjects[$topage] = $tmppageobjects;
20927  if (isset($tmpfooterpos)) {
20928  $this->footerpos[$topage] = $tmpfooterpos;
20929  } elseif (isset($this->footerpos[$topage])) {
20930  unset($this->footerpos[$topage]);
20931  }
20932  if (isset($tmpfooterlen)) {
20933  $this->footerlen[$topage] = $tmpfooterlen;
20934  } elseif (isset($this->footerlen[$topage])) {
20935  unset($this->footerlen[$topage]);
20936  }
20937  if (isset($tmptransfmrk)) {
20938  $this->transfmrk[$topage] = $tmptransfmrk;
20939  } elseif (isset($this->transfmrk[$topage])) {
20940  unset($this->transfmrk[$topage]);
20941  }
20942  if (isset($tmpannots)) {
20943  $this->PageAnnots[$topage] = $tmpannots;
20944  } elseif (isset($this->PageAnnots[$topage])) {
20945  unset($this->PageAnnots[$topage]);
20946  }
20947  // adjust outlines
20948  $tmpoutlines = $this->outlines;
20949  foreach ($tmpoutlines as $key => $outline) {
20950  if (!$outline['f']) {
20951  if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
20952  $this->outlines[$key]['p'] = ($outline['p'] + 1);
20953  } elseif ($outline['p'] == $frompage) {
20954  $this->outlines[$key]['p'] = $topage;
20955  }
20956  }
20957  }
20958  // adjust dests
20959  $tmpdests = $this->dests;
20960  foreach ($tmpdests as $key => $dest) {
20961  if (!$dest['f']) {
20962  if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
20963  $this->dests[$key]['p'] = ($dest['p'] + 1);
20964  } elseif ($dest['p'] == $frompage) {
20965  $this->dests[$key]['p'] = $topage;
20966  }
20967  }
20968  }
20969  // adjust links
20970  $tmplinks = $this->links;
20971  foreach ($tmplinks as $key => $link) {
20972  if (!$link['f']) {
20973  if (($link['p'] >= $topage) AND ($link['p'] < $frompage)) {
20974  $this->links[$key]['p'] = ($link['p'] + 1);
20975  } elseif ($link['p'] == $frompage) {
20976  $this->links[$key]['p'] = $topage;
20977  }
20978  }
20979  }
20980  // adjust javascript
20981  $jfrompage = $frompage;
20982  $jtopage = $topage;
20983  if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) {
20984  foreach($pamatch[0] as $pk => $pmatch) {
20985  $pagenum = intval($pamatch[3][$pk]) + 1;
20986  if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
20987  $newpage = ($pagenum + 1);
20988  } elseif ($pagenum == $jfrompage) {
20989  $newpage = $jtopage;
20990  } else {
20991  $newpage = $pagenum;
20992  }
20993  --$newpage;
20994  $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
20995  $this->javascript = str_replace($pmatch, $newjs, $this->javascript);
20996  }
20997  unset($pamatch);
20998  }
20999  // return to last page
21000  $this->lastPage(true);
21001  return true;
21002  }
21003 
21011  public function deletePage($page) {
21012  if (($page < 1) OR ($page > $this->numpages)) {
21013  return false;
21014  }
21015  // delete current page
21016  unset($this->pages[$page]);
21017  unset($this->pagedim[$page]);
21018  unset($this->pagelen[$page]);
21019  unset($this->intmrk[$page]);
21020  unset($this->bordermrk[$page]);
21021  unset($this->cntmrk[$page]);
21022  foreach ($this->pageobjects[$page] as $oid) {
21023  if (isset($this->offsets[$oid])){
21024  unset($this->offsets[$oid]);
21025  }
21026  }
21027  unset($this->pageobjects[$page]);
21028  if (isset($this->footerpos[$page])) {
21029  unset($this->footerpos[$page]);
21030  }
21031  if (isset($this->footerlen[$page])) {
21032  unset($this->footerlen[$page]);
21033  }
21034  if (isset($this->transfmrk[$page])) {
21035  unset($this->transfmrk[$page]);
21036  }
21037  if (isset($this->PageAnnots[$page])) {
21038  unset($this->PageAnnots[$page]);
21039  }
21040  if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
21041  for ($i = $page; $i > 0; --$i) {
21042  if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) {
21043  --$this->pagegroups[$this->newpagegroup[$i]];
21044  break;
21045  }
21046  }
21047  }
21048  if (isset($this->pageopen[$page])) {
21049  unset($this->pageopen[$page]);
21050  }
21051  if ($page < $this->numpages) {
21052  // update remaining pages
21053  for ($i = $page; $i < $this->numpages; ++$i) {
21054  $j = $i + 1;
21055  // shift pages
21056  $this->setPageBuffer($i, $this->getPageBuffer($j));
21057  $this->pagedim[$i] = $this->pagedim[$j];
21058  $this->pagelen[$i] = $this->pagelen[$j];
21059  $this->intmrk[$i] = $this->intmrk[$j];
21060  $this->bordermrk[$i] = $this->bordermrk[$j];
21061  $this->cntmrk[$i] = $this->cntmrk[$j];
21062  $this->pageobjects[$i] = $this->pageobjects[$j];
21063  if (isset($this->footerpos[$j])) {
21064  $this->footerpos[$i] = $this->footerpos[$j];
21065  } elseif (isset($this->footerpos[$i])) {
21066  unset($this->footerpos[$i]);
21067  }
21068  if (isset($this->footerlen[$j])) {
21069  $this->footerlen[$i] = $this->footerlen[$j];
21070  } elseif (isset($this->footerlen[$i])) {
21071  unset($this->footerlen[$i]);
21072  }
21073  if (isset($this->transfmrk[$j])) {
21074  $this->transfmrk[$i] = $this->transfmrk[$j];
21075  } elseif (isset($this->transfmrk[$i])) {
21076  unset($this->transfmrk[$i]);
21077  }
21078  if (isset($this->PageAnnots[$j])) {
21079  $this->PageAnnots[$i] = $this->PageAnnots[$j];
21080  } elseif (isset($this->PageAnnots[$i])) {
21081  unset($this->PageAnnots[$i]);
21082  }
21083  if (isset($this->newpagegroup[$j])) {
21084  $this->newpagegroup[$i] = $this->newpagegroup[$j];
21085  unset($this->newpagegroup[$j]);
21086  }
21087  if ($this->currpagegroup == $j) {
21088  $this->currpagegroup = $i;
21089  }
21090  if (isset($this->pageopen[$j])) {
21091  $this->pageopen[$i] = $this->pageopen[$j];
21092  } elseif (isset($this->pageopen[$i])) {
21093  unset($this->pageopen[$i]);
21094  }
21095  }
21096  // remove last page
21097  unset($this->pages[$this->numpages]);
21098  unset($this->pagedim[$this->numpages]);
21099  unset($this->pagelen[$this->numpages]);
21100  unset($this->intmrk[$this->numpages]);
21101  unset($this->bordermrk[$this->numpages]);
21102  unset($this->cntmrk[$this->numpages]);
21103  foreach ($this->pageobjects[$this->numpages] as $oid) {
21104  if (isset($this->offsets[$oid])){
21105  unset($this->offsets[$oid]);
21106  }
21107  }
21108  unset($this->pageobjects[$this->numpages]);
21109  if (isset($this->footerpos[$this->numpages])) {
21110  unset($this->footerpos[$this->numpages]);
21111  }
21112  if (isset($this->footerlen[$this->numpages])) {
21113  unset($this->footerlen[$this->numpages]);
21114  }
21115  if (isset($this->transfmrk[$this->numpages])) {
21116  unset($this->transfmrk[$this->numpages]);
21117  }
21118  if (isset($this->PageAnnots[$this->numpages])) {
21119  unset($this->PageAnnots[$this->numpages]);
21120  }
21121  if (isset($this->newpagegroup[$this->numpages])) {
21122  unset($this->newpagegroup[$this->numpages]);
21123  }
21124  if ($this->currpagegroup == $this->numpages) {
21125  $this->currpagegroup = ($this->numpages - 1);
21126  }
21127  if (isset($this->pagegroups[$this->numpages])) {
21128  unset($this->pagegroups[$this->numpages]);
21129  }
21130  if (isset($this->pageopen[$this->numpages])) {
21131  unset($this->pageopen[$this->numpages]);
21132  }
21133  }
21134  --$this->numpages;
21135  $this->page = $this->numpages;
21136  // adjust outlines
21137  $tmpoutlines = $this->outlines;
21138  foreach ($tmpoutlines as $key => $outline) {
21139  if (!$outline['f']) {
21140  if ($outline['p'] > $page) {
21141  $this->outlines[$key]['p'] = $outline['p'] - 1;
21142  } elseif ($outline['p'] == $page) {
21143  unset($this->outlines[$key]);
21144  }
21145  }
21146  }
21147  // adjust dests
21148  $tmpdests = $this->dests;
21149  foreach ($tmpdests as $key => $dest) {
21150  if (!$dest['f']) {
21151  if ($dest['p'] > $page) {
21152  $this->dests[$key]['p'] = $dest['p'] - 1;
21153  } elseif ($dest['p'] == $page) {
21154  unset($this->dests[$key]);
21155  }
21156  }
21157  }
21158  // adjust links
21159  $tmplinks = $this->links;
21160  foreach ($tmplinks as $key => $link) {
21161  if (!$link['f']) {
21162  if ($link['p'] > $page) {
21163  $this->links[$key]['p'] = $link['p'] - 1;
21164  } elseif ($link['p'] == $page) {
21165  unset($this->links[$key]);
21166  }
21167  }
21168  }
21169  // adjust javascript
21170  $jpage = $page;
21171  if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) {
21172  foreach($pamatch[0] as $pk => $pmatch) {
21173  $pagenum = intval($pamatch[3][$pk]) + 1;
21174  if ($pagenum >= $jpage) {
21175  $newpage = ($pagenum - 1);
21176  } elseif ($pagenum == $jpage) {
21177  $newpage = 1;
21178  } else {
21179  $newpage = $pagenum;
21180  }
21181  --$newpage;
21182  $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21183  $this->javascript = str_replace($pmatch, $newjs, $this->javascript);
21184  }
21185  unset($pamatch);
21186  }
21187  // return to last page
21188  if ($this->numpages > 0) {
21189  $this->lastPage(true);
21190  }
21191  return true;
21192  }
21193 
21201  public function copyPage($page=0) {
21202  if ($page == 0) {
21203  // default value
21204  $page = $this->page;
21205  }
21206  if (($page < 1) OR ($page > $this->numpages)) {
21207  return false;
21208  }
21209  // close the last page
21210  $this->endPage();
21211  // copy all page-related states
21212  ++$this->numpages;
21213  $this->page = $this->numpages;
21214  $this->setPageBuffer($this->page, $this->getPageBuffer($page));
21215  $this->pagedim[$this->page] = $this->pagedim[$page];
21216  $this->pagelen[$this->page] = $this->pagelen[$page];
21217  $this->intmrk[$this->page] = $this->intmrk[$page];
21218  $this->bordermrk[$this->page] = $this->bordermrk[$page];
21219  $this->cntmrk[$this->page] = $this->cntmrk[$page];
21220  $this->pageobjects[$this->page] = $this->pageobjects[$page];
21221  $this->pageopen[$this->page] = false;
21222  if (isset($this->footerpos[$page])) {
21223  $this->footerpos[$this->page] = $this->footerpos[$page];
21224  }
21225  if (isset($this->footerlen[$page])) {
21226  $this->footerlen[$this->page] = $this->footerlen[$page];
21227  }
21228  if (isset($this->transfmrk[$page])) {
21229  $this->transfmrk[$this->page] = $this->transfmrk[$page];
21230  }
21231  if (isset($this->PageAnnots[$page])) {
21232  $this->PageAnnots[$this->page] = $this->PageAnnots[$page];
21233  }
21234  if (isset($this->newpagegroup[$page])) {
21235  // start a new group
21236  $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1;
21237  $this->currpagegroup = $this->newpagegroup[$this->page];
21238  $this->pagegroups[$this->currpagegroup] = 1;
21239  } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
21240  ++$this->pagegroups[$this->currpagegroup];
21241  }
21242  // copy outlines
21243  $tmpoutlines = $this->outlines;
21244  foreach ($tmpoutlines as $key => $outline) {
21245  if ($outline['p'] == $page) {
21246  $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'x' => $outline['x'], 'y' => $outline['y'], 'p' => $this->page, 'f' => $outline['f'], 's' => $outline['s'], 'c' => $outline['c']);
21247  }
21248  }
21249  // copy links
21250  $tmplinks = $this->links;
21251  foreach ($tmplinks as $key => $link) {
21252  if ($link['p'] == $page) {
21253  $this->links[] = array('p' => $this->page, 'y' => $link['y'], 'f' => $link['f']);
21254  }
21255  }
21256  // return to last page
21257  $this->lastPage(true);
21258  return true;
21259  }
21260 
21278  public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
21279  $fontsize = $this->FontSizePt;
21280  $fontfamily = $this->FontFamily;
21281  $fontstyle = $this->FontStyle;
21282  $w = $this->w - $this->lMargin - $this->rMargin;
21283  $spacer = $this->GetStringWidth(chr(32)) * 4;
21284  $lmargin = $this->lMargin;
21285  $rmargin = $this->rMargin;
21286  $x_start = $this->GetX();
21287  $page_first = $this->page;
21288  $current_page = $this->page;
21289  $page_fill_start = false;
21290  $page_fill_end = false;
21292  if (TCPDF_STATIC::empty_string($numbersfont)) {
21293  $numbersfont = $this->default_monospaced_font;
21294  }
21295  if (TCPDF_STATIC::empty_string($filler)) {
21296  $filler = ' ';
21297  }
21299  $gap = ' ';
21300  } else {
21301  $gap = '';
21302  if ($page < 1) {
21303  $page = 1;
21304  }
21305  }
21306  $this->SetFont($numbersfont, $fontstyle, $fontsize);
21307  $numwidth = $this->GetStringWidth('00000');
21308  $maxpage = 0; //used for pages on attached documents
21309  foreach ($this->outlines as $key => $outline) {
21310  // check for extra pages (used for attachments)
21311  if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) {
21312  $outline['p'] += ($this->page - $page_first);
21313  }
21314  if ($this->rtl) {
21315  $aligntext = 'R';
21316  $alignnum = 'L';
21317  } else {
21318  $aligntext = 'L';
21319  $alignnum = 'R';
21320  }
21321  if ($outline['l'] == 0) {
21322  $this->SetFont($fontfamily, $outline['s'].'B', $fontsize);
21323  } else {
21324  $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']);
21325  }
21326  $this->SetTextColorArray($outline['c']);
21327  // check for page break
21328  $this->checkPageBreak(2 * $this->getCellHeight($this->FontSize));
21329  // set margins and X position
21330  if (($this->page == $current_page) AND ($this->current_column == $current_column)) {
21331  $this->lMargin = $lmargin;
21332  $this->rMargin = $rmargin;
21333  } else {
21334  if ($this->current_column != $current_column) {
21335  if ($this->rtl) {
21336  $x_start = $this->w - $this->columns[$this->current_column]['x'];
21337  } else {
21338  $x_start = $this->columns[$this->current_column]['x'];
21339  }
21340  }
21341  $lmargin = $this->lMargin;
21342  $rmargin = $this->rMargin;
21343  $current_page = $this->page;
21345  }
21346  $this->SetX($x_start);
21347  $indent = ($spacer * $outline['l']);
21348  if ($this->rtl) {
21349  $this->x -= $indent;
21350  $this->rMargin = $this->w - $this->x;
21351  } else {
21352  $this->x += $indent;
21353  $this->lMargin = $this->x;
21354  }
21355  $link = $this->AddLink();
21356  $this->SetLink($link, $outline['y'], $outline['p']);
21357  // write the text
21358  if ($this->rtl) {
21359  $txt = ' '.$outline['t'];
21360  } else {
21361  $txt = $outline['t'].' ';
21362  }
21363  $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
21364  if ($this->rtl) {
21365  $tw = $this->x - $this->lMargin;
21366  } else {
21367  $tw = $this->w - $this->rMargin - $this->x;
21368  }
21369  $this->SetFont($numbersfont, $fontstyle, $fontsize);
21371  $pagenum = $outline['p'];
21372  } else {
21373  // placemark to be replaced with the correct number
21374  $pagenum = '{#'.($outline['p']).'}';
21375  if ($this->isUnicodeFont()) {
21376  $pagenum = '{'.$pagenum.'}';
21377  }
21378  $maxpage = max($maxpage, $outline['p']);
21379  }
21380  $fw = ($tw - $this->GetStringWidth($pagenum.$filler));
21381  $wfiller = $this->GetStringWidth($filler);
21382  if ($wfiller > 0) {
21383  $numfills = floor($fw / $wfiller);
21384  } else {
21385  $numfills = 0;
21386  }
21387  if ($numfills > 0) {
21388  $rowfill = str_repeat($filler, $numfills);
21389  } else {
21390  $rowfill = '';
21391  }
21392  if ($this->rtl) {
21393  $pagenum = $pagenum.$gap.$rowfill;
21394  } else {
21395  $pagenum = $rowfill.$gap.$pagenum;
21396  }
21397  // write the number
21398  $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
21399  }
21400  $page_last = $this->getPage();
21401  $numpages = ($page_last - $page_first + 1);
21402  // account for booklet mode
21403  if ($this->booklet) {
21404  // check if a blank page is required before TOC
21405  $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21406  $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21407  if ($page_fill_start) {
21408  // add a page at the end (to be moved before TOC)
21409  $this->addPage();
21410  ++$page_last;
21411  ++$numpages;
21412  }
21413  if ($page_fill_end) {
21414  // add a page at the end
21415  $this->addPage();
21416  ++$page_last;
21417  ++$numpages;
21418  }
21419  }
21420  $maxpage = max($maxpage, $page_last);
21422  for ($p = $page_first; $p <= $page_last; ++$p) {
21423  // get page data
21424  $temppage = $this->getPageBuffer($p);
21425  for ($n = 1; $n <= $maxpage; ++$n) {
21426  // update page numbers
21427  $a = '{#'.$n.'}';
21428  // get page number aliases
21429  $pnalias = $this->getInternalPageNumberAliases($a);
21430  // calculate replacement number
21431  if (($n >= $page) AND ($n <= $this->numpages)) {
21432  $np = $n + $numpages;
21433  } else {
21434  $np = $n;
21435  }
21436  $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21437  $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21438  // replace aliases with numbers
21439  foreach ($pnalias['u'] as $u) {
21440  $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
21441  if ($this->rtl) {
21442  $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21443  } else {
21444  $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21445  }
21446  $temppage = str_replace($u, $nr, $temppage);
21447  }
21448  foreach ($pnalias['a'] as $a) {
21449  $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
21450  if ($this->rtl) {
21451  $nr = $na.' '.$sfill;
21452  } else {
21453  $nr = $sfill.' '.$na;
21454  }
21455  $temppage = str_replace($a, $nr, $temppage);
21456  }
21457  }
21458  // save changes
21459  $this->setPageBuffer($p, $temppage);
21460  }
21461  // move pages
21462  $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21463  if ($page_fill_start) {
21464  $this->movePage($page_last, $page_first);
21465  }
21466  for ($i = 0; $i < $numpages; ++$i) {
21467  $this->movePage($page_last, $page);
21468  }
21469  }
21470  }
21471 
21488  public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
21489  $filler = ' ';
21490  $prev_htmlLinkColorArray = $this->htmlLinkColorArray;
21491  $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle;
21492  // set new style for link
21493  $this->htmlLinkColorArray = array();
21494  $this->htmlLinkFontStyle = '';
21495  $page_first = $this->getPage();
21496  $page_fill_start = false;
21497  $page_fill_end = false;
21498  // get the font type used for numbers in each template
21499  $current_font = $this->FontFamily;
21500  foreach ($templates as $level => $html) {
21501  $dom = $this->getHtmlDomArray($html);
21502  foreach ($dom as $key => $value) {
21503  if ($value['value'] == '#TOC_PAGE_NUMBER#') {
21504  $this->SetFont($dom[($key - 1)]['fontname']);
21505  $templates['F'.$level] = $this->isUnicodeFont();
21506  }
21507  }
21508  }
21509  $this->SetFont($current_font);
21510  $maxpage = 0; //used for pages on attached documents
21511  foreach ($this->outlines as $key => $outline) {
21512  // get HTML template
21513  $row = $templates[$outline['l']];
21515  $pagenum = $outline['p'];
21516  } else {
21517  // placemark to be replaced with the correct number
21518  $pagenum = '{#'.($outline['p']).'}';
21519  if ($templates['F'.$outline['l']]) {
21520  $pagenum = '{'.$pagenum.'}';
21521  }
21522  $maxpage = max($maxpage, $outline['p']);
21523  }
21524  // replace templates with current values
21525  $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
21526  $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
21527  // add link to page
21528  $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
21529  // write bookmark entry
21530  $this->writeHTML($row, false, false, true, false, '');
21531  }
21532  // restore link styles
21533  $this->htmlLinkColorArray = $prev_htmlLinkColorArray;
21534  $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle;
21535  // move TOC page and replace numbers
21536  $page_last = $this->getPage();
21537  $numpages = ($page_last - $page_first + 1);
21538  // account for booklet mode
21539  if ($this->booklet) {
21540  // check if a blank page is required before TOC
21541  $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21542  $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21543  if ($page_fill_start) {
21544  // add a page at the end (to be moved before TOC)
21545  $this->addPage();
21546  ++$page_last;
21547  ++$numpages;
21548  }
21549  if ($page_fill_end) {
21550  // add a page at the end
21551  $this->addPage();
21552  ++$page_last;
21553  ++$numpages;
21554  }
21555  }
21556  $maxpage = max($maxpage, $page_last);
21558  for ($p = $page_first; $p <= $page_last; ++$p) {
21559  // get page data
21560  $temppage = $this->getPageBuffer($p);
21561  for ($n = 1; $n <= $maxpage; ++$n) {
21562  // update page numbers
21563  $a = '{#'.$n.'}';
21564  // get page number aliases
21565  $pnalias = $this->getInternalPageNumberAliases($a);
21566  // calculate replacement number
21567  if ($n >= $page) {
21568  $np = $n + $numpages;
21569  } else {
21570  $np = $n;
21571  }
21572  $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21573  $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21574  // replace aliases with numbers
21575  foreach ($pnalias['u'] as $u) {
21576  if ($correct_align) {
21577  $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
21578  if ($this->rtl) {
21579  $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21580  } else {
21581  $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21582  }
21583  } else {
21584  $nr = $nu;
21585  }
21586  $temppage = str_replace($u, $nr, $temppage);
21587  }
21588  foreach ($pnalias['a'] as $a) {
21589  if ($correct_align) {
21590  $sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
21591  if ($this->rtl) {
21592  $nr = $na.' '.$sfill;
21593  } else {
21594  $nr = $sfill.' '.$na;
21595  }
21596  } else {
21597  $nr = $na;
21598  }
21599  $temppage = str_replace($a, $nr, $temppage);
21600  }
21601  }
21602  // save changes
21603  $this->setPageBuffer($p, $temppage);
21604  }
21605  // move pages
21606  $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21607  if ($page_fill_start) {
21608  $this->movePage($page_last, $page_first);
21609  }
21610  for ($i = 0; $i < $numpages; ++$i) {
21611  $this->movePage($page_last, $page);
21612  }
21613  }
21614  }
21615 
21621  public function startTransaction() {
21622  if (isset($this->objcopy)) {
21623  // remove previous copy
21624  $this->commitTransaction();
21625  }
21626  // record current page number and Y position
21627  $this->start_transaction_page = $this->page;
21628  $this->start_transaction_y = $this->y;
21629  // clone current object
21630  $this->objcopy = TCPDF_STATIC::objclone($this);
21631  }
21632 
21638  public function commitTransaction() {
21639  if (isset($this->objcopy)) {
21640  $this->objcopy->_destroy(true, true);
21641  unset($this->objcopy);
21642  }
21643  }
21644 
21652  public function rollbackTransaction($self=false) {
21653  if (isset($this->objcopy)) {
21654  $this->_destroy(true, true);
21655  if ($self) {
21656  $objvars = get_object_vars($this->objcopy);
21657  foreach ($objvars as $key => $value) {
21658  $this->$key = $value;
21659  }
21660  }
21661  return $this->objcopy;
21662  }
21663  return $this;
21664  }
21665 
21666  // --- MULTI COLUMNS METHODS -----------------------
21667 
21676  public function setEqualColumns($numcols=0, $width=0, $y='') {
21677  $this->columns = array();
21678  if ($numcols < 2) {
21679  $numcols = 0;
21680  $this->columns = array();
21681  } else {
21682  // maximum column width
21683  $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
21684  if (($width == 0) OR ($width > $maxwidth)) {
21685  $width = $maxwidth;
21686  }
21688  $y = $this->y;
21689  }
21690  // space between columns
21691  $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
21692  // fill the columns array (with, space, starting Y position)
21693  for ($i = 0; $i < $numcols; ++$i) {
21694  $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
21695  }
21696  }
21697  $this->num_columns = $numcols;
21698  $this->current_column = 0;
21699  $this->column_start_page = $this->page;
21700  $this->selectColumn(0);
21701  }
21702 
21708  public function resetColumns() {
21709  $this->lMargin = $this->original_lMargin;
21710  $this->rMargin = $this->original_rMargin;
21711  $this->setEqualColumns();
21712  }
21713 
21721  public function setColumnsArray($columns) {
21722  $this->columns = $columns;
21723  $this->num_columns = count($columns);
21724  $this->current_column = 0;
21725  $this->column_start_page = $this->page;
21726  $this->selectColumn(0);
21727  }
21728 
21735  public function selectColumn($col='') {
21736  if (is_string($col)) {
21737  $col = $this->current_column;
21738  } elseif ($col >= $this->num_columns) {
21739  $col = 0;
21740  }
21741  $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
21742  $enable_thead = false;
21743  if ($this->num_columns > 1) {
21744  if ($col != $this->current_column) {
21745  // move Y pointer at the top of the column
21746  if ($this->column_start_page == $this->page) {
21747  $this->y = $this->columns[$col]['y'];
21748  } else {
21749  $this->y = $this->tMargin;
21750  }
21751  // Avoid to write table headers more than once
21752  if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) {
21753  $enable_thead = true;
21754  $this->maxselcol['page'] = $this->page;
21755  $this->maxselcol['column'] = $col;
21756  }
21757  }
21758  $xshift = $this->colxshift;
21759  // set X position of the current column by case
21760  $listindent = ($this->listindentlevel * $this->listindent);
21761  // calculate column X position
21762  $colpos = 0;
21763  for ($i = 0; $i < $col; ++$i) {
21764  $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']);
21765  }
21766  if ($this->rtl) {
21767  $x = $this->w - $this->original_rMargin - $colpos;
21768  $this->rMargin = ($this->w - $x + $listindent);
21769  $this->lMargin = ($x - $this->columns[$col]['w']);
21770  $this->x = $x - $listindent;
21771  } else {
21772  $x = $this->original_lMargin + $colpos;
21773  $this->lMargin = ($x + $listindent);
21774  $this->rMargin = ($this->w - $x - $this->columns[$col]['w']);
21775  $this->x = $x + $listindent;
21776  }
21777  $this->columns[$col]['x'] = $x;
21778  }
21779  $this->current_column = $col;
21780  // fix for HTML mode
21781  $this->newline = true;
21782  // print HTML table header (if any)
21783  if ((!TCPDF_STATIC::empty_string($this->thead)) AND (!$this->inthead)) {
21784  if ($enable_thead) {
21785  // print table header
21786  $this->writeHTML($this->thead, false, false, false, false, '');
21787  $this->y += $xshift['s']['V'];
21788  // store end of header position
21789  if (!isset($this->columns[$col]['th'])) {
21790  $this->columns[$col]['th'] = array();
21791  }
21792  $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y;
21793  $this->lasth = 0;
21794  } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) {
21795  $this->y = $this->columns[$col]['th']['\''.$this->page.'\''];
21796  }
21797  }
21798  // account for an html table cell over multiple columns
21799  if ($this->rtl) {
21800  $this->rMargin += $xshift['x'];
21801  $this->x -= ($xshift['x'] + $xshift['p']['R']);
21802  } else {
21803  $this->lMargin += $xshift['x'];
21804  $this->x += $xshift['x'] + $xshift['p']['L'];
21805  }
21806  }
21807 
21814  public function getColumn() {
21815  return $this->current_column;
21816  }
21817 
21824  public function getNumberOfColumns() {
21825  return $this->num_columns;
21826  }
21827 
21836  public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
21837  // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
21838  // convert text rendering parameters
21839  if ($stroke < 0) {
21840  $stroke = 0;
21841  }
21842  if ($fill === true) {
21843  if ($stroke > 0) {
21844  if ($clip === true) {
21845  // Fill, then stroke text and add to path for clipping
21846  $textrendermode = 6;
21847  } else {
21848  // Fill, then stroke text
21849  $textrendermode = 2;
21850  }
21851  $textstrokewidth = $stroke;
21852  } else {
21853  if ($clip === true) {
21854  // Fill text and add to path for clipping
21855  $textrendermode = 4;
21856  } else {
21857  // Fill text
21858  $textrendermode = 0;
21859  }
21860  }
21861  } else {
21862  if ($stroke > 0) {
21863  if ($clip === true) {
21864  // Stroke text and add to path for clipping
21865  $textrendermode = 5;
21866  } else {
21867  // Stroke text
21868  $textrendermode = 1;
21869  }
21870  $textstrokewidth = $stroke;
21871  } else {
21872  if ($clip === true) {
21873  // Add text to path for clipping
21874  $textrendermode = 7;
21875  } else {
21876  // Neither fill nor stroke text (invisible)
21877  $textrendermode = 3;
21878  }
21879  }
21880  }
21881  $this->textrendermode = $textrendermode;
21882  $this->textstrokewidth = $stroke;
21883  }
21884 
21891  public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) {
21892  if (isset($params['enabled'])) {
21893  $this->txtshadow['enabled'] = $params['enabled']?true:false;
21894  } else {
21895  $this->txtshadow['enabled'] = false;
21896  }
21897  if (isset($params['depth_w'])) {
21898  $this->txtshadow['depth_w'] = floatval($params['depth_w']);
21899  } else {
21900  $this->txtshadow['depth_w'] = 0;
21901  }
21902  if (isset($params['depth_h'])) {
21903  $this->txtshadow['depth_h'] = floatval($params['depth_h']);
21904  } else {
21905  $this->txtshadow['depth_h'] = 0;
21906  }
21907  if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) {
21908  $this->txtshadow['color'] = $params['color'];
21909  } else {
21910  $this->txtshadow['color'] = $this->strokecolor;
21911  }
21912  if (isset($params['opacity'])) {
21913  $this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity'])));
21914  } else {
21915  $this->txtshadow['opacity'] = 1;
21916  }
21917  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'))) {
21918  $this->txtshadow['blend_mode'] = $params['blend_mode'];
21919  } else {
21920  $this->txtshadow['blend_mode'] = 'Normal';
21921  }
21922  if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) {
21923  $this->txtshadow['enabled'] = false;
21924  }
21925  }
21926 
21933  public function getTextShadow() {
21934  return $this->txtshadow;
21935  }
21936 
21951  protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
21952  $hyphenword = array(); // hyphens positions
21953  $numchars = count($word);
21954  if ($numchars <= $charmin) {
21955  return $word;
21956  }
21957  $word_string = TCPDF_FONTS::UTF8ArrSubString($word, '', '', $this->isunicode);
21958  // some words will be returned as-is
21959  $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})(\]?)$/';
21960  if (preg_match($pattern, $word_string) > 0) {
21961  // email
21962  return $word;
21963  }
21964  $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})(\]?)$/';
21965  if (preg_match($pattern, $word_string) > 0) {
21966  // URL
21967  return $word;
21968  }
21969  if (isset($dictionary[$word_string])) {
21970  return TCPDF_FONTS::UTF8StringToArray($dictionary[$word_string], $this->isunicode, $this->CurrentFont);
21971  }
21972  // surround word with '_' characters
21973  $tmpword = array_merge(array(46), $word, array(46));
21974  $tmpnumchars = $numchars + 2;
21975  $maxpos = $tmpnumchars - 1;
21976  for ($pos = 0; $pos < $maxpos; ++$pos) {
21977  $imax = min(($tmpnumchars - $pos), $charmax);
21978  for ($i = 1; $i <= $imax; ++$i) {
21979  $subword = strtolower(TCPDF_FONTS::UTF8ArrSubString($tmpword, $pos, ($pos + $i), $this->isunicode));
21980  if (isset($patterns[$subword])) {
21981  $pattern = TCPDF_FONTS::UTF8StringToArray($patterns[$subword], $this->isunicode, $this->CurrentFont);
21982  $pattern_length = count($pattern);
21983  $digits = 1;
21984  for ($j = 0; $j < $pattern_length; ++$j) {
21985  // check if $pattern[$j] is a number = hyphenation level (only numbers from 1 to 5 are valid)
21986  if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
21987  if ($j == 0) {
21988  $zero = $pos - 1;
21989  } else {
21990  $zero = $pos + $j - $digits;
21991  }
21992  // get hyphenation level
21993  $level = ($pattern[$j] - 48);
21994  // if two levels from two different patterns match at the same point, the higher one is selected.
21995  if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] < $level)) {
21996  $hyphenword[$zero] = $level;
21997  }
21998  ++$digits;
21999  }
22000  }
22001  }
22002  }
22003  }
22004  $inserted = 0;
22005  $maxpos = $numchars - $rightmin;
22006  for ($i = $leftmin; $i <= $maxpos; ++$i) {
22007  // only odd levels indicate allowed hyphenation points
22008  if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
22009  // 173 = soft hyphen character
22010  array_splice($word, $i + $inserted, 0, 173);
22011  ++$inserted;
22012  }
22013  }
22014  return $word;
22015  }
22016 
22031  public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22032  $text = $this->unhtmlentities($text);
22033  $word = array(); // last word
22034  $txtarr = array(); // text to be returned
22035  $intag = false; // true if we are inside an HTML tag
22036  $skip = false; // true to skip hyphenation
22037  if (!is_array($patterns)) {
22038  $patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patterns);
22039  }
22040  // get array of characters
22041  $unichars = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
22042  // for each char
22043  foreach ($unichars as $char) {
22044  if ((!$intag) AND (!$skip) AND TCPDF_FONT_DATA::$uni_type[$char] == 'L') {
22045  // letter character
22046  $word[] = $char;
22047  } else {
22048  // other type of character
22049  if (!TCPDF_STATIC::empty_string($word)) {
22050  // hypenate the word
22051  $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22052  $word = array();
22053  }
22054  $txtarr[] = $char;
22055  if (chr($char) == '<') {
22056  // we are inside an HTML tag
22057  $intag = true;
22058  } elseif ($intag AND (chr($char) == '>')) {
22059  // end of HTML tag
22060  $intag = false;
22061  // check for style tag
22062  $expected = array(115, 116, 121, 108, 101); // = 'style'
22063  $current = array_slice($txtarr, -6, 5); // last 5 chars
22064  $compare = array_diff($expected, $current);
22065  if (empty($compare)) {
22066  // check if it is a closing tag
22067  $expected = array(47); // = '/'
22068  $current = array_slice($txtarr, -7, 1);
22069  $compare = array_diff($expected, $current);
22070  if (empty($compare)) {
22071  // closing style tag
22072  $skip = false;
22073  } else {
22074  // opening style tag
22075  $skip = true;
22076  }
22077  }
22078  }
22079  }
22080  }
22081  if (!TCPDF_STATIC::empty_string($word)) {
22082  // hypenate the word
22083  $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22084  }
22085  // convert char array to string and return
22086  return TCPDF_FONTS::UTF8ArrSubString($txtarr, '', '', $this->isunicode);
22087  }
22088 
22095  public function setRasterizeVectorImages($mode) {
22096  $this->rasterize_vector_images = $mode;
22097  }
22098 
22106  public function setFontSubsetting($enable=true) {
22107  if ($this->pdfa_mode) {
22108  $this->font_subsetting = false;
22109  } else {
22110  $this->font_subsetting = $enable ? true : false;
22111  }
22112  }
22113 
22121  public function getFontSubsetting() {
22122  return $this->font_subsetting;
22123  }
22124 
22134  public function stringLeftTrim($str, $replace='') {
22135  return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str);
22136  }
22137 
22147  public function stringRightTrim($str, $replace='') {
22148  return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str);
22149  }
22150 
22160  public function stringTrim($str, $replace='') {
22161  $str = $this->stringLeftTrim($str, $replace);
22162  $str = $this->stringRightTrim($str, $replace);
22163  return $str;
22164  }
22165 
22173  public function isUnicodeFont() {
22174  return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0'));
22175  }
22176 
22185  public function getFontFamilyName($fontfamily) {
22186  // remove spaces and symbols
22187  $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily));
22188  // extract all font names
22189  $fontslist = preg_split('/[,]/', $fontfamily);
22190  // find first valid font name
22191  foreach ($fontslist as $font) {
22192  // replace font variations
22193  $font = preg_replace('/regular$/', '', $font);
22194  $font = preg_replace('/italic$/', 'I', $font);
22195  $font = preg_replace('/oblique$/', 'I', $font);
22196  $font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
22197  // replace common family names and core fonts
22198  $pattern = array();
22199  $replacement = array();
22200  $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
22201  $replacement[] = 'times';
22202  $pattern[] = '/^sansserif/';
22203  $replacement[] = 'helvetica';
22204  $pattern[] = '/^monospace/';
22205  $replacement[] = 'courier';
22206  $font = preg_replace($pattern, $replacement, $font);
22207  if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) {
22208  return $font;
22209  }
22210  }
22211  // return current font as default
22212  return $this->CurrentFont['fontkey'];
22213  }
22214 
22229  public function startTemplate($w=0, $h=0, $group=false) {
22230  if ($this->inxobj) {
22231  // we are already inside an XObject template
22232  return false;
22233  }
22234  $this->inxobj = true;
22235  ++$this->n;
22236  // XObject ID
22237  $this->xobjid = 'XT'.$this->n;
22238  // object ID
22239  $this->xobjects[$this->xobjid] = array('n' => $this->n);
22240  // store current graphic state
22241  $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars();
22242  // initialize data
22243  $this->xobjects[$this->xobjid]['intmrk'] = 0;
22244  $this->xobjects[$this->xobjid]['transfmrk'] = array();
22245  $this->xobjects[$this->xobjid]['outdata'] = '';
22246  $this->xobjects[$this->xobjid]['xobjects'] = array();
22247  $this->xobjects[$this->xobjid]['images'] = array();
22248  $this->xobjects[$this->xobjid]['fonts'] = array();
22249  $this->xobjects[$this->xobjid]['annotations'] = array();
22250  $this->xobjects[$this->xobjid]['extgstates'] = array();
22251  $this->xobjects[$this->xobjid]['gradients'] = array();
22252  $this->xobjects[$this->xobjid]['spot_colors'] = array();
22253  // set new environment
22254  $this->num_columns = 1;
22255  $this->current_column = 0;
22256  $this->SetAutoPageBreak(false);
22257  if (($w === '') OR ($w <= 0)) {
22258  $w = $this->w - $this->lMargin - $this->rMargin;
22259  }
22260  if (($h === '') OR ($h <= 0)) {
22261  $h = $this->h - $this->tMargin - $this->bMargin;
22262  }
22263  $this->xobjects[$this->xobjid]['x'] = 0;
22264  $this->xobjects[$this->xobjid]['y'] = 0;
22265  $this->xobjects[$this->xobjid]['w'] = $w;
22266  $this->xobjects[$this->xobjid]['h'] = $h;
22267  $this->w = $w;
22268  $this->h = $h;
22269  $this->wPt = $this->w * $this->k;
22270  $this->hPt = $this->h * $this->k;
22271  $this->fwPt = $this->wPt;
22272  $this->fhPt = $this->hPt;
22273  $this->x = 0;
22274  $this->y = 0;
22275  $this->lMargin = 0;
22276  $this->rMargin = 0;
22277  $this->tMargin = 0;
22278  $this->bMargin = 0;
22279  // set group mode
22280  $this->xobjects[$this->xobjid]['group'] = $group;
22281  return $this->xobjid;
22282  }
22283 
22294  public function endTemplate() {
22295  if (!$this->inxobj) {
22296  // we are not inside a template
22297  return false;
22298  }
22299  $this->inxobj = false;
22300  // restore previous graphic state
22301  $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true);
22302  return $this->xobjid;
22303  }
22304 
22323  public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
22324  if ($this->state != 2) {
22325  return;
22326  }
22327  if (!isset($this->xobjects[$id])) {
22328  $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
22329  }
22330  if ($this->inxobj) {
22331  if ($id == $this->xobjid) {
22332  // close current template
22333  $this->endTemplate();
22334  } else {
22335  // use the template as resource for the template currently opened
22336  $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id];
22337  }
22338  }
22339  // set default values
22340  if ($x === '') {
22341  $x = $this->x;
22342  }
22343  if ($y === '') {
22344  $y = $this->y;
22345  }
22346  // check page for no-write regions and adapt page margins if necessary
22347  list($x, $y) = $this->checkPageRegions($h, $x, $y);
22348  $ow = $this->xobjects[$id]['w'];
22349  if ($ow <= 0) {
22350  $ow = 1;
22351  }
22352  $oh = $this->xobjects[$id]['h'];
22353  if ($oh <= 0) {
22354  $oh = 1;
22355  }
22356  // calculate template width and height on document
22357  if (($w <= 0) AND ($h <= 0)) {
22358  $w = $ow;
22359  $h = $oh;
22360  } elseif ($w <= 0) {
22361  $w = $h * $ow / $oh;
22362  } elseif ($h <= 0) {
22363  $h = $w * $oh / $ow;
22364  }
22365  // fit the template on available space
22366  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22367  // set page alignment
22368  $rb_y = $y + $h;
22369  // set alignment
22370  if ($this->rtl) {
22371  if ($palign == 'L') {
22372  $xt = $this->lMargin;
22373  } elseif ($palign == 'C') {
22374  $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22375  } elseif ($palign == 'R') {
22376  $xt = $this->w - $this->rMargin - $w;
22377  } else {
22378  $xt = $x - $w;
22379  }
22380  $rb_x = $xt;
22381  } else {
22382  if ($palign == 'L') {
22383  $xt = $this->lMargin;
22384  } elseif ($palign == 'C') {
22385  $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22386  } elseif ($palign == 'R') {
22387  $xt = $this->w - $this->rMargin - $w;
22388  } else {
22389  $xt = $x;
22390  }
22391  $rb_x = $xt + $w;
22392  }
22393  // print XObject Template + Transformation matrix
22394  $this->StartTransform();
22395  // translate and scale
22396  $sx = ($w / $ow);
22397  $sy = ($h / $oh);
22398  $tm = array();
22399  $tm[0] = $sx;
22400  $tm[1] = 0;
22401  $tm[2] = 0;
22402  $tm[3] = $sy;
22403  $tm[4] = $xt * $this->k;
22404  $tm[5] = ($this->h - $h - $y) * $this->k;
22405  $this->Transform($tm);
22406  // set object
22407  $this->_out('/'.$id.' Do');
22408  $this->StopTransform();
22409  // add annotations
22410  if (!empty($this->xobjects[$id]['annotations'])) {
22411  foreach ($this->xobjects[$id]['annotations'] as $annot) {
22412  // transform original coordinates
22413  $coordlt = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k)));
22414  $ax = ($coordlt[4] / $this->k);
22415  $ay = ($this->h - $h - ($coordlt[5] / $this->k));
22416  $coordrb = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k)));
22417  $aw = ($coordrb[4] / $this->k) - $ax;
22418  $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay;
22419  $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
22420  }
22421  }
22422  // set pointer to align the next text/objects
22423  switch($align) {
22424  case 'T': {
22425  $this->y = $y;
22426  $this->x = $rb_x;
22427  break;
22428  }
22429  case 'M': {
22430  $this->y = $y + round($h/2);
22431  $this->x = $rb_x;
22432  break;
22433  }
22434  case 'B': {
22435  $this->y = $rb_y;
22436  $this->x = $rb_x;
22437  break;
22438  }
22439  case 'N': {
22440  $this->SetY($rb_y);
22441  break;
22442  }
22443  default:{
22444  break;
22445  }
22446  }
22447  }
22448 
22456  public function setFontStretching($perc=100) {
22457  $this->font_stretching = $perc;
22458  }
22459 
22467  public function getFontStretching() {
22468  return $this->font_stretching;
22469  }
22470 
22478  public function setFontSpacing($spacing=0) {
22479  $this->font_spacing = $spacing;
22480  }
22481 
22489  public function getFontSpacing() {
22490  return $this->font_spacing;
22491  }
22492 
22501  public function getPageRegions() {
22502  return $this->page_regions;
22503  }
22504 
22516  public function setPageRegions($regions=array()) {
22517  // empty current regions array
22518  $this->page_regions = array();
22519  // add regions
22520  foreach ($regions as $data) {
22521  $this->addPageRegion($data);
22522  }
22523  }
22524 
22536  public function addPageRegion($region) {
22537  if (!isset($region['page']) OR empty($region['page'])) {
22538  $region['page'] = $this->page;
22539  }
22540  if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
22541  AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
22542  AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
22543  $this->page_regions[] = $region;
22544  }
22545  }
22546 
22555  public function removePageRegion($key) {
22556  if (isset($this->page_regions[$key])) {
22557  unset($this->page_regions[$key]);
22558  }
22559  }
22560 
22573  protected function checkPageRegions($h, $x, $y) {
22574  // set default values
22575  if ($x === '') {
22576  $x = $this->x;
22577  }
22578  if ($y === '') {
22579  $y = $this->y;
22580  }
22581  if (!$this->check_page_regions OR empty($this->page_regions)) {
22582  // no page regions defined
22583  return array($x, $y);
22584  }
22585  if (empty($h)) {
22586  $h = $this->getCellHeight($this->FontSize);
22587  }
22588  // check for page break
22589  if ($this->checkPageBreak($h, $y)) {
22590  // the content will be printed on a new page
22591  $x = $this->x;
22592  $y = $this->y;
22593  }
22594  if ($this->num_columns > 1) {
22595  if ($this->rtl) {
22596  $this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22597  } else {
22598  $this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22599  }
22600  } else {
22601  if ($this->rtl) {
22602  $this->lMargin = max($this->clMargin, $this->original_lMargin);
22603  } else {
22604  $this->rMargin = max($this->crMargin, $this->original_rMargin);
22605  }
22606  }
22607  // adjust coordinates and page margins
22608  foreach ($this->page_regions as $regid => $regdata) {
22609  if ($regdata['page'] == $this->page) {
22610  // check region boundaries
22611  if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
22612  // Y is inside the region
22613  $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
22614  $yt = max($y, $regdata['yt']);
22615  $yb = min(($yt + $h), $regdata['yb']);
22616  $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt'];
22617  $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt'];
22618  if ($regdata['side'] == 'L') { // left side
22619  $new_margin = max($xt, $xb);
22620  if ($this->lMargin < $new_margin) {
22621  if ($this->rtl) {
22622  // adjust left page margin
22623  $this->lMargin = max(0, $new_margin);
22624  }
22625  if ($x < $new_margin) {
22626  // adjust x position
22627  $x = $new_margin;
22628  if ($new_margin > ($this->w - $this->rMargin)) {
22629  // adjust y position
22630  $y = $regdata['yb'] - $h;
22631  }
22632  }
22633  }
22634  } elseif ($regdata['side'] == 'R') { // right side
22635  $new_margin = min($xt, $xb);
22636  if (($this->w - $this->rMargin) > $new_margin) {
22637  if (!$this->rtl) {
22638  // adjust right page margin
22639  $this->rMargin = max(0, ($this->w - $new_margin));
22640  }
22641  if ($x > $new_margin) {
22642  // adjust x position
22643  $x = $new_margin;
22644  if ($new_margin > $this->lMargin) {
22645  // adjust y position
22646  $y = $regdata['yb'] - $h;
22647  }
22648  }
22649  }
22650  }
22651  }
22652  }
22653  }
22654  return array($x, $y);
22655  }
22656 
22657  // --- SVG METHODS ---------------------------------------------------------
22658 
22676  public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
22677  if ($this->state != 2) {
22678  return;
22679  }
22680  // reset SVG vars
22681  $this->svggradients = array();
22682  $this->svggradientid = 0;
22683  $this->svgdefsmode = false;
22684  $this->svgdefs = array();
22685  $this->svgclipmode = false;
22686  $this->svgclippaths = array();
22687  $this->svgcliptm = array();
22688  $this->svgclipid = 0;
22689  $this->svgtext = '';
22690  $this->svgtextmode = array();
22691  if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
22692  // convert SVG to raster image using GD or ImageMagick libraries
22693  return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22694  }
22695  if ($file[0] === '@') { // image from string
22696  $this->svgdir = '';
22697  $svgdata = substr($file, 1);
22698  } else { // SVG file
22699  $this->svgdir = dirname($file);
22701  }
22702  if ($svgdata === FALSE) {
22703  $this->Error('SVG file not found: '.$file);
22704  }
22705  if ($x === '') {
22706  $x = $this->x;
22707  }
22708  if ($y === '') {
22709  $y = $this->y;
22710  }
22711  // check page for no-write regions and adapt page margins if necessary
22712  list($x, $y) = $this->checkPageRegions($h, $x, $y);
22713  $k = $this->k;
22714  $ox = 0;
22715  $oy = 0;
22716  $ow = $w;
22717  $oh = $h;
22718  $aspect_ratio_align = 'xMidYMid';
22719  $aspect_ratio_ms = 'meet';
22720  $regs = array();
22721  // get original image width and height
22722  preg_match('/<svg([^>]*)>/si', $svgdata, $regs);
22723  if (isset($regs[1]) AND !empty($regs[1])) {
22724  $tmp = array();
22725  if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22726  $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22727  }
22728  $tmp = array();
22729  if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22730  $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22731  }
22732  $tmp = array();
22733  if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22734  $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22735  }
22736  $tmp = array();
22737  if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22738  $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22739  }
22740  $tmp = array();
22741  $view_box = array();
22742  if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
22743  if (count($tmp) == 5) {
22744  array_shift($tmp);
22745  foreach ($tmp as $key => $val) {
22746  $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
22747  }
22748  $ox = $view_box[0];
22749  $oy = $view_box[1];
22750  }
22751  // get aspect ratio
22752  $tmp = array();
22753  if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22754  $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
22755  switch (count($aspect_ratio)) {
22756  case 3: {
22757  $aspect_ratio_align = $aspect_ratio[1];
22758  $aspect_ratio_ms = $aspect_ratio[2];
22759  break;
22760  }
22761  case 2: {
22762  $aspect_ratio_align = $aspect_ratio[0];
22763  $aspect_ratio_ms = $aspect_ratio[1];
22764  break;
22765  }
22766  case 1: {
22767  $aspect_ratio_align = $aspect_ratio[0];
22768  $aspect_ratio_ms = 'meet';
22769  break;
22770  }
22771  }
22772  }
22773  }
22774  }
22775  if ($ow <= 0) {
22776  $ow = 1;
22777  }
22778  if ($oh <= 0) {
22779  $oh = 1;
22780  }
22781  // calculate image width and height on document
22782  if (($w <= 0) AND ($h <= 0)) {
22783  // convert image size to document unit
22784  $w = $ow;
22785  $h = $oh;
22786  } elseif ($w <= 0) {
22787  $w = $h * $ow / $oh;
22788  } elseif ($h <= 0) {
22789  $h = $w * $oh / $ow;
22790  }
22791  // fit the image on available space
22792  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22793  if ($this->rasterize_vector_images) {
22794  // convert SVG to raster image using GD or ImageMagick libraries
22795  return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22796  }
22797  // set alignment
22798  $this->img_rb_y = $y + $h;
22799  // set alignment
22800  if ($this->rtl) {
22801  if ($palign == 'L') {
22802  $ximg = $this->lMargin;
22803  } elseif ($palign == 'C') {
22804  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22805  } elseif ($palign == 'R') {
22806  $ximg = $this->w - $this->rMargin - $w;
22807  } else {
22808  $ximg = $x - $w;
22809  }
22810  $this->img_rb_x = $ximg;
22811  } else {
22812  if ($palign == 'L') {
22813  $ximg = $this->lMargin;
22814  } elseif ($palign == 'C') {
22815  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22816  } elseif ($palign == 'R') {
22817  $ximg = $this->w - $this->rMargin - $w;
22818  } else {
22819  $ximg = $x;
22820  }
22821  $this->img_rb_x = $ximg + $w;
22822  }
22823  // store current graphic vars
22824  $gvars = $this->getGraphicVars();
22825  // store SVG position and scale factors
22826  $svgoffset_x = ($ximg - $ox) * $this->k;
22827  $svgoffset_y = -($y - $oy) * $this->k;
22828  if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
22829  $ow = $view_box[2];
22830  $oh = $view_box[3];
22831  } else {
22832  if ($ow <= 0) {
22833  $ow = $w;
22834  }
22835  if ($oh <= 0) {
22836  $oh = $h;
22837  }
22838  }
22839  $svgscale_x = $w / $ow;
22840  $svgscale_y = $h / $oh;
22841  // scaling and alignment
22842  if ($aspect_ratio_align != 'none') {
22843  // store current scaling values
22844  $svgscale_old_x = $svgscale_x;
22845  $svgscale_old_y = $svgscale_y;
22846  // force uniform scaling
22847  if ($aspect_ratio_ms == 'slice') {
22848  // the entire viewport is covered by the viewBox
22849  if ($svgscale_x > $svgscale_y) {
22850  $svgscale_y = $svgscale_x;
22851  } elseif ($svgscale_x < $svgscale_y) {
22852  $svgscale_x = $svgscale_y;
22853  }
22854  } else { // meet
22855  // the entire viewBox is visible within the viewport
22856  if ($svgscale_x < $svgscale_y) {
22857  $svgscale_y = $svgscale_x;
22858  } elseif ($svgscale_x > $svgscale_y) {
22859  $svgscale_x = $svgscale_y;
22860  }
22861  }
22862  // correct X alignment
22863  switch (substr($aspect_ratio_align, 1, 3)) {
22864  case 'Min': {
22865  // do nothing
22866  break;
22867  }
22868  case 'Max': {
22869  $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
22870  break;
22871  }
22872  default:
22873  case 'Mid': {
22874  $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
22875  break;
22876  }
22877  }
22878  // correct Y alignment
22879  switch (substr($aspect_ratio_align, 5)) {
22880  case 'Min': {
22881  // do nothing
22882  break;
22883  }
22884  case 'Max': {
22885  $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
22886  break;
22887  }
22888  default:
22889  case 'Mid': {
22890  $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
22891  break;
22892  }
22893  }
22894  }
22895  // store current page break mode
22896  $page_break_mode = $this->AutoPageBreak;
22897  $page_break_margin = $this->getBreakMargin();
22899  $this->SetCellPadding(0);
22900  $this->SetAutoPageBreak(false);
22901  // save the current graphic state
22902  $this->_out('q'.$this->epsmarker);
22903  // set initial clipping mask
22904  $this->Rect($ximg, $y, $w, $h, 'CNZ', array(), array());
22905  // scale and translate
22906  $e = $ox * $this->k * (1 - $svgscale_x);
22907  $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
22908  $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y)));
22909  // creates a new XML parser to be used by the other XML functions
22910  $this->parser = xml_parser_create('UTF-8');
22911  // the following function allows to use parser inside object
22912  xml_set_object($this->parser, $this);
22913  // disable case-folding for this XML parser
22914  xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
22915  // sets the element handler functions for the XML parser
22916  xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler');
22917  // sets the character data handler function for the XML parser
22918  xml_set_character_data_handler($this->parser, 'segSVGContentHandler');
22919  // start parsing an XML document
22920  if (!xml_parse($this->parser, $svgdata)) {
22921  $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));
22922  $this->Error($error_message);
22923  }
22924  // free this XML parser
22925  xml_parser_free($this->parser);
22926  // restore previous graphic state
22927  $this->_out($this->epsmarker.'Q');
22928  // restore graphic vars
22929  $this->setGraphicVars($gvars);
22930  $this->lasth = $gvars['lasth'];
22931  if (!empty($border)) {
22932  $bx = $this->x;
22933  $by = $this->y;
22934  $this->x = $ximg;
22935  if ($this->rtl) {
22936  $this->x += $w;
22937  }
22938  $this->y = $y;
22939  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
22940  $this->x = $bx;
22941  $this->y = $by;
22942  }
22943  if ($link) {
22944  $this->Link($ximg, $y, $w, $h, $link, 0);
22945  }
22946  // set pointer to align the next text/objects
22947  switch($align) {
22948  case 'T':{
22949  $this->y = $y;
22950  $this->x = $this->img_rb_x;
22951  break;
22952  }
22953  case 'M':{
22954  $this->y = $y + round($h/2);
22955  $this->x = $this->img_rb_x;
22956  break;
22957  }
22958  case 'B':{
22959  $this->y = $this->img_rb_y;
22960  $this->x = $this->img_rb_x;
22961  break;
22962  }
22963  case 'N':{
22964  $this->SetY($this->img_rb_y);
22965  break;
22966  }
22967  default:{
22968  // restore pointer to starting position
22969  $this->x = $gvars['x'];
22970  $this->y = $gvars['y'];
22971  $this->page = $gvars['page'];
22972  $this->current_column = $gvars['current_column'];
22973  $this->tMargin = $gvars['tMargin'];
22974  $this->bMargin = $gvars['bMargin'];
22975  $this->w = $gvars['w'];
22976  $this->h = $gvars['h'];
22977  $this->wPt = $gvars['wPt'];
22978  $this->hPt = $gvars['hPt'];
22979  $this->fwPt = $gvars['fwPt'];
22980  $this->fhPt = $gvars['fhPt'];
22981  break;
22982  }
22983  }
22984  $this->endlinex = $this->img_rb_x;
22985  // restore page break
22986  $this->SetAutoPageBreak($page_break_mode, $page_break_margin);
22987  $this->cell_padding = $cell_padding;
22988  }
22989 
22997  protected function convertSVGtMatrix($tm) {
22998  $a = $tm[0];
22999  $b = -$tm[1];
23000  $c = -$tm[2];
23001  $d = $tm[3];
23002  $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
23003  $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
23004  $x = 0;
23005  $y = $this->h * $this->k;
23006  $e = ($x * (1 - $a)) - ($y * $c) + $e;
23007  $f = ($y * (1 - $d)) - ($x * $b) + $f;
23008  return array($a, $b, $c, $d, $e, $f);
23009  }
23010 
23017  protected function SVGTransform($tm) {
23018  $this->Transform($this->convertSVGtMatrix($tm));
23019  }
23020 
23036  protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
23037  if ($this->state != 2) {
23038  return;
23039  }
23040  $objstyle = '';
23041  $minlen = (0.01 / $this->k); // minimum acceptable length
23042  if (!isset($svgstyle['opacity'])) {
23043  return $objstyle;
23044  }
23045  // clip-path
23046  $regs = array();
23047  if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
23048  $clip_path = $this->svgclippaths[$regs[1]];
23049  foreach ($clip_path as $cp) {
23050  $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
23051  }
23052  }
23053  // opacity
23054  if ($svgstyle['opacity'] != 1) {
23055  $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false);
23056  }
23057  // color
23058  $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors);
23059  $this->SetFillColorArray($fill_color);
23060  // text color
23061  $text_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors);
23062  $this->SetTextColorArray($text_color);
23063  // clip
23064  if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
23065  $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
23066  $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
23067  $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
23068  $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
23069  $cx = $x + $left;
23070  $cy = $y + $top;
23071  $cw = $w - $left - $right;
23072  $ch = $h - $top - $bottom;
23073  if ($svgstyle['clip-rule'] == 'evenodd') {
23074  $clip_rule = 'CNZ';
23075  } else {
23076  $clip_rule = 'CEO';
23077  }
23078  $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
23079  }
23080  // fill
23081  $regs = array();
23082  if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
23083  // gradient
23084  $gradient = $this->svggradients[$regs[1]];
23085  if (isset($gradient['xref'])) {
23086  // reference to another gradient definition
23087  $newgradient = $this->svggradients[$gradient['xref']];
23088  $newgradient['coords'] = $gradient['coords'];
23089  $newgradient['mode'] = $gradient['mode'];
23090  $newgradient['type'] = $gradient['type'];
23091  $newgradient['gradientUnits'] = $gradient['gradientUnits'];
23092  if (isset($gradient['gradientTransform'])) {
23093  $newgradient['gradientTransform'] = $gradient['gradientTransform'];
23094  }
23095  $gradient = $newgradient;
23096  }
23097  //save current Graphic State
23098  $this->_outSaveGraphicsState();
23099  //set clipping area
23100  if (!empty($clip_function) AND method_exists($this, $clip_function)) {
23101  $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
23102  if ((!isset($gradient['type']) OR ($gradient['type'] != 3)) AND is_array($bbox) AND (count($bbox) == 4)) {
23103  list($x, $y, $w, $h) = $bbox;
23104  }
23105  }
23106  if ($gradient['mode'] == 'measure') {
23107  if (!isset($gradient['coords'][4])) {
23108  $gradient['coords'][4] = 0.5;
23109  }
23110  if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
23111  $gtm = $gradient['gradientTransform'];
23112  // apply transformation matrix
23113  $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
23114  $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
23115  $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
23116  $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
23117  $r = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
23118  $gradient['coords'][0] = $xa;
23119  $gradient['coords'][1] = $ya;
23120  $gradient['coords'][2] = $xb;
23121  $gradient['coords'][3] = $yb;
23122  $gradient['coords'][4] = $r;
23123  }
23124  // convert SVG coordinates to user units
23125  $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
23126  $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
23127  $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
23128  $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
23129  $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
23130  if ($w <= $minlen) {
23131  $w = $minlen;
23132  }
23133  if ($h <= $minlen) {
23134  $h = $minlen;
23135  }
23136  // shift units
23137  if ($gradient['gradientUnits'] == 'objectBoundingBox') {
23138  // convert to SVG coordinate system
23139  $gradient['coords'][0] += $x;
23140  $gradient['coords'][1] += $y;
23141  $gradient['coords'][2] += $x;
23142  $gradient['coords'][3] += $y;
23143  }
23144  // calculate percentages
23145  $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w);
23146  $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h);
23147  $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w);
23148  $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h);
23149  $gradient['coords'][4] /= $w;
23150  } elseif ($gradient['mode'] == 'percentage') {
23151  foreach($gradient['coords'] as $key => $val) {
23152  $gradient['coords'][$key] = (intval($val) / 100);
23153  if ($val < 0) {
23154  $gradient['coords'][$key] = 0;
23155  } elseif ($val > 1) {
23156  $gradient['coords'][$key] = 1;
23157  }
23158  }
23159  }
23160  if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
23161  // single color (no shading)
23162  $gradient['coords'][0] = 1;
23163  $gradient['coords'][1] = 0;
23164  $gradient['coords'][2] = 0.999;
23165  $gradient['coords'][3] = 0;
23166  }
23167  // swap Y coordinates
23168  $tmp = $gradient['coords'][1];
23169  $gradient['coords'][1] = $gradient['coords'][3];
23170  $gradient['coords'][3] = $tmp;
23171  // set transformation map for gradient
23172  $cy = ($this->h - $y);
23173  if ($gradient['type'] == 3) {
23174  // circular gradient
23175  $cy -= ($gradient['coords'][1] * ($w + $h));
23176  $h = $w = max($w, $h);
23177  } else {
23178  $cy -= $h;
23179  }
23180  $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), ($cy * $this->k)));
23181  if (count($gradient['stops']) > 1) {
23182  $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
23183  }
23184  } elseif ($svgstyle['fill'] != 'none') {
23185  $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors);
23186  if ($svgstyle['fill-opacity'] != 1) {
23187  $this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false);
23188  }
23189  $this->SetFillColorArray($fill_color);
23190  if ($svgstyle['fill-rule'] == 'evenodd') {
23191  $objstyle .= 'F*';
23192  } else {
23193  $objstyle .= 'F';
23194  }
23195  }
23196  // stroke
23197  if ($svgstyle['stroke'] != 'none') {
23198  if ($svgstyle['stroke-opacity'] != 1) {
23199  $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false);
23200  } elseif (preg_match('/rgba\(\d+%?,\s*\d+%?,\s*\d+%?,\s*(\d+(?:\.\d+)?)\)/i', $svgstyle['stroke'], $rgba_matches)) {
23201  $this->setAlpha($rgba_matches[1], 'Normal', $this->alpha['ca'], false);
23202  }
23203  $stroke_style = array(
23204  'color' => TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors),
23205  'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
23206  'cap' => $svgstyle['stroke-linecap'],
23207  'join' => $svgstyle['stroke-linejoin']
23208  );
23209  if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
23210  $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
23211  }
23212  $this->SetLineStyle($stroke_style);
23213  $objstyle .= 'D';
23214  }
23215  // font
23216  $regs = array();
23217  if (!empty($svgstyle['font'])) {
23218  if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
23219  $font_family = $this->getFontFamilyName($regs[1]);
23220  } else {
23221  $font_family = $svgstyle['font-family'];
23222  }
23223  if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23224  $font_size = trim($regs[1]);
23225  } else {
23226  $font_size = $svgstyle['font-size'];
23227  }
23228  if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23229  $font_style = trim($regs[1]);
23230  } else {
23231  $font_style = $svgstyle['font-style'];
23232  }
23233  if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23234  $font_weight = trim($regs[1]);
23235  } else {
23236  $font_weight = $svgstyle['font-weight'];
23237  }
23238  if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23239  $font_stretch = trim($regs[1]);
23240  } else {
23241  $font_stretch = $svgstyle['font-stretch'];
23242  }
23243  if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23244  $font_spacing = trim($regs[1]);
23245  } else {
23246  $font_spacing = $svgstyle['letter-spacing'];
23247  }
23248  } else {
23249  $font_family = $this->getFontFamilyName($svgstyle['font-family']);
23250  $font_size = $svgstyle['font-size'];
23251  $font_style = $svgstyle['font-style'];
23252  $font_weight = $svgstyle['font-weight'];
23253  $font_stretch = $svgstyle['font-stretch'];
23254  $font_spacing = $svgstyle['letter-spacing'];
23255  }
23256  $font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit);
23257  $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
23258  $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
23259  switch ($font_style) {
23260  case 'italic': {
23261  $font_style = 'I';
23262  break;
23263  }
23264  case 'oblique': {
23265  $font_style = 'I';
23266  break;
23267  }
23268  default:
23269  case 'normal': {
23270  $font_style = '';
23271  break;
23272  }
23273  }
23274  switch ($font_weight) {
23275  case 'bold':
23276  case 'bolder': {
23277  $font_style .= 'B';
23278  break;
23279  }
23280  case 'normal': {
23281  if ((substr($font_family, -1) == 'I') AND (substr($font_family, -2, 1) == 'B')) {
23282  $font_family = substr($font_family, 0, -2).'I';
23283  } elseif (substr($font_family, -1) == 'B') {
23284  $font_family = substr($font_family, 0, -1);
23285  }
23286  break;
23287  }
23288  }
23289  switch ($svgstyle['text-decoration']) {
23290  case 'underline': {
23291  $font_style .= 'U';
23292  break;
23293  }
23294  case 'overline': {
23295  $font_style .= 'O';
23296  break;
23297  }
23298  case 'line-through': {
23299  $font_style .= 'D';
23300  break;
23301  }
23302  default:
23303  case 'none': {
23304  break;
23305  }
23306  }
23307  $this->SetFont($font_family, $font_style, $font_size);
23308  $this->setFontStretching($font_stretch);
23309  $this->setFontSpacing($font_spacing);
23310  return $objstyle;
23311  }
23312 
23331  protected function SVGPath($d, $style='') {
23332  if ($this->state != 2) {
23333  return;
23334  }
23335  // set fill/stroke style
23337  if (empty($op)) {
23338  return;
23339  }
23340  $paths = array();
23341  $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
23342  preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER);
23343  $x = 0;
23344  $y = 0;
23345  $x1 = 0;
23346  $y1 = 0;
23347  $x2 = 0;
23348  $y2 = 0;
23349  $xmin = 2147483647;
23350  $xmax = 0;
23351  $ymin = 2147483647;
23352  $ymax = 0;
23353  $relcoord = false;
23354  $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
23355  $firstcmd = true; // used to print first point
23356  // draw curve pieces
23357  foreach ($paths as $key => $val) {
23358  // get curve type
23359  $cmd = trim($val[1]);
23360  if (strtolower($cmd) == $cmd) {
23361  // use relative coordinated instead of absolute
23362  $relcoord = true;
23363  $xoffset = $x;
23364  $yoffset = $y;
23365  } else {
23366  $relcoord = false;
23367  $xoffset = 0;
23368  $yoffset = 0;
23369  }
23370  $params = array();
23371  if (isset($val[2])) {
23372  // get curve parameters
23373  $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
23374  $params = array();
23375  foreach ($rawparams as $ck => $cp) {
23376  $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
23377  if (abs($params[$ck]) < $minlen) {
23378  // approximate little values to zero
23379  $params[$ck] = 0;
23380  }
23381  }
23382  }
23383  // store current origin point
23384  $x0 = $x;
23385  $y0 = $y;
23386  switch (strtoupper($cmd)) {
23387  case 'M': { // moveto
23388  foreach ($params as $ck => $cp) {
23389  if (($ck % 2) == 0) {
23390  $x = $cp + $xoffset;
23391  } else {
23392  $y = $cp + $yoffset;
23393  if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23394  if ($ck == 1) {
23395  $this->_outPoint($x, $y);
23396  $firstcmd = false;
23397  } else {
23398  $this->_outLine($x, $y);
23399  }
23400  $x0 = $x;
23401  $y0 = $y;
23402  }
23403  $xmin = min($xmin, $x);
23404  $ymin = min($ymin, $y);
23405  $xmax = max($xmax, $x);
23406  $ymax = max($ymax, $y);
23407  if ($relcoord) {
23408  $xoffset = $x;
23409  $yoffset = $y;
23410  }
23411  }
23412  }
23413  break;
23414  }
23415  case 'L': { // lineto
23416  foreach ($params as $ck => $cp) {
23417  if (($ck % 2) == 0) {
23418  $x = $cp + $xoffset;
23419  } else {
23420  $y = $cp + $yoffset;
23421  if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23422  $this->_outLine($x, $y);
23423  $x0 = $x;
23424  $y0 = $y;
23425  }
23426  $xmin = min($xmin, $x);
23427  $ymin = min($ymin, $y);
23428  $xmax = max($xmax, $x);
23429  $ymax = max($ymax, $y);
23430  if ($relcoord) {
23431  $xoffset = $x;
23432  $yoffset = $y;
23433  }
23434  }
23435  }
23436  break;
23437  }
23438  case 'H': { // horizontal lineto
23439  foreach ($params as $ck => $cp) {
23440  $x = $cp + $xoffset;
23441  if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23442  $this->_outLine($x, $y);
23443  $x0 = $x;
23444  $y0 = $y;
23445  }
23446  $xmin = min($xmin, $x);
23447  $xmax = max($xmax, $x);
23448  if ($relcoord) {
23449  $xoffset = $x;
23450  }
23451  }
23452  break;
23453  }
23454  case 'V': { // vertical lineto
23455  foreach ($params as $ck => $cp) {
23456  $y = $cp + $yoffset;
23457  if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23458  $this->_outLine($x, $y);
23459  $x0 = $x;
23460  $y0 = $y;
23461  }
23462  $ymin = min($ymin, $y);
23463  $ymax = max($ymax, $y);
23464  if ($relcoord) {
23465  $yoffset = $y;
23466  }
23467  }
23468  break;
23469  }
23470  case 'C': { // curveto
23471  foreach ($params as $ck => $cp) {
23472  $params[$ck] = $cp;
23473  if ((($ck + 1) % 6) == 0) {
23474  $x1 = $params[($ck - 5)] + $xoffset;
23475  $y1 = $params[($ck - 4)] + $yoffset;
23476  $x2 = $params[($ck - 3)] + $xoffset;
23477  $y2 = $params[($ck - 2)] + $yoffset;
23478  $x = $params[($ck - 1)] + $xoffset;
23479  $y = $params[($ck)] + $yoffset;
23480  $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23481  $xmin = min($xmin, $x, $x1, $x2);
23482  $ymin = min($ymin, $y, $y1, $y2);
23483  $xmax = max($xmax, $x, $x1, $x2);
23484  $ymax = max($ymax, $y, $y1, $y2);
23485  if ($relcoord) {
23486  $xoffset = $x;
23487  $yoffset = $y;
23488  }
23489  }
23490  }
23491  break;
23492  }
23493  case 'S': { // shorthand/smooth curveto
23494  foreach ($params as $ck => $cp) {
23495  $params[$ck] = $cp;
23496  if ((($ck + 1) % 4) == 0) {
23497  if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
23498  $x1 = (2 * $x) - $x2;
23499  $y1 = (2 * $y) - $y2;
23500  } else {
23501  $x1 = $x;
23502  $y1 = $y;
23503  }
23504  $x2 = $params[($ck - 3)] + $xoffset;
23505  $y2 = $params[($ck - 2)] + $yoffset;
23506  $x = $params[($ck - 1)] + $xoffset;
23507  $y = $params[($ck)] + $yoffset;
23508  $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23509  $xmin = min($xmin, $x, $x1, $x2);
23510  $ymin = min($ymin, $y, $y1, $y2);
23511  $xmax = max($xmax, $x, $x1, $x2);
23512  $ymax = max($ymax, $y, $y1, $y2);
23513  if ($relcoord) {
23514  $xoffset = $x;
23515  $yoffset = $y;
23516  }
23517  }
23518  }
23519  break;
23520  }
23521  case 'Q': { // quadratic Bezier curveto
23522  foreach ($params as $ck => $cp) {
23523  $params[$ck] = $cp;
23524  if ((($ck + 1) % 4) == 0) {
23525  // convert quadratic points to cubic points
23526  $x1 = $params[($ck - 3)] + $xoffset;
23527  $y1 = $params[($ck - 2)] + $yoffset;
23528  $xa = ($x + (2 * $x1)) / 3;
23529  $ya = ($y + (2 * $y1)) / 3;
23530  $x = $params[($ck - 1)] + $xoffset;
23531  $y = $params[($ck)] + $yoffset;
23532  $xb = ($x + (2 * $x1)) / 3;
23533  $yb = ($y + (2 * $y1)) / 3;
23534  $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23535  $xmin = min($xmin, $x, $xa, $xb);
23536  $ymin = min($ymin, $y, $ya, $yb);
23537  $xmax = max($xmax, $x, $xa, $xb);
23538  $ymax = max($ymax, $y, $ya, $yb);
23539  if ($relcoord) {
23540  $xoffset = $x;
23541  $yoffset = $y;
23542  }
23543  }
23544  }
23545  break;
23546  }
23547  case 'T': { // shorthand/smooth quadratic Bezier curveto
23548  foreach ($params as $ck => $cp) {
23549  $params[$ck] = $cp;
23550  if (($ck % 2) != 0) {
23551  if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
23552  $x1 = (2 * $x) - $x1;
23553  $y1 = (2 * $y) - $y1;
23554  } else {
23555  $x1 = $x;
23556  $y1 = $y;
23557  }
23558  // convert quadratic points to cubic points
23559  $xa = ($x + (2 * $x1)) / 3;
23560  $ya = ($y + (2 * $y1)) / 3;
23561  $x = $params[($ck - 1)] + $xoffset;
23562  $y = $params[($ck)] + $yoffset;
23563  $xb = ($x + (2 * $x1)) / 3;
23564  $yb = ($y + (2 * $y1)) / 3;
23565  $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23566  $xmin = min($xmin, $x, $xa, $xb);
23567  $ymin = min($ymin, $y, $ya, $yb);
23568  $xmax = max($xmax, $x, $xa, $xb);
23569  $ymax = max($ymax, $y, $ya, $yb);
23570  if ($relcoord) {
23571  $xoffset = $x;
23572  $yoffset = $y;
23573  }
23574  }
23575  }
23576  break;
23577  }
23578  case 'A': { // elliptical arc
23579  foreach ($params as $ck => $cp) {
23580  $params[$ck] = $cp;
23581  if ((($ck + 1) % 7) == 0) {
23582  $x0 = $x;
23583  $y0 = $y;
23584  $rx = abs($params[($ck - 6)]);
23585  $ry = abs($params[($ck - 5)]);
23586  $ang = -$rawparams[($ck - 4)];
23587  $angle = deg2rad($ang);
23588  $fa = $rawparams[($ck - 3)]; // large-arc-flag
23589  $fs = $rawparams[($ck - 2)]; // sweep-flag
23590  $x = $params[($ck - 1)] + $xoffset;
23591  $y = $params[$ck] + $yoffset;
23592  if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
23593  // endpoints are almost identical
23594  $xmin = min($xmin, $x);
23595  $ymin = min($ymin, $y);
23596  $xmax = max($xmax, $x);
23597  $ymax = max($ymax, $y);
23598  } else {
23599  $cos_ang = cos($angle);
23600  $sin_ang = sin($angle);
23601  $a = (($x0 - $x) / 2);
23602  $b = (($y0 - $y) / 2);
23603  $xa = ($a * $cos_ang) - ($b * $sin_ang);
23604  $ya = ($a * $sin_ang) + ($b * $cos_ang);
23605  $rx2 = $rx * $rx;
23606  $ry2 = $ry * $ry;
23607  $xa2 = $xa * $xa;
23608  $ya2 = $ya * $ya;
23609  $delta = ($xa2 / $rx2) + ($ya2 / $ry2);
23610  if ($delta > 1) {
23611  $rx *= sqrt($delta);
23612  $ry *= sqrt($delta);
23613  $rx2 = $rx * $rx;
23614  $ry2 = $ry * $ry;
23615  }
23616  $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
23617  if ($numerator < 0) {
23618  $root = 0;
23619  } else {
23620  $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
23621  }
23622  if ($fa == $fs){
23623  $root *= -1;
23624  }
23625  $cax = $root * (($rx * $ya) / $ry);
23626  $cay = -$root * (($ry * $xa) / $rx);
23627  // coordinates of ellipse center
23628  $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
23629  $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
23630  // get angles
23631  $angs = TCPDF_STATIC::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
23632  $dang = TCPDF_STATIC::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
23633  if (($fs == 0) AND ($dang > 0)) {
23634  $dang -= (2 * M_PI);
23635  } elseif (($fs == 1) AND ($dang < 0)) {
23636  $dang += (2 * M_PI);
23637  }
23638  $angf = $angs - $dang;
23639  if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
23640  // reverse angles
23641  $tmp = $angs;
23642  $angs = $angf;
23643  $angf = $tmp;
23644  }
23645  $angs = round(rad2deg($angs), 6);
23646  $angf = round(rad2deg($angf), 6);
23647  // covent angles to positive values
23648  if (($angs < 0) AND ($angf < 0)) {
23649  $angs += 360;
23650  $angf += 360;
23651  }
23652  $pie = false;
23653  if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
23654  $pie = true;
23655  }
23656  list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
23657  $xmin = min($xmin, $x, $axmin);
23658  $ymin = min($ymin, $y, $aymin);
23659  $xmax = max($xmax, $x, $axmax);
23660  $ymax = max($ymax, $y, $aymax);
23661  }
23662  if ($relcoord) {
23663  $xoffset = $x;
23664  $yoffset = $y;
23665  }
23666  }
23667  }
23668  break;
23669  }
23670  case 'Z': {
23671  $this->_out('h');
23672  break;
23673  }
23674  }
23675  $firstcmd = false;
23676  } // end foreach
23677  if (!empty($op)) {
23678  $this->_out($op);
23679  }
23680  return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
23681  }
23682 
23688  protected function removeTagNamespace($name) {
23689  if(strpos($name, ':') !== false) {
23690  $parts = explode(':', $name);
23691  return $parts[(sizeof($parts) - 1)];
23692  }
23693  return $name;
23694  }
23695 
23706  protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
23707  $name = $this->removeTagNamespace($name);
23708  // check if we are in clip mode
23709  if ($this->svgclipmode) {
23710  $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]);
23711  return;
23712  }
23713  if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
23714  if (isset($attribs['id'])) {
23715  $attribs['child_elements'] = array();
23716  $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23717  return;
23718  }
23719  if (end($this->svgdefs) !== FALSE) {
23720  $last_svgdefs_id = key($this->svgdefs);
23721  if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
23722  $attribs['id'] = 'DF_'.(count($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements']) + 1);
23723  $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23724  return;
23725  }
23726  }
23727  return;
23728  }
23729  $clipping = false;
23730  if ($parser == 'clip-path') {
23731  // set clipping mode
23732  $clipping = true;
23733  }
23734  // get styling properties
23735  $prev_svgstyle = $this->svgstyles[max(0,(count($this->svgstyles) - 1))]; // previous style
23736  $svgstyle = $this->svgstyles[0]; // set default style
23737  if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) {
23738  // default fill attribute for clipping
23739  $attribs['fill'] = 'none';
23740  }
23741  if (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) {
23742  // fix style for regular expression
23743  $attribs['style'] = ';'.$attribs['style'];
23744  }
23745  foreach ($prev_svgstyle as $key => $val) {
23746  if (in_array($key, TCPDF_IMAGES::$svginheritprop)) {
23747  // inherit previous value
23748  $svgstyle[$key] = $val;
23749  }
23750  if (isset($attribs[$key]) AND !TCPDF_STATIC::empty_string($attribs[$key])) {
23751  // specific attribute settings
23752  if ($attribs[$key] == 'inherit') {
23753  $svgstyle[$key] = $val;
23754  } else {
23755  $svgstyle[$key] = $attribs[$key];
23756  }
23757  } elseif (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) {
23758  // CSS style syntax
23759  $attrval = array();
23760  if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
23761  if ($attrval[1] == 'inherit') {
23762  $svgstyle[$key] = $val;
23763  } else {
23764  $svgstyle[$key] = $attrval[1];
23765  }
23766  }
23767  }
23768  }
23769  // transformation matrix
23770  if (!empty($ctm)) {
23771  $tm = $ctm;
23772  } else {
23773  $tm = array(1,0,0,1,0,0);
23774  }
23775  if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
23777  }
23778  $svgstyle['transfmatrix'] = $tm;
23779  $invisible = false;
23780  if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
23781  // the current graphics element is invisible (nothing is painted)
23782  $invisible = true;
23783  }
23784  // process tag
23785  switch($name) {
23786  case 'defs': {
23787  $this->svgdefsmode = true;
23788  break;
23789  }
23790  // clipPath
23791  case 'clipPath': {
23792  if ($invisible) {
23793  break;
23794  }
23795  $this->svgclipmode = true;
23796  if (!isset($attribs['id'])) {
23797  $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1);
23798  }
23799  $this->svgclipid = $attribs['id'];
23800  $this->svgclippaths[$this->svgclipid] = array();
23801  $this->svgcliptm[$this->svgclipid] = $tm;
23802  break;
23803  }
23804  case 'svg': {
23805  // start of SVG object
23806  if(++$this->svg_tag_depth <= 1) {
23807  break;
23808  }
23809  // inner SVG
23810  array_push($this->svgstyles, $svgstyle);
23811  $this->StartTransform();
23812  $svgX = (isset($attribs['x'])?$attribs['x']:0);
23813  $svgY = (isset($attribs['y'])?$attribs['y']:0);
23814  $svgW = (isset($attribs['width'])?$attribs['width']:0);
23815  $svgH = (isset($attribs['height'])?$attribs['height']:0);
23816  // set x, y position using transform matrix
23817  $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array( 1, 0, 0, 1, $svgX, $svgY));
23818  $this->SVGTransform($tm);
23819  // set clipping for width and height
23820  $x = 0;
23821  $y = 0;
23822  $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):$this->w);
23823  $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):$this->h);
23824  // draw clipping rect
23825  $this->Rect($x, $y, $w, $h, 'CNZ', array(), array());
23826  // parse viewbox, calculate extra transformation matrix
23827  if (isset($attribs['viewBox'])) {
23828  $tmp = array();
23829  preg_match_all("/[0-9]+/", $attribs['viewBox'], $tmp);
23830  $tmp = $tmp[0];
23831  if (sizeof($tmp) == 4) {
23832  $vx = $tmp[0];
23833  $vy = $tmp[1];
23834  $vw = $tmp[2];
23835  $vh = $tmp[3];
23836  // get aspect ratio
23837  $tmp = array();
23838  $aspectX = 'xMid';
23839  $aspectY = 'YMid';
23840  $fit = 'meet';
23841  if (isset($attribs['preserveAspectRatio'])) {
23842  if($attribs['preserveAspectRatio'] == 'none') {
23843  $fit = 'none';
23844  } else {
23845  preg_match_all('/[a-zA-Z]+/', $attribs['preserveAspectRatio'], $tmp);
23846  $tmp = $tmp[0];
23847  if ((sizeof($tmp) == 2) AND (strlen($tmp[0]) == 8) AND (in_array($tmp[1], array('meet', 'slice', 'none')))) {
23848  $aspectX = substr($tmp[0], 0, 4);
23849  $aspectY = substr($tmp[0], 4, 4);
23850  $fit = $tmp[1];
23851  }
23852  }
23853  }
23854  $wr = ($svgW / $vw);
23855  $hr = ($svgH / $vh);
23856  $ax = $ay = 0;
23857  if ((($fit == 'meet') AND ($hr < $wr)) OR (($fit == 'slice') AND ($hr > $wr))) {
23858  if ($aspectX == 'xMax') {
23859  $ax = (($vw * ($wr / $hr)) - $vw);
23860  }
23861  if ($aspectX == 'xMid') {
23862  $ax = ((($vw * ($wr / $hr)) - $vw) / 2);
23863  }
23864  $wr = $hr;
23865  } elseif ((($fit == 'meet') AND ($hr > $wr)) OR (($fit == 'slice') AND ($hr < $wr))) {
23866  if ($aspectY == 'YMax') {
23867  $ay = (($vh * ($hr / $wr)) - $vh);
23868  }
23869  if ($aspectY == 'YMid') {
23870  $ay = ((($vh * ($hr / $wr)) - $vh) / 2);
23871  }
23872  $hr = $wr;
23873  }
23874  $newtm = array($wr, 0, 0, $hr, (($wr * ($ax - $vx)) - $svgX), (($hr * ($ay - $vy)) - $svgY));
23876  $this->SVGTransform($tm);
23877  }
23878  }
23879  $this->setSVGStyles($svgstyle, $prev_svgstyle);
23880  break;
23881  }
23882  case 'g': {
23883  // group together related graphics elements
23884  array_push($this->svgstyles, $svgstyle);
23885  $this->StartTransform();
23886  $x = (isset($attribs['x'])?$attribs['x']:0);
23887  $y = (isset($attribs['y'])?$attribs['y']:0);
23888  $w = 1;//(isset($attribs['width'])?$attribs['width']:1);
23889  $h = 1;//(isset($attribs['height'])?$attribs['height']:1);
23890  $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
23891  $this->SVGTransform($tm);
23892  $this->setSVGStyles($svgstyle, $prev_svgstyle);
23893  break;
23894  }
23895  case 'linearGradient': {
23896  if ($this->pdfa_mode) {
23897  break;
23898  }
23899  if (!isset($attribs['id'])) {
23900  $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
23901  }
23902  $this->svggradientid = $attribs['id'];
23903  $this->svggradients[$this->svggradientid] = array();
23904  $this->svggradients[$this->svggradientid]['type'] = 2;
23905  $this->svggradients[$this->svggradientid]['stops'] = array();
23906  if (isset($attribs['gradientUnits'])) {
23907  $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
23908  } else {
23909  $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
23910  }
23911  //$attribs['spreadMethod']
23912  if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2'])))
23913  OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%'))
23914  OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%'))
23915  OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%'))
23916  OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) {
23917  $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
23918  } else {
23919  $this->svggradients[$this->svggradientid]['mode'] = 'measure';
23920  }
23921  $x1 = (isset($attribs['x1'])?$attribs['x1']:'0');
23922  $y1 = (isset($attribs['y1'])?$attribs['y1']:'0');
23923  $x2 = (isset($attribs['x2'])?$attribs['x2']:'100');
23924  $y2 = (isset($attribs['y2'])?$attribs['y2']:'0');
23925  if (isset($attribs['gradientTransform'])) {
23926  $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
23927  }
23928  $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
23929  if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23930  // gradient is defined on another place
23931  $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
23932  }
23933  break;
23934  }
23935  case 'radialGradient': {
23936  if ($this->pdfa_mode) {
23937  break;
23938  }
23939  if (!isset($attribs['id'])) {
23940  $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
23941  }
23942  $this->svggradientid = $attribs['id'];
23943  $this->svggradients[$this->svggradientid] = array();
23944  $this->svggradients[$this->svggradientid]['type'] = 3;
23945  $this->svggradients[$this->svggradientid]['stops'] = array();
23946  if (isset($attribs['gradientUnits'])) {
23947  $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
23948  } else {
23949  $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
23950  }
23951  //$attribs['spreadMethod']
23952  if (((!isset($attribs['cx'])) AND (!isset($attribs['cy'])))
23953  OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%'))
23954  OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')))) {
23955  $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
23956  } elseif (isset($attribs['r']) AND is_numeric($attribs['r']) AND ($attribs['r']) <= 1) {
23957  $this->svggradients[$this->svggradientid]['mode'] = 'ratio';
23958  } else {
23959  $this->svggradients[$this->svggradientid]['mode'] = 'measure';
23960  }
23961  $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5);
23962  $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5);
23963  $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx);
23964  $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy);
23965  $r = (isset($attribs['r']) ? $attribs['r'] : 0.5);
23966  if (isset($attribs['gradientTransform'])) {
23967  $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
23968  }
23969  $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
23970  if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23971  // gradient is defined on another place
23972  $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
23973  }
23974  break;
23975  }
23976  case 'stop': {
23977  // gradient stops
23978  if (substr($attribs['offset'], -1) == '%') {
23979  $offset = floatval(substr($attribs['offset'], -1)) / 100;
23980  } else {
23981  $offset = floatval($attribs['offset']);
23982  if ($offset > 1) {
23983  $offset /= 100;
23984  }
23985  }
23986  $stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors):'black';
23987  $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
23988  $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
23989  break;
23990  }
23991  // paths
23992  case 'path': {
23993  if ($invisible) {
23994  break;
23995  }
23996  if (isset($attribs['d'])) {
23997  $d = trim($attribs['d']);
23998  if (!empty($d)) {
23999  $x = (isset($attribs['x'])?$attribs['x']:0);
24000  $y = (isset($attribs['y'])?$attribs['y']:0);
24001  $w = (isset($attribs['width'])?$attribs['width']:1);
24002  $h = (isset($attribs['height'])?$attribs['height']:1);
24003  $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
24004  if ($clipping) {
24005  $this->SVGTransform($tm);
24006  $this->SVGPath($d, 'CNZ');
24007  } else {
24008  $this->StartTransform();
24009  $this->SVGTransform($tm);
24010  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ'));
24011  if (!empty($obstyle)) {
24012  $this->SVGPath($d, $obstyle);
24013  }
24014  $this->StopTransform();
24015  }
24016  }
24017  }
24018  break;
24019  }
24020  // shapes
24021  case 'rect': {
24022  if ($invisible) {
24023  break;
24024  }
24025  $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24026  $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24027  $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24028  $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24029  $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
24030  $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
24031  if ($clipping) {
24032  $this->SVGTransform($tm);
24033  $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
24034  } else {
24035  $this->StartTransform();
24036  $this->SVGTransform($tm);
24037  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
24038  if (!empty($obstyle)) {
24039  $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
24040  }
24041  $this->StopTransform();
24042  }
24043  break;
24044  }
24045  case 'circle': {
24046  if ($invisible) {
24047  break;
24048  }
24049  $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0);
24050  $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
24051  $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
24052  $x = ($cx - $r);
24053  $y = ($cy - $r);
24054  $w = (2 * $r);
24055  $h = $w;
24056  if ($clipping) {
24057  $this->SVGTransform($tm);
24058  $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
24059  } else {
24060  $this->StartTransform();
24061  $this->SVGTransform($tm);
24062  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
24063  if (!empty($obstyle)) {
24064  $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
24065  }
24066  $this->StopTransform();
24067  }
24068  break;
24069  }
24070  case 'ellipse': {
24071  if ($invisible) {
24072  break;
24073  }
24074  $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0);
24075  $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0);
24076  $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
24077  $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
24078  $x = ($cx - $rx);
24079  $y = ($cy - $ry);
24080  $w = (2 * $rx);
24081  $h = (2 * $ry);
24082  if ($clipping) {
24083  $this->SVGTransform($tm);
24084  $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
24085  } else {
24086  $this->StartTransform();
24087  $this->SVGTransform($tm);
24088  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
24089  if (!empty($obstyle)) {
24090  $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
24091  }
24092  $this->StopTransform();
24093  }
24094  break;
24095  }
24096  case 'line': {
24097  if ($invisible) {
24098  break;
24099  }
24100  $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
24101  $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
24102  $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
24103  $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
24104  $x = $x1;
24105  $y = $y1;
24106  $w = abs($x2 - $x1);
24107  $h = abs($y2 - $y1);
24108  if (!$clipping) {
24109  $this->StartTransform();
24110  $this->SVGTransform($tm);
24111  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
24112  $this->Line($x1, $y1, $x2, $y2);
24113  $this->StopTransform();
24114  }
24115  break;
24116  }
24117  case 'polyline':
24118  case 'polygon': {
24119  if ($invisible) {
24120  break;
24121  }
24122  $points = (isset($attribs['points'])?$attribs['points']:'0 0');
24123  $points = trim($points);
24124  // note that point may use a complex syntax not covered here
24125  $points = preg_split('/[\,\s]+/si', $points);
24126  if (count($points) < 4) {
24127  break;
24128  }
24129  $p = array();
24130  $xmin = 2147483647;
24131  $xmax = 0;
24132  $ymin = 2147483647;
24133  $ymax = 0;
24134  foreach ($points as $key => $val) {
24135  $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
24136  if (($key % 2) == 0) {
24137  // X coordinate
24138  $xmin = min($xmin, $p[$key]);
24139  $xmax = max($xmax, $p[$key]);
24140  } else {
24141  // Y coordinate
24142  $ymin = min($ymin, $p[$key]);
24143  $ymax = max($ymax, $p[$key]);
24144  }
24145  }
24146  $x = $xmin;
24147  $y = $ymin;
24148  $w = ($xmax - $xmin);
24149  $h = ($ymax - $ymin);
24150  if ($name == 'polyline') {
24151  $this->StartTransform();
24152  $this->SVGTransform($tm);
24153  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
24154  if (!empty($obstyle)) {
24155  $this->PolyLine($p, $obstyle, array(), array());
24156  }
24157  $this->StopTransform();
24158  } else { // polygon
24159  if ($clipping) {
24160  $this->SVGTransform($tm);
24161  $this->Polygon($p, 'CNZ', array(), array(), true);
24162  } else {
24163  $this->StartTransform();
24164  $this->SVGTransform($tm);
24165  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
24166  if (!empty($obstyle)) {
24167  $this->Polygon($p, $obstyle, array(), array(), true);
24168  }
24169  $this->StopTransform();
24170  }
24171  }
24172  break;
24173  }
24174  // image
24175  case 'image': {
24176  if ($invisible) {
24177  break;
24178  }
24179  if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
24180  break;
24181  }
24182  $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24183  $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24184  $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24185  $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24186  $img = $attribs['xlink:href'];
24187  if (!$clipping) {
24188  $this->StartTransform();
24189  $this->SVGTransform($tm);
24190  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
24191  if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
24192  // embedded image encoded as base64
24193  $img = '@'.base64_decode(substr($img, strlen($m[0])));
24194  } else {
24195  // fix image path
24196  if (!TCPDF_STATIC::empty_string($this->svgdir) AND (($img[0] == '.') OR (basename($img) == $img))) {
24197  // replace relative path with full server path
24198  $img = $this->svgdir.'/'.$img;
24199  }
24200  if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
24201  $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
24202  if (($findroot === false) OR ($findroot > 1)) {
24203  if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
24204  $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
24205  } else {
24206  $img = $_SERVER['DOCUMENT_ROOT'].$img;
24207  }
24208  }
24209  }
24210  $img = urldecode($img);
24211  $testscrtype = @parse_url($img);
24212  if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
24213  // convert URL to server path
24214  $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
24215  }
24216  }
24217  // get image type
24219  if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
24220  $this->ImageEps($img, $x, $y, $w, $h);
24221  } elseif ($imgtype == 'svg') {
24222  // store SVG vars
24233  $this->ImageSVG($img, $x, $y, $w, $h);
24234  // restore SVG vars
24235  $this->svggradients = $svggradients;
24236  $this->svggradientid = $svggradientid;
24237  $this->svgdefsmode = $svgdefsmode;
24238  $this->svgdefs = $svgdefs;
24239  $this->svgclipmode = $svgclipmode;
24240  $this->svgclippaths = $svgclippaths;
24241  $this->svgcliptm = $svgcliptm;
24242  $this->svgclipid = $svgclipid;
24243  $this->svgtext = $svgtext;
24244  $this->svgtextmode = $svgtextmode;
24245  } else {
24246  $this->Image($img, $x, $y, $w, $h);
24247  }
24248  $this->StopTransform();
24249  }
24250  break;
24251  }
24252  // text
24253  case 'text':
24254  case 'tspan': {
24255  if (isset($this->svgtextmode['text-anchor']) AND !empty($this->svgtext)) {
24256  // @TODO: unsupported feature
24257  }
24258  // only basic support - advanced features must be implemented
24259  $this->svgtextmode['invisible'] = $invisible;
24260  if ($invisible) {
24261  break;
24262  }
24263  array_push($this->svgstyles, $svgstyle);
24264  if (isset($attribs['x'])) {
24265  $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false);
24266  } elseif ($name == 'tspan') {
24267  $x = $this->x;
24268  } else {
24269  $x = 0;
24270  }
24271  if (isset($attribs['dx'])) {
24272  $x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false);
24273  }
24274  if (isset($attribs['y'])) {
24275  $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false);
24276  } elseif ($name == 'tspan') {
24277  $y = $this->y;
24278  } else {
24279  $y = 0;
24280  }
24281  if (isset($attribs['dy'])) {
24282  $y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false);
24283  }
24284  $svgstyle['text-color'] = $svgstyle['fill'];
24285  $this->svgtext = '';
24286  if (isset($svgstyle['text-anchor'])) {
24287  $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor'];
24288  } else {
24289  $this->svgtextmode['text-anchor'] = 'start';
24290  }
24291  if (isset($svgstyle['direction'])) {
24292  if ($svgstyle['direction'] == 'rtl') {
24293  $this->svgtextmode['rtl'] = true;
24294  } else {
24295  $this->svgtextmode['rtl'] = false;
24296  }
24297  } else {
24298  $this->svgtextmode['rtl'] = false;
24299  }
24300  if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
24301  $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false);
24302  } else {
24303  $this->svgtextmode['stroke'] = false;
24304  }
24305  $this->StartTransform();
24306  $this->SVGTransform($tm);
24307  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
24308  $this->x = $x;
24309  $this->y = $y;
24310  break;
24311  }
24312  // use
24313  case 'use': {
24314  if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24315  $svgdefid = substr($attribs['xlink:href'], 1);
24316  if (isset($this->svgdefs[$svgdefid])) {
24317  $use = $this->svgdefs[$svgdefid];
24318  if (isset($attribs['xlink:href'])) {
24319  unset($attribs['xlink:href']);
24320  }
24321  if (isset($attribs['id'])) {
24322  unset($attribs['id']);
24323  }
24324  if (isset($use['attribs']['x']) AND isset($attribs['x'])) {
24325  $attribs['x'] += $use['attribs']['x'];
24326  }
24327  if (isset($use['attribs']['y']) AND isset($attribs['y'])) {
24328  $attribs['y'] += $use['attribs']['y'];
24329  }
24330  if (empty($attribs['style'])) {
24331  $attribs['style'] = '';
24332  }
24333  if (!empty($use['attribs']['style'])) {
24334  // merge styles
24335  $attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']);
24336  }
24337  $attribs = array_merge($use['attribs'], $attribs);
24338  $this->startSVGElementHandler($parser, $use['name'], $attribs);
24339  return;
24340  }
24341  }
24342  break;
24343  }
24344  default: {
24345  break;
24346  }
24347  } // end of switch
24348  // process child elements
24349  if (!empty($attribs['child_elements'])) {
24350  $child_elements = $attribs['child_elements'];
24351  unset($attribs['child_elements']);
24352  foreach($child_elements as $child_element) {
24353  if (empty($child_element['attribs']['closing_tag'])) {
24354  $this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']);
24355  } else {
24356  if (isset($child_element['attribs']['content'])) {
24357  $this->svgtext = $child_element['attribs']['content'];
24358  }
24359  $this->endSVGElementHandler('child-tag', $child_element['name']);
24360  }
24361  }
24362  }
24363  }
24364 
24373  protected function endSVGElementHandler($parser, $name) {
24374  $name = $this->removeTagNamespace($name);
24375  if ($this->svgdefsmode AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {;
24376  if (end($this->svgdefs) !== FALSE) {
24377  $last_svgdefs_id = key($this->svgdefs);
24378  if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
24379  foreach($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) {
24380  if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) {
24381  $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24382  return;
24383  }
24384  }
24385  if ($this->svgdefs[$last_svgdefs_id]['name'] == $name) {
24386  $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24387  return;
24388  }
24389  }
24390  }
24391  return;
24392  }
24393  switch($name) {
24394  case 'defs': {
24395  $this->svgdefsmode = false;
24396  break;
24397  }
24398  // clipPath
24399  case 'clipPath': {
24400  $this->svgclipmode = false;
24401  break;
24402  }
24403  case 'svg': {
24404  if (--$this->svg_tag_depth <= 0) {
24405  break;
24406  }
24407  }
24408  case 'g': {
24409  // ungroup: remove last style from array
24410  array_pop($this->svgstyles);
24411  $this->StopTransform();
24412  break;
24413  }
24414  case 'text':
24415  case 'tspan': {
24416  if ($this->svgtextmode['invisible']) {
24417  // This implementation must be fixed to following the rule:
24418  // 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.
24419  break;
24420  }
24421  // print text
24423  //$text = $this->stringTrim($text);
24424  $textlen = $this->GetStringWidth($text);
24425  if ($this->svgtextmode['text-anchor'] != 'start') {
24426  // check if string is RTL text
24427  if ($this->svgtextmode['text-anchor'] == 'end') {
24428  if ($this->svgtextmode['rtl']) {
24429  $this->x += $textlen;
24430  } else {
24431  $this->x -= $textlen;
24432  }
24433  } elseif ($this->svgtextmode['text-anchor'] == 'middle') {
24434  if ($this->svgtextmode['rtl']) {
24435  $this->x += ($textlen / 2);
24436  } else {
24437  $this->x -= ($textlen / 2);
24438  }
24439  }
24440  }
24443  $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false);
24444  if ($name == 'text') {
24445  // store current coordinates
24446  $tmpx = $this->x;
24447  $tmpy = $this->y;
24448  }
24449  // print the text
24450  $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
24451  if ($name == 'text') {
24452  // restore coordinates
24453  $this->x = $tmpx;
24454  $this->y = $tmpy;
24455  }
24456  // restore previous rendering mode
24457  $this->textrendermode = $textrendermode;
24458  $this->textstrokewidth = $textstrokewidth;
24459  $this->svgtext = '';
24460  $this->StopTransform();
24461  if (!$this->svgdefsmode) {
24462  array_pop($this->svgstyles);
24463  }
24464  break;
24465  }
24466  default: {
24467  break;
24468  }
24469  }
24470  }
24471 
24480  protected function segSVGContentHandler($parser, $data) {
24481  $this->svgtext .= $data;
24482  }
24483 
24484  // --- END SVG METHODS -----------------------------------------------------
24485 
24486 } // END OF TCPDF CLASS
24487 
24488 //============================================================+
24489 // END OF FILE
24490 //============================================================+
write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='')
Print a Linear Barcode.
Definition: tcpdf.php:15198
$page_boxes
Define the page boundaries boxes to be set on document.
Definition: tcpdf.php:1802
RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array())
Draws a rounded rectangle.
Definition: tcpdf.php:12072
getGroupPageNo()
Return the current page in the group.
Definition: tcpdf.php:13669
resetLastH()
Reset the last cell height.
Definition: tcpdf.php:2440
$column_start_page
Starting page for columns.
Definition: tcpdf.php:1443
setPageFormat($format, $orientation='P')
Change the format of the current page.
Definition: tcpdf.php:2103
$footerpos
Array used to store footer positions of each page.
Definition: tcpdf.php:980
__destruct()
Default destructor.
Definition: tcpdf.php:1996
$PageBreakTrigger
Threshold used to trigger page breaks.
Definition: tcpdf.php:452
static convertHTMLColorToDec($hcolor, &$spotc, $defcol=array('R'=>128, 'G'=>128, 'B'=>128))
Returns an array (RGB or CMYK) from an html color name, or a six-digit (i.e.
$radiobutton_groups
List of radio buttons parent objects.
Definition: tcpdf.php:1380
static getPathPaintOperator($style, $default='S')
Get the Path-Painting Operators.
$header_line_color
Color for header line (RGB array).
Definition: tcpdf.php:669
$yc
_putocg()
Put pdf layers.
Definition: tcpdf.php:13698
$TextColor
Commands for text color.
Definition: tcpdf.php:434
$encrypted
IBoolean flag indicating whether document is protected.
Definition: tcpdf.php:801
$alpha
Alpha mode array.
Definition: tcpdf.php:1795
addTOCPage($orientation='', $format='', $keepmargins=false)
Adds a new TOC (Table Of Content) page to the document.
Definition: tcpdf.php:3077
Close()
Terminates the PDF document.
Definition: tcpdf.php:2951
setJPEGQuality($quality)
Set the default JPEG compression quality (1-100)
Definition: tcpdf.php:13952
_putshaders()
Output gradient shaders.
Definition: tcpdf.php:14578
$tagvspaces
Array used for custom vertical spaces for HTML tags.
Definition: tcpdf.php:1099
setFooterMargin($fm=10)
Set footer margin.
Definition: tcpdf.php:3321
setPDFVersion($version='1.7')
Set the PDF version (check PDF reference for valid values).
Definition: tcpdf.php:13994
print $file
ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='')
Extract info from a PNG image with alpha channel using the Imagick or GD library. ...
Definition: tcpdf.php:7249
$pagelen
Array containing page lengths in bytes.
Definition: tcpdf.php:1163
getNumberOfColumns()
Return the current number of columns.
Definition: tcpdf.php:21824
$file_id
File ID (used on document trailer).
Definition: tcpdf.php:829
_putviewerpreferences()
Output viewer preferences.
Definition: tcpdf.php:9838
$bgcolor
Current background color.
Definition: tcpdf.php:752
$svgdefsmode
Boolean value true when in SVG defs group.
Definition: tcpdf.php:1624
$empty_signature_appearance
Array of empty digital signature appearances.
Definition: tcpdf.php:1282
getFontSubsetting()
Return the default option for font subsetting.
Definition: tcpdf.php:22121
getCellPaddings()
Get the internal Cell padding array.
Definition: tcpdf.php:2668
static $pageboxes
Array page boxes names static.
setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='')
Defines the color used by the specified type (&#39;draw&#39;, &#39;fill&#39;, &#39;text&#39;).
Definition: tcpdf.php:3892
$listcount
HTML PARSER: array count list items on nested lists.
Definition: tcpdf.php:728
setLastH($h)
Set the last cell height.
Definition: tcpdf.php:2417
$px
_putresourcedict()
Output Resources Dictionary.
Definition: tcpdf.php:9365
$style
Definition: example_012.php:70
$size
Definition: RandomTest.php:79
$tocpage
Boolean flag true when we are on TOC (Table Of Content) page.
Definition: tcpdf.php:1491
static getFormattedDate($time)
Returns a formatted date-time.
static _getTrueTypeFontSubset($font, $subsetchars)
Returns a subset of the TrueType font data without the unused glyphs.
Link($x, $y, $w, $h, $link, $spaces=0)
Puts a link on a rectangular area of the page.
Definition: tcpdf.php:4728
static getTagStyleFromCSSarray($css)
Compact CSS data array into single string.
GetNumChars($s)
Returns the numbero of characters in a string.
Definition: tcpdf.php:4135
setHeaderFont($font)
Set header font.
Definition: tcpdf.php:10303
SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='')
Defines the color used for all filling operations (filled rectangles and cell backgrounds).
Definition: tcpdf.php:3999
getGraphicVars()
Returns current graphic variables as array.
Definition: tcpdf.php:20534
Header()
This method is used to render the page header.
Definition: tcpdf.php:3392
$buffer
Buffer holding in-memory PDF.
Definition: tcpdf.php:166
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
$page_regions
Array of no-write regions.
Definition: tcpdf.php:1555
getFontsList()
Fill the list of available fonts ($this->fontlist).
Definition: tcpdf.php:4147
$linestyleJoin
PDF string for join value of the last line.
Definition: tcpdf.php:1022
$svgclipmode
Boolean value true when in SVG clipPath tag.
Definition: tcpdf.php:1638
$form_obj_id
List of form annotations IDs.
Definition: tcpdf.php:1331
$fonts
Array of used fonts.
Definition: tcpdf.php:324
setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=> 'Normal'))
Set parameters for drop shadow effect for text.
Definition: tcpdf.php:21891
addJavascriptObject($script, $onload=false)
Adds a javascript object and return object ID.
Definition: tcpdf.php:12507
_outPoint($x, $y)
Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line s...
Definition: tcpdf.php:11385
$underline
Underlining flag.
Definition: tcpdf.php:392
setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array())
Apply the requested SVG styles (*** TO BE COMPLETED ***)
Definition: tcpdf.php:23036
SetFontSize($size, $out=true)
Defines the size of the current font.
Definition: tcpdf.php:4455
getHeaderData()
Returns header data:
Definition: tcpdf.php:3284
applyTSA($signature)
NOT YET IMPLEMENTED Request TSA for a timestamp.
Definition: tcpdf.php:13546
MirrorP($x='', $y='')
Point reflection mirroring.
Definition: tcpdf.php:11090
SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone')
Defines the way the document is to be displayed by the viewer.
Definition: tcpdf.php:2814
$svg_tag_depth
Depth of the svg tag, to keep track if the svg tag is a subtag or the root tag.
Definition: tcpdf.php:348
$signature_data
Digital signature data.
Definition: tcpdf.php:1261
startTransaction()
Stores a copy of the current TCPDF object used for undo operation.
Definition: tcpdf.php:21621
setHeaderTemplateAutoreset($val=true)
Set a flag to automatically reset the xobject template used by Header() method at each page...
Definition: tcpdf.php:3383
Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false)
Creates a button field.
Definition: tcpdf.php:13123
TranslateX($t_x)
Translate graphic object horizontally.
Definition: tcpdf.php:11115
TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false)
Creates a text field.
Definition: tcpdf.php:12646
setFooter()
This method is used to render the page footer.
Definition: tcpdf.php:3557
_outRestoreGraphicsState()
Outputs the "restore graphics state" operator &#39;Q&#39;.
Definition: tcpdf.php:20667
$overline
Overlining flag.
Definition: tcpdf.php:398
$footer_margin
Minimum distance between footer and bottom page margin.
Definition: tcpdf.php:581
getFontSpacing()
Get the amount to increase or decrease the space between characters in a text.
Definition: tcpdf.php:22489
setPageBoxTypes($boxes)
Set page boxes to be included on page descriptions.
Definition: tcpdf.php:7883
$tmprtl
Boolean flag used to force RTL or LTR string direction.
Definition: tcpdf.php:792
$AutoPageBreak
Automatic page breaking.
Definition: tcpdf.php:446
$FontAscent
Current font ascent (distance between font top and baseline).
Definition: tcpdf.php:379
$fhPt
Height of page format in points.
Definition: tcpdf.php:214
$n_dests
Object ID for Named Destinations.
Definition: tcpdf.php:1582
SetBooklet($booklet=true, $inner=-1, $outer=-1)
Set the booklet mode for double-sided pages.
Definition: tcpdf.php:20162
$js
setHeader()
This method is used to render the page header.
Definition: tcpdf.php:3516
getAllInternalPageNumberAliases()
Return an array containing all internal page aliases.
Definition: tcpdf.php:7830
$result
getImageRBY()
Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image...
Definition: tcpdf.php:3366
unhtmlentities($text_to_convert)
Reverse function for htmlentities.
Definition: tcpdf.php:10428
static arrUTF8ToUTF16BE($unicode, $setbom=false)
Converts array of UTF-8 characters to UTF16-BE string.
$footerlen
Array used to store footer length of each page.
Definition: tcpdf.php:987
SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='')
Defines the color used for all drawing operations (lines, rectangles and cell borders).
Definition: tcpdf.php:3982
$fontlist
List of available fonts on filesystem.
Definition: tcpdf.php:710
$title
Document title.
Definition: tcpdf.php:488
getScaleFactor()
Returns the scale factor (number of points in user unit).
Definition: tcpdf.php:2547
PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2)
Draw the sector of an ellipse.
Definition: tcpdf.php:14791
getCSSPadding($csspadding, $width=0)
Get the internal Cell padding from CSS attribute.
Definition: tcpdf.php:15951
setFontSubBuffer($font, $key, $data)
Set font buffer content.
Definition: tcpdf.php:20811
Footer()
This method is used to render the page footer.
Definition: tcpdf.php:3469
getFontBBox()
Returns the bounding box of the current font in user units.
Definition: tcpdf.php:4495
getNumPages()
Get the total number of insered pages.
Definition: tcpdf.php:3064
$offsets
Array of object offsets.
Definition: tcpdf.php:154
_putpages()
Output pages (and replace page number aliases).
Definition: tcpdf.php:7896
static _escapeXML($str)
Escape some special characters (< > &) for XML output.
_putXMP()
Put XMP data object and return ID.
Definition: tcpdf.php:9513
setFontSpacing($spacing=0)
Set the amount to increase or decrease the space between characters in a text.
Definition: tcpdf.php:22478
$emptypagemrk
Array used to store page positions to track empty pages (keys are the page numbers).
Definition: tcpdf.php:966
Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2)
Draws an ellipse.
Definition: tcpdf.php:11653
SetCreator($creator)
Defines the creator of the document.
Definition: tcpdf.php:2911
_putbookmarks()
Create a bookmark PDF string.
Definition: tcpdf.php:12368
SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='')
Defines the color used for text.
Definition: tcpdf.php:4016
getFontAscent($font, $style='', $size=0)
Return the font ascent value.
Definition: tcpdf.php:4588
getPageHeight($pagenum='')
Returns the page height in units.
Definition: tcpdf.php:2517
$header_xobj_autoreset
If true reset the Header Xobject template at each page.
Definition: tcpdf.php:569
selectColumn($col='')
Set position at a given column.
Definition: tcpdf.php:21735
$ZoomMode
Zoom display mode.
Definition: tcpdf.php:470
$rMargin
Right margin.
Definition: tcpdf.php:250
$lispacer
Spacer string for LI tags.
Definition: tcpdf.php:764
$numimages
Counts the number of pages.
Definition: tcpdf.php:1170
$cell_height_ratio
Default cell height ratio.
Definition: tcpdf.php:924
sortBookmarks()
Sort bookmarks for page and key.
Definition: tcpdf.php:12350
SetDefaultMonospacedFont($font)
Defines the default monospaced font.
Definition: tcpdf.php:4672
$border
getAutoPageBreak()
Return the auto-page-break mode (true or false).
Definition: tcpdf.php:2802
setPageMark()
Set start-writing mark on current page stream used to put borders and fills.
Definition: tcpdf.php:3223
$FontFamily
Current font family.
Definition: tcpdf.php:366
$signature_max_length
Digital signature max length.
Definition: tcpdf.php:1268
getRemainingWidth()
Returns the remaining width between the current position and margins.
Definition: tcpdf.php:6703
getDestination()
Return the Named Destination array.
Definition: tcpdf.php:12250
setRTL($enable, $resetx=true)
Enable or disable Right-To-Left language mode.
Definition: tcpdf.php:2348
$FontStyle
Current font style.
Definition: tcpdf.php:372
$img_rb_x
The right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image.
Definition: tcpdf.php:526
$transfmatrix
Array of transformation matrix.
Definition: tcpdf.php:1071
static swapPageBoxCoordinates($page, $pagedim)
Swap X and Y coordinates of page boxes (change page boxes orientation).
$header_xobjid
ID of the stored default header template (-1 = not set).
Definition: tcpdf.php:563
static intToRoman($number)
Returns the Roman representation of an integer number.
static _AES($key, $text)
Returns the input text exrypted using AES algorithm and the specified key.
colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A, R, G, B, C, M, Y, K')
Paints color transition registration bars.
Definition: tcpdf.php:14029
Line($x1, $y1, $x2, $y2, $style=array())
Draws a line between two points.
Definition: tcpdf.php:11482
$creator
Document creator.
Definition: tcpdf.php:512
MirrorH($x='')
Horizontal Mirroring.
Definition: tcpdf.php:11067
getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false)
Convert HTML string containing value and unit of measure to user&#39;s units or points.
Definition: tcpdf.php:20248
$FontSize
Current font size in user unit.
Definition: tcpdf.php:416
_encrypt_data($n, $s)
Encrypt the input string.
Definition: tcpdf.php:10463
SetXY($x, $y, $rtloff=false)
Defines the abscissa and ordinate of the current position.
Definition: tcpdf.php:7508
getBreakMargin($pagenum='')
Returns the page break margin.
Definition: tcpdf.php:2533
$js_objects
Javascript objects array.
Definition: tcpdf.php:1345
_outRect($x, $y, $w, $h, $op)
Append a rectangle to the current path as a complete subpath, with lower-left corner (x...
Definition: tcpdf.php:11415
$rtl
Boolean flag to indicate if the document language is Right-To-Left.
Definition: tcpdf.php:785
$code
Definition: example_050.php:99
SetDrawSpotColor($name, $tint=100)
Defines the spot color used for all drawing operations (lines, rectangles and cell borders)...
Definition: tcpdf.php:3778
getRawCharWidth($char)
Returns the length of the char in user unit for the current font.
Definition: tcpdf.php:4109
$hPt
Current height of page in points.
Definition: tcpdf.php:226
$encoding
Default encoding.
Definition: tcpdf.php:771
$efnames
Embedded Files Names.
Definition: tcpdf.php:1589
setHtmlVSpace($tagvs)
Set the vertical spaces for HTML tags.
Definition: tcpdf.php:20202
$tsa_data
Timestamping data.
Definition: tcpdf.php:1296
closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0)
Process closing tags.
Definition: tcpdf.php:19348
_putjavascript()
Create a javascript PDF string.
Definition: tcpdf.php:12523
writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='')
Allows to preserve some HTML formatting (limited support).
Definition: tcpdf.php:17121
GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false)
Returns the length of a string in user unit.
Definition: tcpdf.php:4032
$DrawColor
Commands for drawing color.
Definition: tcpdf.php:422
getImageScale()
Returns the adjusting factor to convert pixels to user units.
Definition: tcpdf.php:2472
$dpi
DPI (Dot Per Inch) Document Resolution (do not change).
Definition: tcpdf.php:882
setDestination($name, $y=-1, $page='', $x=-1)
Add a Named Destination.
Definition: tcpdf.php:12207
const PDF_FONT_NAME_MAIN
Default main font name.
$newline
Boolean flag to indicate if a new line is created.
Definition: tcpdf.php:994
resetHeaderTemplate()
Reset the xobject template used by Header() method.
Definition: tcpdf.php:3374
$FillColor
Commands for filling color.
Definition: tcpdf.php:428
setImageBuffer($image, $data)
Set image buffer content.
Definition: tcpdf.php:20746
getPageBuffer($page)
Get page buffer content.
Definition: tcpdf.php:20731
setBarcode($bc='')
Set document barcode.
Definition: tcpdf.php:15154
$extgstates
Array of transparency objects and parameters.
Definition: tcpdf.php:910
static utf8Bidi($ta, $str='', $forcertl=false, $isunicode=true, &$currentfont)
Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
$FontDescent
Current font descent (distance between font bottom and baseline).
Definition: tcpdf.php:386
convertSVGtMatrix($tm)
Convert SVG transformation matrix to PDF.
Definition: tcpdf.php:22997
RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array())
Draws a rounded rectangle.
Definition: tcpdf.php:12053
$svgclipid
ID of last SVG clipPath.
Definition: tcpdf.php:1659
startSVGElementHandler($parser, $name, $attribs, $ctm=array())
Sets the opening SVG element handler function for the XML parser.
Definition: tcpdf.php:23706
ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false)
Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
Definition: tcpdf.php:14835
$sign
Boolean flag to enable document digital signature.
Definition: tcpdf.php:1254
openHTMLTagHandler($dom, $key, $cell)
Process opening tags.
Definition: tcpdf.php:18678
$strokecolor
Current stroke color.
Definition: tcpdf.php:1478
setSpacesRE($re='/[^\S\xa0]/')
Set regular expression to detect withespaces or word separators.
Definition: tcpdf.php:2323
$cmd
Definition: sahs_server.php:35
static getUserPermissionCode($permissions, $mode=0)
Return the permission code used on encryption (P value).
getFontStyle()
Returns the current font style.
Definition: tcpdf.php:15817
SetAuthor($author)
Defines the author of the document.
Definition: tcpdf.php:2889
StopTransform()
Stops a 2D tranformation restoring previous graphic state.
Definition: tcpdf.php:10969
ScaleXY($s, $x='', $y='')
Vertical and horizontal proportional Scaling.
Definition: tcpdf.php:11020
Ln($h='', $cell=false)
Performs a line break.
Definition: tcpdf.php:7368
$patch_array[0]['f']
PolyLine($p, $style='', $line_style=array(), $fill_color=array())
Draws a polygonal line.
Definition: tcpdf.php:11843
_outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false)
Append an elliptical arc to the current path.
Definition: tcpdf.php:11694
$InFooter
Flag set when processing page footer.
Definition: tcpdf.php:464
Error($msg)
Throw an exception or print an error message and die if the K_TCPDF_PARSER_THROW_EXCEPTION_ERROR cons...
Definition: tcpdf.php:2921
getBuffer()
Get buffer content.
Definition: tcpdf.php:20699
setHtmlLinksStyle($color=array(0, 0, 255), $fontstyle='U')
Set the color and font style for HTML links.
Definition: tcpdf.php:20233
static convertHexStringToString($bs)
Convert hexadecimal string to string.
getCSSMargin($cssmargin, $width=0)
Get the internal Cell margin from CSS attribute.
Definition: tcpdf.php:16005
$inxobj
Boolean value true when we are inside an XObject.
Definition: tcpdf.php:1526
getFontSizePt()
Returns the current font size in points unit.
Definition: tcpdf.php:15797
getColumn()
Return the current column number.
Definition: tcpdf.php:21814
static removeSHY($txt='', $unicode=true)
Removes SHY characters from text.
setExtGState($gs)
Add an extgstate.
Definition: tcpdf.php:13829
getFontSize()
Returns the current font size.
Definition: tcpdf.php:15787
getFontFamily()
Returns the current font family name.
Definition: tcpdf.php:15807
_Ovalue()
Compute O value (used for encryption)
Definition: tcpdf.php:10656
_outCurveV($x2, $y2, $x3, $y3)
Append a cubic Bezier curve to the current path.
Definition: tcpdf.php:11449
Rotate($angle, $x='', $y='')
Rotate object.
Definition: tcpdf.php:11160
$textstrokewidth
Text stroke width in doc units.
Definition: tcpdf.php:1471
startPageGroup($page='')
Create a new page group.
Definition: tcpdf.php:13561
$tMargin
Top margin.
Definition: tcpdf.php:268
$pdflayers
Array of PDF layers data.
Definition: tcpdf.php:1568
getPageDimensions($pagenum='')
Returns an array of page dimensions:
Definition: tcpdf.php:2485
static getEncPermissionsString($protection)
Convert encryption P value to a string of bytes, low-order byte first.
GetX()
Returns the relative X value of current position.
Definition: tcpdf.php:7402
$thead
Table header content to be repeated on each new page.
Definition: tcpdf.php:1240
$listnum
HTML PARSER: current list nesting level.
Definition: tcpdf.php:734
isRTLTextDir()
Return the current temporary RTL status.
Definition: tcpdf.php:2406
getFontDescent($font, $style='', $size=0)
Return the font descent value.
Definition: tcpdf.php:4567
getImageBuffer($image)
Get image buffer content.
Definition: tcpdf.php:20778
static unichr($c, $unicode=true)
Returns the unicode caracter specified by the value.
static _getfontpath()
Return fonts path.
setFooterFont($font)
Set footer font.
Definition: tcpdf.php:10323
setTextRenderingMode($stroke=0, $fill=true, $clip=false)
Set Text rendering mode.
Definition: tcpdf.php:21836
$ur
Array with additional document-wide usage rights for the document.
Definition: tcpdf.php:875
_outCurve($x1, $y1, $x2, $y2, $x3, $y3)
Append a cubic Bezier curve to the current path.
Definition: tcpdf.php:11433
SetDocInfoUnicode($unicode=true)
Turn on/off Unicode mode for document information dictionary (meta tags).
Definition: tcpdf.php:2856
static getHyphenPatternsFromTEX($file)
Returns an array of hyphenation patterns.
getAliasNbPages()
Returns the string alias used for the total number of pages.
Definition: tcpdf.php:13611
setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='')
Enable document timestamping (requires the OpenSSL Library).
Definition: tcpdf.php:13518
_putfonts()
Output fonts.
Definition: tcpdf.php:8775
stringRightTrim($str, $replace='')
Right trim the input string.
Definition: tcpdf.php:22147
$dests
A dictionary of names and corresponding destinations (Dests key on document Catalog).
Definition: tcpdf.php:1575
static getRandomSeed($seed='')
Returns a string containing random data to be used as a seed for encryption methods.
$url
Definition: shib_logout.php:72
static getBorderMode($brd, $position='start', $opencell=true)
Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages)...
$coords_min
static getCSSdataArray($dom, $key, $css)
Returns the styles array that apply for the selected HTML tag.
setSRGBmode($mode=false)
Set flag to force sRGB_IEC61966-2.1 black scaled ICC color profile for the whole document.
Definition: tcpdf.php:2845
$txtshadow
Text shadow data array.
Definition: tcpdf.php:690
SetFillColorArray($color, $ret=false)
Defines the color used for all filling operations (filled rectangles and cell backgrounds).
Definition: tcpdf.php:3862
startLayer($name='', $print=true, $view=true, $lock=true)
Start a new pdf layer.
Definition: tcpdf.php:13726
const K_PATH_IMAGES
Installation path (/var/www/tcpdf/).
static UTF8StringToArray($str, $isunicode=true, &$currentfont)
Converts UTF-8 strings to codepoints array.
$check_page_regions
Boolean value true when page region check is active.
Definition: tcpdf.php:1561
_textstring($s, $n=0)
Format a text string for meta information.
Definition: tcpdf.php:10245
$angle
static getTimestamp($date)
Returns timestamp in seconds from formatted date-time.
static sendOutputData($data, $length)
Output input data and compress it if possible.
addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false)
Add vertical spaces if needed.
Definition: tcpdf.php:19895
setTableHeader()
This method is used to render the table header on new page (if any).
Definition: tcpdf.php:3622
getPageRegions()
Return an array of no-write page regions.
Definition: tcpdf.php:22501
addExtGState($parms)
Add transparency parameters to the current extgstate.
Definition: tcpdf.php:13798
$form_action
Current form action (used during XHTML rendering).
Definition: tcpdf.php:1352
_putinfo()
Adds some Metadata information (Document Information Dictionary) (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference)
Definition: tcpdf.php:9452
static pregSplit($pattern, $modifiers, $subject, $limit=NULL, $flags=NULL)
Split string by a regular expression.
PHP class to creates array representations for 2D barcodes to be used with TCPDF (http://www.tcpdf.org).
$doc_creation_timestamp
Document creation date-time.
Definition: tcpdf.php:1765
$img_rb_y
The right-bottom corner Y coordinate of last inserted image.
Definition: tcpdf.php:534
$CurOrientation
Current page orientation (P = Portrait, L = Landscape).
Definition: tcpdf.php:190
$isunicode
Boolean flag set to true when the input text is unicode (require unicode fonts).
Definition: tcpdf.php:550
$linestyleDash
PDF string for dash value of the last line.
Definition: tcpdf.php:1029
Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array())
Draws a Bezier curve.
Definition: tcpdf.php:11583
static setGDImageTransparency($new_image, $image)
Set the transparency for the given GD image.
static formatTOCPageNumber($num)
Format the page numbers on the Table Of Content.
$footer_line_color
Color for footer line (RGB array).
Definition: tcpdf.php:683
ScaleY($s_y, $x='', $y='')
Vertical Scaling.
Definition: tcpdf.php:11007
_dooverlinew($x, $y, $w)
Overline for rectangular text area.
Definition: tcpdf.php:10156
setDocCreationTimestamp($time)
Set the document creation timestamp.
Definition: tcpdf.php:10183
Static methods used by the TCPDF class.
$num_columns
Number of colums.
Definition: tcpdf.php:1429
$default_graphic_vars
Array of default graphic settings.
Definition: tcpdf.php:1512
getDocCreationTimestamp()
Returns document creation timestamp in seconds.
Definition: tcpdf.php:10209
setLIsymbol($symbol='!')
Set the default bullet to be used as LI bullet symbol.
Definition: tcpdf.php:20139
$pdfa_mode
If true set the document to PDF/A mode.
Definition: tcpdf.php:1758
setHeaderMargin($hm=10)
Set header margin.
Definition: tcpdf.php:3301
$coords
Definition: example_030.php:88
$default_form_prop
Deafult Javascript field properties.
Definition: tcpdf.php:1338
PHP class for generating PDF documents without requiring external extensions.
Definition: tcpdf.php:134
_putcatalog()
Output Catalog.
Definition: tcpdf.php:9651
const K_PATH_CACHE
Cache directory for temporary files (full path).
$CoreFonts
Array of standard font names.
Definition: tcpdf.php:318
setViewerPreferences($preferences)
Set the viewer preferences dictionary controlling the way the document is to be presented on the scre...
Definition: tcpdf.php:14012
$tempfontsize
Temporary font size in points.
Definition: tcpdf.php:758
$pageobjects
Array of object IDs for each page.
Definition: tcpdf.php:160
static isValidURL($url)
Check if the URL exist.
$imagekeys
Store the image keys.
Definition: tcpdf.php:1177
getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0)
This method return the estimated number of lines for print a simple text string using Multicell() met...
Definition: tcpdf.php:6120
SetLink($link, $y=0, $page=-1)
Defines the page and position a link points to.
Definition: tcpdf.php:4699
GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false)
Returns the string length of an array of chars in user unit or an array of characters widths...
Definition: tcpdf.php:4048
$default_monospaced_font
Default monospace font.
Definition: tcpdf.php:1219
$objcopy
Cloned copy of the current class object.
Definition: tcpdf.php:1226
$annotation_fonts
List of fonts used on form fields (fontname => fontkey).
Definition: tcpdf.php:1373
getAbsFontMeasure($s)
Convert a relative font measure into absolute value.
Definition: tcpdf.php:4534
$svgunit
Deafult unit of measure for SVG.
Definition: tcpdf.php:1603
const K_THAI_TOPCHARS
Set to true to enable the special procedure used to avoid the overlappind of symbols on Thai language...
$print_header
Boolean flag to print/hide page header.
Definition: tcpdf.php:625
SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true)
Sets the font used to print character strings.
Definition: tcpdf.php:4427
_putresources()
Output Resources.
Definition: tcpdf.php:9429
$embeddedfiles
Array of files to embedd.
Definition: tcpdf.php:1120
$coords_max
lastPage($resetmargins=false)
Reset pointer to the last document page.
Definition: tcpdf.php:3042
Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15)
Draws a grahic arrow.
Definition: tcpdf.php:12142
$re_space
Array of $re_spaces parts.
Definition: tcpdf.php:1310
static $uni_type
Array of Unicode types.
_outLine($x, $y)
Append a straight line segment from the current point to the point (x, y).
Definition: tcpdf.php:11399
$viewer_preferences
PDF viewer preferences.
Definition: tcpdf.php:931
static empty_string($str)
Determine whether a string is empty.
SetTextColorArray($color, $ret=false)
Defines the color used for text.
Definition: tcpdf.php:3875
_fixAES256Password($password)
Convert password for AES-256 encryption mode.
Definition: tcpdf.php:10707
drawHTMLTagBorder($tag, $xmax)
Draw an HTML block border and fill.
Definition: tcpdf.php:19937
setFooterData($tc=array(0, 0, 0), $lc=array(0, 0, 0))
Set footer data.
Definition: tcpdf.php:3272
writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true)
Prints a cell (rectangular area) with optional borders, background color and html text string...
Definition: tcpdf.php:17104
$subject
Document subject.
Definition: tcpdf.php:494
setCellHeightRatio($h)
Set the height of the cell (line height) respect the font height.
Definition: tcpdf.php:13975
getPageGroupAlias()
Return the alias for the total number of pages in the current page group.
Definition: tcpdf.php:13641
setImageScale($scale)
Set the adjusting factor to convert pixels to user units.
Definition: tcpdf.php:2461
static UTF8ArrToLatin1Arr($unicode)
Converts UTF-8 characters array to array of Latin1 characters array
_Uvalue()
Compute U value (used for encryption)
Definition: tcpdf.php:10611
$lisymbol
Symbol used for HTML unordered list items.
Definition: tcpdf.php:1057
$font_stretching
Percentage of character stretching.
Definition: tcpdf.php:1540
SetAbsXY($x, $y)
Set the absolute X and Y coordinates of the current pointer.
Definition: tcpdf.php:7543
Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='')
This method prints text from the current position.
Definition: tcpdf.php:6272
$default_table_columns
Default number of columns for html table.
Definition: tcpdf.php:696
$k
Scale factor (number of points in user unit).
Definition: tcpdf.php:202
setOpenCell($isopen)
Set the top/bottom cell sides to be open or closed when the cell cross the page.
Definition: tcpdf.php:20222
getAliasNumPage()
Returns the string alias used for the page number.
Definition: tcpdf.php:13626
$bordermrk
Array used to store positions inside the pages buffer (keys are the page numbers).
Definition: tcpdf.php:959
Text($x, $y, $txt, $fstroke=false, $fclip=false, $ffill=true, $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M', $rtloff=false)
Prints a text cell at the specified position.
Definition: tcpdf.php:4893
$lasth
Height of last cell printed.
Definition: tcpdf.php:306
PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90)
Draw the sector of a circle.
Definition: tcpdf.php:14770
_addfield($type, $name, $x, $y, $w, $h, $prop)
Adds a javascript form field.
Definition: tcpdf.php:12581
static set_mqr($mqr)
Sets the current active configuration setting of magic_quotes_runtime (if the set_magic_quotes_runtim...
_objectkey($n)
Compute encryption key depending on object number where the encrypted data is stored.
Definition: tcpdf.php:10443
startPage($orientation='', $format='', $tocpage=false)
Starts a new page to the document.
Definition: tcpdf.php:3152
_getrawstream($s, $n=0)
get raw output stream.
Definition: tcpdf.php:10261
$listindentlevel
HTML PARSER: current list indententation level.
Definition: tcpdf.php:746
_out($s)
Output a string to the document.
Definition: tcpdf.php:10274
$force_srgb
If true force sRGB color profile for all document.
Definition: tcpdf.php:1751
$header_text_color
Color for header text (RGB array).
Definition: tcpdf.php:662
inPageBody()
Check if we are on the page body (excluding page header and footer).
Definition: tcpdf.php:3613
$original_lMargin
Original left margin value.
Definition: tcpdf.php:588
static $alias_tot_pages
String alias for total number of pages.
$textrendermode
Text rendering mode: 0 = Fill text; 1 = Stroke text; 2 = Fill, then stroke text; 3 = Neither fill nor...
Definition: tcpdf.php:1464
Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=> 'Text'), $spaces=0)
Puts a markup annotation on a rectangular area of the page.
Definition: tcpdf.php:4745
if(@file_exists(dirname(__FILE__).'/lang/eng.php')) $preferences
Definition: example_029.php:70
getCSSFontStretching($stretch, $parent=100)
Returns the percentage of font stretching from CSS value.
Definition: tcpdf.php:16124
getAlpha()
Get the alpha mode array (CA, ca, BM, AIS).
Definition: tcpdf.php:13942
$barcode
Barcode to print on page footer (only if set).
Definition: tcpdf.php:619
$svgdefs
Array of SVG defs.
Definition: tcpdf.php:1631
$PageAnnots
Array of Annotations in pages.
Definition: tcpdf.php:354
MirrorL($angle=0, $x='', $y='')
Reflection against a straight line through point (x, y) with the gradient angle (angle).
Definition: tcpdf.php:11103
StarPolygon($x0, $y0, $r, $nv, $ng, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array())
Draws a star polygon.
Definition: tcpdf.php:12011
$w
Current width of page in user unit.
Definition: tcpdf.php:232
$PageMode
A name object specifying how the document should be displayed when opened.
Definition: tcpdf.php:938
$currpagegroup
Current page group number.
Definition: tcpdf.php:903
$feps
Epsilon value used for float calculations.
Definition: tcpdf.php:1092
swapMargins($reverse=true)
Swap the left and right margins.
Definition: tcpdf.php:20178
$font_spacing
Increases or decreases the space between characters in a text by the specified amount (tracking)...
Definition: tcpdf.php:1547
$sig_obj_id
Digital signature object ID.
Definition: tcpdf.php:1317
_dolinethrough($x, $y, $txt)
Line through text.
Definition: tcpdf.php:10117
endLayer()
End the current PDF layer.
Definition: tcpdf.php:13746
getHashForTCPDFtagParams($data)
Return an hash code used to ensure that the serialized data has been generated by this TCPDF instance...
Definition: tcpdf.php:17052
setPrintFooter($val=true)
Set a flag to print page footer.
Definition: tcpdf.php:3348
MirrorV($y='')
Verical Mirroring.
Definition: tcpdf.php:11078
Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M')
Prints a cell (rectangular area) with optional borders, background color and character string...
Definition: tcpdf.php:4994
$header_logo
Header image logo.
Definition: tcpdf.php:637
static getTCPDFProducer()
Return the current TCPDF producer.
getBarcode()
Get current barcode.
Definition: tcpdf.php:15164
$pdfunit
Default unit of measure for document.
Definition: tcpdf.php:1485
$info
Definition: example_052.php:80
static formatPageNumber($num)
Format the page numbers.
SetY($y, $resetx=true, $rtloff=false)
Moves the current abscissa back to the left margin and sets the ordinate.
Definition: tcpdf.php:7475
$numfonts
Counts the number of fonts.
Definition: tcpdf.php:1191
$endlinex
End position of the latest inserted line.
Definition: tcpdf.php:1001
setBuffer($data)
Set buffer content (always append data).
Definition: tcpdf.php:20677
$n
Current object number.
Definition: tcpdf.php:148
SetKeywords($keywords)
Associates keywords with the document, generally in the form &#39;keyword1 keyword2 ...&#39;.
Definition: tcpdf.php:2900
removeTagNamespace($name)
Return the tag name without the namespace.
Definition: tcpdf.php:23688
static replacePageNumAliases($page, $replace, $diff=0)
Replace page number aliases with number.
_outSaveGraphicsState()
Outputs the "save graphics state" operator &#39;q&#39;.
Definition: tcpdf.php:20659
static getFontRefSize($size, $refsize=12)
Get a reference font size.
static extractCSSproperties($cssdata)
Extracts the CSS properties from a CSS string.
setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='')
Set the digital signature appearance (a cliccable rectangle area to get signature properties) ...
Definition: tcpdf.php:13452
$print_footer
Boolean flag to print/hide page footer.
Definition: tcpdf.php:631
static _AESnopad($key, $text)
Returns the input text exrypted using AES algorithm and the specified key.
AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false)
Adds a new page to the document.
Definition: tcpdf.php:3102
$r
Definition: example_031.php:79
$header_logo_width
Width of header image logo in user units.
Definition: tcpdf.php:643
setPageRegions($regions=array())
Set no-write regions on page.
Definition: tcpdf.php:22516
getPage()
Get current document page number.
Definition: tcpdf.php:3053
setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0, 0, 0), $x=-1, $link='')
Adds a bookmark - alias for Bookmark().
Definition: tcpdf.php:12286
getCellHeight($fontsize, $padding=TRUE)
Return the cell height.
Definition: tcpdf.php:2427
static $alias_group_num_page
String alias for group page number.
$pagedim
Page dimensions.
Definition: tcpdf.php:196
$font_subsetting
Boolean flag: if true enables font subsetting by default.
Definition: tcpdf.php:1505
$current_column
Current column number.
Definition: tcpdf.php:1436
_dounderlinew($x, $y, $w)
Underline for rectangular text area.
Definition: tcpdf.php:10105
$header_string
String to pring on page header after title.
Definition: tcpdf.php:655
$data
$listindent
HTML PARSER: indent amount for lists.
Definition: tcpdf.php:740
setExtraXMP($xmp)
Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag...
Definition: tcpdf.php:9503
static $byterange_string
ByteRange placemark used during digital signature process.
stringTrim($str, $replace='')
Trim the input string.
Definition: tcpdf.php:22160
getAllSpotColors()
Returns the array of spot colors.
Definition: tcpdf.php:3699
static utf8StrRev($str, $setbom=false, $forcertl=false, $isunicode=true, &$currentfont)
Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
getCSSBorderWidth($width)
Returns the border width from CSS property.
Definition: tcpdf.php:15844
_endpage()
Mark end of page.
Definition: tcpdf.php:10053
SetCompression($compress=true)
Activates or deactivates page compression.
Definition: tcpdf.php:2831
$state
Current document state.
Definition: tcpdf.php:178
fitBlock($w, $h, $x, $y, $fitonpage=false)
Set the block dimensions accounting for page breaks and page/column fitting.
Definition: tcpdf.php:6723
setUserRights( $enable=true, $document='/FullSave', $annots='/Create/Delete/Modify/Copy/Import/Export', $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate', $signature='/Modify', $ef='/Create/Delete/Modify/Import', $formex='')
Set User&#39;s Rights for PDF Reader WARNING: This is experimental and currently do not work...
Definition: tcpdf.php:13378
_putxobjects()
Output Form XObjects Templates.
Definition: tcpdf.php:9230
_datestring($n=0, $timestamp=0)
Returns a formatted date for meta information.
Definition: tcpdf.php:10231
static getPageSizeFromFormat($format)
Get page dimensions from format name.
$outlines
Outlines for bookmark.
Definition: tcpdf.php:838
getCharBBox($char)
Returns the glyph bounding box of the specified character in the current font in user units...
Definition: tcpdf.php:4544
stringLeftTrim($str, $replace='')
Left trim the input string.
Definition: tcpdf.php:22134
$cell_padding
Array of cell internal paddings (&#39;T&#39; => top, &#39;R&#39; => right, &#39;B&#39; => bottom, &#39;L&#39; => left).
Definition: tcpdf.php:281
_dooverline($x, $y, $txt)
Overline text.
Definition: tcpdf.php:10143
$LineWidth
Line width in user unit.
Definition: tcpdf.php:312
static $alias_num_page
String alias for page number.
hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8)
Returns an array of chars containing soft hyphens.
Definition: tcpdf.php:21951
static getPageMode($mode='UseNone')
Get the canonical page layout mode.
$crMargin
Cell right margin (used by regions).
Definition: tcpdf.php:262
_dolinethroughw($x, $y, $w)
Line through for rectangular text area.
Definition: tcpdf.php:10130
unserializeTCPDFtagParameters($data)
Unserialize parameters to be used with TCPDF tag in HTML code.
Definition: tcpdf.php:17073
$header_font
Default font used on page header.
Definition: tcpdf.php:601
$header
Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2)
Draws a circle.
Definition: tcpdf.php:11825
getCSSBorderDashStyle($style)
Returns the border dash style from CSS property.
Definition: tcpdf.php:15864
static UTF8ToUTF16BE($str, $setbom=false, $isunicode=true, &$currentfont)
Converts UTF-8 strings to UTF16-BE.
commitTransaction()
Delete the copy of the current TCPDF object used for undo operation.
Definition: tcpdf.php:21638
setListIndentWidth($width)
Set custom width for list indentation.
Definition: tcpdf.php:20212
serializeTCPDFtagParameters($data)
Serialize an array of parameters to be used with TCPDF tag in HTML code.
Definition: tcpdf.php:17062
v($data, $pos)
setDefaultTableColumns($cols=4)
Set the default number of columns in a row for HTML tables.
Definition: tcpdf.php:13965
$textindent
Text indentation value (used for text-indent CSS attribute).
Definition: tcpdf.php:1394
$footer_font
Default font used on page footer.
Definition: tcpdf.php:607
static $uni_RE_PATTERN_ARABIC
Pattern to test Arabic strings using regular expressions.
static $alias_group_tot_pages
String alias for total number of pages in a single group.
$form_enctype
Current form encryption type (used during XHTML rendering).
Definition: tcpdf.php:1359
$OutlineRoot
Outline root for bookmark.
Definition: tcpdf.php:845
$numpages
Counts the number of pages.
Definition: tcpdf.php:1156
getCellMargins()
Get the internal Cell margin array.
Definition: tcpdf.php:2704
$xobjects
Array of XObjects.
Definition: tcpdf.php:1519
setEqualColumns($numcols=0, $width=0, $y='')
Set multiple columns of the same size.
Definition: tcpdf.php:21676
ScaleX($s_x, $x='', $y='')
Horizontal Scaling.
Definition: tcpdf.php:10994
_putcidfont0($font)
Output CID-0 fonts.
Definition: tcpdf.php:9031
setRasterizeVectorImages($mode)
Enable/disable rasterization of vector images using ImageMagick library.
Definition: tcpdf.php:22095
$n_js
Javascript counter.
Definition: tcpdf.php:861
AddSpotColor($name, $c, $m, $y, $k)
Defines a new spot color.
Definition: tcpdf.php:3716
GetCharWidth($char, $notlast=true)
Returns the length of the char in user unit for the current font considering current stretching and s...
Definition: tcpdf.php:4087
CoonsPatchMesh($x, $y, $w, $h, $col1=array(), $col2=array(), $col3=array(), $col4=array(), $coords=array(0.00, 0.0, 0.33, 0.00, 0.67, 0.00, 1.00, 0.00, 1.00, 0.33, 1.00, 0.67, 1.00, 1.00, 0.67, 1.00, 0.33, 1.00, 0.00, 1.00, 0.00, 0.67, 0.00, 0.33), $coords_min=0, $coords_max=1, $antialias=false)
Paints a coons patch mesh.
Definition: tcpdf.php:14352
$internal_encoding
PHP internal encoding.
Definition: tcpdf.php:778
static setPageBoxes($page, $type, $llx, $lly, $urx, $ury, $points=false, $k, $pagedim=array())
Set page boundaries.
$signature_appearance
Data for digital signature appearance.
Definition: tcpdf.php:1275
getHtmlDomArray($html)
Returns the HTML DOM array.
Definition: tcpdf.php:16250
copyPage($page=0)
Clone the specified page to a new page.
Definition: tcpdf.php:21201
static getPageLayoutMode($layout='SinglePage')
Get the canonical page layout mode.
setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='')
Enable document signature (requires the OpenSSL Library).
Definition: tcpdf.php:13415
$xobjid
Current XObject ID.
Definition: tcpdf.php:1533
$linethrough
line through state
Definition: tcpdf.php:868
SetLeftMargin($margin)
Defines the left margin.
Definition: tcpdf.php:2583
_UEvalue()
Compute UE value (used for encryption)
Definition: tcpdf.php:10644
setCellMargins($left='', $top='', $right='', $bottom='')
Set the internal Cell margins.
Definition: tcpdf.php:2682
static getTransformationMatrixProduct($ta, $tb)
Get the product of two SVG tranformation matrices.
Output($name='doc.pdf', $dest='I')
Send the document to a given destination: string, local file or browser.
Definition: tcpdf.php:7559
$regions
setPageUnit($unit)
Set the units of measure for the document.
Definition: tcpdf.php:2011
registrationMark($x, $y, $r, $double=false, $cola=array(100, 100, 100, 100, 'All'), $colb=array(0, 0, 0, 0, 'None'))
Paints a registration mark.
Definition: tcpdf.php:14240
$linestyleWidth
PDF string for width value of the last line.
Definition: tcpdf.php:1008
getPDFData()
Returns the PDF data.
Definition: tcpdf.php:10356
rollbackTransaction($self=false)
This method allows to undo the latest transaction by returning the latest saved TCPDF object with sta...
Definition: tcpdf.php:21652
static _escape($s)
Add "\" before "\", "(" and ")".
setFontSubsetting($enable=true)
Enable or disable default option for font subsetting.
Definition: tcpdf.php:22106
$overprint
Overprint mode array.
Definition: tcpdf.php:1787
AcceptPageBreak()
Whenever a page break condition is met, the method is called, and the break is issued or not dependin...
Definition: tcpdf.php:4913
$x
Current horizontal position in user unit for cell positioning.
Definition: tcpdf.php:294
checkPageBreak($h=0, $y='', $addpage=true)
Add page if needed.
Definition: tcpdf.php:4940
$premode
Boolean flag to indicate if we are inside a PRE tag.
Definition: tcpdf.php:1127
const K_TCPDF_THROW_EXCEPTION_ERROR
If true and PHP version is greater than 5, then the Error() method throw new exception instead of ter...
const K_TCPDF_CALLS_IN_HTML
If true allows to call TCPDF methods using HTML syntax IMPORTANT: For security reason, disable this feature if you are printing user HTML content.
$clMargin
Cell left margin (used by regions).
Definition: tcpdf.php:256
getTextShadow()
Return the text shadow parameters array.
Definition: tcpdf.php:21933
Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false, $hidden=false, $fitonpage=false, $alt=false, $altimgs=array())
Puts an image in the page.
Definition: tcpdf.php:6829
static getSpotColor($name, &$spotc)
Return the Spot color array.
_beginpage($orientation='', $format='')
Initialize a new page.
Definition: tcpdf.php:10010
_getxobjectdict()
Return XObjects Dictionary.
Definition: tcpdf.php:9353
getCSSBorderMargin($cssbspace, $width=0)
Get the border-spacing from CSS attribute.
Definition: tcpdf.php:16059
$CurrentFont
Current font info.
Definition: tcpdf.php:404
static UTF8ArrSubString($strarr, $start='', $end='', $unicode=true)
Extract a slice of the $strarr array and return it as string.
_putAPXObject($w=0, $h=0, $stream='')
Put appearance streams XObject used to define annotation&#39;s appearance states.
Definition: tcpdf.php:8745
_destroy($destroyall=false, $preserve_objcopy=false)
Unset all class variables except the following critical variables.
Definition: tcpdf.php:7757
AddLink()
Creates a new internal link and returns its identifier.
Definition: tcpdf.php:4683
setCellPaddings($left='', $top='', $right='', $bottom='')
Set the internal Cell paddings.
Definition: tcpdf.php:2646
getFooterMargin()
Returns footer margin in user units.
Definition: tcpdf.php:3331
static getFontFullPath($file, $fontdir=false)
Return font full path.
$pagegroups
Array that contains the number of pages in each page group.
Definition: tcpdf.php:896
_putsignature()
Add certification signature (DocMDP or UR3) You can set only one signature type.
Definition: tcpdf.php:13290
$javascript
Javascript code.
Definition: tcpdf.php:854
getMargins()
Returns an array containing current margins:
Definition: tcpdf.php:15746
$cntmrk
Array used to store content positions inside the pages buffer (keys are the page numbers).
Definition: tcpdf.php:973
$form_mode
Current method to submit forms.
Definition: tcpdf.php:1366
getFontStretching()
Get the percentage of character stretching.
Definition: tcpdf.php:22467
$txt
Definition: error.php:12
setGraphicVars($gvars, $extended=false)
Set graphic variables.
Definition: tcpdf.php:20595
$svggradients
Array of SVG gradients.
Definition: tcpdf.php:1610
setPage($pnum, $resetmargins=false)
Move pointer at the specified document page and update page dimensions.
Definition: tcpdf.php:2995
Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array())
Draws a rectangle.
Definition: tcpdf.php:11512
$starting_page_number
Starting page number.
Definition: tcpdf.php:518
$font_obj_ids
Store the font object IDs.
Definition: tcpdf.php:1205
getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='')
Get the array that defines the signature appearance (page and rectangle coordinates).
Definition: tcpdf.php:13486
getCellBorder($x, $y, $w, $h, $brd)
Returns the code to draw the cell border.
Definition: tcpdf.php:5554
$h
Current height of page in user unit.
Definition: tcpdf.php:238
static $enc_padding
Encryption padding string.
$start_transaction_y
Store Y position when startTransaction() is called.
Definition: tcpdf.php:1408
$jpeg_quality
Set the default JPEG compression quality (1-100).
Definition: tcpdf.php:917
setStartingPageNumber($num=1)
Set the starting page number.
Definition: tcpdf.php:13574
SetAutoPageBreak($auto, $margin=0)
Enables or disables the automatic page breaking mode.
Definition: tcpdf.php:2790
getPageNumGroupAlias()
Return the alias for the page number on the current page group.
Definition: tcpdf.php:13656
getDocModificationTimestamp()
Returns document modification timestamp in seconds.
Definition: tcpdf.php:10219
$last_enc_key
Last RC4 key encrypted (cached for optimisation).
Definition: tcpdf.php:815
static UniArrSubString($uniarr, $start='', $end='')
Extract a slice of the $uniarr array and return it as string.
$y
Current vertical position in user unit for cell positioning.
Definition: tcpdf.php:300
replaceChar($oldchar, $newchar)
Replace a char if is defined on the current font.
Definition: tcpdf.php:5531
Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array())
Draws a poly-Bezier curve.
Definition: tcpdf.php:11613
$opencell
Boolean flag to indicate if the border of the cell sides that cross the page should be removed...
Definition: tcpdf.php:1113
$FontFiles
Array of font files.
Definition: tcpdf.php:330
$customlistindent
HTML PARSER: custom indent amount for lists.
Definition: tcpdf.php:1106
$barcodeobj
PHP class to creates array representations for common 1D barcodes to be used with TCPDF (http://www...
setSpotColor($type, $name, $tint=100)
Set the spot color for the specified type (&#39;draw&#39;, &#39;fill&#39;, &#39;text&#39;).
Definition: tcpdf.php:3732
$filename
Definition: buildRTE.php:89
setFontStretching($perc=100)
Set the percentage of character stretching.
Definition: tcpdf.php:22456
$tsa_timestamp
Boolean flag to enable document timestamping with TSA.
Definition: tcpdf.php:1289
$radio_groups
List of radio group objects IDs.
Definition: tcpdf.php:1387
movePage($frompage, $topage)
Move a page to a previous position.
Definition: tcpdf.php:20840
_dounderline($x, $y, $txt)
Underline text.
Definition: tcpdf.php:10092
static fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='', &$tagvspaces)
Cleanup HTML code (requires HTML Tidy library).
$svgtext
SVG text.
Definition: tcpdf.php:1666
setColorArray($type, $color, $ret=false)
Set the color array for the specified type (&#39;draw&#39;, &#39;fill&#39;, &#39;text&#39;).
Definition: tcpdf.php:3817
static _md5_16($str)
Encrypts a string using MD5 and returns it&#39;s value as a binary string.
$booklet
Booklet mode for double-sided pages.
Definition: tcpdf.php:1085
_enddoc()
Output end of document (EOF).
Definition: tcpdf.php:9919
$compress
Compression flag.
Definition: tcpdf.php:184
$l
Language templates.
Definition: tcpdf.php:613
registrationMarkCMYK($x, $y, $r)
Paints a CMYK registration mark.
Definition: tcpdf.php:14267
$linestyleCap
PDF string for CAP value of the last line.
Definition: tcpdf.php:1015
ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false)
Creates a List-box field.
Definition: tcpdf.php:12870
$ColorFlag
Indicates whether fill and text colors are different.
Definition: tcpdf.php:440
foreach($mandatory_scripts as $file) $timestamp
Definition: buildRTE.php:81
if(!file_exists(getcwd().'/ilias.ini.php')) if(isset( $_GET["client_id"]))
registration confirmation script for ilias
Definition: confirmReg.php:20
checkPageRegions($h, $x, $y)
Check page for no-write regions and adapt current coordinates and page margins if necessary...
Definition: tcpdf.php:22573
$inthead
True when we are printing the thead section on a new page.
Definition: tcpdf.php:1415
$original_rMargin
Original right margin value.
Definition: tcpdf.php:595
resetColumns()
Remove columns and reset page margins.
Definition: tcpdf.php:21708
setOverprint($stroking=true, $nonstroking='', $mode=0)
Set overprint mode for stroking (OP) and non-stroking (op) painting operations.
Definition: tcpdf.php:13871
static $svginheritprop
Array of hinheritable SVG properties.
$maxselcol
Maximum page and column selected.
Definition: tcpdf.php:1450
SetDrawColorArray($color, $ret=false)
Defines the color used for all drawing operations (lines, rectangles and cell borders).
Definition: tcpdf.php:3848
PageNo()
Returns the current page number.
Definition: tcpdf.php:3689
$htmlLinkFontStyle
Default font style to add to html links.
Definition: tcpdf.php:1149
static $alias_right_shift
String alias for right shift compensation used to correctly align page numbers on the right...
$pageopen
Store the fage status (true when opened, false when closed).
Definition: tcpdf.php:1212
Translate($t_x, $t_y)
Translate graphic object horizontally and vertically.
Definition: tcpdf.php:11138
$openMarkedContent
Boolean flag to indicate if marked-content sequence is open.
Definition: tcpdf.php:1036
$wPt
Current width of page in points.
Definition: tcpdf.php:220
_generateencryptionkey()
Compute encryption key.
Definition: tcpdf.php:10722
replaceMissingChars($text, $font='', $style='', $subs=array())
Replace missing font characters on selected font with specified substitutions.
Definition: tcpdf.php:4635
_putimages()
Output images.
Definition: tcpdf.php:9101
$header_margin
Minimum distance between header and top page margin.
Definition: tcpdf.php:575
getLastH()
Get the last cell height.
Definition: tcpdf.php:2450
GetY()
Returns the ordinate of the current position.
Definition: tcpdf.php:7429
$htmlLinkColorArray
Default color for html links.
Definition: tcpdf.php:1142
SetTopMargin($margin)
Defines the top margin.
Definition: tcpdf.php:2598
_putencryption()
Put encryption on PDF document.
Definition: tcpdf.php:10491
$imgscale
Adjusting factor to convert pixels to user units.
Definition: tcpdf.php:542
$InHeader
Flag set when processing page header.
Definition: tcpdf.php:458
Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true)
Draws a polygon.
Definition: tcpdf.php:11862
getCSSBorderStyle($cssborder)
Returns the border style array from CSS border properties.
Definition: tcpdf.php:15900
PageNoFormatted()
Returns the current page number formatted as a string.
Definition: tcpdf.php:13689
$re_spaces
Regular expression used to find blank characters (required for word-wrapping).
Definition: tcpdf.php:1303
getGroupPageNoFormatted()
Returns the current group page number formatted as a string.
Definition: tcpdf.php:13679
$page_obj_id
ID of page objects.
Definition: tcpdf.php:1324
$htmlvspace
Count the latest inserted vertical spaces on HTML.
Definition: tcpdf.php:1043
printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false)
Print an XObject Template.
Definition: tcpdf.php:22323
SetProtection($permissions=array('print', 'modify', 'copy', 'annot-forms', 'fill-forms', 'extract', 'assemble', 'print-high'), $user_pass='', $owner_pass=null, $mode=0, $pubkeys=null)
Set document protection Remark: the protection against modification is for people who have the full A...
Definition: tcpdf.php:10840
$author
Document author.
Definition: tcpdf.php:500
__construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false)
This is the class constructor.
Definition: tcpdf.php:1838
_putheader()
Output PDF File Header (7.5.2).
Definition: tcpdf.php:9910
$footer_text_color
Color for footer text (RGB array).
Definition: tcpdf.php:676
static _toJPEG($image, $quality, $tempfile)
Convert the loaded image to a JPEG and then return a structure for the PDF creator.
isUnicodeFont()
Return true if the current font is unicode type.
Definition: tcpdf.php:22173
addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0, 0, 0))
Output a Table of Content Index (TOC).
Definition: tcpdf.php:21278
LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0, 0, 1, 0))
Paints a linear colour gradient.
Definition: tcpdf.php:14311
cropMark($x, $y, $w, $h, $type='T, R, B, L', $color=array(100, 100, 100, 100, 'All'))
Paints crop marks.
Definition: tcpdf.php:14169
StartTransform()
Starts a 2D tranformation saving current graphic state.
Definition: tcpdf.php:10946
$svgclippaths
Array of SVG clipPath commands.
Definition: tcpdf.php:1645
SkewY($angle_y, $x='', $y='')
Skew vertically.
Definition: tcpdf.php:11203
$svgstyles
Array of SVG properties.
Definition: tcpdf.php:1680
$newpagegroup
Array of page numbers were a new page group was started (the page numbers are the keys of the array)...
Definition: tcpdf.php:889
segSVGContentHandler($parser, $data)
Sets the character data handler function for the XML parser.
Definition: tcpdf.php:24480
$fontkeys
Store the font keys.
Definition: tcpdf.php:1198
getGDgamma($img, $c)
Get the GD-corrected PNG gamma value from alpha color.
Definition: tcpdf.php:7343
$lMargin
Left margin.
Definition: tcpdf.php:244
_putextgstates()
Put extgstates for object transparency.
Definition: tcpdf.php:13842
static revstrpos($haystack, $needle, $offset=0)
Find position of last occurrence of a substring in a string.
$gdgammacache
Cache array for computed GD gamma values.
Definition: tcpdf.php:1816
SetLineStyle($style, $ret=false)
Set line style.
Definition: tcpdf.php:11323
SetRightMargin($margin)
Defines the right margin.
Definition: tcpdf.php:2613
$columns
Array of column measures (width, space, starting Y position).
Definition: tcpdf.php:1422
setTempRTL($mode)
Force temporary RTL language direction.
Definition: tcpdf.php:2374
static UTF8ToLatin1($str, $isunicode=true, &$currentfont)
Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.
replaceBuffer($data)
Replace the buffer content.
Definition: tcpdf.php:20688
$tcpdflink
If true print TCPDF meta link.
Definition: tcpdf.php:1809
getHeaderMargin()
Returns header margin in user units.
Definition: tcpdf.php:3311
Open()
This method begins the generation of the PDF document.
Definition: tcpdf.php:2939
getInternalPageNumberAliases($a='')
Return an array containing variations for the basic page number alias.
Definition: tcpdf.php:7809
endTOCPage()
Terminate the current TOC (Table Of Content) page.
Definition: tcpdf.php:3087
Clip($x, $y, $w, $h)
Set a rectangular clipping area.
Definition: tcpdf.php:14444
getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0)
This method return the estimated height needed for printing a simple text string using the Multicell(...
Definition: tcpdf.php:6239
$last_enc_key_c
Last RC4 computed key.
Definition: tcpdf.php:822
const K_BLANK_IMAGE
Installation path (/var/www/tcpdf/).
static objclone($object)
Creates a copy of a class object.
$bufferlen
Length of the buffer in bytes.
Definition: tcpdf.php:1184
SetMargins($left, $top, $right=-1, $keepmargins=false)
Defines the left, top and right margins.
Definition: tcpdf.php:2561
$text
endSVGElementHandler($parser, $name)
Sets the closing SVG element handler function for the XML parser.
Definition: tcpdf.php:24373
SetSubject($subject)
Defines the subject of the document.
Definition: tcpdf.php:2878
endTemplate()
End the current XObject Template started with startTemplate() and restore the previous graphic state...
Definition: tcpdf.php:22294
getAliasRightShift()
Returns the string alias used right align page numbers.
Definition: tcpdf.php:13585
setVisibility($v)
Set the visibility of the successive elements.
Definition: tcpdf.php:13765
static $uni_identity_h
ToUnicode map for Identity-H stream static.
static getImageFileType($imgfile, $iminfo=array())
Return the image type given the file name or array returned by getimagesize() function.
putHtmlListBullet($listdepth, $listtype='', $size=10)
Output an HTML list bullet or ordered item symbol.
Definition: tcpdf.php:20332
RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false)
Creates a RadioButton field.
Definition: tcpdf.php:12767
replaceRightShiftPageNumAliases($page, $aliases, $diff)
Replace right shift page number aliases with spaces to correct right alignment.
Definition: tcpdf.php:7848
SetAbsX($x)
Set the absolute X coordinate of the current pointer.
Definition: tcpdf.php:7520
$space
Definition: Sanitizer.php:42
RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5, 0.5, 0.5, 0.5, 1))
Paints a radial colour gradient.
Definition: tcpdf.php:14329
$cache_file_length
Array used to store the lengths of cache files.
Definition: tcpdf.php:1233
TranslateY($t_y)
Translate graphic object vertically.
Definition: tcpdf.php:11126
pixelsToUnits($px)
Converts pixels to User&#39;s Units.
Definition: tcpdf.php:10417
$border_style
Definition: example_022.php:83
isCharDefined($char, $font='', $style='')
Return true in the character is present in the specified font.
Definition: tcpdf.php:4608
getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt')
Convert HTML string containing font size value to points.
Definition: tcpdf.php:16196
$encryptdata
Array containing encryption settings.
Definition: tcpdf.php:808
$spot_colors
Array of Spot colors.
Definition: tcpdf.php:1050
_dochecks()
Check for locale-related bug.
Definition: tcpdf.php:7792
setPageOrientation($orientation, $autopagebreak='', $bottommargin='')
Set page orientation.
Definition: tcpdf.php:2217
$listordered
HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
Definition: tcpdf.php:722
$LayoutMode
Layout display mode.
Definition: tcpdf.php:476
$links
Array of internal links.
Definition: tcpdf.php:360
IncludeJS($script)
Adds a javascript.
Definition: tcpdf.php:12494
setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0, 0, 0), $lc=array(0, 0, 0))
Set header data.
Definition: tcpdf.php:3257
getCellHeightRatio()
return the height of cell repect font height.
Definition: tcpdf.php:13984
static _JScolor($color)
Convert color to javascript color.
RegularPolygon($x0, $y0, $r, $ns, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array())
Draws a regular polygon.
Definition: tcpdf.php:11963
$starty
write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false)
Print 2D Barcode.
Definition: tcpdf.php:15517
AddFont($family, $style='', $fontfile='', $subset='default')
Imports a TrueType, Type1, core, or CID0 font and makes it available.
Definition: tcpdf.php:4171
$bMargin
Page break margin.
Definition: tcpdf.php:274
setColumnsArray($columns)
Set columns array.
Definition: tcpdf.php:21721
SetCellPadding($pad)
Set the same internal Cell padding for top, right, bottom, left-.
Definition: tcpdf.php:2627
setFormDefaultProp($prop=array())
Set default properties for form fields.
Definition: tcpdf.php:12617
static getObjFilename($type='tmp', $file_id='')
Returns a temporary filename for caching object on filesystem.
static _parsejpeg($file)
Extract info from a JPEG file without using the GD library.
ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false)
Embedd a Scalable Vector Graphics (SVG) image.
Definition: tcpdf.php:22676
static _toPNG($image, $tempfile)
Convert the loaded image to a PNG and then return a structure for the PDF creator.
$imgdata
Definition: example_009.php:81
$PDFVersion
PDF version.
Definition: tcpdf.php:557
$keywords
Document keywords.
Definition: tcpdf.php:506
getHeaderFont()
Get header font.
Definition: tcpdf.php:10313
_datastring($s, $n=0)
Format a data string for meta information.
Definition: tcpdf.php:10169
Scale($s_x, $s_y, $x='', $y='')
Vertical and horizontal non-proportional Scaling.
Definition: tcpdf.php:11034
Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0, 0, 0), $x=-1, $link='')
Adds a bookmark.
Definition: tcpdf.php:12303
fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='')
Cleanup HTML code (requires HTML Tidy library).
Definition: tcpdf.php:15833
SetFillSpotColor($name, $tint=100)
Defines the spot color used for all filling operations (filled rectangles and cell backgrounds)...
Definition: tcpdf.php:3790
static _RC4($key, $text, &$last_enc_key, &$last_enc_key_c)
Returns the input text encrypted using RC4 algorithm and the specified key.
$custom_xmp
Custom XMP data.
Definition: tcpdf.php:1779
SetLineWidth($width)
Defines the line width.
Definition: tcpdf.php:11280
hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8)
Returns text with soft hyphens.
Definition: tcpdf.php:22031
_getobj($objid='')
Return the starting object string for the selected object ID.
Definition: tcpdf.php:10075
CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false)
Creates a CheckBox field.
Definition: tcpdf.php:13043
getOriginalMargins()
Returns an array containing original margins:
Definition: tcpdf.php:15773
_getannotsrefs($n)
Get references to page annotations.
Definition: tcpdf.php:8058
$transfmatrix_key
Current key for transformation matrix.
Definition: tcpdf.php:1078
static encodeNameObject($name)
Encode a name object.
$transfmrk
Array used to store positions of graphics transformation blocks inside the page buffer.
Definition: tcpdf.php:1135
$svgcliptm
Array of SVG clipPath tranformation matrix.
Definition: tcpdf.php:1652
_outCurveY($x1, $y1, $x3, $y3)
Append a cubic Bezier curve to the current path.
Definition: tcpdf.php:11465
getRTL()
Return the RTL status.
Definition: tcpdf.php:2364
$doc_modification_timestamp
Document modification date-time.
Definition: tcpdf.php:1772
_putEmbeddedFiles()
Embedd the attached files.
Definition: tcpdf.php:4835
static $uni_RE_PATTERN_RTL
Pattern to test RTL (Righ-To-Left) strings using regular expressions.
$intmrk
Array used to store positions inside the pages buffer (keys are the page numbers).
Definition: tcpdf.php:952
addPageRegion($region)
Add a single no-write region on selected page.
Definition: tcpdf.php:22536
static UTF8ArrayToUniArray($ta, $isunicode=true)
Convert an array of UTF8 values to array of unicode characters.
$diffs
Array of encoding differences.
Definition: tcpdf.php:336
if(! $in) print Initializing normalization quick check tables n
$HREF
HTML PARSER: array to store current link and rendering styles.
Definition: tcpdf.php:704
$epsmarker
String used to mark the beginning and end of EPS image blocks.
Definition: tcpdf.php:1064
getFooterFont()
Get Footer font.
Definition: tcpdf.php:10333
SkewX($angle_x, $x='', $y='')
Skew horizontally.
Definition: tcpdf.php:11190
$docinfounicode
If true set the document information dictionary in Unicode.
Definition: tcpdf.php:482
$page
Current page number.
Definition: tcpdf.php:142
setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false)
Set alpha for stroking (CA) and non-stroking (ca) operations.
Definition: tcpdf.php:13910
$start_transaction_page
Store page number when startTransaction() is called.
Definition: tcpdf.php:1401
removePageRegion($key)
Remove a single no-write region.
Definition: tcpdf.php:22555
GetLineWidth()
Returns the current the line width.
Definition: tcpdf.php:11296
$pages
Array containing pages.
Definition: tcpdf.php:172
Transform($tm)
Apply graphic transformations.
Definition: tcpdf.php:11248
setDocModificationTimestamp($time)
Set the document modification timestamp.
Definition: tcpdf.php:10196
$cell_margin
Array of cell margins (&#39;T&#39; => top, &#39;R&#39; => right, &#39;B&#39; => bottom, &#39;L&#39; => left).
Definition: tcpdf.php:288
SVGPath($d, $style='')
Draws an SVG path.
Definition: tcpdf.php:23331
$colxshift
Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding.
Definition: tcpdf.php:1457
$images
Array of used images.
Definition: tcpdf.php:342
$gradients
Array for storing gradient information.
Definition: tcpdf.php:945
addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='')
Add an empty digital signature appearance (a cliccable rectangle area to get signature properties) ...
Definition: tcpdf.php:13468
getBorderStartPosition()
Return the starting coordinates to draw an html border.
Definition: tcpdf.php:19921
static fopenLocal($filename, $mode)
Wrapper to use fopen only with local files.
$html
Definition: example_001.php:87
$svgtextmode
SVG text properties.
Definition: tcpdf.php:1673
MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $valign='T', $fitcell=false)
This method allows printing text with line breaks.
Definition: tcpdf.php:5757
$params
Definition: example_049.php:96
getPageWidth($pagenum='')
Returns the page width in units.
Definition: tcpdf.php:2501
const K_SMALL_RATIO
Reduction factor for small font.
Skew($angle_x, $angle_y, $x='', $y='')
Skew.
Definition: tcpdf.php:11217
SetX($x, $rtloff=false)
Defines the abscissa of the current position.
Definition: tcpdf.php:7442
adjustCellPadding($brd=0)
Adjust the internal Cell padding array to take account of the line width.
Definition: tcpdf.php:2715
addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false)
Output anchor link.
Definition: tcpdf.php:10375
_putspotcolors()
Output Spot Colors Resources.
Definition: tcpdf.php:9333
static getSVGTransformMatrix($attribute)
Get the tranformation matrix from SVG transform attribute.
getFormDefaultProp()
Return the default properties for form fields.
Definition: tcpdf.php:12628
setPageBuffer($page, $data, $append=false)
Set page buffer content.
Definition: tcpdf.php:20711
startTemplate($w=0, $h=0, $group=false)
Start a new XObject Template.
Definition: tcpdf.php:22229
_OEvalue()
Compute OE value (used for encryption)
Definition: tcpdf.php:10694
static getVectorsAngle($x1, $y1, $x2, $y2)
Returns the angle in radiants between two vectors.
$rasterize_vector_images
Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick librar...
Definition: tcpdf.php:1498
getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M')
Returns the PDF string code to print a cell (rectangular area) with optional borders, background color and character string.
Definition: tcpdf.php:5063
$header_title
Title to be printed on default page header.
Definition: tcpdf.php:649
Gradient($type, $coords, $stops, $background=array(), $antialias=false)
Output gradient.
Definition: tcpdf.php:14471
SetTextSpotColor($name, $tint=100)
Defines the spot color used for text.
Definition: tcpdf.php:3802
const K_CELL_HEIGHT_RATIO
Height of cell respect font height.
addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0, 0, 0))
Output a Table Of Content Index (TOC) using HTML templates.
Definition: tcpdf.php:21488
getSpaceString()
Returns the string used to find spaces.
Definition: tcpdf.php:17038
_putdests()
Insert Named Destinations.
Definition: tcpdf.php:12260
$xc
_newobj()
Begin a new object and return the object number.
Definition: tcpdf.php:10063
static fileGetContents($file)
Reads entire file into a string.
GetAbsX()
Returns the absolute X value of current position.
Definition: tcpdf.php:7418
static get_mqr()
Gets the current active configuration setting of magic_quotes_runtime (if the get_magic_quotes_runtim...
endPage($tocpage=false)
Terminate the current page.
Definition: tcpdf.php:3126
setPrintHeader($val=true)
Set a flag to print page header.
Definition: tcpdf.php:3339
SVGTransform($tm)
Apply SVG graphic transformation matrix.
Definition: tcpdf.php:23017
$fgcolor
Current foreground color.
Definition: tcpdf.php:716
getFontBuffer($font)
Get font buffer content.
Definition: tcpdf.php:20825
$theadMargins
Margins used for table header.
Definition: tcpdf.php:1247
deletePage($page)
Remove the specified page.
Definition: tcpdf.php:21011
$FontSizePt
Current font size in points.
Definition: tcpdf.php:410
$svgdir
Directory used for the last SVG image.
Definition: tcpdf.php:1596
setImageSubBuffer($image, $key, $data)
Set image buffer content for a specified sub-key.
Definition: tcpdf.php:20764
getFontFamilyName($fontfamily)
Return normalized font name.
Definition: tcpdf.php:22185
SetAbsY($y)
Set the absolute Y coordinate of the current pointer.
Definition: tcpdf.php:7531
getCSSFontSpacing($spacing, $parent=0)
Returns the letter-spacing value from CSS value.
Definition: tcpdf.php:16093
setContentMark($page=0)
Set start-writing mark on selected page.
Definition: tcpdf.php:3236
_puttruetypeunicode($font)
Adds unicode fonts.
Definition: tcpdf.php:8927
static getAnnotOptFromJSProp($prop, &$spot_colors, $rtl=false)
Convert JavaScript form fields properties array to Annotation Properties array.
_putannotsobjs()
Output annotations objects for all pages.
Definition: tcpdf.php:8102
setFontBuffer($font, $data)
Set font buffer content.
Definition: tcpdf.php:20792
$svggradientid
ID of last SVG gradient.
Definition: tcpdf.php:1617
SetTitle($title)
Defines the title of the document.
Definition: tcpdf.php:2867
setLanguageArray($language)
Set language array.
Definition: tcpdf.php:10343
getOverprint()
Get the overprint mode array (OP, op, OPM).
Definition: tcpdf.php:13897
$fwPt
Width of page format in points.
Definition: tcpdf.php:208
getImageRBX()
Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image...
Definition: tcpdf.php:3357