ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
tcpdf.php
Go to the documentation of this file.
1 <?php
2 //============================================================+
3 // File name : tcpdf.php
4 // Version : 6.2.12
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 
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 
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 
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  // tcpdf-patch: begin
6769  // 2016-06-02 mbecker will create a pull request to make this patch
6770  if($h)
6771  {
6772  $ratio_wh = ($w / $h);
6773  }
6774  else
6775  {
6776  $ratio_wh = 1;
6777  }
6778  // tcpdf-patch: end
6779  if (($y + $h) > $this->PageBreakTrigger) {
6780  $h = $this->PageBreakTrigger - $y;
6781  $w = ($h * $ratio_wh);
6782  }
6783  if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
6784  $w = $this->w - $this->rMargin - $x;
6785  $h = ($w / $ratio_wh);
6786  } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
6787  $w = $x - $this->lMargin;
6788  $h = ($w / $ratio_wh);
6789  }
6790  }
6791  return array($w, $h, $x, $y);
6792  }
6793 
6828  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()) {
6829  if ($this->state != 2) {
6830  return;
6831  }
6832  if (strcmp($x, '') === 0) {
6833  $x = $this->x;
6834  }
6835  if (strcmp($y, '') === 0) {
6836  $y = $this->y;
6837  }
6838  // check page for no-write regions and adapt page margins if necessary
6839  list($x, $y) = $this->checkPageRegions($h, $x, $y);
6840  $exurl = ''; // external streams
6841  $imsize = FALSE;
6842  // check if we are passing an image as file or string
6843  if ($file[0] === '@') {
6844  // image from string
6845  $imgdata = substr($file, 1);
6846  } else { // image file
6847  if ($file[0] === '*') {
6848  // image as external stream
6849  $file = substr($file, 1);
6850  $exurl = $file;
6851  }
6852  // check if is a local file
6853  if (!@file_exists($file)) {
6854  // try to encode spaces on filename
6855  $tfile = str_replace(' ', '%20', $file);
6856  if (@file_exists($tfile)) {
6857  $file = $tfile;
6858  }
6859  }
6860  if (($imsize = @getimagesize($file)) === FALSE) {
6861  if (in_array($file, $this->imagekeys)) {
6862  // get existing image data
6863  $info = $this->getImageBuffer($file);
6864  $imsize = array($info['w'], $info['h']);
6865  } elseif (strpos($file, '__tcpdf_'.$this->file_id.'_img') === FALSE) {
6867  }
6868  }
6869  }
6870  if (!empty($imgdata)) {
6871  // copy image to cache
6872  $original_file = $file;
6873  $file = TCPDF_STATIC::getObjFilename('img', $this->file_id);
6874  $fp = TCPDF_STATIC::fopenLocal($file, 'w');
6875  if (!$fp) {
6876  $this->Error('Unable to write file: '.$file);
6877  }
6878  fwrite($fp, $imgdata);
6879  fclose($fp);
6880  unset($imgdata);
6881  $imsize = @getimagesize($file);
6882  if ($imsize === FALSE) {
6883  unlink($file);
6884  $file = $original_file;
6885  }
6886  }
6887  if ($imsize === FALSE) {
6888  if (($w > 0) AND ($h > 0)) {
6889  // get measures from specified data
6890  $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6891  $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6892  $imsize = array($pw, $ph);
6893  } else {
6894  $this->Error('[Image] Unable to get the size of the image: '.$file);
6895  }
6896  }
6897  // file hash
6898  $filehash = md5($file);
6899  // get original image width and height in pixels
6900  list($pixw, $pixh) = $imsize;
6901  // calculate image width and height on document
6902  if (($w <= 0) AND ($h <= 0)) {
6903  // convert image size to document unit
6904  $w = $this->pixelsToUnits($pixw);
6905  $h = $this->pixelsToUnits($pixh);
6906  } elseif ($w <= 0) {
6907  $w = $h * $pixw / $pixh;
6908  } elseif ($h <= 0) {
6909  $h = $w * $pixh / $pixw;
6910  } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
6911  if (strlen($fitbox) !== 2) {
6912  // set default alignment
6913  $fitbox = '--';
6914  }
6915  // scale image dimensions proportionally to fit within the ($w, $h) box
6916  if ((($w * $pixh) / ($h * $pixw)) < 1) {
6917  // store current height
6918  $oldh = $h;
6919  // calculate new height
6920  $h = $w * $pixh / $pixw;
6921  // height difference
6922  $hdiff = ($oldh - $h);
6923  // vertical alignment
6924  switch (strtoupper($fitbox[1])) {
6925  case 'T': {
6926  break;
6927  }
6928  case 'M': {
6929  $y += ($hdiff / 2);
6930  break;
6931  }
6932  case 'B': {
6933  $y += $hdiff;
6934  break;
6935  }
6936  }
6937  } else {
6938  // store current width
6939  $oldw = $w;
6940  // calculate new width
6941  $w = $h * $pixw / $pixh;
6942  // width difference
6943  $wdiff = ($oldw - $w);
6944  // horizontal alignment
6945  switch (strtoupper($fitbox[0])) {
6946  case 'L': {
6947  if ($this->rtl) {
6948  $x -= $wdiff;
6949  }
6950  break;
6951  }
6952  case 'C': {
6953  if ($this->rtl) {
6954  $x -= ($wdiff / 2);
6955  } else {
6956  $x += ($wdiff / 2);
6957  }
6958  break;
6959  }
6960  case 'R': {
6961  if (!$this->rtl) {
6962  $x += $wdiff;
6963  }
6964  break;
6965  }
6966  }
6967  }
6968  }
6969  // fit the image on available space
6970  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
6971  // calculate new minimum dimensions in pixels
6972  $neww = round($w * $this->k * $dpi / $this->dpi);
6973  $newh = round($h * $this->k * $dpi / $this->dpi);
6974  // check if resize is necessary (resize is used only to reduce the image)
6975  $newsize = ($neww * $newh);
6976  $pixsize = ($pixw * $pixh);
6977  if (intval($resize) == 2) {
6978  $resize = true;
6979  } elseif ($newsize >= $pixsize) {
6980  $resize = false;
6981  }
6982  // check if image has been already added on document
6983  $newimage = true;
6984  if (in_array($file, $this->imagekeys)) {
6985  $newimage = false;
6986  // get existing image data
6987  $info = $this->getImageBuffer($file);
6988  if (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE) {
6989  // check if the newer image is larger
6990  $oldsize = ($info['w'] * $info['h']);
6991  if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
6992  $newimage = true;
6993  }
6994  }
6995  } elseif (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE)) {
6996  // create temp image file (without alpha channel)
6997  $tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash;
6998  // create temp alpha file
6999  $tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash;
7000  // check for cached images
7001  if (in_array($tempfile_plain, $this->imagekeys)) {
7002  // get existing image data
7003  $info = $this->getImageBuffer($tempfile_plain);
7004  // check if the newer image is larger
7005  $oldsize = ($info['w'] * $info['h']);
7006  if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7007  $newimage = true;
7008  } else {
7009  $newimage = false;
7010  // embed mask image
7011  $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7012  // embed image, masked with previously embedded mask
7013  return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7014  }
7015  }
7016  }
7017  if ($newimage) {
7018  //First use of image, get info
7019  $type = strtolower($type);
7020  if ($type == '') {
7021  $type = TCPDF_IMAGES::getImageFileType($file, $imsize);
7022  } elseif ($type == 'jpg') {
7023  $type = 'jpeg';
7024  }
7025  $mqr = TCPDF_STATIC::get_mqr();
7026  TCPDF_STATIC::set_mqr(false);
7027  // Specific image handlers (defined on TCPDF_IMAGES CLASS)
7028  $mtd = '_parse'.$type;
7029  // GD image handler function
7030  $gdfunction = 'imagecreatefrom'.$type;
7031  $info = false;
7032  if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
7033  // TCPDF image functions
7034  $info = TCPDF_IMAGES::$mtd($file);
7035  if (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE)
7036  AND (($info === 'pngalpha') OR (isset($info['trns']) AND !empty($info['trns'])))) {
7037  return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
7038  }
7039  }
7040  if (($info === false) AND function_exists($gdfunction)) {
7041  try {
7042  // GD library
7043  $img = $gdfunction($file);
7044  if ($img !== false) {
7045  if ($resize) {
7046  $imgr = imagecreatetruecolor($neww, $newh);
7047  if (($type == 'gif') OR ($type == 'png')) {
7049  }
7050  imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
7051  $img = $imgr;
7052  }
7053  if (($type == 'gif') OR ($type == 'png')) {
7054  $info = TCPDF_IMAGES::_toPNG($img, TCPDF_STATIC::getObjFilename('img', $this->file_id));
7055  } else {
7056  $info = TCPDF_IMAGES::_toJPEG($img, $this->jpeg_quality, TCPDF_STATIC::getObjFilename('img', $this->file_id));
7057  }
7058  }
7059  } catch(Exception $e) {
7060  $info = false;
7061  }
7062  }
7063  if (($info === false) AND extension_loaded('imagick')) {
7064  try {
7065  // ImageMagick library
7066  $img = new Imagick();
7067  if ($type == 'svg') {
7068  if ($file[0] === '@') {
7069  // image from string
7070  $svgimg = substr($file, 1);
7071  } else {
7072  // get SVG file content
7074  }
7075  if ($svgimg !== FALSE) {
7076  // get width and height
7077  $regs = array();
7078  if (preg_match('/<svg([^>]*)>/si', $svgimg, $regs)) {
7079  $svgtag = $regs[1];
7080  $tmp = array();
7081  if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7082  $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7083  $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit;
7084  $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
7085  } else {
7086  $ow = $w;
7087  }
7088  $tmp = array();
7089  if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7090  $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7091  $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit;
7092  $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
7093  } else {
7094  $oh = $h;
7095  }
7096  $tmp = array();
7097  if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
7098  $vbw = ($ow * $this->imgscale * $this->k);
7099  $vbh = ($oh * $this->imgscale * $this->k);
7100  $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh);
7101  $svgtag = $vbox.$svgtag;
7102  }
7103  $svgimg = preg_replace('/<svg([^>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
7104  }
7105  $img->readImageBlob($svgimg);
7106  }
7107  } else {
7108  $img->readImage($file);
7109  }
7110  if ($resize) {
7111  $img->resizeImage($neww, $newh, 10, 1, false);
7112  }
7113  $img->setCompressionQuality($this->jpeg_quality);
7114  $img->setImageFormat('jpeg');
7115  $tempname = TCPDF_STATIC::getObjFilename('img', $this->file_id);
7116  $img->writeImage($tempname);
7117  $info = TCPDF_IMAGES::_parsejpeg($tempname);
7118  unlink($tempname);
7119  $img->destroy();
7120  } catch(Exception $e) {
7121  $info = false;
7122  }
7123  }
7124  if ($info === false) {
7125  // unable to process image
7126  return;
7127  }
7128  TCPDF_STATIC::set_mqr($mqr);
7129  if ($ismask) {
7130  // force grayscale
7131  $info['cs'] = 'DeviceGray';
7132  }
7133  if ($imgmask !== false) {
7134  $info['masked'] = $imgmask;
7135  }
7136  if (!empty($exurl)) {
7137  $info['exurl'] = $exurl;
7138  }
7139  // array of alternative images
7140  $info['altimgs'] = $altimgs;
7141  // add image to document
7142  $info['i'] = $this->setImageBuffer($file, $info);
7143  }
7144  // set alignment
7145  $this->img_rb_y = $y + $h;
7146  // set alignment
7147  if ($this->rtl) {
7148  if ($palign == 'L') {
7149  $ximg = $this->lMargin;
7150  } elseif ($palign == 'C') {
7151  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7152  } elseif ($palign == 'R') {
7153  $ximg = $this->w - $this->rMargin - $w;
7154  } else {
7155  $ximg = $x - $w;
7156  }
7157  $this->img_rb_x = $ximg;
7158  } else {
7159  if ($palign == 'L') {
7160  $ximg = $this->lMargin;
7161  } elseif ($palign == 'C') {
7162  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7163  } elseif ($palign == 'R') {
7164  $ximg = $this->w - $this->rMargin - $w;
7165  } else {
7166  $ximg = $x;
7167  }
7168  $this->img_rb_x = $ximg + $w;
7169  }
7170  if ($ismask OR $hidden) {
7171  // image is not displayed
7172  return $info['i'];
7173  }
7174  $xkimg = $ximg * $this->k;
7175  if (!$alt) {
7176  // only non-alternative immages will be set
7177  $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']));
7178  }
7179  if (!empty($border)) {
7180  $bx = $this->x;
7181  $by = $this->y;
7182  $this->x = $ximg;
7183  if ($this->rtl) {
7184  $this->x += $w;
7185  }
7186  $this->y = $y;
7187  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
7188  $this->x = $bx;
7189  $this->y = $by;
7190  }
7191  if ($link) {
7192  $this->Link($ximg, $y, $w, $h, $link, 0);
7193  }
7194  // set pointer to align the next text/objects
7195  switch($align) {
7196  case 'T': {
7197  $this->y = $y;
7198  $this->x = $this->img_rb_x;
7199  break;
7200  }
7201  case 'M': {
7202  $this->y = $y + round($h/2);
7203  $this->x = $this->img_rb_x;
7204  break;
7205  }
7206  case 'B': {
7207  $this->y = $this->img_rb_y;
7208  $this->x = $this->img_rb_x;
7209  break;
7210  }
7211  case 'N': {
7212  $this->SetY($this->img_rb_y);
7213  break;
7214  }
7215  default:{
7216  break;
7217  }
7218  }
7219  $this->endlinex = $this->img_rb_x;
7220  if ($this->inxobj) {
7221  // we are inside an XObject template
7222  $this->xobjects[$this->xobjid]['images'][] = $info['i'];
7223  }
7224  return $info['i'];
7225  }
7226 
7248  protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
7249  // create temp images
7250  if (empty($filehash)) {
7251  $filehash = md5($file);
7252  }
7253  // create temp image file (without alpha channel)
7254  $tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash;
7255  // create temp alpha file
7256  $tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash;
7257  $parsed = false;
7258  $parse_error = '';
7259  // ImageMagick extension
7260  if (($parsed === false) AND extension_loaded('imagick')) {
7261  try {
7262  // ImageMagick library
7263  $img = new Imagick();
7264  $img->readImage($file);
7265  // clone image object
7266  $imga = TCPDF_STATIC::objclone($img);
7267  // extract alpha channel
7268  if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) {
7269  $img->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT);
7270  } else {
7271  $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
7272  $img->negateImage(true);
7273  }
7274  $img->setImageFormat('png');
7275  $img->writeImage($tempfile_alpha);
7276  // remove alpha channel
7277  if (method_exists($imga, 'setImageMatte')) {
7278  $imga->setImageMatte(false);
7279  } else {
7280  $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
7281  }
7282  $imga->setImageFormat('png');
7283  $imga->writeImage($tempfile_plain);
7284  $parsed = true;
7285  } catch (Exception $e) {
7286  // Imagemagick fails, try with GD
7287  $parse_error = 'Imagick library error: '.$e->getMessage();
7288  }
7289  }
7290  // GD extension
7291  if (($parsed === false) AND function_exists('imagecreatefrompng')) {
7292  try {
7293  // generate images
7294  $img = imagecreatefrompng($file);
7295  $imgalpha = imagecreate($wpx, $hpx);
7296  // generate gray scale palette (0 -> 255)
7297  for ($c = 0; $c < 256; ++$c) {
7298  ImageColorAllocate($imgalpha, $c, $c, $c);
7299  }
7300  // extract alpha channel
7301  for ($xpx = 0; $xpx < $wpx; ++$xpx) {
7302  for ($ypx = 0; $ypx < $hpx; ++$ypx) {
7303  $color = imagecolorat($img, $xpx, $ypx);
7304  // get and correct gamma color
7305  $alpha = $this->getGDgamma($img, $color);
7306  imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
7307  }
7308  }
7309  imagepng($imgalpha, $tempfile_alpha);
7310  imagedestroy($imgalpha);
7311  // extract image without alpha channel
7312  $imgplain = imagecreatetruecolor($wpx, $hpx);
7313  imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
7314  imagepng($imgplain, $tempfile_plain);
7315  imagedestroy($imgplain);
7316  $parsed = true;
7317  } catch (Exception $e) {
7318  // GD fails
7319  $parse_error = 'GD library error: '.$e->getMessage();
7320  }
7321  }
7322  if ($parsed === false) {
7323  if (empty($parse_error)) {
7324  $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
7325  } else {
7326  $this->Error($parse_error);
7327  }
7328  }
7329  // embed mask image
7330  $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7331  // embed image, masked with previously embedded mask
7332  $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7333  }
7334 
7342  protected function getGDgamma($img, $c) {
7343  if (!isset($this->gdgammacache['#'.$c])) {
7344  $colors = imagecolorsforindex($img, $c);
7345  // GD alpha is only 7 bit (0 -> 127)
7346  $this->gdgammacache['#'.$c] = (((127 - $colors['alpha']) / 127) * 255);
7347  // correct gamma
7348  $this->gdgammacache['#'.$c] = (pow(($this->gdgammacache['#'.$c] / 255), 2.2) * 255);
7349  // store the latest values on cache to improve performances
7350  if (count($this->gdgammacache) > 8) {
7351  // remove one element from the cache array
7352  array_shift($this->gdgammacache);
7353  }
7354  }
7355  return $this->gdgammacache['#'.$c];
7356  }
7357 
7367  public function Ln($h='', $cell=false) {
7368  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'])) {
7369  // revove vertical space from the top of the column
7370  return;
7371  }
7372  if ($cell) {
7373  if ($this->rtl) {
7374  $cellpadding = $this->cell_padding['R'];
7375  } else {
7376  $cellpadding = $this->cell_padding['L'];
7377  }
7378  } else {
7379  $cellpadding = 0;
7380  }
7381  if ($this->rtl) {
7382  $this->x = $this->w - $this->rMargin - $cellpadding;
7383  } else {
7384  $this->x = $this->lMargin + $cellpadding;
7385  }
7386  if (is_string($h)) {
7387  $h = $this->lasth;
7388  }
7389  $this->y += $h;
7390  $this->newline = true;
7391  }
7392 
7401  public function GetX() {
7402  //Get x position
7403  if ($this->rtl) {
7404  return ($this->w - $this->x);
7405  } else {
7406  return $this->x;
7407  }
7408  }
7409 
7417  public function GetAbsX() {
7418  return $this->x;
7419  }
7420 
7428  public function GetY() {
7429  return $this->y;
7430  }
7431 
7441  public function SetX($x, $rtloff=false) {
7442  $x = floatval($x);
7443  if (!$rtloff AND $this->rtl) {
7444  if ($x >= 0) {
7445  $this->x = $this->w - $x;
7446  } else {
7447  $this->x = abs($x);
7448  }
7449  } else {
7450  if ($x >= 0) {
7451  $this->x = $x;
7452  } else {
7453  $this->x = $this->w + $x;
7454  }
7455  }
7456  if ($this->x < 0) {
7457  $this->x = 0;
7458  }
7459  if ($this->x > $this->w) {
7460  $this->x = $this->w;
7461  }
7462  }
7463 
7474  public function SetY($y, $resetx=true, $rtloff=false) {
7475  $y = floatval($y);
7476  if ($resetx) {
7477  //reset x
7478  if (!$rtloff AND $this->rtl) {
7479  $this->x = $this->w - $this->rMargin;
7480  } else {
7481  $this->x = $this->lMargin;
7482  }
7483  }
7484  if ($y >= 0) {
7485  $this->y = $y;
7486  } else {
7487  $this->y = $this->h + $y;
7488  }
7489  if ($this->y < 0) {
7490  $this->y = 0;
7491  }
7492  if ($this->y > $this->h) {
7493  $this->y = $this->h;
7494  }
7495  }
7496 
7507  public function SetXY($x, $y, $rtloff=false) {
7508  $this->SetY($y, false, $rtloff);
7509  $this->SetX($x, $rtloff);
7510  }
7511 
7519  public function SetAbsX($x) {
7520  $this->x = floatval($x);
7521  }
7522 
7530  public function SetAbsY($y) {
7531  $this->y = floatval($y);
7532  }
7533 
7542  public function SetAbsXY($x, $y) {
7543  $this->SetAbsX($x);
7544  $this->SetAbsY($y);
7545  }
7546 
7558  public function Output($name='doc.pdf', $dest='I') {
7559  //Output PDF to some destination
7560  //Finish document if necessary
7561  if ($this->state < 3) {
7562  $this->Close();
7563  }
7564  //Normalize parameters
7565  if (is_bool($dest)) {
7566  $dest = $dest ? 'D' : 'F';
7567  }
7568  $dest = strtoupper($dest);
7569  if ($dest[0] != 'F') {
7570  $name = preg_replace('/[\s]+/', '_', $name);
7571  $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
7572  }
7573  if ($this->sign) {
7574  // *** apply digital signature to the document ***
7575  // get the document content
7576  $pdfdoc = $this->getBuffer();
7577  // remove last newline
7578  $pdfdoc = substr($pdfdoc, 0, -1);
7579  // remove filler space
7580  $byterange_string_len = strlen(TCPDF_STATIC::$byterange_string);
7581  // define the ByteRange
7582  $byte_range = array();
7583  $byte_range[0] = 0;
7584  $byte_range[1] = strpos($pdfdoc, TCPDF_STATIC::$byterange_string) + $byterange_string_len + 10;
7585  $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
7586  $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
7587  $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
7588  // replace the ByteRange
7589  $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
7590  $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
7591  $pdfdoc = str_replace(TCPDF_STATIC::$byterange_string, $byterange, $pdfdoc);
7592  // write the document to a temporary folder
7593  $tempdoc = TCPDF_STATIC::getObjFilename('doc', $this->file_id);
7594  $f = TCPDF_STATIC::fopenLocal($tempdoc, 'wb');
7595  if (!$f) {
7596  $this->Error('Unable to create temporary file: '.$tempdoc);
7597  }
7598  $pdfdoc_length = strlen($pdfdoc);
7599  fwrite($f, $pdfdoc, $pdfdoc_length);
7600  fclose($f);
7601  // get digital signature via openssl library
7602  $tempsign = TCPDF_STATIC::getObjFilename('sig', $this->file_id);
7603  if (empty($this->signature_data['extracerts'])) {
7604  openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
7605  } else {
7606  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']);
7607  }
7608  // read signature
7609  $signature = file_get_contents($tempsign);
7610  // extract signature
7611  $signature = substr($signature, $pdfdoc_length);
7612  $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
7613  $tmparr = explode("\n\n", $signature);
7614  $signature = $tmparr[1];
7615  // decode signature
7616  $signature = base64_decode(trim($signature));
7617  // add TSA timestamp to signature
7618  $signature = $this->applyTSA($signature);
7619  // convert signature to hex
7620  $signature = current(unpack('H*', $signature));
7621  $signature = str_pad($signature, $this->signature_max_length, '0');
7622  // Add signature to the document
7623  $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
7624  $this->bufferlen = strlen($this->buffer);
7625  }
7626  switch($dest) {
7627  case 'I': {
7628  // Send PDF to the standard output
7629  if (ob_get_contents()) {
7630  $this->Error('Some data has already been output, can\'t send PDF file');
7631  }
7632  if (php_sapi_name() != 'cli') {
7633  // send output to a browser
7634  header('Content-Type: application/pdf');
7635  if (headers_sent()) {
7636  $this->Error('Some data has already been output to browser, can\'t send PDF file');
7637  }
7638  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7639  //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7640  header('Pragma: public');
7641  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7642  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7643  header('Content-Disposition: inline; filename="'.basename($name).'"');
7644  TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7645  } else {
7646  echo $this->getBuffer();
7647  }
7648  break;
7649  }
7650  case 'D': {
7651  // download PDF as file
7652  if (ob_get_contents()) {
7653  $this->Error('Some data has already been output, can\'t send PDF file');
7654  }
7655  header('Content-Description: File Transfer');
7656  if (headers_sent()) {
7657  $this->Error('Some data has already been output to browser, can\'t send PDF file');
7658  }
7659  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7660  //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7661  header('Pragma: public');
7662  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7663  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7664  // force download dialog
7665  if (strpos(php_sapi_name(), 'cgi') === false) {
7666  header('Content-Type: application/force-download');
7667  header('Content-Type: application/octet-stream', false);
7668  header('Content-Type: application/download', false);
7669  header('Content-Type: application/pdf', false);
7670  } else {
7671  header('Content-Type: application/pdf');
7672  }
7673  // use the Content-Disposition header to supply a recommended filename
7674  header('Content-Disposition: attachment; filename="'.basename($name).'"');
7675  header('Content-Transfer-Encoding: binary');
7676  TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7677  break;
7678  }
7679  case 'F':
7680  case 'FI':
7681  case 'FD': {
7682  // save PDF to a local file
7683  $f = TCPDF_STATIC::fopenLocal($name, 'wb');
7684  if (!$f) {
7685  $this->Error('Unable to create output file: '.$name);
7686  }
7687  fwrite($f, $this->getBuffer(), $this->bufferlen);
7688  fclose($f);
7689  if ($dest == 'FI') {
7690  // send headers to browser
7691  header('Content-Type: application/pdf');
7692  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7693  //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7694  header('Pragma: public');
7695  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7696  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7697  header('Content-Disposition: inline; filename="'.basename($name).'"');
7698  TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7699  } elseif ($dest == 'FD') {
7700  // send headers to browser
7701  if (ob_get_contents()) {
7702  $this->Error('Some data has already been output, can\'t send PDF file');
7703  }
7704  header('Content-Description: File Transfer');
7705  if (headers_sent()) {
7706  $this->Error('Some data has already been output to browser, can\'t send PDF file');
7707  }
7708  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7709  header('Pragma: public');
7710  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7711  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7712  // force download dialog
7713  if (strpos(php_sapi_name(), 'cgi') === false) {
7714  header('Content-Type: application/force-download');
7715  header('Content-Type: application/octet-stream', false);
7716  header('Content-Type: application/download', false);
7717  header('Content-Type: application/pdf', false);
7718  } else {
7719  header('Content-Type: application/pdf');
7720  }
7721  // use the Content-Disposition header to supply a recommended filename
7722  header('Content-Disposition: attachment; filename="'.basename($name).'"');
7723  header('Content-Transfer-Encoding: binary');
7724  TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7725  }
7726  break;
7727  }
7728  case 'E': {
7729  // return PDF as base64 mime multi-part email attachment (RFC 2045)
7730  $retval = 'Content-Type: application/pdf;'."\r\n";
7731  $retval .= ' name="'.$name.'"'."\r\n";
7732  $retval .= 'Content-Transfer-Encoding: base64'."\r\n";
7733  $retval .= 'Content-Disposition: attachment;'."\r\n";
7734  $retval .= ' filename="'.$name.'"'."\r\n\r\n";
7735  $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
7736  return $retval;
7737  }
7738  case 'S': {
7739  // returns PDF as a string
7740  return $this->getBuffer();
7741  }
7742  default: {
7743  $this->Error('Incorrect output destination: '.$dest);
7744  }
7745  }
7746  return '';
7747  }
7748 
7756  public function _destroy($destroyall=false, $preserve_objcopy=false) {
7757  if ($destroyall AND !$preserve_objcopy) {
7758  // remove all temporary files
7759  $tmpfiles = glob(K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_*');
7760  if (!empty($tmpfiles)) {
7761  array_map('unlink', $tmpfiles);
7762  }
7763  }
7764  $preserve = array(
7765  'file_id',
7766  'internal_encoding',
7767  'state',
7768  'bufferlen',
7769  'buffer',
7770  'cached_files',
7771  'sign',
7772  'signature_data',
7773  'signature_max_length',
7774  'byterange_string',
7775  'tsa_timestamp',
7776  'tsa_data'
7777  );
7778  foreach (array_keys(get_object_vars($this)) as $val) {
7779  if ($destroyall OR !in_array($val, $preserve)) {
7780  if ((!$preserve_objcopy OR ($val != 'objcopy')) AND ($val != 'file_id') AND isset($this->$val)) {
7781  unset($this->$val);
7782  }
7783  }
7784  }
7785  }
7786 
7791  protected function _dochecks() {
7792  //Check for locale-related bug
7793  if (1.1 == 1) {
7794  $this->Error('Don\'t alter the locale before including class file');
7795  }
7796  //Check for decimal separator
7797  if (sprintf('%.1F', 1.0) != '1.0') {
7798  setlocale(LC_NUMERIC, 'C');
7799  }
7800  }
7801 
7808  protected function getInternalPageNumberAliases($a= '') {
7809  $alias = array();
7810  // build array of Unicode + ASCII variants (the order is important)
7811  $alias = array('u' => array(), 'a' => array());
7812  $u = '{'.$a.'}';
7813  $alias['u'][] = TCPDF_STATIC::_escape($u);
7814  if ($this->isunicode) {
7815  $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($u, $this->isunicode, $this->CurrentFont));
7816  $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($u, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7817  $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($a, $this->isunicode, $this->CurrentFont));
7818  $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($a, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7819  }
7820  $alias['a'][] = TCPDF_STATIC::_escape($a);
7821  return $alias;
7822  }
7823 
7829  protected function getAllInternalPageNumberAliases() {
7831  $pnalias = array();
7832  foreach($basic_alias as $k => $a) {
7833  $pnalias[$k] = $this->getInternalPageNumberAliases($a);
7834  }
7835  return $pnalias;
7836  }
7837 
7847  protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
7848  foreach ($aliases as $type => $alias) {
7849  foreach ($alias as $a) {
7850  // find position of compensation factor
7851  $startnum = (strpos($a, ':') + 1);
7852  $a = substr($a, 0, $startnum);
7853  if (($pos = strpos($page, $a)) !== false) {
7854  // end of alias
7855  $endnum = strpos($page, '}', $pos);
7856  // string to be replaced
7857  $aa = substr($page, $pos, ($endnum - $pos + 1));
7858  // get compensation factor
7859  $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum));
7860  $ratio = preg_replace('/[^0-9\.]/', '', $ratio);
7861  $ratio = floatval($ratio);
7862  if ($type == 'u') {
7863  $chrdiff = floor(($diff + 12) * $ratio);
7864  $shift = str_repeat(' ', $chrdiff);
7865  $shift = TCPDF_FONTS::UTF8ToUTF16BE($shift, false, $this->isunicode, $this->CurrentFont);
7866  } else {
7867  $chrdiff = floor(($diff + 11) * $ratio);
7868  $shift = str_repeat(' ', $chrdiff);
7869  }
7870  $page = str_replace($aa, $shift, $page);
7871  }
7872  }
7873  }
7874  return $page;
7875  }
7876 
7882  protected function setPageBoxTypes($boxes) {
7883  $this->page_boxes = array();
7884  foreach ($boxes as $box) {
7885  if (in_array($box, TCPDF_STATIC::$pageboxes)) {
7886  $this->page_boxes[] = $box;
7887  }
7888  }
7889  }
7890 
7895  protected function _putpages() {
7896  $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
7897  // get internal aliases for page numbers
7898  $pnalias = $this->getAllInternalPageNumberAliases();
7899  $num_pages = $this->numpages;
7900  $ptpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $num_pages - 1));
7901  $ptpu = TCPDF_FONTS::UTF8ToUTF16BE($ptpa, false, $this->isunicode, $this->CurrentFont);
7902  $ptp_num_chars = $this->GetNumChars($ptpa);
7903  $pagegroupnum = 0;
7904  $groupnum = 0;
7905  $ptgu = 1;
7906  $ptga = 1;
7907  $ptg_num_chars = 1;
7908  for ($n = 1; $n <= $num_pages; ++$n) {
7909  // get current page
7910  $temppage = $this->getPageBuffer($n);
7911  $pagelen = strlen($temppage);
7912  // set replacements for total pages number
7913  $pnpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $n - 1));
7914  $pnpu = TCPDF_FONTS::UTF8ToUTF16BE($pnpa, false, $this->isunicode, $this->CurrentFont);
7915  $pnp_num_chars = $this->GetNumChars($pnpa);
7916  $pdiff = 0; // difference used for right shift alignment of page numbers
7917  $gdiff = 0; // difference used for right shift alignment of page group numbers
7918  if (!empty($this->pagegroups)) {
7919  if (isset($this->newpagegroup[$n])) {
7920  $pagegroupnum = 0;
7921  ++$groupnum;
7922  $ptga = TCPDF_STATIC::formatPageNumber($this->pagegroups[$groupnum]);
7923  $ptgu = TCPDF_FONTS::UTF8ToUTF16BE($ptga, false, $this->isunicode, $this->CurrentFont);
7924  $ptg_num_chars = $this->GetNumChars($ptga);
7925  }
7926  ++$pagegroupnum;
7927  $pnga = TCPDF_STATIC::formatPageNumber($pagegroupnum);
7928  $pngu = TCPDF_FONTS::UTF8ToUTF16BE($pnga, false, $this->isunicode, $this->CurrentFont);
7929  $png_num_chars = $this->GetNumChars($pnga);
7930  // replace page numbers
7931  $replace = array();
7932  $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
7933  $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
7934  $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
7935  $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
7936  list($temppage, $gdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $gdiff);
7937  }
7938  // replace page numbers
7939  $replace = array();
7940  $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
7941  $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
7942  $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
7943  $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
7944  list($temppage, $pdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $pdiff);
7945  // replace right shift alias
7946  $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
7947  // replace EPS marker
7948  $temppage = str_replace($this->epsmarker, '', $temppage);
7949  //Page
7950  $this->page_obj_id[$n] = $this->_newobj();
7951  $out = '<<';
7952  $out .= ' /Type /Page';
7953  $out .= ' /Parent 1 0 R';
7954  if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
7955  $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp);
7956  }
7957  $out .= ' /Resources 2 0 R';
7958  foreach ($this->page_boxes as $box) {
7959  $out .= ' /'.$box;
7960  $out .= sprintf(' [%F %F %F %F]', $this->pagedim[$n][$box]['llx'], $this->pagedim[$n][$box]['lly'], $this->pagedim[$n][$box]['urx'], $this->pagedim[$n][$box]['ury']);
7961  }
7962  if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
7963  $out .= ' /BoxColorInfo <<';
7964  foreach ($this->page_boxes as $box) {
7965  if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
7966  $out .= ' /'.$box.' <<';
7967  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
7968  $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
7969  $out .= ' /C [';
7970  $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
7971  $out .= ' ]';
7972  }
7973  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
7974  $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
7975  }
7976  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
7977  $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
7978  }
7979  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
7980  $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
7981  $out .= ' /D [';
7982  foreach ($dashes as $dash) {
7983  $out .= sprintf(' %F', ($dash * $this->k));
7984  }
7985  $out .= ' ]';
7986  }
7987  $out .= ' >>';
7988  }
7989  }
7990  $out .= ' >>';
7991  }
7992  $out .= ' /Contents '.($this->n + 1).' 0 R';
7993  $out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
7994  if (!$this->pdfa_mode) {
7995  $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
7996  }
7997  if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
7998  // page transitions
7999  if (isset($this->pagedim[$n]['trans']['Dur'])) {
8000  $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
8001  }
8002  $out .= ' /Trans <<';
8003  $out .= ' /Type /Trans';
8004  if (isset($this->pagedim[$n]['trans']['S'])) {
8005  $out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
8006  }
8007  if (isset($this->pagedim[$n]['trans']['D'])) {
8008  $out .= ' /D '.$this->pagedim[$n]['trans']['D'];
8009  }
8010  if (isset($this->pagedim[$n]['trans']['Dm'])) {
8011  $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
8012  }
8013  if (isset($this->pagedim[$n]['trans']['M'])) {
8014  $out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
8015  }
8016  if (isset($this->pagedim[$n]['trans']['Di'])) {
8017  $out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
8018  }
8019  if (isset($this->pagedim[$n]['trans']['SS'])) {
8020  $out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
8021  }
8022  if (isset($this->pagedim[$n]['trans']['B'])) {
8023  $out .= ' /B '.$this->pagedim[$n]['trans']['B'];
8024  }
8025  $out .= ' >>';
8026  }
8027  $out .= $this->_getannotsrefs($n);
8028  $out .= ' /PZ '.$this->pagedim[$n]['PZ'];
8029  $out .= ' >>';
8030  $out .= "\n".'endobj';
8031  $this->_out($out);
8032  //Page content
8033  $p = ($this->compress) ? gzcompress($temppage) : $temppage;
8034  $this->_newobj();
8035  $p = $this->_getrawstream($p);
8036  $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
8037  }
8038  //Pages root
8039  $out = $this->_getobj(1)."\n";
8040  $out .= '<< /Type /Pages /Kids [';
8041  foreach($this->page_obj_id as $page_obj) {
8042  $out .= ' '.$page_obj.' 0 R';
8043  }
8044  $out .= ' ] /Count '.$num_pages.' >>';
8045  $out .= "\n".'endobj';
8046  $this->_out($out);
8047  }
8048 
8057  protected function _getannotsrefs($n) {
8058  if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
8059  return '';
8060  }
8061  $out = ' /Annots [';
8062  if (isset($this->PageAnnots[$n])) {
8063  foreach ($this->PageAnnots[$n] as $key => $val) {
8064  if (!in_array($val['n'], $this->radio_groups)) {
8065  $out .= ' '.$val['n'].' 0 R';
8066  }
8067  }
8068  // add radiobutton groups
8069  if (isset($this->radiobutton_groups[$n])) {
8070  foreach ($this->radiobutton_groups[$n] as $key => $data) {
8071  if (isset($data['n'])) {
8072  $out .= ' '.$data['n'].' 0 R';
8073  }
8074  }
8075  }
8076  }
8077  if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
8078  // set reference for signature object
8079  $out .= ' '.$this->sig_obj_id.' 0 R';
8080  }
8081  if (!empty($this->empty_signature_appearance)) {
8082  foreach ($this->empty_signature_appearance as $esa) {
8083  if ($esa['page'] == $n) {
8084  // set reference for empty signature objects
8085  $out .= ' '.$esa['objid'].' 0 R';
8086  }
8087  }
8088  }
8089  $out .= ' ]';
8090  return $out;
8091  }
8092 
8101  protected function _putannotsobjs() {
8102  // reset object counter
8103  for ($n=1; $n <= $this->numpages; ++$n) {
8104  if (isset($this->PageAnnots[$n])) {
8105  // set page annotations
8106  foreach ($this->PageAnnots[$n] as $key => $pl) {
8107  $annot_obj_id = $this->PageAnnots[$n][$key]['n'];
8108  // create annotation object for grouping radiobuttons
8109  if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
8110  $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n'];
8111  $annots = '<<';
8112  $annots .= ' /Type /Annot';
8113  $annots .= ' /Subtype /Widget';
8114  $annots .= ' /Rect [0 0 0 0]';
8115  if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) {
8116  // read only
8117  $annots .= ' /F 68';
8118  $annots .= ' /Ff 49153';
8119  } else {
8120  $annots .= ' /F 4'; // default print for PDF/A
8121  $annots .= ' /Ff 49152';
8122  }
8123  $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
8124  if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8125  $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id);
8126  }
8127  $annots .= ' /FT /Btn';
8128  $annots .= ' /Kids [';
8129  $defval = '';
8130  foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) {
8131  if (isset($data['kid'])) {
8132  $annots .= ' '.$data['kid'].' 0 R';
8133  if ($data['def'] !== 'Off') {
8134  $defval = $data['def'];
8135  }
8136  }
8137  }
8138  $annots .= ' ]';
8139  if (!empty($defval)) {
8140  $annots .= ' /V /'.$defval;
8141  }
8142  $annots .= ' >>';
8143  $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
8144  $this->form_obj_id[] = $radio_button_obj_id;
8145  // store object id to be used on Parent entry of Kids
8146  $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id;
8147  }
8148  $formfield = false;
8149  $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
8150  $a = $pl['x'] * $this->k;
8151  $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k);
8152  $c = $pl['w'] * $this->k;
8153  $d = $pl['h'] * $this->k;
8154  $rect = sprintf('%F %F %F %F', $a, $b, $a+$c, $b+$d);
8155  // create new annotation object
8156  $annots = '<</Type /Annot';
8157  $annots .= ' /Subtype /'.$pl['opt']['subtype'];
8158  $annots .= ' /Rect ['.$rect.']';
8159  $ft = array('Btn', 'Tx', 'Ch', 'Sig');
8160  if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
8161  $annots .= ' /FT /'.$pl['opt']['ft'];
8162  $formfield = true;
8163  }
8164  $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
8165  $annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
8166  $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
8167  $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp);
8168  if (isset($pl['opt']['f'])) {
8169  $fval = 0;
8170  if (is_array($pl['opt']['f'])) {
8171  foreach ($pl['opt']['f'] as $f) {
8172  switch (strtolower($f)) {
8173  case 'invisible': {
8174  $fval += 1 << 0;
8175  break;
8176  }
8177  case 'hidden': {
8178  $fval += 1 << 1;
8179  break;
8180  }
8181  case 'print': {
8182  $fval += 1 << 2;
8183  break;
8184  }
8185  case 'nozoom': {
8186  $fval += 1 << 3;
8187  break;
8188  }
8189  case 'norotate': {
8190  $fval += 1 << 4;
8191  break;
8192  }
8193  case 'noview': {
8194  $fval += 1 << 5;
8195  break;
8196  }
8197  case 'readonly': {
8198  $fval += 1 << 6;
8199  break;
8200  }
8201  case 'locked': {
8202  $fval += 1 << 8;
8203  break;
8204  }
8205  case 'togglenoview': {
8206  $fval += 1 << 9;
8207  break;
8208  }
8209  case 'lockedcontents': {
8210  $fval += 1 << 10;
8211  break;
8212  }
8213  default: {
8214  break;
8215  }
8216  }
8217  }
8218  } else {
8219  $fval = intval($pl['opt']['f']);
8220  }
8221  } else {
8222  $fval = 4;
8223  }
8224  if ($this->pdfa_mode) {
8225  // force print flag for PDF/A mode
8226  $fval |= 4;
8227  }
8228  $annots .= ' /F '.intval($fval);
8229  if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
8230  $annots .= ' /AS /'.$pl['opt']['as'];
8231  }
8232  if (isset($pl['opt']['ap'])) {
8233  // appearance stream
8234  $annots .= ' /AP <<';
8235  if (is_array($pl['opt']['ap'])) {
8236  foreach ($pl['opt']['ap'] as $apmode => $apdef) {
8237  // $apmode can be: n = normal; r = rollover; d = down;
8238  $annots .= ' /'.strtoupper($apmode);
8239  if (is_array($apdef)) {
8240  $annots .= ' <<';
8241  foreach ($apdef as $apstate => $stream) {
8242  // reference to XObject that define the appearance for this mode-state
8243  $apsobjid = $this->_putAPXObject($c, $d, $stream);
8244  $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
8245  }
8246  $annots .= ' >>';
8247  } else {
8248  // reference to XObject that define the appearance for this mode
8249  $apsobjid = $this->_putAPXObject($c, $d, $apdef);
8250  $annots .= ' '.$apsobjid.' 0 R';
8251  }
8252  }
8253  } else {
8254  $annots .= $pl['opt']['ap'];
8255  }
8256  $annots .= ' >>';
8257  }
8258  if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
8259  $annots .= ' /BS <<';
8260  $annots .= ' /Type /Border';
8261  if (isset($pl['opt']['bs']['w'])) {
8262  $annots .= ' /W '.intval($pl['opt']['bs']['w']);
8263  }
8264  $bstyles = array('S', 'D', 'B', 'I', 'U');
8265  if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
8266  $annots .= ' /S /'.$pl['opt']['bs']['s'];
8267  }
8268  if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
8269  $annots .= ' /D [';
8270  foreach ($pl['opt']['bs']['d'] as $cord) {
8271  $annots .= ' '.intval($cord);
8272  }
8273  $annots .= ']';
8274  }
8275  $annots .= ' >>';
8276  } else {
8277  $annots .= ' /Border [';
8278  if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
8279  $annots .= intval($pl['opt']['border'][0]).' ';
8280  $annots .= intval($pl['opt']['border'][1]).' ';
8281  $annots .= intval($pl['opt']['border'][2]);
8282  if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
8283  $annots .= ' [';
8284  foreach ($pl['opt']['border'][3] as $dash) {
8285  $annots .= intval($dash).' ';
8286  }
8287  $annots .= ']';
8288  }
8289  } else {
8290  $annots .= '0 0 0';
8291  }
8292  $annots .= ']';
8293  }
8294  if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
8295  $annots .= ' /BE <<';
8296  $bstyles = array('S', 'C');
8297  if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) {
8298  $annots .= ' /S /'.$pl['opt']['bs']['s'];
8299  } else {
8300  $annots .= ' /S /S';
8301  }
8302  if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
8303  $annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']);
8304  }
8305  $annots .= '>>';
8306  }
8307  if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
8308  $annots .= ' /C '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['c']);
8309  }
8310  //$annots .= ' /StructParent ';
8311  //$annots .= ' /OC ';
8312  $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
8313  if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
8314  // this is a markup type
8315  if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8316  $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
8317  }
8318  //$annots .= ' /Popup ';
8319  if (isset($pl['opt']['ca'])) {
8320  $annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca']));
8321  }
8322  if (isset($pl['opt']['rc'])) {
8323  $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8324  }
8325  $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp);
8326  //$annots .= ' /IRT ';
8327  if (isset($pl['opt']['subj'])) {
8328  $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
8329  }
8330  //$annots .= ' /RT ';
8331  //$annots .= ' /IT ';
8332  //$annots .= ' /ExData ';
8333  }
8334  $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
8335  // Annotation types
8336  switch (strtolower($pl['opt']['subtype'])) {
8337  case 'text': {
8338  if (isset($pl['opt']['open'])) {
8339  $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
8340  }
8341  $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
8342  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8343  $annots .= ' /Name /'.$pl['opt']['name'];
8344  } else {
8345  $annots .= ' /Name /Note';
8346  }
8347  $statemodels = array('Marked', 'Review');
8348  if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
8349  $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8350  } else {
8351  $pl['opt']['statemodel'] = 'Marked';
8352  $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8353  }
8354  if ($pl['opt']['statemodel'] == 'Marked') {
8355  $states = array('Accepted', 'Unmarked');
8356  } else {
8357  $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
8358  }
8359  if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
8360  $annots .= ' /State /'.$pl['opt']['state'];
8361  } else {
8362  if ($pl['opt']['statemodel'] == 'Marked') {
8363  $annots .= ' /State /Unmarked';
8364  } else {
8365  $annots .= ' /State /None';
8366  }
8367  }
8368  break;
8369  }
8370  case 'link': {
8371  if (is_string($pl['txt'])) {
8372  if ($pl['txt'][0] == '#') {
8373  // internal destination
8374  $annots .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($pl['txt'], 1));
8375  } elseif ($pl['txt'][0] == '%') {
8376  // embedded PDF file
8377  $filename = basename(substr($pl['txt'], 1));
8378  $annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
8379  } elseif ($pl['txt'][0] == '*') {
8380  // embedded generic file
8381  $filename = basename(substr($pl['txt'], 1));
8382  $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});';
8383  $annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>';
8384  } else {
8385  $parsedUrl = parse_url($pl['txt']);
8386  if (empty($parsedUrl['scheme']) AND (strtolower(substr($parsedUrl['path'], -4)) == '.pdf')) {
8387  // relative link to a PDF file
8388  $dest = '[0 /Fit]'; // default page 0
8389  if (!empty($parsedUrl['fragment'])) {
8390  // check for named destination
8391  $tmp = explode('=', $parsedUrl['fragment']);
8392  $dest = '('.((count($tmp) == 2) ? $tmp[1] : $tmp[0]).')';
8393  }
8394  $annots .= ' /A <</S /GoToR /D '.$dest.' /F '.$this->_datastring($this->unhtmlentities($parsedUrl['path']), $annot_obj_id).' /NewWindow true>>';
8395  } else {
8396  // external URI link
8397  $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
8398  }
8399  }
8400  } elseif (isset($this->links[$pl['txt']])) {
8401  // internal link ID
8402  $l = $this->links[$pl['txt']];
8403  if (isset($this->page_obj_id[($l['p'])])) {
8404  $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)));
8405  }
8406  }
8407  $hmodes = array('N', 'I', 'O', 'P');
8408  if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
8409  $annots .= ' /H /'.$pl['opt']['h'];
8410  } else {
8411  $annots .= ' /H /I';
8412  }
8413  //$annots .= ' /PA ';
8414  //$annots .= ' /Quadpoints ';
8415  break;
8416  }
8417  case 'freetext': {
8418  if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8419  $annots .= ' /DA ('.$pl['opt']['da'].')';
8420  }
8421  if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8422  $annots .= ' /Q '.intval($pl['opt']['q']);
8423  }
8424  if (isset($pl['opt']['rc'])) {
8425  $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8426  }
8427  if (isset($pl['opt']['ds'])) {
8428  $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
8429  }
8430  if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
8431  $annots .= ' /CL [';
8432  foreach ($pl['opt']['cl'] as $cl) {
8433  $annots .= sprintf('%F ', $cl * $this->k);
8434  }
8435  $annots .= ']';
8436  }
8437  $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
8438  if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
8439  $annots .= ' /IT /'.$pl['opt']['it'];
8440  }
8441  if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
8442  $l = $pl['opt']['rd'][0] * $this->k;
8443  $r = $pl['opt']['rd'][1] * $this->k;
8444  $t = $pl['opt']['rd'][2] * $this->k;
8445  $b = $pl['opt']['rd'][3] * $this->k;
8446  $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']';
8447  }
8448  if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
8449  $annots .= ' /LE /'.$pl['opt']['le'];
8450  }
8451  break;
8452  }
8453  case 'line': {
8454  break;
8455  }
8456  case 'square': {
8457  break;
8458  }
8459  case 'circle': {
8460  break;
8461  }
8462  case 'polygon': {
8463  break;
8464  }
8465  case 'polyline': {
8466  break;
8467  }
8468  case 'highlight': {
8469  break;
8470  }
8471  case 'underline': {
8472  break;
8473  }
8474  case 'squiggly': {
8475  break;
8476  }
8477  case 'strikeout': {
8478  break;
8479  }
8480  case 'stamp': {
8481  break;
8482  }
8483  case 'caret': {
8484  break;
8485  }
8486  case 'ink': {
8487  break;
8488  }
8489  case 'popup': {
8490  break;
8491  }
8492  case 'fileattachment': {
8493  if ($this->pdfa_mode) {
8494  // embedded files are not allowed in PDF/A mode
8495  break;
8496  }
8497  if (!isset($pl['opt']['fs'])) {
8498  break;
8499  }
8500  $filename = basename($pl['opt']['fs']);
8501  if (isset($this->embeddedfiles[$filename]['f'])) {
8502  $annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R';
8503  $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
8504  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8505  $annots .= ' /Name /'.$pl['opt']['name'];
8506  } else {
8507  $annots .= ' /Name /PushPin';
8508  }
8509  // index (zero-based) of the annotation in the Annots array of this page
8510  $this->embeddedfiles[$filename]['a'] = $key;
8511  }
8512  break;
8513  }
8514  case 'sound': {
8515  if (!isset($pl['opt']['fs'])) {
8516  break;
8517  }
8518  $filename = basename($pl['opt']['fs']);
8519  if (isset($this->embeddedfiles[$filename]['f'])) {
8520  // ... TO BE COMPLETED ...
8521  // /R /C /B /E /CO /CP
8522  $annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R';
8523  $iconsapp = array('Speaker', 'Mic');
8524  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8525  $annots .= ' /Name /'.$pl['opt']['name'];
8526  } else {
8527  $annots .= ' /Name /Speaker';
8528  }
8529  }
8530  break;
8531  }
8532  case 'movie': {
8533  break;
8534  }
8535  case 'widget': {
8536  $hmode = array('N', 'I', 'O', 'P', 'T');
8537  if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
8538  $annots .= ' /H /'.$pl['opt']['h'];
8539  }
8540  if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
8541  $annots .= ' /MK <<';
8542  if (isset($pl['opt']['mk']['r'])) {
8543  $annots .= ' /R '.$pl['opt']['mk']['r'];
8544  }
8545  if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
8546  $annots .= ' /BC '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bc']);
8547  }
8548  if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
8549  $annots .= ' /BG '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bg']);
8550  }
8551  if (isset($pl['opt']['mk']['ca'])) {
8552  $annots .= ' /CA '.$pl['opt']['mk']['ca'];
8553  }
8554  if (isset($pl['opt']['mk']['rc'])) {
8555  $annots .= ' /RC '.$pl['opt']['mk']['rc'];
8556  }
8557  if (isset($pl['opt']['mk']['ac'])) {
8558  $annots .= ' /AC '.$pl['opt']['mk']['ac'];
8559  }
8560  if (isset($pl['opt']['mk']['i'])) {
8561  $info = $this->getImageBuffer($pl['opt']['mk']['i']);
8562  if ($info !== false) {
8563  $annots .= ' /I '.$info['n'].' 0 R';
8564  }
8565  }
8566  if (isset($pl['opt']['mk']['ri'])) {
8567  $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
8568  if ($info !== false) {
8569  $annots .= ' /RI '.$info['n'].' 0 R';
8570  }
8571  }
8572  if (isset($pl['opt']['mk']['ix'])) {
8573  $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
8574  if ($info !== false) {
8575  $annots .= ' /IX '.$info['n'].' 0 R';
8576  }
8577  }
8578  if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
8579  $annots .= ' /IF <<';
8580  $if_sw = array('A', 'B', 'S', 'N');
8581  if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
8582  $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
8583  }
8584  $if_s = array('A', 'P');
8585  if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
8586  $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
8587  }
8588  if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
8589  $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
8590  }
8591  if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
8592  $annots .= ' /FB true';
8593  }
8594  $annots .= '>>';
8595  }
8596  if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
8597  $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
8598  }
8599  $annots .= '>>';
8600  } // end MK
8601  // --- Entries for field dictionaries ---
8602  if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
8603  // set parent
8604  $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
8605  }
8606  if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8607  $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
8608  }
8609  if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8610  $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
8611  }
8612  if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
8613  $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
8614  }
8615  if (isset($pl['opt']['ff'])) {
8616  if (is_array($pl['opt']['ff'])) {
8617  // array of bit settings
8618  $flag = 0;
8619  foreach($pl['opt']['ff'] as $val) {
8620  $flag += 1 << ($val - 1);
8621  }
8622  } else {
8623  $flag = intval($pl['opt']['ff']);
8624  }
8625  $annots .= ' /Ff '.$flag;
8626  }
8627  if (isset($pl['opt']['maxlen'])) {
8628  $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
8629  }
8630  if (isset($pl['opt']['v'])) {
8631  $annots .= ' /V';
8632  if (is_array($pl['opt']['v'])) {
8633  foreach ($pl['opt']['v'] AS $optval) {
8634  if (is_float($optval)) {
8635  $optval = sprintf('%F', $optval);
8636  }
8637  $annots .= ' '.$optval;
8638  }
8639  } else {
8640  $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
8641  }
8642  }
8643  if (isset($pl['opt']['dv'])) {
8644  $annots .= ' /DV';
8645  if (is_array($pl['opt']['dv'])) {
8646  foreach ($pl['opt']['dv'] AS $optval) {
8647  if (is_float($optval)) {
8648  $optval = sprintf('%F', $optval);
8649  }
8650  $annots .= ' '.$optval;
8651  }
8652  } else {
8653  $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
8654  }
8655  }
8656  if (isset($pl['opt']['rv'])) {
8657  $annots .= ' /RV';
8658  if (is_array($pl['opt']['rv'])) {
8659  foreach ($pl['opt']['rv'] AS $optval) {
8660  if (is_float($optval)) {
8661  $optval = sprintf('%F', $optval);
8662  }
8663  $annots .= ' '.$optval;
8664  }
8665  } else {
8666  $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
8667  }
8668  }
8669  if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
8670  $annots .= ' /A << '.$pl['opt']['a'].' >>';
8671  }
8672  if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
8673  $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
8674  }
8675  if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8676  $annots .= ' /DA ('.$pl['opt']['da'].')';
8677  }
8678  if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8679  $annots .= ' /Q '.intval($pl['opt']['q']);
8680  }
8681  if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
8682  $annots .= ' /Opt [';
8683  foreach($pl['opt']['opt'] AS $copt) {
8684  if (is_array($copt)) {
8685  $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
8686  } else {
8687  $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
8688  }
8689  }
8690  $annots .= ']';
8691  }
8692  if (isset($pl['opt']['ti'])) {
8693  $annots .= ' /TI '.intval($pl['opt']['ti']);
8694  }
8695  if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
8696  $annots .= ' /I [';
8697  foreach($pl['opt']['i'] AS $copt) {
8698  $annots .= intval($copt).' ';
8699  }
8700  $annots .= ']';
8701  }
8702  break;
8703  }
8704  case 'screen': {
8705  break;
8706  }
8707  case 'printermark': {
8708  break;
8709  }
8710  case 'trapnet': {
8711  break;
8712  }
8713  case 'watermark': {
8714  break;
8715  }
8716  case '3d': {
8717  break;
8718  }
8719  default: {
8720  break;
8721  }
8722  }
8723  $annots .= '>>';
8724  // create new annotation object
8725  $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
8726  if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
8727  // store reference of form object
8728  $this->form_obj_id[] = $annot_obj_id;
8729  }
8730  }
8731  }
8732  } // end for each page
8733  }
8734 
8744  protected function _putAPXObject($w=0, $h=0, $stream='') {
8745  $stream = trim($stream);
8746  $out = $this->_getobj()."\n";
8747  $this->xobjects['AX'.$this->n] = array('n' => $this->n);
8748  $out .= '<<';
8749  $out .= ' /Type /XObject';
8750  $out .= ' /Subtype /Form';
8751  $out .= ' /FormType 1';
8752  if ($this->compress) {
8753  $stream = gzcompress($stream);
8754  $out .= ' /Filter /FlateDecode';
8755  }
8756  $rect = sprintf('%F %F', $w, $h);
8757  $out .= ' /BBox [0 0 '.$rect.']';
8758  $out .= ' /Matrix [1 0 0 1 0 0]';
8759  $out .= ' /Resources 2 0 R';
8760  $stream = $this->_getrawstream($stream);
8761  $out .= ' /Length '.strlen($stream);
8762  $out .= ' >>';
8763  $out .= ' stream'."\n".$stream."\n".'endstream';
8764  $out .= "\n".'endobj';
8765  $this->_out($out);
8766  return $this->n;
8767  }
8768 
8774  protected function _putfonts() {
8775  $nf = $this->n;
8776  foreach ($this->diffs as $diff) {
8777  //Encodings
8778  $this->_newobj();
8779  $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
8780  }
8781  $mqr = TCPDF_STATIC::get_mqr();
8782  TCPDF_STATIC::set_mqr(false);
8783  foreach ($this->FontFiles as $file => $info) {
8784  // search and get font file to embedd
8785  $fontfile = TCPDF_FONTS::getFontFullPath($file, $info['fontdir']);
8786  if (!TCPDF_STATIC::empty_string($fontfile)) {
8787  $font = file_get_contents($fontfile);
8788  $compressed = (substr($file, -2) == '.z');
8789  if ((!$compressed) AND (isset($info['length2']))) {
8790  $header = (ord($font[0]) == 128);
8791  if ($header) {
8792  // strip first binary header
8793  $font = substr($font, 6);
8794  }
8795  if ($header AND (ord($font[$info['length1']]) == 128)) {
8796  // strip second binary header
8797  $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
8798  }
8799  } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
8800  if ($compressed) {
8801  // uncompress font
8802  $font = gzuncompress($font);
8803  }
8804  // merge subset characters
8805  $subsetchars = array(); // used chars
8806  foreach ($info['fontkeys'] as $fontkey) {
8807  $fontinfo = $this->getFontBuffer($fontkey);
8808  $subsetchars += $fontinfo['subsetchars'];
8809  }
8810  // rebuild a font subset
8811  $font = TCPDF_FONTS::_getTrueTypeFontSubset($font, $subsetchars);
8812  // calculate new font length
8813  $info['length1'] = strlen($font);
8814  if ($compressed) {
8815  // recompress font
8816  $font = gzcompress($font);
8817  }
8818  }
8819  $this->_newobj();
8820  $this->FontFiles[$file]['n'] = $this->n;
8821  $stream = $this->_getrawstream($font);
8822  $out = '<< /Length '.strlen($stream);
8823  if ($compressed) {
8824  $out .= ' /Filter /FlateDecode';
8825  }
8826  $out .= ' /Length1 '.$info['length1'];
8827  if (isset($info['length2'])) {
8828  $out .= ' /Length2 '.$info['length2'].' /Length3 0';
8829  }
8830  $out .= ' >>';
8831  $out .= ' stream'."\n".$stream."\n".'endstream';
8832  $out .= "\n".'endobj';
8833  $this->_out($out);
8834  }
8835  }
8836  TCPDF_STATIC::set_mqr($mqr);
8837  foreach ($this->fontkeys as $k) {
8838  //Font objects
8839  $font = $this->getFontBuffer($k);
8840  $type = $font['type'];
8841  $name = $font['name'];
8842  if ($type == 'core') {
8843  // standard core font
8844  $out = $this->_getobj($this->font_obj_ids[$k])."\n";
8845  $out .= '<</Type /Font';
8846  $out .= ' /Subtype /Type1';
8847  $out .= ' /BaseFont /'.$name;
8848  $out .= ' /Name /F'.$font['i'];
8849  if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
8850  $out .= ' /Encoding /WinAnsiEncoding';
8851  }
8852  if ($k == 'helvetica') {
8853  // add default font for annotations
8854  $this->annotation_fonts[$k] = $font['i'];
8855  }
8856  $out .= ' >>';
8857  $out .= "\n".'endobj';
8858  $this->_out($out);
8859  } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
8860  // additional Type1 or TrueType font
8861  $out = $this->_getobj($this->font_obj_ids[$k])."\n";
8862  $out .= '<</Type /Font';
8863  $out .= ' /Subtype /'.$type;
8864  $out .= ' /BaseFont /'.$name;
8865  $out .= ' /Name /F'.$font['i'];
8866  $out .= ' /FirstChar 32 /LastChar 255';
8867  $out .= ' /Widths '.($this->n + 1).' 0 R';
8868  $out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
8869  if ($font['enc']) {
8870  if (isset($font['diff'])) {
8871  $out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
8872  } else {
8873  $out .= ' /Encoding /WinAnsiEncoding';
8874  }
8875  }
8876  $out .= ' >>';
8877  $out .= "\n".'endobj';
8878  $this->_out($out);
8879  // Widths
8880  $this->_newobj();
8881  $s = '[';
8882  for ($i = 32; $i < 256; ++$i) {
8883  if (isset($font['cw'][$i])) {
8884  $s .= $font['cw'][$i].' ';
8885  } else {
8886  $s .= $font['dw'].' ';
8887  }
8888  }
8889  $s .= ']';
8890  $s .= "\n".'endobj';
8891  $this->_out($s);
8892  //Descriptor
8893  $this->_newobj();
8894  $s = '<</Type /FontDescriptor /FontName /'.$name;
8895  foreach ($font['desc'] as $fdk => $fdv) {
8896  if (is_float($fdv)) {
8897  $fdv = sprintf('%F', $fdv);
8898  }
8899  $s .= ' /'.$fdk.' '.$fdv.'';
8900  }
8901  if (!TCPDF_STATIC::empty_string($font['file'])) {
8902  $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
8903  }
8904  $s .= '>>';
8905  $s .= "\n".'endobj';
8906  $this->_out($s);
8907  } else {
8908  // additional types
8909  $mtd = '_put'.strtolower($type);
8910  if (!method_exists($this, $mtd)) {
8911  $this->Error('Unsupported font type: '.$type);
8912  }
8913  $this->$mtd($font);
8914  }
8915  }
8916  }
8917 
8926  protected function _puttruetypeunicode($font) {
8927  $fontname = '';
8928  if ($font['subset']) {
8929  // change name for font subsetting
8930  $subtag = sprintf('%06u', $font['i']);
8931  $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
8932  $fontname .= $subtag.'+';
8933  }
8934  $fontname .= $font['name'];
8935  // Type0 Font
8936  // A composite font composed of other fonts, organized hierarchically
8937  $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
8938  $out .= '<< /Type /Font';
8939  $out .= ' /Subtype /Type0';
8940  $out .= ' /BaseFont /'.$fontname;
8941  $out .= ' /Name /F'.$font['i'];
8942  $out .= ' /Encoding /'.$font['enc'];
8943  $out .= ' /ToUnicode '.($this->n + 1).' 0 R';
8944  $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
8945  $out .= ' >>';
8946  $out .= "\n".'endobj';
8947  $this->_out($out);
8948  // ToUnicode map for Identity-H
8950  // ToUnicode Object
8951  $this->_newobj();
8952  $stream = ($this->compress) ? gzcompress($stream) : $stream;
8953  $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
8954  $stream = $this->_getrawstream($stream);
8955  $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
8956  // CIDFontType2
8957  // A CIDFont whose glyph descriptions are based on TrueType font technology
8958  $oid = $this->_newobj();
8959  $out = '<< /Type /Font';
8960  $out .= ' /Subtype /CIDFontType2';
8961  $out .= ' /BaseFont /'.$fontname;
8962  // A dictionary containing entries that define the character collection of the CIDFont.
8963  $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
8964  $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
8965  $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
8966  $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
8967  $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
8968  $out .= ' /DW '.$font['dw']; // default width
8969  $out .= "\n".TCPDF_FONTS::_putfontwidths($font, 0);
8970  if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
8971  $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
8972  }
8973  $out .= ' >>';
8974  $out .= "\n".'endobj';
8975  $this->_out($out);
8976  // Font descriptor
8977  // A font descriptor describing the CIDFont default metrics other than its glyph widths
8978  $this->_newobj();
8979  $out = '<< /Type /FontDescriptor';
8980  $out .= ' /FontName /'.$fontname;
8981  foreach ($font['desc'] as $key => $value) {
8982  if (is_float($value)) {
8983  $value = sprintf('%F', $value);
8984  }
8985  $out .= ' /'.$key.' '.$value;
8986  }
8987  $fontdir = false;
8988  if (!TCPDF_STATIC::empty_string($font['file'])) {
8989  // A stream containing a TrueType font
8990  $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
8991  $fontdir = $this->FontFiles[$font['file']]['fontdir'];
8992  }
8993  $out .= ' >>';
8994  $out .= "\n".'endobj';
8995  $this->_out($out);
8996  if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
8997  $this->_newobj();
8998  // Embed CIDToGIDMap
8999  // A specification of the mapping from CIDs to glyph indices
9000  // search and get CTG font file to embedd
9001  $ctgfile = strtolower($font['ctg']);
9002  // search and get ctg font file to embedd
9003  $fontfile = TCPDF_FONTS::getFontFullPath($ctgfile, $fontdir);
9004  if (TCPDF_STATIC::empty_string($fontfile)) {
9005  $this->Error('Font file not found: '.$ctgfile);
9006  }
9007  $stream = $this->_getrawstream(file_get_contents($fontfile));
9008  $out = '<< /Length '.strlen($stream).'';
9009  if (substr($fontfile, -2) == '.z') { // check file extension
9010  // Decompresses data encoded using the public-domain
9011  // zlib/deflate compression method, reproducing the
9012  // original text or binary data
9013  $out .= ' /Filter /FlateDecode';
9014  }
9015  $out .= ' >>';
9016  $out .= ' stream'."\n".$stream."\n".'endstream';
9017  $out .= "\n".'endobj';
9018  $this->_out($out);
9019  }
9020  }
9021 
9030  protected function _putcidfont0($font) {
9031  $cidoffset = 0;
9032  if (!isset($font['cw'][1])) {
9033  $cidoffset = 31;
9034  }
9035  if (isset($font['cidinfo']['uni2cid'])) {
9036  // convert unicode to cid.
9037  $uni2cid = $font['cidinfo']['uni2cid'];
9038  $cw = array();
9039  foreach ($font['cw'] as $uni => $width) {
9040  if (isset($uni2cid[$uni])) {
9041  $cw[($uni2cid[$uni] + $cidoffset)] = $width;
9042  } elseif ($uni < 256) {
9043  $cw[$uni] = $width;
9044  } // else unknown character
9045  }
9046  $font = array_merge($font, array('cw' => $cw));
9047  }
9048  $name = $font['name'];
9049  $enc = $font['enc'];
9050  if ($enc) {
9051  $longname = $name.'-'.$enc;
9052  } else {
9053  $longname = $name;
9054  }
9055  $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
9056  $out .= '<</Type /Font';
9057  $out .= ' /Subtype /Type0';
9058  $out .= ' /BaseFont /'.$longname;
9059  $out .= ' /Name /F'.$font['i'];
9060  if ($enc) {
9061  $out .= ' /Encoding /'.$enc;
9062  }
9063  $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
9064  $out .= ' >>';
9065  $out .= "\n".'endobj';
9066  $this->_out($out);
9067  $oid = $this->_newobj();
9068  $out = '<</Type /Font';
9069  $out .= ' /Subtype /CIDFontType0';
9070  $out .= ' /BaseFont /'.$name;
9071  $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9072  $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9073  $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9074  $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
9075  $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
9076  $out .= ' /DW '.$font['dw'];
9077  $out .= "\n".TCPDF_FONTS::_putfontwidths($font, $cidoffset);
9078  $out .= ' >>';
9079  $out .= "\n".'endobj';
9080  $this->_out($out);
9081  $this->_newobj();
9082  $s = '<</Type /FontDescriptor /FontName /'.$name;
9083  foreach ($font['desc'] as $k => $v) {
9084  if ($k != 'Style') {
9085  if (is_float($v)) {
9086  $v = sprintf('%F', $v);
9087  }
9088  $s .= ' /'.$k.' '.$v.'';
9089  }
9090  }
9091  $s .= '>>';
9092  $s .= "\n".'endobj';
9093  $this->_out($s);
9094  }
9095 
9100  protected function _putimages() {
9101  $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9102  foreach ($this->imagekeys as $file) {
9103  $info = $this->getImageBuffer($file);
9104  // set object for alternate images array
9105  if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
9106  $altoid = $this->_newobj();
9107  $out = '[';
9108  foreach ($info['altimgs'] as $altimage) {
9109  if (isset($this->xobjects['I'.$altimage[0]]['n'])) {
9110  $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R';
9111  $out .= ' /DefaultForPrinting';
9112  if ($altimage[1] === true) {
9113  $out .= ' true';
9114  } else {
9115  $out .= ' false';
9116  }
9117  $out .= ' >>';
9118  }
9119  }
9120  $out .= ' ]';
9121  $out .= "\n".'endobj';
9122  $this->_out($out);
9123  }
9124  // set image object
9125  $oid = $this->_newobj();
9126  $this->xobjects['I'.$info['i']] = array('n' => $oid);
9127  $this->setImageSubBuffer($file, 'n', $this->n);
9128  $out = '<</Type /XObject';
9129  $out .= ' /Subtype /Image';
9130  $out .= ' /Width '.$info['w'];
9131  $out .= ' /Height '.$info['h'];
9132  if (array_key_exists('masked', $info)) {
9133  $out .= ' /SMask '.($this->n - 1).' 0 R';
9134  }
9135  // set color space
9136  $icc = false;
9137  if (isset($info['icc']) AND ($info['icc'] !== false)) {
9138  // ICC Colour Space
9139  $icc = true;
9140  $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]';
9141  } elseif ($info['cs'] == 'Indexed') {
9142  // Indexed Colour Space
9143  $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
9144  } else {
9145  // Device Colour Space
9146  $out .= ' /ColorSpace /'.$info['cs'];
9147  }
9148  if ($info['cs'] == 'DeviceCMYK') {
9149  $out .= ' /Decode [1 0 1 0 1 0 1 0]';
9150  }
9151  $out .= ' /BitsPerComponent '.$info['bpc'];
9152  if (isset($altoid) AND ($altoid > 0)) {
9153  // reference to alternate images dictionary
9154  $out .= ' /Alternates '.$altoid.' 0 R';
9155  }
9156  if (isset($info['exurl']) AND !empty($info['exurl'])) {
9157  // external stream
9158  $out .= ' /Length 0';
9159  $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
9160  if (isset($info['f'])) {
9161  $out .= ' /FFilter /'.$info['f'];
9162  }
9163  $out .= ' >>';
9164  $out .= ' stream'."\n".'endstream';
9165  } else {
9166  if (isset($info['f'])) {
9167  $out .= ' /Filter /'.$info['f'];
9168  }
9169  if (isset($info['parms'])) {
9170  $out .= ' '.$info['parms'];
9171  }
9172  if (isset($info['trns']) AND is_array($info['trns'])) {
9173  $trns = '';
9174  $count_info = count($info['trns']);
9175  if ($info['cs'] == 'Indexed') {
9176  $maxval =(pow(2, $info['bpc']) - 1);
9177  for ($i = 0; $i < $count_info; ++$i) {
9178  if (($info['trns'][$i] != 0) AND ($info['trns'][$i] != $maxval)) {
9179  // this is not a binary type mask @TODO: create a SMask
9180  $trns = '';
9181  break;
9182  } elseif (empty($trns) AND ($info['trns'][$i] == 0)) {
9183  // store the first fully transparent value
9184  $trns .= $i.' '.$i.' ';
9185  }
9186  }
9187  } else {
9188  // grayscale or RGB
9189  for ($i = 0; $i < $count_info; ++$i) {
9190  if ($info['trns'][$i] == 0) {
9191  $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
9192  }
9193  }
9194  }
9195  // Colour Key Masking
9196  if (!empty($trns)) {
9197  $out .= ' /Mask ['.$trns.']';
9198  }
9199  }
9200  $stream = $this->_getrawstream($info['data']);
9201  $out .= ' /Length '.strlen($stream).' >>';
9202  $out .= ' stream'."\n".$stream."\n".'endstream';
9203  }
9204  $out .= "\n".'endobj';
9205  $this->_out($out);
9206  if ($icc) {
9207  // ICC colour profile
9208  $this->_newobj();
9209  $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
9210  $icc = $this->_getrawstream($icc);
9211  $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9212  } elseif ($info['cs'] == 'Indexed') {
9213  // colour palette
9214  $this->_newobj();
9215  $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
9216  $pal = $this->_getrawstream($pal);
9217  $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
9218  }
9219  }
9220  }
9221 
9229  protected function _putxobjects() {
9230  foreach ($this->xobjects as $key => $data) {
9231  if (isset($data['outdata'])) {
9232  $stream = str_replace($this->epsmarker, '', trim($data['outdata']));
9233  $out = $this->_getobj($data['n'])."\n";
9234  $out .= '<<';
9235  $out .= ' /Type /XObject';
9236  $out .= ' /Subtype /Form';
9237  $out .= ' /FormType 1';
9238  if ($this->compress) {
9239  $stream = gzcompress($stream);
9240  $out .= ' /Filter /FlateDecode';
9241  }
9242  $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));
9243  $out .= ' /Matrix [1 0 0 1 0 0]';
9244  $out .= ' /Resources <<';
9245  $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9246  if (!$this->pdfa_mode) {
9247  // transparency
9248  if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
9249  $out .= ' /ExtGState <<';
9250  foreach ($data['extgstates'] as $k => $extgstate) {
9251  if (isset($this->extgstates[$k]['name'])) {
9252  $out .= ' /'.$this->extgstates[$k]['name'];
9253  } else {
9254  $out .= ' /GS'.$k;
9255  }
9256  $out .= ' '.$this->extgstates[$k]['n'].' 0 R';
9257  }
9258  $out .= ' >>';
9259  }
9260  if (isset($data['gradients']) AND !empty($data['gradients'])) {
9261  $gp = '';
9262  $gs = '';
9263  foreach ($data['gradients'] as $id => $grad) {
9264  // gradient patterns
9265  $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R';
9266  // gradient shadings
9267  $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R';
9268  }
9269  $out .= ' /Pattern <<'.$gp.' >>';
9270  $out .= ' /Shading <<'.$gs.' >>';
9271  }
9272  }
9273  // spot colors
9274  if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
9275  $out .= ' /ColorSpace <<';
9276  foreach ($data['spot_colors'] as $name => $color) {
9277  $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R';
9278  }
9279  $out .= ' >>';
9280  }
9281  // fonts
9282  if (!empty($data['fonts'])) {
9283  $out .= ' /Font <<';
9284  foreach ($data['fonts'] as $fontkey => $fontid) {
9285  $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9286  }
9287  $out .= ' >>';
9288  }
9289  // images or nested xobjects
9290  if (!empty($data['images']) OR !empty($data['xobjects'])) {
9291  $out .= ' /XObject <<';
9292  foreach ($data['images'] as $imgid) {
9293  $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
9294  }
9295  foreach ($data['xobjects'] as $sub_id => $sub_objid) {
9296  $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
9297  }
9298  $out .= ' >>';
9299  }
9300  $out .= ' >>'; //end resources
9301  if (isset($data['group']) AND ($data['group'] !== false)) {
9302  // set transparency group
9303  $out .= ' /Group << /Type /Group /S /Transparency';
9304  if (is_array($data['group'])) {
9305  if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
9306  $out .= ' /CS /'.$data['group']['CS'];
9307  }
9308  if (isset($data['group']['I'])) {
9309  $out .= ' /I /'.($data['group']['I']===true?'true':'false');
9310  }
9311  if (isset($data['group']['K'])) {
9312  $out .= ' /K /'.($data['group']['K']===true?'true':'false');
9313  }
9314  }
9315  $out .= ' >>';
9316  }
9317  $stream = $this->_getrawstream($stream, $data['n']);
9318  $out .= ' /Length '.strlen($stream);
9319  $out .= ' >>';
9320  $out .= ' stream'."\n".$stream."\n".'endstream';
9321  $out .= "\n".'endobj';
9322  $this->_out($out);
9323  }
9324  }
9325  }
9326 
9332  protected function _putspotcolors() {
9333  foreach ($this->spot_colors as $name => $color) {
9334  $this->_newobj();
9335  $this->spot_colors[$name]['n'] = $this->n;
9336  $out = '[/Separation /'.str_replace(' ', '#20', $name);
9337  $out .= ' /DeviceCMYK <<';
9338  $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
9339  $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
9340  $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
9341  $out .= "\n".'endobj';
9342  $this->_out($out);
9343  }
9344  }
9345 
9352  protected function _getxobjectdict() {
9353  $out = '';
9354  foreach ($this->xobjects as $id => $objid) {
9355  $out .= ' /'.$id.' '.$objid['n'].' 0 R';
9356  }
9357  return $out;
9358  }
9359 
9364  protected function _putresourcedict() {
9365  $out = $this->_getobj(2)."\n";
9366  $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9367  $out .= ' /Font <<';
9368  foreach ($this->fontkeys as $fontkey) {
9369  $font = $this->getFontBuffer($fontkey);
9370  $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
9371  }
9372  $out .= ' >>';
9373  $out .= ' /XObject <<';
9374  $out .= $this->_getxobjectdict();
9375  $out .= ' >>';
9376  // layers
9377  if (!empty($this->pdflayers)) {
9378  $out .= ' /Properties <<';
9379  foreach ($this->pdflayers as $layer) {
9380  $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
9381  }
9382  $out .= ' >>';
9383  }
9384  if (!$this->pdfa_mode) {
9385  // transparency
9386  if (isset($this->extgstates) AND !empty($this->extgstates)) {
9387  $out .= ' /ExtGState <<';
9388  foreach ($this->extgstates as $k => $extgstate) {
9389  if (isset($extgstate['name'])) {
9390  $out .= ' /'.$extgstate['name'];
9391  } else {
9392  $out .= ' /GS'.$k;
9393  }
9394  $out .= ' '.$extgstate['n'].' 0 R';
9395  }
9396  $out .= ' >>';
9397  }
9398  if (isset($this->gradients) AND !empty($this->gradients)) {
9399  $gp = '';
9400  $gs = '';
9401  foreach ($this->gradients as $id => $grad) {
9402  // gradient patterns
9403  $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
9404  // gradient shadings
9405  $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
9406  }
9407  $out .= ' /Pattern <<'.$gp.' >>';
9408  $out .= ' /Shading <<'.$gs.' >>';
9409  }
9410  }
9411  // spot colors
9412  if (isset($this->spot_colors) AND !empty($this->spot_colors)) {
9413  $out .= ' /ColorSpace <<';
9414  foreach ($this->spot_colors as $color) {
9415  $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
9416  }
9417  $out .= ' >>';
9418  }
9419  $out .= ' >>';
9420  $out .= "\n".'endobj';
9421  $this->_out($out);
9422  }
9423 
9428  protected function _putresources() {
9429  $this->_putextgstates();
9430  $this->_putocg();
9431  $this->_putfonts();
9432  $this->_putimages();
9433  $this->_putspotcolors();
9434  $this->_putshaders();
9435  $this->_putxobjects();
9436  $this->_putresourcedict();
9437  $this->_putdests();
9438  $this->_putEmbeddedFiles();
9439  $this->_putannotsobjs();
9440  $this->_putjavascript();
9441  $this->_putbookmarks();
9442  $this->_putencryption();
9443  }
9444 
9451  protected function _putinfo() {
9452  $oid = $this->_newobj();
9453  $out = '<<';
9454  // store current isunicode value
9455  $prev_isunicode = $this->isunicode;
9456  if ($this->docinfounicode) {
9457  $this->isunicode = true;
9458  }
9459  if (!TCPDF_STATIC::empty_string($this->title)) {
9460  // The document's title.
9461  $out .= ' /Title '.$this->_textstring($this->title, $oid);
9462  }
9463  if (!TCPDF_STATIC::empty_string($this->author)) {
9464  // The name of the person who created the document.
9465  $out .= ' /Author '.$this->_textstring($this->author, $oid);
9466  }
9467  if (!TCPDF_STATIC::empty_string($this->subject)) {
9468  // The subject of the document.
9469  $out .= ' /Subject '.$this->_textstring($this->subject, $oid);
9470  }
9471  if (!TCPDF_STATIC::empty_string($this->keywords)) {
9472  // Keywords associated with the document.
9473  $out .= ' /Keywords '.$this->_textstring($this->keywords, $oid);
9474  }
9475  if (!TCPDF_STATIC::empty_string($this->creator)) {
9476  // 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.
9477  $out .= ' /Creator '.$this->_textstring($this->creator, $oid);
9478  }
9479  // restore previous isunicode value
9480  $this->isunicode = $prev_isunicode;
9481  // default producer
9482  $out .= ' /Producer '.$this->_textstring(TCPDF_STATIC::getTCPDFProducer(), $oid);
9483  // The date and time the document was created, in human-readable form
9484  $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp);
9485  // The date and time the document was most recently modified, in human-readable form
9486  $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp);
9487  // A name object indicating whether the document has been modified to include trapping information
9488  $out .= ' /Trapped /False';
9489  $out .= ' >>';
9490  $out .= "\n".'endobj';
9491  $this->_out($out);
9492  return $oid;
9493  }
9494 
9502  public function setExtraXMP($xmp) {
9503  $this->custom_xmp = $xmp;
9504  }
9505 
9512  protected function _putXMP() {
9513  $oid = $this->_newobj();
9514  // store current isunicode value
9515  $prev_isunicode = $this->isunicode;
9516  $this->isunicode = true;
9517  $prev_encrypted = $this->encrypted;
9518  $this->encrypted = false;
9519  // set XMP data
9520  $xmp = '<?xpacket begin="'.TCPDF_FONTS::unichr(0xfeff, $this->isunicode).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
9521  $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";
9522  $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
9523  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
9524  $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
9525  $xmp .= "\t\t\t".'<dc:title>'."\n";
9526  $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9527  $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->title).'</rdf:li>'."\n";
9528  $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9529  $xmp .= "\t\t\t".'</dc:title>'."\n";
9530  $xmp .= "\t\t\t".'<dc:creator>'."\n";
9531  $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
9532  $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->author).'</rdf:li>'."\n";
9533  $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
9534  $xmp .= "\t\t\t".'</dc:creator>'."\n";
9535  $xmp .= "\t\t\t".'<dc:description>'."\n";
9536  $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9537  $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->subject).'</rdf:li>'."\n";
9538  $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9539  $xmp .= "\t\t\t".'</dc:description>'."\n";
9540  $xmp .= "\t\t\t".'<dc:subject>'."\n";
9541  $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9542  $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->keywords).'</rdf:li>'."\n";
9543  $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9544  $xmp .= "\t\t\t".'</dc:subject>'."\n";
9545  $xmp .= "\t\t".'</rdf:Description>'."\n";
9546  // convert doc creation date format
9547  $dcdate = TCPDF_STATIC::getFormattedDate($this->doc_creation_timestamp);
9548  $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2);
9549  $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2);
9550  $doccreationdate .= substr($dcdate, 14, 3).':'.substr($dcdate, 18, 2);
9551  $doccreationdate = TCPDF_STATIC::_escapeXML($doccreationdate);
9552  // convert doc modification date format
9553  $dmdate = TCPDF_STATIC::getFormattedDate($this->doc_modification_timestamp);
9554  $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2);
9555  $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2);
9556  $docmoddate .= substr($dmdate, 14, 3).':'.substr($dmdate, 18, 2);
9557  $docmoddate = TCPDF_STATIC::_escapeXML($docmoddate);
9558  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
9559  $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n";
9560  $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n";
9561  $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n";
9562  $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n";
9563  $xmp .= "\t\t".'</rdf:Description>'."\n";
9564  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
9565  $xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC::_escapeXML($this->keywords).'</pdf:Keywords>'."\n";
9566  $xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC::_escapeXML(TCPDF_STATIC::getTCPDFProducer()).'</pdf:Producer>'."\n";
9567  $xmp .= "\t\t".'</rdf:Description>'."\n";
9568  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
9569  $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);
9570  $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
9571  $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
9572  $xmp .= "\t\t".'</rdf:Description>'."\n";
9573  if ($this->pdfa_mode) {
9574  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
9575  $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n";
9576  $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
9577  $xmp .= "\t\t".'</rdf:Description>'."\n";
9578  }
9579  // XMP extension schemas
9580  $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";
9581  $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
9582  $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9583  $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9584  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
9585  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
9586  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
9587  $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9588  $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9589  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
9590  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
9591  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
9592  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9593  $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9594  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9595  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9596  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
9597  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9598  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9599  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9600  $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9601  $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9602  $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9603  $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9604  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
9605  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
9606  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
9607  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9608  $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9609  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9610  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9611  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
9612  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
9613  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
9614  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9615  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9616  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9617  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
9618  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
9619  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9620  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9621  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9622  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9623  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
9624  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
9625  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9626  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9627  $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9628  $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9629  $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9630  $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9631  $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
9632  $xmp .= "\t\t".'</rdf:Description>'."\n";
9633  $xmp .= "\t".'</rdf:RDF>'."\n";
9634  $xmp .= $this->custom_xmp;
9635  $xmp .= '</x:xmpmeta>'."\n";
9636  $xmp .= '<?xpacket end="w"?>';
9637  $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
9638  // restore previous isunicode value
9639  $this->isunicode = $prev_isunicode;
9640  $this->encrypted = $prev_encrypted;
9641  $this->_out($out);
9642  return $oid;
9643  }
9644 
9650  protected function _putcatalog() {
9651  // put XMP
9652  $xmpobj = $this->_putXMP();
9653  // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile
9654  if ($this->pdfa_mode OR $this->force_srgb) {
9655  $iccobj = $this->_newobj();
9656  $icc = file_get_contents(dirname(__FILE__).'/include/sRGB.icc');
9657  $filter = '';
9658  if ($this->compress) {
9659  $filter = ' /Filter /FlateDecode';
9660  $icc = gzcompress($icc);
9661  }
9662  $icc = $this->_getrawstream($icc);
9663  $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9664  }
9665  // start catalog
9666  $oid = $this->_newobj();
9667  $out = '<< /Type /Catalog';
9668  $out .= ' /Version /'.$this->PDFVersion;
9669  //$out .= ' /Extensions <<>>';
9670  $out .= ' /Pages 1 0 R';
9671  //$out .= ' /PageLabels ' //...;
9672  $out .= ' /Names <<';
9673  if ((!$this->pdfa_mode) AND !empty($this->n_js)) {
9674  $out .= ' /JavaScript '.$this->n_js;
9675  }
9676  if (!empty($this->efnames)) {
9677  $out .= ' /EmbeddedFiles <</Names [';
9678  foreach ($this->efnames AS $fn => $fref) {
9679  $out .= ' '.$this->_datastring($fn).' '.$fref;
9680  }
9681  $out .= ' ]>>';
9682  }
9683  $out .= ' >>';
9684  if (!empty($this->dests)) {
9685  $out .= ' /Dests '.($this->n_dests).' 0 R';
9686  }
9687  $out .= $this->_putviewerpreferences();
9688  if (isset($this->LayoutMode) AND (!TCPDF_STATIC::empty_string($this->LayoutMode))) {
9689  $out .= ' /PageLayout /'.$this->LayoutMode;
9690  }
9691  if (isset($this->PageMode) AND (!TCPDF_STATIC::empty_string($this->PageMode))) {
9692  $out .= ' /PageMode /'.$this->PageMode;
9693  }
9694  if (count($this->outlines) > 0) {
9695  $out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
9696  $out .= ' /PageMode /UseOutlines';
9697  }
9698  //$out .= ' /Threads []';
9699  if ($this->ZoomMode == 'fullpage') {
9700  $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
9701  } elseif ($this->ZoomMode == 'fullwidth') {
9702  $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
9703  } elseif ($this->ZoomMode == 'real') {
9704  $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
9705  } elseif (!is_string($this->ZoomMode)) {
9706  $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100));
9707  }
9708  //$out .= ' /AA <<>>';
9709  //$out .= ' /URI <<>>';
9710  $out .= ' /Metadata '.$xmpobj.' 0 R';
9711  //$out .= ' /StructTreeRoot <<>>';
9712  //$out .= ' /MarkInfo <<>>';
9713  if (isset($this->l['a_meta_language'])) {
9714  $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
9715  }
9716  //$out .= ' /SpiderInfo <<>>';
9717  // set OutputIntent to sRGB IEC61966-2.1 if required
9718  if ($this->pdfa_mode OR $this->force_srgb) {
9719  $out .= ' /OutputIntents [<<';
9720  $out .= ' /Type /OutputIntent';
9721  $out .= ' /S /GTS_PDFA1';
9722  $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9723  $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9724  $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
9725  $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9726  $out .= ' /DestOutputProfile '.$iccobj.' 0 R';
9727  $out .= ' >>]';
9728  }
9729  //$out .= ' /PieceInfo <<>>';
9730  if (!empty($this->pdflayers)) {
9731  $lyrobjs = '';
9732  $lyrobjs_off = '';
9733  $lyrobjs_lock = '';
9734  foreach ($this->pdflayers as $layer) {
9735  $layer_obj_ref = ' '.$layer['objid'].' 0 R';
9736  $lyrobjs .= $layer_obj_ref;
9737  if ($layer['view'] === false) {
9738  $lyrobjs_off .= $layer_obj_ref;
9739  }
9740  if ($layer['lock']) {
9741  $lyrobjs_lock .= $layer_obj_ref;
9742  }
9743  }
9744  $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
9745  $out .= ' /D <<';
9746  $out .= ' /Name '.$this->_textstring('Layers', $oid);
9747  $out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
9748  $out .= ' /BaseState /ON';
9749  $out .= ' /OFF ['.$lyrobjs_off.']';
9750  $out .= ' /Locked ['.$lyrobjs_lock.']';
9751  $out .= ' /Intent /View';
9752  $out .= ' /AS [';
9753  $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
9754  $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
9755  $out .= ' ]';
9756  $out .= ' /Order ['.$lyrobjs.']';
9757  $out .= ' /ListMode /AllPages';
9758  //$out .= ' /RBGroups ['..']';
9759  //$out .= ' /Locked ['..']';
9760  $out .= ' >>';
9761  $out .= ' >>';
9762  }
9763  // AcroForm
9764  if (!empty($this->form_obj_id)
9765  OR ($this->sign AND isset($this->signature_data['cert_type']))
9766  OR !empty($this->empty_signature_appearance)) {
9767  $out .= ' /AcroForm <<';
9768  $objrefs = '';
9769  if ($this->sign AND isset($this->signature_data['cert_type'])) {
9770  // set reference for signature object
9771  $objrefs .= $this->sig_obj_id.' 0 R';
9772  }
9773  if (!empty($this->empty_signature_appearance)) {
9774  foreach ($this->empty_signature_appearance as $esa) {
9775  // set reference for empty signature objects
9776  $objrefs .= ' '.$esa['objid'].' 0 R';
9777  }
9778  }
9779  if (!empty($this->form_obj_id)) {
9780  foreach($this->form_obj_id as $objid) {
9781  $objrefs .= ' '.$objid.' 0 R';
9782  }
9783  }
9784  $out .= ' /Fields ['.$objrefs.']';
9785  // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
9786  if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
9787  $out .= ' /NeedAppearances false';
9788  }
9789  if ($this->sign AND isset($this->signature_data['cert_type'])) {
9790  if ($this->signature_data['cert_type'] > 0) {
9791  $out .= ' /SigFlags 3';
9792  } else {
9793  $out .= ' /SigFlags 1';
9794  }
9795  }
9796  //$out .= ' /CO ';
9797  if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
9798  $out .= ' /DR <<';
9799  $out .= ' /Font <<';
9800  foreach ($this->annotation_fonts as $fontkey => $fontid) {
9801  $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9802  }
9803  $out .= ' >> >>';
9804  }
9805  $font = $this->getFontBuffer('helvetica');
9806  $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
9807  $out .= ' /Q '.(($this->rtl)?'2':'0');
9808  //$out .= ' /XFA ';
9809  $out .= ' >>';
9810  // signatures
9811  if ($this->sign AND isset($this->signature_data['cert_type'])
9812  AND (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A'))) {
9813  if ($this->signature_data['cert_type'] > 0) {
9814  $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
9815  } else {
9816  $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
9817  }
9818  }
9819  }
9820  //$out .= ' /Legal <<>>';
9821  //$out .= ' /Requirements []';
9822  //$out .= ' /Collection <<>>';
9823  //$out .= ' /NeedsRendering true';
9824  $out .= ' >>';
9825  $out .= "\n".'endobj';
9826  $this->_out($out);
9827  return $oid;
9828  }
9829 
9837  protected function _putviewerpreferences() {
9839  $out = ' /ViewerPreferences <<';
9840  if ($this->rtl) {
9841  $out .= ' /Direction /R2L';
9842  } else {
9843  $out .= ' /Direction /L2R';
9844  }
9845  if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) {
9846  $out .= ' /HideToolbar true';
9847  }
9848  if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) {
9849  $out .= ' /HideMenubar true';
9850  }
9851  if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) {
9852  $out .= ' /HideWindowUI true';
9853  }
9854  if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) {
9855  $out .= ' /FitWindow true';
9856  }
9857  if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) {
9858  $out .= ' /CenterWindow true';
9859  }
9860  if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) {
9861  $out .= ' /DisplayDocTitle true';
9862  }
9863  if (isset($vp['NonFullScreenPageMode'])) {
9864  $out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode'];
9865  }
9866  if (isset($vp['ViewArea'])) {
9867  $out .= ' /ViewArea /'.$vp['ViewArea'];
9868  }
9869  if (isset($vp['ViewClip'])) {
9870  $out .= ' /ViewClip /'.$vp['ViewClip'];
9871  }
9872  if (isset($vp['PrintArea'])) {
9873  $out .= ' /PrintArea /'.$vp['PrintArea'];
9874  }
9875  if (isset($vp['PrintClip'])) {
9876  $out .= ' /PrintClip /'.$vp['PrintClip'];
9877  }
9878  if (isset($vp['PrintScaling'])) {
9879  $out .= ' /PrintScaling /'.$vp['PrintScaling'];
9880  }
9881  if (isset($vp['Duplex']) AND (!TCPDF_STATIC::empty_string($vp['Duplex']))) {
9882  $out .= ' /Duplex /'.$vp['Duplex'];
9883  }
9884  if (isset($vp['PickTrayByPDFSize'])) {
9885  if ($vp['PickTrayByPDFSize']) {
9886  $out .= ' /PickTrayByPDFSize true';
9887  } else {
9888  $out .= ' /PickTrayByPDFSize false';
9889  }
9890  }
9891  if (isset($vp['PrintPageRange'])) {
9892  $PrintPageRangeNum = '';
9893  foreach ($vp['PrintPageRange'] as $k => $v) {
9894  $PrintPageRangeNum .= ' '.($v - 1).'';
9895  }
9896  $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
9897  }
9898  if (isset($vp['NumCopies'])) {
9899  $out .= ' /NumCopies '.intval($vp['NumCopies']);
9900  }
9901  $out .= ' >>';
9902  return $out;
9903  }
9904 
9909  protected function _putheader() {
9910  $this->_out('%PDF-'.$this->PDFVersion);
9911  $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
9912  }
9913 
9918  protected function _enddoc() {
9919  if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
9920  // save subset chars of the previous font
9921  $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
9922  }
9923  $this->state = 1;
9924  $this->_putheader();
9925  $this->_putpages();
9926  $this->_putresources();
9927  // empty signature fields
9928  if (!empty($this->empty_signature_appearance)) {
9929  foreach ($this->empty_signature_appearance as $key => $esa) {
9930  // widget annotation for empty signature
9931  $out = $this->_getobj($esa['objid'])."\n";
9932  $out .= '<< /Type /Annot';
9933  $out .= ' /Subtype /Widget';
9934  $out .= ' /Rect ['.$esa['rect'].']';
9935  $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page
9936  $out .= ' /F 4';
9937  $out .= ' /FT /Sig';
9938  $signame = $esa['name'].sprintf(' [%03d]', ($key + 1));
9939  $out .= ' /T '.$this->_textstring($signame, $esa['objid']);
9940  $out .= ' /Ff 0';
9941  $out .= ' >>';
9942  $out .= "\n".'endobj';
9943  $this->_out($out);
9944  }
9945  }
9946  // Signature
9947  if ($this->sign AND isset($this->signature_data['cert_type'])) {
9948  // widget annotation for signature
9949  $out = $this->_getobj($this->sig_obj_id)."\n";
9950  $out .= '<< /Type /Annot';
9951  $out .= ' /Subtype /Widget';
9952  $out .= ' /Rect ['.$this->signature_appearance['rect'].']';
9953  $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
9954  $out .= ' /F 4';
9955  $out .= ' /FT /Sig';
9956  $out .= ' /T '.$this->_textstring($this->signature_appearance['name'], $this->sig_obj_id);
9957  $out .= ' /Ff 0';
9958  $out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
9959  $out .= ' >>';
9960  $out .= "\n".'endobj';
9961  $this->_out($out);
9962  // signature
9963  $this->_putsignature();
9964  }
9965  // Info
9966  $objid_info = $this->_putinfo();
9967  // Catalog
9968  $objid_catalog = $this->_putcatalog();
9969  // Cross-ref
9970  $o = $this->bufferlen;
9971  // XREF section
9972  $this->_out('xref');
9973  $this->_out('0 '.($this->n + 1));
9974  $this->_out('0000000000 65535 f ');
9975  $freegen = ($this->n + 2);
9976  for ($i=1; $i <= $this->n; ++$i) {
9977  if (!isset($this->offsets[$i]) AND ($i > 1)) {
9978  $this->_out(sprintf('0000000000 %05d f ', $freegen));
9979  ++$freegen;
9980  } else {
9981  $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
9982  }
9983  }
9984  // TRAILER
9985  $out = 'trailer'."\n";
9986  $out .= '<<';
9987  $out .= ' /Size '.($this->n + 1);
9988  $out .= ' /Root '.$objid_catalog.' 0 R';
9989  $out .= ' /Info '.$objid_info.' 0 R';
9990  if ($this->encrypted) {
9991  $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
9992  }
9993  $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
9994  $out .= ' >>';
9995  $this->_out($out);
9996  $this->_out('startxref');
9997  $this->_out($o);
9998  $this->_out('%%EOF');
9999  $this->state = 3; // end-of-doc
10000  }
10001 
10009  protected function _beginpage($orientation='', $format='') {
10010  ++$this->page;
10011  $this->pageobjects[$this->page] = array();
10012  $this->setPageBuffer($this->page, '');
10013  // initialize array for graphics tranformation positions inside a page buffer
10014  $this->transfmrk[$this->page] = array();
10015  $this->state = 2;
10016  if (TCPDF_STATIC::empty_string($orientation)) {
10017  if (isset($this->CurOrientation)) {
10018  $orientation = $this->CurOrientation;
10019  } elseif ($this->fwPt > $this->fhPt) {
10020  // landscape
10021  $orientation = 'L';
10022  } else {
10023  // portrait
10024  $orientation = 'P';
10025  }
10026  }
10027  if (TCPDF_STATIC::empty_string($format)) {
10028  $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
10029  $this->setPageOrientation($orientation);
10030  } else {
10031  $this->setPageFormat($format, $orientation);
10032  }
10033  if ($this->rtl) {
10034  $this->x = $this->w - $this->rMargin;
10035  } else {
10036  $this->x = $this->lMargin;
10037  }
10038  $this->y = $this->tMargin;
10039  if (isset($this->newpagegroup[$this->page])) {
10040  // start a new group
10041  $this->currpagegroup = $this->newpagegroup[$this->page];
10042  $this->pagegroups[$this->currpagegroup] = 1;
10043  } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
10044  ++$this->pagegroups[$this->currpagegroup];
10045  }
10046  }
10047 
10052  protected function _endpage() {
10053  $this->setVisibility('all');
10054  $this->state = 1;
10055  }
10056 
10062  protected function _newobj() {
10063  $this->_out($this->_getobj());
10064  return $this->n;
10065  }
10066 
10074  protected function _getobj($objid='') {
10075  if ($objid === '') {
10076  ++$this->n;
10077  $objid = $this->n;
10078  }
10079  $this->offsets[$objid] = $this->bufferlen;
10080  $this->pageobjects[$this->page][] = $objid;
10081  return $objid.' 0 obj';
10082  }
10083 
10091  protected function _dounderline($x, $y, $txt) {
10092  $w = $this->GetStringWidth($txt);
10093  return $this->_dounderlinew($x, $y, $w);
10094  }
10095 
10104  protected function _dounderlinew($x, $y, $w) {
10105  $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10106  return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
10107  }
10108 
10116  protected function _dolinethrough($x, $y, $txt) {
10117  $w = $this->GetStringWidth($txt);
10118  return $this->_dolinethroughw($x, $y, $w);
10119  }
10120 
10129  protected function _dolinethroughw($x, $y, $w) {
10130  $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10131  return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew);
10132  }
10133 
10142  protected function _dooverline($x, $y, $txt) {
10143  $w = $this->GetStringWidth($txt);
10144  return $this->_dooverlinew($x, $y, $w);
10145  }
10146 
10155  protected function _dooverlinew($x, $y, $w) {
10156  $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10157  return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
10158 
10159  }
10160 
10168  protected function _datastring($s, $n=0) {
10169  if ($n == 0) {
10170  $n = $this->n;
10171  }
10172  $s = $this->_encrypt_data($n, $s);
10173  return '('. TCPDF_STATIC::_escape($s).')';
10174  }
10175 
10182  public function setDocCreationTimestamp($time) {
10183  if (is_string($time)) {
10184  $time = TCPDF_STATIC::getTimestamp($time);
10185  }
10186  $this->doc_creation_timestamp = intval($time);
10187  }
10188 
10195  public function setDocModificationTimestamp($time) {
10196  if (is_string($time)) {
10197  $time = TCPDF_STATIC::getTimestamp($time);
10198  }
10199  $this->doc_modification_timestamp = intval($time);
10200  }
10201 
10208  public function getDocCreationTimestamp() {
10210  }
10211 
10218  public function getDocModificationTimestamp() {
10220  }
10221 
10230  protected function _datestring($n=0, $timestamp=0) {
10231  if ((empty($timestamp)) OR ($timestamp < 0)) {
10233  }
10235  }
10236 
10244  protected function _textstring($s, $n=0) {
10245  if ($this->isunicode) {
10246  //Convert string to UTF-16BE
10247  $s = TCPDF_FONTS::UTF8ToUTF16BE($s, true, $this->isunicode, $this->CurrentFont);
10248  }
10249  return $this->_datastring($s, $n);
10250  }
10251 
10260  protected function _getrawstream($s, $n=0) {
10261  if ($n <= 0) {
10262  // default to current object
10263  $n = $this->n;
10264  }
10265  return $this->_encrypt_data($n, $s);
10266  }
10267 
10273  protected function _out($s) {
10274  if ($this->state == 2) {
10275  if ($this->inxobj) {
10276  // we are inside an XObject template
10277  $this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
10278  } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
10279  // puts data before page footer
10280  $pagebuff = $this->getPageBuffer($this->page);
10281  $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
10282  $footer = substr($pagebuff, -$this->footerlen[$this->page]);
10283  $this->setPageBuffer($this->page, $page.$s."\n".$footer);
10284  // update footer position
10285  $this->footerpos[$this->page] += strlen($s."\n");
10286  } else {
10287  // set page data
10288  $this->setPageBuffer($this->page, $s."\n", true);
10289  }
10290  } elseif ($this->state > 0) {
10291  // set general data
10292  $this->setBuffer($s."\n");
10293  }
10294  }
10295 
10302  public function setHeaderFont($font) {
10303  $this->header_font = $font;
10304  }
10305 
10312  public function getHeaderFont() {
10313  return $this->header_font;
10314  }
10315 
10322  public function setFooterFont($font) {
10323  $this->footer_font = $font;
10324  }
10325 
10332  public function getFooterFont() {
10333  return $this->footer_font;
10334  }
10335 
10342  public function setLanguageArray($language) {
10343  $this->l = $language;
10344  if (isset($this->l['a_meta_dir'])) {
10345  $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
10346  } else {
10347  $this->rtl = false;
10348  }
10349  }
10350 
10355  public function getPDFData() {
10356  if ($this->state < 3) {
10357  $this->Close();
10358  }
10359  return $this->buffer;
10360  }
10361 
10374  public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
10375  if (isset($url[1]) AND ($url[0] == '#') AND is_numeric($url[1])) {
10376  // convert url to internal link
10377  $lnkdata = explode(',', $url);
10378  if (isset($lnkdata[0]) ) {
10379  $page = substr($lnkdata[0], 1);
10380  if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
10381  $lnky = floatval($lnkdata[1]);
10382  } else {
10383  $lnky = 0;
10384  }
10385  $url = $this->AddLink();
10386  $this->SetLink($url, $lnky, $page);
10387  }
10388  }
10389  // store current settings
10390  $prevcolor = $this->fgcolor;
10391  $prevstyle = $this->FontStyle;
10392  if (empty($color)) {
10393  $this->SetTextColorArray($this->htmlLinkColorArray);
10394  } else {
10395  $this->SetTextColorArray($color);
10396  }
10397  if ($style == -1) {
10398  $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
10399  } else {
10400  $this->SetFont('', $this->FontStyle.$style);
10401  }
10402  $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
10403  // restore settings
10404  $this->SetFont('', $prevstyle);
10405  $this->SetTextColorArray($prevcolor);
10406  return $ret;
10407  }
10408 
10416  public function pixelsToUnits($px) {
10417  return ($px / ($this->imgscale * $this->k));
10418  }
10419 
10427  public function unhtmlentities($text_to_convert) {
10428  return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
10429  }
10430 
10431  // ENCRYPTION METHODS ----------------------------------
10432 
10442  protected function _objectkey($n) {
10443  $objkey = $this->encryptdata['key'].pack('VXxx', $n);
10444  if ($this->encryptdata['mode'] == 2) { // AES-128
10445  // AES padding
10446  $objkey .= "\x73\x41\x6C\x54"; // sAlT
10447  }
10448  $objkey = substr(TCPDF_STATIC::_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
10449  $objkey = substr($objkey, 0, 16);
10450  return $objkey;
10451  }
10452 
10462  protected function _encrypt_data($n, $s) {
10463  if (!$this->encrypted) {
10464  return $s;
10465  }
10466  switch ($this->encryptdata['mode']) {
10467  case 0: // RC4-40
10468  case 1: { // RC4-128
10469  $s = TCPDF_STATIC::_RC4($this->_objectkey($n), $s, $this->last_enc_key, $this->last_enc_key_c);
10470  break;
10471  }
10472  case 2: { // AES-128
10473  $s = TCPDF_STATIC::_AES($this->_objectkey($n), $s);
10474  break;
10475  }
10476  case 3: { // AES-256
10477  $s = TCPDF_STATIC::_AES($this->encryptdata['key'], $s);
10478  break;
10479  }
10480  }
10481  return $s;
10482  }
10483 
10490  protected function _putencryption() {
10491  if (!$this->encrypted) {
10492  return;
10493  }
10494  $this->encryptdata['objid'] = $this->_newobj();
10495  $out = '<<';
10496  if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
10497  $this->encryptdata['Filter'] = 'Standard';
10498  }
10499  $out .= ' /Filter /'.$this->encryptdata['Filter'];
10500  if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
10501  $out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
10502  }
10503  if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
10504  $this->encryptdata['V'] = 1;
10505  }
10506  // V is a code specifying the algorithm to be used in encrypting and decrypting the document
10507  $out .= ' /V '.$this->encryptdata['V'];
10508  if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
10509  // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
10510  $out .= ' /Length '.$this->encryptdata['Length'];
10511  } else {
10512  $out .= ' /Length 40';
10513  }
10514  if ($this->encryptdata['V'] >= 4) {
10515  if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
10516  $this->encryptdata['StmF'] = 'Identity';
10517  }
10518  if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
10519  // The name of the crypt filter that shall be used when decrypting all strings in the document.
10520  $this->encryptdata['StrF'] = 'Identity';
10521  }
10522  // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
10523  if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
10524  $out .= ' /CF <<';
10525  $out .= ' /'.$this->encryptdata['StmF'].' <<';
10526  $out .= ' /Type /CryptFilter';
10527  if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
10528  // The method used
10529  $out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
10530  if ($this->encryptdata['pubkey']) {
10531  $out .= ' /Recipients [';
10532  foreach ($this->encryptdata['Recipients'] as $rec) {
10533  $out .= ' <'.$rec.'>';
10534  }
10535  $out .= ' ]';
10536  if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
10537  $out .= ' /EncryptMetadata false';
10538  } else {
10539  $out .= ' /EncryptMetadata true';
10540  }
10541  }
10542  } else {
10543  $out .= ' /CFM /None';
10544  }
10545  if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
10546  // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
10547  $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
10548  } else {
10549  $out .= ' /AuthEvent /DocOpen';
10550  }
10551  if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
10552  // The bit length of the encryption key.
10553  $out .= ' /Length '.$this->encryptdata['CF']['Length'];
10554  }
10555  $out .= ' >> >>';
10556  }
10557  // The name of the crypt filter that shall be used by default when decrypting streams.
10558  $out .= ' /StmF /'.$this->encryptdata['StmF'];
10559  // The name of the crypt filter that shall be used when decrypting all strings in the document.
10560  $out .= ' /StrF /'.$this->encryptdata['StrF'];
10561  if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
10562  // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
10563  $out .= ' /EFF /'.$this->encryptdata[''];
10564  }
10565  }
10566  // Additional encryption dictionary entries for the standard security handler
10567  if ($this->encryptdata['pubkey']) {
10568  if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
10569  $out .= ' /Recipients [';
10570  foreach ($this->encryptdata['Recipients'] as $rec) {
10571  $out .= ' <'.$rec.'>';
10572  }
10573  $out .= ' ]';
10574  }
10575  } else {
10576  $out .= ' /R';
10577  if ($this->encryptdata['V'] == 5) { // AES-256
10578  $out .= ' 5';
10579  $out .= ' /OE ('.TCPDF_STATIC::_escape($this->encryptdata['OE']).')';
10580  $out .= ' /UE ('.TCPDF_STATIC::_escape($this->encryptdata['UE']).')';
10581  $out .= ' /Perms ('.TCPDF_STATIC::_escape($this->encryptdata['perms']).')';
10582  } elseif ($this->encryptdata['V'] == 4) { // AES-128
10583  $out .= ' 4';
10584  } elseif ($this->encryptdata['V'] < 2) { // RC-40
10585  $out .= ' 2';
10586  } else { // RC-128
10587  $out .= ' 3';
10588  }
10589  $out .= ' /O ('.TCPDF_STATIC::_escape($this->encryptdata['O']).')';
10590  $out .= ' /U ('.TCPDF_STATIC::_escape($this->encryptdata['U']).')';
10591  $out .= ' /P '.$this->encryptdata['P'];
10592  if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
10593  $out .= ' /EncryptMetadata false';
10594  } else {
10595  $out .= ' /EncryptMetadata true';
10596  }
10597  }
10598  $out .= ' >>';
10599  $out .= "\n".'endobj';
10600  $this->_out($out);
10601  }
10602 
10610  protected function _Uvalue() {
10611  if ($this->encryptdata['mode'] == 0) { // RC4-40
10612  return TCPDF_STATIC::_RC4($this->encryptdata['key'], TCPDF_STATIC::$enc_padding, $this->last_enc_key, $this->last_enc_key_c);
10613  } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
10614  $tmp = TCPDF_STATIC::_md5_16(TCPDF_STATIC::$enc_padding.$this->encryptdata['fileid']);
10615  $enc = TCPDF_STATIC::_RC4($this->encryptdata['key'], $tmp, $this->last_enc_key, $this->last_enc_key_c);
10616  $len = strlen($tmp);
10617  for ($i = 1; $i <= 19; ++$i) {
10618  $ek = '';
10619  for ($j = 0; $j < $len; ++$j) {
10620  $ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i);
10621  }
10622  $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10623  }
10624  $enc .= str_repeat("\x00", 16);
10625  return substr($enc, 0, 32);
10626  } elseif ($this->encryptdata['mode'] == 3) { // AES-256
10628  // User Validation Salt
10629  $this->encryptdata['UVS'] = substr($seed, 0, 8);
10630  // User Key Salt
10631  $this->encryptdata['UKS'] = substr($seed, 8, 16);
10632  return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
10633  }
10634  }
10635 
10643  protected function _UEvalue() {
10644  $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
10645  return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']);
10646  }
10647 
10655  protected function _Ovalue() {
10656  if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
10657  $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['owner_password']);
10658  if ($this->encryptdata['mode'] > 0) {
10659  for ($i = 0; $i < 50; ++$i) {
10660  $tmp = TCPDF_STATIC::_md5_16($tmp);
10661  }
10662  }
10663  $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
10664  $enc = TCPDF_STATIC::_RC4($owner_key, $this->encryptdata['user_password'], $this->last_enc_key, $this->last_enc_key_c);
10665  if ($this->encryptdata['mode'] > 0) {
10666  $len = strlen($owner_key);
10667  for ($i = 1; $i <= 19; ++$i) {
10668  $ek = '';
10669  for ($j = 0; $j < $len; ++$j) {
10670  $ek .= chr(ord($owner_key[$j]) ^ $i);
10671  }
10672  $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10673  }
10674  }
10675  return $enc;
10676  } elseif ($this->encryptdata['mode'] == 3) { // AES-256
10678  // Owner Validation Salt
10679  $this->encryptdata['OVS'] = substr($seed, 0, 8);
10680  // Owner Key Salt
10681  $this->encryptdata['OKS'] = substr($seed, 8, 16);
10682  return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
10683  }
10684  }
10685 
10693  protected function _OEvalue() {
10694  $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
10695  return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']);
10696  }
10697 
10706  protected function _fixAES256Password($password) {
10707  $psw = ''; // password to be returned
10708  $psw_array = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($password, $this->isunicode, $this->CurrentFont), $password, $this->rtl, $this->isunicode, $this->CurrentFont);
10709  foreach ($psw_array as $c) {
10710  $psw .= TCPDF_FONTS::unichr($c, $this->isunicode);
10711  }
10712  return substr($psw, 0, 127);
10713  }
10714 
10721  protected function _generateencryptionkey() {
10722  $keybytelen = ($this->encryptdata['Length'] / 8);
10723  if (!$this->encryptdata['pubkey']) { // standard mode
10724  if ($this->encryptdata['mode'] == 3) { // AES-256
10725  // generate 256 bit random key
10726  $this->encryptdata['key'] = substr(hash('sha256', TCPDF_STATIC::getRandomSeed(), true), 0, $keybytelen);
10727  // truncate passwords
10728  $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
10729  $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
10730  // Compute U value
10731  $this->encryptdata['U'] = $this->_Uvalue();
10732  // Compute UE value
10733  $this->encryptdata['UE'] = $this->_UEvalue();
10734  // Compute O value
10735  $this->encryptdata['O'] = $this->_Ovalue();
10736  // Compute OE value
10737  $this->encryptdata['OE'] = $this->_OEvalue();
10738  // Compute P value
10739  $this->encryptdata['P'] = $this->encryptdata['protection'];
10740  // Computing the encryption dictionary's Perms (permissions) value
10741  $perms = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
10742  $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
10743  if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
10744  $perms .= 'F';
10745  } else {
10746  $perms .= 'T';
10747  }
10748  $perms .= 'adb'; // bytes 9-11
10749  $perms .= 'nick'; // bytes 12-15
10750  $this->encryptdata['perms'] = TCPDF_STATIC::_AESnopad($this->encryptdata['key'], $perms);
10751  } else { // RC4-40, RC4-128, AES-128
10752  // Pad passwords
10753  $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10754  $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10755  // Compute O value
10756  $this->encryptdata['O'] = $this->_Ovalue();
10757  // get default permissions (reverse byte order)
10758  $permissions = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']);
10759  // Compute encryption key
10760  $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
10761  if ($this->encryptdata['mode'] > 0) {
10762  for ($i = 0; $i < 50; ++$i) {
10763  $tmp = TCPDF_STATIC::_md5_16(substr($tmp, 0, $keybytelen));
10764  }
10765  }
10766  $this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
10767  // Compute U value
10768  $this->encryptdata['U'] = $this->_Uvalue();
10769  // Compute P value
10770  $this->encryptdata['P'] = $this->encryptdata['protection'];
10771  }
10772  } else { // Public-Key mode
10773  // random 20-byte seed
10774  $seed = sha1(TCPDF_STATIC::getRandomSeed(), true);
10775  $recipient_bytes = '';
10776  foreach ($this->encryptdata['pubkeys'] as $pubkey) {
10777  // for each public certificate
10778  if (isset($pubkey['p'])) {
10779  $pkprotection = TCPDF_STATIC::getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
10780  } else {
10781  $pkprotection = $this->encryptdata['protection'];
10782  }
10783  // get default permissions (reverse byte order)
10784  $pkpermissions = TCPDF_STATIC::getEncPermissionsString($pkprotection);
10785  // envelope data
10786  $envelope = $seed.$pkpermissions;
10787  // write the envelope data to a temporary file
10788  $tempkeyfile = TCPDF_STATIC::getObjFilename('key', $this->file_id);
10789  $f = TCPDF_STATIC::fopenLocal($tempkeyfile, 'wb');
10790  if (!$f) {
10791  $this->Error('Unable to create temporary key file: '.$tempkeyfile);
10792  }
10793  $envelope_length = strlen($envelope);
10794  fwrite($f, $envelope, $envelope_length);
10795  fclose($f);
10796  $tempencfile = TCPDF_STATIC::getObjFilename('enc', $this->file_id);
10797  if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) {
10798  $this->Error('Unable to encrypt the file: '.$tempkeyfile);
10799  }
10800  // read encryption signature
10801  $signature = file_get_contents($tempencfile, false, null, $envelope_length);
10802  // extract signature
10803  $signature = substr($signature, strpos($signature, 'Content-Disposition'));
10804  $tmparr = explode("\n\n", $signature);
10805  $signature = trim($tmparr[1]);
10806  unset($tmparr);
10807  // decode signature
10808  $signature = base64_decode($signature);
10809  // convert signature to hex
10810  $hexsignature = current(unpack('H*', $signature));
10811  // store signature on recipients array
10812  $this->encryptdata['Recipients'][] = $hexsignature;
10813  // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
10814  $recipient_bytes .= $signature;
10815  }
10816  // calculate encryption key
10817  if ($this->encryptdata['mode'] == 3) { // AES-256
10818  $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
10819  } else { // RC4-40, RC4-128, AES-128
10820  $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
10821  }
10822  }
10823  }
10824 
10839  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) {
10840  if ($this->pdfa_mode) {
10841  // encryption is not allowed in PDF/A mode
10842  return;
10843  }
10844  $this->encryptdata['protection'] = TCPDF_STATIC::getUserPermissionCode($permissions, $mode);
10845  if (($pubkeys !== null) AND (is_array($pubkeys))) {
10846  // public-key mode
10847  $this->encryptdata['pubkeys'] = $pubkeys;
10848  if ($mode == 0) {
10849  // public-Key Security requires at least 128 bit
10850  $mode = 1;
10851  }
10852  if (!function_exists('openssl_pkcs7_encrypt')) {
10853  $this->Error('Public-Key Security requires openssl library.');
10854  }
10855  // Set Public-Key filter (available are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
10856  $this->encryptdata['pubkey'] = true;
10857  $this->encryptdata['Filter'] = 'Adobe.PubSec';
10858  $this->encryptdata['StmF'] = 'DefaultCryptFilter';
10859  $this->encryptdata['StrF'] = 'DefaultCryptFilter';
10860  } else {
10861  // standard mode (password mode)
10862  $this->encryptdata['pubkey'] = false;
10863  $this->encryptdata['Filter'] = 'Standard';
10864  $this->encryptdata['StmF'] = 'StdCF';
10865  $this->encryptdata['StrF'] = 'StdCF';
10866  }
10867  if ($mode > 1) { // AES
10868  if (!extension_loaded('openssl') && !extension_loaded('mcrypt')) {
10869  $this->Error('AES encryption requires openssl or mcrypt extension (http://www.php.net/manual/en/mcrypt.requirements.php).');
10870  }
10871  if (extension_loaded('openssl') && !in_array('aes-256-cbc', openssl_get_cipher_methods())) {
10872  $this->Error('AES encryption requires openssl/aes-256-cbc cypher.');
10873  }
10874  if (extension_loaded('mcrypt') && mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
10875  $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
10876  }
10877  if (($mode == 3) AND !function_exists('hash')) {
10878  // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
10879  $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
10880  }
10881  }
10882  if ($owner_pass === null) {
10883  $owner_pass = md5(TCPDF_STATIC::getRandomSeed());
10884  }
10885  $this->encryptdata['user_password'] = $user_pass;
10886  $this->encryptdata['owner_password'] = $owner_pass;
10887  $this->encryptdata['mode'] = $mode;
10888  switch ($mode) {
10889  case 0: { // RC4 40 bit
10890  $this->encryptdata['V'] = 1;
10891  $this->encryptdata['Length'] = 40;
10892  $this->encryptdata['CF']['CFM'] = 'V2';
10893  break;
10894  }
10895  case 1: { // RC4 128 bit
10896  $this->encryptdata['V'] = 2;
10897  $this->encryptdata['Length'] = 128;
10898  $this->encryptdata['CF']['CFM'] = 'V2';
10899  if ($this->encryptdata['pubkey']) {
10900  $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
10901  $this->encryptdata['Recipients'] = array();
10902  }
10903  break;
10904  }
10905  case 2: { // AES 128 bit
10906  $this->encryptdata['V'] = 4;
10907  $this->encryptdata['Length'] = 128;
10908  $this->encryptdata['CF']['CFM'] = 'AESV2';
10909  $this->encryptdata['CF']['Length'] = 128;
10910  if ($this->encryptdata['pubkey']) {
10911  $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
10912  $this->encryptdata['Recipients'] = array();
10913  }
10914  break;
10915  }
10916  case 3: { // AES 256 bit
10917  $this->encryptdata['V'] = 5;
10918  $this->encryptdata['Length'] = 256;
10919  $this->encryptdata['CF']['CFM'] = 'AESV3';
10920  $this->encryptdata['CF']['Length'] = 256;
10921  if ($this->encryptdata['pubkey']) {
10922  $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
10923  $this->encryptdata['Recipients'] = array();
10924  }
10925  break;
10926  }
10927  }
10928  $this->encrypted = true;
10929  $this->encryptdata['fileid'] = TCPDF_STATIC::convertHexStringToString($this->file_id);
10930  $this->_generateencryptionkey();
10931  }
10932 
10933  // END OF ENCRYPTION FUNCTIONS -------------------------
10934 
10935  // START TRANSFORMATIONS SECTION -----------------------
10936 
10945  public function StartTransform() {
10946  if ($this->state != 2) {
10947  return;
10948  }
10949  $this->_outSaveGraphicsState();
10950  if ($this->inxobj) {
10951  // we are inside an XObject template
10952  $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
10953  } else {
10954  $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
10955  }
10957  $this->transfmatrix[$this->transfmatrix_key] = array();
10958  }
10959 
10968  public function StopTransform() {
10969  if ($this->state != 2) {
10970  return;
10971  }
10972  $this->_outRestoreGraphicsState();
10973  if (isset($this->transfmatrix[$this->transfmatrix_key])) {
10974  array_pop($this->transfmatrix[$this->transfmatrix_key]);
10976  }
10977  if ($this->inxobj) {
10978  // we are inside an XObject template
10979  array_pop($this->xobjects[$this->xobjid]['transfmrk']);
10980  } else {
10981  array_pop($this->transfmrk[$this->page]);
10982  }
10983  }
10993  public function ScaleX($s_x, $x='', $y='') {
10994  $this->Scale($s_x, 100, $x, $y);
10995  }
10996 
11006  public function ScaleY($s_y, $x='', $y='') {
11007  $this->Scale(100, $s_y, $x, $y);
11008  }
11009 
11019  public function ScaleXY($s, $x='', $y='') {
11020  $this->Scale($s, $s, $x, $y);
11021  }
11022 
11033  public function Scale($s_x, $s_y, $x='', $y='') {
11034  if ($x === '') {
11035  $x = $this->x;
11036  }
11037  if ($y === '') {
11038  $y = $this->y;
11039  }
11040  if (($s_x == 0) OR ($s_y == 0)) {
11041  $this->Error('Please do not use values equal to zero for scaling');
11042  }
11043  $y = ($this->h - $y) * $this->k;
11044  $x *= $this->k;
11045  //calculate elements of transformation matrix
11046  $s_x /= 100;
11047  $s_y /= 100;
11048  $tm = array();
11049  $tm[0] = $s_x;
11050  $tm[1] = 0;
11051  $tm[2] = 0;
11052  $tm[3] = $s_y;
11053  $tm[4] = $x * (1 - $s_x);
11054  $tm[5] = $y * (1 - $s_y);
11055  //scale the coordinate system
11056  $this->Transform($tm);
11057  }
11058 
11066  public function MirrorH($x='') {
11067  $this->Scale(-100, 100, $x);
11068  }
11069 
11077  public function MirrorV($y='') {
11078  $this->Scale(100, -100, '', $y);
11079  }
11080 
11089  public function MirrorP($x='',$y='') {
11090  $this->Scale(-100, -100, $x, $y);
11091  }
11092 
11102  public function MirrorL($angle=0, $x='',$y='') {
11103  $this->Scale(-100, 100, $x, $y);
11104  $this->Rotate(-2*($angle-90), $x, $y);
11105  }
11106 
11114  public function TranslateX($t_x) {
11115  $this->Translate($t_x, 0);
11116  }
11117 
11125  public function TranslateY($t_y) {
11126  $this->Translate(0, $t_y);
11127  }
11128 
11137  public function Translate($t_x, $t_y) {
11138  //calculate elements of transformation matrix
11139  $tm = array();
11140  $tm[0] = 1;
11141  $tm[1] = 0;
11142  $tm[2] = 0;
11143  $tm[3] = 1;
11144  $tm[4] = $t_x * $this->k;
11145  $tm[5] = -$t_y * $this->k;
11146  //translate the coordinate system
11147  $this->Transform($tm);
11148  }
11149 
11159  public function Rotate($angle, $x='', $y='') {
11160  if ($x === '') {
11161  $x = $this->x;
11162  }
11163  if ($y === '') {
11164  $y = $this->y;
11165  }
11166  $y = ($this->h - $y) * $this->k;
11167  $x *= $this->k;
11168  //calculate elements of transformation matrix
11169  $tm = array();
11170  $tm[0] = cos(deg2rad($angle));
11171  $tm[1] = sin(deg2rad($angle));
11172  $tm[2] = -$tm[1];
11173  $tm[3] = $tm[0];
11174  $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
11175  $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
11176  //rotate the coordinate system around ($x,$y)
11177  $this->Transform($tm);
11178  }
11179 
11189  public function SkewX($angle_x, $x='', $y='') {
11190  $this->Skew($angle_x, 0, $x, $y);
11191  }
11192 
11202  public function SkewY($angle_y, $x='', $y='') {
11203  $this->Skew(0, $angle_y, $x, $y);
11204  }
11205 
11216  public function Skew($angle_x, $angle_y, $x='', $y='') {
11217  if ($x === '') {
11218  $x = $this->x;
11219  }
11220  if ($y === '') {
11221  $y = $this->y;
11222  }
11223  if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
11224  $this->Error('Please use values between -90 and +90 degrees for Skewing.');
11225  }
11226  $x *= $this->k;
11227  $y = ($this->h - $y) * $this->k;
11228  //calculate elements of transformation matrix
11229  $tm = array();
11230  $tm[0] = 1;
11231  $tm[1] = tan(deg2rad($angle_y));
11232  $tm[2] = tan(deg2rad($angle_x));
11233  $tm[3] = 1;
11234  $tm[4] = -$tm[2] * $y;
11235  $tm[5] = -$tm[1] * $x;
11236  //skew the coordinate system
11237  $this->Transform($tm);
11238  }
11239 
11247  protected function Transform($tm) {
11248  if ($this->state != 2) {
11249  return;
11250  }
11251  $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
11252  // add tranformation matrix
11253  $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
11254  // update transformation mark
11255  if ($this->inxobj) {
11256  // we are inside an XObject template
11257  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
11258  $key = key($this->xobjects[$this->xobjid]['transfmrk']);
11259  $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
11260  }
11261  } elseif (end($this->transfmrk[$this->page]) !== false) {
11262  $key = key($this->transfmrk[$this->page]);
11263  $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
11264  }
11265  }
11266 
11267  // END TRANSFORMATIONS SECTION -------------------------
11268 
11269  // START GRAPHIC FUNCTIONS SECTION ---------------------
11270  // The following section is based on the code provided by David Hernandez Sanz
11271 
11279  public function SetLineWidth($width) {
11280  //Set line width
11281  $this->LineWidth = $width;
11282  $this->linestyleWidth = sprintf('%F w', ($width * $this->k));
11283  if ($this->state == 2) {
11284  $this->_out($this->linestyleWidth);
11285  }
11286  }
11287 
11295  public function GetLineWidth() {
11296  return $this->LineWidth;
11297  }
11298 
11322  public function SetLineStyle($style, $ret=false) {
11323  $s = ''; // string to be returned
11324  if (!is_array($style)) {
11325  return;
11326  }
11327  if (isset($style['width'])) {
11328  $this->LineWidth = $style['width'];
11329  $this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k));
11330  $s .= $this->linestyleWidth.' ';
11331  }
11332  if (isset($style['cap'])) {
11333  $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
11334  if (isset($ca[$style['cap']])) {
11335  $this->linestyleCap = $ca[$style['cap']].' J';
11336  $s .= $this->linestyleCap.' ';
11337  }
11338  }
11339  if (isset($style['join'])) {
11340  $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
11341  if (isset($ja[$style['join']])) {
11342  $this->linestyleJoin = $ja[$style['join']].' j';
11343  $s .= $this->linestyleJoin.' ';
11344  }
11345  }
11346  if (isset($style['dash'])) {
11347  $dash_string = '';
11348  if ($style['dash']) {
11349  if (preg_match('/^.+,/', $style['dash']) > 0) {
11350  $tab = explode(',', $style['dash']);
11351  } else {
11352  $tab = array($style['dash']);
11353  }
11354  $dash_string = '';
11355  foreach ($tab as $i => $v) {
11356  if ($i) {
11357  $dash_string .= ' ';
11358  }
11359  $dash_string .= sprintf('%F', $v);
11360  }
11361  }
11362  if (!isset($style['phase']) OR !$style['dash']) {
11363  $style['phase'] = 0;
11364  }
11365  $this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']);
11366  $s .= $this->linestyleDash.' ';
11367  }
11368  if (isset($style['color'])) {
11369  $s .= $this->SetDrawColorArray($style['color'], true).' ';
11370  }
11371  if (!$ret AND ($this->state == 2)) {
11372  $this->_out($s);
11373  }
11374  return $s;
11375  }
11376 
11384  protected function _outPoint($x, $y) {
11385  if ($this->state == 2) {
11386  $this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k)));
11387  }
11388  }
11389 
11398  protected function _outLine($x, $y) {
11399  if ($this->state == 2) {
11400  $this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k)));
11401  }
11402  }
11403 
11414  protected function _outRect($x, $y, $w, $h, $op) {
11415  if ($this->state == 2) {
11416  $this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k), (($this->h - $y) * $this->k), ($w * $this->k), (-$h * $this->k), $op));
11417  }
11418  }
11419 
11432  protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
11433  if ($this->state == 2) {
11434  $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)));
11435  }
11436  }
11437 
11448  protected function _outCurveV($x2, $y2, $x3, $y3) {
11449  if ($this->state == 2) {
11450  $this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11451  }
11452  }
11453 
11464  protected function _outCurveY($x1, $y1, $x3, $y3) {
11465  if ($this->state == 2) {
11466  $this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11467  }
11468  }
11469 
11481  public function Line($x1, $y1, $x2, $y2, $style=array()) {
11482  if ($this->state != 2) {
11483  return;
11484  }
11485  if (is_array($style)) {
11486  $this->SetLineStyle($style);
11487  }
11488  $this->_outPoint($x1, $y1);
11489  $this->_outLine($x2, $y2);
11490  $this->_out('S');
11491  }
11492 
11511  public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
11512  if ($this->state != 2) {
11513  return;
11514  }
11515  if (empty($style)) {
11516  $style = 'S';
11517  }
11518  if (!(strpos($style, 'F') === false) AND !empty($fill_color)) {
11519  // set background color
11520  $this->SetFillColorArray($fill_color);
11521  }
11522  if (!empty($border_style)) {
11523  if (isset($border_style['all']) AND !empty($border_style['all'])) {
11524  //set global style for border
11525  $this->SetLineStyle($border_style['all']);
11526  $border_style = array();
11527  } else {
11528  // remove stroke operator from style
11529  $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*' );
11530  if (isset($opnostroke[$style])) {
11531  $style = $opnostroke[$style];
11532  }
11533  }
11534  }
11535  if (!empty($style)) {
11537  $this->_outRect($x, $y, $w, $h, $op);
11538  }
11539  if (!empty($border_style)) {
11540  $border_style2 = array();
11541  foreach ($border_style as $line => $value) {
11542  $length = strlen($line);
11543  for ($i = 0; $i < $length; ++$i) {
11544  $border_style2[$line[$i]] = $value;
11545  }
11546  }
11547  $border_style = $border_style2;
11548  if (isset($border_style['L']) AND $border_style['L']) {
11549  $this->Line($x, $y, $x, $y + $h, $border_style['L']);
11550  }
11551  if (isset($border_style['T']) AND $border_style['T']) {
11552  $this->Line($x, $y, $x + $w, $y, $border_style['T']);
11553  }
11554  if (isset($border_style['R']) AND $border_style['R']) {
11555  $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
11556  }
11557  if (isset($border_style['B']) AND $border_style['B']) {
11558  $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
11559  }
11560  }
11561  }
11562 
11582  public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
11583  if ($this->state != 2) {
11584  return;
11585  }
11586  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11587  $this->SetFillColorArray($fill_color);
11588  }
11590  if ($line_style) {
11591  $this->SetLineStyle($line_style);
11592  }
11593  $this->_outPoint($x0, $y0);
11594  $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11595  $this->_out($op);
11596  }
11597 
11612  public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
11613  if ($this->state != 2) {
11614  return;
11615  }
11616  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11617  $this->SetFillColorArray($fill_color);
11618  }
11620  if ($op == 'f') {
11621  $line_style = array();
11622  }
11623  if ($line_style) {
11624  $this->SetLineStyle($line_style);
11625  }
11626  $this->_outPoint($x0, $y0);
11627  foreach ($segments as $segment) {
11628  list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
11629  $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11630  }
11631  $this->_out($op);
11632  }
11633 
11652  public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11653  if ($this->state != 2) {
11654  return;
11655  }
11656  if (TCPDF_STATIC::empty_string($ry) OR ($ry == 0)) {
11657  $ry = $rx;
11658  }
11659  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11660  $this->SetFillColorArray($fill_color);
11661  }
11663  if ($op == 'f') {
11664  $line_style = array();
11665  }
11666  if ($line_style) {
11667  $this->SetLineStyle($line_style);
11668  }
11669  $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
11670  $this->_out($op);
11671  }
11672 
11693  protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
11694  if (($rx <= 0) OR ($ry < 0)) {
11695  return;
11696  }
11697  $k = $this->k;
11698  if ($nc < 2) {
11699  $nc = 2;
11700  }
11701  $xmin = 2147483647;
11702  $ymin = 2147483647;
11703  $xmax = 0;
11704  $ymax = 0;
11705  if ($pie) {
11706  // center of the arc
11707  $this->_outPoint($xc, $yc);
11708  }
11709  $xang = deg2rad((float) $xang);
11710  $angs = deg2rad((float) $angs);
11711  $angf = deg2rad((float) $angf);
11712  if ($svg) {
11713  $as = $angs;
11714  $af = $angf;
11715  } else {
11716  $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
11717  $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
11718  }
11719  if ($as < 0) {
11720  $as += (2 * M_PI);
11721  }
11722  if ($af < 0) {
11723  $af += (2 * M_PI);
11724  }
11725  if ($ccw AND ($as > $af)) {
11726  // reverse rotation
11727  $as -= (2 * M_PI);
11728  } elseif (!$ccw AND ($as < $af)) {
11729  // reverse rotation
11730  $af -= (2 * M_PI);
11731  }
11732  $total_angle = ($af - $as);
11733  if ($nc < 2) {
11734  $nc = 2;
11735  }
11736  // total arcs to draw
11737  $nc *= (2 * abs($total_angle) / M_PI);
11738  $nc = round($nc) + 1;
11739  // angle of each arc
11740  $arcang = ($total_angle / $nc);
11741  // center point in PDF coordinates
11742  $x0 = $xc;
11743  $y0 = ($this->h - $yc);
11744  // starting angle
11745  $ang = $as;
11746  $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
11747  $cos_xang = cos($xang);
11748  $sin_xang = sin($xang);
11749  $cos_ang = cos($ang);
11750  $sin_ang = sin($ang);
11751  // first arc point
11752  $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11753  $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11754  // first Bezier control point
11755  $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11756  $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11757  if ($pie) {
11758  // line from center to arc starting point
11759  $this->_outLine($px1, $this->h - $py1);
11760  } elseif ($startpoint) {
11761  // arc starting point
11762  $this->_outPoint($px1, $this->h - $py1);
11763  }
11764  // draw arcs
11765  for ($i = 1; $i <= $nc; ++$i) {
11766  // starting angle
11767  $ang = $as + ($i * $arcang);
11768  if ($i == $nc) {
11769  $ang = $af;
11770  }
11771  $cos_ang = cos($ang);
11772  $sin_ang = sin($ang);
11773  // second arc point
11774  $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11775  $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11776  // second Bezier control point
11777  $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11778  $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11779  // draw arc
11780  $cx1 = ($px1 + $qx1);
11781  $cy1 = ($this->h - ($py1 + $qy1));
11782  $cx2 = ($px2 - $qx2);
11783  $cy2 = ($this->h - ($py2 - $qy2));
11784  $cx3 = $px2;
11785  $cy3 = ($this->h - $py2);
11786  $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
11787  // get bounding box coordinates
11788  $xmin = min($xmin, $cx1, $cx2, $cx3);
11789  $ymin = min($ymin, $cy1, $cy2, $cy3);
11790  $xmax = max($xmax, $cx1, $cx2, $cx3);
11791  $ymax = max($ymax, $cy1, $cy2, $cy3);
11792  // move to next point
11793  $px1 = $px2;
11794  $py1 = $py2;
11795  $qx1 = $qx2;
11796  $qy1 = $qy2;
11797  }
11798  if ($pie) {
11799  $this->_outLine($xc, $yc);
11800  // get bounding box coordinates
11801  $xmin = min($xmin, $xc);
11802  $ymin = min($ymin, $yc);
11803  $xmax = max($xmax, $xc);
11804  $ymax = max($ymax, $yc);
11805  }
11806  return array($xmin, $ymin, $xmax, $ymax);
11807  }
11808 
11824  public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11825  $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
11826  }
11827 
11842  public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
11843  $this->Polygon($p, $style, $line_style, $fill_color, false);
11844  }
11845 
11861  public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
11862  if ($this->state != 2) {
11863  return;
11864  }
11865  $nc = count($p); // number of coordinates
11866  $np = $nc / 2; // number of points
11867  if ($closed) {
11868  // close polygon by adding the first 2 points at the end (one line)
11869  for ($i = 0; $i < 4; ++$i) {
11870  $p[$nc + $i] = $p[$i];
11871  }
11872  // copy style for the last added line
11873  if (isset($line_style[0])) {
11874  $line_style[$np] = $line_style[0];
11875  }
11876  $nc += 4;
11877  }
11878  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11879  $this->SetFillColorArray($fill_color);
11880  }
11882  if ($op == 'f') {
11883  $line_style = array();
11884  }
11885  $draw = true;
11886  if ($line_style) {
11887  if (isset($line_style['all'])) {
11888  $this->SetLineStyle($line_style['all']);
11889  } else {
11890  $draw = false;
11891  if ($op == 'B') {
11892  // draw fill
11893  $op = 'f';
11894  $this->_outPoint($p[0], $p[1]);
11895  for ($i = 2; $i < $nc; $i = $i + 2) {
11896  $this->_outLine($p[$i], $p[$i + 1]);
11897  }
11898  $this->_out($op);
11899  }
11900  // draw outline
11901  $this->_outPoint($p[0], $p[1]);
11902  for ($i = 2; $i < $nc; $i = $i + 2) {
11903  $line_num = ($i / 2) - 1;
11904  if (isset($line_style[$line_num])) {
11905  if ($line_style[$line_num] != 0) {
11906  if (is_array($line_style[$line_num])) {
11907  $this->_out('S');
11908  $this->SetLineStyle($line_style[$line_num]);
11909  $this->_outPoint($p[$i - 2], $p[$i - 1]);
11910  $this->_outLine($p[$i], $p[$i + 1]);
11911  $this->_out('S');
11912  $this->_outPoint($p[$i], $p[$i + 1]);
11913  } else {
11914  $this->_outLine($p[$i], $p[$i + 1]);
11915  }
11916  }
11917  } else {
11918  $this->_outLine($p[$i], $p[$i + 1]);
11919  }
11920  }
11921  $this->_out($op);
11922  }
11923  }
11924  if ($draw) {
11925  $this->_outPoint($p[0], $p[1]);
11926  for ($i = 2; $i < $nc; $i = $i + 2) {
11927  $this->_outLine($p[$i], $p[$i + 1]);
11928  }
11929  $this->_out($op);
11930  }
11931  }
11932 
11962  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()) {
11963  if (3 > $ns) {
11964  $ns = 3;
11965  }
11966  if ($draw_circle) {
11967  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
11968  }
11969  $p = array();
11970  for ($i = 0; $i < $ns; ++$i) {
11971  $a = $angle + ($i * 360 / $ns);
11972  $a_rad = deg2rad((float) $a);
11973  $p[] = $x0 + ($r * sin($a_rad));
11974  $p[] = $y0 + ($r * cos($a_rad));
11975  }
11976  $this->Polygon($p, $style, $line_style, $fill_color);
11977  }
11978 
12010  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()) {
12011  if ($nv < 2) {
12012  $nv = 2;
12013  }
12014  if ($draw_circle) {
12015  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12016  }
12017  $p2 = array();
12018  $visited = array();
12019  for ($i = 0; $i < $nv; ++$i) {
12020  $a = $angle + ($i * 360 / $nv);
12021  $a_rad = deg2rad((float) $a);
12022  $p2[] = $x0 + ($r * sin($a_rad));
12023  $p2[] = $y0 + ($r * cos($a_rad));
12024  $visited[] = false;
12025  }
12026  $p = array();
12027  $i = 0;
12028  do {
12029  $p[] = $p2[$i * 2];
12030  $p[] = $p2[($i * 2) + 1];
12031  $visited[$i] = true;
12032  $i += $ng;
12033  $i %= $nv;
12034  } while (!$visited[$i]);
12035  $this->Polygon($p, $style, $line_style, $fill_color);
12036  }
12037 
12052  public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12053  $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
12054  }
12055 
12071  public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12072  if ($this->state != 2) {
12073  return;
12074  }
12075  if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
12076  // Not rounded
12077  $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
12078  return;
12079  }
12080  // Rounded
12081  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12082  $this->SetFillColorArray($fill_color);
12083  }
12085  if ($op == 'f') {
12086  $border_style = array();
12087  }
12088  if ($border_style) {
12089  $this->SetLineStyle($border_style);
12090  }
12091  $MyArc = 4 / 3 * (sqrt(2) - 1);
12092  $this->_outPoint($x + $rx, $y);
12093  $xc = $x + $w - $rx;
12094  $yc = $y + $ry;
12095  $this->_outLine($xc, $y);
12096  if ($round_corner[0]) {
12097  $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
12098  } else {
12099  $this->_outLine($x + $w, $y);
12100  }
12101  $xc = $x + $w - $rx;
12102  $yc = $y + $h - $ry;
12103  $this->_outLine($x + $w, $yc);
12104  if ($round_corner[1]) {
12105  $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
12106  } else {
12107  $this->_outLine($x + $w, $y + $h);
12108  }
12109  $xc = $x + $rx;
12110  $yc = $y + $h - $ry;
12111  $this->_outLine($xc, $y + $h);
12112  if ($round_corner[2]) {
12113  $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
12114  } else {
12115  $this->_outLine($x, $y + $h);
12116  }
12117  $xc = $x + $rx;
12118  $yc = $y + $ry;
12119  $this->_outLine($x, $yc);
12120  if ($round_corner[3]) {
12121  $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
12122  } else {
12123  $this->_outLine($x, $y);
12124  $this->_outLine($x + $rx, $y);
12125  }
12126  $this->_out($op);
12127  }
12128 
12141  public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
12142  // getting arrow direction angle
12143  // 0 deg angle is when both arms go along X axis. angle grows clockwise.
12144  $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
12145  if ($dir_angle < 0) {
12146  $dir_angle += (2 * M_PI);
12147  }
12148  $arm_angle = deg2rad($arm_angle);
12149  $sx1 = $x1;
12150  $sy1 = $y1;
12151  if ($head_style > 0) {
12152  // calculate the stopping point for the arrow shaft
12153  $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
12154  $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
12155  }
12156  // main arrow line / shaft
12157  $this->Line($x0, $y0, $sx1, $sy1);
12158  // left arrowhead arm tip
12159  $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
12160  $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
12161  // right arrowhead arm tip
12162  $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
12163  $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
12164  $mode = 'D';
12165  $style = array();
12166  switch ($head_style) {
12167  case 0: {
12168  // draw only arrowhead arms
12169  $mode = 'D';
12170  $style = array(1, 1, 0);
12171  break;
12172  }
12173  case 1: {
12174  // draw closed arrowhead, but no fill
12175  $mode = 'D';
12176  break;
12177  }
12178  case 2: {
12179  // closed and filled arrowhead
12180  $mode = 'DF';
12181  break;
12182  }
12183  case 3: {
12184  // filled arrowhead
12185  $mode = 'F';
12186  break;
12187  }
12188  }
12189  $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
12190  }
12191 
12192  // END GRAPHIC FUNCTIONS SECTION -----------------------
12193 
12206  public function setDestination($name, $y=-1, $page='', $x=-1) {
12207  // remove unsupported characters
12208  $name = TCPDF_STATIC::encodeNameObject($name);
12209  if (TCPDF_STATIC::empty_string($name)) {
12210  return false;
12211  }
12212  if ($y == -1) {
12213  $y = $this->GetY();
12214  } elseif ($y < 0) {
12215  $y = 0;
12216  } elseif ($y > $this->h) {
12217  $y = $this->h;
12218  }
12219  if ($x == -1) {
12220  $x = $this->GetX();
12221  } elseif ($x < 0) {
12222  $x = 0;
12223  } elseif ($x > $this->w) {
12224  $x = $this->w;
12225  }
12226  $fixed = false;
12227  if (!empty($page) AND ($page[0] == '*')) {
12228  $page = intval(substr($page, 1));
12229  // this page number will not be changed when moving/add/deleting pages
12230  $fixed = true;
12231  }
12232  if (empty($page)) {
12233  $page = $this->PageNo();
12234  if (empty($page)) {
12235  return;
12236  }
12237  }
12238  $this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed);
12239  return $name;
12240  }
12241 
12249  public function getDestination() {
12250  return $this->dests;
12251  }
12252 
12259  protected function _putdests() {
12260  if (empty($this->dests)) {
12261  return;
12262  }
12263  $this->n_dests = $this->_newobj();
12264  $out = ' <<';
12265  foreach($this->dests as $name => $o) {
12266  $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)));
12267  }
12268  $out .= ' >>';
12269  $out .= "\n".'endobj';
12270  $this->_out($out);
12271  }
12272 
12285  public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12286  $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link);
12287  }
12288 
12302  public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12303  if ($level < 0) {
12304  $level = 0;
12305  }
12306  if (isset($this->outlines[0])) {
12307  $lastoutline = end($this->outlines);
12308  $maxlevel = $lastoutline['l'] + 1;
12309  } else {
12310  $maxlevel = 0;
12311  }
12312  if ($level > $maxlevel) {
12313  $level = $maxlevel;
12314  }
12315  if ($y == -1) {
12316  $y = $this->GetY();
12317  } elseif ($y < 0) {
12318  $y = 0;
12319  } elseif ($y > $this->h) {
12320  $y = $this->h;
12321  }
12322  if ($x == -1) {
12323  $x = $this->GetX();
12324  } elseif ($x < 0) {
12325  $x = 0;
12326  } elseif ($x > $this->w) {
12327  $x = $this->w;
12328  }
12329  $fixed = false;
12330  if (!empty($page) AND ($page[0] == '*')) {
12331  $page = intval(substr($page, 1));
12332  // this page number will not be changed when moving/add/deleting pages
12333  $fixed = true;
12334  }
12335  if (empty($page)) {
12336  $page = $this->PageNo();
12337  if (empty($page)) {
12338  return;
12339  }
12340  }
12341  $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed, 's' => strtoupper($style), 'c' => $color, 'u' => $link);
12342  }
12343 
12349  protected function sortBookmarks() {
12350  // get sorting columns
12351  $outline_p = array();
12352  $outline_y = array();
12353  foreach ($this->outlines as $key => $row) {
12354  $outline_p[$key] = $row['p'];
12355  $outline_k[$key] = $key;
12356  }
12357  // sort outlines by page and original position
12358  array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
12359  }
12360 
12367  protected function _putbookmarks() {
12368  $nb = count($this->outlines);
12369  if ($nb == 0) {
12370  return;
12371  }
12372  // sort bookmarks
12373  $this->sortBookmarks();
12374  $lru = array();
12375  $level = 0;
12376  foreach ($this->outlines as $i => $o) {
12377  if ($o['l'] > 0) {
12378  $parent = $lru[($o['l'] - 1)];
12379  //Set parent and last pointers
12380  $this->outlines[$i]['parent'] = $parent;
12381  $this->outlines[$parent]['last'] = $i;
12382  if ($o['l'] > $level) {
12383  //Level increasing: set first pointer
12384  $this->outlines[$parent]['first'] = $i;
12385  }
12386  } else {
12387  $this->outlines[$i]['parent'] = $nb;
12388  }
12389  if (($o['l'] <= $level) AND ($i > 0)) {
12390  //Set prev and next pointers
12391  $prev = $lru[$o['l']];
12392  $this->outlines[$prev]['next'] = $i;
12393  $this->outlines[$i]['prev'] = $prev;
12394  }
12395  $lru[$o['l']] = $i;
12396  $level = $o['l'];
12397  }
12398  //Outline items
12399  $n = $this->n + 1;
12400  $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';
12401  foreach ($this->outlines as $i => $o) {
12402  $oid = $this->_newobj();
12403  // covert HTML title to string
12404  $title = preg_replace($nltags, "\n", $o['t']);
12405  $title = preg_replace("/[\r]+/si", '', $title);
12406  $title = preg_replace("/[\n]+/si", "\n", $title);
12407  $title = strip_tags($title);
12408  $title = $this->stringTrim($title);
12409  $out = '<</Title '.$this->_textstring($title, $oid);
12410  $out .= ' /Parent '.($n + $o['parent']).' 0 R';
12411  if (isset($o['prev'])) {
12412  $out .= ' /Prev '.($n + $o['prev']).' 0 R';
12413  }
12414  if (isset($o['next'])) {
12415  $out .= ' /Next '.($n + $o['next']).' 0 R';
12416  }
12417  if (isset($o['first'])) {
12418  $out .= ' /First '.($n + $o['first']).' 0 R';
12419  }
12420  if (isset($o['last'])) {
12421  $out .= ' /Last '.($n + $o['last']).' 0 R';
12422  }
12423  if (isset($o['u']) AND !empty($o['u'])) {
12424  // link
12425  if (is_string($o['u'])) {
12426  if ($o['u'][0] == '#') {
12427  // internal destination
12428  $out .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($o['u'], 1));
12429  } elseif ($o['u'][0] == '%') {
12430  // embedded PDF file
12431  $filename = basename(substr($o['u'], 1));
12432  $out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
12433  } elseif ($o['u'][0] == '*') {
12434  // embedded generic file
12435  $filename = basename(substr($o['u'], 1));
12436  $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});';
12437  $out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>';
12438  } else {
12439  // external URI link
12440  $out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>';
12441  }
12442  } elseif (isset($this->links[$o['u']])) {
12443  // internal link ID
12444  $l = $this->links[$o['u']];
12445  if (isset($this->page_obj_id[($l['p'])])) {
12446  $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)));
12447  }
12448  }
12449  } elseif (isset($this->page_obj_id[($o['p'])])) {
12450  // link to a page
12451  $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)));
12452  }
12453  // set font style
12454  $style = 0;
12455  if (!empty($o['s'])) {
12456  // bold
12457  if (strpos($o['s'], 'B') !== false) {
12458  $style |= 2;
12459  }
12460  // oblique
12461  if (strpos($o['s'], 'I') !== false) {
12462  $style |= 1;
12463  }
12464  }
12465  $out .= sprintf(' /F %d', $style);
12466  // set bookmark color
12467  if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
12468  $color = array_values($o['c']);
12469  $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
12470  } else {
12471  // black
12472  $out .= ' /C [0.0 0.0 0.0]';
12473  }
12474  $out .= ' /Count 0'; // normally closed item
12475  $out .= ' >>';
12476  $out .= "\n".'endobj';
12477  $this->_out($out);
12478  }
12479  //Outline root
12480  $this->OutlineRoot = $this->_newobj();
12481  $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj');
12482  }
12483 
12484  // --- JAVASCRIPT ------------------------------------------------------
12485 
12493  public function IncludeJS($script) {
12494  $this->javascript .= $script;
12495  }
12496 
12506  public function addJavascriptObject($script, $onload=false) {
12507  if ($this->pdfa_mode) {
12508  // javascript is not allowed in PDF/A mode
12509  return false;
12510  }
12511  ++$this->n;
12512  $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload);
12513  return $this->n;
12514  }
12515 
12522  protected function _putjavascript() {
12523  if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) {
12524  return;
12525  }
12526  if (strpos($this->javascript, 'this.addField') > 0) {
12527  if (!$this->ur['enabled']) {
12528  //$this->setUserRights();
12529  }
12530  // the following two lines are used to avoid form fields duplication after saving
12531  // The addField method only works when releasing user rights (UR3)
12532  $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
12533  $jsb = "getField('tcpdfdocsaved').value='saved';";
12534  $this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
12535  }
12536  // name tree for javascript
12537  $this->n_js = '<< /Names [';
12538  if (!empty($this->javascript)) {
12539  $this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
12540  }
12541  if (!empty($this->js_objects)) {
12542  foreach ($this->js_objects as $key => $val) {
12543  if ($val['onload']) {
12544  $this->n_js .= ' (JS'.$key.') '.$key.' 0 R';
12545  }
12546  }
12547  }
12548  $this->n_js .= ' ] >>';
12549  // default Javascript object
12550  if (!empty($this->javascript)) {
12551  $obj_id = $this->_newobj();
12552  $out = '<< /S /JavaScript';
12553  $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id);
12554  $out .= ' >>';
12555  $out .= "\n".'endobj';
12556  $this->_out($out);
12557  }
12558  // additional Javascript objects
12559  if (!empty($this->js_objects)) {
12560  foreach ($this->js_objects as $key => $val) {
12561  $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
12562  $this->_out($out);
12563  }
12564  }
12565  }
12566 
12580  protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
12581  if ($this->rtl) {
12582  $x = $x - $w;
12583  }
12584  // the followind avoid fields duplication after saving the document
12585  $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {";
12586  $k = $this->k;
12587  $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";
12588  $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
12589  while (list($key, $val) = each($prop)) {
12590  if (strcmp(substr($key, -5), 'Color') == 0) {
12591  $val = TCPDF_COLORS::_JScolor($val);
12592  } else {
12593  $val = "'".$val."'";
12594  }
12595  $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
12596  }
12597  if ($this->rtl) {
12598  $this->x -= $w;
12599  } else {
12600  $this->x += $w;
12601  }
12602  $this->javascript .= '}';
12603  }
12604 
12605  // --- FORM FIELDS -----------------------------------------------------
12606 
12607 
12608 
12616  public function setFormDefaultProp($prop=array()) {
12617  $this->default_form_prop = $prop;
12618  }
12619 
12627  public function getFormDefaultProp() {
12628  return $this->default_form_prop;
12629  }
12630 
12645  public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12646  if ($x === '') {
12647  $x = $this->x;
12648  }
12649  if ($y === '') {
12650  $y = $this->y;
12651  }
12652  // check page for no-write regions and adapt page margins if necessary
12653  list($x, $y) = $this->checkPageRegions($h, $x, $y);
12654  if ($js) {
12655  $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
12656  return;
12657  }
12658  // get default style
12659  $prop = array_merge($this->getFormDefaultProp(), $prop);
12660  // get annotation data
12661  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12662  // set default appearance stream
12663  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12664  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12665  $popt['da'] = $fontstyle;
12666  // build appearance stream
12667  $popt['ap'] = array();
12668  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12669  $text = '';
12670  if (isset($prop['value']) AND !empty($prop['value'])) {
12671  $text = $prop['value'];
12672  } elseif (isset($opt['v']) AND !empty($opt['v'])) {
12673  $text = $opt['v'];
12674  }
12675  $tmpid = $this->startTemplate($w, $h, false);
12676  $align = '';
12677  if (isset($popt['q'])) {
12678  switch ($popt['q']) {
12679  case 0: {
12680  $align = 'L';
12681  break;
12682  }
12683  case 1: {
12684  $align = 'C';
12685  break;
12686  }
12687  case 2: {
12688  $align = 'R';
12689  break;
12690  }
12691  default: {
12692  $align = '';
12693  break;
12694  }
12695  }
12696  }
12697  $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12698  $this->endTemplate();
12699  --$this->n;
12700  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12701  unset($this->xobjects[$tmpid]);
12702  $popt['ap']['n'] .= 'Q EMC';
12703  // merge options
12704  $opt = array_merge($popt, $opt);
12705  // remove some conflicting options
12706  unset($opt['bs']);
12707  // set remaining annotation data
12708  $opt['Subtype'] = 'Widget';
12709  $opt['ft'] = 'Tx';
12710  $opt['t'] = $name;
12711  // Additional annotation's parameters (check _putannotsobj() method):
12712  //$opt['f']
12713  //$opt['as']
12714  //$opt['bs']
12715  //$opt['be']
12716  //$opt['c']
12717  //$opt['border']
12718  //$opt['h']
12719  //$opt['mk'];
12720  //$opt['mk']['r']
12721  //$opt['mk']['bc'];
12722  //$opt['mk']['bg'];
12723  unset($opt['mk']['ca']);
12724  unset($opt['mk']['rc']);
12725  unset($opt['mk']['ac']);
12726  unset($opt['mk']['i']);
12727  unset($opt['mk']['ri']);
12728  unset($opt['mk']['ix']);
12729  unset($opt['mk']['if']);
12730  //$opt['mk']['if']['sw'];
12731  //$opt['mk']['if']['s'];
12732  //$opt['mk']['if']['a'];
12733  //$opt['mk']['if']['fb'];
12734  unset($opt['mk']['tp']);
12735  //$opt['tu']
12736  //$opt['tm']
12737  //$opt['ff']
12738  //$opt['v']
12739  //$opt['dv']
12740  //$opt['a']
12741  //$opt['aa']
12742  //$opt['q']
12743  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12744  if ($this->rtl) {
12745  $this->x -= $w;
12746  } else {
12747  $this->x += $w;
12748  }
12749  }
12750 
12766  public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
12767  if ($x === '') {
12768  $x = $this->x;
12769  }
12770  if ($y === '') {
12771  $y = $this->y;
12772  }
12773  // check page for no-write regions and adapt page margins if necessary
12774  list($x, $y) = $this->checkPageRegions($w, $x, $y);
12775  if ($js) {
12776  $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
12777  return;
12778  }
12779  if (TCPDF_STATIC::empty_string($onvalue)) {
12780  $onvalue = 'On';
12781  }
12782  if ($checked) {
12783  $defval = $onvalue;
12784  } else {
12785  $defval = 'Off';
12786  }
12787  // set font
12788  $font = 'zapfdingbats';
12789  if ($this->pdfa_mode) {
12790  // all fonts must be embedded
12791  $font = 'pdfa'.$font;
12792  }
12793  $this->AddFont($font);
12794  $tmpfont = $this->getFontBuffer($font);
12795  // set data for parent group
12796  if (!isset($this->radiobutton_groups[$this->page])) {
12797  $this->radiobutton_groups[$this->page] = array();
12798  }
12799  if (!isset($this->radiobutton_groups[$this->page][$name])) {
12800  $this->radiobutton_groups[$this->page][$name] = array();
12801  ++$this->n;
12802  $this->radiobutton_groups[$this->page][$name]['n'] = $this->n;
12803  $this->radio_groups[] = $this->n;
12804  }
12805  $kid = ($this->n + 1);
12806  // save object ID to be added on Kids entry on parent object
12807  $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval);
12808  // get default style
12809  $prop = array_merge($this->getFormDefaultProp(), $prop);
12810  $prop['NoToggleToOff'] = 'true';
12811  $prop['Radio'] = 'true';
12812  $prop['borderStyle'] = 'inset';
12813  // get annotation data
12814  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12815  // set additional default options
12816  $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
12817  $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
12818  $popt['da'] = $fontstyle;
12819  // build appearance stream
12820  $popt['ap'] = array();
12821  $popt['ap']['n'] = array();
12822  $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k);
12823  $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
12824  $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);
12825  $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);
12826  if (!isset($popt['mk'])) {
12827  $popt['mk'] = array();
12828  }
12829  $popt['mk']['ca'] = '(l)';
12830  // merge options
12831  $opt = array_merge($popt, $opt);
12832  // set remaining annotation data
12833  $opt['Subtype'] = 'Widget';
12834  $opt['ft'] = 'Btn';
12835  if ($checked) {
12836  $opt['v'] = array('/'.$onvalue);
12837  $opt['as'] = $onvalue;
12838  } else {
12839  $opt['as'] = 'Off';
12840  }
12841  // store readonly flag
12842  if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) {
12843  $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false;
12844  }
12845  $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64);
12846  $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
12847  if ($this->rtl) {
12848  $this->x -= $w;
12849  } else {
12850  $this->x += $w;
12851  }
12852  }
12853 
12869  public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12870  if ($x === '') {
12871  $x = $this->x;
12872  }
12873  if ($y === '') {
12874  $y = $this->y;
12875  }
12876  // check page for no-write regions and adapt page margins if necessary
12877  list($x, $y) = $this->checkPageRegions($h, $x, $y);
12878  if ($js) {
12879  $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
12880  $s = '';
12881  foreach ($values as $value) {
12882  if (is_array($value)) {
12883  $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12884  } else {
12885  $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12886  }
12887  }
12888  $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12889  return;
12890  }
12891  // get default style
12892  $prop = array_merge($this->getFormDefaultProp(), $prop);
12893  // get annotation data
12894  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12895  // set additional default values
12896  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12897  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12898  $popt['da'] = $fontstyle;
12899  // build appearance stream
12900  $popt['ap'] = array();
12901  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12902  $text = '';
12903  foreach($values as $item) {
12904  if (is_array($item)) {
12905  $text .= $item[1]."\n";
12906  } else {
12907  $text .= $item."\n";
12908  }
12909  }
12910  $tmpid = $this->startTemplate($w, $h, false);
12911  $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12912  $this->endTemplate();
12913  --$this->n;
12914  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12915  unset($this->xobjects[$tmpid]);
12916  $popt['ap']['n'] .= 'Q EMC';
12917  // merge options
12918  $opt = array_merge($popt, $opt);
12919  // set remaining annotation data
12920  $opt['Subtype'] = 'Widget';
12921  $opt['ft'] = 'Ch';
12922  $opt['t'] = $name;
12923  $opt['opt'] = $values;
12924  unset($opt['mk']['ca']);
12925  unset($opt['mk']['rc']);
12926  unset($opt['mk']['ac']);
12927  unset($opt['mk']['i']);
12928  unset($opt['mk']['ri']);
12929  unset($opt['mk']['ix']);
12930  unset($opt['mk']['if']);
12931  unset($opt['mk']['tp']);
12932  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12933  if ($this->rtl) {
12934  $this->x -= $w;
12935  } else {
12936  $this->x += $w;
12937  }
12938  }
12939 
12955  public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12956  if ($x === '') {
12957  $x = $this->x;
12958  }
12959  if ($y === '') {
12960  $y = $this->y;
12961  }
12962  // check page for no-write regions and adapt page margins if necessary
12963  list($x, $y) = $this->checkPageRegions($h, $x, $y);
12964  if ($js) {
12965  $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
12966  $s = '';
12967  foreach ($values as $value) {
12968  if (is_array($value)) {
12969  $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12970  } else {
12971  $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12972  }
12973  }
12974  $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12975  return;
12976  }
12977  // get default style
12978  $prop = array_merge($this->getFormDefaultProp(), $prop);
12979  $prop['Combo'] = true;
12980  // get annotation data
12981  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12982  // set additional default options
12983  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12984  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12985  $popt['da'] = $fontstyle;
12986  // build appearance stream
12987  $popt['ap'] = array();
12988  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12989  $text = '';
12990  foreach($values as $item) {
12991  if (is_array($item)) {
12992  $text .= $item[1]."\n";
12993  } else {
12994  $text .= $item."\n";
12995  }
12996  }
12997  $tmpid = $this->startTemplate($w, $h, false);
12998  $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12999  $this->endTemplate();
13000  --$this->n;
13001  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13002  unset($this->xobjects[$tmpid]);
13003  $popt['ap']['n'] .= 'Q EMC';
13004  // merge options
13005  $opt = array_merge($popt, $opt);
13006  // set remaining annotation data
13007  $opt['Subtype'] = 'Widget';
13008  $opt['ft'] = 'Ch';
13009  $opt['t'] = $name;
13010  $opt['opt'] = $values;
13011  unset($opt['mk']['ca']);
13012  unset($opt['mk']['rc']);
13013  unset($opt['mk']['ac']);
13014  unset($opt['mk']['i']);
13015  unset($opt['mk']['ri']);
13016  unset($opt['mk']['ix']);
13017  unset($opt['mk']['if']);
13018  unset($opt['mk']['tp']);
13019  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13020  if ($this->rtl) {
13021  $this->x -= $w;
13022  } else {
13023  $this->x += $w;
13024  }
13025  }
13026 
13042  public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
13043  if ($x === '') {
13044  $x = $this->x;
13045  }
13046  if ($y === '') {
13047  $y = $this->y;
13048  }
13049  // check page for no-write regions and adapt page margins if necessary
13050  list($x, $y) = $this->checkPageRegions($w, $x, $y);
13051  if ($js) {
13052  $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
13053  return;
13054  }
13055  if (!isset($prop['value'])) {
13056  $prop['value'] = array('Yes');
13057  }
13058  // get default style
13059  $prop = array_merge($this->getFormDefaultProp(), $prop);
13060  $prop['borderStyle'] = 'inset';
13061  // get annotation data
13062  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13063  // set additional default options
13064  $font = 'zapfdingbats';
13065  if ($this->pdfa_mode) {
13066  // all fonts must be embedded
13067  $font = 'pdfa'.$font;
13068  }
13069  $this->AddFont($font);
13070  $tmpfont = $this->getFontBuffer($font);
13071  $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
13072  $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
13073  $popt['da'] = $fontstyle;
13074  // build appearance stream
13075  $popt['ap'] = array();
13076  $popt['ap']['n'] = array();
13077  $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k);
13078  $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
13079  $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);
13080  $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);
13081  // merge options
13082  $opt = array_merge($popt, $opt);
13083  // set remaining annotation data
13084  $opt['Subtype'] = 'Widget';
13085  $opt['ft'] = 'Btn';
13086  $opt['t'] = $name;
13087  if (TCPDF_STATIC::empty_string($onvalue)) {
13088  $onvalue = 'Yes';
13089  }
13090  $opt['opt'] = array($onvalue);
13091  if ($checked) {
13092  $opt['v'] = array('/Yes');
13093  $opt['as'] = 'Yes';
13094  } else {
13095  $opt['v'] = array('/Off');
13096  $opt['as'] = 'Off';
13097  }
13098  $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
13099  if ($this->rtl) {
13100  $this->x -= $w;
13101  } else {
13102  $this->x += $w;
13103  }
13104  }
13105 
13122  public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13123  if ($x === '') {
13124  $x = $this->x;
13125  }
13126  if ($y === '') {
13127  $y = $this->y;
13128  }
13129  // check page for no-write regions and adapt page margins if necessary
13130  list($x, $y) = $this->checkPageRegions($h, $x, $y);
13131  if ($js) {
13132  $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
13133  $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
13134  $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
13135  $this->javascript .= 'f'.$name.".highlight='push';\n";
13136  $this->javascript .= 'f'.$name.".print=false;\n";
13137  return;
13138  }
13139  // get default style
13140  $prop = array_merge($this->getFormDefaultProp(), $prop);
13141  $prop['Pushbutton'] = 'true';
13142  $prop['highlight'] = 'push';
13143  $prop['display'] = 'display.noPrint';
13144  // get annotation data
13145  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13146  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13147  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13148  $popt['da'] = $fontstyle;
13149  // build appearance stream
13150  $popt['ap'] = array();
13151  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13152  $tmpid = $this->startTemplate($w, $h, false);
13153  $bw = (2 / $this->k); // border width
13154  $border = array(
13155  'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13156  'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
13157  'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13158  'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
13159  $this->SetFillColor(204);
13160  $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
13161  $this->endTemplate();
13162  --$this->n;
13163  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13164  unset($this->xobjects[$tmpid]);
13165  $popt['ap']['n'] .= 'Q EMC';
13166  // set additional default options
13167  if (!isset($popt['mk'])) {
13168  $popt['mk'] = array();
13169  }
13170  $ann_obj_id = ($this->n + 1);
13171  if (!empty($action) AND !is_array($action)) {
13172  $ann_obj_id = ($this->n + 2);
13173  }
13174  $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
13175  $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
13176  $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
13177  // merge options
13178  $opt = array_merge($popt, $opt);
13179  // set remaining annotation data
13180  $opt['Subtype'] = 'Widget';
13181  $opt['ft'] = 'Btn';
13182  $opt['t'] = $caption;
13183  $opt['v'] = $name;
13184  if (!empty($action)) {
13185  if (is_array($action)) {
13186  // form action options as on section 12.7.5 of PDF32000_2008.
13187  $opt['aa'] = '/D <<';
13188  $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
13189  foreach ($action AS $key => $val) {
13190  if (($key == 'S') AND in_array($val, $bmode)) {
13191  $opt['aa'] .= ' /S /'.$val;
13192  } elseif (($key == 'F') AND (!empty($val))) {
13193  $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
13194  } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
13195  $opt['aa'] .= ' /Fields [';
13196  foreach ($val AS $field) {
13197  $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
13198  }
13199  $opt['aa'] .= ']';
13200  } elseif (($key == 'Flags')) {
13201  $ff = 0;
13202  if (is_array($val)) {
13203  foreach ($val AS $flag) {
13204  switch ($flag) {
13205  case 'Include/Exclude': {
13206  $ff += 1 << 0;
13207  break;
13208  }
13209  case 'IncludeNoValueFields': {
13210  $ff += 1 << 1;
13211  break;
13212  }
13213  case 'ExportFormat': {
13214  $ff += 1 << 2;
13215  break;
13216  }
13217  case 'GetMethod': {
13218  $ff += 1 << 3;
13219  break;
13220  }
13221  case 'SubmitCoordinates': {
13222  $ff += 1 << 4;
13223  break;
13224  }
13225  case 'XFDF': {
13226  $ff += 1 << 5;
13227  break;
13228  }
13229  case 'IncludeAppendSaves': {
13230  $ff += 1 << 6;
13231  break;
13232  }
13233  case 'IncludeAnnotations': {
13234  $ff += 1 << 7;
13235  break;
13236  }
13237  case 'SubmitPDF': {
13238  $ff += 1 << 8;
13239  break;
13240  }
13241  case 'CanonicalFormat': {
13242  $ff += 1 << 9;
13243  break;
13244  }
13245  case 'ExclNonUserAnnots': {
13246  $ff += 1 << 10;
13247  break;
13248  }
13249  case 'ExclFKey': {
13250  $ff += 1 << 11;
13251  break;
13252  }
13253  case 'EmbedForm': {
13254  $ff += 1 << 13;
13255  break;
13256  }
13257  }
13258  }
13259  } else {
13260  $ff = intval($val);
13261  }
13262  $opt['aa'] .= ' /Flags '.$ff;
13263  }
13264  }
13265  $opt['aa'] .= ' >>';
13266  } else {
13267  // Javascript action or raw action command
13268  $js_obj_id = $this->addJavascriptObject($action);
13269  $opt['aa'] = '/D '.$js_obj_id.' 0 R';
13270  }
13271  }
13272  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13273  if ($this->rtl) {
13274  $this->x -= $w;
13275  } else {
13276  $this->x += $w;
13277  }
13278  }
13279 
13280  // --- END FORMS FIELDS ------------------------------------------------
13281 
13289  protected function _putsignature() {
13290  if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
13291  return;
13292  }
13293  $sigobjid = ($this->sig_obj_id + 1);
13294  $out = $this->_getobj($sigobjid)."\n";
13295  $out .= '<< /Type /Sig';
13296  $out .= ' /Filter /Adobe.PPKLite';
13297  $out .= ' /SubFilter /adbe.pkcs7.detached';
13298  $out .= ' '.TCPDF_STATIC::$byterange_string;
13299  $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>';
13300  if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
13301  $out .= ' /Reference ['; // array of signature reference dictionaries
13302  $out .= ' << /Type /SigRef';
13303  if ($this->signature_data['cert_type'] > 0) {
13304  $out .= ' /TransformMethod /DocMDP';
13305  $out .= ' /TransformParams <<';
13306  $out .= ' /Type /TransformParams';
13307  $out .= ' /P '.$this->signature_data['cert_type'];
13308  $out .= ' /V /1.2';
13309  } else {
13310  $out .= ' /TransformMethod /UR3';
13311  $out .= ' /TransformParams <<';
13312  $out .= ' /Type /TransformParams';
13313  $out .= ' /V /2.2';
13314  if (!TCPDF_STATIC::empty_string($this->ur['document'])) {
13315  $out .= ' /Document['.$this->ur['document'].']';
13316  }
13317  if (!TCPDF_STATIC::empty_string($this->ur['form'])) {
13318  $out .= ' /Form['.$this->ur['form'].']';
13319  }
13320  if (!TCPDF_STATIC::empty_string($this->ur['signature'])) {
13321  $out .= ' /Signature['.$this->ur['signature'].']';
13322  }
13323  if (!TCPDF_STATIC::empty_string($this->ur['annots'])) {
13324  $out .= ' /Annots['.$this->ur['annots'].']';
13325  }
13326  if (!TCPDF_STATIC::empty_string($this->ur['ef'])) {
13327  $out .= ' /EF['.$this->ur['ef'].']';
13328  }
13329  if (!TCPDF_STATIC::empty_string($this->ur['formex'])) {
13330  $out .= ' /FormEX['.$this->ur['formex'].']';
13331  }
13332  }
13333  $out .= ' >>'; // close TransformParams
13334  // optional digest data (values must be calculated and replaced later)
13335  //$out .= ' /Data ********** 0 R';
13336  //$out .= ' /DigestMethod/MD5';
13337  //$out .= ' /DigestLocation[********** 34]';
13338  //$out .= ' /DigestValue<********************************>';
13339  $out .= ' >>';
13340  $out .= ' ]'; // end of reference
13341  }
13342  if (isset($this->signature_data['info']['Name']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Name'])) {
13343  $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid);
13344  }
13345  if (isset($this->signature_data['info']['Location']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Location'])) {
13346  $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid);
13347  }
13348  if (isset($this->signature_data['info']['Reason']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Reason'])) {
13349  $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid);
13350  }
13351  if (isset($this->signature_data['info']['ContactInfo']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['ContactInfo'])) {
13352  $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid);
13353  }
13354  $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp);
13355  $out .= ' >>';
13356  $out .= "\n".'endobj';
13357  $this->_out($out);
13358  }
13359 
13377  public function setUserRights(
13378  $enable=true,
13379  $document='/FullSave',
13380  $annots='/Create/Delete/Modify/Copy/Import/Export',
13381  $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
13382  $signature='/Modify',
13383  $ef='/Create/Delete/Modify/Import',
13384  $formex='') {
13385  $this->ur['enabled'] = $enable;
13386  $this->ur['document'] = $document;
13387  $this->ur['annots'] = $annots;
13388  $this->ur['form'] = $form;
13389  $this->ur['signature'] = $signature;
13390  $this->ur['ef'] = $ef;
13391  $this->ur['formex'] = $formex;
13392  if (!$this->sign) {
13393  $this->setSignature('', '', '', '', 0, array());
13394  }
13395  }
13396 
13414  public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='') {
13415  // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13416  // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13417  // to convert pfx certificate to pem: openssl
13418  // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
13419  $this->sign = true;
13420  ++$this->n;
13421  $this->sig_obj_id = $this->n; // signature widget
13422  ++$this->n; // signature object ($this->sig_obj_id + 1)
13423  $this->signature_data = array();
13424  if (strlen($signing_cert) == 0) {
13425  $this->Error('Please provide a certificate file and password!');
13426  }
13427  if (strlen($private_key) == 0) {
13428  $private_key = $signing_cert;
13429  }
13430  $this->signature_data['signcert'] = $signing_cert;
13431  $this->signature_data['privkey'] = $private_key;
13432  $this->signature_data['password'] = $private_key_password;
13433  $this->signature_data['extracerts'] = $extracerts;
13434  $this->signature_data['cert_type'] = $cert_type;
13435  $this->signature_data['info'] = $info;
13436  $this->signature_data['approval'] = $approval;
13437  }
13438 
13451  public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13452  $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13453  }
13454 
13467  public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13468  ++$this->n;
13469  $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13470  }
13471 
13485  protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13486  $sigapp = array();
13487  if (($page < 1) OR ($page > $this->numpages)) {
13488  $sigapp['page'] = $this->page;
13489  } else {
13490  $sigapp['page'] = intval($page);
13491  }
13492  if (empty($name)) {
13493  $sigapp['name'] = 'Signature';
13494  } else {
13495  $sigapp['name'] = $name;
13496  }
13497  $a = $x * $this->k;
13498  $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k);
13499  $c = $w * $this->k;
13500  $d = $h * $this->k;
13501  $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d));
13502  return $sigapp;
13503  }
13504 
13517  public function setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='') {
13518  $this->tsa_data = array();
13519  if (!function_exists('curl_init')) {
13520  $this->Error('Please enable cURL PHP extension!');
13521  }
13522  if (strlen($tsa_host) == 0) {
13523  $this->Error('Please specify the host of Time Stamping Authority (TSA)!');
13524  }
13525  $this->tsa_data['tsa_host'] = $tsa_host;
13526  if (is_file($tsa_username)) {
13527  $this->tsa_data['tsa_auth'] = $tsa_username;
13528  } else {
13529  $this->tsa_data['tsa_username'] = $tsa_username;
13530  }
13531  $this->tsa_data['tsa_password'] = $tsa_password;
13532  $this->tsa_data['tsa_cert'] = $tsa_cert;
13533  $this->tsa_timestamp = true;
13534  }
13535 
13545  protected function applyTSA($signature) {
13546  if (!$this->tsa_timestamp) {
13547  return $signature;
13548  }
13549  //@TODO: implement this feature
13550  return $signature;
13551  }
13552 
13560  public function startPageGroup($page='') {
13561  if (empty($page)) {
13562  $page = $this->page + 1;
13563  }
13564  $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1;
13565  }
13566 
13573  public function setStartingPageNumber($num=1) {
13574  $this->starting_page_number = max(0, intval($num));
13575  }
13576 
13584  public function getAliasRightShift() {
13585  // calculate aproximatively the ratio between widths of aliases and replacements.
13586  $ref = '{'.TCPDF_STATIC::$alias_right_shift.'}{'.TCPDF_STATIC::$alias_tot_pages.'}{'.TCPDF_STATIC::$alias_num_page.'}';
13587  $rep = str_repeat(' ', $this->GetNumChars($ref));
13588  $wrep = $this->GetStringWidth($rep);
13589  if ($wrep > 0) {
13590  $wdiff = max(1, ($this->GetStringWidth($ref) / $wrep));
13591  } else {
13592  $wdiff = 1;
13593  }
13594  $sdiff = sprintf('%F', $wdiff);
13595  $alias = TCPDF_STATIC::$alias_right_shift.$sdiff.'}';
13596  if ($this->isUnicodeFont()) {
13597  $alias = '{'.$alias;
13598  }
13599  return $alias;
13600  }
13601 
13610  public function getAliasNbPages() {
13611  if ($this->isUnicodeFont()) {
13612  return '{'.TCPDF_STATIC::$alias_tot_pages.'}';
13613  }
13615  }
13616 
13625  public function getAliasNumPage() {
13626  if ($this->isUnicodeFont()) {
13627  return '{'.TCPDF_STATIC::$alias_num_page.'}';
13628  }
13630  }
13631 
13640  public function getPageGroupAlias() {
13641  if ($this->isUnicodeFont()) {
13642  return '{'.TCPDF_STATIC::$alias_group_tot_pages.'}';
13643  }
13645  }
13646 
13655  public function getPageNumGroupAlias() {
13656  if ($this->isUnicodeFont()) {
13657  return '{'.TCPDF_STATIC::$alias_group_num_page.'}';
13658  }
13660  }
13661 
13668  public function getGroupPageNo() {
13669  return $this->pagegroups[$this->currpagegroup];
13670  }
13671 
13678  public function getGroupPageNoFormatted() {
13680  }
13681 
13688  public function PageNoFormatted() {
13689  return TCPDF_STATIC::formatPageNumber($this->PageNo());
13690  }
13691 
13697  protected function _putocg() {
13698  if (empty($this->pdflayers)) {
13699  return;
13700  }
13701  foreach ($this->pdflayers as $key => $layer) {
13702  $this->pdflayers[$key]['objid'] = $this->_newobj();
13703  $out = '<< /Type /OCG';
13704  $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']);
13705  $out .= ' /Usage <<';
13706  if (isset($layer['print']) AND ($layer['print'] !== NULL)) {
13707  $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>';
13708  }
13709  $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>';
13710  $out .= ' >> >>';
13711  $out .= "\n".'endobj';
13712  $this->_out($out);
13713  }
13714  }
13715 
13725  public function startLayer($name='', $print=true, $view=true, $lock=true) {
13726  if ($this->state != 2) {
13727  return;
13728  }
13729  $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1));
13730  if (empty($name)) {
13731  $name = $layer;
13732  } else {
13733  $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
13734  }
13735  $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view, 'lock' => $lock);
13736  $this->openMarkedContent = true;
13737  $this->_out('/OC /'.$layer.' BDC');
13738  }
13739 
13745  public function endLayer() {
13746  if ($this->state != 2) {
13747  return;
13748  }
13749  if ($this->openMarkedContent) {
13750  // close existing open marked-content layer
13751  $this->_out('EMC');
13752  $this->openMarkedContent = false;
13753  }
13754  }
13755 
13764  public function setVisibility($v) {
13765  if ($this->state != 2) {
13766  return;
13767  }
13768  $this->endLayer();
13769  switch($v) {
13770  case 'print': {
13771  $this->startLayer('Print', true, false);
13772  break;
13773  }
13774  case 'view':
13775  case 'screen': {
13776  $this->startLayer('View', false, true);
13777  break;
13778  }
13779  case 'all': {
13780  $this->_out('');
13781  break;
13782  }
13783  default: {
13784  $this->Error('Incorrect visibility: '.$v);
13785  break;
13786  }
13787  }
13788  }
13789 
13797  protected function addExtGState($parms) {
13798  if ($this->pdfa_mode) {
13799  // transparencies are not allowed in PDF/A mode
13800  return;
13801  }
13802  // check if this ExtGState already exist
13803  foreach ($this->extgstates as $i => $ext) {
13804  if ($ext['parms'] == $parms) {
13805  if ($this->inxobj) {
13806  // we are inside an XObject template
13807  $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext;
13808  }
13809  // return reference to existing ExtGState
13810  return $i;
13811  }
13812  }
13813  $n = (count($this->extgstates) + 1);
13814  $this->extgstates[$n] = array('parms' => $parms);
13815  if ($this->inxobj) {
13816  // we are inside an XObject template
13817  $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n];
13818  }
13819  return $n;
13820  }
13821 
13828  protected function setExtGState($gs) {
13829  if ($this->pdfa_mode OR ($this->state != 2)) {
13830  // transparency is not allowed in PDF/A mode
13831  return;
13832  }
13833  $this->_out(sprintf('/GS%d gs', $gs));
13834  }
13835 
13841  protected function _putextgstates() {
13842  foreach ($this->extgstates as $i => $ext) {
13843  $this->extgstates[$i]['n'] = $this->_newobj();
13844  $out = '<< /Type /ExtGState';
13845  foreach ($ext['parms'] as $k => $v) {
13846  if (is_float($v)) {
13847  $v = sprintf('%F', $v);
13848  } elseif ($v === true) {
13849  $v = 'true';
13850  } elseif ($v === false) {
13851  $v = 'false';
13852  }
13853  $out .= ' /'.$k.' '.$v;
13854  }
13855  $out .= ' >>';
13856  $out .= "\n".'endobj';
13857  $this->_out($out);
13858  }
13859  }
13860 
13870  public function setOverprint($stroking=true, $nonstroking='', $mode=0) {
13871  if ($this->state != 2) {
13872  return;
13873  }
13874  $stroking = $stroking ? true : false;
13875  if (TCPDF_STATIC::empty_string($nonstroking)) {
13876  // default value if not set
13877  $nonstroking = $stroking;
13878  } else {
13879  $nonstroking = $nonstroking ? true : false;
13880  }
13881  if (($mode != 0) AND ($mode != 1)) {
13882  $mode = 0;
13883  }
13884  $this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode);
13885  $gs = $this->addExtGState($this->overprint);
13886  $this->setExtGState($gs);
13887  }
13888 
13896  public function getOverprint() {
13897  return $this->overprint;
13898  }
13899 
13909  public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) {
13910  if ($this->pdfa_mode) {
13911  // transparency is not allowed in PDF/A mode
13912  return;
13913  }
13914  $stroking = floatval($stroking);
13915  if (TCPDF_STATIC::empty_string($nonstroking)) {
13916  // default value if not set
13917  $nonstroking = $stroking;
13918  } else {
13919  $nonstroking = floatval($nonstroking);
13920  }
13921  if ($bm[0] == '/') {
13922  // remove trailing slash
13923  $bm = substr($bm, 1);
13924  }
13925  if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
13926  $bm = 'Normal';
13927  }
13928  $ais = $ais ? true : false;
13929  $this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais);
13930  $gs = $this->addExtGState($this->alpha);
13931  $this->setExtGState($gs);
13932  }
13933 
13941  public function getAlpha() {
13942  return $this->alpha;
13943  }
13944 
13951  public function setJPEGQuality($quality) {
13952  if (($quality < 1) OR ($quality > 100)) {
13953  $quality = 75;
13954  }
13955  $this->jpeg_quality = intval($quality);
13956  }
13957 
13964  public function setDefaultTableColumns($cols=4) {
13965  $this->default_table_columns = intval($cols);
13966  }
13967 
13974  public function setCellHeightRatio($h) {
13975  $this->cell_height_ratio = $h;
13976  }
13977 
13983  public function getCellHeightRatio() {
13984  return $this->cell_height_ratio;
13985  }
13986 
13993  public function setPDFVersion($version='1.7') {
13994  if ($this->pdfa_mode) {
13995  // PDF/A mode
13996  $this->PDFVersion = '1.4';
13997  } else {
13998  $this->PDFVersion = $version;
13999  }
14000  }
14001 
14012  $this->viewer_preferences = $preferences;
14013  }
14014 
14028  public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
14029  if (strpos($colors, 'ALLSPOT') !== false) {
14030  // expand spot colors
14031  $spot_colors = '';
14032  foreach ($this->spot_colors as $spot_color_name => $v) {
14033  $spot_colors .= ','.$spot_color_name;
14034  }
14035  if (!empty($spot_colors)) {
14036  $spot_colors = substr($spot_colors, 1);
14037  $colors = str_replace('ALLSPOT', $spot_colors, $colors);
14038  } else {
14039  $colors = str_replace('ALLSPOT', 'NONE', $colors);
14040  }
14041  }
14042  $bars = explode(',', $colors);
14043  $numbars = count($bars); // number of bars to print
14044  if ($numbars <= 0) {
14045  return;
14046  }
14047  // set bar measures
14048  if ($vertical) {
14049  $coords = array(0, 0, 0, 1);
14050  $wb = $w / $numbars; // bar width
14051  $hb = $h; // bar height
14052  $xd = $wb; // delta x
14053  $yd = 0; // delta y
14054  } else {
14055  $coords = array(1, 0, 0, 0);
14056  $wb = $w; // bar width
14057  $hb = $h / $numbars; // bar height
14058  $xd = 0; // delta x
14059  $yd = $hb; // delta y
14060  }
14061  $xb = $x;
14062  $yb = $y;
14063  foreach ($bars as $col) {
14064  switch ($col) {
14065  // set transition colors
14066  case 'A': { // BLACK (GRAYSCALE)
14067  $col_a = array(255);
14068  $col_b = array(0);
14069  break;
14070  }
14071  case 'W': { // WHITE (GRAYSCALE)
14072  $col_a = array(0);
14073  $col_b = array(255);
14074  break;
14075  }
14076  case 'R': { // RED (RGB)
14077  $col_a = array(255,255,255);
14078  $col_b = array(255,0,0);
14079  break;
14080  }
14081  case 'G': { // GREEN (RGB)
14082  $col_a = array(255,255,255);
14083  $col_b = array(0,255,0);
14084  break;
14085  }
14086  case 'B': { // BLUE (RGB)
14087  $col_a = array(255,255,255);
14088  $col_b = array(0,0,255);
14089  break;
14090  }
14091  case 'C': { // CYAN (CMYK)
14092  $col_a = array(0,0,0,0);
14093  $col_b = array(100,0,0,0);
14094  break;
14095  }
14096  case 'M': { // MAGENTA (CMYK)
14097  $col_a = array(0,0,0,0);
14098  $col_b = array(0,100,0,0);
14099  break;
14100  }
14101  case 'Y': { // YELLOW (CMYK)
14102  $col_a = array(0,0,0,0);
14103  $col_b = array(0,0,100,0);
14104  break;
14105  }
14106  case 'K': { // KEY - BLACK (CMYK)
14107  $col_a = array(0,0,0,0);
14108  $col_b = array(0,0,0,100);
14109  break;
14110  }
14111  case 'RGB': { // BLACK REGISTRATION (RGB)
14112  $col_a = array(255,255,255);
14113  $col_b = array(0,0,0);
14114  break;
14115  }
14116  case 'CMYK': { // BLACK REGISTRATION (CMYK)
14117  $col_a = array(0,0,0,0);
14118  $col_b = array(100,100,100,100);
14119  break;
14120  }
14121  case 'ALL': { // SPOT COLOR REGISTRATION
14122  $col_a = array(0,0,0,0,'None');
14123  $col_b = array(100,100,100,100,'All');
14124  break;
14125  }
14126  case 'NONE': { // SKIP THIS COLOR
14127  $col_a = array(0,0,0,0,'None');
14128  $col_b = array(0,0,0,0,'None');
14129  break;
14130  }
14131  default: { // SPECIFIC SPOT COLOR NAME
14132  $col_a = array(0,0,0,0,'None');
14133  $col_b = TCPDF_COLORS::getSpotColor($col, $this->spot_colors);
14134  if ($col_b === false) {
14135  // in case of error defaults to the registration color
14136  $col_b = array(100,100,100,100,'All');
14137  }
14138  break;
14139  }
14140  }
14141  if ($col != 'NONE') {
14142  if ($transition) {
14143  // color gradient
14144  $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
14145  } else {
14146  $this->SetFillColorArray($col_b);
14147  // colored rectangle
14148  $this->Rect($xb, $yb, $wb, $hb, 'F', array());
14149  }
14150  $xb += $xd;
14151  $yb += $yd;
14152  }
14153  }
14154  }
14155 
14168  public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(100,100,100,100,'All')) {
14169  $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
14170  $type = strtoupper($type);
14171  $type = preg_replace('/[^A-Z\-\,]*/', '', $type);
14172  // split type in single components
14173  $type = str_replace('-', ',', $type);
14174  $type = str_replace('TL', 'T,L', $type);
14175  $type = str_replace('TR', 'T,R', $type);
14176  $type = str_replace('BL', 'F,L', $type);
14177  $type = str_replace('BR', 'F,R', $type);
14178  $type = str_replace('A', 'T,L', $type);
14179  $type = str_replace('B', 'T,R', $type);
14180  $type = str_replace('T,RO', 'BO', $type);
14181  $type = str_replace('C', 'F,L', $type);
14182  $type = str_replace('D', 'F,R', $type);
14183  $crops = explode(',', strtoupper($type));
14184  // remove duplicates
14185  $crops = array_unique($crops);
14186  $dw = ($w / 4); // horizontal space to leave before the intersection point
14187  $dh = ($h / 4); // vertical space to leave before the intersection point
14188  foreach ($crops as $crop) {
14189  switch ($crop) {
14190  case 'T':
14191  case 'TOP': {
14192  $x1 = $x;
14193  $y1 = ($y - $h);
14194  $x2 = $x;
14195  $y2 = ($y - $dh);
14196  break;
14197  }
14198  case 'F':
14199  case 'BOTTOM': {
14200  $x1 = $x;
14201  $y1 = ($y + $dh);
14202  $x2 = $x;
14203  $y2 = ($y + $h);
14204  break;
14205  }
14206  case 'L':
14207  case 'LEFT': {
14208  $x1 = ($x - $w);
14209  $y1 = $y;
14210  $x2 = ($x - $dw);
14211  $y2 = $y;
14212  break;
14213  }
14214  case 'R':
14215  case 'RIGHT': {
14216  $x1 = ($x + $dw);
14217  $y1 = $y;
14218  $x2 = ($x + $w);
14219  $y2 = $y;
14220  break;
14221  }
14222  }
14223  $this->Line($x1, $y1, $x2, $y2);
14224  }
14225  }
14226 
14239  public function registrationMark($x, $y, $r, $double=false, $cola=array(100,100,100,100,'All'), $colb=array(0,0,0,0,'None')) {
14240  $line_style = array('width' => max((0.5 / $this->k),($r / 30)), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
14241  $this->SetFillColorArray($cola);
14242  $this->PieSector($x, $y, $r, 90, 180, 'F');
14243  $this->PieSector($x, $y, $r, 270, 360, 'F');
14244  $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14245  if ($double) {
14246  $ri = $r * 0.5;
14247  $this->SetFillColorArray($colb);
14248  $this->PieSector($x, $y, $ri, 90, 180, 'F');
14249  $this->PieSector($x, $y, $ri, 270, 360, 'F');
14250  $this->SetFillColorArray($cola);
14251  $this->PieSector($x, $y, $ri, 0, 90, 'F');
14252  $this->PieSector($x, $y, $ri, 180, 270, 'F');
14253  $this->Circle($x, $y, $ri, 0, 360, 'C', $line_style, array(), 8);
14254  }
14255  }
14256 
14266  public function registrationMarkCMYK($x, $y, $r) {
14267  // line width
14268  $lw = max((0.5 / $this->k),($r / 8));
14269  // internal radius
14270  $ri = ($r * 0.6);
14271  // external radius
14272  $re = ($r * 1.3);
14273  // Cyan
14274  $this->SetFillColorArray(array(100,0,0,0));
14275  $this->PieSector($x, $y, $ri, 270, 360, 'F');
14276  // Magenta
14277  $this->SetFillColorArray(array(0,100,0,0));
14278  $this->PieSector($x, $y, $ri, 0, 90, 'F');
14279  // Yellow
14280  $this->SetFillColorArray(array(0,0,100,0));
14281  $this->PieSector($x, $y, $ri, 90, 180, 'F');
14282  // Key - black
14283  $this->SetFillColorArray(array(0,0,0,100));
14284  $this->PieSector($x, $y, $ri, 180, 270, 'F');
14285  // registration color
14286  $line_style = array('width' => $lw, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(100,100,100,100,'All'));
14287  $this->SetFillColorArray(array(100,100,100,100,'All'));
14288  // external circle
14289  $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14290  // cross lines
14291  $this->Line($x, ($y - $re), $x, ($y - $ri));
14292  $this->Line($x, ($y + $ri), $x, ($y + $re));
14293  $this->Line(($x - $re), $y, ($x - $ri), $y);
14294  $this->Line(($x + $ri), $y, ($x + $re), $y);
14295  }
14296 
14310  public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
14311  $this->Clip($x, $y, $w, $h);
14312  $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14313  }
14314 
14328  public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
14329  $this->Clip($x, $y, $w, $h);
14330  $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14331  }
14332 
14351  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) {
14352  if ($this->pdfa_mode OR ($this->state != 2)) {
14353  return;
14354  }
14355  $this->Clip($x, $y, $w, $h);
14356  $n = count($this->gradients) + 1;
14357  $this->gradients[$n] = array();
14358  $this->gradients[$n]['type'] = 6; //coons patch mesh
14359  $this->gradients[$n]['coords'] = array();
14360  $this->gradients[$n]['antialias'] = $antialias;
14361  $this->gradients[$n]['colors'] = array();
14362  $this->gradients[$n]['transparency'] = false;
14363  //check the coords array if it is the simple array or the multi patch array
14364  if (!isset($coords[0]['f'])) {
14365  //simple array -> convert to multi patch array
14366  if (!isset($col1[1])) {
14367  $col1[1] = $col1[2] = $col1[0];
14368  }
14369  if (!isset($col2[1])) {
14370  $col2[1] = $col2[2] = $col2[0];
14371  }
14372  if (!isset($col3[1])) {
14373  $col3[1] = $col3[2] = $col3[0];
14374  }
14375  if (!isset($col4[1])) {
14376  $col4[1] = $col4[2] = $col4[0];
14377  }
14378  $patch_array[0]['f'] = 0;
14379  $patch_array[0]['points'] = $coords;
14380  $patch_array[0]['colors'][0]['r'] = $col1[0];
14381  $patch_array[0]['colors'][0]['g'] = $col1[1];
14382  $patch_array[0]['colors'][0]['b'] = $col1[2];
14383  $patch_array[0]['colors'][1]['r'] = $col2[0];
14384  $patch_array[0]['colors'][1]['g'] = $col2[1];
14385  $patch_array[0]['colors'][1]['b'] = $col2[2];
14386  $patch_array[0]['colors'][2]['r'] = $col3[0];
14387  $patch_array[0]['colors'][2]['g'] = $col3[1];
14388  $patch_array[0]['colors'][2]['b'] = $col3[2];
14389  $patch_array[0]['colors'][3]['r'] = $col4[0];
14390  $patch_array[0]['colors'][3]['g'] = $col4[1];
14391  $patch_array[0]['colors'][3]['b'] = $col4[2];
14392  } else {
14393  //multi patch array
14395  }
14396  $bpcd = 65535; //16 bits per coordinate
14397  //build the data stream
14398  $this->gradients[$n]['stream'] = '';
14399  $count_patch = count($patch_array);
14400  for ($i=0; $i < $count_patch; ++$i) {
14401  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
14402  $count_points = count($patch_array[$i]['points']);
14403  for ($j=0; $j < $count_points; ++$j) {
14404  //each point as 16 bit
14405  $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
14406  if ($patch_array[$i]['points'][$j] < 0) {
14407  $patch_array[$i]['points'][$j] = 0;
14408  }
14409  if ($patch_array[$i]['points'][$j] > $bpcd) {
14410  $patch_array[$i]['points'][$j] = $bpcd;
14411  }
14412  $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
14413  $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
14414  }
14415  $count_cols = count($patch_array[$i]['colors']);
14416  for ($j=0; $j < $count_cols; ++$j) {
14417  //each color component as 8 bit
14418  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
14419  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
14420  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
14421  }
14422  }
14423  //paint the gradient
14424  $this->_out('/Sh'.$n.' sh');
14425  //restore previous Graphic State
14426  $this->_outRestoreGraphicsState();
14427  if ($this->inxobj) {
14428  // we are inside an XObject template
14429  $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14430  }
14431  }
14432 
14443  protected function Clip($x, $y, $w, $h) {
14444  if ($this->state != 2) {
14445  return;
14446  }
14447  if ($this->rtl) {
14448  $x = $this->w - $x - $w;
14449  }
14450  //save current Graphic State
14451  $s = 'q';
14452  //set clipping area
14453  $s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
14454  //set up transformation matrix for gradient
14455  $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
14456  $this->_out($s);
14457  }
14458 
14470  public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
14471  if ($this->pdfa_mode OR ($this->state != 2)) {
14472  return;
14473  }
14474  $n = count($this->gradients) + 1;
14475  $this->gradients[$n] = array();
14476  $this->gradients[$n]['type'] = $type;
14477  $this->gradients[$n]['coords'] = $coords;
14478  $this->gradients[$n]['antialias'] = $antialias;
14479  $this->gradients[$n]['colors'] = array();
14480  $this->gradients[$n]['transparency'] = false;
14481  // color space
14482  $numcolspace = count($stops[0]['color']);
14483  $bcolor = array_values($background);
14484  switch($numcolspace) {
14485  case 5: // SPOT
14486  case 4: { // CMYK
14487  $this->gradients[$n]['colspace'] = 'DeviceCMYK';
14488  if (!empty($background)) {
14489  $this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
14490  }
14491  break;
14492  }
14493  case 3: { // RGB
14494  $this->gradients[$n]['colspace'] = 'DeviceRGB';
14495  if (!empty($background)) {
14496  $this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
14497  }
14498  break;
14499  }
14500  case 1: { // GRAY SCALE
14501  $this->gradients[$n]['colspace'] = 'DeviceGray';
14502  if (!empty($background)) {
14503  $this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255);
14504  }
14505  break;
14506  }
14507  }
14508  $num_stops = count($stops);
14509  $last_stop_id = $num_stops - 1;
14510  foreach ($stops as $key => $stop) {
14511  $this->gradients[$n]['colors'][$key] = array();
14512  // offset represents a location along the gradient vector
14513  if (isset($stop['offset'])) {
14514  $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
14515  } else {
14516  if ($key == 0) {
14517  $this->gradients[$n]['colors'][$key]['offset'] = 0;
14518  } elseif ($key == $last_stop_id) {
14519  $this->gradients[$n]['colors'][$key]['offset'] = 1;
14520  } else {
14521  $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
14522  $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
14523  }
14524  }
14525  if (isset($stop['opacity'])) {
14526  $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
14527  if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) {
14528  $this->gradients[$n]['transparency'] = true;
14529  }
14530  } else {
14531  $this->gradients[$n]['colors'][$key]['opacity'] = 1;
14532  }
14533  // exponent for the exponential interpolation function
14534  if (isset($stop['exponent'])) {
14535  $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
14536  } else {
14537  $this->gradients[$n]['colors'][$key]['exponent'] = 1;
14538  }
14539  // set colors
14540  $color = array_values($stop['color']);
14541  switch($numcolspace) {
14542  case 5: // SPOT
14543  case 4: { // CMYK
14544  $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
14545  break;
14546  }
14547  case 3: { // RGB
14548  $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255);
14549  break;
14550  }
14551  case 1: { // GRAY SCALE
14552  $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255);
14553  break;
14554  }
14555  }
14556  }
14557  if ($this->gradients[$n]['transparency']) {
14558  // paint luminosity gradient
14559  $this->_out('/TGS'.$n.' gs');
14560  }
14561  //paint the gradient
14562  $this->_out('/Sh'.$n.' sh');
14563  //restore previous Graphic State
14564  $this->_outRestoreGraphicsState();
14565  if ($this->inxobj) {
14566  // we are inside an XObject template
14567  $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14568  }
14569  }
14570 
14577  function _putshaders() {
14578  if ($this->pdfa_mode) {
14579  return;
14580  }
14581  $idt = count($this->gradients); //index for transparency gradients
14582  foreach ($this->gradients as $id => $grad) {
14583  if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
14584  $fc = $this->_newobj();
14585  $out = '<<';
14586  $out .= ' /FunctionType 3';
14587  $out .= ' /Domain [0 1]';
14588  $functions = '';
14589  $bounds = '';
14590  $encode = '';
14591  $i = 1;
14592  $num_cols = count($grad['colors']);
14593  $lastcols = $num_cols - 1;
14594  for ($i = 1; $i < $num_cols; ++$i) {
14595  $functions .= ($fc + $i).' 0 R ';
14596  if ($i < $lastcols) {
14597  $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']);
14598  }
14599  $encode .= '0 1 ';
14600  }
14601  $out .= ' /Functions ['.trim($functions).']';
14602  $out .= ' /Bounds ['.trim($bounds).']';
14603  $out .= ' /Encode ['.trim($encode).']';
14604  $out .= ' >>';
14605  $out .= "\n".'endobj';
14606  $this->_out($out);
14607  for ($i = 1; $i < $num_cols; ++$i) {
14608  $this->_newobj();
14609  $out = '<<';
14610  $out .= ' /FunctionType 2';
14611  $out .= ' /Domain [0 1]';
14612  $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
14613  $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
14614  $out .= ' /N '.$grad['colors'][$i]['exponent'];
14615  $out .= ' >>';
14616  $out .= "\n".'endobj';
14617  $this->_out($out);
14618  }
14619  // set transparency functions
14620  if ($grad['transparency']) {
14621  $ft = $this->_newobj();
14622  $out = '<<';
14623  $out .= ' /FunctionType 3';
14624  $out .= ' /Domain [0 1]';
14625  $functions = '';
14626  $i = 1;
14627  $num_cols = count($grad['colors']);
14628  for ($i = 1; $i < $num_cols; ++$i) {
14629  $functions .= ($ft + $i).' 0 R ';
14630  }
14631  $out .= ' /Functions ['.trim($functions).']';
14632  $out .= ' /Bounds ['.trim($bounds).']';
14633  $out .= ' /Encode ['.trim($encode).']';
14634  $out .= ' >>';
14635  $out .= "\n".'endobj';
14636  $this->_out($out);
14637  for ($i = 1; $i < $num_cols; ++$i) {
14638  $this->_newobj();
14639  $out = '<<';
14640  $out .= ' /FunctionType 2';
14641  $out .= ' /Domain [0 1]';
14642  $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
14643  $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
14644  $out .= ' /N '.$grad['colors'][$i]['exponent'];
14645  $out .= ' >>';
14646  $out .= "\n".'endobj';
14647  $this->_out($out);
14648  }
14649  }
14650  }
14651  // set shading object
14652  $this->_newobj();
14653  $out = '<< /ShadingType '.$grad['type'];
14654  if (isset($grad['colspace'])) {
14655  $out .= ' /ColorSpace /'.$grad['colspace'];
14656  } else {
14657  $out .= ' /ColorSpace /DeviceRGB';
14658  }
14659  if (isset($grad['background']) AND !empty($grad['background'])) {
14660  $out .= ' /Background ['.$grad['background'].']';
14661  }
14662  if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
14663  $out .= ' /AntiAlias true';
14664  }
14665  if ($grad['type'] == 2) {
14666  $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
14667  $out .= ' /Domain [0 1]';
14668  $out .= ' /Function '.$fc.' 0 R';
14669  $out .= ' /Extend [true true]';
14670  $out .= ' >>';
14671  } elseif ($grad['type'] == 3) {
14672  //x0, y0, r0, x1, y1, r1
14673  //at this this time radius of inner circle is 0
14674  $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
14675  $out .= ' /Domain [0 1]';
14676  $out .= ' /Function '.$fc.' 0 R';
14677  $out .= ' /Extend [true true]';
14678  $out .= ' >>';
14679  } elseif ($grad['type'] == 6) {
14680  $out .= ' /BitsPerCoordinate 16';
14681  $out .= ' /BitsPerComponent 8';
14682  $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
14683  $out .= ' /BitsPerFlag 8';
14684  $stream = $this->_getrawstream($grad['stream']);
14685  $out .= ' /Length '.strlen($stream);
14686  $out .= ' >>';
14687  $out .= ' stream'."\n".$stream."\n".'endstream';
14688  }
14689  $out .= "\n".'endobj';
14690  $this->_out($out);
14691  if ($grad['transparency']) {
14692  $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
14693  $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
14694  }
14695  $this->gradients[$id]['id'] = $this->n;
14696  // set pattern object
14697  $this->_newobj();
14698  $out = '<< /Type /Pattern /PatternType 2';
14699  $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
14700  $out .= ' >>';
14701  $out .= "\n".'endobj';
14702  $this->_out($out);
14703  $this->gradients[$id]['pattern'] = $this->n;
14704  // set shading and pattern for transparency mask
14705  if ($grad['transparency']) {
14706  // luminosity pattern
14707  $idgs = $id + $idt;
14708  $this->_newobj();
14709  $this->_out($shading_transparency);
14710  $this->gradients[$idgs]['id'] = $this->n;
14711  $this->_newobj();
14712  $out = '<< /Type /Pattern /PatternType 2';
14713  $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
14714  $out .= ' >>';
14715  $out .= "\n".'endobj';
14716  $this->_out($out);
14717  $this->gradients[$idgs]['pattern'] = $this->n;
14718  // luminosity XObject
14719  $oid = $this->_newobj();
14720  $this->xobjects['LX'.$oid] = array('n' => $oid);
14721  $filter = '';
14722  $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
14723  if ($this->compress) {
14724  $filter = ' /Filter /FlateDecode';
14725  $stream = gzcompress($stream);
14726  }
14727  $stream = $this->_getrawstream($stream);
14728  $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
14729  $out .= ' /Length '.strlen($stream);
14730  $rect = sprintf('%F %F', $this->wPt, $this->hPt);
14731  $out .= ' /BBox [0 0 '.$rect.']';
14732  $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
14733  $out .= ' /Resources <<';
14734  $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
14735  $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
14736  $out .= ' >>';
14737  $out .= ' >> ';
14738  $out .= ' stream'."\n".$stream."\n".'endstream';
14739  $out .= "\n".'endobj';
14740  $this->_out($out);
14741  // SMask
14742  $this->_newobj();
14743  $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj';
14744  $this->_out($out);
14745  // ExtGState
14746  $this->_newobj();
14747  $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj';
14748  $this->_out($out);
14749  $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
14750  }
14751  }
14752  }
14753 
14769  public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
14770  $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
14771  }
14772 
14790  public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
14791  if ($this->state != 2) {
14792  return;
14793  }
14794  if ($this->rtl) {
14795  $xc = ($this->w - $xc);
14796  }
14798  if ($op == 'f') {
14799  $line_style = array();
14800  }
14801  if ($cw) {
14802  $d = $b;
14803  $b = (360 - $a + $o);
14804  $a = (360 - $d + $o);
14805  } else {
14806  $b += $o;
14807  $a += $o;
14808  }
14809  $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
14810  $this->_out($op);
14811  }
14812 
14834  public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
14835  if ($this->state != 2) {
14836  return;
14837  }
14838  if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
14839  // convert EPS to raster image using GD or ImageMagick libraries
14840  return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14841  }
14842  if ($x === '') {
14843  $x = $this->x;
14844  }
14845  if ($y === '') {
14846  $y = $this->y;
14847  }
14848  // check page for no-write regions and adapt page margins if necessary
14849  list($x, $y) = $this->checkPageRegions($h, $x, $y);
14850  $k = $this->k;
14851  if ($file[0] === '@') { // image from string
14852  $data = substr($file, 1);
14853  } else { // EPS/AI file
14855  }
14856  if ($data === FALSE) {
14857  $this->Error('EPS file not found: '.$file);
14858  }
14859  $regs = array();
14860  // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
14861  preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
14862  if (count($regs) > 1) {
14863  $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
14864  if (strpos($version_str, 'Adobe Illustrator') !== false) {
14865  $versexp = explode(' ', $version_str);
14866  $version = (float)array_pop($versexp);
14867  if ($version >= 9) {
14868  $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
14869  }
14870  }
14871  }
14872  // strip binary bytes in front of PS-header
14873  $start = strpos($data, '%!PS-Adobe');
14874  if ($start > 0) {
14875  $data = substr($data, $start);
14876  }
14877  // find BoundingBox params
14878  preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
14879  if (count($regs) > 1) {
14880  list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
14881  } else {
14882  $this->Error('No BoundingBox found in EPS/AI file: '.$file);
14883  }
14884  $start = strpos($data, '%%EndSetup');
14885  if ($start === false) {
14886  $start = strpos($data, '%%EndProlog');
14887  }
14888  if ($start === false) {
14889  $start = strpos($data, '%%BoundingBox');
14890  }
14891  $data = substr($data, $start);
14892  $end = strpos($data, '%%PageTrailer');
14893  if ($end===false) {
14894  $end = strpos($data, 'showpage');
14895  }
14896  if ($end) {
14897  $data = substr($data, 0, $end);
14898  }
14899  // calculate image width and height on document
14900  if (($w <= 0) AND ($h <= 0)) {
14901  $w = ($x2 - $x1) / $k;
14902  $h = ($y2 - $y1) / $k;
14903  } elseif ($w <= 0) {
14904  $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
14905  } elseif ($h <= 0) {
14906  $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
14907  }
14908  // fit the image on available space
14909  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
14910  if ($this->rasterize_vector_images) {
14911  // convert EPS to raster image using GD or ImageMagick libraries
14912  return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14913  }
14914  // set scaling factors
14915  $scale_x = $w / (($x2 - $x1) / $k);
14916  $scale_y = $h / (($y2 - $y1) / $k);
14917  // set alignment
14918  $this->img_rb_y = $y + $h;
14919  // set alignment
14920  if ($this->rtl) {
14921  if ($palign == 'L') {
14922  $ximg = $this->lMargin;
14923  } elseif ($palign == 'C') {
14924  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
14925  } elseif ($palign == 'R') {
14926  $ximg = $this->w - $this->rMargin - $w;
14927  } else {
14928  $ximg = $x - $w;
14929  }
14930  $this->img_rb_x = $ximg;
14931  } else {
14932  if ($palign == 'L') {
14933  $ximg = $this->lMargin;
14934  } elseif ($palign == 'C') {
14935  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
14936  } elseif ($palign == 'R') {
14937  $ximg = $this->w - $this->rMargin - $w;
14938  } else {
14939  $ximg = $x;
14940  }
14941  $this->img_rb_x = $ximg + $w;
14942  }
14943  if ($useBoundingBox) {
14944  $dx = $ximg * $k - $x1;
14945  $dy = $y * $k - $y1;
14946  } else {
14947  $dx = $ximg * $k;
14948  $dy = $y * $k;
14949  }
14950  // save the current graphic state
14951  $this->_out('q'.$this->epsmarker);
14952  // translate
14953  $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
14954  // scale
14955  if (isset($scale_x)) {
14956  $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
14957  }
14958  // handle pc/unix/mac line endings
14959  $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY);
14960  $u=0;
14961  $cnt = count($lines);
14962  for ($i=0; $i < $cnt; ++$i) {
14963  $line = $lines[$i];
14964  if (($line == '') OR ($line[0] == '%')) {
14965  continue;
14966  }
14967  $len = strlen($line);
14968  // check for spot color names
14969  $color_name = '';
14970  if (strcasecmp('x', substr(trim($line), -1)) == 0) {
14971  if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) {
14972  // extract spot color name
14973  $color_name = $matches[0];
14974  // remove color name from string
14975  $line = str_replace(' '.$color_name, '', $line);
14976  // remove pharentesis from color name
14977  $color_name = substr($color_name, 1, -1);
14978  }
14979  }
14980  $chunks = explode(' ', $line);
14981  $cmd = trim(array_pop($chunks));
14982  // RGB
14983  if (($cmd == 'Xa') OR ($cmd == 'XA')) {
14984  $b = array_pop($chunks);
14985  $g = array_pop($chunks);
14986  $r = array_pop($chunks);
14987  $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!
14988  continue;
14989  }
14990  $skip = false;
14991  if ($fixoutvals) {
14992  // check for values outside the bounding box
14993  switch ($cmd) {
14994  case 'm':
14995  case 'l':
14996  case 'L': {
14997  // skip values outside bounding box
14998  foreach ($chunks as $key => $val) {
14999  if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) {
15000  $skip = true;
15001  } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) {
15002  $skip = true;
15003  }
15004  }
15005  }
15006  }
15007  }
15008  switch ($cmd) {
15009  case 'm':
15010  case 'l':
15011  case 'v':
15012  case 'y':
15013  case 'c':
15014  case 'k':
15015  case 'K':
15016  case 'g':
15017  case 'G':
15018  case 's':
15019  case 'S':
15020  case 'J':
15021  case 'j':
15022  case 'w':
15023  case 'M':
15024  case 'd':
15025  case 'n': {
15026  if ($skip) {
15027  break;
15028  }
15029  $this->_out($line);
15030  break;
15031  }
15032  case 'x': {// custom fill color
15033  if (empty($color_name)) {
15034  // CMYK color
15035  list($col_c, $col_m, $col_y, $col_k) = $chunks;
15036  $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
15037  } else {
15038  // Spot Color (CMYK + tint)
15039  list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15040  $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15041  $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15042  $this->_out($color_cmd);
15043  }
15044  break;
15045  }
15046  case 'X': { // custom stroke color
15047  if (empty($color_name)) {
15048  // CMYK color
15049  list($col_c, $col_m, $col_y, $col_k) = $chunks;
15050  $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
15051  } else {
15052  // Spot Color (CMYK + tint)
15053  list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15054  $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15055  $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15056  $this->_out($color_cmd);
15057  }
15058  break;
15059  }
15060  case 'Y':
15061  case 'N':
15062  case 'V':
15063  case 'L':
15064  case 'C': {
15065  if ($skip) {
15066  break;
15067  }
15068  $line[($len - 1)] = strtolower($cmd);
15069  $this->_out($line);
15070  break;
15071  }
15072  case 'b':
15073  case 'B': {
15074  $this->_out($cmd . '*');
15075  break;
15076  }
15077  case 'f':
15078  case 'F': {
15079  if ($u > 0) {
15080  $isU = false;
15081  $max = min(($i + 5), $cnt);
15082  for ($j = ($i + 1); $j < $max; ++$j) {
15083  $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
15084  }
15085  if ($isU) {
15086  $this->_out('f*');
15087  }
15088  } else {
15089  $this->_out('f*');
15090  }
15091  break;
15092  }
15093  case '*u': {
15094  ++$u;
15095  break;
15096  }
15097  case '*U': {
15098  --$u;
15099  break;
15100  }
15101  }
15102  }
15103  // restore previous graphic state
15104  $this->_out($this->epsmarker.'Q');
15105  if (!empty($border)) {
15106  $bx = $this->x;
15107  $by = $this->y;
15108  $this->x = $ximg;
15109  if ($this->rtl) {
15110  $this->x += $w;
15111  }
15112  $this->y = $y;
15113  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
15114  $this->x = $bx;
15115  $this->y = $by;
15116  }
15117  if ($link) {
15118  $this->Link($ximg, $y, $w, $h, $link, 0);
15119  }
15120  // set pointer to align the next text/objects
15121  switch($align) {
15122  case 'T':{
15123  $this->y = $y;
15124  $this->x = $this->img_rb_x;
15125  break;
15126  }
15127  case 'M':{
15128  $this->y = $y + round($h/2);
15129  $this->x = $this->img_rb_x;
15130  break;
15131  }
15132  case 'B':{
15133  $this->y = $this->img_rb_y;
15134  $this->x = $this->img_rb_x;
15135  break;
15136  }
15137  case 'N':{
15138  $this->SetY($this->img_rb_y);
15139  break;
15140  }
15141  default:{
15142  break;
15143  }
15144  }
15145  $this->endlinex = $this->img_rb_x;
15146  }
15147 
15153  public function setBarcode($bc='') {
15154  $this->barcode = $bc;
15155  }
15156 
15163  public function getBarcode() {
15164  return $this->barcode;
15165  }
15166 
15197  public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
15198  if (TCPDF_STATIC::empty_string(trim($code))) {
15199  return;
15200  }
15201  require_once(dirname(__FILE__).'/tcpdf_barcodes_1d.php');
15202  // save current graphic settings
15203  $gvars = $this->getGraphicVars();
15204  // create new barcode object
15205  $barcodeobj = new TCPDFBarcode($code, $type);
15206  $arrcode = $barcodeobj->getBarcodeArray();
15207  if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] <= 0)) {
15208  $this->Error('Error in 1D barcode string');
15209  }
15210  if ($arrcode['maxh'] <= 0) {
15211  $arrcode['maxh'] = 1;
15212  }
15213  // set default values
15214  if (!isset($style['position'])) {
15215  $style['position'] = '';
15216  } elseif ($style['position'] == 'S') {
15217  // keep this for backward compatibility
15218  $style['position'] = '';
15219  $style['stretch'] = true;
15220  }
15221  if (!isset($style['fitwidth'])) {
15222  if (!isset($style['stretch'])) {
15223  $style['fitwidth'] = true;
15224  } else {
15225  $style['fitwidth'] = false;
15226  }
15227  }
15228  if ($style['fitwidth']) {
15229  // disable stretch
15230  $style['stretch'] = false;
15231  }
15232  if (!isset($style['stretch'])) {
15233  if (($w === '') OR ($w <= 0)) {
15234  $style['stretch'] = false;
15235  } else {
15236  $style['stretch'] = true;
15237  }
15238  }
15239  if (!isset($style['fgcolor'])) {
15240  $style['fgcolor'] = array(0,0,0); // default black
15241  }
15242  if (!isset($style['bgcolor'])) {
15243  $style['bgcolor'] = false; // default transparent
15244  }
15245  if (!isset($style['border'])) {
15246  $style['border'] = false;
15247  }
15248  $fontsize = 0;
15249  if (!isset($style['text'])) {
15250  $style['text'] = false;
15251  }
15252  if ($style['text'] AND isset($style['font'])) {
15253  if (isset($style['fontsize'])) {
15254  $fontsize = $style['fontsize'];
15255  }
15256  $this->SetFont($style['font'], '', $fontsize);
15257  }
15258  if (!isset($style['stretchtext'])) {
15259  $style['stretchtext'] = 4;
15260  }
15261  if ($x === '') {
15262  $x = $this->x;
15263  }
15264  if ($y === '') {
15265  $y = $this->y;
15266  }
15267  // check page for no-write regions and adapt page margins if necessary
15268  list($x, $y) = $this->checkPageRegions($h, $x, $y);
15269  if (($w === '') OR ($w <= 0)) {
15270  if ($this->rtl) {
15271  $w = $x - $this->lMargin;
15272  } else {
15273  $w = $this->w - $this->rMargin - $x;
15274  }
15275  }
15276  // padding
15277  if (!isset($style['padding'])) {
15278  $padding = 0;
15279  } elseif ($style['padding'] === 'auto') {
15280  $padding = 10 * ($w / ($arrcode['maxw'] + 20));
15281  } else {
15282  $padding = floatval($style['padding']);
15283  }
15284  // horizontal padding
15285  if (!isset($style['hpadding'])) {
15286  $hpadding = $padding;
15287  } elseif ($style['hpadding'] === 'auto') {
15288  $hpadding = 10 * ($w / ($arrcode['maxw'] + 20));
15289  } else {
15290  $hpadding = floatval($style['hpadding']);
15291  }
15292  // vertical padding
15293  if (!isset($style['vpadding'])) {
15294  $vpadding = $padding;
15295  } elseif ($style['vpadding'] === 'auto') {
15296  $vpadding = ($hpadding / 2);
15297  } else {
15298  $vpadding = floatval($style['vpadding']);
15299  }
15300  // calculate xres (single bar width)
15301  $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
15302  if ($style['stretch']) {
15303  $xres = $max_xres;
15304  } else {
15305  if (TCPDF_STATIC::empty_string($xres)) {
15306  $xres = (0.141 * $this->k); // default bar width = 0.4 mm
15307  }
15308  if ($xres > $max_xres) {
15309  // correct xres to fit on $w
15310  $xres = $max_xres;
15311  }
15312  if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
15313  OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
15314  $hpadding = 10 * $xres;
15315  if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
15316  $vpadding = ($hpadding / 2);
15317  }
15318  }
15319  }
15320  if ($style['fitwidth']) {
15321  $wold = $w;
15322  $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding));
15323  if (isset($style['cellfitalign'])) {
15324  switch ($style['cellfitalign']) {
15325  case 'L': {
15326  if ($this->rtl) {
15327  $x -= ($wold - $w);
15328  }
15329  break;
15330  }
15331  case 'R': {
15332  if (!$this->rtl) {
15333  $x += ($wold - $w);
15334  }
15335  break;
15336  }
15337  case 'C': {
15338  if ($this->rtl) {
15339  $x -= (($wold - $w) / 2);
15340  } else {
15341  $x += (($wold - $w) / 2);
15342  }
15343  break;
15344  }
15345  default : {
15346  break;
15347  }
15348  }
15349  }
15350  }
15351  $text_height = $this->getCellHeight($fontsize / $this->k);
15352  // height
15353  if (($h === '') OR ($h <= 0)) {
15354  // set default height
15355  $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height;
15356  }
15357  $barh = $h - $text_height - (2 * $vpadding);
15358  if ($barh <=0) {
15359  // try to reduce font or padding to fit barcode on available height
15360  if ($text_height > $h) {
15361  $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio));
15362  $text_height = $this->getCellHeight($fontsize / $this->k);
15363  $this->SetFont($style['font'], '', $fontsize);
15364  }
15365  if ($vpadding > 0) {
15366  $vpadding = (($h - $text_height) / 4);
15367  }
15368  $barh = $h - $text_height - (2 * $vpadding);
15369  }
15370  // fit the barcode on available space
15371  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15372  // set alignment
15373  $this->img_rb_y = $y + $h;
15374  // set alignment
15375  if ($this->rtl) {
15376  if ($style['position'] == 'L') {
15377  $xpos = $this->lMargin;
15378  } elseif ($style['position'] == 'C') {
15379  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15380  } elseif ($style['position'] == 'R') {
15381  $xpos = $this->w - $this->rMargin - $w;
15382  } else {
15383  $xpos = $x - $w;
15384  }
15385  $this->img_rb_x = $xpos;
15386  } else {
15387  if ($style['position'] == 'L') {
15388  $xpos = $this->lMargin;
15389  } elseif ($style['position'] == 'C') {
15390  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15391  } elseif ($style['position'] == 'R') {
15392  $xpos = $this->w - $this->rMargin - $w;
15393  } else {
15394  $xpos = $x;
15395  }
15396  $this->img_rb_x = $xpos + $w;
15397  }
15398  $xpos_rect = $xpos;
15399  if (!isset($style['align'])) {
15400  $style['align'] = 'C';
15401  }
15402  switch ($style['align']) {
15403  case 'L': {
15404  $xpos = $xpos_rect + $hpadding;
15405  break;
15406  }
15407  case 'R': {
15408  $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding;
15409  break;
15410  }
15411  case 'C':
15412  default : {
15413  $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2);
15414  break;
15415  }
15416  }
15417  $xpos_text = $xpos;
15418  // barcode is always printed in LTR direction
15419  $tempRTL = $this->rtl;
15420  $this->rtl = false;
15421  // print background color
15422  if ($style['bgcolor']) {
15423  $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15424  } elseif ($style['border']) {
15425  $this->Rect($xpos_rect, $y, $w, $h, 'D');
15426  }
15427  // set foreground color
15428  $this->SetDrawColorArray($style['fgcolor']);
15429  $this->SetTextColorArray($style['fgcolor']);
15430  // print bars
15431  foreach ($arrcode['bcode'] as $k => $v) {
15432  $bw = ($v['w'] * $xres);
15433  if ($v['t']) {
15434  // draw a vertical bar
15435  $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']);
15436  $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
15437  }
15438  $xpos += $bw;
15439  }
15440  // print text
15441  if ($style['text']) {
15442  if (isset($style['label']) AND !TCPDF_STATIC::empty_string($style['label'])) {
15443  $label = $style['label'];
15444  } else {
15445  $label = $code;
15446  }
15447  $txtwidth = ($arrcode['maxw'] * $xres);
15448  if ($this->GetStringWidth($label) > $txtwidth) {
15449  $style['stretchtext'] = 2;
15450  }
15451  // print text
15452  $this->x = $xpos_text;
15453  $this->y = $y + $vpadding + $barh;
15454  $cellpadding = $this->cell_padding;
15455  $this->SetCellPadding(0);
15456  $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
15457  $this->cell_padding = $cellpadding;
15458  }
15459  // restore original direction
15460  $this->rtl = $tempRTL;
15461  // restore previous settings
15462  $this->setGraphicVars($gvars);
15463  // set pointer to align the next text/objects
15464  switch($align) {
15465  case 'T':{
15466  $this->y = $y;
15467  $this->x = $this->img_rb_x;
15468  break;
15469  }
15470  case 'M':{
15471  $this->y = $y + round($h / 2);
15472  $this->x = $this->img_rb_x;
15473  break;
15474  }
15475  case 'B':{
15476  $this->y = $this->img_rb_y;
15477  $this->x = $this->img_rb_x;
15478  break;
15479  }
15480  case 'N':{
15481  $this->SetY($this->img_rb_y);
15482  break;
15483  }
15484  default:{
15485  break;
15486  }
15487  }
15488  $this->endlinex = $this->img_rb_x;
15489  }
15490 
15516  public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) {
15517  if (TCPDF_STATIC::empty_string(trim($code))) {
15518  return;
15519  }
15520  require_once(dirname(__FILE__).'/tcpdf_barcodes_2d.php');
15521  // save current graphic settings
15522  $gvars = $this->getGraphicVars();
15523  // create new barcode object
15524  $barcodeobj = new TCPDF2DBarcode($code, $type);
15525  $arrcode = $barcodeobj->getBarcodeArray();
15526  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)) {
15527  $this->Error('Error in 2D barcode string');
15528  }
15529  // set default values
15530  if (!isset($style['position'])) {
15531  $style['position'] = '';
15532  }
15533  if (!isset($style['fgcolor'])) {
15534  $style['fgcolor'] = array(0,0,0); // default black
15535  }
15536  if (!isset($style['bgcolor'])) {
15537  $style['bgcolor'] = false; // default transparent
15538  }
15539  if (!isset($style['border'])) {
15540  $style['border'] = false;
15541  }
15542  // padding
15543  if (!isset($style['padding'])) {
15544  $style['padding'] = 0;
15545  } elseif ($style['padding'] === 'auto') {
15546  $style['padding'] = 4;
15547  }
15548  if (!isset($style['hpadding'])) {
15549  $style['hpadding'] = $style['padding'];
15550  } elseif ($style['hpadding'] === 'auto') {
15551  $style['hpadding'] = 4;
15552  }
15553  if (!isset($style['vpadding'])) {
15554  $style['vpadding'] = $style['padding'];
15555  } elseif ($style['vpadding'] === 'auto') {
15556  $style['vpadding'] = 4;
15557  }
15558  $hpad = (2 * $style['hpadding']);
15559  $vpad = (2 * $style['vpadding']);
15560  // cell (module) dimension
15561  if (!isset($style['module_width'])) {
15562  $style['module_width'] = 1; // width of a single module in points
15563  }
15564  if (!isset($style['module_height'])) {
15565  $style['module_height'] = 1; // height of a single module in points
15566  }
15567  if ($x === '') {
15568  $x = $this->x;
15569  }
15570  if ($y === '') {
15571  $y = $this->y;
15572  }
15573  // check page for no-write regions and adapt page margins if necessary
15574  list($x, $y) = $this->checkPageRegions($h, $x, $y);
15575  // number of barcode columns and rows
15576  $rows = $arrcode['num_rows'];
15577  $cols = $arrcode['num_cols'];
15578  if (($rows <= 0) || ($cols <= 0)){
15579  $this->Error('Error in 2D barcode string');
15580  }
15581  // module width and height
15582  $mw = $style['module_width'];
15583  $mh = $style['module_height'];
15584  if (($mw <= 0) OR ($mh <= 0)) {
15585  $this->Error('Error in 2D barcode string');
15586  }
15587  // get max dimensions
15588  if ($this->rtl) {
15589  $maxw = $x - $this->lMargin;
15590  } else {
15591  $maxw = $this->w - $this->rMargin - $x;
15592  }
15593  $maxh = ($this->h - $this->tMargin - $this->bMargin);
15594  $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad));
15595  $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad));
15596  if (!$distort) {
15597  if (($maxw * $ratioHW) > $maxh) {
15598  $maxw = $maxh * $ratioWH;
15599  }
15600  if (($maxh * $ratioWH) > $maxw) {
15601  $maxh = $maxw * $ratioHW;
15602  }
15603  }
15604  // set maximum dimensions
15605  if ($w > $maxw) {
15606  $w = $maxw;
15607  }
15608  if ($h > $maxh) {
15609  $h = $maxh;
15610  }
15611  // set dimensions
15612  if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
15613  $w = ($cols + $hpad) * ($mw / $this->k);
15614  $h = ($rows + $vpad) * ($mh / $this->k);
15615  } elseif (($w === '') OR ($w <= 0)) {
15616  $w = $h * $ratioWH;
15617  } elseif (($h === '') OR ($h <= 0)) {
15618  $h = $w * $ratioHW;
15619  }
15620  // barcode size (excluding padding)
15621  $bw = ($w * $cols) / ($cols + $hpad);
15622  $bh = ($h * $rows) / ($rows + $vpad);
15623  // dimension of single barcode cell unit
15624  $cw = $bw / $cols;
15625  $ch = $bh / $rows;
15626  if (!$distort) {
15627  if (($cw / $ch) > ($mw / $mh)) {
15628  // correct horizontal distortion
15629  $cw = $ch * $mw / $mh;
15630  $bw = $cw * $cols;
15631  $style['hpadding'] = ($w - $bw) / (2 * $cw);
15632  } else {
15633  // correct vertical distortion
15634  $ch = $cw * $mh / $mw;
15635  $bh = $ch * $rows;
15636  $style['vpadding'] = ($h - $bh) / (2 * $ch);
15637  }
15638  }
15639  // fit the barcode on available space
15640  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15641  // set alignment
15642  $this->img_rb_y = $y + $h;
15643  // set alignment
15644  if ($this->rtl) {
15645  if ($style['position'] == 'L') {
15646  $xpos = $this->lMargin;
15647  } elseif ($style['position'] == 'C') {
15648  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15649  } elseif ($style['position'] == 'R') {
15650  $xpos = $this->w - $this->rMargin - $w;
15651  } else {
15652  $xpos = $x - $w;
15653  }
15654  $this->img_rb_x = $xpos;
15655  } else {
15656  if ($style['position'] == 'L') {
15657  $xpos = $this->lMargin;
15658  } elseif ($style['position'] == 'C') {
15659  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15660  } elseif ($style['position'] == 'R') {
15661  $xpos = $this->w - $this->rMargin - $w;
15662  } else {
15663  $xpos = $x;
15664  }
15665  $this->img_rb_x = $xpos + $w;
15666  }
15667  $xstart = $xpos + ($style['hpadding'] * $cw);
15668  $ystart = $y + ($style['vpadding'] * $ch);
15669  // barcode is always printed in LTR direction
15670  $tempRTL = $this->rtl;
15671  $this->rtl = false;
15672  // print background color
15673  if ($style['bgcolor']) {
15674  $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15675  } elseif ($style['border']) {
15676  $this->Rect($xpos, $y, $w, $h, 'D');
15677  }
15678  // set foreground color
15679  $this->SetDrawColorArray($style['fgcolor']);
15680  // print barcode cells
15681  // for each row
15682  for ($r = 0; $r < $rows; ++$r) {
15683  $xr = $xstart;
15684  // for each column
15685  for ($c = 0; $c < $cols; ++$c) {
15686  if ($arrcode['bcode'][$r][$c] == 1) {
15687  // draw a single barcode cell
15688  $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
15689  }
15690  $xr += $cw;
15691  }
15692  $ystart += $ch;
15693  }
15694  // restore original direction
15695  $this->rtl = $tempRTL;
15696  // restore previous settings
15697  $this->setGraphicVars($gvars);
15698  // set pointer to align the next text/objects
15699  switch($align) {
15700  case 'T':{
15701  $this->y = $y;
15702  $this->x = $this->img_rb_x;
15703  break;
15704  }
15705  case 'M':{
15706  $this->y = $y + round($h/2);
15707  $this->x = $this->img_rb_x;
15708  break;
15709  }
15710  case 'B':{
15711  $this->y = $this->img_rb_y;
15712  $this->x = $this->img_rb_x;
15713  break;
15714  }
15715  case 'N':{
15716  $this->SetY($this->img_rb_y);
15717  break;
15718  }
15719  default:{
15720  break;
15721  }
15722  }
15723  $this->endlinex = $this->img_rb_x;
15724  }
15725 
15745  public function getMargins() {
15746  $ret = array(
15747  'left' => $this->lMargin,
15748  'right' => $this->rMargin,
15749  'top' => $this->tMargin,
15750  'bottom' => $this->bMargin,
15751  'header' => $this->header_margin,
15752  'footer' => $this->footer_margin,
15753  'cell' => $this->cell_padding,
15754  'padding_left' => $this->cell_padding['L'],
15755  'padding_top' => $this->cell_padding['T'],
15756  'padding_right' => $this->cell_padding['R'],
15757  'padding_bottom' => $this->cell_padding['B']
15758  );
15759  return $ret;
15760  }
15761 
15772  public function getOriginalMargins() {
15773  $ret = array(
15774  'left' => $this->original_lMargin,
15775  'right' => $this->original_rMargin
15776  );
15777  return $ret;
15778  }
15779 
15786  public function getFontSize() {
15787  return $this->FontSize;
15788  }
15789 
15796  public function getFontSizePt() {
15797  return $this->FontSizePt;
15798  }
15799 
15806  public function getFontFamily() {
15807  return $this->FontFamily;
15808  }
15809 
15816  public function getFontStyle() {
15817  return $this->FontStyle;
15818  }
15819 
15832  public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
15833  return TCPDF_STATIC::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces);
15834  }
15835 
15843  protected function getCSSBorderWidth($width) {
15844  if ($width == 'thin') {
15845  $width = (2 / $this->k);
15846  } elseif ($width == 'medium') {
15847  $width = (4 / $this->k);
15848  } elseif ($width == 'thick') {
15849  $width = (6 / $this->k);
15850  } else {
15851  $width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
15852  }
15853  return $width;
15854  }
15855 
15863  protected function getCSSBorderDashStyle($style) {
15864  switch (strtolower($style)) {
15865  case 'none':
15866  case 'hidden': {
15867  $dash = -1;
15868  break;
15869  }
15870  case 'dotted': {
15871  $dash = 1;
15872  break;
15873  }
15874  case 'dashed': {
15875  $dash = 3;
15876  break;
15877  }
15878  case 'double':
15879  case 'groove':
15880  case 'ridge':
15881  case 'inset':
15882  case 'outset':
15883  case 'solid':
15884  default: {
15885  $dash = 0;
15886  break;
15887  }
15888  }
15889  return $dash;
15890  }
15891 
15899  protected function getCSSBorderStyle($cssborder) {
15900  $bprop = preg_split('/[\s]+/', trim($cssborder));
15901  $border = array(); // value to be returned
15902  switch (count($bprop)) {
15903  case 3: {
15904  $width = $bprop[0];
15905  $style = $bprop[1];
15906  $color = $bprop[2];
15907  break;
15908  }
15909  case 2: {
15910  $width = 'medium';
15911  $style = $bprop[0];
15912  $color = $bprop[1];
15913  break;
15914  }
15915  case 1: {
15916  $width = 'medium';
15917  $style = $bprop[0];
15918  $color = 'black';
15919  break;
15920  }
15921  default: {
15922  $width = 'medium';
15923  $style = 'solid';
15924  $color = 'black';
15925  break;
15926  }
15927  }
15928  if ($style == 'none') {
15929  return array();
15930  }
15931  $border['cap'] = 'square';
15932  $border['join'] = 'miter';
15933  $border['dash'] = $this->getCSSBorderDashStyle($style);
15934  if ($border['dash'] < 0) {
15935  return array();
15936  }
15937  $border['width'] = $this->getCSSBorderWidth($width);
15938  $border['color'] = TCPDF_COLORS::convertHTMLColorToDec($color, $this->spot_colors);
15939  return $border;
15940  }
15941 
15950  public function getCSSPadding($csspadding, $width=0) {
15951  $padding = preg_split('/[\s]+/', trim($csspadding));
15952  $cell_padding = array(); // value to be returned
15953  switch (count($padding)) {
15954  case 4: {
15955  $cell_padding['T'] = $padding[0];
15956  $cell_padding['R'] = $padding[1];
15957  $cell_padding['B'] = $padding[2];
15958  $cell_padding['L'] = $padding[3];
15959  break;
15960  }
15961  case 3: {
15962  $cell_padding['T'] = $padding[0];
15963  $cell_padding['R'] = $padding[1];
15964  $cell_padding['B'] = $padding[2];
15965  $cell_padding['L'] = $padding[1];
15966  break;
15967  }
15968  case 2: {
15969  $cell_padding['T'] = $padding[0];
15970  $cell_padding['R'] = $padding[1];
15971  $cell_padding['B'] = $padding[0];
15972  $cell_padding['L'] = $padding[1];
15973  break;
15974  }
15975  case 1: {
15976  $cell_padding['T'] = $padding[0];
15977  $cell_padding['R'] = $padding[0];
15978  $cell_padding['B'] = $padding[0];
15979  $cell_padding['L'] = $padding[0];
15980  break;
15981  }
15982  default: {
15983  return $this->cell_padding;
15984  }
15985  }
15986  if ($width == 0) {
15987  $width = $this->w - $this->lMargin - $this->rMargin;
15988  }
15989  $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
15990  $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
15991  $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
15992  $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
15993  return $cell_padding;
15994  }
15995 
16004  public function getCSSMargin($cssmargin, $width=0) {
16005  $margin = preg_split('/[\s]+/', trim($cssmargin));
16006  $cell_margin = array(); // value to be returned
16007  switch (count($margin)) {
16008  case 4: {
16009  $cell_margin['T'] = $margin[0];
16010  $cell_margin['R'] = $margin[1];
16011  $cell_margin['B'] = $margin[2];
16012  $cell_margin['L'] = $margin[3];
16013  break;
16014  }
16015  case 3: {
16016  $cell_margin['T'] = $margin[0];
16017  $cell_margin['R'] = $margin[1];
16018  $cell_margin['B'] = $margin[2];
16019  $cell_margin['L'] = $margin[1];
16020  break;
16021  }
16022  case 2: {
16023  $cell_margin['T'] = $margin[0];
16024  $cell_margin['R'] = $margin[1];
16025  $cell_margin['B'] = $margin[0];
16026  $cell_margin['L'] = $margin[1];
16027  break;
16028  }
16029  case 1: {
16030  $cell_margin['T'] = $margin[0];
16031  $cell_margin['R'] = $margin[0];
16032  $cell_margin['B'] = $margin[0];
16033  $cell_margin['L'] = $margin[0];
16034  break;
16035  }
16036  default: {
16037  return $this->cell_margin;
16038  }
16039  }
16040  if ($width == 0) {
16041  $width = $this->w - $this->lMargin - $this->rMargin;
16042  }
16043  $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
16044  $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
16045  $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
16046  $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
16047  return $cell_margin;
16048  }
16049 
16058  public function getCSSBorderMargin($cssbspace, $width=0) {
16059  $space = preg_split('/[\s]+/', trim($cssbspace));
16060  $border_spacing = array(); // value to be returned
16061  switch (count($space)) {
16062  case 2: {
16063  $border_spacing['H'] = $space[0];
16064  $border_spacing['V'] = $space[1];
16065  break;
16066  }
16067  case 1: {
16068  $border_spacing['H'] = $space[0];
16069  $border_spacing['V'] = $space[0];
16070  break;
16071  }
16072  default: {
16073  return array('H' => 0, 'V' => 0);
16074  }
16075  }
16076  if ($width == 0) {
16077  $width = $this->w - $this->lMargin - $this->rMargin;
16078  }
16079  $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
16080  $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
16081  return $border_spacing;
16082  }
16083 
16092  protected function getCSSFontSpacing($spacing, $parent=0) {
16093  $val = 0; // value to be returned
16094  $spacing = trim($spacing);
16095  switch ($spacing) {
16096  case 'normal': {
16097  $val = 0;
16098  break;
16099  }
16100  case 'inherit': {
16101  if ($parent == 'normal') {
16102  $val = 0;
16103  } else {
16104  $val = $parent;
16105  }
16106  break;
16107  }
16108  default: {
16109  $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
16110  }
16111  }
16112  return $val;
16113  }
16114 
16123  protected function getCSSFontStretching($stretch, $parent=100) {
16124  $val = 100; // value to be returned
16125  $stretch = trim($stretch);
16126  switch ($stretch) {
16127  case 'ultra-condensed': {
16128  $val = 40;
16129  break;
16130  }
16131  case 'extra-condensed': {
16132  $val = 55;
16133  break;
16134  }
16135  case 'condensed': {
16136  $val = 70;
16137  break;
16138  }
16139  case 'semi-condensed': {
16140  $val = 85;
16141  break;
16142  }
16143  case 'normal': {
16144  $val = 100;
16145  break;
16146  }
16147  case 'semi-expanded': {
16148  $val = 115;
16149  break;
16150  }
16151  case 'expanded': {
16152  $val = 130;
16153  break;
16154  }
16155  case 'extra-expanded': {
16156  $val = 145;
16157  break;
16158  }
16159  case 'ultra-expanded': {
16160  $val = 160;
16161  break;
16162  }
16163  case 'wider': {
16164  $val = ($parent + 10);
16165  break;
16166  }
16167  case 'narrower': {
16168  $val = ($parent - 10);
16169  break;
16170  }
16171  case 'inherit': {
16172  if ($parent == 'normal') {
16173  $val = 100;
16174  } else {
16175  $val = $parent;
16176  }
16177  break;
16178  }
16179  default: {
16180  $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
16181  }
16182  }
16183  return $val;
16184  }
16185 
16195  public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') {
16196  $refsize = TCPDF_FONTS::getFontRefSize($refsize);
16197  $parent_size = TCPDF_FONTS::getFontRefSize($parent_size, $refsize);
16198  switch ($val) {
16199  case 'xx-small': {
16200  $size = ($refsize - 4);
16201  break;
16202  }
16203  case 'x-small': {
16204  $size = ($refsize - 3);
16205  break;
16206  }
16207  case 'small': {
16208  $size = ($refsize - 2);
16209  break;
16210  }
16211  case 'medium': {
16212  $size = $refsize;
16213  break;
16214  }
16215  case 'large': {
16216  $size = ($refsize + 2);
16217  break;
16218  }
16219  case 'x-large': {
16220  $size = ($refsize + 4);
16221  break;
16222  }
16223  case 'xx-large': {
16224  $size = ($refsize + 6);
16225  break;
16226  }
16227  case 'smaller': {
16228  $size = ($parent_size - 3);
16229  break;
16230  }
16231  case 'larger': {
16232  $size = ($parent_size + 3);
16233  break;
16234  }
16235  default: {
16236  $size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true);
16237  }
16238  }
16239  return $size;
16240  }
16241 
16249  protected function getHtmlDomArray($html) {
16250  // array of CSS styles ( selector => properties).
16251  $css = array();
16252  // get CSS array defined at previous call
16253  $matches = array();
16254  if (preg_match_all('/<cssarray>([^<]*)<\/cssarray>/isU', $html, $matches) > 0) {
16255  if (isset($matches[1][0])) {
16256  $css = array_merge($css, json_decode($this->unhtmlentities($matches[1][0]), true));
16257  }
16258  $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
16259  }
16260  // extract external CSS files
16261  $matches = array();
16262  if (preg_match_all('/<link([^>]*)>/isU', $html, $matches) > 0) {
16263  foreach ($matches[1] as $key => $link) {
16264  $type = array();
16265  if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
16266  $type = array();
16267  preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
16268  // get 'all' and 'print' media, other media types are discarded
16269  // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16270  if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16271  $type = array();
16272  if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
16273  // read CSS data file
16274  $cssdata = TCPDF_STATIC::fileGetContents(trim($type[1]));
16275  if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) {
16276  $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16277  }
16278  }
16279  }
16280  }
16281  }
16282  }
16283  // extract style tags
16284  $matches = array();
16285  if (preg_match_all('/<style([^>]*)>([^<]*)<\/style>/isU', $html, $matches) > 0) {
16286  foreach ($matches[1] as $key => $media) {
16287  $type = array();
16288  preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
16289  // get 'all' and 'print' media, other media types are discarded
16290  // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16291  if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16292  $cssdata = $matches[2][$key];
16293  $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16294  }
16295  }
16296  }
16297  // create a special tag to contain the CSS array (used for table content)
16298  $csstagarray = '<cssarray>'.htmlentities(json_encode($css)).'</cssarray>';
16299  // remove head and style blocks
16300  $html = preg_replace('/<head([^>]*)>(.*?)<\/head>/siU', '', $html);
16301  $html = preg_replace('/<style([^>]*)>([^<]*)<\/style>/isU', '', $html);
16302  // define block tags
16303  $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
16304  // define self-closing tags
16305  $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
16306  // remove all unsupported tags (the line below lists all supported tags)
16307  $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>');
16308  //replace some blank characters
16309  $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
16310  $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);
16311  $html = preg_replace('@(\r\n|\r)@', "\n", $html);
16312  $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
16313  $html = strtr($html, $repTable);
16314  $offset = 0;
16315  while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
16316  $html_a = substr($html, 0, $offset);
16317  $html_b = substr($html, $offset, ($pos - $offset + 6));
16318  while (preg_match("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
16319  // preserve newlines on <pre> tag
16320  $html_b = preg_replace("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
16321  }
16322  while (preg_match("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) {
16323  // preserve spaces on <pre> tag
16324  $html_b = preg_replace("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2&nbsp;\\3</pre>", $html_b);
16325  }
16326  $html = $html_a.$html_b.substr($html, $pos + 6);
16327  $offset = strlen($html_a.$html_b);
16328  }
16329  $offset = 0;
16330  while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
16331  $html_a = substr($html, 0, $offset);
16332  $html_b = substr($html, $offset, ($pos - $offset + 11));
16333  while (preg_match("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
16334  // preserve newlines on <textarea> tag
16335  $html_b = preg_replace("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
16336  $html_b = preg_replace("'<textarea([^>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
16337  }
16338  $html = $html_a.$html_b.substr($html, $pos + 11);
16339  $offset = strlen($html_a.$html_b);
16340  }
16341  $html = preg_replace('/([\s]*)<option/si', '<option', $html);
16342  $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
16343  $offset = 0;
16344  while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
16345  $html_a = substr($html, 0, $offset);
16346  $html_b = substr($html, $offset, ($pos - $offset + 9));
16347  while (preg_match("'<option([^>]*)>(.*?)</option>'si", $html_b)) {
16348  $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
16349  $html_b = preg_replace("'<option([^>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
16350  }
16351  $html = $html_a.$html_b.substr($html, $pos + 9);
16352  $offset = strlen($html_a.$html_b);
16353  }
16354  if (preg_match("'</select'si", $html)) {
16355  $html = preg_replace("'<select([^>]*)>'si", "<select\\1 opt=\"", $html);
16356  $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
16357  }
16358  $html = str_replace("\n", ' ', $html);
16359  // restore textarea newlines
16360  $html = str_replace('<TBR>', "\n", $html);
16361  // remove extra spaces from code
16362  $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
16363  $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html);
16364  $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
16365  $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html);
16366  $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);
16367  $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
16368  $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
16369  $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html);
16370  $html = preg_replace('/<img([^>]*)>[\s]+([^<])/xi', '<img\\1>&nbsp;\\2', $html);
16371  $html = preg_replace('/<img([^>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
16372  $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
16373  $html = preg_replace('/<textarea([^>]*)>([^<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
16374  $html = preg_replace('/<li([^>]*)><\/li>/', '<li\\1>&nbsp;</li>', $html);
16375  $html = preg_replace('/<li([^>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1">&nbsp;</font><img', $html);
16376  $html = preg_replace('/<([^>\/]*)>[\s]/', '<\\1>&nbsp;', $html); // preserve some spaces
16377  $html = preg_replace('/[\s]<\/([^>]*)>/', '&nbsp;</\\1>', $html); // preserve some spaces
16378  $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment
16379  $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment
16380  $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space
16381  // trim string
16382  $html = $this->stringTrim($html);
16383  // fix br tag after li
16384  $html = preg_replace('/<li><br([^>]*)>/', '<li> <br\\1>', $html);
16385  // fix first image tag alignment
16386  $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
16387  // pattern for generic tag
16388  $tagpattern = '/(<[^>]+>)/';
16389  // explodes the string
16390  $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
16391  // count elements
16392  $maxel = count($a);
16393  $elkey = 0;
16394  $key = 0;
16395  // create an array of elements
16396  $dom = array();
16397  $dom[$key] = array();
16398  // set inheritable properties fot the first void element
16399  // 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
16400  $dom[$key]['tag'] = false;
16401  $dom[$key]['block'] = false;
16402  $dom[$key]['value'] = '';
16403  $dom[$key]['parent'] = 0;
16404  $dom[$key]['hide'] = false;
16405  $dom[$key]['fontname'] = $this->FontFamily;
16406  $dom[$key]['fontstyle'] = $this->FontStyle;
16407  $dom[$key]['fontsize'] = $this->FontSizePt;
16408  $dom[$key]['font-stretch'] = $this->font_stretching;
16409  $dom[$key]['letter-spacing'] = $this->font_spacing;
16410  $dom[$key]['stroke'] = $this->textstrokewidth;
16411  $dom[$key]['fill'] = (($this->textrendermode % 2) == 0);
16412  $dom[$key]['clip'] = ($this->textrendermode > 3);
16413  $dom[$key]['line-height'] = $this->cell_height_ratio;
16414  $dom[$key]['bgcolor'] = false;
16415  $dom[$key]['fgcolor'] = $this->fgcolor; // color
16416  $dom[$key]['strokecolor'] = $this->strokecolor;
16417  $dom[$key]['align'] = '';
16418  $dom[$key]['listtype'] = '';
16419  $dom[$key]['text-indent'] = 0;
16420  $dom[$key]['text-transform'] = '';
16421  $dom[$key]['border'] = array();
16422  $dom[$key]['dir'] = $this->rtl?'rtl':'ltr';
16423  $thead = false; // true when we are inside the THEAD tag
16424  ++$key;
16425  $level = array();
16426  array_push($level, 0); // root
16427  while ($elkey < $maxel) {
16428  $dom[$key] = array();
16429  $element = $a[$elkey];
16430  $dom[$key]['elkey'] = $elkey;
16431  if (preg_match($tagpattern, $element)) {
16432  // html tag
16433  $element = substr($element, 1, -1);
16434  // get tag name
16435  preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
16436  $tagname = strtolower($tag[1]);
16437  // check if we are inside a table header
16438  if ($tagname == 'thead') {
16439  if ($element[0] == '/') {
16440  $thead = false;
16441  } else {
16442  $thead = true;
16443  }
16444  ++$elkey;
16445  continue;
16446  }
16447  $dom[$key]['tag'] = true;
16448  $dom[$key]['value'] = $tagname;
16449  if (in_array($dom[$key]['value'], $blocktags)) {
16450  $dom[$key]['block'] = true;
16451  } else {
16452  $dom[$key]['block'] = false;
16453  }
16454  if ($element[0] == '/') {
16455  // *** closing html tag
16456  $dom[$key]['opening'] = false;
16457  $dom[$key]['parent'] = end($level);
16458  array_pop($level);
16459  $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
16460  $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
16461  $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
16462  $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
16463  $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
16464  $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
16465  $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
16466  $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
16467  $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
16468  $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
16469  $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
16470  $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
16471  $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
16472  $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
16473  $dom[$key]['text-transform'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['text-transform'];
16474  $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
16475  if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
16476  $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
16477  }
16478  // set the number of columns in table tag
16479  if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
16480  $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
16481  }
16482  if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
16483  $dom[($dom[$key]['parent'])]['content'] = $csstagarray;
16484  for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
16485  $dom[($dom[$key]['parent'])]['content'] .= stripslashes($a[$dom[$i]['elkey']]);
16486  }
16487  $key = $i;
16488  // mark nested tables
16489  $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
16490  // remove thead sections from nested tables
16491  $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
16492  $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
16493  }
16494  // store header rows on a new table
16495  if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
16496  if (TCPDF_STATIC::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
16497  $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
16498  }
16499  for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
16500  $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
16501  }
16502  if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
16503  $dom[($dom[$key]['parent'])]['attribute'] = array();
16504  }
16505  // header elements must be always contained in a single page
16506  $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
16507  }
16508  if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC::empty_string($dom[($dom[$key]['parent'])]['thead']))) {
16509  // remove the nobr attributes from the table header
16510  $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
16511  $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
16512  }
16513  } else {
16514  // *** opening or self-closing html tag
16515  $dom[$key]['opening'] = true;
16516  $dom[$key]['parent'] = end($level);
16517  if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
16518  // self-closing tag
16519  $dom[$key]['self'] = true;
16520  } else {
16521  // opening tag
16522  array_push($level, $key);
16523  $dom[$key]['self'] = false;
16524  }
16525  // copy some values from parent
16526  $parentkey = 0;
16527  if ($key > 0) {
16528  $parentkey = $dom[$key]['parent'];
16529  $dom[$key]['hide'] = $dom[$parentkey]['hide'];
16530  $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
16531  $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
16532  $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
16533  $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
16534  $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
16535  $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
16536  $dom[$key]['fill'] = $dom[$parentkey]['fill'];
16537  $dom[$key]['clip'] = $dom[$parentkey]['clip'];
16538  $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16539  $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
16540  $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
16541  $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
16542  $dom[$key]['align'] = $dom[$parentkey]['align'];
16543  $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16544  $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16545  $dom[$key]['text-transform'] = $dom[$parentkey]['text-transform'];
16546  $dom[$key]['border'] = array();
16547  $dom[$key]['dir'] = $dom[$parentkey]['dir'];
16548  }
16549  // get attributes
16550  preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER);
16551  $dom[$key]['attribute'] = array(); // reset attribute array
16552  while (list($id, $name) = each($attr_array[1])) {
16553  $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
16554  }
16555  if (!empty($css)) {
16556  // merge CSS style to current style
16557  list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC::getCSSdataArray($dom, $key, $css);
16558  $dom[$key]['attribute']['style'] = TCPDF_STATIC::getTagStyleFromCSSarray($dom[$key]['cssdata']);
16559  }
16560  // split style attributes
16561  if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
16562  // get style attributes
16563  preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
16564  $dom[$key]['style'] = array(); // reset style attribute array
16565  while (list($id, $name) = each($style_array[1])) {
16566  // in case of duplicate attribute the last replace the previous
16567  $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
16568  }
16569  // --- get some style attributes ---
16570  // text direction
16571  if (isset($dom[$key]['style']['direction'])) {
16572  $dom[$key]['dir'] = $dom[$key]['style']['direction'];
16573  }
16574  // display
16575  if (isset($dom[$key]['style']['display'])) {
16576  $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
16577  }
16578  // font family
16579  if (isset($dom[$key]['style']['font-family'])) {
16580  $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
16581  }
16582  // list-style-type
16583  if (isset($dom[$key]['style']['list-style-type'])) {
16584  $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
16585  if ($dom[$key]['listtype'] == 'inherit') {
16586  $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16587  }
16588  }
16589  // text-indent
16590  if (isset($dom[$key]['style']['text-indent'])) {
16591  $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
16592  if ($dom[$key]['text-indent'] == 'inherit') {
16593  $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16594  }
16595  }
16596  // text-transform
16597  if (isset($dom[$key]['style']['text-transform'])) {
16598  $dom[$key]['text-transform'] = $dom[$key]['style']['text-transform'];
16599  }
16600  // font size
16601  if (isset($dom[$key]['style']['font-size'])) {
16602  $fsize = trim($dom[$key]['style']['font-size']);
16603  $dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt');
16604  }
16605  // font-stretch
16606  if (isset($dom[$key]['style']['font-stretch'])) {
16607  $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
16608  }
16609  // letter-spacing
16610  if (isset($dom[$key]['style']['letter-spacing'])) {
16611  $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
16612  }
16613  // line-height (internally is the cell height ratio)
16614  if (isset($dom[$key]['style']['line-height'])) {
16615  $lineheight = trim($dom[$key]['style']['line-height']);
16616  switch ($lineheight) {
16617  // A normal line height. This is default
16618  case 'normal': {
16619  $dom[$key]['line-height'] = $dom[0]['line-height'];
16620  break;
16621  }
16622  case 'inherit': {
16623  $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16624  }
16625  default: {
16626  if (is_numeric($lineheight)) {
16627  // convert to percentage of font height
16628  $lineheight = ($lineheight * 100).'%';
16629  }
16630  $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
16631  if (substr($lineheight, -1) !== '%') {
16632  if ($dom[$key]['fontsize'] <= 0) {
16633  $dom[$key]['line-height'] = 1;
16634  } else {
16635  $dom[$key]['line-height'] = (($dom[$key]['line-height'] - $this->cell_padding['T'] - $this->cell_padding['B']) / $dom[$key]['fontsize']);
16636  }
16637  }
16638  }
16639  }
16640  }
16641  // font style
16642  if (isset($dom[$key]['style']['font-weight'])) {
16643  if (strtolower($dom[$key]['style']['font-weight'][0]) == 'n') {
16644  if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
16645  $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
16646  }
16647  } elseif (strtolower($dom[$key]['style']['font-weight'][0]) == 'b') {
16648  $dom[$key]['fontstyle'] .= 'B';
16649  }
16650  }
16651  if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style'][0]) == 'i')) {
16652  $dom[$key]['fontstyle'] .= 'I';
16653  }
16654  // font color
16655  if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['color']))) {
16656  $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors);
16657  } elseif ($dom[$key]['value'] == 'a') {
16658  $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16659  }
16660  // background color
16661  if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['background-color']))) {
16662  $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors);
16663  }
16664  // text-decoration
16665  if (isset($dom[$key]['style']['text-decoration'])) {
16666  $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
16667  foreach ($decors as $dec) {
16668  $dec = trim($dec);
16669  if (!TCPDF_STATIC::empty_string($dec)) {
16670  if ($dec[0] == 'u') {
16671  // underline
16672  $dom[$key]['fontstyle'] .= 'U';
16673  } elseif ($dec[0] == 'l') {
16674  // line-through
16675  $dom[$key]['fontstyle'] .= 'D';
16676  } elseif ($dec[0] == 'o') {
16677  // overline
16678  $dom[$key]['fontstyle'] .= 'O';
16679  }
16680  }
16681  }
16682  } elseif ($dom[$key]['value'] == 'a') {
16683  $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16684  }
16685  // check for width attribute
16686  if (isset($dom[$key]['style']['width'])) {
16687  $dom[$key]['width'] = $dom[$key]['style']['width'];
16688  }
16689  // check for height attribute
16690  if (isset($dom[$key]['style']['height'])) {
16691  $dom[$key]['height'] = $dom[$key]['style']['height'];
16692  }
16693  // check for text alignment
16694  if (isset($dom[$key]['style']['text-align'])) {
16695  $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align'][0]);
16696  }
16697  // check for CSS border properties
16698  if (isset($dom[$key]['style']['border'])) {
16699  $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
16700  if (!empty($borderstyle)) {
16701  $dom[$key]['border']['LTRB'] = $borderstyle;
16702  }
16703  }
16704  if (isset($dom[$key]['style']['border-color'])) {
16705  $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
16706  if (isset($brd_colors[3])) {
16707  $dom[$key]['border']['L']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[3], $this->spot_colors);
16708  }
16709  if (isset($brd_colors[1])) {
16710  $dom[$key]['border']['R']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[1], $this->spot_colors);
16711  }
16712  if (isset($brd_colors[0])) {
16713  $dom[$key]['border']['T']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[0], $this->spot_colors);
16714  }
16715  if (isset($brd_colors[2])) {
16716  $dom[$key]['border']['B']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[2], $this->spot_colors);
16717  }
16718  }
16719  if (isset($dom[$key]['style']['border-width'])) {
16720  $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
16721  if (isset($brd_widths[3])) {
16722  $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
16723  }
16724  if (isset($brd_widths[1])) {
16725  $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
16726  }
16727  if (isset($brd_widths[0])) {
16728  $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
16729  }
16730  if (isset($brd_widths[2])) {
16731  $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
16732  }
16733  }
16734  if (isset($dom[$key]['style']['border-style'])) {
16735  $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
16736  if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) {
16737  $dom[$key]['border']['L']['cap'] = 'square';
16738  $dom[$key]['border']['L']['join'] = 'miter';
16739  $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
16740  if ($dom[$key]['border']['L']['dash'] < 0) {
16741  $dom[$key]['border']['L'] = array();
16742  }
16743  }
16744  if (isset($brd_styles[1])) {
16745  $dom[$key]['border']['R']['cap'] = 'square';
16746  $dom[$key]['border']['R']['join'] = 'miter';
16747  $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
16748  if ($dom[$key]['border']['R']['dash'] < 0) {
16749  $dom[$key]['border']['R'] = array();
16750  }
16751  }
16752  if (isset($brd_styles[0])) {
16753  $dom[$key]['border']['T']['cap'] = 'square';
16754  $dom[$key]['border']['T']['join'] = 'miter';
16755  $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
16756  if ($dom[$key]['border']['T']['dash'] < 0) {
16757  $dom[$key]['border']['T'] = array();
16758  }
16759  }
16760  if (isset($brd_styles[2])) {
16761  $dom[$key]['border']['B']['cap'] = 'square';
16762  $dom[$key]['border']['B']['join'] = 'miter';
16763  $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
16764  if ($dom[$key]['border']['B']['dash'] < 0) {
16765  $dom[$key]['border']['B'] = array();
16766  }
16767  }
16768  }
16769  $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
16770  foreach ($cellside as $bsk => $bsv) {
16771  if (isset($dom[$key]['style']['border-'.$bsv])) {
16772  $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
16773  if (!empty($borderstyle)) {
16774  $dom[$key]['border'][$bsk] = $borderstyle;
16775  }
16776  }
16777  if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
16778  $dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors);
16779  }
16780  if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
16781  $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
16782  }
16783  if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
16784  $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
16785  if ($dom[$key]['border'][$bsk]['dash'] < 0) {
16786  $dom[$key]['border'][$bsk] = array();
16787  }
16788  }
16789  }
16790  // check for CSS padding properties
16791  if (isset($dom[$key]['style']['padding'])) {
16792  $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
16793  } else {
16794  $dom[$key]['padding'] = $this->cell_padding;
16795  }
16796  foreach ($cellside as $psk => $psv) {
16797  if (isset($dom[$key]['style']['padding-'.$psv])) {
16798  $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
16799  }
16800  }
16801  // check for CSS margin properties
16802  if (isset($dom[$key]['style']['margin'])) {
16803  $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
16804  } else {
16805  $dom[$key]['margin'] = $this->cell_margin;
16806  }
16807  foreach ($cellside as $psk => $psv) {
16808  if (isset($dom[$key]['style']['margin-'.$psv])) {
16809  $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
16810  }
16811  }
16812  // check for CSS border-spacing properties
16813  if (isset($dom[$key]['style']['border-spacing'])) {
16814  $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
16815  }
16816  // page-break-inside
16817  if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
16818  $dom[$key]['attribute']['nobr'] = 'true';
16819  }
16820  // page-break-before
16821  if (isset($dom[$key]['style']['page-break-before'])) {
16822  if ($dom[$key]['style']['page-break-before'] == 'always') {
16823  $dom[$key]['attribute']['pagebreak'] = 'true';
16824  } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
16825  $dom[$key]['attribute']['pagebreak'] = 'left';
16826  } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
16827  $dom[$key]['attribute']['pagebreak'] = 'right';
16828  }
16829  }
16830  // page-break-after
16831  if (isset($dom[$key]['style']['page-break-after'])) {
16832  if ($dom[$key]['style']['page-break-after'] == 'always') {
16833  $dom[$key]['attribute']['pagebreakafter'] = 'true';
16834  } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
16835  $dom[$key]['attribute']['pagebreakafter'] = 'left';
16836  } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
16837  $dom[$key]['attribute']['pagebreakafter'] = 'right';
16838  }
16839  }
16840  }
16841  if (isset($dom[$key]['attribute']['display'])) {
16842  $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
16843  }
16844  if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
16845  $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
16846  if (!empty($borderstyle)) {
16847  $dom[$key]['border']['LTRB'] = $borderstyle;
16848  }
16849  }
16850  // check for font tag
16851  if ($dom[$key]['value'] == 'font') {
16852  // font family
16853  if (isset($dom[$key]['attribute']['face'])) {
16854  $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
16855  }
16856  // font size
16857  if (isset($dom[$key]['attribute']['size'])) {
16858  if ($key > 0) {
16859  if ($dom[$key]['attribute']['size'][0] == '+') {
16860  $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
16861  } elseif ($dom[$key]['attribute']['size'][0] == '-') {
16862  $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
16863  } else {
16864  $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16865  }
16866  } else {
16867  $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16868  }
16869  }
16870  }
16871  // force natural alignment for lists
16872  if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
16873  AND (!isset($dom[$key]['align']) OR TCPDF_STATIC::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
16874  if ($this->rtl) {
16875  $dom[$key]['align'] = 'R';
16876  } else {
16877  $dom[$key]['align'] = 'L';
16878  }
16879  }
16880  if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
16881  if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16882  $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
16883  }
16884  }
16885  if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
16886  $dom[$key]['fontstyle'] .= 'B';
16887  }
16888  if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
16889  $dom[$key]['fontstyle'] .= 'I';
16890  }
16891  if ($dom[$key]['value'] == 'u') {
16892  $dom[$key]['fontstyle'] .= 'U';
16893  }
16894  if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
16895  $dom[$key]['fontstyle'] .= 'D';
16896  }
16897  if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
16898  $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16899  }
16900  if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
16901  $dom[$key]['fontname'] = $this->default_monospaced_font;
16902  }
16903  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)) {
16904  // headings h1, h2, h3, h4, h5, h6
16905  if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16906  $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
16907  $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
16908  }
16909  if (!isset($dom[$key]['style']['font-weight'])) {
16910  $dom[$key]['fontstyle'] .= 'B';
16911  }
16912  }
16913  if (($dom[$key]['value'] == 'table')) {
16914  $dom[$key]['rows'] = 0; // number of rows
16915  $dom[$key]['trids'] = array(); // IDs of TR elements
16916  $dom[$key]['thead'] = ''; // table header rows
16917  }
16918  if (($dom[$key]['value'] == 'tr')) {
16919  $dom[$key]['cols'] = 0;
16920  if ($thead) {
16921  $dom[$key]['thead'] = true;
16922  // rows on thead block are printed as a separate table
16923  } else {
16924  $dom[$key]['thead'] = false;
16925  // store the number of rows on table element
16926  ++$dom[($dom[$key]['parent'])]['rows'];
16927  // store the TR elements IDs on table element
16928  array_push($dom[($dom[$key]['parent'])]['trids'], $key);
16929  }
16930  }
16931  if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
16932  if (isset($dom[$key]['attribute']['colspan'])) {
16933  $colspan = intval($dom[$key]['attribute']['colspan']);
16934  } else {
16935  $colspan = 1;
16936  }
16937  $dom[$key]['attribute']['colspan'] = $colspan;
16938  $dom[($dom[$key]['parent'])]['cols'] += $colspan;
16939  }
16940  // text direction
16941  if (isset($dom[$key]['attribute']['dir'])) {
16942  $dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
16943  }
16944  // set foreground color attribute
16945  if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['color']))) {
16946  $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors);
16947  } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
16948  $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16949  }
16950  // set background color attribute
16951  if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['bgcolor']))) {
16952  $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors);
16953  }
16954  // set stroke color attribute
16955  if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['strokecolor']))) {
16956  $dom[$key]['strokecolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors);
16957  }
16958  // check for width attribute
16959  if (isset($dom[$key]['attribute']['width'])) {
16960  $dom[$key]['width'] = $dom[$key]['attribute']['width'];
16961  }
16962  // check for height attribute
16963  if (isset($dom[$key]['attribute']['height'])) {
16964  $dom[$key]['height'] = $dom[$key]['attribute']['height'];
16965  }
16966  // check for text alignment
16967  if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
16968  $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align'][0]);
16969  }
16970  // check for text rendering mode (the following attributes do not exist in HTML)
16971  if (isset($dom[$key]['attribute']['stroke'])) {
16972  // font stroke width
16973  $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
16974  }
16975  if (isset($dom[$key]['attribute']['fill'])) {
16976  // font fill
16977  if ($dom[$key]['attribute']['fill'] == 'true') {
16978  $dom[$key]['fill'] = true;
16979  } else {
16980  $dom[$key]['fill'] = false;
16981  }
16982  }
16983  if (isset($dom[$key]['attribute']['clip'])) {
16984  // clipping mode
16985  if ($dom[$key]['attribute']['clip'] == 'true') {
16986  $dom[$key]['clip'] = true;
16987  } else {
16988  $dom[$key]['clip'] = false;
16989  }
16990  }
16991  } // end opening tag
16992  } else {
16993  // text
16994  $dom[$key]['tag'] = false;
16995  $dom[$key]['block'] = false;
16996  $dom[$key]['parent'] = end($level);
16997  $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
16998  if (!empty($dom[$dom[$key]['parent']]['text-transform'])) {
16999  // text-transform for unicode requires mb_convert_case (Multibyte String Functions)
17000  if (function_exists('mb_convert_case')) {
17001  $ttm = array('capitalize' => MB_CASE_TITLE, 'uppercase' => MB_CASE_UPPER, 'lowercase' => MB_CASE_LOWER);
17002  if (isset($ttm[$dom[$dom[$key]['parent']]['text-transform']])) {
17003  $element = mb_convert_case($element, $ttm[$dom[$dom[$key]['parent']]['text-transform']], $this->encoding);
17004  }
17005  } elseif (!$this->isunicode) {
17006  switch ($dom[$dom[$key]['parent']]['text-transform']) {
17007  case 'capitalize': {
17008  $element = ucwords(strtolower($element));
17009  break;
17010  }
17011  case 'uppercase': {
17012  $element = strtoupper($element);
17013  break;
17014  }
17015  case 'lowercase': {
17016  $element = strtolower($element);
17017  break;
17018  }
17019  }
17020  }
17021  }
17022  $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
17023  }
17024  ++$elkey;
17025  ++$key;
17026  }
17027  return $dom;
17028  }
17029 
17037  protected function getSpaceString() {
17038  $spacestr = chr(32);
17039  if ($this->isUnicodeFont()) {
17040  $spacestr = chr(0).chr(32);
17041  }
17042  return $spacestr;
17043  }
17044 
17051  protected function getHashForTCPDFtagParams($data) {
17052  return md5(strlen($data).$this->file_id.$data);
17053  }
17054 
17062  $encoded = urlencode(json_encode($data));
17063  return $this->getHashForTCPDFtagParams($encoded).$encoded;
17064  }
17065 
17072  protected function unserializeTCPDFtagParameters($data) {
17073  $hash = substr($data, 0, 32);
17074  $encoded = substr($data, 32);
17075  if ($hash != $this->getHashForTCPDFtagParams($encoded)) {
17076  $this->Error('Invalid parameters');
17077  }
17078  return json_decode(urldecode($encoded), true);
17079  }
17080 
17103  public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
17104  return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
17105  }
17106 
17120  public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
17121  $gvars = $this->getGraphicVars();
17122  // store current values
17123  $prev_cell_margin = $this->cell_margin;
17124  $prev_cell_padding = $this->cell_padding;
17125  $prevPage = $this->page;
17126  $prevlMargin = $this->lMargin;
17127  $prevrMargin = $this->rMargin;
17128  $curfontname = $this->FontFamily;
17129  $curfontstyle = $this->FontStyle;
17130  $curfontsize = $this->FontSizePt;
17131  $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
17132  $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
17133  $curfontstretcing = $this->font_stretching;
17134  $curfonttracking = $this->font_spacing;
17135  $this->newline = true;
17136  $newline = true;
17137  $startlinepage = $this->page;
17138  $minstartliney = $this->y;
17139  $maxbottomliney = 0;
17140  $startlinex = $this->x;
17141  $startliney = $this->y;
17142  $yshift = 0;
17143  $loop = 0;
17144  $curpos = 0;
17145  $this_method_vars = array();
17146  $undo = false;
17147  $fontaligned = false;
17148  $reverse_dir = false; // true when the text direction is reversed
17149  $this->premode = false;
17150  if ($this->inxobj) {
17151  // we are inside an XObject template
17152  $pask = count($this->xobjects[$this->xobjid]['annotations']);
17153  } elseif (isset($this->PageAnnots[$this->page])) {
17154  $pask = count($this->PageAnnots[$this->page]);
17155  } else {
17156  $pask = 0;
17157  }
17158  if ($this->inxobj) {
17159  // we are inside an XObject template
17160  $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17161  } elseif (!$this->InFooter) {
17162  if (isset($this->footerlen[$this->page])) {
17163  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17164  } else {
17165  $this->footerpos[$this->page] = $this->pagelen[$this->page];
17166  }
17167  $startlinepos = $this->footerpos[$this->page];
17168  } else {
17169  // we are inside the footer
17170  $startlinepos = $this->pagelen[$this->page];
17171  }
17172  $lalign = $align;
17173  $plalign = $align;
17174  if ($this->rtl) {
17175  $w = $this->x - $this->lMargin;
17176  } else {
17177  $w = $this->w - $this->rMargin - $this->x;
17178  }
17179  $w -= ($this->cell_padding['L'] + $this->cell_padding['R']);
17180  if ($cell) {
17181  if ($this->rtl) {
17182  $this->x -= $this->cell_padding['R'];
17183  $this->lMargin += $this->cell_padding['R'];
17184  } else {
17185  $this->x += $this->cell_padding['L'];
17186  $this->rMargin += $this->cell_padding['L'];
17187  }
17188  }
17189  if ($this->customlistindent >= 0) {
17190  $this->listindent = $this->customlistindent;
17191  } else {
17192  $this->listindent = $this->GetStringWidth('000000');
17193  }
17194  $this->listindentlevel = 0;
17195  // save previous states
17196  $prev_cell_height_ratio = $this->cell_height_ratio;
17197  $prev_listnum = $this->listnum;
17198  $prev_listordered = $this->listordered;
17199  $prev_listcount = $this->listcount;
17200  $prev_lispacer = $this->lispacer;
17201  $this->listnum = 0;
17202  $this->listordered = array();
17203  $this->listcount = array();
17204  $this->lispacer = '';
17205  if ((TCPDF_STATIC::empty_string($this->lasth)) OR ($reseth)) {
17206  // reset row height
17207  $this->resetLastH();
17208  }
17209  $dom = $this->getHtmlDomArray($html);
17210  $maxel = count($dom);
17211  $key = 0;
17212  while ($key < $maxel) {
17213  if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND $dom[$key]['hide']) {
17214  // store the node key
17215  $hidden_node_key = $key;
17216  if ($dom[$key]['self']) {
17217  // skip just this self-closing tag
17218  ++$key;
17219  } else {
17220  // skip this and all children tags
17221  while (($key < $maxel) AND (!$dom[$key]['tag'] OR $dom[$key]['opening'] OR ($dom[$key]['parent'] != $hidden_node_key))) {
17222  // skip hidden objects
17223  ++$key;
17224  }
17225  ++$key;
17226  }
17227  }
17228  if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
17229  // check for pagebreak
17230  if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
17231  // add a page (or trig AcceptPageBreak() for multicolumn mode)
17232  $this->checkPageBreak($this->PageBreakTrigger + 1);
17233  $this->htmlvspace = ($this->PageBreakTrigger + 1);
17234  }
17235  if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
17236  OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
17237  // add a page (or trig AcceptPageBreak() for multicolumn mode)
17238  $this->checkPageBreak($this->PageBreakTrigger + 1);
17239  $this->htmlvspace = ($this->PageBreakTrigger + 1);
17240  }
17241  }
17242  if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
17243  if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
17244  $dom[$key]['attribute']['nobr'] = false;
17245  } else {
17246  // store current object
17247  $this->startTransaction();
17248  // save this method vars
17249  $this_method_vars['html'] = $html;
17250  $this_method_vars['ln'] = $ln;
17251  $this_method_vars['fill'] = $fill;
17252  $this_method_vars['reseth'] = $reseth;
17253  $this_method_vars['cell'] = $cell;
17254  $this_method_vars['align'] = $align;
17255  $this_method_vars['gvars'] = $gvars;
17256  $this_method_vars['prevPage'] = $prevPage;
17257  $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
17258  $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
17259  $this_method_vars['prevlMargin'] = $prevlMargin;
17260  $this_method_vars['prevrMargin'] = $prevrMargin;
17261  $this_method_vars['curfontname'] = $curfontname;
17262  $this_method_vars['curfontstyle'] = $curfontstyle;
17263  $this_method_vars['curfontsize'] = $curfontsize;
17264  $this_method_vars['curfontascent'] = $curfontascent;
17265  $this_method_vars['curfontdescent'] = $curfontdescent;
17266  $this_method_vars['curfontstretcing'] = $curfontstretcing;
17267  $this_method_vars['curfonttracking'] = $curfonttracking;
17268  $this_method_vars['minstartliney'] = $minstartliney;
17269  $this_method_vars['maxbottomliney'] = $maxbottomliney;
17270  $this_method_vars['yshift'] = $yshift;
17271  $this_method_vars['startlinepage'] = $startlinepage;
17272  $this_method_vars['startlinepos'] = $startlinepos;
17273  $this_method_vars['startlinex'] = $startlinex;
17274  $this_method_vars['startliney'] = $startliney;
17275  $this_method_vars['newline'] = $newline;
17276  $this_method_vars['loop'] = $loop;
17277  $this_method_vars['curpos'] = $curpos;
17278  $this_method_vars['pask'] = $pask;
17279  $this_method_vars['lalign'] = $lalign;
17280  $this_method_vars['plalign'] = $plalign;
17281  $this_method_vars['w'] = $w;
17282  $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
17283  $this_method_vars['prev_listnum'] = $prev_listnum;
17284  $this_method_vars['prev_listordered'] = $prev_listordered;
17285  $this_method_vars['prev_listcount'] = $prev_listcount;
17286  $this_method_vars['prev_lispacer'] = $prev_lispacer;
17287  $this_method_vars['fontaligned'] = $fontaligned;
17288  $this_method_vars['key'] = $key;
17289  $this_method_vars['dom'] = $dom;
17290  }
17291  }
17292  // print THEAD block
17293  if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
17294  if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC::empty_string($dom[$dom[$key]['parent']]['thead'])) {
17295  $this->inthead = true;
17296  // print table header (thead)
17297  $this->writeHTML($this->thead, false, false, false, false, '');
17298  // check if we are on a new page or on a new column
17299  if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
17300  // we are on a new page or on a new column and the total object height is less than the available vertical space.
17301  // restore previous object
17302  $this->rollbackTransaction(true);
17303  // restore previous values
17304  foreach ($this_method_vars as $vkey => $vval) {
17305  $$vkey = $vval;
17306  }
17307  // disable table header
17308  $tmp_thead = $this->thead;
17309  $this->thead = '';
17310  // add a page (or trig AcceptPageBreak() for multicolumn mode)
17311  $pre_y = $this->y;
17312  if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
17313  // fix for multicolumn mode
17314  $startliney = $this->y;
17315  }
17316  $this->start_transaction_page = $this->page;
17317  $this->start_transaction_y = $this->y;
17318  // restore table header
17319  $this->thead = $tmp_thead;
17320  // fix table border properties
17321  if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
17322  $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
17323  } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
17324  $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
17325  } else {
17326  $tmp_cellspacing = 0;
17327  }
17328  $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
17329  $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
17330  $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
17331  $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']);
17332  $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
17333  $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
17334  // print table header (thead)
17335  $this->writeHTML($this->thead, false, false, false, false, '');
17336  }
17337  }
17338  // move $key index forward to skip THEAD block
17339  while ( ($key < $maxel) AND (!(
17340  ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
17341  OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
17342  ++$key;
17343  }
17344  }
17345  if ($dom[$key]['tag'] OR ($key == 0)) {
17346  if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
17347  $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
17348  }
17349  // vertically align image in line
17350  if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
17351  // get image height
17352  $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], ($dom[$key]['fontsize'] / $this->k), 'px');
17353  $autolinebreak = false;
17354  if (!empty($dom[$key]['width'])) {
17355  $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], ($dom[$key]['fontsize'] / $this->k), 'px', false);
17356  if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R']))
17357  AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L'])))
17358  OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) {
17359  // add automatic line break
17360  $autolinebreak = true;
17361  $this->Ln('', $cell);
17362  if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
17363  // go back to evaluate this line break
17364  --$key;
17365  }
17366  }
17367  }
17368  if (!$autolinebreak) {
17369  if ($this->inPageBody()) {
17370  $pre_y = $this->y;
17371  // check for page break
17372  if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
17373  // fix for multicolumn mode
17374  $startliney = $this->y;
17375  }
17376  }
17377  if ($this->page > $startlinepage) {
17378  // fix line splitted over two pages
17379  if (isset($this->footerlen[$startlinepage])) {
17380  $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17381  }
17382  // line to be moved one page forward
17383  $pagebuff = $this->getPageBuffer($startlinepage);
17384  $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17385  $tstart = substr($pagebuff, 0, $startlinepos);
17386  $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17387  // remove line from previous page
17388  $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17389  $pagebuff = $this->getPageBuffer($this->page);
17390  $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17391  $tend = substr($pagebuff, $this->cntmrk[$this->page]);
17392  // add line start to current page
17393  $yshift = ($minstartliney - $this->y);
17394  if ($fontaligned) {
17395  $yshift += ($curfontsize / $this->k);
17396  }
17397  $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17398  $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17399  // shift the annotations and links
17400  if (isset($this->PageAnnots[$this->page])) {
17401  $next_pask = count($this->PageAnnots[$this->page]);
17402  } else {
17403  $next_pask = 0;
17404  }
17405  if (isset($this->PageAnnots[$startlinepage])) {
17406  foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17407  if ($pak >= $pask) {
17408  $this->PageAnnots[$this->page][] = $pac;
17409  unset($this->PageAnnots[$startlinepage][$pak]);
17410  $npak = count($this->PageAnnots[$this->page]) - 1;
17411  $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17412  }
17413  }
17414  }
17415  $pask = $next_pask;
17416  $startlinepos = $this->cntmrk[$this->page];
17417  $startlinepage = $this->page;
17418  $startliney = $this->y;
17419  $this->newline = false;
17420  }
17421  $this->y += ($this->getCellHeight($curfontsize / $this->k) - ($curfontdescent * $this->cell_height_ratio) - $imgh);
17422  $minstartliney = min($this->y, $minstartliney);
17423  $maxbottomliney = ($startliney + $this->getCellHeight($curfontsize / $this->k));
17424  }
17425  } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
17426  // account for different font size
17427  $pfontname = $curfontname;
17428  $pfontstyle = $curfontstyle;
17429  $pfontsize = $curfontsize;
17430  $fontname = (isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname);
17431  $fontstyle = (isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle);
17432  $fontsize = (isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize);
17433  $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
17434  $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
17435  if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
17436  OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17437  OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
17438  if (($key < ($maxel - 1)) AND (
17439  ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
17440  OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17441  OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize)
17442  AND ($fontsize >= 0) AND ($curfontsize >= 0)
17443  AND (($fontsize != $curfontsize) OR ($fontstyle != $curfontstyle) OR ($fontname != $curfontname)))
17444  )) {
17445  if ($this->page > $startlinepage) {
17446  // fix lines splitted over two pages
17447  if (isset($this->footerlen[$startlinepage])) {
17448  $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17449  }
17450  // line to be moved one page forward
17451  $pagebuff = $this->getPageBuffer($startlinepage);
17452  $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17453  $tstart = substr($pagebuff, 0, $startlinepos);
17454  $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17455  // remove line start from previous page
17456  $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17457  $pagebuff = $this->getPageBuffer($this->page);
17458  $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17459  $tend = substr($pagebuff, $this->cntmrk[$this->page]);
17460  // add line start to current page
17461  $yshift = ($minstartliney - $this->y);
17462  $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17463  $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17464  // shift the annotations and links
17465  if (isset($this->PageAnnots[$this->page])) {
17466  $next_pask = count($this->PageAnnots[$this->page]);
17467  } else {
17468  $next_pask = 0;
17469  }
17470  if (isset($this->PageAnnots[$startlinepage])) {
17471  foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17472  if ($pak >= $pask) {
17473  $this->PageAnnots[$this->page][] = $pac;
17474  unset($this->PageAnnots[$startlinepage][$pak]);
17475  $npak = count($this->PageAnnots[$this->page]) - 1;
17476  $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17477  }
17478  }
17479  }
17480  $pask = $next_pask;
17481  $startlinepos = $this->cntmrk[$this->page];
17482  $startlinepage = $this->page;
17483  $startliney = $this->y;
17484  }
17485  if (!isset($dom[$key]['line-height'])) {
17486  $dom[$key]['line-height'] = $this->cell_height_ratio;
17487  }
17488  if (!$dom[$key]['block']) {
17489  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']))) {
17490  $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
17491  }
17492  if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
17493  $current_line_align_data = array($key, $minstartliney, $maxbottomliney);
17494  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)))) {
17495  $minstartliney = min($this->y, $line_align_data[1]);
17496  $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $line_align_data[2]);
17497  } else {
17498  $minstartliney = min($this->y, $minstartliney);
17499  $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $maxbottomliney);
17500  }
17501  $line_align_data = $current_line_align_data;
17502  }
17503  }
17504  $this->cell_height_ratio = $dom[$key]['line-height'];
17505  $fontaligned = true;
17506  }
17507  $this->SetFont($fontname, $fontstyle, $fontsize);
17508  // reset row height
17509  $this->resetLastH();
17510  $curfontname = $fontname;
17511  $curfontstyle = $fontstyle;
17512  $curfontsize = $fontsize;
17513  $curfontascent = $fontascent;
17514  $curfontdescent = $fontdescent;
17515  }
17516  }
17517  // set text rendering mode
17518  $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
17519  $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
17520  $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
17521  $this->setTextRenderingMode($textstroke, $textfill, $textclip);
17522  if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
17523  $this->setFontStretching($dom[$key]['font-stretch']);
17524  }
17525  if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
17526  $this->setFontSpacing($dom[$key]['letter-spacing']);
17527  }
17528  if (($plalign == 'J') AND $dom[$key]['block']) {
17529  $plalign = '';
17530  }
17531  // get current position on page buffer
17532  $curpos = $this->pagelen[$startlinepage];
17533  if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
17534  $this->SetFillColorArray($dom[$key]['bgcolor']);
17535  $wfill = true;
17536  } else {
17537  $wfill = $fill | false;
17538  }
17539  if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
17540  $this->SetTextColorArray($dom[$key]['fgcolor']);
17541  }
17542  if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
17543  $this->SetDrawColorArray($dom[$key]['strokecolor']);
17544  }
17545  if (isset($dom[$key]['align'])) {
17546  $lalign = $dom[$key]['align'];
17547  }
17548  if (TCPDF_STATIC::empty_string($lalign)) {
17549  $lalign = $align;
17550  }
17551  }
17552  // align lines
17553  if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
17554  $newline = true;
17555  $fontaligned = false;
17556  // we are at the beginning of a new line
17557  if (isset($startlinex)) {
17558  $yshift = ($minstartliney - $startliney);
17559  if (($yshift > 0) OR ($this->page > $startlinepage)) {
17560  $yshift = 0;
17561  }
17562  $t_x = 0;
17563  // the last line must be shifted to be aligned as requested
17564  $linew = abs($this->endlinex - $startlinex);
17565  if ($this->inxobj) {
17566  // we are inside an XObject template
17567  $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
17568  if (isset($opentagpos)) {
17569  $midpos = $opentagpos;
17570  } else {
17571  $midpos = 0;
17572  }
17573  if ($midpos > 0) {
17574  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
17575  $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
17576  } else {
17577  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
17578  $pend = '';
17579  }
17580  } else {
17581  $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
17582  if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17583  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17584  $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
17585  } elseif (isset($opentagpos)) {
17586  $midpos = $opentagpos;
17587  } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17588  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17589  $midpos = $this->footerpos[$startlinepage];
17590  } else {
17591  $midpos = 0;
17592  }
17593  if ($midpos > 0) {
17594  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
17595  $pend = substr($this->getPageBuffer($startlinepage), $midpos);
17596  } else {
17597  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
17598  $pend = '';
17599  }
17600  }
17601  if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
17602  // calculate shifting amount
17603  $tw = $w;
17604  if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
17605  $tw += $this->cell_padding['R'];
17606  }
17607  if ($this->lMargin != $prevlMargin) {
17608  $tw += ($prevlMargin - $this->lMargin);
17609  }
17610  if ($this->rMargin != $prevrMargin) {
17611  $tw += ($prevrMargin - $this->rMargin);
17612  }
17613  $one_space_width = $this->GetStringWidth(chr(32));
17614  $no = 0; // number of spaces on a line contained on a single block
17615  if ($this->isRTLTextDir()) { // RTL
17616  // remove left space if exist
17617  $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
17618  if ($pos1 > 0) {
17619  $pos1 = intval($pos1);
17620  if ($this->isUnicodeFont()) {
17621  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
17622  $spacelen = 2;
17623  } else {
17624  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
17625  $spacelen = 1;
17626  }
17627  if ($pos1 == $pos2) {
17628  $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
17629  if (substr($pmid, $pos1, 4) == '[()]') {
17630  $linew -= $one_space_width;
17631  } elseif ($pos1 == strpos($pmid, '[(')) {
17632  $no = 1;
17633  }
17634  }
17635  }
17636  } else { // LTR
17637  // remove right space if exist
17638  $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
17639  if ($pos1 > 0) {
17640  $pos1 = intval($pos1);
17641  if ($this->isUnicodeFont()) {
17642  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
17643  $spacelen = 2;
17644  } else {
17645  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
17646  $spacelen = 1;
17647  }
17648  if ($pos1 == $pos2) {
17649  $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
17650  $linew -= $one_space_width;
17651  }
17652  }
17653  }
17654  $mdiff = ($tw - $linew);
17655  if ($plalign == 'C') {
17656  if ($this->rtl) {
17657  $t_x = -($mdiff / 2);
17658  } else {
17659  $t_x = ($mdiff / 2);
17660  }
17661  } elseif ($plalign == 'R') {
17662  // right alignment on LTR document
17663  $t_x = $mdiff;
17664  } elseif ($plalign == 'L') {
17665  // left alignment on RTL document
17666  $t_x = -$mdiff;
17667  } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
17668  // Justification
17669  if ($this->isRTLTextDir()) {
17670  // align text on the left
17671  $t_x = -$mdiff;
17672  }
17673  $ns = 0; // number of spaces
17674  $pmidtemp = $pmid;
17675  // escape special characters
17676  $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
17677  $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
17678  // search spaces
17679  if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
17680  $spacestr = $this->getSpaceString();
17681  $maxkk = count($lnstring[1]) - 1;
17682  for ($kk=0; $kk <= $maxkk; ++$kk) {
17683  // restore special characters
17684  $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
17685  $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
17686  // store number of spaces on the strings
17687  $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
17688  // count total spaces on line
17689  $ns += $lnstring[2][$kk];
17690  $lnstring[3][$kk] = $ns;
17691  }
17692  if ($ns == 0) {
17693  $ns = 1;
17694  }
17695  // calculate additional space to add to each existing space
17696  $spacewidth = ($mdiff / ($ns - $no)) * $this->k;
17697  if ($this->FontSize <= 0) {
17698  $this->FontSize = 1;
17699  }
17700  $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize;
17701  if ($this->font_spacing != 0) {
17702  // fixed spacing mode
17703  $osw = -1000 * $this->font_spacing / $this->FontSize;
17704  $spacewidthu += $osw;
17705  }
17706  $nsmax = $ns;
17707  $ns = 0;
17708  reset($lnstring);
17709  $offset = 0;
17710  $strcount = 0;
17711  $prev_epsposbeg = 0;
17712  $textpos = 0;
17713  if ($this->isRTLTextDir()) {
17714  $textpos = $this->wPt;
17715  }
17716  while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
17717  // check if we are inside a string section '[( ... )]'
17718  $stroffset = strpos($pmid, '[(', $offset);
17719  if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
17720  // set offset to the end of string section
17721  $offset = strpos($pmid, ')]', $stroffset);
17722  while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
17723  $offset = strpos($pmid, ')]', ($offset + 1));
17724  }
17725  if ($offset === false) {
17726  $this->Error('HTML Justification: malformed PDF code.');
17727  }
17728  continue;
17729  }
17730  if ($this->isRTLTextDir()) {
17731  $spacew = ($spacewidth * ($nsmax - $ns));
17732  } else {
17733  $spacew = ($spacewidth * $ns);
17734  }
17735  $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
17736  $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset);
17737  if ($epsposend !== null) {
17738  $epsposend += strlen($this->epsmarker.'Q');
17739  $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
17740  if ($epsposbeg === null) {
17741  $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
17742  $prev_epsposbeg = $epsposbeg;
17743  }
17744  if (($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) {
17745  // shift EPS images
17746  $trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
17747  $pmid_b = substr($pmid, 0, $epsposbeg);
17748  $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
17749  $pmid_e = substr($pmid, $epsposend);
17750  $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
17751  $offset = $epsposend;
17752  continue;
17753  }
17754  }
17755  $currentxpos = 0;
17756  // shift blocks of code
17757  switch ($strpiece[2][0]) {
17758  case 'Td':
17759  case 'cm':
17760  case 'm':
17761  case 'l': {
17762  // get current X position
17763  preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
17764  if (!isset($xmatches[1])) {
17765  break;
17766  }
17767  $currentxpos = $xmatches[1];
17768  $textpos = $currentxpos;
17769  if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
17770  $ns = $lnstring[3][$strcount];
17771  if ($this->isRTLTextDir()) {
17772  $spacew = ($spacewidth * ($nsmax - $ns));
17773  }
17774  ++$strcount;
17775  }
17776  // justify block
17777  if (preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $pmatch) == 1) {
17778  $newpmid = sprintf('%F',(floatval($pmatch[1]) + $spacew)).' '.$pmatch[2].' x*#!#*x'.$pmatch[3].$pmatch[4];
17779  $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17780  unset($pmatch, $newpmid);
17781  }
17782  break;
17783  }
17784  case 're': {
17785  // justify block
17786  if (!TCPDF_STATIC::empty_string($this->lispacer)) {
17787  $this->lispacer = '';
17788  continue;
17789  }
17790  preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
17791  if (!isset($xmatches[1])) {
17792  break;
17793  }
17794  $currentxpos = $xmatches[1];
17795  $x_diff = 0;
17796  $w_diff = 0;
17797  if ($this->isRTLTextDir()) { // RTL
17798  if ($currentxpos < $textpos) {
17799  $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
17800  $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17801  } else {
17802  if ($strcount > 0) {
17803  $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
17804  $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17805  }
17806  }
17807  } else { // LTR
17808  if ($currentxpos > $textpos) {
17809  if ($strcount > 0) {
17810  $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
17811  }
17812  $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17813  } else {
17814  if ($strcount > 1) {
17815  $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
17816  }
17817  if ($strcount > 0) {
17818  $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17819  }
17820  }
17821  }
17822  if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $pmatch) == 1) {
17823  $newx = sprintf('%F',(floatval($pmatch[1]) + $x_diff));
17824  $neww = sprintf('%F',(floatval($pmatch[3]) + $w_diff));
17825  $newpmid = $newx.' '.$pmatch[2].' '.$neww.' '.$pmatch[4].' x*#!#*x'.$pmatch[5].$pmatch[6];
17826  $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17827  unset($pmatch, $newpmid, $newx, $neww);
17828  }
17829  break;
17830  }
17831  case 'c': {
17832  // get current X position
17833  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);
17834  if (!isset($xmatches[1])) {
17835  break;
17836  }
17837  $currentxpos = $xmatches[1];
17838  // justify block
17839  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) {
17840  $newx1 = sprintf('%F',(floatval($pmatch[1]) + $spacew));
17841  $newx2 = sprintf('%F',(floatval($pmatch[3]) + $spacew));
17842  $newx3 = sprintf('%F',(floatval($pmatch[5]) + $spacew));
17843  $newpmid = $newx1.' '.$pmatch[2].' '.$newx2.' '.$pmatch[4].' '.$newx3.' '.$pmatch[6].' x*#!#*x'.$pmatch[7].$pmatch[8];
17844  $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17845  unset($pmatch, $newpmid, $newx1, $newx2, $newx3);
17846  }
17847  break;
17848  }
17849  }
17850  // shift the annotations and links
17851  $cxpos = ($currentxpos / $this->k);
17852  $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps);
17853  if ($this->inxobj) {
17854  // we are inside an XObject template
17855  foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17856  if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17857  if ($cxpos > $lmpos) {
17858  $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k);
17859  $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17860  } else {
17861  $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17862  }
17863  break;
17864  }
17865  }
17866  } elseif (isset($this->PageAnnots[$this->page])) {
17867  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17868  if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17869  if ($cxpos > $lmpos) {
17870  $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
17871  $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17872  } else {
17873  $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17874  }
17875  break;
17876  }
17877  }
17878  }
17879  } // end of while
17880  // remove markers
17881  $pmid = str_replace('x*#!#*x', '', $pmid);
17882  if ($this->isUnicodeFont()) {
17883  // multibyte characters
17884  $spacew = $spacewidthu;
17885  if ($this->font_stretching != 100) {
17886  // word spacing is affected by stretching
17887  $spacew /= ($this->font_stretching / 100);
17888  }
17889  // escape special characters
17890  $pos = 0;
17891  $pmid = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmid);
17892  $pmid = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmid);
17893  if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmid, $pamatch) > 0) {
17894  foreach($pamatch[0] as $pk => $pmatch) {
17895  $replace = $pamatch[1][$pk];
17896  $replace = str_replace('#!#OP#!#', '(', $replace);
17897  $replace = str_replace('#!#CP#!#', ')', $replace);
17898  $newpmid = '[('.str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacew).' (', $replace).')]';
17899  $pos = strpos($pmid, $pmatch, $pos);
17900  if ($pos !== FALSE) {
17901  $pmid = substr_replace($pmid, $newpmid, $pos, strlen($pmatch));
17902  }
17903  ++$pos;
17904  }
17905  unset($pamatch);
17906  }
17907  if ($this->inxobj) {
17908  // we are inside an XObject template
17909  $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend;
17910  } else {
17911  $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
17912  }
17913  $endlinepos = strlen($pstart."\n".$pmid."\n");
17914  } else {
17915  // non-unicode (single-byte characters)
17916  if ($this->font_stretching != 100) {
17917  // word spacing (Tw) is affected by stretching
17918  $spacewidth /= ($this->font_stretching / 100);
17919  }
17920  $rs = sprintf('%F Tw', $spacewidth);
17921  $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
17922  if ($this->inxobj) {
17923  // we are inside an XObject template
17924  $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
17925  } else {
17926  $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
17927  }
17928  $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
17929  }
17930  }
17931  } // end of J
17932  } // end if $startlinex
17933  if (($t_x != 0) OR ($yshift < 0)) {
17934  // shift the line
17935  $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
17936  $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
17937  $endlinepos = strlen($pstart);
17938  if ($this->inxobj) {
17939  // we are inside an XObject template
17940  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
17941  foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17942  if ($pak >= $pask) {
17943  $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
17944  $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
17945  }
17946  }
17947  } else {
17948  $this->setPageBuffer($startlinepage, $pstart.$pend);
17949  // shift the annotations and links
17950  if (isset($this->PageAnnots[$this->page])) {
17951  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17952  if ($pak >= $pask) {
17953  $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
17954  $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
17955  }
17956  }
17957  }
17958  }
17959  $this->y -= $yshift;
17960  }
17961  }
17962  $pbrk = $this->checkPageBreak($this->lasth);
17963  $this->newline = false;
17964  $startlinex = $this->x;
17965  $startliney = $this->y;
17966  if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
17967  $startliney -= ((0.3 * $this->FontSizePt) / $this->k);
17968  } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
17969  $startliney -= (($this->FontSizePt / 0.7) / $this->k);
17970  } else {
17971  $minstartliney = $startliney;
17972  $maxbottomliney = ($this->y + $this->getCellHeight($fontsize / $this->k));
17973  }
17974  $startlinepage = $this->page;
17975  if (isset($endlinepos) AND (!$pbrk)) {
17976  $startlinepos = $endlinepos;
17977  } else {
17978  if ($this->inxobj) {
17979  // we are inside an XObject template
17980  $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17981  } elseif (!$this->InFooter) {
17982  if (isset($this->footerlen[$this->page])) {
17983  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17984  } else {
17985  $this->footerpos[$this->page] = $this->pagelen[$this->page];
17986  }
17987  $startlinepos = $this->footerpos[$this->page];
17988  } else {
17989  $startlinepos = $this->pagelen[$this->page];
17990  }
17991  }
17992  unset($endlinepos);
17993  $plalign = $lalign;
17994  if (isset($this->PageAnnots[$this->page])) {
17995  $pask = count($this->PageAnnots[$this->page]);
17996  } else {
17997  $pask = 0;
17998  }
17999  if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
18000  AND (isset($this->emptypagemrk[$this->page]))
18001  AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) {
18002  $this->SetFont($fontname, $fontstyle, $fontsize);
18003  if ($wfill) {
18004  $this->SetFillColorArray($this->bgcolor);
18005  }
18006  }
18007  } // end newline
18008  if (isset($opentagpos)) {
18009  unset($opentagpos);
18010  }
18011  if ($dom[$key]['tag']) {
18012  if ($dom[$key]['opening']) {
18013  // get text indentation (if any)
18014  if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
18015  $this->textindent = $dom[$key]['text-indent'];
18016  $this->newline = true;
18017  }
18018  // table
18019  if (($dom[$key]['value'] == 'table') AND isset($dom[$key]['cols']) AND ($dom[$key]['cols'] > 0)) {
18020  // available page width
18021  if ($this->rtl) {
18022  $wtmp = $this->x - $this->lMargin;
18023  } else {
18024  $wtmp = $this->w - $this->rMargin - $this->x;
18025  }
18026  // get cell spacing
18027  if (isset($dom[$key]['attribute']['cellspacing'])) {
18028  $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
18029  $cellspacing = array('H' => $clsp, 'V' => $clsp);
18030  } elseif (isset($dom[$key]['border-spacing'])) {
18031  $cellspacing = $dom[$key]['border-spacing'];
18032  } else {
18033  $cellspacing = array('H' => 0, 'V' => 0);
18034  }
18035  // table width
18036  if (isset($dom[$key]['width'])) {
18037  $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
18038  } else {
18039  $table_width = $wtmp;
18040  }
18041  $table_width -= (2 * $cellspacing['H']);
18042  if (!$this->inthead) {
18043  $this->y += $cellspacing['V'];
18044  }
18045  if ($this->rtl) {
18046  $cellspacingx = -$cellspacing['H'];
18047  } else {
18048  $cellspacingx = $cellspacing['H'];
18049  }
18050  // total table width without cellspaces
18051  $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
18052  // minimum column width
18053  // tcpdf-patch: begin
18054  if ($table_columns_width == 0 || $dom[$key]['cols'] == 0)
18055  {
18056  $table_min_column_width = 1;
18057  }
18058  else
18059  {
18060  $table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
18061  }
18062  if ($dom[$key]['cols'] != 0)
18063  {
18064  // array of custom column widths
18065  $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
18066  }
18067  // tcpdf-patch: end
18068  }
18069  // table row
18070  if ($dom[$key]['value'] == 'tr') {
18071  // reset column counter
18072  $colid = 0;
18073  }
18074  // table cell
18075  if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
18076  $trid = $dom[$key]['parent'];
18077  $table_el = $dom[$trid]['parent'];
18078  if (!isset($dom[$table_el]['cols'])) {
18079  $dom[$table_el]['cols'] = $dom[$trid]['cols'];
18080  }
18081  // store border info
18082  $tdborder = 0;
18083  if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
18084  $tdborder = $dom[$key]['border'];
18085  }
18086  $colspan = intval($dom[$key]['attribute']['colspan']);
18087  if ($colspan <= 0) {
18088  $colspan = 1;
18089  }
18090  $old_cell_padding = $this->cell_padding;
18091  if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
18092  $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
18093  $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
18094  } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
18095  $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
18096  } else {
18097  $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
18098  }
18099  $this->cell_padding = $current_cell_padding;
18100  if (isset($dom[$key]['height'])) {
18101  // minimum cell height
18102  $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
18103  } else {
18104  $cellh = 0;
18105  }
18106  if (isset($dom[$key]['content'])) {
18107  $cell_content = $dom[$key]['content'];
18108  } else {
18109  $cell_content = '&nbsp;';
18110  }
18111  $tagtype = $dom[$key]['value'];
18112  $parentid = $key;
18113  while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
18114  // move $key index forward
18115  ++$key;
18116  }
18117  if (!isset($dom[$trid]['startpage'])) {
18118  $dom[$trid]['startpage'] = $this->page;
18119  } else {
18120  $this->setPage($dom[$trid]['startpage']);
18121  }
18122  if (!isset($dom[$trid]['startcolumn'])) {
18123  $dom[$trid]['startcolumn'] = $this->current_column;
18124  } elseif ($this->current_column != $dom[$trid]['startcolumn']) {
18125  $tmpx = $this->x;
18126  $this->selectColumn($dom[$trid]['startcolumn']);
18127  $this->x = $tmpx;
18128  }
18129  if (!isset($dom[$trid]['starty'])) {
18130  $dom[$trid]['starty'] = $this->y;
18131  } else {
18132  $this->y = $dom[$trid]['starty'];
18133  }
18134  if (!isset($dom[$trid]['startx'])) {
18135  $dom[$trid]['startx'] = $this->x;
18136  $this->x += $cellspacingx;
18137  } else {
18138  $this->x += ($cellspacingx / 2);
18139  }
18140  if (isset($dom[$parentid]['attribute']['rowspan'])) {
18141  $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
18142  } else {
18143  $rowspan = 1;
18144  }
18145  // skip row-spanned cells started on the previous rows
18146  if (isset($dom[$table_el]['rowspans'])) {
18147  $rsk = 0;
18148  $rskmax = count($dom[$table_el]['rowspans']);
18149  while ($rsk < $rskmax) {
18150  $trwsp = $dom[$table_el]['rowspans'][$rsk];
18151  $rsstartx = $trwsp['startx'];
18152  $rsendx = $trwsp['endx'];
18153  // account for margin changes
18154  if ($trwsp['startpage'] < $this->page) {
18155  if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
18156  $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
18157  $rsstartx -= $dl;
18158  $rsendx -= $dl;
18159  } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
18160  $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
18161  $rsstartx += $dl;
18162  $rsendx += $dl;
18163  }
18164  }
18165  if (($trwsp['rowspan'] > 0)
18166  AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps))
18167  AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps))
18168  AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) {
18169  // set the starting X position of the current cell
18170  $this->x = $rsendx + $cellspacingx;
18171  // increment column indicator
18172  $colid += $trwsp['colspan'];
18173  if (($trwsp['rowspan'] == 1)
18174  AND (isset($dom[$trid]['endy']))
18175  AND (isset($dom[$trid]['endpage']))
18176  AND (isset($dom[$trid]['endcolumn']))
18177  AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
18178  AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18179  // set ending Y position for row
18180  $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18181  $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
18182  }
18183  $rsk = 0;
18184  } else {
18185  ++$rsk;
18186  }
18187  }
18188  }
18189  if (isset($dom[$parentid]['width'])) {
18190  // user specified width
18191  $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
18192  $tmpcw = ($cellw / $colspan);
18193  for ($i = 0; $i < $colspan; ++$i) {
18194  $table_colwidths[($colid + $i)] = $tmpcw;
18195  }
18196  } else {
18197  // inherit column width
18198  $cellw = 0;
18199  for ($i = 0; $i < $colspan; ++$i) {
18200  $cellw += (isset($table_colwidths[($colid + $i)]) ? $table_colwidths[($colid + $i)] : 0);
18201  }
18202  }
18203  $cellw += (($colspan - 1) * $cellspacing['H']);
18204  // increment column indicator
18205  $colid += $colspan;
18206  // add rowspan information to table element
18207  if ($rowspan > 1) {
18208  $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));
18209  }
18210  $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
18211  if ($rowspan > 1) {
18212  $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
18213  }
18214  // push background colors
18215  if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
18216  $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
18217  }
18218  // store border info
18219  if (isset($tdborder) AND !empty($tdborder)) {
18220  $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
18221  }
18222  $prevLastH = $this->lasth;
18223  // store some info for multicolumn mode
18224  if ($this->rtl) {
18225  $this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
18226  } else {
18227  $this->colxshift['x'] = $this->x - $this->lMargin;
18228  }
18229  $this->colxshift['s'] = $cellspacing;
18230  $this->colxshift['p'] = $current_cell_padding;
18231  // ****** write the cell content ******
18232  $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
18233  // restore some values
18234  $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
18235  $this->lasth = $prevLastH;
18236  $this->cell_padding = $old_cell_padding;
18237  $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
18238  // update the end of row position
18239  if ($rowspan <= 1) {
18240  if (isset($dom[$trid]['endy'])) {
18241  if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) {
18242  $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
18243  } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) {
18244  $dom[$trid]['endy'] = $this->y;
18245  }
18246  } else {
18247  $dom[$trid]['endy'] = $this->y;
18248  }
18249  if (isset($dom[$trid]['endpage'])) {
18250  $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
18251  } else {
18252  $dom[$trid]['endpage'] = $this->page;
18253  }
18254  if (isset($dom[$trid]['endcolumn'])) {
18255  $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
18256  } else {
18257  $dom[$trid]['endcolumn'] = $this->current_column;
18258  }
18259  } else {
18260  // account for row-spanned cells
18261  $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
18262  $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
18263  $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
18264  $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column;
18265  }
18266  if (isset($dom[$table_el]['rowspans'])) {
18267  // update endy and endpage on rowspanned cells
18268  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
18269  if ($trwsp['rowspan'] > 0) {
18270  if (isset($dom[$trid]['endpage'])) {
18271  if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18272  $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18273  } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
18274  $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
18275  $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
18276  $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
18277  } else {
18278  $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
18279  }
18280  }
18281  }
18282  }
18283  }
18284  $this->x += ($cellspacingx / 2);
18285  } else {
18286  // opening tag (or self-closing tag)
18287  if (!isset($opentagpos)) {
18288  if ($this->inxobj) {
18289  // we are inside an XObject template
18290  $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
18291  } elseif (!$this->InFooter) {
18292  if (isset($this->footerlen[$this->page])) {
18293  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
18294  } else {
18295  $this->footerpos[$this->page] = $this->pagelen[$this->page];
18296  }
18297  $opentagpos = $this->footerpos[$this->page];
18298  }
18299  }
18300  $dom = $this->openHTMLTagHandler($dom, $key, $cell);
18301  }
18302  } else { // closing tag
18303  $prev_numpages = $this->numpages;
18304  $old_bordermrk = $this->bordermrk[$this->page];
18305  $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
18306  if ($this->bordermrk[$this->page] > $old_bordermrk) {
18307  $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk);
18308  }
18309  if ($prev_numpages > $this->numpages) {
18310  $startlinepage = $this->page;
18311  }
18312  }
18313  } elseif (strlen($dom[$key]['value']) > 0) {
18314  // print list-item
18315  if (!TCPDF_STATIC::empty_string($this->lispacer) AND ($this->lispacer != '^')) {
18316  $this->SetFont($pfontname, $pfontstyle, $pfontsize);
18317  $this->resetLastH();
18318  $minstartliney = $this->y;
18319  $maxbottomliney = ($startliney + $this->getCellHeight($this->FontSize));
18320  if (is_numeric($pfontsize) AND ($pfontsize > 0)) {
18321  $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
18322  }
18323  $this->SetFont($curfontname, $curfontstyle, $curfontsize);
18324  $this->resetLastH();
18325  if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
18326  $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
18327  $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
18328  $this->y += ($this->getCellHeight(($pfontsize - $curfontsize) / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
18329  $minstartliney = min($this->y, $minstartliney);
18330  $maxbottomliney = max(($this->y + $this->getCellHeight($pfontsize / $this->k)), $maxbottomliney);
18331  }
18332  }
18333  // text
18334  $this->htmlvspace = 0;
18335  if ((!$this->premode) AND $this->isRTLTextDir()) {
18336  // reverse spaces order
18337  $lsp = ''; // left spaces
18338  $rsp = ''; // right spaces
18339  if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18340  $lsp = $matches[1];
18341  }
18342  if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18343  $rsp = $matches[1];
18344  }
18345  $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
18346  }
18347  if ($newline) {
18348  if (!$this->premode) {
18349  $prelen = strlen($dom[$key]['value']);
18350  if ($this->isRTLTextDir()) {
18351  // right trim except non-breaking space
18352  $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
18353  } else {
18354  // left trim except non-breaking space
18355  $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
18356  }
18357  $postlen = strlen($dom[$key]['value']);
18358  if (($postlen == 0) AND ($prelen > 0)) {
18359  $dom[$key]['trimmed_space'] = true;
18360  }
18361  }
18362  $newline = false;
18363  $firstblock = true;
18364  } else {
18365  $firstblock = false;
18366  // replace empty multiple spaces string with a single space
18367  $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']);
18368  }
18369  $strrest = '';
18370  if ($this->rtl) {
18371  $this->x -= $this->textindent;
18372  } else {
18373  $this->x += $this->textindent;
18374  }
18375  if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
18376  $strlinelen = $this->GetStringWidth($dom[$key]['value']);
18377  if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
18378  // HTML <a> Link
18379  $hrefcolor = '';
18380  if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
18381  $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
18382  }
18383  $hrefstyle = -1;
18384  if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
18385  $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
18386  }
18387  $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
18388  } else {
18389  $wadj = 0; // space to leave for block continuity
18390  if ($this->rtl) {
18391  $cwa = ($this->x - $this->lMargin);
18392  } else {
18393  $cwa = ($this->w - $this->rMargin - $this->x);
18394  }
18395  if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) {
18396  // check the next text blocks for continuity
18397  $nkey = ($key + 1);
18398  $write_block = true;
18399  $same_textdir = true;
18400  $tmp_fontname = $this->FontFamily;
18401  $tmp_fontstyle = $this->FontStyle;
18402  $tmp_fontsize = $this->FontSizePt;
18403  while ($write_block AND isset($dom[$nkey])) {
18404  if ($dom[$nkey]['tag']) {
18405  if ($dom[$nkey]['block']) {
18406  // end of block
18407  $write_block = false;
18408  }
18409  $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
18410  $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
18411  $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
18412  $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
18413  } else {
18414  $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'+/', $this->re_space['m'], $dom[$nkey]['value']);
18415  if (isset($nextstr[0]) AND $same_textdir) {
18416  $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
18417  if (isset($nextstr[1])) {
18418  $write_block = false;
18419  }
18420  }
18421  }
18422  ++$nkey;
18423  }
18424  }
18425  if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) {
18426  $wadj = 0;
18427  $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $dom[$key]['value']);
18428  $numblks = count($nextstr);
18429  if ($numblks > 1) {
18430  // try to split on blank spaces
18431  $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)]));
18432  } else {
18433  // set the entire block on new line
18434  $wadj = $this->GetStringWidth($nextstr[0]);
18435  }
18436  }
18437  // check for reversed text direction
18438  if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) {
18439  // LTR text on RTL direction or RTL text on LTR direction
18440  $reverse_dir = true;
18441  $this->rtl = !$this->rtl;
18442  $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems
18443  if ($this->rtl) {
18444  $this->x += $revshift;
18445  } else {
18446  $this->x -= $revshift;
18447  }
18448  $xws = $this->x;
18449  }
18450  // ****** write only until the end of the line and get the rest ******
18451  $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
18452  // restore default direction
18453  if ($reverse_dir AND ($wadj == 0)) {
18454  $this->x = $xws;
18455  $this->rtl = !$this->rtl;
18456  $reverse_dir = false;
18457  }
18458  }
18459  }
18460  $this->textindent = 0;
18461  if (strlen($strrest) > 0) {
18462  // store the remaining string on the previous $key position
18463  $this->newline = true;
18464  if ($strrest == $dom[$key]['value']) {
18465  // used to avoid infinite loop
18466  ++$loop;
18467  } else {
18468  $loop = 0;
18469  }
18470  $dom[$key]['value'] = $strrest;
18471  if ($cell) {
18472  if ($this->rtl) {
18473  $this->x -= $this->cell_padding['R'];
18474  } else {
18475  $this->x += $this->cell_padding['L'];
18476  }
18477  }
18478  if ($loop < 3) {
18479  --$key;
18480  }
18481  } else {
18482  $loop = 0;
18483  // add the positive font spacing of the last character (if any)
18484  if ($this->font_spacing > 0) {
18485  if ($this->rtl) {
18486  $this->x -= $this->font_spacing;
18487  } else {
18488  $this->x += $this->font_spacing;
18489  }
18490  }
18491  }
18492  }
18493  ++$key;
18494  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')) {
18495  // check if we are on a new page or on a new column
18496  if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) {
18497  // we are on a new page or on a new column and the total object height is less than the available vertical space.
18498  // restore previous object
18499  $this->rollbackTransaction(true);
18500  // restore previous values
18501  foreach ($this_method_vars as $vkey => $vval) {
18502  $$vkey = $vval;
18503  }
18504  if (!empty($dom[$key]['thead'])) {
18505  $this->inthead = true;
18506  }
18507  // add a page (or trig AcceptPageBreak() for multicolumn mode)
18508  $pre_y = $this->y;
18509  if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
18510  $startliney = $this->y;
18511  }
18512  $undo = true; // avoid infinite loop
18513  } else {
18514  $undo = false;
18515  }
18516  }
18517  } // end for each $key
18518  // align the last line
18519  if (isset($startlinex)) {
18520  $yshift = ($minstartliney - $startliney);
18521  if (($yshift > 0) OR ($this->page > $startlinepage)) {
18522  $yshift = 0;
18523  }
18524  $t_x = 0;
18525  // the last line must be shifted to be aligned as requested
18526  $linew = abs($this->endlinex - $startlinex);
18527  if ($this->inxobj) {
18528  // we are inside an XObject template
18529  $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
18530  if (isset($opentagpos)) {
18531  $midpos = $opentagpos;
18532  } else {
18533  $midpos = 0;
18534  }
18535  if ($midpos > 0) {
18536  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
18537  $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
18538  } else {
18539  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
18540  $pend = '';
18541  }
18542  } else {
18543  $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
18544  if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18545  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18546  $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
18547  } elseif (isset($opentagpos)) {
18548  $midpos = $opentagpos;
18549  } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18550  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18551  $midpos = $this->footerpos[$startlinepage];
18552  } else {
18553  $midpos = 0;
18554  }
18555  if ($midpos > 0) {
18556  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
18557  $pend = substr($this->getPageBuffer($startlinepage), $midpos);
18558  } else {
18559  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
18560  $pend = '';
18561  }
18562  }
18563  if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
18564  // calculate shifting amount
18565  $tw = $w;
18566  if ($this->lMargin != $prevlMargin) {
18567  $tw += ($prevlMargin - $this->lMargin);
18568  }
18569  if ($this->rMargin != $prevrMargin) {
18570  $tw += ($prevrMargin - $this->rMargin);
18571  }
18572  $one_space_width = $this->GetStringWidth(chr(32));
18573  $no = 0; // number of spaces on a line contained on a single block
18574  if ($this->isRTLTextDir()) { // RTL
18575  // remove left space if exist
18576  $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
18577  if ($pos1 > 0) {
18578  $pos1 = intval($pos1);
18579  if ($this->isUnicodeFont()) {
18580  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
18581  $spacelen = 2;
18582  } else {
18583  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
18584  $spacelen = 1;
18585  }
18586  if ($pos1 == $pos2) {
18587  $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
18588  if (substr($pmid, $pos1, 4) == '[()]') {
18589  $linew -= $one_space_width;
18590  } elseif ($pos1 == strpos($pmid, '[(')) {
18591  $no = 1;
18592  }
18593  }
18594  }
18595  } else { // LTR
18596  // remove right space if exist
18597  $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
18598  if ($pos1 > 0) {
18599  $pos1 = intval($pos1);
18600  if ($this->isUnicodeFont()) {
18601  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
18602  $spacelen = 2;
18603  } else {
18604  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
18605  $spacelen = 1;
18606  }
18607  if ($pos1 == $pos2) {
18608  $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
18609  $linew -= $one_space_width;
18610  }
18611  }
18612  }
18613  $mdiff = ($tw - $linew);
18614  if ($plalign == 'C') {
18615  if ($this->rtl) {
18616  $t_x = -($mdiff / 2);
18617  } else {
18618  $t_x = ($mdiff / 2);
18619  }
18620  } elseif ($plalign == 'R') {
18621  // right alignment on LTR document
18622  $t_x = $mdiff;
18623  } elseif ($plalign == 'L') {
18624  // left alignment on RTL document
18625  $t_x = -$mdiff;
18626  }
18627  } // end if startlinex
18628  if (($t_x != 0) OR ($yshift < 0)) {
18629  // shift the line
18630  $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
18631  $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18632  $endlinepos = strlen($pstart);
18633  if ($this->inxobj) {
18634  // we are inside an XObject template
18635  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
18636  foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
18637  if ($pak >= $pask) {
18638  $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
18639  $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
18640  }
18641  }
18642  } else {
18643  $this->setPageBuffer($startlinepage, $pstart.$pend);
18644  // shift the annotations and links
18645  if (isset($this->PageAnnots[$this->page])) {
18646  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
18647  if ($pak >= $pask) {
18648  $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
18649  $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
18650  }
18651  }
18652  }
18653  }
18654  $this->y -= $yshift;
18655  $yshift = 0;
18656  }
18657  }
18658  // restore previous values
18659  $this->setGraphicVars($gvars);
18660  if ($this->num_columns > 1) {
18661  $this->selectColumn();
18662  } elseif ($this->page > $prevPage) {
18663  $this->lMargin = $this->pagedim[$this->page]['olm'];
18664  $this->rMargin = $this->pagedim[$this->page]['orm'];
18665  }
18666  // restore previous list state
18667  $this->cell_height_ratio = $prev_cell_height_ratio;
18668  $this->listnum = $prev_listnum;
18669  $this->listordered = $prev_listordered;
18670  $this->listcount = $prev_listcount;
18671  $this->lispacer = $prev_lispacer;
18672  if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
18673  $this->Ln($this->lasth);
18674  if (($this->y < $maxbottomliney) AND ($startlinepage == $this->page)) {
18675  $this->y = $maxbottomliney;
18676  }
18677  }
18678  unset($dom);
18679  }
18680 
18689  protected function openHTMLTagHandler($dom, $key, $cell) {
18690  $tag = $dom[$key];
18691  $parent = $dom[($dom[$key]['parent'])];
18692  $firsttag = ($key == 1);
18693  // check for text direction attribute
18694  if (isset($tag['dir'])) {
18695  $this->setTempRTL($tag['dir']);
18696  } else {
18697  $this->tmprtl = false;
18698  }
18699  if ($tag['block']) {
18700  $hbz = 0; // distance from y to line bottom
18701  $hb = 0; // vertical space between block tags
18702  // calculate vertical space for block tags
18703  if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
18704  $cur_h = $this->tagvspaces[$tag['value']][0]['h'];
18705  } elseif (isset($tag['fontsize'])) {
18706  $cur_h = $this->getCellHeight($tag['fontsize'] / $this->k);
18707  } else {
18708  $cur_h = $this->getCellHeight($this->FontSize);
18709  }
18710  if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
18711  $on = $this->tagvspaces[$tag['value']][0]['n'];
18712  } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18713  $on = 0.6;
18714  } else {
18715  $on = 1;
18716  }
18717  if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br', 'hr')))) {
18718  $hb = 0;
18719  } else {
18720  $hb = ($on * $cur_h);
18721  }
18722  if (($this->htmlvspace <= 0) AND ($on > 0)) {
18723  if (isset($parent['fontsize'])) {
18724  $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
18725  } else {
18726  $hbz = $this->getCellHeight($this->FontSize);
18727  }
18728  }
18729  if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) {
18730  // fix vertical space after table
18731  $hbz = 0;
18732  }
18733  // closing vertical space
18734  $hbc = 0;
18735  if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
18736  $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
18737  } elseif (isset($parent['fontsize'])) {
18738  $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
18739  } else {
18740  $pre_h = $this->getCellHeight($this->FontSize);
18741  }
18742  if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
18743  $cn = $this->tagvspaces[$tag['value']][1]['n'];
18744  } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18745  $cn = 0.6;
18746  } else {
18747  $cn = 1;
18748  }
18749  if (isset($this->tagvspaces[$tag['value']][1])) {
18750  $hbc = ($cn * $pre_h);
18751  }
18752  }
18753  // Opening tag
18754  switch($tag['value']) {
18755  case 'table': {
18756  $cp = 0;
18757  $cs = 0;
18758  $dom[$key]['rowspans'] = array();
18759  if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
18760  $this->htmlvspace = 0;
18761  // set table header
18762  if (!TCPDF_STATIC::empty_string($dom[$key]['thead'])) {
18763  // set table header
18764  $this->thead = $dom[$key]['thead'];
18765  if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
18766  $this->theadMargins = array();
18767  $this->theadMargins['cell_padding'] = $this->cell_padding;
18768  $this->theadMargins['lmargin'] = $this->lMargin;
18769  $this->theadMargins['rmargin'] = $this->rMargin;
18770  $this->theadMargins['page'] = $this->page;
18771  $this->theadMargins['cell'] = $cell;
18772  $this->theadMargins['gvars'] = $this->getGraphicVars();
18773  }
18774  }
18775  }
18776  // store current margins and page
18777  $dom[$key]['old_cell_padding'] = $this->cell_padding;
18778  if (isset($tag['attribute']['cellpadding'])) {
18779  $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
18780  $this->SetCellPadding($pad);
18781  } elseif (isset($tag['padding'])) {
18782  $this->cell_padding = $tag['padding'];
18783  }
18784  if (isset($tag['attribute']['cellspacing'])) {
18785  $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
18786  } elseif (isset($tag['border-spacing'])) {
18787  $cs = $tag['border-spacing']['V'];
18788  }
18789  $prev_y = $this->y;
18790  if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) {
18791  $this->inthead = true;
18792  // add a page (or trig AcceptPageBreak() for multicolumn mode)
18793  $this->checkPageBreak($this->PageBreakTrigger + 1);
18794  }
18795  break;
18796  }
18797  case 'tr': {
18798  // array of columns positions
18799  $dom[$key]['cellpos'] = array();
18800  break;
18801  }
18802  case 'hr': {
18803  if ((isset($tag['height'])) AND ($tag['height'] != '')) {
18804  $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
18805  } else {
18806  $hrHeight = $this->GetLineWidth();
18807  }
18808  $this->addHTMLVertSpace($hbz, max($hb, ($hrHeight / 2)), $cell, $firsttag);
18809  $x = $this->GetX();
18810  $y = $this->GetY();
18811  $wtmp = $this->w - $this->lMargin - $this->rMargin;
18812  if ($cell) {
18813  $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']);
18814  }
18815  if ((isset($tag['width'])) AND ($tag['width'] != '')) {
18816  $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
18817  } else {
18818  $hrWidth = $wtmp;
18819  }
18820  $prevlinewidth = $this->GetLineWidth();
18821  $this->SetLineWidth($hrHeight);
18822  $this->Line($x, $y, $x + $hrWidth, $y);
18823  $this->SetLineWidth($prevlinewidth);
18824  $this->addHTMLVertSpace(max($hbc, ($hrHeight / 2)), 0, $cell, !isset($dom[($key + 1)]));
18825  break;
18826  }
18827  case 'a': {
18828  if (array_key_exists('href', $tag['attribute'])) {
18829  $this->HREF['url'] = $tag['attribute']['href'];
18830  }
18831  break;
18832  }
18833  case 'img': {
18834  if (!empty($tag['attribute']['src'])) {
18835  if ($tag['attribute']['src'][0] === '@') {
18836  // data stream
18837  $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1));
18838  $type = '';
18839  } else {
18840  // get image type
18841  $type = TCPDF_IMAGES::getImageFileType($tag['attribute']['src']);
18842  }
18843  if (!isset($tag['width'])) {
18844  $tag['width'] = 0;
18845  }
18846  if (!isset($tag['height'])) {
18847  $tag['height'] = 0;
18848  }
18849  //if (!isset($tag['attribute']['align'])) {
18850  // the only alignment supported is "bottom"
18851  // further development is required for other modes.
18852  $tag['attribute']['align'] = 'bottom';
18853  //}
18854  switch($tag['attribute']['align']) {
18855  case 'top': {
18856  $align = 'T';
18857  break;
18858  }
18859  case 'middle': {
18860  $align = 'M';
18861  break;
18862  }
18863  case 'bottom': {
18864  $align = 'B';
18865  break;
18866  }
18867  default: {
18868  $align = 'B';
18869  break;
18870  }
18871  }
18872  $prevy = $this->y;
18873  $xpos = $this->x;
18874  $imglink = '';
18875  if (isset($this->HREF['url']) AND !TCPDF_STATIC::empty_string($this->HREF['url'])) {
18876  $imglink = $this->HREF['url'];
18877  if ($imglink[0] == '#') {
18878  // convert url to internal link
18879  $lnkdata = explode(',', $imglink);
18880  if (isset($lnkdata[0])) {
18881  $page = intval(substr($lnkdata[0], 1));
18882  if (empty($page) OR ($page <= 0)) {
18883  $page = $this->page;
18884  }
18885  if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
18886  $lnky = floatval($lnkdata[1]);
18887  } else {
18888  $lnky = 0;
18889  }
18890  $imglink = $this->AddLink();
18891  $this->SetLink($imglink, $lnky, $page);
18892  }
18893  }
18894  }
18895  $border = 0;
18896  if (isset($tag['border']) AND !empty($tag['border'])) {
18897  // currently only support 1 (frame) or a combination of 'LTRB'
18898  $border = $tag['border'];
18899  }
18900  $iw = '';
18901  if (isset($tag['width'])) {
18902  $iw = $this->getHTMLUnitToUnits($tag['width'], ($tag['fontsize'] / $this->k), 'px', false);
18903  }
18904  $ih = '';
18905  if (isset($tag['height'])) {
18906  $ih = $this->getHTMLUnitToUnits($tag['height'], ($tag['fontsize'] / $this->k), 'px', false);
18907  }
18908  if (($type == 'eps') OR ($type == 'ai')) {
18909  $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
18910  } elseif ($type == 'svg') {
18911  $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
18912  } else {
18913  $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
18914  }
18915  switch($align) {
18916  case 'T': {
18917  $this->y = $prevy;
18918  break;
18919  }
18920  case 'M': {
18921  $this->y = (($this->img_rb_y + $prevy - ($this->getCellHeight($tag['fontsize'] / $this->k))) / 2);
18922  break;
18923  }
18924  case 'B': {
18925  $this->y = $this->img_rb_y - ($this->getCellHeight($tag['fontsize'] / $this->k) - ($this->getFontDescent($tag['fontname'], $tag['fontstyle'], $tag['fontsize']) * $this->cell_height_ratio));
18926  break;
18927  }
18928  }
18929  }
18930  break;
18931  }
18932  case 'dl': {
18933  ++$this->listnum;
18934  if ($this->listnum == 1) {
18935  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18936  } else {
18937  $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18938  }
18939  break;
18940  }
18941  case 'dt': {
18942  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18943  break;
18944  }
18945  case 'dd': {
18946  if ($this->rtl) {
18947  $this->rMargin += $this->listindent;
18948  } else {
18949  $this->lMargin += $this->listindent;
18950  }
18952  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18953  break;
18954  }
18955  case 'ul':
18956  case 'ol': {
18957  ++$this->listnum;
18958  if ($tag['value'] == 'ol') {
18959  $this->listordered[$this->listnum] = true;
18960  } else {
18961  $this->listordered[$this->listnum] = false;
18962  }
18963  if (isset($tag['attribute']['start'])) {
18964  $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
18965  } else {
18966  $this->listcount[$this->listnum] = 0;
18967  }
18968  if ($this->rtl) {
18969  $this->rMargin += $this->listindent;
18970  $this->x -= $this->listindent;
18971  } else {
18972  $this->lMargin += $this->listindent;
18973  $this->x += $this->listindent;
18974  }
18976  if ($this->listnum == 1) {
18977  if ($key > 1) {
18978  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18979  }
18980  } else {
18981  $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18982  }
18983  break;
18984  }
18985  case 'li': {
18986  if ($key > 2) {
18987  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18988  }
18989  if ($this->listordered[$this->listnum]) {
18990  // ordered item
18991  if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
18992  $this->lispacer = $parent['attribute']['type'];
18993  } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
18994  $this->lispacer = $parent['listtype'];
18995  } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
18996  $this->lispacer = $this->lisymbol;
18997  } else {
18998  $this->lispacer = '#';
18999  }
19000  ++$this->listcount[$this->listnum];
19001  if (isset($tag['attribute']['value'])) {
19002  $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
19003  }
19004  } else {
19005  // unordered item
19006  if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
19007  $this->lispacer = $parent['attribute']['type'];
19008  } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
19009  $this->lispacer = $parent['listtype'];
19010  } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
19011  $this->lispacer = $this->lisymbol;
19012  } else {
19013  $this->lispacer = '!';
19014  }
19015  }
19016  break;
19017  }
19018  case 'blockquote': {
19019  if ($this->rtl) {
19020  $this->rMargin += $this->listindent;
19021  } else {
19022  $this->lMargin += $this->listindent;
19023  }
19025  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19026  break;
19027  }
19028  case 'br': {
19029  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19030  break;
19031  }
19032  case 'div': {
19033  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19034  break;
19035  }
19036  case 'p': {
19037  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19038  break;
19039  }
19040  case 'pre': {
19041  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19042  $this->premode = true;
19043  break;
19044  }
19045  case 'sup': {
19046  $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
19047  break;
19048  }
19049  case 'sub': {
19050  $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
19051  break;
19052  }
19053  case 'h1':
19054  case 'h2':
19055  case 'h3':
19056  case 'h4':
19057  case 'h5':
19058  case 'h6': {
19059  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19060  break;
19061  }
19062  // Form fields (since 4.8.000 - 2009-09-07)
19063  case 'form': {
19064  if (isset($tag['attribute']['action'])) {
19065  $this->form_action = $tag['attribute']['action'];
19066  } else {
19067  $this->Error('Please explicitly set action attribute path!');
19068  }
19069  if (isset($tag['attribute']['enctype'])) {
19070  $this->form_enctype = $tag['attribute']['enctype'];
19071  } else {
19072  $this->form_enctype = 'application/x-www-form-urlencoded';
19073  }
19074  if (isset($tag['attribute']['method'])) {
19075  $this->form_mode = $tag['attribute']['method'];
19076  } else {
19077  $this->form_mode = 'post';
19078  }
19079  break;
19080  }
19081  case 'input': {
19082  if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19083  $name = $tag['attribute']['name'];
19084  } else {
19085  break;
19086  }
19087  $prop = array();
19088  $opt = array();
19089  if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19090  $prop['readonly'] = true;
19091  }
19092  if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19093  $value = $tag['attribute']['value'];
19094  }
19095  if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC::empty_string($tag['attribute']['maxlength'])) {
19096  $opt['maxlen'] = intval($tag['attribute']['maxlength']);
19097  }
19098  $h = $this->getCellHeight($this->FontSize);
19099  if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19100  $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
19101  } else {
19102  $w = $h;
19103  }
19104  if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
19105  $checked = true;
19106  } else {
19107  $checked = false;
19108  }
19109  if (isset($tag['align'])) {
19110  switch ($tag['align']) {
19111  case 'C': {
19112  $opt['q'] = 1;
19113  break;
19114  }
19115  case 'R': {
19116  $opt['q'] = 2;
19117  break;
19118  }
19119  case 'L':
19120  default: {
19121  break;
19122  }
19123  }
19124  }
19125  switch ($tag['attribute']['type']) {
19126  case 'text': {
19127  if (isset($value)) {
19128  $opt['v'] = $value;
19129  }
19130  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19131  break;
19132  }
19133  case 'password': {
19134  if (isset($value)) {
19135  $opt['v'] = $value;
19136  }
19137  $prop['password'] = 'true';
19138  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19139  break;
19140  }
19141  case 'checkbox': {
19142  if (!isset($value)) {
19143  break;
19144  }
19145  $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
19146  break;
19147  }
19148  case 'radio': {
19149  if (!isset($value)) {
19150  break;
19151  }
19152  $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
19153  break;
19154  }
19155  case 'submit': {
19156  if (!isset($value)) {
19157  $value = 'submit';
19158  }
19159  $w = $this->GetStringWidth($value) * 1.5;
19160  $h *= 1.6;
19161  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19162  $action = array();
19163  $action['S'] = 'SubmitForm';
19164  $action['F'] = $this->form_action;
19165  if ($this->form_enctype != 'FDF') {
19166  $action['Flags'] = array('ExportFormat');
19167  }
19168  if ($this->form_mode == 'get') {
19169  $action['Flags'] = array('GetMethod');
19170  }
19171  $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
19172  break;
19173  }
19174  case 'reset': {
19175  if (!isset($value)) {
19176  $value = 'reset';
19177  }
19178  $w = $this->GetStringWidth($value) * 1.5;
19179  $h *= 1.6;
19180  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19181  $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
19182  break;
19183  }
19184  case 'file': {
19185  $prop['fileSelect'] = 'true';
19186  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19187  if (!isset($value)) {
19188  $value = '*';
19189  }
19190  $w = $this->GetStringWidth($value) * 2;
19191  $h *= 1.2;
19192  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19193  $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
19194  $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19195  break;
19196  }
19197  case 'hidden': {
19198  if (isset($value)) {
19199  $opt['v'] = $value;
19200  }
19201  $opt['f'] = array('invisible', 'hidden');
19202  $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
19203  break;
19204  }
19205  case 'image': {
19206  // THIS TYPE MUST BE FIXED
19207  if (isset($tag['attribute']['src']) AND !TCPDF_STATIC::empty_string($tag['attribute']['src'])) {
19208  $img = $tag['attribute']['src'];
19209  } else {
19210  break;
19211  }
19212  $value = 'img';
19213  //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
19214  if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19215  $jsaction = $tag['attribute']['onclick'];
19216  } else {
19217  $jsaction = '';
19218  }
19219  $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19220  break;
19221  }
19222  case 'button': {
19223  if (!isset($value)) {
19224  $value = ' ';
19225  }
19226  $w = $this->GetStringWidth($value) * 1.5;
19227  $h *= 1.6;
19228  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19229  if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19230  $jsaction = $tag['attribute']['onclick'];
19231  } else {
19232  $jsaction = '';
19233  }
19234  $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19235  break;
19236  }
19237  }
19238  break;
19239  }
19240  case 'textarea': {
19241  $prop = array();
19242  $opt = array();
19243  if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19244  $prop['readonly'] = true;
19245  }
19246  if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19247  $name = $tag['attribute']['name'];
19248  } else {
19249  break;
19250  }
19251  if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19252  $opt['v'] = $tag['attribute']['value'];
19253  }
19254  if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC::empty_string($tag['attribute']['cols'])) {
19255  $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
19256  } else {
19257  $w = 40;
19258  }
19259  if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC::empty_string($tag['attribute']['rows'])) {
19260  $h = intval($tag['attribute']['rows']) * $this->getCellHeight($this->FontSize);
19261  } else {
19262  $h = 10;
19263  }
19264  $prop['multiline'] = 'true';
19265  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19266  break;
19267  }
19268  case 'select': {
19269  $h = $this->getCellHeight($this->FontSize);
19270  if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19271  $h *= ($tag['attribute']['size'] + 1);
19272  }
19273  $prop = array();
19274  $opt = array();
19275  if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19276  $name = $tag['attribute']['name'];
19277  } else {
19278  break;
19279  }
19280  $w = 0;
19281  if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC::empty_string($tag['attribute']['opt'])) {
19282  $options = explode('#!NwL!#', $tag['attribute']['opt']);
19283  $values = array();
19284  foreach ($options as $val) {
19285  if (strpos($val, '#!TaB!#') !== false) {
19286  $opts = explode('#!TaB!#', $val);
19287  $values[] = $opts;
19288  $w = max($w, $this->GetStringWidth($opts[1]));
19289  } else {
19290  $values[] = $val;
19291  $w = max($w, $this->GetStringWidth($val));
19292  }
19293  }
19294  } else {
19295  break;
19296  }
19297  $w *= 2;
19298  if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
19299  $prop['multipleSelection'] = 'true';
19300  $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19301  } else {
19302  $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19303  }
19304  break;
19305  }
19306  case 'tcpdf': {
19307  if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
19308  // Special tag used to call TCPDF methods
19309  if (isset($tag['attribute']['method'])) {
19310  $tcpdf_method = $tag['attribute']['method'];
19311  if (method_exists($this, $tcpdf_method)) {
19312  if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
19313  $params = $this->unserializeTCPDFtagParameters($tag['attribute']['params']);
19314  call_user_func_array(array($this, $tcpdf_method), $params);
19315  } else {
19316  $this->$tcpdf_method();
19317  }
19318  $this->newline = true;
19319  }
19320  }
19321  }
19322  break;
19323  }
19324  default: {
19325  break;
19326  }
19327  }
19328  // define tags that support borders and background colors
19329  $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
19330  if (in_array($tag['value'], $bordertags)) {
19331  // set border
19332  $dom[$key]['borderposition'] = $this->getBorderStartPosition();
19333  }
19334  if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
19335  $pba = $dom[$key]['attribute']['pagebreakafter'];
19336  // check for pagebreak
19337  if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19338  // add a page (or trig AcceptPageBreak() for multicolumn mode)
19339  $this->checkPageBreak($this->PageBreakTrigger + 1);
19340  }
19341  if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19342  OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19343  // add a page (or trig AcceptPageBreak() for multicolumn mode)
19344  $this->checkPageBreak($this->PageBreakTrigger + 1);
19345  }
19346  }
19347  return $dom;
19348  }
19349 
19359  protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
19360  $tag = $dom[$key];
19361  $parent = $dom[($dom[$key]['parent'])];
19362  $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
19363  $in_table_head = false;
19364  // maximum x position (used to draw borders)
19365  if ($this->rtl) {
19366  $xmax = $this->w;
19367  } else {
19368  $xmax = 0;
19369  }
19370  if ($tag['block']) {
19371  $hbz = 0; // distance from y to line bottom
19372  $hb = 0; // vertical space between block tags
19373  // calculate vertical space for block tags
19374  if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
19375  $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
19376  } elseif (isset($parent['fontsize'])) {
19377  $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
19378  } else {
19379  $pre_h = $this->getCellHeight($this->FontSize);
19380  }
19381  if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
19382  $cn = $this->tagvspaces[$tag['value']][1]['n'];
19383  } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
19384  $cn = 0.6;
19385  } else {
19386  $cn = 1;
19387  }
19388  if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) {
19389  $hb = 0;
19390  } else {
19391  $hb = ($cn * $pre_h);
19392  }
19393  if ($maxbottomliney > $this->PageBreakTrigger) {
19394  $hbz = $this->getCellHeight($this->FontSize);
19395  } elseif ($this->y < $maxbottomliney) {
19396  $hbz = ($maxbottomliney - $this->y);
19397  }
19398  }
19399  // Closing tag
19400  switch($tag['value']) {
19401  case 'tr': {
19402  $table_el = $dom[($dom[$key]['parent'])]['parent'];
19403  if (!isset($parent['endy'])) {
19404  $dom[($dom[$key]['parent'])]['endy'] = $this->y;
19405  $parent['endy'] = $this->y;
19406  }
19407  if (!isset($parent['endpage'])) {
19408  $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
19409  $parent['endpage'] = $this->page;
19410  }
19411  if (!isset($parent['endcolumn'])) {
19412  $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column;
19413  $parent['endcolumn'] = $this->current_column;
19414  }
19415  // update row-spanned cells
19416  if (isset($dom[$table_el]['rowspans'])) {
19417  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19418  $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
19419  if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19420  if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
19421  $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
19422  } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
19423  $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19424  $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19425  $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19426  }
19427  }
19428  }
19429  // report new endy and endpage to the 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'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
19433  $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19434  $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
19435  $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19436  $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
19437  $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19438  }
19439  }
19440  // update remaining rowspanned cells
19441  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19442  if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19443  $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
19444  $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
19445  $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
19446  }
19447  }
19448  }
19449  $prev_page = $this->page;
19450  $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
19451  if ($this->num_columns > 1) {
19452  if (($prev_page < $this->page)
19453  AND ((($this->current_column == 0) AND ($dom[($dom[$key]['parent'])]['endcolumn'] == ($this->num_columns - 1)))
19454  OR ($this->current_column == $dom[($dom[$key]['parent'])]['endcolumn']))) {
19455  // page jump
19456  $this->selectColumn(0);
19457  $dom[($dom[$key]['parent'])]['endcolumn'] = 0;
19458  $dom[($dom[$key]['parent'])]['endy'] = $this->y;
19459  } else {
19460  $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
19461  $this->y = $dom[($dom[$key]['parent'])]['endy'];
19462  }
19463  } else {
19464  $this->y = $dom[($dom[$key]['parent'])]['endy'];
19465  }
19466  if (isset($dom[$table_el]['attribute']['cellspacing'])) {
19467  $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
19468  } elseif (isset($dom[$table_el]['border-spacing'])) {
19469  $this->y += $dom[$table_el]['border-spacing']['V'];
19470  }
19471  $this->Ln(0, $cell);
19472  if ($this->current_column == $parent['startcolumn']) {
19473  $this->x = $parent['startx'];
19474  }
19475  // account for booklet mode
19476  if ($this->page > $parent['startpage']) {
19477  if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
19478  $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
19479  } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
19480  $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
19481  }
19482  }
19483  break;
19484  }
19485  case 'tablehead':
19486  // closing tag used for the thead part
19487  $in_table_head = true;
19488  $this->inthead = false;
19489  case 'table': {
19490  $table_el = $parent;
19491  // set default border
19492  if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
19493  // set default border
19494  $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
19495  } else {
19496  $border = 0;
19497  }
19498  $default_border = $border;
19499  // fix bottom line alignment of last line before page break
19500  foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
19501  // update row-spanned cells
19502  if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19503  foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19504  if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) {
19505  $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
19506  }
19507  if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) {
19508  $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
19509  }
19510  }
19511  }
19512  if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
19513  $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
19514  $dom[$prevtrkey]['endy'] = $pgendy;
19515  // update row-spanned cells
19516  if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19517  foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19518  if (($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
19519  $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
19520  $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
19521  }
19522  }
19523  }
19524  }
19525  $prevtrkey = $trkey;
19526  $table_el = $dom[($dom[$key]['parent'])];
19527  }
19528  // for each row
19529  if (count($table_el['trids']) > 0) {
19530  unset($xmax);
19531  }
19532  foreach ($table_el['trids'] as $j => $trkey) {
19533  $parent = $dom[$trkey];
19534  if (!isset($xmax)) {
19535  $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
19536  }
19537  // for each cell on the row
19538  foreach ($parent['cellpos'] as $k => $cellpos) {
19539  if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
19540  $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
19541  $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
19542  $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
19543  $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
19544  $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
19545  $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
19546  $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
19547  } else {
19548  $endy = $parent['endy'];
19549  $startpage = $parent['startpage'];
19550  $endpage = $parent['endpage'];
19551  $startcolumn = $parent['startcolumn'];
19552  $endcolumn = $parent['endcolumn'];
19553  }
19554  if ($this->num_columns == 0) {
19555  $this->num_columns = 1;
19556  }
19557  if (isset($cellpos['border'])) {
19558  $border = $cellpos['border'];
19559  }
19560  if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
19561  $this->SetFillColorArray($cellpos['bgcolor']);
19562  $fill = true;
19563  } else {
19564  $fill = false;
19565  }
19566  $x = $cellpos['startx'];
19567  $y = $parent['starty'];
19568  $starty = $y;
19569  $w = abs($cellpos['endx'] - $cellpos['startx']);
19570  // get border modes
19571  $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
19572  $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
19573  $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19574  // design borders around HTML cells.
19575  for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
19576  $ccode = '';
19577  $this->setPage($page);
19578  if ($this->num_columns < 2) {
19579  // single-column mode
19580  $this->x = $x;
19581  $this->y = $this->tMargin;
19582  }
19583  // account for margin changes
19584  if ($page > $startpage) {
19585  if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
19586  $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
19587  } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
19588  $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
19589  }
19590  }
19591  if ($startpage == $endpage) { // single page
19592  $deltacol = 0;
19593  $deltath = 0;
19594  for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
19595  $this->selectColumn($column);
19596  if ($startcolumn == $endcolumn) { // single column
19597  $cborder = $border;
19598  $h = $endy - $parent['starty'];
19599  $this->y = $y;
19600  $this->x = $x;
19601  } elseif ($column == $startcolumn) { // first column
19602  $cborder = $border_start;
19603  $this->y = $starty;
19604  $this->x = $x;
19605  $h = $this->h - $this->y - $this->bMargin;
19606  if ($this->rtl) {
19607  $deltacol = $this->x + $this->rMargin - $this->w;
19608  } else {
19609  $deltacol = $this->x - $this->lMargin;
19610  }
19611  } elseif ($column == $endcolumn) { // end column
19612  $cborder = $border_end;
19613  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19614  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19615  }
19616  $this->x += $deltacol;
19617  $h = $endy - $this->y;
19618  } else { // middle column
19619  $cborder = $border_middle;
19620  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19621  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19622  }
19623  $this->x += $deltacol;
19624  $h = $this->h - $this->y - $this->bMargin;
19625  }
19626  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19627  } // end for each column
19628  } elseif ($page == $startpage) { // first page
19629  $deltacol = 0;
19630  $deltath = 0;
19631  for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
19632  $this->selectColumn($column);
19633  if ($column == $startcolumn) { // first column
19634  $cborder = $border_start;
19635  $this->y = $starty;
19636  $this->x = $x;
19637  $h = $this->h - $this->y - $this->bMargin;
19638  if ($this->rtl) {
19639  $deltacol = $this->x + $this->rMargin - $this->w;
19640  } else {
19641  $deltacol = $this->x - $this->lMargin;
19642  }
19643  } else { // middle column
19644  $cborder = $border_middle;
19645  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19646  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19647  }
19648  $this->x += $deltacol;
19649  $h = $this->h - $this->y - $this->bMargin;
19650  }
19651  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19652  } // end for each column
19653  } elseif ($page == $endpage) { // last page
19654  $deltacol = 0;
19655  $deltath = 0;
19656  for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
19657  $this->selectColumn($column);
19658  if ($column == $endcolumn) { // end column
19659  $cborder = $border_end;
19660  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19661  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19662  }
19663  $this->x += $deltacol;
19664  $h = $endy - $this->y;
19665  } else { // middle column
19666  $cborder = $border_middle;
19667  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19668  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19669  }
19670  $this->x += $deltacol;
19671  $h = $this->h - $this->y - $this->bMargin;
19672  }
19673  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19674  } // end for each column
19675  } else { // middle page
19676  $deltacol = 0;
19677  $deltath = 0;
19678  for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
19679  $this->selectColumn($column);
19680  $cborder = $border_middle;
19681  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19682  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19683  }
19684  $this->x += $deltacol;
19685  $h = $this->h - $this->y - $this->bMargin;
19686  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19687  } // end for each column
19688  }
19689  if (!empty($cborder) OR !empty($fill)) {
19690  $offsetlen = strlen($ccode);
19691  // draw border and fill
19692  if ($this->inxobj) {
19693  // we are inside an XObject template
19694  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
19695  $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
19696  $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
19697  $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
19698  } else {
19699  $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
19700  $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
19701  }
19702  $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
19703  $pstart = substr($pagebuff, 0, $pagemark);
19704  $pend = substr($pagebuff, $pagemark);
19705  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
19706  } else {
19707  // draw border and fill
19708  if (end($this->transfmrk[$this->page]) !== false) {
19709  $pagemarkkey = key($this->transfmrk[$this->page]);
19710  $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
19711  } elseif ($this->InFooter) {
19712  $pagemark = $this->footerpos[$this->page];
19713  } else {
19714  $pagemark = $this->intmrk[$this->page];
19715  }
19716  $pagebuff = $this->getPageBuffer($this->page);
19717  $pstart = substr($pagebuff, 0, $pagemark);
19718  $pend = substr($pagebuff, $pagemark);
19719  $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
19720  }
19721  }
19722  } // end for each page
19723  // restore default border
19724  $border = $default_border;
19725  } // end for each cell on the row
19726  if (isset($table_el['attribute']['cellspacing'])) {
19727  $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
19728  } elseif (isset($table_el['border-spacing'])) {
19729  $this->y += $table_el['border-spacing']['V'];
19730  }
19731  $this->Ln(0, $cell);
19732  $this->x = $parent['startx'];
19733  if ($endpage > $startpage) {
19734  if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
19735  $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
19736  } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
19737  $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
19738  }
19739  }
19740  }
19741  if (!$in_table_head) { // we are not inside a thead section
19742  $this->cell_padding = $table_el['old_cell_padding'];
19743  // reset row height
19744  $this->resetLastH();
19745  if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) {
19746  $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]);
19747  if (($plendiff > 0) AND ($plendiff < 60)) {
19748  $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff);
19749  if (substr($pagediff, 0, 5) == 'BT /F') {
19750  // the difference is only a font setting
19751  $plendiff = 0;
19752  }
19753  }
19754  if ($plendiff == 0) {
19755  // remove last blank page
19756  $this->deletePage($this->numpages);
19757  }
19758  }
19759  if (isset($this->theadMargins['top'])) {
19760  // restore top margin
19761  $this->tMargin = $this->theadMargins['top'];
19762  }
19763  if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
19764  // reset main table header
19765  $this->thead = '';
19766  $this->theadMargins = array();
19767  $this->pagedim[$this->page]['tm'] = $this->tMargin;
19768  }
19769  }
19770  $parent = $table_el;
19771  break;
19772  }
19773  case 'a': {
19774  $this->HREF = '';
19775  break;
19776  }
19777  case 'sup': {
19778  $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
19779  break;
19780  }
19781  case 'sub': {
19782  $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k));
19783  break;
19784  }
19785  case 'div': {
19786  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19787  break;
19788  }
19789  case 'blockquote': {
19790  if ($this->rtl) {
19791  $this->rMargin -= $this->listindent;
19792  } else {
19793  $this->lMargin -= $this->listindent;
19794  }
19796  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19797  break;
19798  }
19799  case 'p': {
19800  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19801  break;
19802  }
19803  case 'pre': {
19804  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19805  $this->premode = false;
19806  break;
19807  }
19808  case 'dl': {
19809  --$this->listnum;
19810  if ($this->listnum <= 0) {
19811  $this->listnum = 0;
19812  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19813  } else {
19814  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19815  }
19816  $this->resetLastH();
19817  break;
19818  }
19819  case 'dt': {
19820  $this->lispacer = '';
19821  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19822  break;
19823  }
19824  case 'dd': {
19825  $this->lispacer = '';
19826  if ($this->rtl) {
19827  $this->rMargin -= $this->listindent;
19828  } else {
19829  $this->lMargin -= $this->listindent;
19830  }
19832  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19833  break;
19834  }
19835  case 'ul':
19836  case 'ol': {
19837  --$this->listnum;
19838  $this->lispacer = '';
19839  if ($this->rtl) {
19840  $this->rMargin -= $this->listindent;
19841  } else {
19842  $this->lMargin -= $this->listindent;
19843  }
19845  if ($this->listnum <= 0) {
19846  $this->listnum = 0;
19847  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19848  } else {
19849  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19850  }
19851  $this->resetLastH();
19852  break;
19853  }
19854  case 'li': {
19855  $this->lispacer = '';
19856  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19857  break;
19858  }
19859  case 'h1':
19860  case 'h2':
19861  case 'h3':
19862  case 'h4':
19863  case 'h5':
19864  case 'h6': {
19865  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19866  break;
19867  }
19868  // Form fields (since 4.8.000 - 2009-09-07)
19869  case 'form': {
19870  $this->form_action = '';
19871  $this->form_enctype = 'application/x-www-form-urlencoded';
19872  break;
19873  }
19874  default : {
19875  break;
19876  }
19877  }
19878  // draw border and background (if any)
19879  $this->drawHTMLTagBorder($parent, $xmax);
19880  if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
19881  $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
19882  // check for pagebreak
19883  if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19884  // add a page (or trig AcceptPageBreak() for multicolumn mode)
19885  $this->checkPageBreak($this->PageBreakTrigger + 1);
19886  }
19887  if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19888  OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19889  // add a page (or trig AcceptPageBreak() for multicolumn mode)
19890  $this->checkPageBreak($this->PageBreakTrigger + 1);
19891  }
19892  }
19893  $this->tmprtl = false;
19894  return $dom;
19895  }
19896 
19906  protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
19907  if ($firsttag) {
19908  $this->Ln(0, $cell);
19909  $this->htmlvspace = 0;
19910  return;
19911  }
19912  if ($lasttag) {
19913  $this->Ln($hbz, $cell);
19914  $this->htmlvspace = 0;
19915  return;
19916  }
19917  if ($hb < $this->htmlvspace) {
19918  $hd = 0;
19919  } else {
19920  $hd = $hb - $this->htmlvspace;
19921  $this->htmlvspace = $hb;
19922  }
19923  $this->Ln(($hbz + $hd), $cell);
19924  }
19925 
19932  protected function getBorderStartPosition() {
19933  if ($this->rtl) {
19934  $xmax = $this->lMargin;
19935  } else {
19936  $xmax = $this->w - $this->rMargin;
19937  }
19938  return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax);
19939  }
19940 
19948  protected function drawHTMLTagBorder($tag, $xmax) {
19949  if (!isset($tag['borderposition'])) {
19950  // nothing to draw
19951  return;
19952  }
19953  $prev_x = $this->x;
19954  $prev_y = $this->y;
19955  $prev_lasth = $this->lasth;
19956  $border = 0;
19957  $fill = false;
19958  $this->lasth = 0;
19959  if (isset($tag['border']) AND !empty($tag['border'])) {
19960  // get border style
19961  $border = $tag['border'];
19962  if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
19963  // border for table header
19964  $border = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19965  }
19966  }
19967  if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
19968  // get background color
19969  $old_bgcolor = $this->bgcolor;
19970  $this->SetFillColorArray($tag['bgcolor']);
19971  $fill = true;
19972  }
19973  if (!$border AND !$fill) {
19974  // nothing to draw
19975  return;
19976  }
19977  if (isset($tag['attribute']['cellspacing'])) {
19978  $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
19979  $cellspacing = array('H' => $clsp, 'V' => $clsp);
19980  } elseif (isset($tag['border-spacing'])) {
19981  $cellspacing = $tag['border-spacing'];
19982  } else {
19983  $cellspacing = array('H' => 0, 'V' => 0);
19984  }
19985  if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
19986  // draw the border externally respect the sqare edge.
19987  $border['mode'] = 'ext';
19988  }
19989  if ($this->rtl) {
19990  if ($xmax >= $tag['borderposition']['x']) {
19991  $xmax = $tag['borderposition']['xmax'];
19992  }
19993  $w = ($tag['borderposition']['x'] - $xmax);
19994  } else {
19995  if ($xmax <= $tag['borderposition']['x']) {
19996  $xmax = $tag['borderposition']['xmax'];
19997  }
19998  $w = ($xmax - $tag['borderposition']['x']);
19999  }
20000  if ($w <= 0) {
20001  return;
20002  }
20003  $w += $cellspacing['H'];
20004  $startpage = $tag['borderposition']['page'];
20005  $startcolumn = $tag['borderposition']['column'];
20006  $x = $tag['borderposition']['x'];
20007  $y = $tag['borderposition']['y'];
20008  $endpage = $this->page;
20009  $starty = $tag['borderposition']['y'] - $cellspacing['V'];
20010  $currentY = $this->y;
20011  $this->x = $x;
20012  // get latest column
20013  $endcolumn = $this->current_column;
20014  if ($this->num_columns == 0) {
20015  $this->num_columns = 1;
20016  }
20017  // get border modes
20018  $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
20019  $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
20020  $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
20021  // temporary disable page regions
20022  $temp_page_regions = $this->page_regions;
20023  $this->page_regions = array();
20024  // design borders around HTML cells.
20025  for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
20026  $ccode = '';
20027  $this->setPage($page);
20028  if ($this->num_columns < 2) {
20029  // single-column mode
20030  $this->x = $x;
20031  $this->y = $this->tMargin;
20032  }
20033  // account for margin changes
20034  if ($page > $startpage) {
20035  if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
20036  $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
20037  } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
20038  $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
20039  }
20040  }
20041  if ($startpage == $endpage) {
20042  // single page
20043  for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
20044  $this->selectColumn($column);
20045  if ($startcolumn == $endcolumn) { // single column
20046  $cborder = $border;
20047  $h = ($currentY - $y) + $cellspacing['V'];
20048  $this->y = $starty;
20049  } elseif ($column == $startcolumn) { // first column
20050  $cborder = $border_start;
20051  $this->y = $starty;
20052  $h = $this->h - $this->y - $this->bMargin;
20053  } elseif ($column == $endcolumn) { // end column
20054  $cborder = $border_end;
20055  $h = $currentY - $this->y;
20056  } else { // middle column
20057  $cborder = $border_middle;
20058  $h = $this->h - $this->y - $this->bMargin;
20059  }
20060  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20061  } // end for each column
20062  } elseif ($page == $startpage) { // first page
20063  for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
20064  $this->selectColumn($column);
20065  if ($column == $startcolumn) { // first column
20066  $cborder = $border_start;
20067  $this->y = $starty;
20068  $h = $this->h - $this->y - $this->bMargin;
20069  } else { // middle column
20070  $cborder = $border_middle;
20071  $h = $this->h - $this->y - $this->bMargin;
20072  }
20073  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20074  } // end for each column
20075  } elseif ($page == $endpage) { // last page
20076  for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
20077  $this->selectColumn($column);
20078  if ($column == $endcolumn) {
20079  // end column
20080  $cborder = $border_end;
20081  $h = $currentY - $this->y;
20082  } else {
20083  // middle column
20084  $cborder = $border_middle;
20085  $h = $this->h - $this->y - $this->bMargin;
20086  }
20087  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20088  } // end for each column
20089  } else { // middle page
20090  for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
20091  $this->selectColumn($column);
20092  $cborder = $border_middle;
20093  $h = $this->h - $this->y - $this->bMargin;
20094  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20095  } // end for each column
20096  }
20097  if ($cborder OR $fill) {
20098  $offsetlen = strlen($ccode);
20099  // draw border and fill
20100  if ($this->inxobj) {
20101  // we are inside an XObject template
20102  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
20103  $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
20104  $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
20105  $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
20106  } else {
20107  $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
20108  $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
20109  }
20110  $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
20111  $pstart = substr($pagebuff, 0, $pagemark);
20112  $pend = substr($pagebuff, $pagemark);
20113  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
20114  } else {
20115  if (end($this->transfmrk[$this->page]) !== false) {
20116  $pagemarkkey = key($this->transfmrk[$this->page]);
20117  $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
20118  } elseif ($this->InFooter) {
20119  $pagemark = $this->footerpos[$this->page];
20120  } else {
20121  $pagemark = $this->intmrk[$this->page];
20122  }
20123  $pagebuff = $this->getPageBuffer($this->page);
20124  $pstart = substr($pagebuff, 0, $pagemark);
20125  $pend = substr($pagebuff, $pagemark);
20126  $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
20127  $this->bordermrk[$this->page] += $offsetlen;
20128  $this->cntmrk[$this->page] += $offsetlen;
20129  }
20130  }
20131  } // end for each page
20132  // restore page regions
20133  $this->page_regions = $temp_page_regions;
20134  if (isset($old_bgcolor)) {
20135  // restore background color
20136  $this->SetFillColorArray($old_bgcolor);
20137  }
20138  // restore pointer position
20139  $this->x = $prev_x;
20140  $this->y = $prev_y;
20141  $this->lasth = $prev_lasth;
20142  }
20143 
20150  public function setLIsymbol($symbol='!') {
20151  // check for custom image symbol
20152  if (substr($symbol, 0, 4) == 'img|') {
20153  $this->lisymbol = $symbol;
20154  return;
20155  }
20156  $symbol = strtolower($symbol);
20157  $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');
20158  if (in_array($symbol, $valid_symbols)) {
20159  $this->lisymbol = $symbol;
20160  } else {
20161  $this->lisymbol = '';
20162  }
20163  }
20164 
20173  public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
20174  $this->booklet = $booklet;
20175  if ($inner >= 0) {
20176  $this->lMargin = $inner;
20177  }
20178  if ($outer >= 0) {
20179  $this->rMargin = $outer;
20180  }
20181  }
20182 
20189  protected function swapMargins($reverse=true) {
20190  if ($reverse) {
20191  // swap left and right margins
20192  $mtemp = $this->original_lMargin;
20193  $this->original_lMargin = $this->original_rMargin;
20194  $this->original_rMargin = $mtemp;
20195  $deltam = $this->original_lMargin - $this->original_rMargin;
20196  $this->lMargin += $deltam;
20197  $this->rMargin -= $deltam;
20198  }
20199  }
20200 
20213  public function setHtmlVSpace($tagvs) {
20214  $this->tagvspaces = $tagvs;
20215  }
20216 
20223  public function setListIndentWidth($width) {
20224  return $this->customlistindent = floatval($width);
20225  }
20226 
20233  public function setOpenCell($isopen) {
20234  $this->opencell = $isopen;
20235  }
20236 
20244  public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
20245  $this->htmlLinkColorArray = $color;
20246  $this->htmlLinkFontStyle = $fontstyle;
20247  }
20248 
20259  public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
20260  $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
20261  $retval = 0;
20262  $value = 0;
20263  $unit = 'px';
20264  if ($points) {
20265  $k = 1;
20266  } else {
20267  $k = $this->k;
20268  }
20269  if (in_array($defaultunit, $supportedunits)) {
20270  $unit = $defaultunit;
20271  }
20272  if (is_numeric($htmlval)) {
20273  $value = floatval($htmlval);
20274  } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
20275  $value = floatval($mnum[1]);
20276  if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
20277  if (in_array($munit[1], $supportedunits)) {
20278  $unit = $munit[1];
20279  }
20280  }
20281  }
20282  switch ($unit) {
20283  // percentage
20284  case '%': {
20285  $retval = (($value * $refsize) / 100);
20286  break;
20287  }
20288  // relative-size
20289  case 'em': {
20290  $retval = ($value * $refsize);
20291  break;
20292  }
20293  // height of lower case 'x' (about half the font-size)
20294  case 'ex': {
20295  $retval = ($value * ($refsize / 2));
20296  break;
20297  }
20298  // absolute-size
20299  case 'in': {
20300  $retval = (($value * $this->dpi) / $k);
20301  break;
20302  }
20303  // centimeters
20304  case 'cm': {
20305  $retval = (($value / 2.54 * $this->dpi) / $k);
20306  break;
20307  }
20308  // millimeters
20309  case 'mm': {
20310  $retval = (($value / 25.4 * $this->dpi) / $k);
20311  break;
20312  }
20313  // one pica is 12 points
20314  case 'pc': {
20315  $retval = (($value * 12) / $k);
20316  break;
20317  }
20318  // points
20319  case 'pt': {
20320  $retval = ($value / $k);
20321  break;
20322  }
20323  // pixels
20324  case 'px': {
20325  $retval = $this->pixelsToUnits($value);
20326  if ($points) {
20327  $retval *= $this->k;
20328  }
20329  break;
20330  }
20331  }
20332  return $retval;
20333  }
20334 
20343  protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
20344  if ($this->state != 2) {
20345  return;
20346  }
20347  $size /= $this->k;
20348  $fill = '';
20350  $color = $this->fgcolor;
20352  $width = 0;
20353  $textitem = '';
20354  $tmpx = $this->x;
20355  $lspace = $this->GetStringWidth(' ');
20356  if ($listtype == '^') {
20357  // special symbol used for avoid justification of rect bullet
20358  $this->lispacer = '';
20359  return;
20360  } elseif ($listtype == '!') {
20361  // set default list type for unordered list
20362  $deftypes = array('disc', 'circle', 'square');
20363  $listtype = $deftypes[($listdepth - 1) % 3];
20364  } elseif ($listtype == '#') {
20365  // set default list type for ordered list
20366  $listtype = 'decimal';
20367  } elseif (substr($listtype, 0, 4) == 'img|') {
20368  // custom image type ('img|type|width|height|image.ext')
20369  $img = explode('|', $listtype);
20370  $listtype = 'img';
20371  }
20372  switch ($listtype) {
20373  // unordered types
20374  case 'none': {
20375  break;
20376  }
20377  case 'disc': {
20378  $r = $size / 6;
20379  $lspace += (2 * $r);
20380  if ($this->rtl) {
20381  $this->x += $lspace;
20382  } else {
20383  $this->x -= $lspace;
20384  }
20385  $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8);
20386  break;
20387  }
20388  case 'circle': {
20389  $r = $size / 6;
20390  $lspace += (2 * $r);
20391  if ($this->rtl) {
20392  $this->x += $lspace;
20393  } else {
20394  $this->x -= $lspace;
20395  }
20396  $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor;
20397  $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
20398  $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
20399  $this->_out($prev_line_style); // restore line settings
20400  break;
20401  }
20402  case 'square': {
20403  $l = $size / 3;
20404  $lspace += $l;
20405  if ($this->rtl) {;
20406  $this->x += $lspace;
20407  } else {
20408  $this->x -= $lspace;
20409  }
20410  $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color);
20411  break;
20412  }
20413  case 'img': {
20414  // 1=>type, 2=>width, 3=>height, 4=>image.ext
20415  $lspace += $img[2];
20416  if ($this->rtl) {;
20417  $this->x += $lspace;
20418  } else {
20419  $this->x -= $lspace;
20420  }
20421  $imgtype = strtolower($img[1]);
20422  $prev_y = $this->y;
20423  switch ($imgtype) {
20424  case 'svg': {
20425  $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
20426  break;
20427  }
20428  case 'ai':
20429  case 'eps': {
20430  $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
20431  break;
20432  }
20433  default: {
20434  $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);
20435  break;
20436  }
20437  }
20438  $this->y = $prev_y;
20439  break;
20440  }
20441  // ordered types
20442  // $this->listcount[$this->listnum];
20443  // $textitem
20444  case '1':
20445  case 'decimal': {
20446  $textitem = $this->listcount[$this->listnum];
20447  break;
20448  }
20449  case 'decimal-leading-zero': {
20450  $textitem = sprintf('%02d', $this->listcount[$this->listnum]);
20451  break;
20452  }
20453  case 'i':
20454  case 'lower-roman': {
20455  $textitem = strtolower(TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]));
20456  break;
20457  }
20458  case 'I':
20459  case 'upper-roman': {
20460  $textitem = TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]);
20461  break;
20462  }
20463  case 'a':
20464  case 'lower-alpha':
20465  case 'lower-latin': {
20466  $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
20467  break;
20468  }
20469  case 'A':
20470  case 'upper-alpha':
20471  case 'upper-latin': {
20472  $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
20473  break;
20474  }
20475  case 'lower-greek': {
20476  $textitem = TCPDF_FONTS::unichr((945 + $this->listcount[$this->listnum] - 1), $this->isunicode);
20477  break;
20478  }
20479  /*
20480  // Types to be implemented (special handling)
20481  case 'hebrew': {
20482  break;
20483  }
20484  case 'armenian': {
20485  break;
20486  }
20487  case 'georgian': {
20488  break;
20489  }
20490  case 'cjk-ideographic': {
20491  break;
20492  }
20493  case 'hiragana': {
20494  break;
20495  }
20496  case 'katakana': {
20497  break;
20498  }
20499  case 'hiragana-iroha': {
20500  break;
20501  }
20502  case 'katakana-iroha': {
20503  break;
20504  }
20505  */
20506  default: {
20507  $textitem = $this->listcount[$this->listnum];
20508  }
20509  }
20510  if (!TCPDF_STATIC::empty_string($textitem)) {
20511  // Check whether we need a new page or new column
20512  $prev_y = $this->y;
20513  $h = $this->getCellHeight($this->FontSize);
20514  if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) {
20515  $tmpx = $this->x;
20516  }
20517  // print ordered item
20518  if ($this->rtl) {
20519  $textitem = '.'.$textitem;
20520  } else {
20521  $textitem = $textitem.'.';
20522  }
20523  $lspace += $this->GetStringWidth($textitem);
20524  if ($this->rtl) {
20525  $this->x += $lspace;
20526  } else {
20527  $this->x -= $lspace;
20528  }
20529  $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
20530  }
20531  $this->x = $tmpx;
20532  $this->lispacer = '^';
20533  // restore colors
20534  $this->SetFillColorArray($bgcolor);
20536  $this->SettextColorArray($color);
20537  }
20538 
20545  protected function getGraphicVars() {
20546  $grapvars = array(
20547  'FontFamily' => $this->FontFamily,
20548  'FontStyle' => $this->FontStyle,
20549  'FontSizePt' => $this->FontSizePt,
20550  'rMargin' => $this->rMargin,
20551  'lMargin' => $this->lMargin,
20552  'cell_padding' => $this->cell_padding,
20553  'cell_margin' => $this->cell_margin,
20554  'LineWidth' => $this->LineWidth,
20555  'linestyleWidth' => $this->linestyleWidth,
20556  'linestyleCap' => $this->linestyleCap,
20557  'linestyleJoin' => $this->linestyleJoin,
20558  'linestyleDash' => $this->linestyleDash,
20559  'textrendermode' => $this->textrendermode,
20560  'textstrokewidth' => $this->textstrokewidth,
20561  'DrawColor' => $this->DrawColor,
20562  'FillColor' => $this->FillColor,
20563  'TextColor' => $this->TextColor,
20564  'ColorFlag' => $this->ColorFlag,
20565  'bgcolor' => $this->bgcolor,
20566  'fgcolor' => $this->fgcolor,
20567  'htmlvspace' => $this->htmlvspace,
20568  'listindent' => $this->listindent,
20569  'listindentlevel' => $this->listindentlevel,
20570  'listnum' => $this->listnum,
20571  'listordered' => $this->listordered,
20572  'listcount' => $this->listcount,
20573  'lispacer' => $this->lispacer,
20574  'cell_height_ratio' => $this->cell_height_ratio,
20575  'font_stretching' => $this->font_stretching,
20576  'font_spacing' => $this->font_spacing,
20577  'alpha' => $this->alpha,
20578  // extended
20579  'lasth' => $this->lasth,
20580  'tMargin' => $this->tMargin,
20581  'bMargin' => $this->bMargin,
20582  'AutoPageBreak' => $this->AutoPageBreak,
20583  'PageBreakTrigger' => $this->PageBreakTrigger,
20584  'x' => $this->x,
20585  'y' => $this->y,
20586  'w' => $this->w,
20587  'h' => $this->h,
20588  'wPt' => $this->wPt,
20589  'hPt' => $this->hPt,
20590  'fwPt' => $this->fwPt,
20591  'fhPt' => $this->fhPt,
20592  'page' => $this->page,
20593  'current_column' => $this->current_column,
20594  'num_columns' => $this->num_columns
20595  );
20596  return $grapvars;
20597  }
20598 
20606  protected function setGraphicVars($gvars, $extended=false) {
20607  if ($this->state != 2) {
20608  return;
20609  }
20610  $this->FontFamily = $gvars['FontFamily'];
20611  $this->FontStyle = $gvars['FontStyle'];
20612  $this->FontSizePt = $gvars['FontSizePt'];
20613  $this->rMargin = $gvars['rMargin'];
20614  $this->lMargin = $gvars['lMargin'];
20615  $this->cell_padding = $gvars['cell_padding'];
20616  $this->cell_margin = $gvars['cell_margin'];
20617  $this->LineWidth = $gvars['LineWidth'];
20618  $this->linestyleWidth = $gvars['linestyleWidth'];
20619  $this->linestyleCap = $gvars['linestyleCap'];
20620  $this->linestyleJoin = $gvars['linestyleJoin'];
20621  $this->linestyleDash = $gvars['linestyleDash'];
20622  $this->textrendermode = $gvars['textrendermode'];
20623  $this->textstrokewidth = $gvars['textstrokewidth'];
20624  $this->DrawColor = $gvars['DrawColor'];
20625  $this->FillColor = $gvars['FillColor'];
20626  $this->TextColor = $gvars['TextColor'];
20627  $this->ColorFlag = $gvars['ColorFlag'];
20628  $this->bgcolor = $gvars['bgcolor'];
20629  $this->fgcolor = $gvars['fgcolor'];
20630  $this->htmlvspace = $gvars['htmlvspace'];
20631  $this->listindent = $gvars['listindent'];
20632  $this->listindentlevel = $gvars['listindentlevel'];
20633  $this->listnum = $gvars['listnum'];
20634  $this->listordered = $gvars['listordered'];
20635  $this->listcount = $gvars['listcount'];
20636  $this->lispacer = $gvars['lispacer'];
20637  $this->cell_height_ratio = $gvars['cell_height_ratio'];
20638  $this->font_stretching = $gvars['font_stretching'];
20639  $this->font_spacing = $gvars['font_spacing'];
20640  $this->alpha = $gvars['alpha'];
20641  if ($extended) {
20642  // restore extended values
20643  $this->lasth = $gvars['lasth'];
20644  $this->tMargin = $gvars['tMargin'];
20645  $this->bMargin = $gvars['bMargin'];
20646  $this->AutoPageBreak = $gvars['AutoPageBreak'];
20647  $this->PageBreakTrigger = $gvars['PageBreakTrigger'];
20648  $this->x = $gvars['x'];
20649  $this->y = $gvars['y'];
20650  $this->w = $gvars['w'];
20651  $this->h = $gvars['h'];
20652  $this->wPt = $gvars['wPt'];
20653  $this->hPt = $gvars['hPt'];
20654  $this->fwPt = $gvars['fwPt'];
20655  $this->fhPt = $gvars['fhPt'];
20656  $this->page = $gvars['page'];
20657  $this->current_column = $gvars['current_column'];
20658  $this->num_columns = $gvars['num_columns'];
20659  }
20660  $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
20661  if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
20662  $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
20663  }
20664  }
20665 
20670  protected function _outSaveGraphicsState() {
20671  $this->_out('q');
20672  }
20673 
20678  protected function _outRestoreGraphicsState() {
20679  $this->_out('Q');
20680  }
20681 
20688  protected function setBuffer($data) {
20689  $this->bufferlen += strlen($data);
20690  $this->buffer .= $data;
20691  }
20692 
20699  protected function replaceBuffer($data) {
20700  $this->bufferlen = strlen($data);
20701  $this->buffer = $data;
20702  }
20703 
20710  protected function getBuffer() {
20711  return $this->buffer;
20712  }
20713 
20722  protected function setPageBuffer($page, $data, $append=false) {
20723  if ($append) {
20724  $this->pages[$page] .= $data;
20725  } else {
20726  $this->pages[$page] = $data;
20727  }
20728  if ($append AND isset($this->pagelen[$page])) {
20729  $this->pagelen[$page] += strlen($data);
20730  } else {
20731  $this->pagelen[$page] = strlen($data);
20732  }
20733  }
20734 
20742  protected function getPageBuffer($page) {
20743  if (isset($this->pages[$page])) {
20744  return $this->pages[$page];
20745  }
20746  return false;
20747  }
20748 
20757  protected function setImageBuffer($image, $data) {
20758  if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) {
20759  $this->imagekeys[$this->numimages] = $image;
20760  $data['i'] = $this->numimages;
20761  ++$this->numimages;
20762  }
20763  $this->images[$image] = $data;
20764  return $data['i'];
20765  }
20766 
20775  protected function setImageSubBuffer($image, $key, $data) {
20776  if (!isset($this->images[$image])) {
20777  $this->setImageBuffer($image, array());
20778  }
20779  $this->images[$image][$key] = $data;
20780  }
20781 
20789  protected function getImageBuffer($image) {
20790  if (isset($this->images[$image])) {
20791  return $this->images[$image];
20792  }
20793  return false;
20794  }
20795 
20803  protected function setFontBuffer($font, $data) {
20804  $this->fonts[$font] = $data;
20805  if (!in_array($font, $this->fontkeys)) {
20806  $this->fontkeys[] = $font;
20807  // store object ID for current font
20808  ++$this->n;
20809  $this->font_obj_ids[$font] = $this->n;
20810  $this->setFontSubBuffer($font, 'n', $this->n);
20811  }
20812  }
20813 
20822  protected function setFontSubBuffer($font, $key, $data) {
20823  if (!isset($this->fonts[$font])) {
20824  $this->setFontBuffer($font, array());
20825  }
20826  $this->fonts[$font][$key] = $data;
20827  }
20828 
20836  protected function getFontBuffer($font) {
20837  if (isset($this->fonts[$font])) {
20838  return $this->fonts[$font];
20839  }
20840  return false;
20841  }
20842 
20851  public function movePage($frompage, $topage) {
20852  if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
20853  return false;
20854  }
20855  if ($frompage == $this->page) {
20856  // close the page before moving it
20857  $this->endPage();
20858  }
20859  // move all page-related states
20860  $tmppage = $this->getPageBuffer($frompage);
20861  $tmppagedim = $this->pagedim[$frompage];
20862  $tmppagelen = $this->pagelen[$frompage];
20863  $tmpintmrk = $this->intmrk[$frompage];
20864  $tmpbordermrk = $this->bordermrk[$frompage];
20865  $tmpcntmrk = $this->cntmrk[$frompage];
20866  $tmppageobjects = $this->pageobjects[$frompage];
20867  if (isset($this->footerpos[$frompage])) {
20868  $tmpfooterpos = $this->footerpos[$frompage];
20869  }
20870  if (isset($this->footerlen[$frompage])) {
20871  $tmpfooterlen = $this->footerlen[$frompage];
20872  }
20873  if (isset($this->transfmrk[$frompage])) {
20874  $tmptransfmrk = $this->transfmrk[$frompage];
20875  }
20876  if (isset($this->PageAnnots[$frompage])) {
20877  $tmpannots = $this->PageAnnots[$frompage];
20878  }
20879  if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
20880  for ($i = $frompage; $i > $topage; --$i) {
20881  if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) {
20882  --$this->pagegroups[$this->newpagegroup[$i]];
20883  break;
20884  }
20885  }
20886  for ($i = $topage; $i > 0; --$i) {
20887  if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) {
20888  ++$this->pagegroups[$this->newpagegroup[$i]];
20889  break;
20890  }
20891  }
20892  }
20893  for ($i = $frompage; $i > $topage; --$i) {
20894  $j = $i - 1;
20895  // shift pages down
20896  $this->setPageBuffer($i, $this->getPageBuffer($j));
20897  $this->pagedim[$i] = $this->pagedim[$j];
20898  $this->pagelen[$i] = $this->pagelen[$j];
20899  $this->intmrk[$i] = $this->intmrk[$j];
20900  $this->bordermrk[$i] = $this->bordermrk[$j];
20901  $this->cntmrk[$i] = $this->cntmrk[$j];
20902  $this->pageobjects[$i] = $this->pageobjects[$j];
20903  if (isset($this->footerpos[$j])) {
20904  $this->footerpos[$i] = $this->footerpos[$j];
20905  } elseif (isset($this->footerpos[$i])) {
20906  unset($this->footerpos[$i]);
20907  }
20908  if (isset($this->footerlen[$j])) {
20909  $this->footerlen[$i] = $this->footerlen[$j];
20910  } elseif (isset($this->footerlen[$i])) {
20911  unset($this->footerlen[$i]);
20912  }
20913  if (isset($this->transfmrk[$j])) {
20914  $this->transfmrk[$i] = $this->transfmrk[$j];
20915  } elseif (isset($this->transfmrk[$i])) {
20916  unset($this->transfmrk[$i]);
20917  }
20918  if (isset($this->PageAnnots[$j])) {
20919  $this->PageAnnots[$i] = $this->PageAnnots[$j];
20920  } elseif (isset($this->PageAnnots[$i])) {
20921  unset($this->PageAnnots[$i]);
20922  }
20923  if (isset($this->newpagegroup[$j])) {
20924  $this->newpagegroup[$i] = $this->newpagegroup[$j];
20925  unset($this->newpagegroup[$j]);
20926  }
20927  if ($this->currpagegroup == $j) {
20928  $this->currpagegroup = $i;
20929  }
20930  }
20931  $this->setPageBuffer($topage, $tmppage);
20932  $this->pagedim[$topage] = $tmppagedim;
20933  $this->pagelen[$topage] = $tmppagelen;
20934  $this->intmrk[$topage] = $tmpintmrk;
20935  $this->bordermrk[$topage] = $tmpbordermrk;
20936  $this->cntmrk[$topage] = $tmpcntmrk;
20937  $this->pageobjects[$topage] = $tmppageobjects;
20938  if (isset($tmpfooterpos)) {
20939  $this->footerpos[$topage] = $tmpfooterpos;
20940  } elseif (isset($this->footerpos[$topage])) {
20941  unset($this->footerpos[$topage]);
20942  }
20943  if (isset($tmpfooterlen)) {
20944  $this->footerlen[$topage] = $tmpfooterlen;
20945  } elseif (isset($this->footerlen[$topage])) {
20946  unset($this->footerlen[$topage]);
20947  }
20948  if (isset($tmptransfmrk)) {
20949  $this->transfmrk[$topage] = $tmptransfmrk;
20950  } elseif (isset($this->transfmrk[$topage])) {
20951  unset($this->transfmrk[$topage]);
20952  }
20953  if (isset($tmpannots)) {
20954  $this->PageAnnots[$topage] = $tmpannots;
20955  } elseif (isset($this->PageAnnots[$topage])) {
20956  unset($this->PageAnnots[$topage]);
20957  }
20958  // adjust outlines
20959  $tmpoutlines = $this->outlines;
20960  foreach ($tmpoutlines as $key => $outline) {
20961  if (!$outline['f']) {
20962  if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
20963  $this->outlines[$key]['p'] = ($outline['p'] + 1);
20964  } elseif ($outline['p'] == $frompage) {
20965  $this->outlines[$key]['p'] = $topage;
20966  }
20967  }
20968  }
20969  // adjust dests
20970  $tmpdests = $this->dests;
20971  foreach ($tmpdests as $key => $dest) {
20972  if (!$dest['f']) {
20973  if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
20974  $this->dests[$key]['p'] = ($dest['p'] + 1);
20975  } elseif ($dest['p'] == $frompage) {
20976  $this->dests[$key]['p'] = $topage;
20977  }
20978  }
20979  }
20980  // adjust links
20981  $tmplinks = $this->links;
20982  foreach ($tmplinks as $key => $link) {
20983  if (!$link['f']) {
20984  if (($link['p'] >= $topage) AND ($link['p'] < $frompage)) {
20985  $this->links[$key]['p'] = ($link['p'] + 1);
20986  } elseif ($link['p'] == $frompage) {
20987  $this->links[$key]['p'] = $topage;
20988  }
20989  }
20990  }
20991  // adjust javascript
20992  $jfrompage = $frompage;
20993  $jtopage = $topage;
20994  if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) {
20995  foreach($pamatch[0] as $pk => $pmatch) {
20996  $pagenum = intval($pamatch[3][$pk]) + 1;
20997  if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
20998  $newpage = ($pagenum + 1);
20999  } elseif ($pagenum == $jfrompage) {
21000  $newpage = $jtopage;
21001  } else {
21002  $newpage = $pagenum;
21003  }
21004  --$newpage;
21005  $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21006  $this->javascript = str_replace($pmatch, $newjs, $this->javascript);
21007  }
21008  unset($pamatch);
21009  }
21010  // return to last page
21011  $this->lastPage(true);
21012  return true;
21013  }
21014 
21022  public function deletePage($page) {
21023  if (($page < 1) OR ($page > $this->numpages)) {
21024  return false;
21025  }
21026  // delete current page
21027  unset($this->pages[$page]);
21028  unset($this->pagedim[$page]);
21029  unset($this->pagelen[$page]);
21030  unset($this->intmrk[$page]);
21031  unset($this->bordermrk[$page]);
21032  unset($this->cntmrk[$page]);
21033  foreach ($this->pageobjects[$page] as $oid) {
21034  if (isset($this->offsets[$oid])){
21035  unset($this->offsets[$oid]);
21036  }
21037  }
21038  unset($this->pageobjects[$page]);
21039  if (isset($this->footerpos[$page])) {
21040  unset($this->footerpos[$page]);
21041  }
21042  if (isset($this->footerlen[$page])) {
21043  unset($this->footerlen[$page]);
21044  }
21045  if (isset($this->transfmrk[$page])) {
21046  unset($this->transfmrk[$page]);
21047  }
21048  if (isset($this->PageAnnots[$page])) {
21049  unset($this->PageAnnots[$page]);
21050  }
21051  if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
21052  for ($i = $page; $i > 0; --$i) {
21053  if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) {
21054  --$this->pagegroups[$this->newpagegroup[$i]];
21055  break;
21056  }
21057  }
21058  }
21059  if (isset($this->pageopen[$page])) {
21060  unset($this->pageopen[$page]);
21061  }
21062  if ($page < $this->numpages) {
21063  // update remaining pages
21064  for ($i = $page; $i < $this->numpages; ++$i) {
21065  $j = $i + 1;
21066  // shift pages
21067  $this->setPageBuffer($i, $this->getPageBuffer($j));
21068  $this->pagedim[$i] = $this->pagedim[$j];
21069  $this->pagelen[$i] = $this->pagelen[$j];
21070  $this->intmrk[$i] = $this->intmrk[$j];
21071  $this->bordermrk[$i] = $this->bordermrk[$j];
21072  $this->cntmrk[$i] = $this->cntmrk[$j];
21073  $this->pageobjects[$i] = $this->pageobjects[$j];
21074  if (isset($this->footerpos[$j])) {
21075  $this->footerpos[$i] = $this->footerpos[$j];
21076  } elseif (isset($this->footerpos[$i])) {
21077  unset($this->footerpos[$i]);
21078  }
21079  if (isset($this->footerlen[$j])) {
21080  $this->footerlen[$i] = $this->footerlen[$j];
21081  } elseif (isset($this->footerlen[$i])) {
21082  unset($this->footerlen[$i]);
21083  }
21084  if (isset($this->transfmrk[$j])) {
21085  $this->transfmrk[$i] = $this->transfmrk[$j];
21086  } elseif (isset($this->transfmrk[$i])) {
21087  unset($this->transfmrk[$i]);
21088  }
21089  if (isset($this->PageAnnots[$j])) {
21090  $this->PageAnnots[$i] = $this->PageAnnots[$j];
21091  } elseif (isset($this->PageAnnots[$i])) {
21092  unset($this->PageAnnots[$i]);
21093  }
21094  if (isset($this->newpagegroup[$j])) {
21095  $this->newpagegroup[$i] = $this->newpagegroup[$j];
21096  unset($this->newpagegroup[$j]);
21097  }
21098  if ($this->currpagegroup == $j) {
21099  $this->currpagegroup = $i;
21100  }
21101  if (isset($this->pageopen[$j])) {
21102  $this->pageopen[$i] = $this->pageopen[$j];
21103  } elseif (isset($this->pageopen[$i])) {
21104  unset($this->pageopen[$i]);
21105  }
21106  }
21107  // remove last page
21108  unset($this->pages[$this->numpages]);
21109  unset($this->pagedim[$this->numpages]);
21110  unset($this->pagelen[$this->numpages]);
21111  unset($this->intmrk[$this->numpages]);
21112  unset($this->bordermrk[$this->numpages]);
21113  unset($this->cntmrk[$this->numpages]);
21114  foreach ($this->pageobjects[$this->numpages] as $oid) {
21115  if (isset($this->offsets[$oid])){
21116  unset($this->offsets[$oid]);
21117  }
21118  }
21119  unset($this->pageobjects[$this->numpages]);
21120  if (isset($this->footerpos[$this->numpages])) {
21121  unset($this->footerpos[$this->numpages]);
21122  }
21123  if (isset($this->footerlen[$this->numpages])) {
21124  unset($this->footerlen[$this->numpages]);
21125  }
21126  if (isset($this->transfmrk[$this->numpages])) {
21127  unset($this->transfmrk[$this->numpages]);
21128  }
21129  if (isset($this->PageAnnots[$this->numpages])) {
21130  unset($this->PageAnnots[$this->numpages]);
21131  }
21132  if (isset($this->newpagegroup[$this->numpages])) {
21133  unset($this->newpagegroup[$this->numpages]);
21134  }
21135  if ($this->currpagegroup == $this->numpages) {
21136  $this->currpagegroup = ($this->numpages - 1);
21137  }
21138  if (isset($this->pagegroups[$this->numpages])) {
21139  unset($this->pagegroups[$this->numpages]);
21140  }
21141  if (isset($this->pageopen[$this->numpages])) {
21142  unset($this->pageopen[$this->numpages]);
21143  }
21144  }
21145  --$this->numpages;
21146  $this->page = $this->numpages;
21147  // adjust outlines
21148  $tmpoutlines = $this->outlines;
21149  foreach ($tmpoutlines as $key => $outline) {
21150  if (!$outline['f']) {
21151  if ($outline['p'] > $page) {
21152  $this->outlines[$key]['p'] = $outline['p'] - 1;
21153  } elseif ($outline['p'] == $page) {
21154  unset($this->outlines[$key]);
21155  }
21156  }
21157  }
21158  // adjust dests
21159  $tmpdests = $this->dests;
21160  foreach ($tmpdests as $key => $dest) {
21161  if (!$dest['f']) {
21162  if ($dest['p'] > $page) {
21163  $this->dests[$key]['p'] = $dest['p'] - 1;
21164  } elseif ($dest['p'] == $page) {
21165  unset($this->dests[$key]);
21166  }
21167  }
21168  }
21169  // adjust links
21170  $tmplinks = $this->links;
21171  foreach ($tmplinks as $key => $link) {
21172  if (!$link['f']) {
21173  if ($link['p'] > $page) {
21174  $this->links[$key]['p'] = $link['p'] - 1;
21175  } elseif ($link['p'] == $page) {
21176  unset($this->links[$key]);
21177  }
21178  }
21179  }
21180  // adjust javascript
21181  $jpage = $page;
21182  if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) {
21183  foreach($pamatch[0] as $pk => $pmatch) {
21184  $pagenum = intval($pamatch[3][$pk]) + 1;
21185  if ($pagenum >= $jpage) {
21186  $newpage = ($pagenum - 1);
21187  } elseif ($pagenum == $jpage) {
21188  $newpage = 1;
21189  } else {
21190  $newpage = $pagenum;
21191  }
21192  --$newpage;
21193  $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21194  $this->javascript = str_replace($pmatch, $newjs, $this->javascript);
21195  }
21196  unset($pamatch);
21197  }
21198  // return to last page
21199  if ($this->numpages > 0) {
21200  $this->lastPage(true);
21201  }
21202  return true;
21203  }
21204 
21212  public function copyPage($page=0) {
21213  if ($page == 0) {
21214  // default value
21215  $page = $this->page;
21216  }
21217  if (($page < 1) OR ($page > $this->numpages)) {
21218  return false;
21219  }
21220  // close the last page
21221  $this->endPage();
21222  // copy all page-related states
21223  ++$this->numpages;
21224  $this->page = $this->numpages;
21225  $this->setPageBuffer($this->page, $this->getPageBuffer($page));
21226  $this->pagedim[$this->page] = $this->pagedim[$page];
21227  $this->pagelen[$this->page] = $this->pagelen[$page];
21228  $this->intmrk[$this->page] = $this->intmrk[$page];
21229  $this->bordermrk[$this->page] = $this->bordermrk[$page];
21230  $this->cntmrk[$this->page] = $this->cntmrk[$page];
21231  $this->pageobjects[$this->page] = $this->pageobjects[$page];
21232  $this->pageopen[$this->page] = false;
21233  if (isset($this->footerpos[$page])) {
21234  $this->footerpos[$this->page] = $this->footerpos[$page];
21235  }
21236  if (isset($this->footerlen[$page])) {
21237  $this->footerlen[$this->page] = $this->footerlen[$page];
21238  }
21239  if (isset($this->transfmrk[$page])) {
21240  $this->transfmrk[$this->page] = $this->transfmrk[$page];
21241  }
21242  if (isset($this->PageAnnots[$page])) {
21243  $this->PageAnnots[$this->page] = $this->PageAnnots[$page];
21244  }
21245  if (isset($this->newpagegroup[$page])) {
21246  // start a new group
21247  $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1;
21248  $this->currpagegroup = $this->newpagegroup[$this->page];
21249  $this->pagegroups[$this->currpagegroup] = 1;
21250  } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
21251  ++$this->pagegroups[$this->currpagegroup];
21252  }
21253  // copy outlines
21254  $tmpoutlines = $this->outlines;
21255  foreach ($tmpoutlines as $key => $outline) {
21256  if ($outline['p'] == $page) {
21257  $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']);
21258  }
21259  }
21260  // copy links
21261  $tmplinks = $this->links;
21262  foreach ($tmplinks as $key => $link) {
21263  if ($link['p'] == $page) {
21264  $this->links[] = array('p' => $this->page, 'y' => $link['y'], 'f' => $link['f']);
21265  }
21266  }
21267  // return to last page
21268  $this->lastPage(true);
21269  return true;
21270  }
21271 
21289  public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
21290  $fontsize = $this->FontSizePt;
21291  $fontfamily = $this->FontFamily;
21292  $fontstyle = $this->FontStyle;
21293  $w = $this->w - $this->lMargin - $this->rMargin;
21294  $spacer = $this->GetStringWidth(chr(32)) * 4;
21295  $lmargin = $this->lMargin;
21296  $rmargin = $this->rMargin;
21297  $x_start = $this->GetX();
21298  $page_first = $this->page;
21299  $current_page = $this->page;
21300  $page_fill_start = false;
21301  $page_fill_end = false;
21303  if (TCPDF_STATIC::empty_string($numbersfont)) {
21304  $numbersfont = $this->default_monospaced_font;
21305  }
21306  if (TCPDF_STATIC::empty_string($filler)) {
21307  $filler = ' ';
21308  }
21310  $gap = ' ';
21311  } else {
21312  $gap = '';
21313  if ($page < 1) {
21314  $page = 1;
21315  }
21316  }
21317  $this->SetFont($numbersfont, $fontstyle, $fontsize);
21318  $numwidth = $this->GetStringWidth('00000');
21319  $maxpage = 0; //used for pages on attached documents
21320  foreach ($this->outlines as $key => $outline) {
21321  // check for extra pages (used for attachments)
21322  if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) {
21323  $outline['p'] += ($this->page - $page_first);
21324  }
21325  if ($this->rtl) {
21326  $aligntext = 'R';
21327  $alignnum = 'L';
21328  } else {
21329  $aligntext = 'L';
21330  $alignnum = 'R';
21331  }
21332  if ($outline['l'] == 0) {
21333  $this->SetFont($fontfamily, $outline['s'].'B', $fontsize);
21334  } else {
21335  $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']);
21336  }
21337  $this->SetTextColorArray($outline['c']);
21338  // check for page break
21339  $this->checkPageBreak(2 * $this->getCellHeight($this->FontSize));
21340  // set margins and X position
21341  if (($this->page == $current_page) AND ($this->current_column == $current_column)) {
21342  $this->lMargin = $lmargin;
21343  $this->rMargin = $rmargin;
21344  } else {
21345  if ($this->current_column != $current_column) {
21346  if ($this->rtl) {
21347  $x_start = $this->w - $this->columns[$this->current_column]['x'];
21348  } else {
21349  $x_start = $this->columns[$this->current_column]['x'];
21350  }
21351  }
21352  $lmargin = $this->lMargin;
21353  $rmargin = $this->rMargin;
21354  $current_page = $this->page;
21356  }
21357  $this->SetX($x_start);
21358  $indent = ($spacer * $outline['l']);
21359  if ($this->rtl) {
21360  $this->x -= $indent;
21361  $this->rMargin = $this->w - $this->x;
21362  } else {
21363  $this->x += $indent;
21364  $this->lMargin = $this->x;
21365  }
21366  $link = $this->AddLink();
21367  $this->SetLink($link, $outline['y'], $outline['p']);
21368  // write the text
21369  if ($this->rtl) {
21370  $txt = ' '.$outline['t'];
21371  } else {
21372  $txt = $outline['t'].' ';
21373  }
21374  $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
21375  if ($this->rtl) {
21376  $tw = $this->x - $this->lMargin;
21377  } else {
21378  $tw = $this->w - $this->rMargin - $this->x;
21379  }
21380  $this->SetFont($numbersfont, $fontstyle, $fontsize);
21382  $pagenum = $outline['p'];
21383  } else {
21384  // placemark to be replaced with the correct number
21385  $pagenum = '{#'.($outline['p']).'}';
21386  if ($this->isUnicodeFont()) {
21387  $pagenum = '{'.$pagenum.'}';
21388  }
21389  $maxpage = max($maxpage, $outline['p']);
21390  }
21391  $fw = ($tw - $this->GetStringWidth($pagenum.$filler));
21392  $wfiller = $this->GetStringWidth($filler);
21393  if ($wfiller > 0) {
21394  $numfills = floor($fw / $wfiller);
21395  } else {
21396  $numfills = 0;
21397  }
21398  if ($numfills > 0) {
21399  $rowfill = str_repeat($filler, $numfills);
21400  } else {
21401  $rowfill = '';
21402  }
21403  if ($this->rtl) {
21404  $pagenum = $pagenum.$gap.$rowfill;
21405  } else {
21406  $pagenum = $rowfill.$gap.$pagenum;
21407  }
21408  // write the number
21409  $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
21410  }
21411  $page_last = $this->getPage();
21412  $numpages = ($page_last - $page_first + 1);
21413  // account for booklet mode
21414  if ($this->booklet) {
21415  // check if a blank page is required before TOC
21416  $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21417  $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21418  if ($page_fill_start) {
21419  // add a page at the end (to be moved before TOC)
21420  $this->addPage();
21421  ++$page_last;
21422  ++$numpages;
21423  }
21424  if ($page_fill_end) {
21425  // add a page at the end
21426  $this->addPage();
21427  ++$page_last;
21428  ++$numpages;
21429  }
21430  }
21431  $maxpage = max($maxpage, $page_last);
21433  for ($p = $page_first; $p <= $page_last; ++$p) {
21434  // get page data
21435  $temppage = $this->getPageBuffer($p);
21436  for ($n = 1; $n <= $maxpage; ++$n) {
21437  // update page numbers
21438  $a = '{#'.$n.'}';
21439  // get page number aliases
21440  $pnalias = $this->getInternalPageNumberAliases($a);
21441  // calculate replacement number
21442  if (($n >= $page) AND ($n <= $this->numpages)) {
21443  $np = $n + $numpages;
21444  } else {
21445  $np = $n;
21446  }
21447  $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21448  $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21449  // replace aliases with numbers
21450  foreach ($pnalias['u'] as $u) {
21451  $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
21452  if ($this->rtl) {
21453  $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21454  } else {
21455  $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21456  }
21457  $temppage = str_replace($u, $nr, $temppage);
21458  }
21459  foreach ($pnalias['a'] as $a) {
21460  $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
21461  if ($this->rtl) {
21462  $nr = $na.' '.$sfill;
21463  } else {
21464  $nr = $sfill.' '.$na;
21465  }
21466  $temppage = str_replace($a, $nr, $temppage);
21467  }
21468  }
21469  // save changes
21470  $this->setPageBuffer($p, $temppage);
21471  }
21472  // move pages
21473  $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21474  if ($page_fill_start) {
21475  $this->movePage($page_last, $page_first);
21476  }
21477  for ($i = 0; $i < $numpages; ++$i) {
21478  $this->movePage($page_last, $page);
21479  }
21480  }
21481  }
21482 
21499  public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
21500  $filler = ' ';
21501  $prev_htmlLinkColorArray = $this->htmlLinkColorArray;
21502  $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle;
21503  // set new style for link
21504  $this->htmlLinkColorArray = array();
21505  $this->htmlLinkFontStyle = '';
21506  $page_first = $this->getPage();
21507  $page_fill_start = false;
21508  $page_fill_end = false;
21509  // get the font type used for numbers in each template
21510  $current_font = $this->FontFamily;
21511  foreach ($templates as $level => $html) {
21512  $dom = $this->getHtmlDomArray($html);
21513  foreach ($dom as $key => $value) {
21514  if ($value['value'] == '#TOC_PAGE_NUMBER#') {
21515  $this->SetFont($dom[($key - 1)]['fontname']);
21516  $templates['F'.$level] = $this->isUnicodeFont();
21517  }
21518  }
21519  }
21520  $this->SetFont($current_font);
21521  $maxpage = 0; //used for pages on attached documents
21522  foreach ($this->outlines as $key => $outline) {
21523  // get HTML template
21524  $row = $templates[$outline['l']];
21526  $pagenum = $outline['p'];
21527  } else {
21528  // placemark to be replaced with the correct number
21529  $pagenum = '{#'.($outline['p']).'}';
21530  if ($templates['F'.$outline['l']]) {
21531  $pagenum = '{'.$pagenum.'}';
21532  }
21533  $maxpage = max($maxpage, $outline['p']);
21534  }
21535  // replace templates with current values
21536  $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
21537  $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
21538  // add link to page
21539  $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
21540  // write bookmark entry
21541  $this->writeHTML($row, false, false, true, false, '');
21542  }
21543  // restore link styles
21544  $this->htmlLinkColorArray = $prev_htmlLinkColorArray;
21545  $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle;
21546  // move TOC page and replace numbers
21547  $page_last = $this->getPage();
21548  $numpages = ($page_last - $page_first + 1);
21549  // account for booklet mode
21550  if ($this->booklet) {
21551  // check if a blank page is required before TOC
21552  $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21553  $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21554  if ($page_fill_start) {
21555  // add a page at the end (to be moved before TOC)
21556  $this->addPage();
21557  ++$page_last;
21558  ++$numpages;
21559  }
21560  if ($page_fill_end) {
21561  // add a page at the end
21562  $this->addPage();
21563  ++$page_last;
21564  ++$numpages;
21565  }
21566  }
21567  $maxpage = max($maxpage, $page_last);
21569  for ($p = $page_first; $p <= $page_last; ++$p) {
21570  // get page data
21571  $temppage = $this->getPageBuffer($p);
21572  for ($n = 1; $n <= $maxpage; ++$n) {
21573  // update page numbers
21574  $a = '{#'.$n.'}';
21575  // get page number aliases
21576  $pnalias = $this->getInternalPageNumberAliases($a);
21577  // calculate replacement number
21578  if ($n >= $page) {
21579  $np = $n + $numpages;
21580  } else {
21581  $np = $n;
21582  }
21583  $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21584  $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21585  // replace aliases with numbers
21586  foreach ($pnalias['u'] as $u) {
21587  if ($correct_align) {
21588  $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
21589  if ($this->rtl) {
21590  $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21591  } else {
21592  $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21593  }
21594  } else {
21595  $nr = $nu;
21596  }
21597  $temppage = str_replace($u, $nr, $temppage);
21598  }
21599  foreach ($pnalias['a'] as $a) {
21600  if ($correct_align) {
21601  $sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
21602  if ($this->rtl) {
21603  $nr = $na.' '.$sfill;
21604  } else {
21605  $nr = $sfill.' '.$na;
21606  }
21607  } else {
21608  $nr = $na;
21609  }
21610  $temppage = str_replace($a, $nr, $temppage);
21611  }
21612  }
21613  // save changes
21614  $this->setPageBuffer($p, $temppage);
21615  }
21616  // move pages
21617  $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21618  if ($page_fill_start) {
21619  $this->movePage($page_last, $page_first);
21620  }
21621  for ($i = 0; $i < $numpages; ++$i) {
21622  $this->movePage($page_last, $page);
21623  }
21624  }
21625  }
21626 
21632  public function startTransaction() {
21633  if (isset($this->objcopy)) {
21634  // remove previous copy
21635  $this->commitTransaction();
21636  }
21637  // record current page number and Y position
21638  $this->start_transaction_page = $this->page;
21639  $this->start_transaction_y = $this->y;
21640  // clone current object
21641  $this->objcopy = TCPDF_STATIC::objclone($this);
21642  }
21643 
21649  public function commitTransaction() {
21650  if (isset($this->objcopy)) {
21651  $this->objcopy->_destroy(true, true);
21652  unset($this->objcopy);
21653  }
21654  }
21655 
21663  public function rollbackTransaction($self=false) {
21664  if (isset($this->objcopy)) {
21665  $this->_destroy(true, true);
21666  if ($self) {
21667  $objvars = get_object_vars($this->objcopy);
21668  foreach ($objvars as $key => $value) {
21669  $this->$key = $value;
21670  }
21671  }
21672  return $this->objcopy;
21673  }
21674  return $this;
21675  }
21676 
21677  // --- MULTI COLUMNS METHODS -----------------------
21678 
21687  public function setEqualColumns($numcols=0, $width=0, $y='') {
21688  $this->columns = array();
21689  if ($numcols < 2) {
21690  $numcols = 0;
21691  $this->columns = array();
21692  } else {
21693  // maximum column width
21694  $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
21695  if (($width == 0) OR ($width > $maxwidth)) {
21696  $width = $maxwidth;
21697  }
21699  $y = $this->y;
21700  }
21701  // space between columns
21702  $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
21703  // fill the columns array (with, space, starting Y position)
21704  for ($i = 0; $i < $numcols; ++$i) {
21705  $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
21706  }
21707  }
21708  $this->num_columns = $numcols;
21709  $this->current_column = 0;
21710  $this->column_start_page = $this->page;
21711  $this->selectColumn(0);
21712  }
21713 
21719  public function resetColumns() {
21720  $this->lMargin = $this->original_lMargin;
21721  $this->rMargin = $this->original_rMargin;
21722  $this->setEqualColumns();
21723  }
21724 
21732  public function setColumnsArray($columns) {
21733  $this->columns = $columns;
21734  $this->num_columns = count($columns);
21735  $this->current_column = 0;
21736  $this->column_start_page = $this->page;
21737  $this->selectColumn(0);
21738  }
21739 
21746  public function selectColumn($col='') {
21747  if (is_string($col)) {
21748  $col = $this->current_column;
21749  } elseif ($col >= $this->num_columns) {
21750  $col = 0;
21751  }
21752  $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
21753  $enable_thead = false;
21754  if ($this->num_columns > 1) {
21755  if ($col != $this->current_column) {
21756  // move Y pointer at the top of the column
21757  if ($this->column_start_page == $this->page) {
21758  $this->y = $this->columns[$col]['y'];
21759  } else {
21760  $this->y = $this->tMargin;
21761  }
21762  // Avoid to write table headers more than once
21763  if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) {
21764  $enable_thead = true;
21765  $this->maxselcol['page'] = $this->page;
21766  $this->maxselcol['column'] = $col;
21767  }
21768  }
21769  $xshift = $this->colxshift;
21770  // set X position of the current column by case
21771  $listindent = ($this->listindentlevel * $this->listindent);
21772  // calculate column X position
21773  $colpos = 0;
21774  for ($i = 0; $i < $col; ++$i) {
21775  $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']);
21776  }
21777  if ($this->rtl) {
21778  $x = $this->w - $this->original_rMargin - $colpos;
21779  $this->rMargin = ($this->w - $x + $listindent);
21780  $this->lMargin = ($x - $this->columns[$col]['w']);
21781  $this->x = $x - $listindent;
21782  } else {
21783  $x = $this->original_lMargin + $colpos;
21784  $this->lMargin = ($x + $listindent);
21785  $this->rMargin = ($this->w - $x - $this->columns[$col]['w']);
21786  $this->x = $x + $listindent;
21787  }
21788  $this->columns[$col]['x'] = $x;
21789  }
21790  $this->current_column = $col;
21791  // fix for HTML mode
21792  $this->newline = true;
21793  // print HTML table header (if any)
21794  if ((!TCPDF_STATIC::empty_string($this->thead)) AND (!$this->inthead)) {
21795  if ($enable_thead) {
21796  // print table header
21797  $this->writeHTML($this->thead, false, false, false, false, '');
21798  $this->y += $xshift['s']['V'];
21799  // store end of header position
21800  if (!isset($this->columns[$col]['th'])) {
21801  $this->columns[$col]['th'] = array();
21802  }
21803  $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y;
21804  $this->lasth = 0;
21805  } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) {
21806  $this->y = $this->columns[$col]['th']['\''.$this->page.'\''];
21807  }
21808  }
21809  // account for an html table cell over multiple columns
21810  if ($this->rtl) {
21811  $this->rMargin += $xshift['x'];
21812  $this->x -= ($xshift['x'] + $xshift['p']['R']);
21813  } else {
21814  $this->lMargin += $xshift['x'];
21815  $this->x += $xshift['x'] + $xshift['p']['L'];
21816  }
21817  }
21818 
21825  public function getColumn() {
21826  return $this->current_column;
21827  }
21828 
21835  public function getNumberOfColumns() {
21836  return $this->num_columns;
21837  }
21838 
21847  public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
21848  // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
21849  // convert text rendering parameters
21850  if ($stroke < 0) {
21851  $stroke = 0;
21852  }
21853  if ($fill === true) {
21854  if ($stroke > 0) {
21855  if ($clip === true) {
21856  // Fill, then stroke text and add to path for clipping
21857  $textrendermode = 6;
21858  } else {
21859  // Fill, then stroke text
21860  $textrendermode = 2;
21861  }
21862  $textstrokewidth = $stroke;
21863  } else {
21864  if ($clip === true) {
21865  // Fill text and add to path for clipping
21866  $textrendermode = 4;
21867  } else {
21868  // Fill text
21869  $textrendermode = 0;
21870  }
21871  }
21872  } else {
21873  if ($stroke > 0) {
21874  if ($clip === true) {
21875  // Stroke text and add to path for clipping
21876  $textrendermode = 5;
21877  } else {
21878  // Stroke text
21879  $textrendermode = 1;
21880  }
21881  $textstrokewidth = $stroke;
21882  } else {
21883  if ($clip === true) {
21884  // Add text to path for clipping
21885  $textrendermode = 7;
21886  } else {
21887  // Neither fill nor stroke text (invisible)
21888  $textrendermode = 3;
21889  }
21890  }
21891  }
21892  $this->textrendermode = $textrendermode;
21893  $this->textstrokewidth = $stroke;
21894  }
21895 
21902  public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) {
21903  if (isset($params['enabled'])) {
21904  $this->txtshadow['enabled'] = $params['enabled']?true:false;
21905  } else {
21906  $this->txtshadow['enabled'] = false;
21907  }
21908  if (isset($params['depth_w'])) {
21909  $this->txtshadow['depth_w'] = floatval($params['depth_w']);
21910  } else {
21911  $this->txtshadow['depth_w'] = 0;
21912  }
21913  if (isset($params['depth_h'])) {
21914  $this->txtshadow['depth_h'] = floatval($params['depth_h']);
21915  } else {
21916  $this->txtshadow['depth_h'] = 0;
21917  }
21918  if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) {
21919  $this->txtshadow['color'] = $params['color'];
21920  } else {
21921  $this->txtshadow['color'] = $this->strokecolor;
21922  }
21923  if (isset($params['opacity'])) {
21924  $this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity'])));
21925  } else {
21926  $this->txtshadow['opacity'] = 1;
21927  }
21928  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'))) {
21929  $this->txtshadow['blend_mode'] = $params['blend_mode'];
21930  } else {
21931  $this->txtshadow['blend_mode'] = 'Normal';
21932  }
21933  if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) {
21934  $this->txtshadow['enabled'] = false;
21935  }
21936  }
21937 
21944  public function getTextShadow() {
21945  return $this->txtshadow;
21946  }
21947 
21962  protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
21963  $hyphenword = array(); // hyphens positions
21964  $numchars = count($word);
21965  if ($numchars <= $charmin) {
21966  return $word;
21967  }
21968  $word_string = TCPDF_FONTS::UTF8ArrSubString($word, '', '', $this->isunicode);
21969  // some words will be returned as-is
21970  $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})(\]?)$/';
21971  if (preg_match($pattern, $word_string) > 0) {
21972  // email
21973  return $word;
21974  }
21975  $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})(\]?)$/';
21976  if (preg_match($pattern, $word_string) > 0) {
21977  // URL
21978  return $word;
21979  }
21980  if (isset($dictionary[$word_string])) {
21981  return TCPDF_FONTS::UTF8StringToArray($dictionary[$word_string], $this->isunicode, $this->CurrentFont);
21982  }
21983  // surround word with '_' characters
21984  $tmpword = array_merge(array(46), $word, array(46));
21985  $tmpnumchars = $numchars + 2;
21986  $maxpos = $tmpnumchars - 1;
21987  for ($pos = 0; $pos < $maxpos; ++$pos) {
21988  $imax = min(($tmpnumchars - $pos), $charmax);
21989  for ($i = 1; $i <= $imax; ++$i) {
21990  $subword = strtolower(TCPDF_FONTS::UTF8ArrSubString($tmpword, $pos, ($pos + $i), $this->isunicode));
21991  if (isset($patterns[$subword])) {
21992  $pattern = TCPDF_FONTS::UTF8StringToArray($patterns[$subword], $this->isunicode, $this->CurrentFont);
21993  $pattern_length = count($pattern);
21994  $digits = 1;
21995  for ($j = 0; $j < $pattern_length; ++$j) {
21996  // check if $pattern[$j] is a number = hyphenation level (only numbers from 1 to 5 are valid)
21997  if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
21998  if ($j == 0) {
21999  $zero = $pos - 1;
22000  } else {
22001  $zero = $pos + $j - $digits;
22002  }
22003  // get hyphenation level
22004  $level = ($pattern[$j] - 48);
22005  // if two levels from two different patterns match at the same point, the higher one is selected.
22006  if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] < $level)) {
22007  $hyphenword[$zero] = $level;
22008  }
22009  ++$digits;
22010  }
22011  }
22012  }
22013  }
22014  }
22015  $inserted = 0;
22016  $maxpos = $numchars - $rightmin;
22017  for ($i = $leftmin; $i <= $maxpos; ++$i) {
22018  // only odd levels indicate allowed hyphenation points
22019  if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
22020  // 173 = soft hyphen character
22021  array_splice($word, $i + $inserted, 0, 173);
22022  ++$inserted;
22023  }
22024  }
22025  return $word;
22026  }
22027 
22042  public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22043  $text = $this->unhtmlentities($text);
22044  $word = array(); // last word
22045  $txtarr = array(); // text to be returned
22046  $intag = false; // true if we are inside an HTML tag
22047  $skip = false; // true to skip hyphenation
22048  if (!is_array($patterns)) {
22049  $patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patterns);
22050  }
22051  // get array of characters
22052  $unichars = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
22053  // for each char
22054  foreach ($unichars as $char) {
22055  if ((!$intag) AND (!$skip) AND TCPDF_FONT_DATA::$uni_type[$char] == 'L') {
22056  // letter character
22057  $word[] = $char;
22058  } else {
22059  // other type of character
22060  if (!TCPDF_STATIC::empty_string($word)) {
22061  // hypenate the word
22062  $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22063  $word = array();
22064  }
22065  $txtarr[] = $char;
22066  if (chr($char) == '<') {
22067  // we are inside an HTML tag
22068  $intag = true;
22069  } elseif ($intag AND (chr($char) == '>')) {
22070  // end of HTML tag
22071  $intag = false;
22072  // check for style tag
22073  $expected = array(115, 116, 121, 108, 101); // = 'style'
22074  $current = array_slice($txtarr, -6, 5); // last 5 chars
22075  $compare = array_diff($expected, $current);
22076  if (empty($compare)) {
22077  // check if it is a closing tag
22078  $expected = array(47); // = '/'
22079  $current = array_slice($txtarr, -7, 1);
22080  $compare = array_diff($expected, $current);
22081  if (empty($compare)) {
22082  // closing style tag
22083  $skip = false;
22084  } else {
22085  // opening style tag
22086  $skip = true;
22087  }
22088  }
22089  }
22090  }
22091  }
22092  if (!TCPDF_STATIC::empty_string($word)) {
22093  // hypenate the word
22094  $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22095  }
22096  // convert char array to string and return
22097  return TCPDF_FONTS::UTF8ArrSubString($txtarr, '', '', $this->isunicode);
22098  }
22099 
22106  public function setRasterizeVectorImages($mode) {
22107  $this->rasterize_vector_images = $mode;
22108  }
22109 
22117  public function setFontSubsetting($enable=true) {
22118  if ($this->pdfa_mode) {
22119  $this->font_subsetting = false;
22120  } else {
22121  $this->font_subsetting = $enable ? true : false;
22122  }
22123  }
22124 
22132  public function getFontSubsetting() {
22133  return $this->font_subsetting;
22134  }
22135 
22145  public function stringLeftTrim($str, $replace='') {
22146  return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str);
22147  }
22148 
22158  public function stringRightTrim($str, $replace='') {
22159  return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str);
22160  }
22161 
22171  public function stringTrim($str, $replace='') {
22172  $str = $this->stringLeftTrim($str, $replace);
22173  $str = $this->stringRightTrim($str, $replace);
22174  return $str;
22175  }
22176 
22184  public function isUnicodeFont() {
22185  return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0'));
22186  }
22187 
22196  public function getFontFamilyName($fontfamily) {
22197  // remove spaces and symbols
22198  $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily));
22199  // extract all font names
22200  $fontslist = preg_split('/[,]/', $fontfamily);
22201  // find first valid font name
22202  foreach ($fontslist as $font) {
22203  // replace font variations
22204  $font = preg_replace('/regular$/', '', $font);
22205  $font = preg_replace('/italic$/', 'I', $font);
22206  $font = preg_replace('/oblique$/', 'I', $font);
22207  $font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
22208  // replace common family names and core fonts
22209  $pattern = array();
22210  $replacement = array();
22211  $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
22212  $replacement[] = 'times';
22213  $pattern[] = '/^sansserif/';
22214  $replacement[] = 'helvetica';
22215  $pattern[] = '/^monospace/';
22216  $replacement[] = 'courier';
22217  $font = preg_replace($pattern, $replacement, $font);
22218  if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) {
22219  return $font;
22220  }
22221  }
22222  // return current font as default
22223  return $this->CurrentFont['fontkey'];
22224  }
22225 
22240  public function startTemplate($w=0, $h=0, $group=false) {
22241  if ($this->inxobj) {
22242  // we are already inside an XObject template
22243  return false;
22244  }
22245  $this->inxobj = true;
22246  ++$this->n;
22247  // XObject ID
22248  $this->xobjid = 'XT'.$this->n;
22249  // object ID
22250  $this->xobjects[$this->xobjid] = array('n' => $this->n);
22251  // store current graphic state
22252  $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars();
22253  // initialize data
22254  $this->xobjects[$this->xobjid]['intmrk'] = 0;
22255  $this->xobjects[$this->xobjid]['transfmrk'] = array();
22256  $this->xobjects[$this->xobjid]['outdata'] = '';
22257  $this->xobjects[$this->xobjid]['xobjects'] = array();
22258  $this->xobjects[$this->xobjid]['images'] = array();
22259  $this->xobjects[$this->xobjid]['fonts'] = array();
22260  $this->xobjects[$this->xobjid]['annotations'] = array();
22261  $this->xobjects[$this->xobjid]['extgstates'] = array();
22262  $this->xobjects[$this->xobjid]['gradients'] = array();
22263  $this->xobjects[$this->xobjid]['spot_colors'] = array();
22264  // set new environment
22265  $this->num_columns = 1;
22266  $this->current_column = 0;
22267  $this->SetAutoPageBreak(false);
22268  if (($w === '') OR ($w <= 0)) {
22269  $w = $this->w - $this->lMargin - $this->rMargin;
22270  }
22271  if (($h === '') OR ($h <= 0)) {
22272  $h = $this->h - $this->tMargin - $this->bMargin;
22273  }
22274  $this->xobjects[$this->xobjid]['x'] = 0;
22275  $this->xobjects[$this->xobjid]['y'] = 0;
22276  $this->xobjects[$this->xobjid]['w'] = $w;
22277  $this->xobjects[$this->xobjid]['h'] = $h;
22278  $this->w = $w;
22279  $this->h = $h;
22280  $this->wPt = $this->w * $this->k;
22281  $this->hPt = $this->h * $this->k;
22282  $this->fwPt = $this->wPt;
22283  $this->fhPt = $this->hPt;
22284  $this->x = 0;
22285  $this->y = 0;
22286  $this->lMargin = 0;
22287  $this->rMargin = 0;
22288  $this->tMargin = 0;
22289  $this->bMargin = 0;
22290  // set group mode
22291  $this->xobjects[$this->xobjid]['group'] = $group;
22292  return $this->xobjid;
22293  }
22294 
22305  public function endTemplate() {
22306  if (!$this->inxobj) {
22307  // we are not inside a template
22308  return false;
22309  }
22310  $this->inxobj = false;
22311  // restore previous graphic state
22312  $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true);
22313  return $this->xobjid;
22314  }
22315 
22334  public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
22335  if ($this->state != 2) {
22336  return;
22337  }
22338  if (!isset($this->xobjects[$id])) {
22339  $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
22340  }
22341  if ($this->inxobj) {
22342  if ($id == $this->xobjid) {
22343  // close current template
22344  $this->endTemplate();
22345  } else {
22346  // use the template as resource for the template currently opened
22347  $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id];
22348  }
22349  }
22350  // set default values
22351  if ($x === '') {
22352  $x = $this->x;
22353  }
22354  if ($y === '') {
22355  $y = $this->y;
22356  }
22357  // check page for no-write regions and adapt page margins if necessary
22358  list($x, $y) = $this->checkPageRegions($h, $x, $y);
22359  $ow = $this->xobjects[$id]['w'];
22360  if ($ow <= 0) {
22361  $ow = 1;
22362  }
22363  $oh = $this->xobjects[$id]['h'];
22364  if ($oh <= 0) {
22365  $oh = 1;
22366  }
22367  // calculate template width and height on document
22368  if (($w <= 0) AND ($h <= 0)) {
22369  $w = $ow;
22370  $h = $oh;
22371  } elseif ($w <= 0) {
22372  $w = $h * $ow / $oh;
22373  } elseif ($h <= 0) {
22374  $h = $w * $oh / $ow;
22375  }
22376  // fit the template on available space
22377  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22378  // set page alignment
22379  $rb_y = $y + $h;
22380  // set alignment
22381  if ($this->rtl) {
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 - $w;
22390  }
22391  $rb_x = $xt;
22392  } else {
22393  if ($palign == 'L') {
22394  $xt = $this->lMargin;
22395  } elseif ($palign == 'C') {
22396  $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22397  } elseif ($palign == 'R') {
22398  $xt = $this->w - $this->rMargin - $w;
22399  } else {
22400  $xt = $x;
22401  }
22402  $rb_x = $xt + $w;
22403  }
22404  // print XObject Template + Transformation matrix
22405  $this->StartTransform();
22406  // translate and scale
22407  $sx = ($w / $ow);
22408  $sy = ($h / $oh);
22409  $tm = array();
22410  $tm[0] = $sx;
22411  $tm[1] = 0;
22412  $tm[2] = 0;
22413  $tm[3] = $sy;
22414  $tm[4] = $xt * $this->k;
22415  $tm[5] = ($this->h - $h - $y) * $this->k;
22416  $this->Transform($tm);
22417  // set object
22418  $this->_out('/'.$id.' Do');
22419  $this->StopTransform();
22420  // add annotations
22421  if (!empty($this->xobjects[$id]['annotations'])) {
22422  foreach ($this->xobjects[$id]['annotations'] as $annot) {
22423  // transform original coordinates
22424  $coordlt = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k)));
22425  $ax = ($coordlt[4] / $this->k);
22426  $ay = ($this->h - $h - ($coordlt[5] / $this->k));
22427  $coordrb = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k)));
22428  $aw = ($coordrb[4] / $this->k) - $ax;
22429  $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay;
22430  $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
22431  }
22432  }
22433  // set pointer to align the next text/objects
22434  switch($align) {
22435  case 'T': {
22436  $this->y = $y;
22437  $this->x = $rb_x;
22438  break;
22439  }
22440  case 'M': {
22441  $this->y = $y + round($h/2);
22442  $this->x = $rb_x;
22443  break;
22444  }
22445  case 'B': {
22446  $this->y = $rb_y;
22447  $this->x = $rb_x;
22448  break;
22449  }
22450  case 'N': {
22451  $this->SetY($rb_y);
22452  break;
22453  }
22454  default:{
22455  break;
22456  }
22457  }
22458  }
22459 
22467  public function setFontStretching($perc=100) {
22468  $this->font_stretching = $perc;
22469  }
22470 
22478  public function getFontStretching() {
22479  return $this->font_stretching;
22480  }
22481 
22489  public function setFontSpacing($spacing=0) {
22490  $this->font_spacing = $spacing;
22491  }
22492 
22500  public function getFontSpacing() {
22501  return $this->font_spacing;
22502  }
22503 
22512  public function getPageRegions() {
22513  return $this->page_regions;
22514  }
22515 
22527  public function setPageRegions($regions=array()) {
22528  // empty current regions array
22529  $this->page_regions = array();
22530  // add regions
22531  foreach ($regions as $data) {
22532  $this->addPageRegion($data);
22533  }
22534  }
22535 
22547  public function addPageRegion($region) {
22548  if (!isset($region['page']) OR empty($region['page'])) {
22549  $region['page'] = $this->page;
22550  }
22551  if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
22552  AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
22553  AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
22554  $this->page_regions[] = $region;
22555  }
22556  }
22557 
22566  public function removePageRegion($key) {
22567  if (isset($this->page_regions[$key])) {
22568  unset($this->page_regions[$key]);
22569  }
22570  }
22571 
22584  protected function checkPageRegions($h, $x, $y) {
22585  // set default values
22586  if ($x === '') {
22587  $x = $this->x;
22588  }
22589  if ($y === '') {
22590  $y = $this->y;
22591  }
22592  if (!$this->check_page_regions OR empty($this->page_regions)) {
22593  // no page regions defined
22594  return array($x, $y);
22595  }
22596  if (empty($h)) {
22597  $h = $this->getCellHeight($this->FontSize);
22598  }
22599  // check for page break
22600  if ($this->checkPageBreak($h, $y)) {
22601  // the content will be printed on a new page
22602  $x = $this->x;
22603  $y = $this->y;
22604  }
22605  if ($this->num_columns > 1) {
22606  if ($this->rtl) {
22607  $this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22608  } else {
22609  $this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22610  }
22611  } else {
22612  if ($this->rtl) {
22613  $this->lMargin = max($this->clMargin, $this->original_lMargin);
22614  } else {
22615  $this->rMargin = max($this->crMargin, $this->original_rMargin);
22616  }
22617  }
22618  // adjust coordinates and page margins
22619  foreach ($this->page_regions as $regid => $regdata) {
22620  if ($regdata['page'] == $this->page) {
22621  // check region boundaries
22622  if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
22623  // Y is inside the region
22624  $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
22625  $yt = max($y, $regdata['yt']);
22626  $yb = min(($yt + $h), $regdata['yb']);
22627  $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt'];
22628  $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt'];
22629  if ($regdata['side'] == 'L') { // left side
22630  $new_margin = max($xt, $xb);
22631  if ($this->lMargin < $new_margin) {
22632  if ($this->rtl) {
22633  // adjust left page margin
22634  $this->lMargin = max(0, $new_margin);
22635  }
22636  if ($x < $new_margin) {
22637  // adjust x position
22638  $x = $new_margin;
22639  if ($new_margin > ($this->w - $this->rMargin)) {
22640  // adjust y position
22641  $y = $regdata['yb'] - $h;
22642  }
22643  }
22644  }
22645  } elseif ($regdata['side'] == 'R') { // right side
22646  $new_margin = min($xt, $xb);
22647  if (($this->w - $this->rMargin) > $new_margin) {
22648  if (!$this->rtl) {
22649  // adjust right page margin
22650  $this->rMargin = max(0, ($this->w - $new_margin));
22651  }
22652  if ($x > $new_margin) {
22653  // adjust x position
22654  $x = $new_margin;
22655  if ($new_margin > $this->lMargin) {
22656  // adjust y position
22657  $y = $regdata['yb'] - $h;
22658  }
22659  }
22660  }
22661  }
22662  }
22663  }
22664  }
22665  return array($x, $y);
22666  }
22667 
22668  // --- SVG METHODS ---------------------------------------------------------
22669 
22687  public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
22688  if ($this->state != 2) {
22689  return;
22690  }
22691  // reset SVG vars
22692  $this->svggradients = array();
22693  $this->svggradientid = 0;
22694  $this->svgdefsmode = false;
22695  $this->svgdefs = array();
22696  $this->svgclipmode = false;
22697  $this->svgclippaths = array();
22698  $this->svgcliptm = array();
22699  $this->svgclipid = 0;
22700  $this->svgtext = '';
22701  $this->svgtextmode = array();
22702  if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
22703  // convert SVG to raster image using GD or ImageMagick libraries
22704  return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22705  }
22706  if ($file[0] === '@') { // image from string
22707  $this->svgdir = '';
22708  $svgdata = substr($file, 1);
22709  } else { // SVG file
22710  $this->svgdir = dirname($file);
22712  }
22713  if ($svgdata === FALSE) {
22714  $this->Error('SVG file not found: '.$file);
22715  }
22716  if ($x === '') {
22717  $x = $this->x;
22718  }
22719  if ($y === '') {
22720  $y = $this->y;
22721  }
22722  // check page for no-write regions and adapt page margins if necessary
22723  list($x, $y) = $this->checkPageRegions($h, $x, $y);
22724  $k = $this->k;
22725  $ox = 0;
22726  $oy = 0;
22727  $ow = $w;
22728  $oh = $h;
22729  $aspect_ratio_align = 'xMidYMid';
22730  $aspect_ratio_ms = 'meet';
22731  $regs = array();
22732  // get original image width and height
22733  preg_match('/<svg([^>]*)>/si', $svgdata, $regs);
22734  if (isset($regs[1]) AND !empty($regs[1])) {
22735  $tmp = array();
22736  if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22737  $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22738  }
22739  $tmp = array();
22740  if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22741  $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22742  }
22743  $tmp = array();
22744  if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22745  $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22746  }
22747  $tmp = array();
22748  if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22749  $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22750  }
22751  $tmp = array();
22752  $view_box = array();
22753  if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
22754  if (count($tmp) == 5) {
22755  array_shift($tmp);
22756  foreach ($tmp as $key => $val) {
22757  $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
22758  }
22759  $ox = $view_box[0];
22760  $oy = $view_box[1];
22761  }
22762  // get aspect ratio
22763  $tmp = array();
22764  if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22765  $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
22766  switch (count($aspect_ratio)) {
22767  case 3: {
22768  $aspect_ratio_align = $aspect_ratio[1];
22769  $aspect_ratio_ms = $aspect_ratio[2];
22770  break;
22771  }
22772  case 2: {
22773  $aspect_ratio_align = $aspect_ratio[0];
22774  $aspect_ratio_ms = $aspect_ratio[1];
22775  break;
22776  }
22777  case 1: {
22778  $aspect_ratio_align = $aspect_ratio[0];
22779  $aspect_ratio_ms = 'meet';
22780  break;
22781  }
22782  }
22783  }
22784  }
22785  }
22786  if ($ow <= 0) {
22787  $ow = 1;
22788  }
22789  if ($oh <= 0) {
22790  $oh = 1;
22791  }
22792  // calculate image width and height on document
22793  if (($w <= 0) AND ($h <= 0)) {
22794  // convert image size to document unit
22795  $w = $ow;
22796  $h = $oh;
22797  } elseif ($w <= 0) {
22798  $w = $h * $ow / $oh;
22799  } elseif ($h <= 0) {
22800  $h = $w * $oh / $ow;
22801  }
22802  // fit the image on available space
22803  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22804  if ($this->rasterize_vector_images) {
22805  // convert SVG to raster image using GD or ImageMagick libraries
22806  return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22807  }
22808  // set alignment
22809  $this->img_rb_y = $y + $h;
22810  // set alignment
22811  if ($this->rtl) {
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 - $w;
22820  }
22821  $this->img_rb_x = $ximg;
22822  } else {
22823  if ($palign == 'L') {
22824  $ximg = $this->lMargin;
22825  } elseif ($palign == 'C') {
22826  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22827  } elseif ($palign == 'R') {
22828  $ximg = $this->w - $this->rMargin - $w;
22829  } else {
22830  $ximg = $x;
22831  }
22832  $this->img_rb_x = $ximg + $w;
22833  }
22834  // store current graphic vars
22835  $gvars = $this->getGraphicVars();
22836  // store SVG position and scale factors
22837  $svgoffset_x = ($ximg - $ox) * $this->k;
22838  $svgoffset_y = -($y - $oy) * $this->k;
22839  if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
22840  $ow = $view_box[2];
22841  $oh = $view_box[3];
22842  } else {
22843  if ($ow <= 0) {
22844  $ow = $w;
22845  }
22846  if ($oh <= 0) {
22847  $oh = $h;
22848  }
22849  }
22850  $svgscale_x = $w / $ow;
22851  $svgscale_y = $h / $oh;
22852  // scaling and alignment
22853  if ($aspect_ratio_align != 'none') {
22854  // store current scaling values
22855  $svgscale_old_x = $svgscale_x;
22856  $svgscale_old_y = $svgscale_y;
22857  // force uniform scaling
22858  if ($aspect_ratio_ms == 'slice') {
22859  // the entire viewport is covered by the viewBox
22860  if ($svgscale_x > $svgscale_y) {
22861  $svgscale_y = $svgscale_x;
22862  } elseif ($svgscale_x < $svgscale_y) {
22863  $svgscale_x = $svgscale_y;
22864  }
22865  } else { // meet
22866  // the entire viewBox is visible within the viewport
22867  if ($svgscale_x < $svgscale_y) {
22868  $svgscale_y = $svgscale_x;
22869  } elseif ($svgscale_x > $svgscale_y) {
22870  $svgscale_x = $svgscale_y;
22871  }
22872  }
22873  // correct X alignment
22874  switch (substr($aspect_ratio_align, 1, 3)) {
22875  case 'Min': {
22876  // do nothing
22877  break;
22878  }
22879  case 'Max': {
22880  $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
22881  break;
22882  }
22883  default:
22884  case 'Mid': {
22885  $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
22886  break;
22887  }
22888  }
22889  // correct Y alignment
22890  switch (substr($aspect_ratio_align, 5)) {
22891  case 'Min': {
22892  // do nothing
22893  break;
22894  }
22895  case 'Max': {
22896  $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
22897  break;
22898  }
22899  default:
22900  case 'Mid': {
22901  $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
22902  break;
22903  }
22904  }
22905  }
22906  // store current page break mode
22907  $page_break_mode = $this->AutoPageBreak;
22908  $page_break_margin = $this->getBreakMargin();
22910  $this->SetCellPadding(0);
22911  $this->SetAutoPageBreak(false);
22912  // save the current graphic state
22913  $this->_out('q'.$this->epsmarker);
22914  // set initial clipping mask
22915  $this->Rect($ximg, $y, $w, $h, 'CNZ', array(), array());
22916  // scale and translate
22917  $e = $ox * $this->k * (1 - $svgscale_x);
22918  $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
22919  $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y)));
22920  // creates a new XML parser to be used by the other XML functions
22921  $this->parser = xml_parser_create('UTF-8');
22922  // the following function allows to use parser inside object
22923  xml_set_object($this->parser, $this);
22924  // disable case-folding for this XML parser
22925  xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
22926  // sets the element handler functions for the XML parser
22927  xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler');
22928  // sets the character data handler function for the XML parser
22929  xml_set_character_data_handler($this->parser, 'segSVGContentHandler');
22930  // start parsing an XML document
22931  if (!xml_parse($this->parser, $svgdata)) {
22932  $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));
22933  $this->Error($error_message);
22934  }
22935  // free this XML parser
22936  xml_parser_free($this->parser);
22937  // restore previous graphic state
22938  $this->_out($this->epsmarker.'Q');
22939  // restore graphic vars
22940  $this->setGraphicVars($gvars);
22941  $this->lasth = $gvars['lasth'];
22942  if (!empty($border)) {
22943  $bx = $this->x;
22944  $by = $this->y;
22945  $this->x = $ximg;
22946  if ($this->rtl) {
22947  $this->x += $w;
22948  }
22949  $this->y = $y;
22950  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
22951  $this->x = $bx;
22952  $this->y = $by;
22953  }
22954  if ($link) {
22955  $this->Link($ximg, $y, $w, $h, $link, 0);
22956  }
22957  // set pointer to align the next text/objects
22958  switch($align) {
22959  case 'T':{
22960  $this->y = $y;
22961  $this->x = $this->img_rb_x;
22962  break;
22963  }
22964  case 'M':{
22965  $this->y = $y + round($h/2);
22966  $this->x = $this->img_rb_x;
22967  break;
22968  }
22969  case 'B':{
22970  $this->y = $this->img_rb_y;
22971  $this->x = $this->img_rb_x;
22972  break;
22973  }
22974  case 'N':{
22975  $this->SetY($this->img_rb_y);
22976  break;
22977  }
22978  default:{
22979  // restore pointer to starting position
22980  $this->x = $gvars['x'];
22981  $this->y = $gvars['y'];
22982  $this->page = $gvars['page'];
22983  $this->current_column = $gvars['current_column'];
22984  $this->tMargin = $gvars['tMargin'];
22985  $this->bMargin = $gvars['bMargin'];
22986  $this->w = $gvars['w'];
22987  $this->h = $gvars['h'];
22988  $this->wPt = $gvars['wPt'];
22989  $this->hPt = $gvars['hPt'];
22990  $this->fwPt = $gvars['fwPt'];
22991  $this->fhPt = $gvars['fhPt'];
22992  break;
22993  }
22994  }
22995  $this->endlinex = $this->img_rb_x;
22996  // restore page break
22997  $this->SetAutoPageBreak($page_break_mode, $page_break_margin);
22998  $this->cell_padding = $cell_padding;
22999  }
23000 
23008  protected function convertSVGtMatrix($tm) {
23009  $a = $tm[0];
23010  $b = -$tm[1];
23011  $c = -$tm[2];
23012  $d = $tm[3];
23013  $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
23014  $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
23015  $x = 0;
23016  $y = $this->h * $this->k;
23017  $e = ($x * (1 - $a)) - ($y * $c) + $e;
23018  $f = ($y * (1 - $d)) - ($x * $b) + $f;
23019  return array($a, $b, $c, $d, $e, $f);
23020  }
23021 
23028  protected function SVGTransform($tm) {
23029  $this->Transform($this->convertSVGtMatrix($tm));
23030  }
23031 
23047  protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
23048  if ($this->state != 2) {
23049  return;
23050  }
23051  $objstyle = '';
23052  $minlen = (0.01 / $this->k); // minimum acceptable length
23053  if (!isset($svgstyle['opacity'])) {
23054  return $objstyle;
23055  }
23056  // clip-path
23057  $regs = array();
23058  if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
23059  $clip_path = $this->svgclippaths[$regs[1]];
23060  foreach ($clip_path as $cp) {
23061  $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
23062  }
23063  }
23064  // opacity
23065  if ($svgstyle['opacity'] != 1) {
23066  $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false);
23067  }
23068  // color
23069  $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors);
23070  $this->SetFillColorArray($fill_color);
23071  // text color
23072  $text_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors);
23073  $this->SetTextColorArray($text_color);
23074  // clip
23075  if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
23076  $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
23077  $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
23078  $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
23079  $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
23080  $cx = $x + $left;
23081  $cy = $y + $top;
23082  $cw = $w - $left - $right;
23083  $ch = $h - $top - $bottom;
23084  if ($svgstyle['clip-rule'] == 'evenodd') {
23085  $clip_rule = 'CNZ';
23086  } else {
23087  $clip_rule = 'CEO';
23088  }
23089  $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
23090  }
23091  // fill
23092  $regs = array();
23093  if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
23094  // gradient
23095  $gradient = $this->svggradients[$regs[1]];
23096  if (isset($gradient['xref'])) {
23097  // reference to another gradient definition
23098  $newgradient = $this->svggradients[$gradient['xref']];
23099  $newgradient['coords'] = $gradient['coords'];
23100  $newgradient['mode'] = $gradient['mode'];
23101  $newgradient['type'] = $gradient['type'];
23102  $newgradient['gradientUnits'] = $gradient['gradientUnits'];
23103  if (isset($gradient['gradientTransform'])) {
23104  $newgradient['gradientTransform'] = $gradient['gradientTransform'];
23105  }
23106  $gradient = $newgradient;
23107  }
23108  //save current Graphic State
23109  $this->_outSaveGraphicsState();
23110  //set clipping area
23111  if (!empty($clip_function) AND method_exists($this, $clip_function)) {
23112  $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
23113  if ((!isset($gradient['type']) OR ($gradient['type'] != 3)) AND is_array($bbox) AND (count($bbox) == 4)) {
23114  list($x, $y, $w, $h) = $bbox;
23115  }
23116  }
23117  if ($gradient['mode'] == 'measure') {
23118  if (!isset($gradient['coords'][4])) {
23119  $gradient['coords'][4] = 0.5;
23120  }
23121  if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
23122  $gtm = $gradient['gradientTransform'];
23123  // apply transformation matrix
23124  $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
23125  $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
23126  $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
23127  $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
23128  $r = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
23129  $gradient['coords'][0] = $xa;
23130  $gradient['coords'][1] = $ya;
23131  $gradient['coords'][2] = $xb;
23132  $gradient['coords'][3] = $yb;
23133  $gradient['coords'][4] = $r;
23134  }
23135  // convert SVG coordinates to user units
23136  $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
23137  $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
23138  $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
23139  $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
23140  $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
23141  if ($w <= $minlen) {
23142  $w = $minlen;
23143  }
23144  if ($h <= $minlen) {
23145  $h = $minlen;
23146  }
23147  // shift units
23148  if ($gradient['gradientUnits'] == 'objectBoundingBox') {
23149  // convert to SVG coordinate system
23150  $gradient['coords'][0] += $x;
23151  $gradient['coords'][1] += $y;
23152  $gradient['coords'][2] += $x;
23153  $gradient['coords'][3] += $y;
23154  }
23155  // calculate percentages
23156  $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w);
23157  $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h);
23158  $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w);
23159  $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h);
23160  $gradient['coords'][4] /= $w;
23161  } elseif ($gradient['mode'] == 'percentage') {
23162  foreach($gradient['coords'] as $key => $val) {
23163  $gradient['coords'][$key] = (intval($val) / 100);
23164  if ($val < 0) {
23165  $gradient['coords'][$key] = 0;
23166  } elseif ($val > 1) {
23167  $gradient['coords'][$key] = 1;
23168  }
23169  }
23170  }
23171  if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
23172  // single color (no shading)
23173  $gradient['coords'][0] = 1;
23174  $gradient['coords'][1] = 0;
23175  $gradient['coords'][2] = 0.999;
23176  $gradient['coords'][3] = 0;
23177  }
23178  // swap Y coordinates
23179  $tmp = $gradient['coords'][1];
23180  $gradient['coords'][1] = $gradient['coords'][3];
23181  $gradient['coords'][3] = $tmp;
23182  // set transformation map for gradient
23183  $cy = ($this->h - $y);
23184  if ($gradient['type'] == 3) {
23185  // circular gradient
23186  $cy -= ($gradient['coords'][1] * ($w + $h));
23187  $h = $w = max($w, $h);
23188  } else {
23189  $cy -= $h;
23190  }
23191  $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), ($cy * $this->k)));
23192  if (count($gradient['stops']) > 1) {
23193  $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
23194  }
23195  } elseif ($svgstyle['fill'] != 'none') {
23196  $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors);
23197  if ($svgstyle['fill-opacity'] != 1) {
23198  $this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false);
23199  }
23200  $this->SetFillColorArray($fill_color);
23201  if ($svgstyle['fill-rule'] == 'evenodd') {
23202  $objstyle .= 'F*';
23203  } else {
23204  $objstyle .= 'F';
23205  }
23206  }
23207  // stroke
23208  if ($svgstyle['stroke'] != 'none') {
23209  if ($svgstyle['stroke-opacity'] != 1) {
23210  $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false);
23211  } elseif (preg_match('/rgba\(\d+%?,\s*\d+%?,\s*\d+%?,\s*(\d+(?:\.\d+)?)\)/i', $svgstyle['stroke'], $rgba_matches)) {
23212  $this->setAlpha($rgba_matches[1], 'Normal', $this->alpha['ca'], false);
23213  }
23214  $stroke_style = array(
23215  'color' => TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors),
23216  'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
23217  'cap' => $svgstyle['stroke-linecap'],
23218  'join' => $svgstyle['stroke-linejoin']
23219  );
23220  if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
23221  $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
23222  }
23223  $this->SetLineStyle($stroke_style);
23224  $objstyle .= 'D';
23225  }
23226  // font
23227  $regs = array();
23228  if (!empty($svgstyle['font'])) {
23229  if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
23230  $font_family = $this->getFontFamilyName($regs[1]);
23231  } else {
23232  $font_family = $svgstyle['font-family'];
23233  }
23234  if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23235  $font_size = trim($regs[1]);
23236  } else {
23237  $font_size = $svgstyle['font-size'];
23238  }
23239  if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23240  $font_style = trim($regs[1]);
23241  } else {
23242  $font_style = $svgstyle['font-style'];
23243  }
23244  if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23245  $font_weight = trim($regs[1]);
23246  } else {
23247  $font_weight = $svgstyle['font-weight'];
23248  }
23249  if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23250  $font_stretch = trim($regs[1]);
23251  } else {
23252  $font_stretch = $svgstyle['font-stretch'];
23253  }
23254  if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23255  $font_spacing = trim($regs[1]);
23256  } else {
23257  $font_spacing = $svgstyle['letter-spacing'];
23258  }
23259  } else {
23260  $font_family = $this->getFontFamilyName($svgstyle['font-family']);
23261  $font_size = $svgstyle['font-size'];
23262  $font_style = $svgstyle['font-style'];
23263  $font_weight = $svgstyle['font-weight'];
23264  $font_stretch = $svgstyle['font-stretch'];
23265  $font_spacing = $svgstyle['letter-spacing'];
23266  }
23267  $font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit);
23268  $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
23269  $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
23270  switch ($font_style) {
23271  case 'italic': {
23272  $font_style = 'I';
23273  break;
23274  }
23275  case 'oblique': {
23276  $font_style = 'I';
23277  break;
23278  }
23279  default:
23280  case 'normal': {
23281  $font_style = '';
23282  break;
23283  }
23284  }
23285  switch ($font_weight) {
23286  case 'bold':
23287  case 'bolder': {
23288  $font_style .= 'B';
23289  break;
23290  }
23291  case 'normal': {
23292  if ((substr($font_family, -1) == 'I') AND (substr($font_family, -2, 1) == 'B')) {
23293  $font_family = substr($font_family, 0, -2).'I';
23294  } elseif (substr($font_family, -1) == 'B') {
23295  $font_family = substr($font_family, 0, -1);
23296  }
23297  break;
23298  }
23299  }
23300  switch ($svgstyle['text-decoration']) {
23301  case 'underline': {
23302  $font_style .= 'U';
23303  break;
23304  }
23305  case 'overline': {
23306  $font_style .= 'O';
23307  break;
23308  }
23309  case 'line-through': {
23310  $font_style .= 'D';
23311  break;
23312  }
23313  default:
23314  case 'none': {
23315  break;
23316  }
23317  }
23318  $this->SetFont($font_family, $font_style, $font_size);
23319  $this->setFontStretching($font_stretch);
23320  $this->setFontSpacing($font_spacing);
23321  return $objstyle;
23322  }
23323 
23342  protected function SVGPath($d, $style='') {
23343  if ($this->state != 2) {
23344  return;
23345  }
23346  // set fill/stroke style
23348  if (empty($op)) {
23349  return;
23350  }
23351  $paths = array();
23352  $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
23353  preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER);
23354  $x = 0;
23355  $y = 0;
23356  $x1 = 0;
23357  $y1 = 0;
23358  $x2 = 0;
23359  $y2 = 0;
23360  $xmin = 2147483647;
23361  $xmax = 0;
23362  $ymin = 2147483647;
23363  $ymax = 0;
23364  $relcoord = false;
23365  $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
23366  $firstcmd = true; // used to print first point
23367  // draw curve pieces
23368  foreach ($paths as $key => $val) {
23369  // get curve type
23370  $cmd = trim($val[1]);
23371  if (strtolower($cmd) == $cmd) {
23372  // use relative coordinated instead of absolute
23373  $relcoord = true;
23374  $xoffset = $x;
23375  $yoffset = $y;
23376  } else {
23377  $relcoord = false;
23378  $xoffset = 0;
23379  $yoffset = 0;
23380  }
23381  $params = array();
23382  if (isset($val[2])) {
23383  // get curve parameters
23384  $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
23385  $params = array();
23386  foreach ($rawparams as $ck => $cp) {
23387  $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
23388  if (abs($params[$ck]) < $minlen) {
23389  // approximate little values to zero
23390  $params[$ck] = 0;
23391  }
23392  }
23393  }
23394  // store current origin point
23395  $x0 = $x;
23396  $y0 = $y;
23397  switch (strtoupper($cmd)) {
23398  case 'M': { // moveto
23399  foreach ($params as $ck => $cp) {
23400  if (($ck % 2) == 0) {
23401  $x = $cp + $xoffset;
23402  } else {
23403  $y = $cp + $yoffset;
23404  if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23405  if ($ck == 1) {
23406  $this->_outPoint($x, $y);
23407  $firstcmd = false;
23408  } else {
23409  $this->_outLine($x, $y);
23410  }
23411  $x0 = $x;
23412  $y0 = $y;
23413  }
23414  $xmin = min($xmin, $x);
23415  $ymin = min($ymin, $y);
23416  $xmax = max($xmax, $x);
23417  $ymax = max($ymax, $y);
23418  if ($relcoord) {
23419  $xoffset = $x;
23420  $yoffset = $y;
23421  }
23422  }
23423  }
23424  break;
23425  }
23426  case 'L': { // lineto
23427  foreach ($params as $ck => $cp) {
23428  if (($ck % 2) == 0) {
23429  $x = $cp + $xoffset;
23430  } else {
23431  $y = $cp + $yoffset;
23432  if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23433  $this->_outLine($x, $y);
23434  $x0 = $x;
23435  $y0 = $y;
23436  }
23437  $xmin = min($xmin, $x);
23438  $ymin = min($ymin, $y);
23439  $xmax = max($xmax, $x);
23440  $ymax = max($ymax, $y);
23441  if ($relcoord) {
23442  $xoffset = $x;
23443  $yoffset = $y;
23444  }
23445  }
23446  }
23447  break;
23448  }
23449  case 'H': { // horizontal lineto
23450  foreach ($params as $ck => $cp) {
23451  $x = $cp + $xoffset;
23452  if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23453  $this->_outLine($x, $y);
23454  $x0 = $x;
23455  $y0 = $y;
23456  }
23457  $xmin = min($xmin, $x);
23458  $xmax = max($xmax, $x);
23459  if ($relcoord) {
23460  $xoffset = $x;
23461  }
23462  }
23463  break;
23464  }
23465  case 'V': { // vertical lineto
23466  foreach ($params as $ck => $cp) {
23467  $y = $cp + $yoffset;
23468  if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23469  $this->_outLine($x, $y);
23470  $x0 = $x;
23471  $y0 = $y;
23472  }
23473  $ymin = min($ymin, $y);
23474  $ymax = max($ymax, $y);
23475  if ($relcoord) {
23476  $yoffset = $y;
23477  }
23478  }
23479  break;
23480  }
23481  case 'C': { // curveto
23482  foreach ($params as $ck => $cp) {
23483  $params[$ck] = $cp;
23484  if ((($ck + 1) % 6) == 0) {
23485  $x1 = $params[($ck - 5)] + $xoffset;
23486  $y1 = $params[($ck - 4)] + $yoffset;
23487  $x2 = $params[($ck - 3)] + $xoffset;
23488  $y2 = $params[($ck - 2)] + $yoffset;
23489  $x = $params[($ck - 1)] + $xoffset;
23490  $y = $params[($ck)] + $yoffset;
23491  $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23492  $xmin = min($xmin, $x, $x1, $x2);
23493  $ymin = min($ymin, $y, $y1, $y2);
23494  $xmax = max($xmax, $x, $x1, $x2);
23495  $ymax = max($ymax, $y, $y1, $y2);
23496  if ($relcoord) {
23497  $xoffset = $x;
23498  $yoffset = $y;
23499  }
23500  }
23501  }
23502  break;
23503  }
23504  case 'S': { // shorthand/smooth curveto
23505  foreach ($params as $ck => $cp) {
23506  $params[$ck] = $cp;
23507  if ((($ck + 1) % 4) == 0) {
23508  if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
23509  $x1 = (2 * $x) - $x2;
23510  $y1 = (2 * $y) - $y2;
23511  } else {
23512  $x1 = $x;
23513  $y1 = $y;
23514  }
23515  $x2 = $params[($ck - 3)] + $xoffset;
23516  $y2 = $params[($ck - 2)] + $yoffset;
23517  $x = $params[($ck - 1)] + $xoffset;
23518  $y = $params[($ck)] + $yoffset;
23519  $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23520  $xmin = min($xmin, $x, $x1, $x2);
23521  $ymin = min($ymin, $y, $y1, $y2);
23522  $xmax = max($xmax, $x, $x1, $x2);
23523  $ymax = max($ymax, $y, $y1, $y2);
23524  if ($relcoord) {
23525  $xoffset = $x;
23526  $yoffset = $y;
23527  }
23528  }
23529  }
23530  break;
23531  }
23532  case 'Q': { // quadratic Bezier curveto
23533  foreach ($params as $ck => $cp) {
23534  $params[$ck] = $cp;
23535  if ((($ck + 1) % 4) == 0) {
23536  // convert quadratic points to cubic points
23537  $x1 = $params[($ck - 3)] + $xoffset;
23538  $y1 = $params[($ck - 2)] + $yoffset;
23539  $xa = ($x + (2 * $x1)) / 3;
23540  $ya = ($y + (2 * $y1)) / 3;
23541  $x = $params[($ck - 1)] + $xoffset;
23542  $y = $params[($ck)] + $yoffset;
23543  $xb = ($x + (2 * $x1)) / 3;
23544  $yb = ($y + (2 * $y1)) / 3;
23545  $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23546  $xmin = min($xmin, $x, $xa, $xb);
23547  $ymin = min($ymin, $y, $ya, $yb);
23548  $xmax = max($xmax, $x, $xa, $xb);
23549  $ymax = max($ymax, $y, $ya, $yb);
23550  if ($relcoord) {
23551  $xoffset = $x;
23552  $yoffset = $y;
23553  }
23554  }
23555  }
23556  break;
23557  }
23558  case 'T': { // shorthand/smooth quadratic Bezier curveto
23559  foreach ($params as $ck => $cp) {
23560  $params[$ck] = $cp;
23561  if (($ck % 2) != 0) {
23562  if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
23563  $x1 = (2 * $x) - $x1;
23564  $y1 = (2 * $y) - $y1;
23565  } else {
23566  $x1 = $x;
23567  $y1 = $y;
23568  }
23569  // convert quadratic points to cubic points
23570  $xa = ($x + (2 * $x1)) / 3;
23571  $ya = ($y + (2 * $y1)) / 3;
23572  $x = $params[($ck - 1)] + $xoffset;
23573  $y = $params[($ck)] + $yoffset;
23574  $xb = ($x + (2 * $x1)) / 3;
23575  $yb = ($y + (2 * $y1)) / 3;
23576  $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23577  $xmin = min($xmin, $x, $xa, $xb);
23578  $ymin = min($ymin, $y, $ya, $yb);
23579  $xmax = max($xmax, $x, $xa, $xb);
23580  $ymax = max($ymax, $y, $ya, $yb);
23581  if ($relcoord) {
23582  $xoffset = $x;
23583  $yoffset = $y;
23584  }
23585  }
23586  }
23587  break;
23588  }
23589  case 'A': { // elliptical arc
23590  foreach ($params as $ck => $cp) {
23591  $params[$ck] = $cp;
23592  if ((($ck + 1) % 7) == 0) {
23593  $x0 = $x;
23594  $y0 = $y;
23595  $rx = abs($params[($ck - 6)]);
23596  $ry = abs($params[($ck - 5)]);
23597  $ang = -$rawparams[($ck - 4)];
23598  $angle = deg2rad($ang);
23599  $fa = $rawparams[($ck - 3)]; // large-arc-flag
23600  $fs = $rawparams[($ck - 2)]; // sweep-flag
23601  $x = $params[($ck - 1)] + $xoffset;
23602  $y = $params[$ck] + $yoffset;
23603  if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
23604  // endpoints are almost identical
23605  $xmin = min($xmin, $x);
23606  $ymin = min($ymin, $y);
23607  $xmax = max($xmax, $x);
23608  $ymax = max($ymax, $y);
23609  } else {
23610  $cos_ang = cos($angle);
23611  $sin_ang = sin($angle);
23612  $a = (($x0 - $x) / 2);
23613  $b = (($y0 - $y) / 2);
23614  $xa = ($a * $cos_ang) - ($b * $sin_ang);
23615  $ya = ($a * $sin_ang) + ($b * $cos_ang);
23616  $rx2 = $rx * $rx;
23617  $ry2 = $ry * $ry;
23618  $xa2 = $xa * $xa;
23619  $ya2 = $ya * $ya;
23620  $delta = ($xa2 / $rx2) + ($ya2 / $ry2);
23621  if ($delta > 1) {
23622  $rx *= sqrt($delta);
23623  $ry *= sqrt($delta);
23624  $rx2 = $rx * $rx;
23625  $ry2 = $ry * $ry;
23626  }
23627  $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
23628  if ($numerator < 0) {
23629  $root = 0;
23630  } else {
23631  $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
23632  }
23633  if ($fa == $fs){
23634  $root *= -1;
23635  }
23636  $cax = $root * (($rx * $ya) / $ry);
23637  $cay = -$root * (($ry * $xa) / $rx);
23638  // coordinates of ellipse center
23639  $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
23640  $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
23641  // get angles
23642  $angs = TCPDF_STATIC::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
23643  $dang = TCPDF_STATIC::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
23644  if (($fs == 0) AND ($dang > 0)) {
23645  $dang -= (2 * M_PI);
23646  } elseif (($fs == 1) AND ($dang < 0)) {
23647  $dang += (2 * M_PI);
23648  }
23649  $angf = $angs - $dang;
23650  if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
23651  // reverse angles
23652  $tmp = $angs;
23653  $angs = $angf;
23654  $angf = $tmp;
23655  }
23656  $angs = round(rad2deg($angs), 6);
23657  $angf = round(rad2deg($angf), 6);
23658  // covent angles to positive values
23659  if (($angs < 0) AND ($angf < 0)) {
23660  $angs += 360;
23661  $angf += 360;
23662  }
23663  $pie = false;
23664  if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
23665  $pie = true;
23666  }
23667  list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
23668  $xmin = min($xmin, $x, $axmin);
23669  $ymin = min($ymin, $y, $aymin);
23670  $xmax = max($xmax, $x, $axmax);
23671  $ymax = max($ymax, $y, $aymax);
23672  }
23673  if ($relcoord) {
23674  $xoffset = $x;
23675  $yoffset = $y;
23676  }
23677  }
23678  }
23679  break;
23680  }
23681  case 'Z': {
23682  $this->_out('h');
23683  break;
23684  }
23685  }
23686  $firstcmd = false;
23687  } // end foreach
23688  if (!empty($op)) {
23689  $this->_out($op);
23690  }
23691  return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
23692  }
23693 
23699  protected function removeTagNamespace($name) {
23700  if(strpos($name, ':') !== false) {
23701  $parts = explode(':', $name);
23702  return $parts[(sizeof($parts) - 1)];
23703  }
23704  return $name;
23705  }
23706 
23717  protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
23718  $name = $this->removeTagNamespace($name);
23719  // check if we are in clip mode
23720  if ($this->svgclipmode) {
23721  $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]);
23722  return;
23723  }
23724  if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
23725  if (isset($attribs['id'])) {
23726  $attribs['child_elements'] = array();
23727  $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23728  return;
23729  }
23730  if (end($this->svgdefs) !== FALSE) {
23731  $last_svgdefs_id = key($this->svgdefs);
23732  if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
23733  $attribs['id'] = 'DF_'.(count($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements']) + 1);
23734  $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23735  return;
23736  }
23737  }
23738  return;
23739  }
23740  $clipping = false;
23741  if ($parser == 'clip-path') {
23742  // set clipping mode
23743  $clipping = true;
23744  }
23745  // get styling properties
23746  $prev_svgstyle = $this->svgstyles[max(0,(count($this->svgstyles) - 1))]; // previous style
23747  $svgstyle = $this->svgstyles[0]; // set default style
23748  if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) {
23749  // default fill attribute for clipping
23750  $attribs['fill'] = 'none';
23751  }
23752  if (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) {
23753  // fix style for regular expression
23754  $attribs['style'] = ';'.$attribs['style'];
23755  }
23756  foreach ($prev_svgstyle as $key => $val) {
23757  if (in_array($key, TCPDF_IMAGES::$svginheritprop)) {
23758  // inherit previous value
23759  $svgstyle[$key] = $val;
23760  }
23761  if (isset($attribs[$key]) AND !TCPDF_STATIC::empty_string($attribs[$key])) {
23762  // specific attribute settings
23763  if ($attribs[$key] == 'inherit') {
23764  $svgstyle[$key] = $val;
23765  } else {
23766  $svgstyle[$key] = $attribs[$key];
23767  }
23768  } elseif (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) {
23769  // CSS style syntax
23770  $attrval = array();
23771  if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
23772  if ($attrval[1] == 'inherit') {
23773  $svgstyle[$key] = $val;
23774  } else {
23775  $svgstyle[$key] = $attrval[1];
23776  }
23777  }
23778  }
23779  }
23780  // transformation matrix
23781  if (!empty($ctm)) {
23782  $tm = $ctm;
23783  } else {
23784  $tm = array(1,0,0,1,0,0);
23785  }
23786  if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
23788  }
23789  $svgstyle['transfmatrix'] = $tm;
23790  $invisible = false;
23791  if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
23792  // the current graphics element is invisible (nothing is painted)
23793  $invisible = true;
23794  }
23795  // process tag
23796  switch($name) {
23797  case 'defs': {
23798  $this->svgdefsmode = true;
23799  break;
23800  }
23801  // clipPath
23802  case 'clipPath': {
23803  if ($invisible) {
23804  break;
23805  }
23806  $this->svgclipmode = true;
23807  if (!isset($attribs['id'])) {
23808  $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1);
23809  }
23810  $this->svgclipid = $attribs['id'];
23811  $this->svgclippaths[$this->svgclipid] = array();
23812  $this->svgcliptm[$this->svgclipid] = $tm;
23813  break;
23814  }
23815  case 'svg': {
23816  // start of SVG object
23817  if(++$this->svg_tag_depth <= 1) {
23818  break;
23819  }
23820  // inner SVG
23821  array_push($this->svgstyles, $svgstyle);
23822  $this->StartTransform();
23823  $svgX = (isset($attribs['x'])?$attribs['x']:0);
23824  $svgY = (isset($attribs['y'])?$attribs['y']:0);
23825  $svgW = (isset($attribs['width'])?$attribs['width']:0);
23826  $svgH = (isset($attribs['height'])?$attribs['height']:0);
23827  // set x, y position using transform matrix
23828  $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array( 1, 0, 0, 1, $svgX, $svgY));
23829  $this->SVGTransform($tm);
23830  // set clipping for width and height
23831  $x = 0;
23832  $y = 0;
23833  $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):$this->w);
23834  $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):$this->h);
23835  // draw clipping rect
23836  $this->Rect($x, $y, $w, $h, 'CNZ', array(), array());
23837  // parse viewbox, calculate extra transformation matrix
23838  if (isset($attribs['viewBox'])) {
23839  $tmp = array();
23840  preg_match_all("/[0-9]+/", $attribs['viewBox'], $tmp);
23841  $tmp = $tmp[0];
23842  if (sizeof($tmp) == 4) {
23843  $vx = $tmp[0];
23844  $vy = $tmp[1];
23845  $vw = $tmp[2];
23846  $vh = $tmp[3];
23847  // get aspect ratio
23848  $tmp = array();
23849  $aspectX = 'xMid';
23850  $aspectY = 'YMid';
23851  $fit = 'meet';
23852  if (isset($attribs['preserveAspectRatio'])) {
23853  if($attribs['preserveAspectRatio'] == 'none') {
23854  $fit = 'none';
23855  } else {
23856  preg_match_all('/[a-zA-Z]+/', $attribs['preserveAspectRatio'], $tmp);
23857  $tmp = $tmp[0];
23858  if ((sizeof($tmp) == 2) AND (strlen($tmp[0]) == 8) AND (in_array($tmp[1], array('meet', 'slice', 'none')))) {
23859  $aspectX = substr($tmp[0], 0, 4);
23860  $aspectY = substr($tmp[0], 4, 4);
23861  $fit = $tmp[1];
23862  }
23863  }
23864  }
23865  $wr = ($svgW / $vw);
23866  $hr = ($svgH / $vh);
23867  $ax = $ay = 0;
23868  if ((($fit == 'meet') AND ($hr < $wr)) OR (($fit == 'slice') AND ($hr > $wr))) {
23869  if ($aspectX == 'xMax') {
23870  $ax = (($vw * ($wr / $hr)) - $vw);
23871  }
23872  if ($aspectX == 'xMid') {
23873  $ax = ((($vw * ($wr / $hr)) - $vw) / 2);
23874  }
23875  $wr = $hr;
23876  } elseif ((($fit == 'meet') AND ($hr > $wr)) OR (($fit == 'slice') AND ($hr < $wr))) {
23877  if ($aspectY == 'YMax') {
23878  $ay = (($vh * ($hr / $wr)) - $vh);
23879  }
23880  if ($aspectY == 'YMid') {
23881  $ay = ((($vh * ($hr / $wr)) - $vh) / 2);
23882  }
23883  $hr = $wr;
23884  }
23885  $newtm = array($wr, 0, 0, $hr, (($wr * ($ax - $vx)) - $svgX), (($hr * ($ay - $vy)) - $svgY));
23887  $this->SVGTransform($tm);
23888  }
23889  }
23890  $this->setSVGStyles($svgstyle, $prev_svgstyle);
23891  break;
23892  }
23893  case 'g': {
23894  // group together related graphics elements
23895  array_push($this->svgstyles, $svgstyle);
23896  $this->StartTransform();
23897  $x = (isset($attribs['x'])?$attribs['x']:0);
23898  $y = (isset($attribs['y'])?$attribs['y']:0);
23899  $w = 1;//(isset($attribs['width'])?$attribs['width']:1);
23900  $h = 1;//(isset($attribs['height'])?$attribs['height']:1);
23902  $this->SVGTransform($tm);
23903  $this->setSVGStyles($svgstyle, $prev_svgstyle);
23904  break;
23905  }
23906  case 'linearGradient': {
23907  if ($this->pdfa_mode) {
23908  break;
23909  }
23910  if (!isset($attribs['id'])) {
23911  $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
23912  }
23913  $this->svggradientid = $attribs['id'];
23914  $this->svggradients[$this->svggradientid] = array();
23915  $this->svggradients[$this->svggradientid]['type'] = 2;
23916  $this->svggradients[$this->svggradientid]['stops'] = array();
23917  if (isset($attribs['gradientUnits'])) {
23918  $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
23919  } else {
23920  $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
23921  }
23922  //$attribs['spreadMethod']
23923  if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2'])))
23924  OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%'))
23925  OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%'))
23926  OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%'))
23927  OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) {
23928  $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
23929  } else {
23930  $this->svggradients[$this->svggradientid]['mode'] = 'measure';
23931  }
23932  $x1 = (isset($attribs['x1'])?$attribs['x1']:'0');
23933  $y1 = (isset($attribs['y1'])?$attribs['y1']:'0');
23934  $x2 = (isset($attribs['x2'])?$attribs['x2']:'100');
23935  $y2 = (isset($attribs['y2'])?$attribs['y2']:'0');
23936  if (isset($attribs['gradientTransform'])) {
23937  $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
23938  }
23939  $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
23940  if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23941  // gradient is defined on another place
23942  $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
23943  }
23944  break;
23945  }
23946  case 'radialGradient': {
23947  if ($this->pdfa_mode) {
23948  break;
23949  }
23950  if (!isset($attribs['id'])) {
23951  $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
23952  }
23953  $this->svggradientid = $attribs['id'];
23954  $this->svggradients[$this->svggradientid] = array();
23955  $this->svggradients[$this->svggradientid]['type'] = 3;
23956  $this->svggradients[$this->svggradientid]['stops'] = array();
23957  if (isset($attribs['gradientUnits'])) {
23958  $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
23959  } else {
23960  $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
23961  }
23962  //$attribs['spreadMethod']
23963  if (((!isset($attribs['cx'])) AND (!isset($attribs['cy'])))
23964  OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%'))
23965  OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')))) {
23966  $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
23967  } elseif (isset($attribs['r']) AND is_numeric($attribs['r']) AND ($attribs['r']) <= 1) {
23968  $this->svggradients[$this->svggradientid]['mode'] = 'ratio';
23969  } else {
23970  $this->svggradients[$this->svggradientid]['mode'] = 'measure';
23971  }
23972  $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5);
23973  $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5);
23974  $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx);
23975  $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy);
23976  $r = (isset($attribs['r']) ? $attribs['r'] : 0.5);
23977  if (isset($attribs['gradientTransform'])) {
23978  $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
23979  }
23980  $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
23981  if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23982  // gradient is defined on another place
23983  $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
23984  }
23985  break;
23986  }
23987  case 'stop': {
23988  // gradient stops
23989  if (substr($attribs['offset'], -1) == '%') {
23990  $offset = floatval(substr($attribs['offset'], -1)) / 100;
23991  } else {
23992  $offset = floatval($attribs['offset']);
23993  if ($offset > 1) {
23994  $offset /= 100;
23995  }
23996  }
23997  $stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors):'black';
23998  $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
23999  $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
24000  break;
24001  }
24002  // paths
24003  case 'path': {
24004  if ($invisible) {
24005  break;
24006  }
24007  if (isset($attribs['d'])) {
24008  $d = trim($attribs['d']);
24009  if (!empty($d)) {
24010  $x = (isset($attribs['x'])?$attribs['x']:0);
24011  $y = (isset($attribs['y'])?$attribs['y']:0);
24012  $w = (isset($attribs['width'])?$attribs['width']:1);
24013  $h = (isset($attribs['height'])?$attribs['height']:1);
24015  if ($clipping) {
24016  $this->SVGTransform($tm);
24017  $this->SVGPath($d, 'CNZ');
24018  } else {
24019  $this->StartTransform();
24020  $this->SVGTransform($tm);
24021  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ'));
24022  if (!empty($obstyle)) {
24023  $this->SVGPath($d, $obstyle);
24024  }
24025  $this->StopTransform();
24026  }
24027  }
24028  }
24029  break;
24030  }
24031  // shapes
24032  case 'rect': {
24033  if ($invisible) {
24034  break;
24035  }
24036  $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24037  $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24038  $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24039  $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24040  $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
24041  $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
24042  if ($clipping) {
24043  $this->SVGTransform($tm);
24044  $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
24045  } else {
24046  $this->StartTransform();
24047  $this->SVGTransform($tm);
24048  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
24049  if (!empty($obstyle)) {
24050  $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
24051  }
24052  $this->StopTransform();
24053  }
24054  break;
24055  }
24056  case 'circle': {
24057  if ($invisible) {
24058  break;
24059  }
24060  $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0);
24061  $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
24062  $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
24063  $x = ($cx - $r);
24064  $y = ($cy - $r);
24065  $w = (2 * $r);
24066  $h = $w;
24067  if ($clipping) {
24068  $this->SVGTransform($tm);
24069  $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
24070  } else {
24071  $this->StartTransform();
24072  $this->SVGTransform($tm);
24073  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
24074  if (!empty($obstyle)) {
24075  $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
24076  }
24077  $this->StopTransform();
24078  }
24079  break;
24080  }
24081  case 'ellipse': {
24082  if ($invisible) {
24083  break;
24084  }
24085  $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0);
24086  $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0);
24087  $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
24088  $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
24089  $x = ($cx - $rx);
24090  $y = ($cy - $ry);
24091  $w = (2 * $rx);
24092  $h = (2 * $ry);
24093  if ($clipping) {
24094  $this->SVGTransform($tm);
24095  $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
24096  } else {
24097  $this->StartTransform();
24098  $this->SVGTransform($tm);
24099  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
24100  if (!empty($obstyle)) {
24101  $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
24102  }
24103  $this->StopTransform();
24104  }
24105  break;
24106  }
24107  case 'line': {
24108  if ($invisible) {
24109  break;
24110  }
24111  $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
24112  $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
24113  $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
24114  $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
24115  $x = $x1;
24116  $y = $y1;
24117  $w = abs($x2 - $x1);
24118  $h = abs($y2 - $y1);
24119  if (!$clipping) {
24120  $this->StartTransform();
24121  $this->SVGTransform($tm);
24122  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
24123  $this->Line($x1, $y1, $x2, $y2);
24124  $this->StopTransform();
24125  }
24126  break;
24127  }
24128  case 'polyline':
24129  case 'polygon': {
24130  if ($invisible) {
24131  break;
24132  }
24133  $points = (isset($attribs['points'])?$attribs['points']:'0 0');
24134  $points = trim($points);
24135  // note that point may use a complex syntax not covered here
24136  $points = preg_split('/[\,\s]+/si', $points);
24137  if (count($points) < 4) {
24138  break;
24139  }
24140  $p = array();
24141  $xmin = 2147483647;
24142  $xmax = 0;
24143  $ymin = 2147483647;
24144  $ymax = 0;
24145  foreach ($points as $key => $val) {
24146  $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
24147  if (($key % 2) == 0) {
24148  // X coordinate
24149  $xmin = min($xmin, $p[$key]);
24150  $xmax = max($xmax, $p[$key]);
24151  } else {
24152  // Y coordinate
24153  $ymin = min($ymin, $p[$key]);
24154  $ymax = max($ymax, $p[$key]);
24155  }
24156  }
24157  $x = $xmin;
24158  $y = $ymin;
24159  $w = ($xmax - $xmin);
24160  $h = ($ymax - $ymin);
24161  if ($name == 'polyline') {
24162  $this->StartTransform();
24163  $this->SVGTransform($tm);
24164  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
24165  if (!empty($obstyle)) {
24166  $this->PolyLine($p, $obstyle, array(), array());
24167  }
24168  $this->StopTransform();
24169  } else { // polygon
24170  if ($clipping) {
24171  $this->SVGTransform($tm);
24172  $this->Polygon($p, 'CNZ', array(), array(), true);
24173  } else {
24174  $this->StartTransform();
24175  $this->SVGTransform($tm);
24176  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
24177  if (!empty($obstyle)) {
24178  $this->Polygon($p, $obstyle, array(), array(), true);
24179  }
24180  $this->StopTransform();
24181  }
24182  }
24183  break;
24184  }
24185  // image
24186  case 'image': {
24187  if ($invisible) {
24188  break;
24189  }
24190  if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
24191  break;
24192  }
24193  $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24194  $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24195  $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24196  $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24197  $img = $attribs['xlink:href'];
24198  if (!$clipping) {
24199  $this->StartTransform();
24200  $this->SVGTransform($tm);
24201  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
24202  if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
24203  // embedded image encoded as base64
24204  $img = '@'.base64_decode(substr($img, strlen($m[0])));
24205  } else {
24206  // fix image path
24207  if (!TCPDF_STATIC::empty_string($this->svgdir) AND (($img[0] == '.') OR (basename($img) == $img))) {
24208  // replace relative path with full server path
24209  $img = $this->svgdir.'/'.$img;
24210  }
24211  if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
24212  $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
24213  if (($findroot === false) OR ($findroot > 1)) {
24214  if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
24215  $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
24216  } else {
24217  $img = $_SERVER['DOCUMENT_ROOT'].$img;
24218  }
24219  }
24220  }
24221  $img = urldecode($img);
24222  $testscrtype = @parse_url($img);
24223  if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
24224  // convert URL to server path
24225  $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
24226  }
24227  }
24228  // get image type
24230  if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
24231  $this->ImageEps($img, $x, $y, $w, $h);
24232  } elseif ($imgtype == 'svg') {
24233  // store SVG vars
24244  $this->ImageSVG($img, $x, $y, $w, $h);
24245  // restore SVG vars
24246  $this->svggradients = $svggradients;
24247  $this->svggradientid = $svggradientid;
24248  $this->svgdefsmode = $svgdefsmode;
24249  $this->svgdefs = $svgdefs;
24250  $this->svgclipmode = $svgclipmode;
24251  $this->svgclippaths = $svgclippaths;
24252  $this->svgcliptm = $svgcliptm;
24253  $this->svgclipid = $svgclipid;
24254  $this->svgtext = $svgtext;
24255  $this->svgtextmode = $svgtextmode;
24256  } else {
24257  $this->Image($img, $x, $y, $w, $h);
24258  }
24259  $this->StopTransform();
24260  }
24261  break;
24262  }
24263  // text
24264  case 'text':
24265  case 'tspan': {
24266  if (isset($this->svgtextmode['text-anchor']) AND !empty($this->svgtext)) {
24267  // @TODO: unsupported feature
24268  }
24269  // only basic support - advanced features must be implemented
24270  $this->svgtextmode['invisible'] = $invisible;
24271  if ($invisible) {
24272  break;
24273  }
24274  array_push($this->svgstyles, $svgstyle);
24275  if (isset($attribs['x'])) {
24276  $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false);
24277  } elseif ($name == 'tspan') {
24278  $x = $this->x;
24279  } else {
24280  $x = 0;
24281  }
24282  if (isset($attribs['dx'])) {
24283  $x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false);
24284  }
24285  if (isset($attribs['y'])) {
24286  $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false);
24287  } elseif ($name == 'tspan') {
24288  $y = $this->y;
24289  } else {
24290  $y = 0;
24291  }
24292  if (isset($attribs['dy'])) {
24293  $y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false);
24294  }
24295  $svgstyle['text-color'] = $svgstyle['fill'];
24296  $this->svgtext = '';
24297  if (isset($svgstyle['text-anchor'])) {
24298  $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor'];
24299  } else {
24300  $this->svgtextmode['text-anchor'] = 'start';
24301  }
24302  if (isset($svgstyle['direction'])) {
24303  if ($svgstyle['direction'] == 'rtl') {
24304  $this->svgtextmode['rtl'] = true;
24305  } else {
24306  $this->svgtextmode['rtl'] = false;
24307  }
24308  } else {
24309  $this->svgtextmode['rtl'] = false;
24310  }
24311  if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
24312  $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false);
24313  } else {
24314  $this->svgtextmode['stroke'] = false;
24315  }
24316  $this->StartTransform();
24317  $this->SVGTransform($tm);
24318  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
24319  $this->x = $x;
24320  $this->y = $y;
24321  break;
24322  }
24323  // use
24324  case 'use': {
24325  if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24326  $svgdefid = substr($attribs['xlink:href'], 1);
24327  if (isset($this->svgdefs[$svgdefid])) {
24328  $use = $this->svgdefs[$svgdefid];
24329  if (isset($attribs['xlink:href'])) {
24330  unset($attribs['xlink:href']);
24331  }
24332  if (isset($attribs['id'])) {
24333  unset($attribs['id']);
24334  }
24335  if (isset($use['attribs']['x']) AND isset($attribs['x'])) {
24336  $attribs['x'] += $use['attribs']['x'];
24337  }
24338  if (isset($use['attribs']['y']) AND isset($attribs['y'])) {
24339  $attribs['y'] += $use['attribs']['y'];
24340  }
24341  if (empty($attribs['style'])) {
24342  $attribs['style'] = '';
24343  }
24344  if (!empty($use['attribs']['style'])) {
24345  // merge styles
24346  $attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']);
24347  }
24348  $attribs = array_merge($use['attribs'], $attribs);
24349  $this->startSVGElementHandler($parser, $use['name'], $attribs);
24350  return;
24351  }
24352  }
24353  break;
24354  }
24355  default: {
24356  break;
24357  }
24358  } // end of switch
24359  // process child elements
24360  if (!empty($attribs['child_elements'])) {
24361  $child_elements = $attribs['child_elements'];
24362  unset($attribs['child_elements']);
24363  foreach($child_elements as $child_element) {
24364  if (empty($child_element['attribs']['closing_tag'])) {
24365  $this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']);
24366  } else {
24367  if (isset($child_element['attribs']['content'])) {
24368  $this->svgtext = $child_element['attribs']['content'];
24369  }
24370  $this->endSVGElementHandler('child-tag', $child_element['name']);
24371  }
24372  }
24373  }
24374  }
24375 
24384  protected function endSVGElementHandler($parser, $name) {
24385  $name = $this->removeTagNamespace($name);
24386  if ($this->svgdefsmode AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {;
24387  if (end($this->svgdefs) !== FALSE) {
24388  $last_svgdefs_id = key($this->svgdefs);
24389  if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
24390  foreach($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) {
24391  if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) {
24392  $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24393  return;
24394  }
24395  }
24396  if ($this->svgdefs[$last_svgdefs_id]['name'] == $name) {
24397  $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24398  return;
24399  }
24400  }
24401  }
24402  return;
24403  }
24404  switch($name) {
24405  case 'defs': {
24406  $this->svgdefsmode = false;
24407  break;
24408  }
24409  // clipPath
24410  case 'clipPath': {
24411  $this->svgclipmode = false;
24412  break;
24413  }
24414  case 'svg': {
24415  if (--$this->svg_tag_depth <= 0) {
24416  break;
24417  }
24418  }
24419  case 'g': {
24420  // ungroup: remove last style from array
24421  array_pop($this->svgstyles);
24422  $this->StopTransform();
24423  break;
24424  }
24425  case 'text':
24426  case 'tspan': {
24427  if ($this->svgtextmode['invisible']) {
24428  // This implementation must be fixed to following the rule:
24429  // 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.
24430  break;
24431  }
24432  // print text
24434  //$text = $this->stringTrim($text);
24435  $textlen = $this->GetStringWidth($text);
24436  if ($this->svgtextmode['text-anchor'] != 'start') {
24437  // check if string is RTL text
24438  if ($this->svgtextmode['text-anchor'] == 'end') {
24439  if ($this->svgtextmode['rtl']) {
24440  $this->x += $textlen;
24441  } else {
24442  $this->x -= $textlen;
24443  }
24444  } elseif ($this->svgtextmode['text-anchor'] == 'middle') {
24445  if ($this->svgtextmode['rtl']) {
24446  $this->x += ($textlen / 2);
24447  } else {
24448  $this->x -= ($textlen / 2);
24449  }
24450  }
24451  }
24454  $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false);
24455  if ($name == 'text') {
24456  // store current coordinates
24457  $tmpx = $this->x;
24458  $tmpy = $this->y;
24459  }
24460  // print the text
24461  $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
24462  if ($name == 'text') {
24463  // restore coordinates
24464  $this->x = $tmpx;
24465  $this->y = $tmpy;
24466  }
24467  // restore previous rendering mode
24468  $this->textrendermode = $textrendermode;
24469  $this->textstrokewidth = $textstrokewidth;
24470  $this->svgtext = '';
24471  $this->StopTransform();
24472  if (!$this->svgdefsmode) {
24473  array_pop($this->svgstyles);
24474  }
24475  break;
24476  }
24477  default: {
24478  break;
24479  }
24480  }
24481  }
24482 
24491  protected function segSVGContentHandler($parser, $data) {
24492  $this->svgtext .= $data;
24493  }
24494 
24495  // --- END SVG METHODS -----------------------------------------------------
24496 
24497 } // END OF TCPDF CLASS
24498 
24499 //============================================================+
24500 // END OF FILE
24501 //============================================================+
write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='')
Print a Linear Barcode.
Definition: tcpdf.php:15197
$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:12071
getGroupPageNo()
Return the current page in the group.
Definition: tcpdf.php:13668
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:13697
$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:13951
_putshaders()
Output gradient shaders.
Definition: tcpdf.php:14577
$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:13993
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:7248
$pagelen
Array containing page lengths in bytes.
Definition: tcpdf.php:1163
getNumberOfColumns()
Return the current number of columns.
Definition: tcpdf.php:21835
$file_id
File ID (used on document trailer).
Definition: tcpdf.php:829
_putviewerpreferences()
Output viewer preferences.
Definition: tcpdf.php:9837
$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:22132
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:9364
$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:10302
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:20545
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:21902
addJavascriptObject($script, $onload=false)
Adds a javascript object and return object ID.
Definition: tcpdf.php:12506
_outPoint($x, $y)
Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line s...
Definition: tcpdf.php:11384
$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:23047
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:13545
MirrorP($x='', $y='')
Point reflection mirroring.
Definition: tcpdf.php:11089
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:21632
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:13122
TranslateX($t_x)
Translate graphic object horizontally.
Definition: tcpdf.php:11114
TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false)
Creates a text field.
Definition: tcpdf.php:12645
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:20678
$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:22500
setPageBoxTypes($boxes)
Set page boxes to be included on page descriptions.
Definition: tcpdf.php:7882
$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:20173
$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:7829
$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:10427
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:14790
getCSSPadding($csspadding, $width=0)
Get the internal Cell padding from CSS attribute.
Definition: tcpdf.php:15950
setFontSubBuffer($font, $key, $data)
Set font buffer content.
Definition: tcpdf.php:20822
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:7895
static _escapeXML($str)
Escape some special characters (< > &) for XML output.
_putXMP()
Put XMP data object and return ID.
Definition: tcpdf.php:9512
setFontSpacing($spacing=0)
Set the amount to increase or decrease the space between characters in a text.
Definition: tcpdf.php:22489
$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:11652
SetCreator($creator)
Defines the creator of the document.
Definition: tcpdf.php:2911
_putbookmarks()
Create a bookmark PDF string.
Definition: tcpdf.php:12367
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:21746
$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:12349
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:12249
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:14028
Line($x1, $y1, $x2, $y2, $style=array())
Draws a line between two points.
Definition: tcpdf.php:11481
$creator
Document creator.
Definition: tcpdf.php:512
MirrorH($x='')
Horizontal Mirroring.
Definition: tcpdf.php:11066
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:20259
$FontSize
Current font size in user unit.
Definition: tcpdf.php:416
_encrypt_data($n, $s)
Encrypt the input string.
Definition: tcpdf.php:10462
SetXY($x, $y, $rtloff=false)
Defines the abscissa and ordinate of the current position.
Definition: tcpdf.php:7507
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:11414
$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
if(PHP_SAPI !='cli') color
Definition: langcheck.php:120
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:20213
$tsa_data
Timestamping data.
Definition: tcpdf.php:1296
margin left
Definition: langcheck.php:164
closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0)
Process closing tags.
Definition: tcpdf.php:19359
_putjavascript()
Create a javascript PDF string.
Definition: tcpdf.php:12522
writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='')
Allows to preserve some HTML formatting (limited support).
Definition: tcpdf.php:17120
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:12206
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:20757
getPageBuffer($page)
Get page buffer content.
Definition: tcpdf.php:20742
setBarcode($bc='')
Set document barcode.
Definition: tcpdf.php:15153
$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:23008
RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array())
Draws a rounded rectangle.
Definition: tcpdf.php:12052
$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:23717
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:14834
$sign
Boolean flag to enable document digital signature.
Definition: tcpdf.php:1254
openHTMLTagHandler($dom, $key, $cell)
Process opening tags.
Definition: tcpdf.php:18689
$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:15816
SetAuthor($author)
Defines the author of the document.
Definition: tcpdf.php:2889
StopTransform()
Stops a 2D tranformation restoring previous graphic state.
Definition: tcpdf.php:10968
ScaleXY($s, $x='', $y='')
Vertical and horizontal proportional Scaling.
Definition: tcpdf.php:11019
Ln($h='', $cell=false)
Performs a line break.
Definition: tcpdf.php:7367
$patch_array[0]['f']
PolyLine($p, $style='', $line_style=array(), $fill_color=array())
Draws a polygonal line.
Definition: tcpdf.php:11842
_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:11693
$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:20710
setHtmlLinksStyle($color=array(0, 0, 255), $fontstyle='U')
Set the color and font style for HTML links.
Definition: tcpdf.php:20244
static convertHexStringToString($bs)
Convert hexadecimal string to string.
getCSSMargin($cssmargin, $width=0)
Get the internal Cell margin from CSS attribute.
Definition: tcpdf.php:16004
$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:15796
getColumn()
Return the current column number.
Definition: tcpdf.php:21825
static removeSHY($txt='', $unicode=true)
Removes SHY characters from text.
setExtGState($gs)
Add an extgstate.
Definition: tcpdf.php:13828
getFontSize()
Returns the current font size.
Definition: tcpdf.php:15786
getFontFamily()
Returns the current font family name.
Definition: tcpdf.php:15806
_Ovalue()
Compute O value (used for encryption)
Definition: tcpdf.php:10655
_outCurveV($x2, $y2, $x3, $y3)
Append a cubic Bezier curve to the current path.
Definition: tcpdf.php:11448
Rotate($angle, $x='', $y='')
Rotate object.
Definition: tcpdf.php:11159
$textstrokewidth
Text stroke width in doc units.
Definition: tcpdf.php:1471
startPageGroup($page='')
Create a new page group.
Definition: tcpdf.php:13560
$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.
if(! $in) print Initializing normalization quick check tables n
GetX()
Returns the relative X value of current position.
Definition: tcpdf.php:7401
$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:20789
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:10322
setTextRenderingMode($stroke=0, $fill=true, $clip=false)
Set Text rendering mode.
Definition: tcpdf.php:21847
$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:11432
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:13610
setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='')
Enable document timestamping (requires the OpenSSL Library).
Definition: tcpdf.php:13517
_putfonts()
Output fonts.
Definition: tcpdf.php:8774
stringRightTrim($str, $replace='')
Right trim the input string.
Definition: tcpdf.php:22158
$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:13725
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:10244
$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:19906
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:22512
addExtGState($parms)
Add transparency parameters to the current extgstate.
Definition: tcpdf.php:13797
$form_action
Current form action (used during XHTML rendering).
Definition: tcpdf.php:1352
p
Definition: langcheck.php:169
_putinfo()
Adds some Metadata information (Document Information Dictionary) (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference)
Definition: tcpdf.php:9451
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:11582
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:11006
_dooverlinew($x, $y, $w)
Overline for rectangular text area.
Definition: tcpdf.php:10155
setDocCreationTimestamp($time)
Set the document creation timestamp.
Definition: tcpdf.php:10182
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:10208
setLIsymbol($symbol='!')
Set the default bullet to be used as LI bullet symbol.
Definition: tcpdf.php:20150
$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:9650
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:14011
$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
for($col=0; $col< 50; $col++) $d
$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:9428
$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:12141
$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:11398
$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:10706
drawHTMLTagBorder($tag, $xmax)
Draw an HTML block border and fill.
Definition: tcpdf.php:19948
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:17103
$subject
Document subject.
Definition: tcpdf.php:494
setCellHeightRatio($h)
Set the height of the cell (line height) respect the font height.
Definition: tcpdf.php:13974
getPageGroupAlias()
Return the alias for the total number of pages in the current page group.
Definition: tcpdf.php:13640
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:10610
$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:7542
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:20233
getAliasNumPage()
Returns the string alias used for the page number.
Definition: tcpdf.php:13625
$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:14769
_addfield($type, $name, $x, $y, $w, $h, $prop)
Adds a javascript form field.
Definition: tcpdf.php:12580
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:10442
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:10260
$listindentlevel
HTML PARSER: current list indententation level.
Definition: tcpdf.php:746
Done rendering charts as images
_out($s)
Output a string to the document.
Definition: tcpdf.php:10273
$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:16123
getAlpha()
Get the alpha mode array (CA, ca, BM, AIS).
Definition: tcpdf.php:13941
$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:11102
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:12010
$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
textarea
Definition: langcheck.php:160
$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:20189
$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:10116
endLayer()
End the current PDF layer.
Definition: tcpdf.php:13745
getHashForTCPDFtagParams($data)
Return an hash code used to ensure that the serialized data has been generated by this TCPDF instance...
Definition: tcpdf.php:17051
setPrintFooter($val=true)
Set a flag to print page footer.
Definition: tcpdf.php:3348
MirrorV($y='')
Verical Mirroring.
Definition: tcpdf.php:11077
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
if(!is_dir( $entity_dir)) exit("Fatal Error ([A-Za-z0-9]+)\+" &#(? foreach( $entity_files as $file) $output
$header_logo
Header image logo.
Definition: tcpdf.php:637
static getTCPDFProducer()
Return the current TCPDF producer.
getBarcode()
Get current barcode.
Definition: tcpdf.php:15163
$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:7474
$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:20688
$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:23699
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:20670
Generate an image
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:13451
$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:22527
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:12285
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:10104
$header_string
String to pring on page header after title.
Definition: tcpdf.php:655
$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:9502
static $byterange_string
ByteRange placemark used during digital signature process.
stringTrim($str, $replace='')
Trim the input string.
Definition: tcpdf.php:22171
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/).
$column
Definition: 39dropdown.php:62
getCSSBorderWidth($width)
Returns the border width from CSS property.
Definition: tcpdf.php:15843
font size
Definition: langcheck.php:162
Set fonts
_endpage()
Mark end of page.
Definition: tcpdf.php:10052
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:13377
_putxobjects()
Output Form XObjects Templates.
Definition: tcpdf.php:9229
_datestring($n=0, $timestamp=0)
Returns a formatted date for meta information.
Definition: tcpdf.php:10230
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:22145
$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:10142
$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:21962
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:10129
unserializeTCPDFtagParameters($data)
Unserialize parameters to be used with TCPDF tag in HTML code.
Definition: tcpdf.php:17072
$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:11824
getCSSBorderDashStyle($style)
Returns the border dash style from CSS property.
Definition: tcpdf.php:15863
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:21649
setListIndentWidth($width)
Set custom width for list indentation.
Definition: tcpdf.php:20223
serializeTCPDFtagParameters($data)
Serialize an array of parameters to be used with TCPDF tag in HTML code.
Definition: tcpdf.php:17061
setDefaultTableColumns($cols=4)
Set the default number of columns in a row for HTML tables.
Definition: tcpdf.php:13964
$textindent
Text indentation value (used for text-indent CSS attribute).
Definition: tcpdf.php:1394
Play around with inserting and removing rows and columns
$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:21687
e($cmd)
Definition: flush.php:14
ScaleX($s_x, $x='', $y='')
Horizontal Scaling.
Definition: tcpdf.php:10993
_putcidfont0($font)
Output CID-0 fonts.
Definition: tcpdf.php:9030
setRasterizeVectorImages($mode)
Enable/disable rasterization of vector images using ImageMagick library.
Definition: tcpdf.php:22106
$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:14351
$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:16249
copyPage($page=0)
Clone the specified page to a new page.
Definition: tcpdf.php:21212
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:13414
$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:10643
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:7558
$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:14239
$linestyleWidth
PDF string for width value of the last line.
Definition: tcpdf.php:1008
getPDFData()
Returns the PDF data.
Definition: tcpdf.php:10355
rollbackTransaction($self=false)
This method allows to undo the latest transaction by returning the latest saved TCPDF object with sta...
Definition: tcpdf.php:21663
static _escape($s)
Add "\" before "\", "(" and ")".
setFontSubsetting($enable=true)
Enable or disable default option for font subsetting.
Definition: tcpdf.php:22117
$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:21944
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:6828
static getSpotColor($name, &$spotc)
Return the Spot color array.
_beginpage($orientation='', $format='')
Initialize a new page.
Definition: tcpdf.php:10009
_getxobjectdict()
Return XObjects Dictionary.
Definition: tcpdf.php:9352
getCSSBorderMargin($cssbspace, $width=0)
Get the border-spacing from CSS attribute.
Definition: tcpdf.php:16058
$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:8744
_destroy($destroyall=false, $preserve_objcopy=false)
Unset all class variables except the following critical variables.
Definition: tcpdf.php:7756
AddLink()
Creates a new internal link and returns its identifier.
Definition: tcpdf.php:4683
Add a drawing to the header
Definition: 04printing.php:69
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:13289
$javascript
Javascript code.
Definition: tcpdf.php:854
getMargins()
Returns an array containing current margins:
Definition: tcpdf.php:15745
$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:22478
$txt
Definition: error.php:12
setGraphicVars($gvars, $extended=false)
Set graphic variables.
Definition: tcpdf.php:20606
$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:11511
$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:13485
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:13573
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:13655
getDocModificationTimestamp()
Returns document modification timestamp in seconds.
Definition: tcpdf.php:10218
$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.
Create styles array
The data for the language used.
$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:11612
$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
Remove unnecessary rows
setFontStretching($perc=100)
Set the percentage of character stretching.
Definition: tcpdf.php:22467
$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:20851
_dounderline($x, $y, $txt)
Underline text.
Definition: tcpdf.php:10091
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:9918
$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:14266
$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:12869
$ColorFlag
Indicates whether fill and text colors are different.
Definition: tcpdf.php:440
foreach($mandatory_scripts as $file) $timestamp
Definition: buildRTE.php:81
checkPageRegions($h, $x, $y)
Check page for no-write regions and adapt current coordinates and page margins if necessary...
Definition: tcpdf.php:22584
$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:21719
setOverprint($stroking=true, $nonstroking='', $mode=0)
Set overprint mode for stroking (OP) and non-stroking (op) painting operations.
Definition: tcpdf.php:13870
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:11137
$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:10721
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:9100
$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:7428
$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:10490
$imgscale
Adjusting factor to convert pixels to user units.
Definition: tcpdf.php:542
$parser
Definition: BPMN2Parser.php:24
select
Definition: langcheck.php:166
$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:11861
getCSSBorderStyle($cssborder)
Returns the border style array from CSS border properties.
Definition: tcpdf.php:15899
PageNoFormatted()
Returns the current page number formatted as a string.
Definition: tcpdf.php:13688
$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:13678
$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:22334
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:10839
$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:9909
$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:22184
addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0, 0, 0))
Output a Table of Content Index (TOC).
Definition: tcpdf.php:21289
LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0, 0, 1, 0))
Paints a linear colour gradient.
Definition: tcpdf.php:14310
cropMark($x, $y, $w, $h, $type='T, R, B, L', $color=array(100, 100, 100, 100, 'All'))
Paints crop marks.
Definition: tcpdf.php:14168
StartTransform()
Starts a 2D tranformation saving current graphic state.
Definition: tcpdf.php:10945
$svgclippaths
Array of SVG clipPath commands.
Definition: tcpdf.php:1645
SkewY($angle_y, $x='', $y='')
Skew vertically.
Definition: tcpdf.php:11202
$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:24491
$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:7342
$lMargin
Left margin.
Definition: tcpdf.php:244
_putextgstates()
Put extgstates for object transparency.
Definition: tcpdf.php:13841
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:11322
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:20699
$tcpdflink
If true print TCPDF meta link.
Definition: tcpdf.php:1809
getHeaderMargin()
Returns header margin in user units.
Definition: tcpdf.php:3311
$ret
Definition: parser.php:6
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:7808
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:14443
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:24384
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:22305
getAliasRightShift()
Returns the string alias used right align page numbers.
Definition: tcpdf.php:13584
setVisibility($v)
Set the visibility of the successive elements.
Definition: tcpdf.php:13764
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:20343
RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false)
Creates a RadioButton field.
Definition: tcpdf.php:12766
replaceRightShiftPageNumAliases($page, $aliases, $diff)
Replace right shift page number aliases with spaces to correct right alignment.
Definition: tcpdf.php:7847
SetAbsX($x)
Set the absolute X coordinate of the current pointer.
Definition: tcpdf.php:7519
$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:14328
$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:11125
pixelsToUnits($px)
Converts pixels to User&#39;s Units.
Definition: tcpdf.php:10416
$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:16195
$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:7791
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:12493
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:13983
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:11962
$starty
write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false)
Print 2D Barcode.
Definition: tcpdf.php:15516
AddFont($family, $style='', $fontfile='', $subset='default')
Imports a TrueType, Type1, core, or CID0 font and makes it available.
Definition: tcpdf.php:4171
Add data(end) time
Method that wraps PHPs time in order to allow simulations with the workflow.
if(!file_exists("$old.txt")) if($old===$new) if(file_exists("$new.txt")) $file
$bMargin
Page break margin.
Definition: tcpdf.php:274
setColumnsArray($columns)
Set columns array.
Definition: tcpdf.php:21732
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:12616
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:22687
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:10312
_datastring($s, $n=0)
Format a data string for meta information.
Definition: tcpdf.php:10168
Scale($s_x, $s_y, $x='', $y='')
Vertical and horizontal non-proportional Scaling.
Definition: tcpdf.php:11033
Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0, 0, 0), $x=-1, $link='')
Adds a bookmark.
Definition: tcpdf.php:12302
fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='')
Cleanup HTML code (requires HTML Tidy library).
Definition: tcpdf.php:15832
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:11279
hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8)
Returns text with soft hyphens.
Definition: tcpdf.php:22042
_getobj($objid='')
Return the starting object string for the selected object ID.
Definition: tcpdf.php:10074
CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false)
Creates a CheckBox field.
Definition: tcpdf.php:13042
defined( 'APPLICATION_ENV')||define( 'APPLICATION_ENV'
Definition: bootstrap.php:27
getOriginalMargins()
Returns an array containing original margins:
Definition: tcpdf.php:15772
_getannotsrefs($n)
Get references to page annotations.
Definition: tcpdf.php:8057
$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:11464
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:22547
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
$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:10332
SkewX($angle_x, $x='', $y='')
Skew horizontally.
Definition: tcpdf.php:11189
$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:13909
$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:22566
GetLineWidth()
Returns the current the line width.
Definition: tcpdf.php:11295
$pages
Array containing pages.
Definition: tcpdf.php:172
Transform($tm)
Apply graphic transformations.
Definition: tcpdf.php:11247
setDocModificationTimestamp($time)
Set the document modification timestamp.
Definition: tcpdf.php:10195
$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:23342
$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:13467
getBorderStartPosition()
Return the starting coordinates to draw an html border.
Definition: tcpdf.php:19932
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:11216
SetX($x, $rtloff=false)
Defines the abscissa of the current position.
Definition: tcpdf.php:7441
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:10374
_putspotcolors()
Output Spot Colors Resources.
Definition: tcpdf.php:9332
static getSVGTransformMatrix($attribute)
Get the tranformation matrix from SVG transform attribute.
getFormDefaultProp()
Return the default properties for form fields.
Definition: tcpdf.php:12627
setPageBuffer($page, $data, $append=false)
Set page buffer content.
Definition: tcpdf.php:20722
startTemplate($w=0, $h=0, $group=false)
Start a new XObject Template.
Definition: tcpdf.php:22240
_OEvalue()
Compute OE value (used for encryption)
Definition: tcpdf.php:10693
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:14470
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:21499
getSpaceString()
Returns the string used to find spaces.
Definition: tcpdf.php:17037
_putdests()
Insert Named Destinations.
Definition: tcpdf.php:12259
$xc
_newobj()
Begin a new object and return the object number.
Definition: tcpdf.php:10062
static fileGetContents($file)
Reads entire file into a string.
GetAbsX()
Returns the absolute X value of current position.
Definition: tcpdf.php:7417
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:23028
$fgcolor
Current foreground color.
Definition: tcpdf.php:716
getFontBuffer($font)
Get font buffer content.
Definition: tcpdf.php:20836
$theadMargins
Margins used for table header.
Definition: tcpdf.php:1247
deletePage($page)
Remove the specified page.
Definition: tcpdf.php:21022
$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:20775
getFontFamilyName($fontfamily)
Return normalized font name.
Definition: tcpdf.php:22196
SetAbsY($y)
Set the absolute Y coordinate of the current pointer.
Definition: tcpdf.php:7530
getCSSFontSpacing($spacing, $parent=0)
Returns the letter-spacing value from CSS value.
Definition: tcpdf.php:16092
setContentMark($page=0)
Set start-writing mark on selected page.
Definition: tcpdf.php:3236
_puttruetypeunicode($font)
Adds unicode fonts.
Definition: tcpdf.php:8926
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:8101
setFontBuffer($font, $data)
Set font buffer content.
Definition: tcpdf.php:20803
$svggradientid
ID of last SVG gradient.
Definition: tcpdf.php:1617
PHPExcel root directory.
Definition: Database.php:30
SetTitle($title)
Defines the title of the document.
Definition: tcpdf.php:2867
setLanguageArray($language)
Set language array.
Definition: tcpdf.php:10342
getOverprint()
Get the overprint mode array (OP, op, OPM).
Definition: tcpdf.php:13896
$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