tcpdf.php Quellcode

tcpdf.php
gehe zur Dokumentation dieser Datei
1 <?php
2 //============================================================+
3 // File name : tcpdf.php
4 // Version : 6.0.065
5 // Begin : 2002-08-03
6 // Last Update : 2014-04-07
7 // Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
8 // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
9 // -------------------------------------------------------------------
10 // Copyright (C) 2002-2014 Nicola Asuni - Tecnick.com LTD
11 //
12 // This file is part of TCPDF software library.
13 //
14 // TCPDF is free software: you can ioredistribute it and/or modify it
15 // under the terms of the GNU Lesser General Public License as
16 // published by the Free Software Foundation, either version 3 of the
17 // License, or (at your option) any later version.
18 //
19 // TCPDF is distributed in the hope that it will be useful, but
20 // WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22 // See the GNU Lesser General Public License for more details.
23 //
24 // You should have received a copy of the License
25 // along with TCPDF. If not, see
26 // <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>.
27 //
28 // See LICENSE.TXT file for more information.
29 // -------------------------------------------------------------------
30 //
31 // Description :
32 // This is a PHP class for generating PDF documents without requiring external extensions.
33 //
34 // NOTE:
35 // This class was originally derived in 2002 from the Public
36 // Domain FPDF class by Olivier Plathey (http://www.fpdf.org),
37 // but now is almost entirely rewritten and contains thousands of
38 // new lines of code and hundreds new features.
39 //
40 // Main features:
41 // * no external libraries are required for the basic functions;
42 // * all standard page formats, custom page formats, custom margins and units of measure;
43 // * UTF-8 Unicode and Right-To-Left languages;
44 // * TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;
45 // * font subsetting;
46 // * methods to publish some XHTML + CSS code, Javascript and Forms;
47 // * images, graphic (geometric figures) and transformation methods;
48 // * supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)
49 // * 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417;
50 // * JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;
51 // * automatic page header and footer management;
52 // * document encryption up to 256 bit and digital signature certifications;
53 // * transactions to UNDO commands;
54 // * PDF annotations, including links, text and file attachments;
55 // * text rendering modes (fill, stroke and clipping);
56 // * multiple columns mode;
57 // * no-write page regions;
58 // * bookmarks, named destinations and table of content;
59 // * text hyphenation;
60 // * text stretching and spacing (tracking);
61 // * automatic page break, line break and text alignments including justification;
62 // * automatic page numbering and page groups;
63 // * move and delete pages;
64 // * page compression (requires php-zlib extension);
65 // * XOBject Templates;
66 // * Layers and object visibility.
67 // * PDF/A-1b support
68 //============================================================+
69 
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 $cached_files = array();
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 $diskcache = false;
1192 
1198  protected $numfonts = 0;
1199 
1205  protected $fontkeys = array();
1206 
1212  protected $font_obj_ids = array();
1213 
1219  protected $pageopen = array();
1220 
1226  protected $default_monospaced_font = 'courier';
1227 
1233  protected $objcopy;
1234 
1240  protected $cache_file_length = array();
1241 
1247  protected $thead = '';
1248 
1254  protected $theadMargins = array();
1255 
1261  protected $sign = false;
1262 
1268  protected $signature_data = array();
1269 
1275  protected $signature_max_length = 11742;
1276 
1282  protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
1283 
1289  protected $empty_signature_appearance = array();
1290 
1296  protected $re_spaces = '/[^\S\xa0]/';
1297 
1303  protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
1304 
1310  protected $sig_obj_id = 0;
1311 
1317  protected $page_obj_id = array();
1318 
1324  protected $form_obj_id = array();
1325 
1331  protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1332 
1338  protected $js_objects = array();
1339 
1345  protected $form_action = '';
1346 
1352  protected $form_enctype = 'application/x-www-form-urlencoded';
1353 
1359  protected $form_mode = 'post';
1360 
1366  protected $annotation_fonts = array();
1367 
1373  protected $radiobutton_groups = array();
1374 
1380  protected $radio_groups = array();
1381 
1387  protected $textindent = 0;
1388 
1395 
1401  protected $start_transaction_y = 0;
1402 
1408  protected $inthead = false;
1409 
1415  protected $columns = array();
1416 
1422  protected $num_columns = 1;
1423 
1429  protected $current_column = 0;
1430 
1436  protected $column_start_page = 0;
1437 
1443  protected $maxselcol = array('page' => 0, 'column' => 0);
1444 
1450  protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
1451 
1457  protected $textrendermode = 0;
1458 
1464  protected $textstrokewidth = 0;
1465 
1471  protected $strokecolor;
1472 
1478  protected $pdfunit = 'mm';
1479 
1484  protected $tocpage = false;
1485 
1491  protected $rasterize_vector_images = false;
1492 
1498  protected $font_subsetting = true;
1499 
1505  protected $default_graphic_vars = array();
1506 
1512  protected $xobjects = array();
1513 
1519  protected $inxobj = false;
1520 
1526  protected $xobjid = '';
1527 
1533  protected $font_stretching = 100;
1534 
1540  protected $font_spacing = 0;
1541 
1548  protected $page_regions = array();
1549 
1554  protected $check_page_regions = true;
1555 
1561  protected $pdflayers = array();
1562 
1568  protected $dests = array();
1569 
1575  protected $n_dests;
1576 
1582  protected $efnames = array();
1583 
1589  protected $svgdir = '';
1590 
1596  protected $svgunit = 'px';
1597 
1603  protected $svggradients = array();
1604 
1610  protected $svggradientid = 0;
1611 
1617  protected $svgdefsmode = false;
1618 
1624  protected $svgdefs = array();
1625 
1631  protected $svgclipmode = false;
1632 
1638  protected $svgclippaths = array();
1639 
1645  protected $svgcliptm = array();
1646 
1652  protected $svgclipid = 0;
1653 
1659  protected $svgtext = '';
1660 
1666  protected $svgtextmode = array();
1667 
1673  protected $svgstyles = array(array(
1674  'alignment-baseline' => 'auto',
1675  'baseline-shift' => 'baseline',
1676  'clip' => 'auto',
1677  'clip-path' => 'none',
1678  'clip-rule' => 'nonzero',
1679  'color' => 'black',
1680  'color-interpolation' => 'sRGB',
1681  'color-interpolation-filters' => 'linearRGB',
1682  'color-profile' => 'auto',
1683  'color-rendering' => 'auto',
1684  'cursor' => 'auto',
1685  'direction' => 'ltr',
1686  'display' => 'inline',
1687  'dominant-baseline' => 'auto',
1688  'enable-background' => 'accumulate',
1689  'fill' => 'black',
1690  'fill-opacity' => 1,
1691  'fill-rule' => 'nonzero',
1692  'filter' => 'none',
1693  'flood-color' => 'black',
1694  'flood-opacity' => 1,
1695  'font' => '',
1696  'font-family' => 'helvetica',
1697  'font-size' => 'medium',
1698  'font-size-adjust' => 'none',
1699  'font-stretch' => 'normal',
1700  'font-style' => 'normal',
1701  'font-variant' => 'normal',
1702  'font-weight' => 'normal',
1703  'glyph-orientation-horizontal' => '0deg',
1704  'glyph-orientation-vertical' => 'auto',
1705  'image-rendering' => 'auto',
1706  'kerning' => 'auto',
1707  'letter-spacing' => 'normal',
1708  'lighting-color' => 'white',
1709  'marker' => '',
1710  'marker-end' => 'none',
1711  'marker-mid' => 'none',
1712  'marker-start' => 'none',
1713  'mask' => 'none',
1714  'opacity' => 1,
1715  'overflow' => 'auto',
1716  'pointer-events' => 'visiblePainted',
1717  'shape-rendering' => 'auto',
1718  'stop-color' => 'black',
1719  'stop-opacity' => 1,
1720  'stroke' => 'none',
1721  'stroke-dasharray' => 'none',
1722  'stroke-dashoffset' => 0,
1723  'stroke-linecap' => 'butt',
1724  'stroke-linejoin' => 'miter',
1725  'stroke-miterlimit' => 4,
1726  'stroke-opacity' => 1,
1727  'stroke-width' => 1,
1728  'text-anchor' => 'start',
1729  'text-decoration' => 'none',
1730  'text-rendering' => 'auto',
1731  'unicode-bidi' => 'normal',
1732  'visibility' => 'visible',
1733  'word-spacing' => 'normal',
1734  'writing-mode' => 'lr-tb',
1735  'text-color' => 'black',
1736  'transfmatrix' => array(1, 0, 0, 1, 0, 0)
1737  ));
1738 
1744  protected $force_srgb = false;
1745 
1751  protected $pdfa_mode = false;
1752 
1759 
1766 
1772  protected $custom_xmp = '';
1773 
1780  protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0);
1781 
1788  protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false);
1789 
1795  protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
1796 
1802  protected $tcpdflink = true;
1803 
1809  protected $gdgammacache = array();
1810 
1811  //------------------------------------------------------------
1812  // METHODS
1813  //------------------------------------------------------------
1814 
1831  public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) {
1832  /* Set internal character encoding to ASCII */
1833  if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1834  $this->internal_encoding = mb_internal_encoding();
1835  mb_internal_encoding('ASCII');
1836  }
1837  $this->font_obj_ids = array();
1838  $this->page_obj_id = array();
1839  $this->form_obj_id = array();
1840  // set pdf/a mode
1841  $this->pdfa_mode = $pdfa;
1842  $this->force_srgb = false;
1843  // set disk caching
1844  $this->diskcache = $diskcache ? true : false;
1845  // set language direction
1846  $this->rtl = false;
1847  $this->tmprtl = false;
1848  // some checks
1849  $this->_dochecks();
1850  // initialization of properties
1851  $this->isunicode = $unicode;
1852  $this->page = 0;
1853  $this->transfmrk[0] = array();
1854  $this->pagedim = array();
1855  $this->n = 2;
1856  $this->buffer = '';
1857  $this->pages = array();
1858  $this->state = 0;
1859  $this->fonts = array();
1860  $this->FontFiles = array();
1861  $this->diffs = array();
1862  $this->images = array();
1863  $this->links = array();
1864  $this->gradients = array();
1865  $this->InFooter = false;
1866  $this->lasth = 0;
1867  $this->FontFamily = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
1868  $this->FontStyle = '';
1869  $this->FontSizePt = 12;
1870  $this->underline = false;
1871  $this->overline = false;
1872  $this->linethrough = false;
1873  $this->DrawColor = '0 G';
1874  $this->FillColor = '0 g';
1875  $this->TextColor = '0 g';
1876  $this->ColorFlag = false;
1877  $this->pdflayers = array();
1878  // encryption values
1879  $this->encrypted = false;
1880  $this->last_enc_key = '';
1881  // standard Unicode fonts
1882  $this->CoreFonts = array(
1883  'courier'=>'Courier',
1884  'courierB'=>'Courier-Bold',
1885  'courierI'=>'Courier-Oblique',
1886  'courierBI'=>'Courier-BoldOblique',
1887  'helvetica'=>'Helvetica',
1888  'helveticaB'=>'Helvetica-Bold',
1889  'helveticaI'=>'Helvetica-Oblique',
1890  'helveticaBI'=>'Helvetica-BoldOblique',
1891  'times'=>'Times-Roman',
1892  'timesB'=>'Times-Bold',
1893  'timesI'=>'Times-Italic',
1894  'timesBI'=>'Times-BoldItalic',
1895  'symbol'=>'Symbol',
1896  'zapfdingbats'=>'ZapfDingbats'
1897  );
1898  // set scale factor
1899  $this->setPageUnit($unit);
1900  // set page format and orientation
1901  $this->setPageFormat($format, $orientation);
1902  // page margins (1 cm)
1903  $margin = 28.35 / $this->k;
1904  $this->SetMargins($margin, $margin);
1905  $this->clMargin = $this->lMargin;
1906  $this->crMargin = $this->rMargin;
1907  // internal cell padding
1908  $cpadding = $margin / 10;
1909  $this->setCellPaddings($cpadding, 0, $cpadding, 0);
1910  // cell margins
1911  $this->setCellMargins(0, 0, 0, 0);
1912  // line width (0.2 mm)
1913  $this->LineWidth = 0.57 / $this->k;
1914  $this->linestyleWidth = sprintf('%F w', ($this->LineWidth * $this->k));
1915  $this->linestyleCap = '0 J';
1916  $this->linestyleJoin = '0 j';
1917  $this->linestyleDash = '[] 0 d';
1918  // automatic page break
1919  $this->SetAutoPageBreak(true, (2 * $margin));
1920  // full width display mode
1921  $this->SetDisplayMode('fullwidth');
1922  // compression
1923  $this->SetCompression();
1924  // set default PDF version number
1925  $this->setPDFVersion();
1926  $this->tcpdflink = true;
1927  $this->encoding = $encoding;
1928  $this->HREF = array();
1929  $this->getFontsList();
1930  $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1931  $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0);
1932  $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1933  $this->extgstates = array();
1934  $this->setTextShadow();
1935  // user's rights
1936  $this->sign = false;
1937  $this->ur['enabled'] = false;
1938  $this->ur['document'] = '/FullSave';
1939  $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
1940  $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1941  $this->ur['signature'] = '/Modify';
1942  $this->ur['ef'] = '/Create/Delete/Modify/Import';
1943  $this->ur['formex'] = '';
1944  $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0', 'name' => 'Signature');
1945  $this->empty_signature_appearance = array();
1946  // set default JPEG quality
1947  $this->jpeg_quality = 75;
1948  // initialize some settings
1949  TCPDF_FONTS::utf8Bidi(array(''), '', false, $this->isunicode, $this->CurrentFont);
1950  // set default font
1951  $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
1952  $this->setHeaderFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt));
1953  $this->setFooterFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt));
1954  // check if PCRE Unicode support is enabled
1955  if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
1956  // PCRE unicode support is turned ON
1957  // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
1958  // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
1959  // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
1960  //$this->setSpacesRE('/[^\S\P{Z}\P{Lo}\xa0]/u');
1961  $this->setSpacesRE('/[^\S\P{Z}\xa0]/u');
1962  } else {
1963  // PCRE unicode support is turned OFF
1964  $this->setSpacesRE('/[^\S\xa0]/');
1965  }
1966  $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1967  // set file ID for trailer
1968  $serformat = (is_array($format) ? serialize($format) : $format);
1969  $this->file_id = md5(TCPDF_STATIC::getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding));
1970  // set document creation and modification timestamp
1971  $this->doc_creation_timestamp = time();
1972  $this->doc_modification_timestamp = $this->doc_creation_timestamp;
1973  // get default graphic vars
1974  $this->default_graphic_vars = $this->getGraphicVars();
1975  $this->header_xobj_autoreset = false;
1976  $this->custom_xmp = '';
1977  }
1978 
1984  public function __destruct() {
1985  // restore internal encoding
1986  if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1987  mb_internal_encoding($this->internal_encoding);
1988  }
1989  // unset all class variables
1990  $this->_destroy(true);
1991  }
1992 
1999  public function setPageUnit($unit) {
2000  $unit = strtolower($unit);
2001  //Set scale factor
2002  switch ($unit) {
2003  // points
2004  case 'px':
2005  case 'pt': {
2006  $this->k = 1;
2007  break;
2008  }
2009  // millimeters
2010  case 'mm': {
2011  $this->k = $this->dpi / 25.4;
2012  break;
2013  }
2014  // centimeters
2015  case 'cm': {
2016  $this->k = $this->dpi / 2.54;
2017  break;
2018  }
2019  // inches
2020  case 'in': {
2021  $this->k = $this->dpi;
2022  break;
2023  }
2024  // unsupported unit
2025  default : {
2026  $this->Error('Incorrect unit: '.$unit);
2027  break;
2028  }
2029  }
2030  $this->pdfunit = $unit;
2031  if (isset($this->CurOrientation)) {
2032  $this->setPageOrientation($this->CurOrientation);
2033  }
2034  }
2035 
2091  protected function setPageFormat($format, $orientation='P') {
2092  if (!empty($format) AND isset($this->pagedim[$this->page])) {
2093  // remove inherited values
2094  unset($this->pagedim[$this->page]);
2095  }
2096  if (is_string($format)) {
2097  // get page measures from format name
2098  $pf = TCPDF_STATIC::getPageSizeFromFormat($format);
2099  $this->fwPt = $pf[0];
2100  $this->fhPt = $pf[1];
2101  } else {
2102  // the boundaries of the physical medium on which the page shall be displayed or printed
2103  if (isset($format['MediaBox'])) {
2104  $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);
2105  $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k);
2106  $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k);
2107  } else {
2108  if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) {
2109  $pf = array(($format[0] * $this->k), ($format[1] * $this->k));
2110  } else {
2111  if (!isset($format['format'])) {
2112  // default value
2113  $format['format'] = 'A4';
2114  }
2115  $pf = TCPDF_STATIC::getPageSizeFromFormat($format['format']);
2116  }
2117  $this->fwPt = $pf[0];
2118  $this->fhPt = $pf[1];
2119  $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2120  }
2121  // the visible region of default user space
2122  if (isset($format['CropBox'])) {
2123  $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);
2124  }
2125  // the region to which the contents of the page shall be clipped when output in a production environment
2126  if (isset($format['BleedBox'])) {
2127  $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);
2128  }
2129  // the intended dimensions of the finished page after trimming
2130  if (isset($format['TrimBox'])) {
2131  $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);
2132  }
2133  // the page's meaningful content (including potential white space)
2134  if (isset($format['ArtBox'])) {
2135  $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);
2136  }
2137  // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
2138  if (isset($format['BoxColorInfo'])) {
2139  $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo'];
2140  }
2141  if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) {
2142  // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2143  $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']);
2144  }
2145  if (isset($format['PZ'])) {
2146  // The page's preferred zoom (magnification) factor
2147  $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']);
2148  }
2149  if (isset($format['trans'])) {
2150  // The style and duration of the visual transition to use when moving from another page to the given page during a presentation
2151  if (isset($format['trans']['Dur'])) {
2152  // The page's display duration
2153  $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']);
2154  }
2155  $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
2156  if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
2157  // The transition style that shall be used when moving to this page from another during a presentation
2158  $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S'];
2159  $valid_effect = array('Split', 'Blinds');
2160  $valid_vals = array('H', 'V');
2161  if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
2162  $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm'];
2163  }
2164  $valid_effect = array('Split', 'Box', 'Fly');
2165  $valid_vals = array('I', 'O');
2166  if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
2167  $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M'];
2168  }
2169  $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
2170  if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
2171  if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
2172  OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
2173  OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
2174  $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']);
2175  }
2176  }
2177  if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
2178  $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']);
2179  }
2180  if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
2181  $this->pagedim[$this->page]['trans']['B'] = 'true';
2182  }
2183  } else {
2184  $this->pagedim[$this->page]['trans']['S'] = 'R';
2185  }
2186  if (isset($format['trans']['D'])) {
2187  // The duration of the transition effect, in seconds
2188  $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']);
2189  } else {
2190  $this->pagedim[$this->page]['trans']['D'] = 1;
2191  }
2192  }
2193  }
2194  $this->setPageOrientation($orientation);
2195  }
2196 
2205  public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
2206  if (!isset($this->pagedim[$this->page]['MediaBox'])) {
2207  // the boundaries of the physical medium on which the page shall be displayed or printed
2208  $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2209  }
2210  if (!isset($this->pagedim[$this->page]['CropBox'])) {
2211  // the visible region of default user space
2212  $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);
2213  }
2214  if (!isset($this->pagedim[$this->page]['BleedBox'])) {
2215  // the region to which the contents of the page shall be clipped when output in a production environment
2216  $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);
2217  }
2218  if (!isset($this->pagedim[$this->page]['TrimBox'])) {
2219  // the intended dimensions of the finished page after trimming
2220  $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);
2221  }
2222  if (!isset($this->pagedim[$this->page]['ArtBox'])) {
2223  // the page's meaningful content (including potential white space)
2224  $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);
2225  }
2226  if (!isset($this->pagedim[$this->page]['Rotate'])) {
2227  // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2228  $this->pagedim[$this->page]['Rotate'] = 0;
2229  }
2230  if (!isset($this->pagedim[$this->page]['PZ'])) {
2231  // The page's preferred zoom (magnification) factor
2232  $this->pagedim[$this->page]['PZ'] = 1;
2233  }
2234  if ($this->fwPt > $this->fhPt) {
2235  // landscape
2236  $default_orientation = 'L';
2237  } else {
2238  // portrait
2239  $default_orientation = 'P';
2240  }
2241  $valid_orientations = array('P', 'L');
2242  if (empty($orientation)) {
2243  $orientation = $default_orientation;
2244  } else {
2245  $orientation = strtoupper($orientation{0});
2246  }
2247  if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
2248  $this->CurOrientation = $orientation;
2249  $this->wPt = $this->fhPt;
2250  $this->hPt = $this->fwPt;
2251  } else {
2252  $this->CurOrientation = $default_orientation;
2253  $this->wPt = $this->fwPt;
2254  $this->hPt = $this->fhPt;
2255  }
2256  if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
2257  // swap X and Y coordinates (change page orientation)
2258  $this->pagedim = TCPDF_STATIC::swapPageBoxCoordinates($this->page, $this->pagedim);
2259  }
2260  $this->w = ($this->wPt / $this->k);
2261  $this->h = ($this->hPt / $this->k);
2262  if (TCPDF_STATIC::empty_string($autopagebreak)) {
2263  if (isset($this->AutoPageBreak)) {
2264  $autopagebreak = $this->AutoPageBreak;
2265  } else {
2266  $autopagebreak = true;
2267  }
2268  }
2269  if (TCPDF_STATIC::empty_string($bottommargin)) {
2270  if (isset($this->bMargin)) {
2271  $bottommargin = $this->bMargin;
2272  } else {
2273  // default value = 2 cm
2274  $bottommargin = 2 * 28.35 / $this->k;
2275  }
2276  }
2277  $this->SetAutoPageBreak($autopagebreak, $bottommargin);
2278  // store page dimensions
2279  $this->pagedim[$this->page]['w'] = $this->wPt;
2280  $this->pagedim[$this->page]['h'] = $this->hPt;
2281  $this->pagedim[$this->page]['wk'] = $this->w;
2282  $this->pagedim[$this->page]['hk'] = $this->h;
2283  $this->pagedim[$this->page]['tm'] = $this->tMargin;
2284  $this->pagedim[$this->page]['bm'] = $bottommargin;
2285  $this->pagedim[$this->page]['lm'] = $this->lMargin;
2286  $this->pagedim[$this->page]['rm'] = $this->rMargin;
2287  $this->pagedim[$this->page]['pb'] = $autopagebreak;
2288  $this->pagedim[$this->page]['or'] = $this->CurOrientation;
2289  $this->pagedim[$this->page]['olm'] = $this->original_lMargin;
2290  $this->pagedim[$this->page]['orm'] = $this->original_rMargin;
2291  }
2292 
2310  public function setSpacesRE($re='/[^\S\xa0]/') {
2311  $this->re_spaces = $re;
2312  $re_parts = explode('/', $re);
2313  // get pattern parts
2314  $this->re_space = array();
2315  if (isset($re_parts[1]) AND !empty($re_parts[1])) {
2316  $this->re_space['p'] = $re_parts[1];
2317  } else {
2318  $this->re_space['p'] = '[\s]';
2319  }
2320  // set pattern modifiers
2321  if (isset($re_parts[2]) AND !empty($re_parts[2])) {
2322  $this->re_space['m'] = $re_parts[2];
2323  } else {
2324  $this->re_space['m'] = '';
2325  }
2326  }
2327 
2335  public function setRTL($enable, $resetx=true) {
2336  $enable = $enable ? true : false;
2337  $resetx = ($resetx AND ($enable != $this->rtl));
2338  $this->rtl = $enable;
2339  $this->tmprtl = false;
2340  if ($resetx) {
2341  $this->Ln(0);
2342  }
2343  }
2344 
2351  public function getRTL() {
2352  return $this->rtl;
2353  }
2354 
2361  public function setTempRTL($mode) {
2362  $newmode = false;
2363  switch (strtoupper($mode)) {
2364  case 'LTR':
2365  case 'L': {
2366  if ($this->rtl) {
2367  $newmode = 'L';
2368  }
2369  break;
2370  }
2371  case 'RTL':
2372  case 'R': {
2373  if (!$this->rtl) {
2374  $newmode = 'R';
2375  }
2376  break;
2377  }
2378  case false:
2379  default: {
2380  $newmode = false;
2381  break;
2382  }
2383  }
2384  $this->tmprtl = $newmode;
2385  }
2386 
2393  public function isRTLTextDir() {
2394  return ($this->rtl OR ($this->tmprtl == 'R'));
2395  }
2396 
2404  public function setLastH($h) {
2405  $this->lasth = $h;
2406  }
2407 
2414  public function getCellHeight($fontsize, $padding=TRUE) {
2415  $height = ($fontsize * $this->cell_height_ratio);
2416  if ($padding) {
2417  $height += ($this->cell_padding['T'] + $this->cell_padding['B']);
2418  }
2419  return round($height, 3);
2420  }
2421 
2427  public function resetLastH() {
2428  $this->lasth = $this->getCellHeight($this->FontSize);
2429  }
2430 
2437  public function getLastH() {
2438  return $this->lasth;
2439  }
2440 
2448  public function setImageScale($scale) {
2449  $this->imgscale = $scale;
2450  }
2451 
2459  public function getImageScale() {
2460  return $this->imgscale;
2461  }
2462 
2472  public function getPageDimensions($pagenum='') {
2473  if (empty($pagenum)) {
2474  $pagenum = $this->page;
2475  }
2476  return $this->pagedim[$pagenum];
2477  }
2478 
2488  public function getPageWidth($pagenum='') {
2489  if (empty($pagenum)) {
2490  return $this->w;
2491  }
2492  return $this->pagedim[$pagenum]['w'];
2493  }
2494 
2504  public function getPageHeight($pagenum='') {
2505  if (empty($pagenum)) {
2506  return $this->h;
2507  }
2508  return $this->pagedim[$pagenum]['h'];
2509  }
2510 
2520  public function getBreakMargin($pagenum='') {
2521  if (empty($pagenum)) {
2522  return $this->bMargin;
2523  }
2524  return $this->pagedim[$pagenum]['bm'];
2525  }
2526 
2534  public function getScaleFactor() {
2535  return $this->k;
2536  }
2537 
2548  public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
2549  //Set left, top and right margins
2550  $this->lMargin = $left;
2551  $this->tMargin = $top;
2552  if ($right == -1) {
2553  $right = $left;
2554  }
2555  $this->rMargin = $right;
2556  if ($keepmargins) {
2557  // overwrite original values
2558  $this->original_lMargin = $this->lMargin;
2559  $this->original_rMargin = $this->rMargin;
2560  }
2561  }
2562 
2570  public function SetLeftMargin($margin) {
2571  //Set left margin
2572  $this->lMargin = $margin;
2573  if (($this->page > 0) AND ($this->x < $margin)) {
2574  $this->x = $margin;
2575  }
2576  }
2577 
2585  public function SetTopMargin($margin) {
2586  //Set top margin
2587  $this->tMargin = $margin;
2588  if (($this->page > 0) AND ($this->y < $margin)) {
2589  $this->y = $margin;
2590  }
2591  }
2592 
2600  public function SetRightMargin($margin) {
2601  $this->rMargin = $margin;
2602  if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
2603  $this->x = $this->w - $margin;
2604  }
2605  }
2606 
2614  public function SetCellPadding($pad) {
2615  if ($pad >= 0) {
2616  $this->cell_padding['L'] = $pad;
2617  $this->cell_padding['T'] = $pad;
2618  $this->cell_padding['R'] = $pad;
2619  $this->cell_padding['B'] = $pad;
2620  }
2621  }
2622 
2633  public function setCellPaddings($left='', $top='', $right='', $bottom='') {
2634  if (($left !== '') AND ($left >= 0)) {
2635  $this->cell_padding['L'] = $left;
2636  }
2637  if (($top !== '') AND ($top >= 0)) {
2638  $this->cell_padding['T'] = $top;
2639  }
2640  if (($right !== '') AND ($right >= 0)) {
2641  $this->cell_padding['R'] = $right;
2642  }
2643  if (($bottom !== '') AND ($bottom >= 0)) {
2644  $this->cell_padding['B'] = $bottom;
2645  }
2646  }
2647 
2655  public function getCellPaddings() {
2656  return $this->cell_padding;
2657  }
2658 
2669  public function setCellMargins($left='', $top='', $right='', $bottom='') {
2670  if (($left !== '') AND ($left >= 0)) {
2671  $this->cell_margin['L'] = $left;
2672  }
2673  if (($top !== '') AND ($top >= 0)) {
2674  $this->cell_margin['T'] = $top;
2675  }
2676  if (($right !== '') AND ($right >= 0)) {
2677  $this->cell_margin['R'] = $right;
2678  }
2679  if (($bottom !== '') AND ($bottom >= 0)) {
2680  $this->cell_margin['B'] = $bottom;
2681  }
2682  }
2683 
2691  public function getCellMargins() {
2692  return $this->cell_margin;
2693  }
2694 
2702  protected function adjustCellPadding($brd=0) {
2703  if (empty($brd)) {
2704  return;
2705  }
2706  if (is_string($brd)) {
2707  // convert string to array
2708  $slen = strlen($brd);
2709  $newbrd = array();
2710  for ($i = 0; $i < $slen; ++$i) {
2711  $newbrd[$brd[$i]] = true;
2712  }
2713  $brd = $newbrd;
2714  } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) {
2715  $brd = array('LRTB' => true);
2716  }
2717  if (!is_array($brd)) {
2718  return;
2719  }
2720  // store current cell padding
2721  $cp = $this->cell_padding;
2722  // select border mode
2723  if (isset($brd['mode'])) {
2724  $mode = $brd['mode'];
2725  unset($brd['mode']);
2726  } else {
2727  $mode = 'normal';
2728  }
2729  // process borders
2730  foreach ($brd as $border => $style) {
2731  $line_width = $this->LineWidth;
2732  if (is_array($style) AND isset($style['width'])) {
2733  // get border width
2734  $line_width = $style['width'];
2735  }
2736  $adj = 0; // line width inside the cell
2737  switch ($mode) {
2738  case 'ext': {
2739  $adj = 0;
2740  break;
2741  }
2742  case 'int': {
2743  $adj = $line_width;
2744  break;
2745  }
2746  case 'normal':
2747  default: {
2748  $adj = ($line_width / 2);
2749  break;
2750  }
2751  }
2752  // correct internal cell padding if required to avoid overlap between text and lines
2753  if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) {
2754  $this->cell_padding['T'] = $adj;
2755  }
2756  if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) {
2757  $this->cell_padding['R'] = $adj;
2758  }
2759  if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) {
2760  $this->cell_padding['B'] = $adj;
2761  }
2762  if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) {
2763  $this->cell_padding['L'] = $adj;
2764  }
2765  }
2766  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']));
2767  }
2768 
2777  public function SetAutoPageBreak($auto, $margin=0) {
2778  $this->AutoPageBreak = $auto ? true : false;
2779  $this->bMargin = $margin;
2780  $this->PageBreakTrigger = $this->h - $margin;
2781  }
2782 
2789  public function getAutoPageBreak() {
2790  return $this->AutoPageBreak;
2791  }
2792 
2801  public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
2802  if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
2803  $this->ZoomMode = $zoom;
2804  } else {
2805  $this->Error('Incorrect zoom display mode: '.$zoom);
2806  }
2807  $this->LayoutMode = TCPDF_STATIC::getPageLayoutMode($layout);
2808  $this->PageMode = TCPDF_STATIC::getPageMode($mode);
2809  }
2810 
2818  public function SetCompression($compress=true) {
2819  if (function_exists('gzcompress')) {
2820  $this->compress = $compress ? true : false;
2821  } else {
2822  $this->compress = false;
2823  }
2824  }
2825 
2832  public function setSRGBmode($mode=false) {
2833  $this->force_srgb = $mode ? true : false;
2834  }
2835 
2843  public function SetDocInfoUnicode($unicode=true) {
2844  $this->docinfounicode = $unicode ? true : false;
2845  }
2846 
2854  public function SetTitle($title) {
2855  $this->title = $title;
2856  }
2857 
2865  public function SetSubject($subject) {
2866  $this->subject = $subject;
2867  }
2868 
2876  public function SetAuthor($author) {
2877  $this->author = $author;
2878  }
2879 
2887  public function SetKeywords($keywords) {
2888  $this->keywords = $keywords;
2889  }
2890 
2898  public function SetCreator($creator) {
2899  $this->creator = $creator;
2900  }
2901 
2908  public function Error($msg) {
2909  // unset all class variables
2910  $this->_destroy(true);
2911  if (defined('K_TCPDF_THROW_EXCEPTION_ERROR') AND !K_TCPDF_THROW_EXCEPTION_ERROR) {
2912  die('<strong>TCPDF ERROR: </strong>'.$msg);
2913  } else {
2914  throw new Exception('TCPDF ERROR: '.$msg);
2915  }
2916  }
2917 
2926  public function Open() {
2927  $this->state = 1;
2928  }
2929 
2938  public function Close() {
2939  if ($this->state == 3) {
2940  return;
2941  }
2942  if ($this->page == 0) {
2943  $this->AddPage();
2944  }
2945  $this->endLayer();
2946  if ($this->tcpdflink) {
2947  // save current graphic settings
2948  $gvars = $this->getGraphicVars();
2949  $this->setEqualColumns();
2950  $this->lastpage(true);
2951  $this->SetAutoPageBreak(false);
2952  $this->x = 0;
2953  $this->y = $this->h - (1 / $this->k);
2954  $this->lMargin = 0;
2955  $this->_outSaveGraphicsState();
2956  $font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
2957  $this->SetFont($font, '', 1);
2958  $this->setTextRenderingMode(0, false, false);
2959  $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";
2960  $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
2961  $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
2962  $this->_outRestoreGraphicsState();
2963  // restore graphic settings
2964  $this->setGraphicVars($gvars);
2965  }
2966  // close page
2967  $this->endPage();
2968  // close document
2969  $this->_enddoc();
2970  // unset all class variables (except critical ones)
2971  $this->_destroy(false);
2972  }
2973 
2982  public function setPage($pnum, $resetmargins=false) {
2983  if (($pnum == $this->page) AND ($this->state == 2)) {
2984  return;
2985  }
2986  if (($pnum > 0) AND ($pnum <= $this->numpages)) {
2987  $this->state = 2;
2988  // save current graphic settings
2989  //$gvars = $this->getGraphicVars();
2990  $oldpage = $this->page;
2991  $this->page = $pnum;
2992  $this->wPt = $this->pagedim[$this->page]['w'];
2993  $this->hPt = $this->pagedim[$this->page]['h'];
2994  $this->w = $this->pagedim[$this->page]['wk'];
2995  $this->h = $this->pagedim[$this->page]['hk'];
2996  $this->tMargin = $this->pagedim[$this->page]['tm'];
2997  $this->bMargin = $this->pagedim[$this->page]['bm'];
2998  $this->original_lMargin = $this->pagedim[$this->page]['olm'];
2999  $this->original_rMargin = $this->pagedim[$this->page]['orm'];
3000  $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
3001  $this->CurOrientation = $this->pagedim[$this->page]['or'];
3002  $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
3003  // restore graphic settings
3004  //$this->setGraphicVars($gvars);
3005  if ($resetmargins) {
3006  $this->lMargin = $this->pagedim[$this->page]['olm'];
3007  $this->rMargin = $this->pagedim[$this->page]['orm'];
3008  $this->SetY($this->tMargin);
3009  } else {
3010  // account for booklet mode
3011  if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3012  $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
3013  $this->lMargin += $deltam;
3014  $this->rMargin -= $deltam;
3015  }
3016  }
3017  } else {
3018  $this->Error('Wrong page number on setPage() function: '.$pnum);
3019  }
3020  }
3021 
3029  public function lastPage($resetmargins=false) {
3030  $this->setPage($this->getNumPages(), $resetmargins);
3031  }
3032 
3040  public function getPage() {
3041  return $this->page;
3042  }
3043 
3051  public function getNumPages() {
3052  return $this->numpages;
3053  }
3054 
3064  public function addTOCPage($orientation='', $format='', $keepmargins=false) {
3065  $this->AddPage($orientation, $format, $keepmargins, true);
3066  }
3067 
3074  public function endTOCPage() {
3075  $this->endPage(true);
3076  }
3077 
3089  public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
3090  if ($this->inxobj) {
3091  // we are inside an XObject template
3092  return;
3093  }
3094  if (!isset($this->original_lMargin) OR $keepmargins) {
3095  $this->original_lMargin = $this->lMargin;
3096  }
3097  if (!isset($this->original_rMargin) OR $keepmargins) {
3098  $this->original_rMargin = $this->rMargin;
3099  }
3100  // terminate previous page
3101  $this->endPage();
3102  // start new page
3103  $this->startPage($orientation, $format, $tocpage);
3104  }
3105 
3113  public function endPage($tocpage=false) {
3114  // check if page is already closed
3115  if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
3116  return;
3117  }
3118  // print page footer
3119  $this->setFooter();
3120  // close page
3121  $this->_endpage();
3122  // mark page as closed
3123  $this->pageopen[$this->page] = false;
3124  if ($tocpage) {
3125  $this->tocpage = false;
3126  }
3127  }
3128 
3139  public function startPage($orientation='', $format='', $tocpage=false) {
3140  if ($tocpage) {
3141  $this->tocpage = true;
3142  }
3143  // move page numbers of documents to be attached
3144  if ($this->tocpage) {
3145  // move reference to unexistent pages (used for page attachments)
3146  // adjust outlines
3147  $tmpoutlines = $this->outlines;
3148  foreach ($tmpoutlines as $key => $outline) {
3149  if ($outline['p'] > $this->numpages) {
3150  $this->outlines[$key]['p'] = ($outline['p'] + 1);
3151  }
3152  }
3153  // adjust dests
3154  $tmpdests = $this->dests;
3155  foreach ($tmpdests as $key => $dest) {
3156  if ($dest['p'] > $this->numpages) {
3157  $this->dests[$key]['p'] = ($dest['p'] + 1);
3158  }
3159  }
3160  // adjust links
3161  $tmplinks = $this->links;
3162  foreach ($tmplinks as $key => $link) {
3163  if ($link[0] > $this->numpages) {
3164  $this->links[$key][0] = ($link[0] + 1);
3165  }
3166  }
3167  }
3168  if ($this->numpages > $this->page) {
3169  // this page has been already added
3170  $this->setPage($this->page + 1);
3171  $this->SetY($this->tMargin);
3172  return;
3173  }
3174  // start a new page
3175  if ($this->state == 0) {
3176  $this->Open();
3177  }
3178  ++$this->numpages;
3179  $this->swapMargins($this->booklet);
3180  // save current graphic settings
3181  $gvars = $this->getGraphicVars();
3182  // start new page
3183  $this->_beginpage($orientation, $format);
3184  // mark page as open
3185  $this->pageopen[$this->page] = true;
3186  // restore graphic settings
3187  $this->setGraphicVars($gvars);
3188  // mark this point
3189  $this->setPageMark();
3190  // print page header
3191  $this->setHeader();
3192  // restore graphic settings
3193  $this->setGraphicVars($gvars);
3194  // mark this point
3195  $this->setPageMark();
3196  // print table header (if any)
3197  $this->setTableHeader();
3198  // set mark for empty page check
3199  $this->emptypagemrk[$this->page]= $this->pagelen[$this->page];
3200  }
3201 
3210  public function setPageMark() {
3211  $this->intmrk[$this->page] = $this->pagelen[$this->page];
3212  $this->bordermrk[$this->page] = $this->intmrk[$this->page];
3213  $this->setContentMark();
3214  }
3215 
3223  protected function setContentMark($page=0) {
3224  if ($page <= 0) {
3225  $page = $this->page;
3226  }
3227  if (isset($this->footerlen[$page])) {
3228  $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
3229  } else {
3230  $this->cntmrk[$page] = $this->pagelen[$page];
3231  }
3232  }
3233 
3244  public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) {
3245  $this->header_logo = $ln;
3246  $this->header_logo_width = $lw;
3247  $this->header_title = $ht;
3248  $this->header_string = $hs;
3249  $this->header_text_color = $tc;
3250  $this->header_line_color = $lc;
3251  }
3252 
3259  public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) {
3260  $this->footer_text_color = $tc;
3261  $this->footer_line_color = $lc;
3262  }
3263 
3271  public function getHeaderData() {
3272  $ret = array();
3273  $ret['logo'] = $this->header_logo;
3274  $ret['logo_width'] = $this->header_logo_width;
3275  $ret['title'] = $this->header_title;
3276  $ret['string'] = $this->header_string;
3277  $ret['text_color'] = $this->header_text_color;
3278  $ret['line_color'] = $this->header_line_color;
3279  return $ret;
3280  }
3281 
3288  public function setHeaderMargin($hm=10) {
3289  $this->header_margin = $hm;
3290  }
3291 
3298  public function getHeaderMargin() {
3299  return $this->header_margin;
3300  }
3301 
3308  public function setFooterMargin($fm=10) {
3309  $this->footer_margin = $fm;
3310  }
3311 
3318  public function getFooterMargin() {
3319  return $this->footer_margin;
3320  }
3326  public function setPrintHeader($val=true) {
3327  $this->print_header = $val ? true : false;
3328  }
3329 
3335  public function setPrintFooter($val=true) {
3336  $this->print_footer = $val ? true : false;
3337  }
3338 
3344  public function getImageRBX() {
3345  return $this->img_rb_x;
3346  }
3347 
3353  public function getImageRBY() {
3354  return $this->img_rb_y;
3355  }
3356 
3361  public function resetHeaderTemplate() {
3362  $this->header_xobjid = false;
3363  }
3364 
3370  public function setHeaderTemplateAutoreset($val=true) {
3371  $this->header_xobj_autoreset = $val ? true : false;
3372  }
3373 
3379  public function Header() {
3380  if ($this->header_xobjid === false) {
3381  // start a new XObject Template
3382  $this->header_xobjid = $this->startTemplate($this->w, $this->tMargin);
3383  $headerfont = $this->getHeaderFont();
3384  $headerdata = $this->getHeaderData();
3385  $this->y = $this->header_margin;
3386  if ($this->rtl) {
3387  $this->x = $this->w - $this->original_rMargin;
3388  } else {
3389  $this->x = $this->original_lMargin;
3390  }
3391  if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
3392  $imgtype = TCPDF_IMAGES::getImageFileType(K_PATH_IMAGES.$headerdata['logo']);
3393  if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
3394  $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3395  } elseif ($imgtype == 'svg') {
3396  $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3397  } else {
3398  $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3399  }
3400  $imgy = $this->getImageRBY();
3401  } else {
3402  $imgy = $this->y;
3403  }
3404  $cell_height = $this->getCellHeight($headerfont[2] / $this->k);
3405  // set starting margin for text data cell
3406  if ($this->getRTL()) {
3407  $header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1);
3408  } else {
3409  $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1);
3410  }
3411  $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1);
3412  $this->SetTextColorArray($this->header_text_color);
3413  // header title
3414  $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
3415  $this->SetX($header_x);
3416  $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
3417  // header string
3418  $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
3419  $this->SetX($header_x);
3420  $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false);
3421  // print an ending header line
3422  $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color']));
3423  $this->SetY((2.835 / $this->k) + max($imgy, $this->y));
3424  if ($this->rtl) {
3425  $this->SetX($this->original_rMargin);
3426  } else {
3427  $this->SetX($this->original_lMargin);
3428  }
3429  $this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C');
3430  $this->endTemplate();
3431  }
3432  // print header template
3433  $x = 0;
3434  $dx = 0;
3435  if (!$this->header_xobj_autoreset AND $this->booklet AND (($this->page % 2) == 0)) {
3436  // adjust margins for booklet mode
3437  $dx = ($this->original_lMargin - $this->original_rMargin);
3438  }
3439  if ($this->rtl) {
3440  $x = $this->w + $dx;
3441  } else {
3442  $x = 0 + $dx;
3443  }
3444  $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false);
3445  if ($this->header_xobj_autoreset) {
3446  // reset header xobject template at each page
3447  $this->header_xobjid = false;
3448  }
3449  }
3450 
3456  public function Footer() {
3457  $cur_y = $this->y;
3458  $this->SetTextColorArray($this->footer_text_color);
3459  //set style for cell border
3460  $line_width = (0.85 / $this->k);
3461  $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color));
3462  //print document barcode
3463  $barcode = $this->getBarcode();
3464  if (!empty($barcode)) {
3465  $this->Ln($line_width);
3466  $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3);
3467  $style = array(
3468  'position' => $this->rtl?'R':'L',
3469  'align' => $this->rtl?'R':'L',
3470  'stretch' => false,
3471  'fitwidth' => true,
3472  'cellfitalign' => '',
3473  'border' => false,
3474  'padding' => 0,
3475  'fgcolor' => array(0,0,0),
3476  'bgcolor' => false,
3477  'text' => false
3478  );
3479  $this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, '');
3480  }
3481  $w_page = isset($this->l['w_page']) ? $this->l['w_page'].' ' : '';
3482  if (empty($this->pagegroups)) {
3483  $pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
3484  } else {
3485  $pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
3486  }
3487  $this->SetY($cur_y);
3488  //Print page number
3489  if ($this->getRTL()) {
3490  $this->SetX($this->original_rMargin);
3491  $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
3492  } else {
3493  $this->SetX($this->original_lMargin);
3494  $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R');
3495  }
3496  }
3497 
3503  protected function setHeader() {
3504  if (!$this->print_header OR ($this->state != 2)) {
3505  return;
3506  }
3507  $this->InHeader = true;
3508  $this->setGraphicVars($this->default_graphic_vars);
3509  $temp_thead = $this->thead;
3510  $temp_theadMargins = $this->theadMargins;
3511  $lasth = $this->lasth;
3513  $this->_outSaveGraphicsState();
3514  $this->rMargin = $this->original_rMargin;
3515  $this->lMargin = $this->original_lMargin;
3516  $this->SetCellPadding(0);
3517  //set current position
3518  if ($this->rtl) {
3519  $this->SetXY($this->original_rMargin, $this->header_margin);
3520  } else {
3521  $this->SetXY($this->original_lMargin, $this->header_margin);
3522  }
3523  $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
3524  $this->Header();
3525  //restore position
3526  if ($this->rtl) {
3527  $this->SetXY($this->original_rMargin, $this->tMargin);
3528  } else {
3529  $this->SetXY($this->original_lMargin, $this->tMargin);
3530  }
3531  $this->_outRestoreGraphicsState();
3532  $this->lasth = $lasth;
3533  $this->thead = $temp_thead;
3534  $this->theadMargins = $temp_theadMargins;
3535  $this->newline = $newline;
3536  $this->InHeader = false;
3537  }
3538 
3544  protected function setFooter() {
3545  if ($this->state != 2) {
3546  return;
3547  }
3548  $this->InFooter = true;
3549  // save current graphic settings
3550  $gvars = $this->getGraphicVars();
3551  // mark this point
3552  $this->footerpos[$this->page] = $this->pagelen[$this->page];
3553  $this->_out("\n");
3554  if ($this->print_footer) {
3555  $this->setGraphicVars($this->default_graphic_vars);
3556  $this->current_column = 0;
3557  $this->num_columns = 1;
3558  $temp_thead = $this->thead;
3559  $temp_theadMargins = $this->theadMargins;
3560  $lasth = $this->lasth;
3561  $this->_outSaveGraphicsState();
3562  $this->rMargin = $this->original_rMargin;
3563  $this->lMargin = $this->original_lMargin;
3564  $this->SetCellPadding(0);
3565  //set current position
3566  $footer_y = $this->h - $this->footer_margin;
3567  if ($this->rtl) {
3568  $this->SetXY($this->original_rMargin, $footer_y);
3569  } else {
3570  $this->SetXY($this->original_lMargin, $footer_y);
3571  }
3572  $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
3573  $this->Footer();
3574  //restore position
3575  if ($this->rtl) {
3576  $this->SetXY($this->original_rMargin, $this->tMargin);
3577  } else {
3578  $this->SetXY($this->original_lMargin, $this->tMargin);
3579  }
3580  $this->_outRestoreGraphicsState();
3581  $this->lasth = $lasth;
3582  $this->thead = $temp_thead;
3583  $this->theadMargins = $temp_theadMargins;
3584  }
3585  // restore graphic settings
3586  $this->setGraphicVars($gvars);
3587  $this->current_column = $gvars['current_column'];
3588  $this->num_columns = $gvars['num_columns'];
3589  // calculate footer length
3590  $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
3591  $this->InFooter = false;
3592  }
3593 
3600  protected function inPageBody() {
3601  return (($this->InHeader === false) AND ($this->InFooter === false));
3602  }
3603 
3609  protected function setTableHeader() {
3610  if ($this->num_columns > 1) {
3611  // multi column mode
3612  return;
3613  }
3614  if (isset($this->theadMargins['top'])) {
3615  // restore the original top-margin
3616  $this->tMargin = $this->theadMargins['top'];
3617  $this->pagedim[$this->page]['tm'] = $this->tMargin;
3618  $this->y = $this->tMargin;
3619  }
3620  if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
3621  // set margins
3622  $prev_lMargin = $this->lMargin;
3623  $prev_rMargin = $this->rMargin;
3624  $prev_cell_padding = $this->cell_padding;
3625  $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']);
3626  $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']);
3627  $this->cell_padding = $this->theadMargins['cell_padding'];
3628  if ($this->rtl) {
3629  $this->x = $this->w - $this->rMargin;
3630  } else {
3631  $this->x = $this->lMargin;
3632  }
3633  // account for special "cell" mode
3634  if ($this->theadMargins['cell']) {
3635  if ($this->rtl) {
3636  $this->x -= $this->cell_padding['R'];
3637  } else {
3638  $this->x += $this->cell_padding['L'];
3639  }
3640  }
3641  $gvars = $this->getGraphicVars();
3642  if (!empty($this->theadMargins['gvars'])) {
3643  // set the correct graphic style
3644  $this->setGraphicVars($this->theadMargins['gvars']);
3645  $this->rMargin = $gvars['rMargin'];
3646  $this->lMargin = $gvars['lMargin'];
3647  }
3648  // print table header
3649  $this->writeHTML($this->thead, false, false, false, false, '');
3650  $this->setGraphicVars($gvars);
3651  // set new top margin to skip the table headers
3652  if (!isset($this->theadMargins['top'])) {
3653  $this->theadMargins['top'] = $this->tMargin;
3654  }
3655  // store end of header position
3656  if (!isset($this->columns[0]['th'])) {
3657  $this->columns[0]['th'] = array();
3658  }
3659  $this->columns[0]['th']['\''.$this->page.'\''] = $this->y;
3660  $this->tMargin = $this->y;
3661  $this->pagedim[$this->page]['tm'] = $this->tMargin;
3662  $this->lasth = 0;
3663  $this->lMargin = $prev_lMargin;
3664  $this->rMargin = $prev_rMargin;
3665  $this->cell_padding = $prev_cell_padding;
3666  }
3667  }
3668 
3676  public function PageNo() {
3677  return $this->page;
3678  }
3679 
3686  public function getAllSpotColors() {
3687  return $this->spot_colors;
3688  }
3689 
3703  public function AddSpotColor($name, $c, $m, $y, $k) {
3704  if (!isset($this->spot_colors[$name])) {
3705  $i = (1 + count($this->spot_colors));
3706  $this->spot_colors[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i);
3707  }
3708  }
3709 
3719  public function setSpotColor($type, $name, $tint=100) {
3720  $spotcolor = TCPDF_COLORS::getSpotColor($name, $this->spot_colors);
3721  if ($spotcolor === false) {
3722  $this->Error('Undefined spot color: '.$name.', you must add it using the AddSpotColor() method.');
3723  }
3724  $tint = (max(0, min(100, $tint)) / 100);
3725  $pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']);
3726  switch ($type) {
3727  case 'draw': {
3728  $pdfcolor .= sprintf('CS %F SCN', $tint);
3729  $this->DrawColor = $pdfcolor;
3730  $this->strokecolor = $spotcolor;
3731  break;
3732  }
3733  case 'fill': {
3734  $pdfcolor .= sprintf('cs %F scn', $tint);
3735  $this->FillColor = $pdfcolor;
3736  $this->bgcolor = $spotcolor;
3737  break;
3738  }
3739  case 'text': {
3740  $pdfcolor .= sprintf('cs %F scn', $tint);
3741  $this->TextColor = $pdfcolor;
3742  $this->fgcolor = $spotcolor;
3743  break;
3744  }
3745  }
3746  $this->ColorFlag = ($this->FillColor != $this->TextColor);
3747  if ($this->state == 2) {
3748  $this->_out($pdfcolor);
3749  }
3750  if ($this->inxobj) {
3751  // we are inside an XObject template
3752  $this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name];
3753  }
3754  return $pdfcolor;
3755  }
3756 
3765  public function SetDrawSpotColor($name, $tint=100) {
3766  $this->setSpotColor('draw', $name, $tint);
3767  }
3768 
3777  public function SetFillSpotColor($name, $tint=100) {
3778  $this->setSpotColor('fill', $name, $tint);
3779  }
3780 
3789  public function SetTextSpotColor($name, $tint=100) {
3790  $this->setSpotColor('text', $name, $tint);
3791  }
3792 
3804  public function setColorArray($type, $color, $ret=false) {
3805  if (is_array($color)) {
3806  $color = array_values($color);
3807  // component: grey, RGB red or CMYK cyan
3808  $c = isset($color[0]) ? $color[0] : -1;
3809  // component: RGB green or CMYK magenta
3810  $m = isset($color[1]) ? $color[1] : -1;
3811  // component: RGB blue or CMYK yellow
3812  $y = isset($color[2]) ? $color[2] : -1;
3813  // component: CMYK black
3814  $k = isset($color[3]) ? $color[3] : -1;
3815  // color name
3816  $name = isset($color[4]) ? $color[4] : '';
3817  if ($c >= 0) {
3818  return $this->setColor($type, $c, $m, $y, $k, $ret, $name);
3819  }
3820  }
3821  return '';
3822  }
3823 
3835  public function SetDrawColorArray($color, $ret=false) {
3836  return $this->setColorArray('draw', $color, $ret);
3837  }
3838 
3849  public function SetFillColorArray($color, $ret=false) {
3850  return $this->setColorArray('fill', $color, $ret);
3851  }
3852 
3862  public function SetTextColorArray($color, $ret=false) {
3863  return $this->setColorArray('text', $color, $ret);
3864  }
3865 
3879  public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3880  // set default values
3881  if (!is_numeric($col1)) {
3882  $col1 = 0;
3883  }
3884  if (!is_numeric($col2)) {
3885  $col2 = -1;
3886  }
3887  if (!is_numeric($col3)) {
3888  $col3 = -1;
3889  }
3890  if (!is_numeric($col4)) {
3891  $col4 = -1;
3892  }
3893  // set color by case
3894  $suffix = '';
3895  if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
3896  // Grey scale
3897  $col1 = max(0, min(255, $col1));
3898  $intcolor = array('G' => $col1);
3899  $pdfcolor = sprintf('%F ', ($col1 / 255));
3900  $suffix = 'g';
3901  } elseif ($col4 == -1) {
3902  // RGB
3903  $col1 = max(0, min(255, $col1));
3904  $col2 = max(0, min(255, $col2));
3905  $col3 = max(0, min(255, $col3));
3906  $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
3907  $pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255));
3908  $suffix = 'rg';
3909  } else {
3910  $col1 = max(0, min(100, $col1));
3911  $col2 = max(0, min(100, $col2));
3912  $col3 = max(0, min(100, $col3));
3913  $col4 = max(0, min(100, $col4));
3914  if (empty($name)) {
3915  // CMYK
3916  $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
3917  $pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
3918  $suffix = 'k';
3919  } else {
3920  // SPOT COLOR
3921  $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
3922  $this->AddSpotColor($name, $col1, $col2, $col3, $col4);
3923  $pdfcolor = $this->setSpotColor($type, $name, 100);
3924  }
3925  }
3926  switch ($type) {
3927  case 'draw': {
3928  $pdfcolor .= strtoupper($suffix);
3929  $this->DrawColor = $pdfcolor;
3930  $this->strokecolor = $intcolor;
3931  break;
3932  }
3933  case 'fill': {
3934  $pdfcolor .= $suffix;
3935  $this->FillColor = $pdfcolor;
3936  $this->bgcolor = $intcolor;
3937  break;
3938  }
3939  case 'text': {
3940  $pdfcolor .= $suffix;
3941  $this->TextColor = $pdfcolor;
3942  $this->fgcolor = $intcolor;
3943  break;
3944  }
3945  }
3946  $this->ColorFlag = ($this->FillColor != $this->TextColor);
3947  if (($type != 'text') AND ($this->state == 2)) {
3948  if (!$ret) {
3949  $this->_out($pdfcolor);
3950  }
3951  return $pdfcolor;
3952  }
3953  return '';
3954  }
3955 
3969  public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3970  return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name);
3971  }
3972 
3986  public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3987  return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name);
3988  }
3989 
4003  public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4004  return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name);
4005  }
4006 
4019  public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4020  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);
4021  }
4022 
4035  public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4036  // store current values
4037  if (!TCPDF_STATIC::empty_string($fontname)) {
4038  $prev_FontFamily = $this->FontFamily;
4039  $prev_FontStyle = $this->FontStyle;
4040  $prev_FontSizePt = $this->FontSizePt;
4041  $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false);
4042  }
4043  // convert UTF-8 array to Latin1 if required
4044  if ($this->isunicode AND (!$this->isUnicodeFont())) {
4046  }
4047  $w = 0; // total width
4048  $wa = array(); // array of characters widths
4049  foreach ($sa as $ck => $char) {
4050  // character width
4051  $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)]));
4052  $wa[] = $cw;
4053  $w += $cw;
4054  }
4055  // restore previous values
4056  if (!TCPDF_STATIC::empty_string($fontname)) {
4057  $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
4058  }
4059  if ($getarray) {
4060  return $wa;
4061  }
4062  return $w;
4063  }
4064 
4074  public function GetCharWidth($char, $notlast=true) {
4075  // get raw width
4076  $chw = $this->getRawCharWidth($char);
4077  if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) {
4078  // increase/decrease font spacing
4079  $chw += $this->font_spacing;
4080  }
4081  if ($this->font_stretching != 100) {
4082  // fixed stretching mode
4083  $chw *= ($this->font_stretching / 100);
4084  }
4085  return $chw;
4086  }
4087 
4096  public function getRawCharWidth($char) {
4097  if ($char == 173) {
4098  // SHY character will not be printed
4099  return (0);
4100  }
4101  if (isset($this->CurrentFont['cw'][$char])) {
4102  $w = $this->CurrentFont['cw'][$char];
4103  } elseif (isset($this->CurrentFont['dw'])) {
4104  // default width
4105  $w = $this->CurrentFont['dw'];
4106  } elseif (isset($this->CurrentFont['cw'][32])) {
4107  // default width
4108  $w = $this->CurrentFont['cw'][32];
4109  } else {
4110  $w = 600;
4111  }
4112  return $this->getAbsFontMeasure($w);
4113  }
4114 
4122  public function GetNumChars($s) {
4123  if ($this->isUnicodeFont()) {
4124  return count(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont));
4125  }
4126  return strlen($s);
4127  }
4128 
4134  protected function getFontsList() {
4135  if (($fontsdir = opendir(TCPDF_FONTS::_getfontpath())) !== false) {
4136  while (($file = readdir($fontsdir)) !== false) {
4137  if (substr($file, -4) == '.php') {
4138  array_push($this->fontlist, strtolower(basename($file, '.php')));
4139  }
4140  }
4141  closedir($fontsdir);
4142  }
4143  }
4144 
4153  public function unichr($c) {
4154  return TCPDF_FONTS::unichr($c, $this->isunicode);
4155  }
4156 
4173  public function addTTFfont($fontfile, $fonttype='', $enc='', $flags=32, $outpath='', $platid=3, $encid=1, $addcbbox=false) {
4174  return TCPDF_FONTS::addTTFfont($fontfile, $fonttype, $enc, $flags, $outpath, $platid, $encid, $addcbbox);
4175  }
4176 
4190  public function AddFont($family, $style='', $fontfile='', $subset='default') {
4191  if ($subset === 'default') {
4192  $subset = $this->font_subsetting;
4193  }
4194  if ($this->pdfa_mode) {
4195  $subset = false;
4196  }
4197  if (TCPDF_STATIC::empty_string($family)) {
4198  if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
4199  $family = $this->FontFamily;
4200  } else {
4201  $this->Error('Empty font family');
4202  }
4203  }
4204  // move embedded styles on $style
4205  if (substr($family, -1) == 'I') {
4206  $style .= 'I';
4207  $family = substr($family, 0, -1);
4208  }
4209  if (substr($family, -1) == 'B') {
4210  $style .= 'B';
4211  $family = substr($family, 0, -1);
4212  }
4213  // normalize family name
4214  $family = strtolower($family);
4215  if ((!$this->isunicode) AND ($family == 'arial')) {
4216  $family = 'helvetica';
4217  }
4218  if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
4219  $style = '';
4220  }
4221  if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) {
4222  // all fonts must be embedded
4223  $family = 'pdfa'.$family;
4224  }
4225  $tempstyle = strtoupper($style);
4226  $style = '';
4227  // underline
4228  if (strpos($tempstyle, 'U') !== false) {
4229  $this->underline = true;
4230  } else {
4231  $this->underline = false;
4232  }
4233  // line-through (deleted)
4234  if (strpos($tempstyle, 'D') !== false) {
4235  $this->linethrough = true;
4236  } else {
4237  $this->linethrough = false;
4238  }
4239  // overline
4240  if (strpos($tempstyle, 'O') !== false) {
4241  $this->overline = true;
4242  } else {
4243  $this->overline = false;
4244  }
4245  // bold
4246  if (strpos($tempstyle, 'B') !== false) {
4247  $style .= 'B';
4248  }
4249  // oblique
4250  if (strpos($tempstyle, 'I') !== false) {
4251  $style .= 'I';
4252  }
4253  $bistyle = $style;
4254  $fontkey = $family.$style;
4255  $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : '');
4256  $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
4257  // check if the font has been already added
4258  $fb = $this->getFontBuffer($fontkey);
4259  if ($fb !== false) {
4260  if ($this->inxobj) {
4261  // we are inside an XObject template
4262  $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i'];
4263  }
4264  return $fontdata;
4265  }
4266  // get specified font directory (if any)
4267  $fontdir = false;
4268  if (!TCPDF_STATIC::empty_string($fontfile)) {
4269  $fontdir = dirname($fontfile);
4270  if (TCPDF_STATIC::empty_string($fontdir) OR ($fontdir == '.')) {
4271  $fontdir = '';
4272  } else {
4273  $fontdir .= '/';
4274  }
4275  }
4276  // true when the font style variation is missing
4277  $missing_style = false;
4278  // search and include font file
4279  if (TCPDF_STATIC::empty_string($fontfile) OR (!@file_exists($fontfile))) {
4280  // build a standard filenames for specified font
4281  $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
4282  $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);
4283  if (TCPDF_STATIC::empty_string($fontfile)) {
4284  $missing_style = true;
4285  // try to remove the style part
4286  $tmp_fontfile = str_replace(' ', '', $family).'.php';
4287  $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);
4288  }
4289  }
4290  // include font file
4291  if (!TCPDF_STATIC::empty_string($fontfile) AND (@file_exists($fontfile))) {
4292  include($fontfile);
4293  } else {
4294  $this->Error('Could not include font definition file: '.$family.'');
4295  }
4296  // check font parameters
4297  if ((!isset($type)) OR (!isset($cw))) {
4298  $this->Error('The font definition file has a bad format: '.$fontfile.'');
4299  }
4300  // SET default parameters
4301  if (!isset($file) OR TCPDF_STATIC::empty_string($file)) {
4302  $file = '';
4303  }
4304  if (!isset($enc) OR TCPDF_STATIC::empty_string($enc)) {
4305  $enc = '';
4306  }
4307  if (!isset($cidinfo) OR TCPDF_STATIC::empty_string($cidinfo)) {
4308  $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0);
4309  $cidinfo['uni2cid'] = array();
4310  }
4311  if (!isset($ctg) OR TCPDF_STATIC::empty_string($ctg)) {
4312  $ctg = '';
4313  }
4314  if (!isset($desc) OR TCPDF_STATIC::empty_string($desc)) {
4315  $desc = array();
4316  }
4317  if (!isset($up) OR TCPDF_STATIC::empty_string($up)) {
4318  $up = -100;
4319  }
4320  if (!isset($ut) OR TCPDF_STATIC::empty_string($ut)) {
4321  $ut = 50;
4322  }
4323  if (!isset($cw) OR TCPDF_STATIC::empty_string($cw)) {
4324  $cw = array();
4325  }
4326  if (!isset($dw) OR TCPDF_STATIC::empty_string($dw)) {
4327  // set default width
4328  if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
4329  $dw = $desc['MissingWidth'];
4330  } elseif (isset($cw[32])) {
4331  $dw = $cw[32];
4332  } else {
4333  $dw = 600;
4334  }
4335  }
4336  ++$this->numfonts;
4337  if ($type == 'core') {
4338  $name = $this->CoreFonts[$fontkey];
4339  $subset = false;
4340  } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
4341  $subset = false;
4342  } elseif ($type == 'TrueTypeUnicode') {
4343  $enc = 'Identity-H';
4344  } elseif ($type == 'cidfont0') {
4345  if ($this->pdfa_mode) {
4346  $this->Error('All fonts must be embedded in PDF/A mode!');
4347  }
4348  } else {
4349  $this->Error('Unknow font type: '.$type.'');
4350  }
4351  // set name if unset
4352  if (!isset($name) OR empty($name)) {
4353  $name = $fontkey;
4354  }
4355  // create artificial font style variations if missing (only works with non-embedded fonts)
4356  if (($type != 'core') AND $missing_style) {
4357  // style variations
4358  $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
4359  $name .= $styles[$bistyle];
4360  // artificial bold
4361  if (strpos($bistyle, 'B') !== false) {
4362  if (isset($desc['StemV'])) {
4363  // from normal to bold
4364  $desc['StemV'] = round($desc['StemV'] * 1.75);
4365  } else {
4366  // bold
4367  $desc['StemV'] = 123;
4368  }
4369  }
4370  // artificial italic
4371  if (strpos($bistyle, 'I') !== false) {
4372  if (isset($desc['ItalicAngle'])) {
4373  $desc['ItalicAngle'] -= 11;
4374  } else {
4375  $desc['ItalicAngle'] = -11;
4376  }
4377  if (isset($desc['Flags'])) {
4378  $desc['Flags'] |= 64; //bit 7
4379  } else {
4380  $desc['Flags'] = 64;
4381  }
4382  }
4383  }
4384  // check if the array of characters bounding boxes is defined
4385  if (!isset($cbbox)) {
4386  $cbbox = array();
4387  }
4388  // initialize subsetchars
4389  $subsetchars = array_fill(0, 255, true);
4390  $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));
4391  if ($this->inxobj) {
4392  // we are inside an XObject template
4393  $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts;
4394  }
4395  if (isset($diff) AND (!empty($diff))) {
4396  //Search existing encodings
4397  $d = 0;
4398  $nb = count($this->diffs);
4399  for ($i=1; $i <= $nb; ++$i) {
4400  if ($this->diffs[$i] == $diff) {
4401  $d = $i;
4402  break;
4403  }
4404  }
4405  if ($d == 0) {
4406  $d = $nb + 1;
4407  $this->diffs[$d] = $diff;
4408  }
4409  $this->setFontSubBuffer($fontkey, 'diff', $d);
4410  }
4411  if (!TCPDF_STATIC::empty_string($file)) {
4412  if (!isset($this->FontFiles[$file])) {
4413  if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
4414  $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4415  } elseif ($type != 'core') {
4416  $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4417  }
4418  } else {
4419  // update fontkeys that are sharing this font file
4420  $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset);
4421  if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) {
4422  $this->FontFiles[$file]['fontkeys'][] = $fontkey;
4423  }
4424  }
4425  }
4426  return $fontdata;
4427  }
4428 
4446  public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) {
4447  //Select a font; size given in points
4448  if ($size === null) {
4449  $size = $this->FontSizePt;
4450  }
4451  if ($size < 0) {
4452  $size = 0;
4453  }
4454  // try to add font (if not already added)
4455  $fontdata = $this->AddFont($family, $style, $fontfile, $subset);
4456  $this->FontFamily = $fontdata['family'];
4457  $this->FontStyle = $fontdata['style'];
4458  if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
4459  // save subset chars of the previous font
4460  $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
4461  }
4462  $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
4463  $this->SetFontSize($size, $out);
4464  }
4465 
4474  public function SetFontSize($size, $out=true) {
4475  // font size in points
4476  $this->FontSizePt = $size;
4477  // font size in user units
4478  $this->FontSize = $size / $this->k;
4479  // calculate some font metrics
4480  if (isset($this->CurrentFont['desc']['FontBBox'])) {
4481  $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4482  $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
4483  } else {
4484  $font_height = $size * 1.219;
4485  }
4486  if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
4487  $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000);
4488  }
4489  if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) {
4490  $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000);
4491  }
4492  if (!isset($font_ascent) AND !isset($font_descent)) {
4493  // core font
4494  $font_ascent = 0.76 * $font_height;
4495  $font_descent = $font_height - $font_ascent;
4496  } elseif (!isset($font_descent)) {
4497  $font_descent = $font_height - $font_ascent;
4498  } elseif (!isset($font_ascent)) {
4499  $font_ascent = $font_height - $font_descent;
4500  }
4501  $this->FontAscent = ($font_ascent / $this->k);
4502  $this->FontDescent = ($font_descent / $this->k);
4503  if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i'])) AND ($this->state == 2)) {
4504  $this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4505  }
4506  }
4507 
4514  public function getFontBBox() {
4515  $fbbox = array();
4516  if (isset($this->CurrentFont['desc']['FontBBox'])) {
4517  $tmpbbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4518  $fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox);
4519  } else {
4520  // Find max width
4521  if (isset($this->CurrentFont['desc']['MaxWidth'])) {
4522  $maxw = $this->getAbsFontMeasure(intval($this->CurrentFont['desc']['MaxWidth']));
4523  } else {
4524  $maxw = 0;
4525  if (isset($this->CurrentFont['desc']['MissingWidth'])) {
4526  $maxw = max($maxw, $this->CurrentFont['desc']['MissingWidth']);
4527  }
4528  if (isset($this->CurrentFont['desc']['AvgWidth'])) {
4529  $maxw = max($maxw, $this->CurrentFont['desc']['AvgWidth']);
4530  }
4531  if (isset($this->CurrentFont['dw'])) {
4532  $maxw = max($maxw, $this->CurrentFont['dw']);
4533  }
4534  foreach ($this->CurrentFont['cw'] as $char => $w) {
4535  $maxw = max($maxw, $w);
4536  }
4537  if ($maxw == 0) {
4538  $maxw = 600;
4539  }
4540  $maxw = $this->getAbsFontMeasure($maxw);
4541  }
4542  $fbbox = array(0, (0 - $this->FontDescent), $maxw, $this->FontAscent);
4543  }
4544  return $fbbox;
4545  }
4546 
4553  public function getAbsFontMeasure($s) {
4554  return ($s * $this->FontSize / 1000);
4555  }
4556 
4563  public function getCharBBox($char) {
4564  if (isset($this->CurrentFont['cbbox'][$char])) {
4565  return array_map(array($this,'getAbsFontMeasure'), $this->CurrentFont['cbbox'][intval($char)]);
4566  }
4567  return false;
4568  }
4569 
4580  public function getFontDescent($font, $style='', $size=0) {
4581  $fontdata = $this->AddFont($font, $style);
4582  $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4583  if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
4584  $descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
4585  } else {
4586  $descent = (1.219 * 0.24 * $size);
4587  }
4588  return ($descent / $this->k);
4589  }
4590 
4601  public function getFontAscent($font, $style='', $size=0) {
4602  $fontdata = $this->AddFont($font, $style);
4603  $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4604  if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
4605  $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
4606  } else {
4607  $ascent = 1.219 * 0.76 * $size;
4608  }
4609  return ($ascent / $this->k);
4610  }
4611 
4621  public function isCharDefined($char, $font='', $style='') {
4622  if (is_string($char)) {
4623  // get character code
4624  $char = TCPDF_FONTS::UTF8StringToArray($char, $this->isunicode, $this->CurrentFont);
4625  $char = $char[0];
4626  }
4627  if (TCPDF_STATIC::empty_string($font)) {
4628  if (TCPDF_STATIC::empty_string($style)) {
4629  return (isset($this->CurrentFont['cw'][intval($char)]));
4630  }
4631  $font = $this->FontFamily;
4632  }
4633  $fontdata = $this->AddFont($font, $style);
4634  $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4635  return (isset($fontinfo['cw'][intval($char)]));
4636  }
4637 
4648  public function replaceMissingChars($text, $font='', $style='', $subs=array()) {
4649  if (empty($subs)) {
4650  return $text;
4651  }
4652  if (TCPDF_STATIC::empty_string($font)) {
4653  $font = $this->FontFamily;
4654  }
4655  $fontdata = $this->AddFont($font, $style);
4656  $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4657  $uniarr = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
4658  foreach ($uniarr as $k => $chr) {
4659  if (!isset($fontinfo['cw'][$chr])) {
4660  // this character is missing on the selected font
4661  if (isset($subs[$chr])) {
4662  // we have available substitutions
4663  if (is_array($subs[$chr])) {
4664  foreach($subs[$chr] as $s) {
4665  if (isset($fontinfo['cw'][$s])) {
4666  $uniarr[$k] = $s;
4667  break;
4668  }
4669  }
4670  } elseif (isset($fontinfo['cw'][$subs[$chr]])) {
4671  $uniarr[$k] = $subs[$chr];
4672  }
4673  }
4674  }
4675  }
4676  return TCPDF_FONTS::UniArrSubString(TCPDF_FONTS::UTF8ArrayToUniArray($uniarr, $this->isunicode));
4677  }
4678 
4685  public function SetDefaultMonospacedFont($font) {
4686  $this->default_monospaced_font = $font;
4687  }
4688 
4696  public function AddLink() {
4697  //Create a new internal link
4698  $n = count($this->links) + 1;
4699  $this->links[$n] = array(0, 0);
4700  return $n;
4701  }
4702 
4712  public function SetLink($link, $y=0, $page=-1) {
4713  if ($y == -1) {
4714  $y = $this->y;
4715  }
4716  if ($page == -1) {
4717  $page = $this->page;
4718  }
4719  $this->links[$link] = array($page, $y);
4720  }
4721 
4735  public function Link($x, $y, $w, $h, $link, $spaces=0) {
4736  $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
4737  }
4738 
4752  public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
4753  if ($this->inxobj) {
4754  // store parameters for later use on template
4755  $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
4756  return;
4757  }
4758  if ($x === '') {
4759  $x = $this->x;
4760  }
4761  if ($y === '') {
4762  $y = $this->y;
4763  }
4764  // check page for no-write regions and adapt page margins if necessary
4765  list($x, $y) = $this->checkPageRegions($h, $x, $y);
4766  // recalculate coordinates to account for graphic transformations
4767  if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) {
4768  for ($i=$this->transfmatrix_key; $i > 0; --$i) {
4769  $maxid = count($this->transfmatrix[$i]) - 1;
4770  for ($j=$maxid; $j >= 0; --$j) {
4771  $ctm = $this->transfmatrix[$i][$j];
4772  if (isset($ctm['a'])) {
4773  $x = $x * $this->k;
4774  $y = ($this->h - $y) * $this->k;
4775  $w = $w * $this->k;
4776  $h = $h * $this->k;
4777  // top left
4778  $xt = $x;
4779  $yt = $y;
4780  $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4781  $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4782  // top right
4783  $xt = $x + $w;
4784  $yt = $y;
4785  $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4786  $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4787  // bottom left
4788  $xt = $x;
4789  $yt = $y - $h;
4790  $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4791  $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4792  // bottom right
4793  $xt = $x + $w;
4794  $yt = $y - $h;
4795  $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4796  $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4797  // new coordinates (rectangle area)
4798  $x = min($x1, $x2, $x3, $x4);
4799  $y = max($y1, $y2, $y3, $y4);
4800  $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
4801  $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
4802  $x = $x / $this->k;
4803  $y = $this->h - ($y / $this->k);
4804  }
4805  }
4806  }
4807  }
4808  if ($this->page <= 0) {
4809  $page = 1;
4810  } else {
4811  $page = $this->page;
4812  }
4813  if (!isset($this->PageAnnots[$page])) {
4814  $this->PageAnnots[$page] = array();
4815  }
4816  $this->PageAnnots[$page][] = array('n' => ++$this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
4817  if (!$this->pdfa_mode) {
4818  if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!TCPDF_STATIC::empty_string($opt['FS']))
4819  AND (@file_exists($opt['FS']) OR TCPDF_STATIC::isValidURL($opt['FS']))
4820  AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
4821  $this->embeddedfiles[basename($opt['FS'])] = array('f' => ++$this->n, 'n' => ++$this->n, 'file' => $opt['FS']);
4822  }
4823  }
4824  // Add widgets annotation's icons
4825  if (isset($opt['mk']['i']) AND @file_exists($opt['mk']['i'])) {
4826  $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
4827  }
4828  if (isset($opt['mk']['ri']) AND @file_exists($opt['mk']['ri'])) {
4829  $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4830  }
4831  if (isset($opt['mk']['ix']) AND @file_exists($opt['mk']['ix'])) {
4832  $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4833  }
4834  }
4835 
4842  protected function _putEmbeddedFiles() {
4843  if ($this->pdfa_mode) {
4844  // embedded files are not allowed in PDF/A mode
4845  return;
4846  }
4847  reset($this->embeddedfiles);
4848  foreach ($this->embeddedfiles as $filename => $filedata) {
4849  $data = TCPDF_STATIC::fileGetContents($filedata['file']);
4850  if ($data !== FALSE) {
4851  $rawsize = strlen($data);
4852  if ($rawsize > 0) {
4853  // update name tree
4854  $this->efnames[$filename] = $filedata['f'].' 0 R';
4855  // embedded file specification object
4856  $out = $this->_getobj($filedata['f'])."\n";
4857  $out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']).' /EF <</F '.$filedata['n'].' 0 R>> >>';
4858  $out .= "\n".'endobj';
4859  $this->_out($out);
4860  // embedded file object
4861  $filter = '';
4862  if ($this->compress) {
4863  $data = gzcompress($data);
4864  $filter = ' /Filter /FlateDecode';
4865  }
4866  $stream = $this->_getrawstream($data, $filedata['n']);
4867  $out = $this->_getobj($filedata['n'])."\n";
4868  $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>';
4869  $out .= ' stream'."\n".$stream."\n".'endstream';
4870  $out .= "\n".'endobj';
4871  $this->_out($out);
4872  }
4873  }
4874  }
4875  }
4876 
4900  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) {
4903  $this->setTextRenderingMode($fstroke, $ffill, $fclip);
4904  $this->SetXY($x, $y, $rtloff);
4905  $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
4906  // restore previous rendering mode
4907  $this->textrendermode = $textrendermode;
4908  $this->textstrokewidth = $textstrokewidth;
4909  }
4910 
4920  public function AcceptPageBreak() {
4921  if ($this->num_columns > 1) {
4922  // multi column mode
4923  if ($this->current_column < ($this->num_columns - 1)) {
4924  // go to next column
4925  $this->selectColumn($this->current_column + 1);
4926  } elseif ($this->AutoPageBreak) {
4927  // add a new page
4928  $this->AddPage();
4929  // set first column
4930  $this->selectColumn(0);
4931  }
4932  // avoid page breaking from checkPageBreak()
4933  return false;
4934  }
4935  return $this->AutoPageBreak;
4936  }
4937 
4947  protected function checkPageBreak($h=0, $y='', $addpage=true) {
4949  $y = $this->y;
4950  }
4951  $current_page = $this->page;
4952  if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) {
4953  if ($addpage) {
4954  //Automatic page break
4955  $x = $this->x;
4956  $this->AddPage($this->CurOrientation);
4957  $this->y = $this->tMargin;
4958  $oldpage = $this->page - 1;
4959  if ($this->rtl) {
4960  if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
4961  $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
4962  } else {
4963  $this->x = $x;
4964  }
4965  } else {
4966  if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
4967  $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
4968  } else {
4969  $this->x = $x;
4970  }
4971  }
4972  }
4973  return true;
4974  }
4975  if ($current_page != $this->page) {
4976  // account for columns mode
4977  return true;
4978  }
4979  return false;
4980  }
4981 
5001  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') {
5002  $prev_cell_margin = $this->cell_margin;
5003  $prev_cell_padding = $this->cell_padding;
5004  $this->adjustCellPadding($border);
5005  if (!$ignore_min_height) {
5006  $min_cell_height = $this->getCellHeight($this->FontSize);
5007  if ($h < $min_cell_height) {
5008  $h = $min_cell_height;
5009  }
5010  }
5011  $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']);
5012  // apply text shadow if enabled
5013  if ($this->txtshadow['enabled']) {
5014  // save data
5015  $x = $this->x;
5016  $y = $this->y;
5017  $bc = $this->bgcolor;
5018  $fc = $this->fgcolor;
5019  $sc = $this->strokecolor;
5020  $alpha = $this->alpha;
5021  // print shadow
5022  $this->x += $this->txtshadow['depth_w'];
5023  $this->y += $this->txtshadow['depth_h'];
5024  $this->SetFillColorArray($this->txtshadow['color']);
5025  $this->SetTextColorArray($this->txtshadow['color']);
5026  $this->SetDrawColorArray($this->txtshadow['color']);
5027  if ($this->txtshadow['opacity'] != $alpha['CA']) {
5028  $this->setAlpha($this->txtshadow['opacity'], $this->txtshadow['blend_mode']);
5029  }
5030  if ($this->state == 2) {
5031  $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5032  }
5033  //restore data
5034  $this->x = $x;
5035  $this->y = $y;
5036  $this->SetFillColorArray($bc);
5037  $this->SetTextColorArray($fc);
5038  $this->SetDrawColorArray($sc);
5039  if ($this->txtshadow['opacity'] != $alpha['CA']) {
5040  $this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']);
5041  }
5042  }
5043  if ($this->state == 2) {
5044  $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5045  }
5046  $this->cell_padding = $prev_cell_padding;
5047  $this->cell_margin = $prev_cell_margin;
5048  }
5049 
5070  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') {
5071  // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space
5072  $txt = str_replace(TCPDF_FONTS::unichr(160, $this->isunicode), ' ', $txt);
5073  $prev_cell_margin = $this->cell_margin;
5074  $prev_cell_padding = $this->cell_padding;
5075  $txt = TCPDF_STATIC::removeSHY($txt, $this->isunicode);
5076  $rs = ''; //string to be returned
5077  $this->adjustCellPadding($border);
5078  if (!$ignore_min_height) {
5079  $min_cell_height = $this->getCellHeight($this->FontSize);
5080  if ($h < $min_cell_height) {
5081  $h = $min_cell_height;
5082  }
5083  }
5084  $k = $this->k;
5085  // check page for no-write regions and adapt page margins if necessary
5086  list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
5087  if ($this->rtl) {
5088  $x = $this->x - $this->cell_margin['R'];
5089  } else {
5090  $x = $this->x + $this->cell_margin['L'];
5091  }
5092  $y = $this->y + $this->cell_margin['T'];
5093  $prev_font_stretching = $this->font_stretching;
5094  $prev_font_spacing = $this->font_spacing;
5095  // cell vertical alignment
5096  switch ($calign) {
5097  case 'A': {
5098  // font top
5099  switch ($valign) {
5100  case 'T': {
5101  // top
5102  $y -= $this->cell_padding['T'];
5103  break;
5104  }
5105  case 'B': {
5106  // bottom
5107  $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent);
5108  break;
5109  }
5110  default:
5111  case 'C':
5112  case 'M': {
5113  // center
5114  $y -= (($h - $this->FontAscent - $this->FontDescent) / 2);
5115  break;
5116  }
5117  }
5118  break;
5119  }
5120  case 'L': {
5121  // font baseline
5122  switch ($valign) {
5123  case 'T': {
5124  // top
5125  $y -= ($this->cell_padding['T'] + $this->FontAscent);
5126  break;
5127  }
5128  case 'B': {
5129  // bottom
5130  $y -= ($h - $this->cell_padding['B'] - $this->FontDescent);
5131  break;
5132  }
5133  default:
5134  case 'C':
5135  case 'M': {
5136  // center
5137  $y -= (($h + $this->FontAscent - $this->FontDescent) / 2);
5138  break;
5139  }
5140  }
5141  break;
5142  }
5143  case 'D': {
5144  // font bottom
5145  switch ($valign) {
5146  case 'T': {
5147  // top
5148  $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent);
5149  break;
5150  }
5151  case 'B': {
5152  // bottom
5153  $y -= ($h - $this->cell_padding['B']);
5154  break;
5155  }
5156  default:
5157  case 'C':
5158  case 'M': {
5159  // center
5160  $y -= (($h + $this->FontAscent + $this->FontDescent) / 2);
5161  break;
5162  }
5163  }
5164  break;
5165  }
5166  case 'B': {
5167  // cell bottom
5168  $y -= $h;
5169  break;
5170  }
5171  case 'C':
5172  case 'M': {
5173  // cell center
5174  $y -= ($h / 2);
5175  break;
5176  }
5177  default:
5178  case 'T': {
5179  // cell top
5180  break;
5181  }
5182  }
5183  // text vertical alignment
5184  switch ($valign) {
5185  case 'T': {
5186  // top
5187  $yt = $y + $this->cell_padding['T'];
5188  break;
5189  }
5190  case 'B': {
5191  // bottom
5192  $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent;
5193  break;
5194  }
5195  default:
5196  case 'C':
5197  case 'M': {
5198  // center
5199  $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2);
5200  break;
5201  }
5202  }
5203  $basefonty = $yt + $this->FontAscent;
5204  if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5205  if ($this->rtl) {
5206  $w = $x - $this->lMargin;
5207  } else {
5208  $w = $this->w - $this->rMargin - $x;
5209  }
5210  }
5211  $s = '';
5212  // fill and borders
5213  if (is_string($border) AND (strlen($border) == 4)) {
5214  // full border
5215  $border = 1;
5216  }
5217  if ($fill OR ($border == 1)) {
5218  if ($fill) {
5219  $op = ($border == 1) ? 'B' : 'f';
5220  } else {
5221  $op = 'S';
5222  }
5223  if ($this->rtl) {
5224  $xk = (($x - $w) * $k);
5225  } else {
5226  $xk = ($x * $k);
5227  }
5228  $s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op);
5229  }
5230  // draw borders
5231  $s .= $this->getCellBorder($x, $y, $w, $h, $border);
5232  if ($txt != '') {
5233  $txt2 = $txt;
5234  if ($this->isunicode) {
5235  if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5236  $txt2 = TCPDF_FONTS::UTF8ToLatin1($txt2, $this->isunicode, $this->CurrentFont);
5237  } else {
5238  $unicode = TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont); // array of UTF-8 unicode values
5239  $unicode = TCPDF_FONTS::utf8Bidi($unicode, '', $this->tmprtl, $this->isunicode, $this->CurrentFont);
5240  // replace thai chars (if any)
5241  if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) {
5242  // number of chars
5243  $numchars = count($unicode);
5244  // po pla, for far, for fan
5245  $longtail = array(0x0e1b, 0x0e1d, 0x0e1f);
5246  // do chada, to patak
5247  $lowtail = array(0x0e0e, 0x0e0f);
5248  // mai hun arkad, sara i, sara ii, sara ue, sara uee
5249  $upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37);
5250  // mai ek, mai tho, mai tri, mai chattawa, karan
5251  $tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c);
5252  // sara u, sara uu, pinthu
5253  $lowvowel = array(0x0e38, 0x0e39, 0x0e3a);
5254  $output = array();
5255  for ($i = 0; $i < $numchars; $i++) {
5256  if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) {
5257  $ch0 = $unicode[$i];
5258  $ch1 = ($i > 0) ? $unicode[($i - 1)] : 0;
5259  $ch2 = ($i > 1) ? $unicode[($i - 2)] : 0;
5260  $chn = ($i < ($numchars - 1)) ? $unicode[($i + 1)] : 0;
5261  if (in_array($ch0, $tonemark)) {
5262  if ($chn == 0x0e33) {
5263  // sara um
5264  if (in_array($ch1, $longtail)) {
5265  // tonemark at upper left
5266  $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5267  } else {
5268  // tonemark at upper right (normal position)
5269  $output[] = $ch0;
5270  }
5271  } elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) {
5272  // tonemark at lower left
5273  $output[] = $this->replaceChar($ch0, (0xf705 + $ch0 - 0x0e48));
5274  } elseif (in_array($ch1, $upvowel)) {
5275  if (in_array($ch2, $longtail)) {
5276  // tonemark at upper left
5277  $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5278  } else {
5279  // tonemark at upper right (normal position)
5280  $output[] = $ch0;
5281  }
5282  } else {
5283  // tonemark at lower right
5284  $output[] = $this->replaceChar($ch0, (0xf70a + $ch0 - 0x0e48));
5285  }
5286  } elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) {
5287  // add lower left nikhahit and sara aa
5288  if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) {
5289  $output[] = 0xf711;
5290  $this->CurrentFont['subsetchars'][0xf711] = true;
5291  $output[] = 0x0e32;
5292  $this->CurrentFont['subsetchars'][0x0e32] = true;
5293  } else {
5294  $output[] = $ch0;
5295  }
5296  } elseif (in_array($ch1, $longtail)) {
5297  if ($ch0 == 0x0e31) {
5298  // lower left mai hun arkad
5299  $output[] = $this->replaceChar($ch0, 0xf710);
5300  } elseif (in_array($ch0, $upvowel)) {
5301  // lower left
5302  $output[] = $this->replaceChar($ch0, (0xf701 + $ch0 - 0x0e34));
5303  } elseif ($ch0 == 0x0e47) {
5304  // lower left mai tai koo
5305  $output[] = $this->replaceChar($ch0, 0xf712);
5306  } else {
5307  // normal character
5308  $output[] = $ch0;
5309  }
5310  } elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) {
5311  // lower vowel
5312  $output[] = $this->replaceChar($ch0, (0xf718 + $ch0 - 0x0e38));
5313  } elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) {
5314  // yo ying without lower part
5315  $output[] = $this->replaceChar($ch0, 0xf70f);
5316  } elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) {
5317  // tho santan without lower part
5318  $output[] = $this->replaceChar($ch0, 0xf700);
5319  } else {
5320  $output[] = $ch0;
5321  }
5322  } else {
5323  // non-thai character
5324  $output[] = $unicode[$i];
5325  }
5326  }
5327  $unicode = $output;
5328  // update font subsetchars
5329  $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
5330  } // end of K_THAI_TOPCHARS
5331  $txt2 = TCPDF_FONTS::arrUTF8ToUTF16BE($unicode, false);
5332  }
5333  }
5334  $txt2 = TCPDF_STATIC::_escape($txt2);
5335  // get current text width (considering general font stretching and spacing)
5336  $txwidth = $this->GetStringWidth($txt);
5337  $width = $txwidth;
5338  // check for stretch mode
5339  if ($stretch > 0) {
5340  // calculate ratio between cell width and text width
5341  if ($width <= 0) {
5342  $ratio = 1;
5343  } else {
5344  $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width);
5345  }
5346  // check if stretching is required
5347  if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) {
5348  // the text will be stretched to fit cell width
5349  if ($stretch > 2) {
5350  // set new character spacing
5351  $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100));
5352  } else {
5353  // set new horizontal stretching
5354  $this->font_stretching *= $ratio;
5355  }
5356  // recalculate text width (the text fills the entire cell)
5357  $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5358  // reset alignment
5359  $align = '';
5360  }
5361  }
5362  if ($this->font_stretching != 100) {
5363  // apply font stretching
5364  $rs .= sprintf('BT %F Tz ET ', $this->font_stretching);
5365  }
5366  if ($this->font_spacing != 0) {
5367  // increase/decrease font spacing
5368  $rs .= sprintf('BT %F Tc ET ', ($this->font_spacing * $this->k));
5369  }
5370  if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5371  $s .= 'q '.$this->TextColor.' ';
5372  }
5373  // rendering mode
5374  $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, ($this->textstrokewidth * $this->k));
5375  // count number of spaces
5376  $ns = substr_count($txt, chr(32));
5377  // Justification
5378  $spacewidth = 0;
5379  if (($align == 'J') AND ($ns > 0)) {
5380  if ($this->isUnicodeFont()) {
5381  // get string width without spaces
5382  $width = $this->GetStringWidth(str_replace(' ', '', $txt));
5383  // calculate average space width
5384  $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / ($this->FontSize?$this->FontSize:1);
5385  if ($this->font_stretching != 100) {
5386  // word spacing is affected by stretching
5387  $spacewidth /= ($this->font_stretching / 100);
5388  }
5389  // set word position to be used with TJ operator
5390  $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2);
5391  $unicode_justification = true;
5392  } else {
5393  // get string width
5394  $width = $txwidth;
5395  // new space width
5396  $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k;
5397  if ($this->font_stretching != 100) {
5398  // word spacing (Tw) is affected by stretching
5399  $spacewidth /= ($this->font_stretching / 100);
5400  }
5401  // set word spacing
5402  $rs .= sprintf('BT %F Tw ET ', $spacewidth);
5403  }
5404  $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5405  }
5406  // replace carriage return characters
5407  $txt2 = str_replace("\r", ' ', $txt2);
5408  switch ($align) {
5409  case 'C': {
5410  $dx = ($w - $width) / 2;
5411  break;
5412  }
5413  case 'R': {
5414  if ($this->rtl) {
5415  $dx = $this->cell_padding['R'];
5416  } else {
5417  $dx = $w - $width - $this->cell_padding['R'];
5418  }
5419  break;
5420  }
5421  case 'L': {
5422  if ($this->rtl) {
5423  $dx = $w - $width - $this->cell_padding['L'];
5424  } else {
5425  $dx = $this->cell_padding['L'];
5426  }
5427  break;
5428  }
5429  case 'J':
5430  default: {
5431  if ($this->rtl) {
5432  $dx = $this->cell_padding['R'];
5433  } else {
5434  $dx = $this->cell_padding['L'];
5435  }
5436  break;
5437  }
5438  }
5439  if ($this->rtl) {
5440  $xdx = $x - $dx - $width;
5441  } else {
5442  $xdx = $x + $dx;
5443  }
5444  $xdk = $xdx * $k;
5445  // print text
5446  $s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
5447  if (isset($uniblock)) {
5448  // print overlapping characters as separate string
5449  $xshift = 0; // horizontal shift
5450  $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k);
5451  $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1));
5452  foreach ($uniblock as $uk => $uniarr) {
5453  if (($uk % 2) == 0) {
5454  // x space to skip
5455  if ($spacewidth != 0) {
5456  // justification shift
5457  $xshift += (count(array_keys($uniarr, 32)) * $spw);
5458  }
5459  $xshift += $this->GetArrStringWidth($uniarr); // + shift justification
5460  } else {
5461  // character to print
5462  $topchr = TCPDF_FONTS::arrUTF8ToUTF16BE($uniarr, false);
5463  $topchr = TCPDF_STATIC::_escape($topchr);
5464  $s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr);
5465  }
5466  }
5467  }
5468  if ($this->underline) {
5469  $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
5470  }
5471  if ($this->linethrough) {
5472  $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
5473  }
5474  if ($this->overline) {
5475  $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
5476  }
5477  if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5478  $s .= ' Q';
5479  }
5480  if ($link) {
5481  $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns);
5482  }
5483  }
5484  // output cell
5485  if ($s) {
5486  // output cell
5487  $rs .= $s;
5488  if ($this->font_spacing != 0) {
5489  // reset font spacing mode
5490  $rs .= ' BT 0 Tc ET';
5491  }
5492  if ($this->font_stretching != 100) {
5493  // reset font stretching mode
5494  $rs .= ' BT 100 Tz ET';
5495  }
5496  }
5497  // reset word spacing
5498  if (!$this->isUnicodeFont() AND ($align == 'J')) {
5499  $rs .= ' BT 0 Tw ET';
5500  }
5501  // reset stretching and spacing
5502  $this->font_stretching = $prev_font_stretching;
5503  $this->font_spacing = $prev_font_spacing;
5504  $this->lasth = $h;
5505  if ($ln > 0) {
5506  //Go to the beginning of the next line
5507  $this->y = $y + $h + $this->cell_margin['B'];
5508  if ($ln == 1) {
5509  if ($this->rtl) {
5510  $this->x = $this->w - $this->rMargin;
5511  } else {
5512  $this->x = $this->lMargin;
5513  }
5514  }
5515  } else {
5516  // go left or right by case
5517  if ($this->rtl) {
5518  $this->x = $x - $w - $this->cell_margin['L'];
5519  } else {
5520  $this->x = $x + $w + $this->cell_margin['R'];
5521  }
5522  }
5523  $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
5524  $rs = $gstyles.$rs;
5525  $this->cell_padding = $prev_cell_padding;
5526  $this->cell_margin = $prev_cell_margin;
5527  return $rs;
5528  }
5529 
5538  protected function replaceChar($oldchar, $newchar) {
5539  if ($this->isCharDefined($newchar)) {
5540  // add the new char on the subset list
5541  $this->CurrentFont['subsetchars'][$newchar] = true;
5542  // return the new character
5543  return $newchar;
5544  }
5545  // return the old char
5546  return $oldchar;
5547  }
5548 
5561  protected function getCellBorder($x, $y, $w, $h, $brd) {
5562  $s = ''; // string to be returned
5563  if (empty($brd)) {
5564  return $s;
5565  }
5566  if ($brd == 1) {
5567  $brd = array('LRTB' => true);
5568  }
5569  // calculate coordinates for border
5570  $k = $this->k;
5571  if ($this->rtl) {
5572  $xeL = ($x - $w) * $k;
5573  $xeR = $x * $k;
5574  } else {
5575  $xeL = $x * $k;
5576  $xeR = ($x + $w) * $k;
5577  }
5578  $yeL = (($this->h - ($y + $h)) * $k);
5579  $yeT = (($this->h - $y) * $k);
5580  $xeT = $xeL;
5581  $xeB = $xeR;
5582  $yeR = $yeT;
5583  $yeB = $yeL;
5584  if (is_string($brd)) {
5585  // convert string to array
5586  $slen = strlen($brd);
5587  $newbrd = array();
5588  for ($i = 0; $i < $slen; ++$i) {
5589  $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
5590  }
5591  $brd = $newbrd;
5592  }
5593  if (isset($brd['mode'])) {
5594  $mode = $brd['mode'];
5595  unset($brd['mode']);
5596  } else {
5597  $mode = 'normal';
5598  }
5599  foreach ($brd as $border => $style) {
5600  if (is_array($style) AND !empty($style)) {
5601  // apply border style
5602  $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' ';
5603  $s .= $this->SetLineStyle($style, true)."\n";
5604  }
5605  switch ($mode) {
5606  case 'ext': {
5607  $off = (($this->LineWidth / 2) * $k);
5608  $xL = $xeL - $off;
5609  $xR = $xeR + $off;
5610  $yT = $yeT + $off;
5611  $yL = $yeL - $off;
5612  $xT = $xL;
5613  $xB = $xR;
5614  $yR = $yT;
5615  $yB = $yL;
5616  $w += $this->LineWidth;
5617  $h += $this->LineWidth;
5618  break;
5619  }
5620  case 'int': {
5621  $off = ($this->LineWidth / 2) * $k;
5622  $xL = $xeL + $off;
5623  $xR = $xeR - $off;
5624  $yT = $yeT - $off;
5625  $yL = $yeL + $off;
5626  $xT = $xL;
5627  $xB = $xR;
5628  $yR = $yT;
5629  $yB = $yL;
5630  $w -= $this->LineWidth;
5631  $h -= $this->LineWidth;
5632  break;
5633  }
5634  case 'normal':
5635  default: {
5636  $xL = $xeL;
5637  $xT = $xeT;
5638  $xB = $xeB;
5639  $xR = $xeR;
5640  $yL = $yeL;
5641  $yT = $yeT;
5642  $yB = $yeB;
5643  $yR = $yeR;
5644  break;
5645  }
5646  }
5647  // draw borders by case
5648  if (strlen($border) == 4) {
5649  $s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k));
5650  } elseif (strlen($border) == 3) {
5651  if (strpos($border,'B') === false) { // LTR
5652  $s .= sprintf('%F %F m ', $xL, $yL);
5653  $s .= sprintf('%F %F l ', $xT, $yT);
5654  $s .= sprintf('%F %F l ', $xR, $yR);
5655  $s .= sprintf('%F %F l ', $xB, $yB);
5656  $s .= 'S ';
5657  } elseif (strpos($border,'L') === false) { // TRB
5658  $s .= sprintf('%F %F m ', $xT, $yT);
5659  $s .= sprintf('%F %F l ', $xR, $yR);
5660  $s .= sprintf('%F %F l ', $xB, $yB);
5661  $s .= sprintf('%F %F l ', $xL, $yL);
5662  $s .= 'S ';
5663  } elseif (strpos($border,'T') === false) { // RBL
5664  $s .= sprintf('%F %F m ', $xR, $yR);
5665  $s .= sprintf('%F %F l ', $xB, $yB);
5666  $s .= sprintf('%F %F l ', $xL, $yL);
5667  $s .= sprintf('%F %F l ', $xT, $yT);
5668  $s .= 'S ';
5669  } elseif (strpos($border,'R') === false) { // BLT
5670  $s .= sprintf('%F %F m ', $xB, $yB);
5671  $s .= sprintf('%F %F l ', $xL, $yL);
5672  $s .= sprintf('%F %F l ', $xT, $yT);
5673  $s .= sprintf('%F %F l ', $xR, $yR);
5674  $s .= 'S ';
5675  }
5676  } elseif (strlen($border) == 2) {
5677  if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
5678  $s .= sprintf('%F %F m ', $xL, $yL);
5679  $s .= sprintf('%F %F l ', $xT, $yT);
5680  $s .= sprintf('%F %F l ', $xR, $yR);
5681  $s .= 'S ';
5682  } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
5683  $s .= sprintf('%F %F m ', $xT, $yT);
5684  $s .= sprintf('%F %F l ', $xR, $yR);
5685  $s .= sprintf('%F %F l ', $xB, $yB);
5686  $s .= 'S ';
5687  } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
5688  $s .= sprintf('%F %F m ', $xR, $yR);
5689  $s .= sprintf('%F %F l ', $xB, $yB);
5690  $s .= sprintf('%F %F l ', $xL, $yL);
5691  $s .= 'S ';
5692  } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
5693  $s .= sprintf('%F %F m ', $xB, $yB);
5694  $s .= sprintf('%F %F l ', $xL, $yL);
5695  $s .= sprintf('%F %F l ', $xT, $yT);
5696  $s .= 'S ';
5697  } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
5698  $s .= sprintf('%F %F m ', $xL, $yL);
5699  $s .= sprintf('%F %F l ', $xT, $yT);
5700  $s .= 'S ';
5701  $s .= sprintf('%F %F m ', $xR, $yR);
5702  $s .= sprintf('%F %F l ', $xB, $yB);
5703  $s .= 'S ';
5704  } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
5705  $s .= sprintf('%F %F m ', $xT, $yT);
5706  $s .= sprintf('%F %F l ', $xR, $yR);
5707  $s .= 'S ';
5708  $s .= sprintf('%F %F m ', $xB, $yB);
5709  $s .= sprintf('%F %F l ', $xL, $yL);
5710  $s .= 'S ';
5711  }
5712  } else { // strlen($border) == 1
5713  if (strpos($border,'L') !== false) { // L
5714  $s .= sprintf('%F %F m ', $xL, $yL);
5715  $s .= sprintf('%F %F l ', $xT, $yT);
5716  $s .= 'S ';
5717  } elseif (strpos($border,'T') !== false) { // T
5718  $s .= sprintf('%F %F m ', $xT, $yT);
5719  $s .= sprintf('%F %F l ', $xR, $yR);
5720  $s .= 'S ';
5721  } elseif (strpos($border,'R') !== false) { // R
5722  $s .= sprintf('%F %F m ', $xR, $yR);
5723  $s .= sprintf('%F %F l ', $xB, $yB);
5724  $s .= 'S ';
5725  } elseif (strpos($border,'B') !== false) { // B
5726  $s .= sprintf('%F %F m ', $xB, $yB);
5727  $s .= sprintf('%F %F l ', $xL, $yL);
5728  $s .= 'S ';
5729  }
5730  }
5731  if (is_array($style) AND !empty($style)) {
5732  // reset border style to previous value
5733  $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n";
5734  }
5735  }
5736  return $s;
5737  }
5738 
5764  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) {
5765  $prev_cell_margin = $this->cell_margin;
5766  $prev_cell_padding = $this->cell_padding;
5767  // adjust internal padding
5768  $this->adjustCellPadding($border);
5769  $mc_padding = $this->cell_padding;
5770  $mc_margin = $this->cell_margin;
5771  $this->cell_padding['T'] = 0;
5772  $this->cell_padding['B'] = 0;
5773  $this->setCellMargins(0, 0, 0, 0);
5774  if (TCPDF_STATIC::empty_string($this->lasth) OR $reseth) {
5775  // reset row height
5776  $this->resetLastH();
5777  }
5779  $this->SetY($y);
5780  } else {
5781  $y = $this->GetY();
5782  }
5783  $resth = 0;
5784  if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) {
5785  // spit cell in more pages/columns
5786  $newh = ($this->PageBreakTrigger - $y);
5787  $resth = ($h - $newh); // cell to be printed on the next page/column
5788  $h = $newh;
5789  }
5790  // get current page number
5791  $startpage = $this->page;
5792  // get current column
5793  $startcolumn = $this->current_column;
5795  $this->SetX($x);
5796  } else {
5797  $x = $this->GetX();
5798  }
5799  // check page for no-write regions and adapt page margins if necessary
5800  list($x, $y) = $this->checkPageRegions(0, $x, $y);
5801  // apply margins
5802  $oy = $y + $mc_margin['T'];
5803  if ($this->rtl) {
5804  $ox = ($this->w - $x - $mc_margin['R']);
5805  } else {
5806  $ox = ($x + $mc_margin['L']);
5807  }
5808  $this->x = $ox;
5809  $this->y = $oy;
5810  // set width
5811  if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5812  if ($this->rtl) {
5813  $w = ($this->x - $this->lMargin - $mc_margin['L']);
5814  } else {
5815  $w = ($this->w - $this->x - $this->rMargin - $mc_margin['R']);
5816  }
5817  }
5818  // store original margin values
5821  if ($this->rtl) {
5822  $this->rMargin = ($this->w - $this->x);
5823  $this->lMargin = ($this->x - $w);
5824  } else {
5825  $this->lMargin = ($this->x);
5826  $this->rMargin = ($this->w - $this->x - $w);
5827  }
5828  $this->clMargin = $this->lMargin;
5829  $this->crMargin = $this->rMargin;
5830  if ($autopadding) {
5831  // add top padding
5832  $this->y += $mc_padding['T'];
5833  }
5834  if ($ishtml) { // ******* Write HTML text
5835  $this->writeHTML($txt, true, false, $reseth, true, $align);
5836  $nl = 1;
5837  } else { // ******* Write simple text
5838  $prev_FontSizePt = $this->FontSizePt;
5839  // vertical alignment
5840  if ($maxh > 0) {
5841  // get text height
5842  $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5843  if ($fitcell) {
5844  // try to reduce font size to fit text on cell (use a quick search algorithm)
5845  $fmin = 1;
5846  $fmax = $this->FontSizePt;
5847  $prev_text_height = $text_height;
5848  $maxit = 100; // max number of iterations
5849  while ($maxit > 0) {
5850  $fmid = (($fmax + $fmin) / 2);
5851  $this->SetFontSize($fmid, false);
5852  $this->resetLastH();
5853  $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5854  if (($text_height == $maxh) OR (($text_height < $maxh) AND ($fmin >= ($fmax - 0.01)))) {
5855  break;
5856  } elseif ($text_height < $maxh) {
5857  $fmin = $fmid;
5858  } else {
5859  $fmax = $fmid;
5860  }
5861  --$maxit;
5862  }
5863  $this->SetFontSize($this->FontSizePt);
5864  }
5865  if ($text_height < $maxh) {
5866  if ($valign == 'M') {
5867  // text vertically centered
5868  $this->y += (($maxh - $text_height) / 2);
5869  } elseif ($valign == 'B') {
5870  // text vertically aligned on bottom
5871  $this->y += ($maxh - $text_height);
5872  }
5873  }
5874  }
5875  $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
5876  if ($fitcell) {
5877  // restore font size
5878  $this->SetFontSize($prev_FontSizePt);
5879  }
5880  }
5881  if ($autopadding) {
5882  // add bottom padding
5883  $this->y += $mc_padding['B'];
5884  }
5885  // Get end-of-text Y position
5886  $currentY = $this->y;
5887  // get latest page number
5888  $endpage = $this->page;
5889  if ($resth > 0) {
5890  $skip = ($endpage - $startpage);
5891  $tmpresth = $resth;
5892  while ($tmpresth > 0) {
5893  if ($skip <= 0) {
5894  // add a page (or trig AcceptPageBreak() for multicolumn mode)
5895  $this->checkPageBreak($this->PageBreakTrigger + 1);
5896  }
5897  if ($this->num_columns > 1) {
5898  $tmpresth -= ($this->h - $this->y - $this->bMargin);
5899  } else {
5900  $tmpresth -= ($this->h - $this->tMargin - $this->bMargin);
5901  }
5902  --$skip;
5903  }
5904  $currentY = $this->y;
5905  $endpage = $this->page;
5906  }
5907  // get latest column
5908  $endcolumn = $this->current_column;
5909  if ($this->num_columns == 0) {
5910  $this->num_columns = 1;
5911  }
5912  // disable page regions check
5914  $this->check_page_regions = false;
5915  // get border modes
5916  $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
5917  $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
5918  $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
5919  // design borders around HTML cells.
5920  for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
5921  $ccode = '';
5922  $this->setPage($page);
5923  if ($this->num_columns < 2) {
5924  // single-column mode
5925  $this->SetX($x);
5926  $this->y = $this->tMargin;
5927  }
5928  // account for margin changes
5929  if ($page > $startpage) {
5930  if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
5931  $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
5932  } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
5933  $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
5934  }
5935  }
5936  if ($startpage == $endpage) {
5937  // single page
5938  for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
5939  $this->selectColumn($column);
5940  if ($this->rtl) {
5941  $this->x -= $mc_margin['R'];
5942  } else {
5943  $this->x += $mc_margin['L'];
5944  }
5945  if ($startcolumn == $endcolumn) { // single column
5946  $cborder = $border;
5947  $h = max($h, ($currentY - $oy));
5948  $this->y = $oy;
5949  } elseif ($column == $startcolumn) { // first column
5950  $cborder = $border_start;
5951  $this->y = $oy;
5952  $h = $this->h - $this->y - $this->bMargin;
5953  } elseif ($column == $endcolumn) { // end column
5954  $cborder = $border_end;
5955  $h = $currentY - $this->y;
5956  if ($resth > $h) {
5957  $h = $resth;
5958  }
5959  } else { // middle column
5960  $cborder = $border_middle;
5961  $h = $this->h - $this->y - $this->bMargin;
5962  $resth -= $h;
5963  }
5964  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
5965  } // end for each column
5966  } elseif ($page == $startpage) { // first page
5967  for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
5968  $this->selectColumn($column);
5969  if ($this->rtl) {
5970  $this->x -= $mc_margin['R'];
5971  } else {
5972  $this->x += $mc_margin['L'];
5973  }
5974  if ($column == $startcolumn) { // first column
5975  $cborder = $border_start;
5976  $this->y = $oy;
5977  $h = $this->h - $this->y - $this->bMargin;
5978  } else { // middle column
5979  $cborder = $border_middle;
5980  $h = $this->h - $this->y - $this->bMargin;
5981  $resth -= $h;
5982  }
5983  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
5984  } // end for each column
5985  } elseif ($page == $endpage) { // last page
5986  for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
5987  $this->selectColumn($column);
5988  if ($this->rtl) {
5989  $this->x -= $mc_margin['R'];
5990  } else {
5991  $this->x += $mc_margin['L'];
5992  }
5993  if ($column == $endcolumn) {
5994  // end column
5995  $cborder = $border_end;
5996  $h = $currentY - $this->y;
5997  if ($resth > $h) {
5998  $h = $resth;
5999  }
6000  } else {
6001  // middle column
6002  $cborder = $border_middle;
6003  $h = $this->h - $this->y - $this->bMargin;
6004  $resth -= $h;
6005  }
6006  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6007  } // end for each column
6008  } else { // middle page
6009  for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
6010  $this->selectColumn($column);
6011  if ($this->rtl) {
6012  $this->x -= $mc_margin['R'];
6013  } else {
6014  $this->x += $mc_margin['L'];
6015  }
6016  $cborder = $border_middle;
6017  $h = $this->h - $this->y - $this->bMargin;
6018  $resth -= $h;
6019  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6020  } // end for each column
6021  }
6022  if ($cborder OR $fill) {
6023  $offsetlen = strlen($ccode);
6024  // draw border and fill
6025  if ($this->inxobj) {
6026  // we are inside an XObject template
6027  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
6028  $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
6029  $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
6030  $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
6031  } else {
6032  $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
6033  $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
6034  }
6035  $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
6036  $pstart = substr($pagebuff, 0, $pagemark);
6037  $pend = substr($pagebuff, $pagemark);
6038  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
6039  } else {
6040  if (end($this->transfmrk[$this->page]) !== false) {
6041  $pagemarkkey = key($this->transfmrk[$this->page]);
6042  $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
6043  $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
6044  } elseif ($this->InFooter) {
6045  $pagemark = $this->footerpos[$this->page];
6046  $this->footerpos[$this->page] += $offsetlen;
6047  } else {
6048  $pagemark = $this->intmrk[$this->page];
6049  $this->intmrk[$this->page] += $offsetlen;
6050  }
6051  $pagebuff = $this->getPageBuffer($this->page);
6052  $pstart = substr($pagebuff, 0, $pagemark);
6053  $pend = substr($pagebuff, $pagemark);
6054  $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
6055  }
6056  }
6057  } // end for each page
6058  // restore page regions check
6059  $this->check_page_regions = $check_page_regions;
6060  // Get end-of-cell Y position
6061  $currentY = $this->GetY();
6062  // restore previous values
6063  if ($this->num_columns > 1) {
6064  $this->selectColumn();
6065  } else {
6066  // restore original margins
6067  $this->lMargin = $lMargin;
6068  $this->rMargin = $rMargin;
6069  if ($this->page > $startpage) {
6070  // check for margin variations between pages (i.e. booklet mode)
6071  $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']);
6072  $dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']);
6073  if (($dl != 0) OR ($dr != 0)) {
6074  $this->lMargin += $dl;
6075  $this->rMargin += $dr;
6076  }
6077  }
6078  }
6079  if ($ln > 0) {
6080  //Go to the beginning of the next line
6081  $this->SetY($currentY + $mc_margin['B']);
6082  if ($ln == 2) {
6083  $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6084  }
6085  } else {
6086  // go left or right by case
6087  $this->setPage($startpage);
6088  $this->y = $y;
6089  $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6090  }
6091  $this->setContentMark();
6092  $this->cell_padding = $prev_cell_padding;
6093  $this->cell_margin = $prev_cell_margin;
6094  $this->clMargin = $this->lMargin;
6095  $this->crMargin = $this->rMargin;
6096  return $nl;
6097  }
6098 
6112  public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6113  if ($txt === NULL) {
6114  return 0;
6115  }
6116  if ($txt === '') {
6117  // empty string
6118  return 1;
6119  }
6120  // adjust internal padding
6121  $prev_cell_padding = $this->cell_padding;
6122  $prev_lasth = $this->lasth;
6123  if (is_array($cellpadding)) {
6124  $this->cell_padding = $cellpadding;
6125  }
6126  $this->adjustCellPadding($border);
6127  if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
6128  if ($this->rtl) {
6129  $w = $this->x - $this->lMargin;
6130  } else {
6131  $w = $this->w - $this->rMargin - $this->x;
6132  }
6133  }
6134  $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6135  if ($reseth) {
6136  // reset row height
6137  $this->resetLastH();
6138  }
6139  $lines = 1;
6140  $sum = 0;
6141  $chars = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont), $txt, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6142  $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
6143  $length = count($chars);
6144  $lastSeparator = -1;
6145  for ($i = 0; $i < $length; ++$i) {
6146  $c = $chars[$i];
6147  $charWidth = $charsWidth[$i];
6148  if (($c != 160)
6149  AND (($c == 173)
6150  OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6151  OR (($c == 45)
6152  AND ($i > 0) AND ($i < ($length - 1))
6153  AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i - 1)], $this->isunicode))
6154  AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6155  )
6156  )
6157  ) {
6158  $lastSeparator = $i;
6159  }
6160  if ((($sum + $charWidth) > $wmax) OR ($c == 10)) {
6161  ++$lines;
6162  if ($c == 10) {
6163  $lastSeparator = -1;
6164  $sum = 0;
6165  } elseif ($lastSeparator != -1) {
6166  $i = $lastSeparator;
6167  $lastSeparator = -1;
6168  $sum = 0;
6169  } else {
6170  $sum = $charWidth;
6171  }
6172  } else {
6173  $sum += $charWidth;
6174  }
6175  }
6176  if ($chars[($length - 1)] == 10) {
6177  --$lines;
6178  }
6179  $this->cell_padding = $prev_cell_padding;
6180  $this->lasth = $prev_lasth;
6181  return $lines;
6182  }
6183 
6231  public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6232  // adjust internal padding
6233  $prev_cell_padding = $this->cell_padding;
6234  $prev_lasth = $this->lasth;
6235  if (is_array($cellpadding)) {
6236  $this->cell_padding = $cellpadding;
6237  }
6238  $this->adjustCellPadding($border);
6239  $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
6240  $height = $this->getCellHeight(($lines * $this->FontSize), $autopadding);
6241  $this->cell_padding = $prev_cell_padding;
6242  $this->lasth = $prev_lasth;
6243  return $height;
6244  }
6245 
6264  public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
6265  // check page for no-write regions and adapt page margins if necessary
6266  list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
6267  if (strlen($txt) == 0) {
6268  // fix empty text
6269  $txt = ' ';
6270  }
6271  if ($margin === '') {
6272  // set default margins
6273  $margin = $this->cell_margin;
6274  }
6275  // remove carriage returns
6276  $s = str_replace("\r", '', $txt);
6277  // check if string contains arabic text
6278  if (preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_ARABIC, $s)) {
6279  $arabic = true;
6280  } else {
6281  $arabic = false;
6282  }
6283  // check if string contains RTL text
6284  if ($arabic OR ($this->tmprtl == 'R') OR preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $s)) {
6285  $rtlmode = true;
6286  } else {
6287  $rtlmode = false;
6288  }
6289  // get a char width
6290  $chrwidth = $this->GetCharWidth(46); // dot character
6291  // get array of unicode values
6292  $chars = TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont);
6293  // calculate maximum width for a single character on string
6294  $chrw = $this->GetArrStringWidth($chars, '', '', 0, true);
6295  array_walk($chrw, array($this, 'getRawCharWidth'));
6296  $maxchwidth = max($chrw);
6297  // get array of chars
6298  $uchars = TCPDF_FONTS::UTF8ArrayToUniArray($chars, $this->isunicode);
6299  // get the number of characters
6300  $nb = count($chars);
6301  // replacement for SHY character (minus symbol)
6302  $shy_replacement = 45;
6303  $shy_replacement_char = TCPDF_FONTS::unichr($shy_replacement, $this->isunicode);
6304  // widht for SHY replacement
6305  $shy_replacement_width = $this->GetCharWidth($shy_replacement);
6306  // max Y
6307  $maxy = $this->y + $maxh - $h - $this->cell_padding['T'] - $this->cell_padding['B'];
6308  // page width
6309  $pw = $w = $this->w - $this->lMargin - $this->rMargin;
6310  // calculate remaining line width ($w)
6311  if ($this->rtl) {
6312  $w = $this->x - $this->lMargin;
6313  } else {
6314  $w = $this->w - $this->rMargin - $this->x;
6315  }
6316  // max column width
6317  $wmax = ($w - $wadj);
6318  if (!$firstline) {
6319  $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']);
6320  }
6321  if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) {
6322  // the maximum width character do not fit on column
6323  return '';
6324  }
6325  // minimum row height
6326  $row_height = max($h, $this->getCellHeight($this->FontSize));
6327  $start_page = $this->page;
6328  $i = 0; // character position
6329  $j = 0; // current starting position
6330  $sep = -1; // position of the last blank space
6331  $shy = false; // true if the last blank is a soft hypen (SHY)
6332  $l = 0; // current string length
6333  $nl = 0; //number of lines
6334  $linebreak = false;
6335  $pc = 0; // previous character
6336  // for each character
6337  while ($i < $nb) {
6338  if (($maxh > 0) AND ($this->y >= $maxy) ) {
6339  break;
6340  }
6341  //Get the current character
6342  $c = $chars[$i];
6343  if ($c == 10) { // 10 = "\n" = new line
6344  //Explicit line break
6345  if ($align == 'J') {
6346  if ($this->rtl) {
6347  $talign = 'R';
6348  } else {
6349  $talign = 'L';
6350  }
6351  } else {
6352  $talign = $align;
6353  }
6354  $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6355  if ($firstline) {
6356  $startx = $this->x;
6357  $tmparr = array_slice($chars, $j, ($i - $j));
6358  if ($rtlmode) {
6359  $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6360  }
6361  $linew = $this->GetArrStringWidth($tmparr);
6362  unset($tmparr);
6363  if ($this->rtl) {
6364  $this->endlinex = $startx - $linew;
6365  } else {
6366  $this->endlinex = $startx + $linew;
6367  }
6368  $w = $linew;
6369  $tmpcellpadding = $this->cell_padding;
6370  if ($maxh == 0) {
6371  $this->SetCellPadding(0);
6372  }
6373  }
6374  if ($firstblock AND $this->isRTLTextDir()) {
6375  $tmpstr = $this->stringRightTrim($tmpstr);
6376  }
6377  // Skip newlines at the begining of a page or column
6378  if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) {
6379  $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
6380  }
6381  unset($tmpstr);
6382  if ($firstline) {
6383  $this->cell_padding = $tmpcellpadding;
6384  return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6385  }
6386  ++$nl;
6387  $j = $i + 1;
6388  $l = 0;
6389  $sep = -1;
6390  $shy = false;
6391  // account for margin changes
6392  if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6393  $this->AcceptPageBreak();
6394  if ($this->rtl) {
6395  $this->x -= $margin['R'];
6396  } else {
6397  $this->x += $margin['L'];
6398  }
6399  $this->lMargin += $margin['L'];
6400  $this->rMargin += $margin['R'];
6401  }
6402  $w = $this->getRemainingWidth();
6403  $wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']);
6404  } else {
6405  // 160 is the non-breaking space.
6406  // 173 is SHY (Soft Hypen).
6407  // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
6408  // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
6409  // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
6410  if (($c != 160)
6411  AND (($c == 173)
6412  OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6413  OR (($c == 45)
6414  AND ($i < ($nb - 1))
6415  AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($pc, $this->isunicode))
6416  AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6417  )
6418  )
6419  ) {
6420  // update last blank space position
6421  $sep = $i;
6422  // check if is a SHY
6423  if (($c == 173) OR ($c == 45)) {
6424  $shy = true;
6425  if ($pc == 45) {
6426  $tmp_shy_replacement_width = 0;
6427  $tmp_shy_replacement_char = '';
6428  } else {
6429  $tmp_shy_replacement_width = $shy_replacement_width;
6430  $tmp_shy_replacement_char = $shy_replacement_char;
6431  }
6432  } else {
6433  $shy = false;
6434  }
6435  }
6436  // update string length
6437  if ($this->isUnicodeFont() AND ($arabic)) {
6438  // with bidirectional algorithm some chars may be changed affecting the line length
6439  // *** very slow ***
6440  $l = $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl, $this->isunicode, $this->CurrentFont));
6441  } else {
6442  $l += $this->GetCharWidth($c);
6443  }
6444  if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) ) {
6445  // we have reached the end of column
6446  if ($sep == -1) {
6447  // check if the line was already started
6448  if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth)))
6449  OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) {
6450  // print a void cell and go to next line
6451  $this->Cell($w, $h, '', 0, 1);
6452  $linebreak = true;
6453  if ($firstline) {
6454  return (TCPDF_FONTS::UniArrSubString($uchars, $j));
6455  }
6456  } else {
6457  // truncate the word because do not fit on column
6458  $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6459  if ($firstline) {
6460  $startx = $this->x;
6461  $tmparr = array_slice($chars, $j, ($i - $j));
6462  if ($rtlmode) {
6463  $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6464  }
6465  $linew = $this->GetArrStringWidth($tmparr);
6466  unset($tmparr);
6467  if ($this->rtl) {
6468  $this->endlinex = $startx - $linew;
6469  } else {
6470  $this->endlinex = $startx + $linew;
6471  }
6472  $w = $linew;
6473  $tmpcellpadding = $this->cell_padding;
6474  if ($maxh == 0) {
6475  $this->SetCellPadding(0);
6476  }
6477  }
6478  if ($firstblock AND $this->isRTLTextDir()) {
6479  $tmpstr = $this->stringRightTrim($tmpstr);
6480  }
6481  $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6482  unset($tmpstr);
6483  if ($firstline) {
6484  $this->cell_padding = $tmpcellpadding;
6485  return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6486  }
6487  $j = $i;
6488  --$i;
6489  }
6490  } else {
6491  // word wrapping
6492  if ($this->rtl AND (!$firstblock) AND ($sep < $i)) {
6493  $endspace = 1;
6494  } else {
6495  $endspace = 0;
6496  }
6497  // check the length of the next string
6498  $strrest = TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace));
6499  $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $this->stringTrim($strrest));
6500  if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
6501  // truncate the word because do not fit on a full page width
6502  $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6503  if ($firstline) {
6504  $startx = $this->x;
6505  $tmparr = array_slice($chars, $j, ($i - $j));
6506  if ($rtlmode) {
6507  $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6508  }
6509  $linew = $this->GetArrStringWidth($tmparr);
6510  unset($tmparr);
6511  if ($this->rtl) {
6512  $this->endlinex = ($startx - $linew);
6513  } else {
6514  $this->endlinex = ($startx + $linew);
6515  }
6516  $w = $linew;
6517  $tmpcellpadding = $this->cell_padding;
6518  if ($maxh == 0) {
6519  $this->SetCellPadding(0);
6520  }
6521  }
6522  if ($firstblock AND $this->isRTLTextDir()) {
6523  $tmpstr = $this->stringRightTrim($tmpstr);
6524  }
6525  $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6526  unset($tmpstr);
6527  if ($firstline) {
6528  $this->cell_padding = $tmpcellpadding;
6529  return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6530  }
6531  $j = $i;
6532  --$i;
6533  } else {
6534  // word wrapping
6535  if ($shy) {
6536  // add hypen (minus symbol) at the end of the line
6537  $shy_width = $tmp_shy_replacement_width;
6538  if ($this->rtl) {
6539  $shy_char_left = $tmp_shy_replacement_char;
6540  $shy_char_right = '';
6541  } else {
6542  $shy_char_left = '';
6543  $shy_char_right = $tmp_shy_replacement_char;
6544  }
6545  } else {
6546  $shy_width = 0;
6547  $shy_char_left = '';
6548  $shy_char_right = '';
6549  }
6550  $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, ($sep + $endspace));
6551  if ($firstline) {
6552  $startx = $this->x;
6553  $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
6554  if ($rtlmode) {
6555  $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6556  }
6557  $linew = $this->GetArrStringWidth($tmparr);
6558  unset($tmparr);
6559  if ($this->rtl) {
6560  $this->endlinex = $startx - $linew - $shy_width;
6561  } else {
6562  $this->endlinex = $startx + $linew + $shy_width;
6563  }
6564  $w = $linew;
6565  $tmpcellpadding = $this->cell_padding;
6566  if ($maxh == 0) {
6567  $this->SetCellPadding(0);
6568  }
6569  }
6570  // print the line
6571  if ($firstblock AND $this->isRTLTextDir()) {
6572  $tmpstr = $this->stringRightTrim($tmpstr);
6573  }
6574  $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
6575  unset($tmpstr);
6576  if ($firstline) {
6577  if ($chars[$sep] == 45) {
6578  $endspace += 1;
6579  }
6580  // return the remaining text
6581  $this->cell_padding = $tmpcellpadding;
6582  return (TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace)));
6583  }
6584  $i = $sep;
6585  $sep = -1;
6586  $shy = false;
6587  $j = ($i + 1);
6588  }
6589  }
6590  // account for margin changes
6591  if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6592  $this->AcceptPageBreak();
6593  if ($this->rtl) {
6594  $this->x -= $margin['R'];
6595  } else {
6596  $this->x += $margin['L'];
6597  }
6598  $this->lMargin += $margin['L'];
6599  $this->rMargin += $margin['R'];
6600  }
6601  $w = $this->getRemainingWidth();
6602  $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6603  if ($linebreak) {
6604  $linebreak = false;
6605  } else {
6606  ++$nl;
6607  $l = 0;
6608  }
6609  }
6610  }
6611  // save last character
6612  $pc = $c;
6613  ++$i;
6614  } // end while i < nb
6615  // print last substring (if any)
6616  if ($l > 0) {
6617  switch ($align) {
6618  case 'J':
6619  case 'C': {
6620  $w = $w;
6621  break;
6622  }
6623  case 'L': {
6624  if ($this->rtl) {
6625  $w = $w;
6626  } else {
6627  $w = $l;
6628  }
6629  break;
6630  }
6631  case 'R': {
6632  if ($this->rtl) {
6633  $w = $l;
6634  } else {
6635  $w = $w;
6636  }
6637  break;
6638  }
6639  default: {
6640  $w = $l;
6641  break;
6642  }
6643  }
6644  $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $nb);
6645  if ($firstline) {
6646  $startx = $this->x;
6647  $tmparr = array_slice($chars, $j, ($nb - $j));
6648  if ($rtlmode) {
6649  $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6650  }
6651  $linew = $this->GetArrStringWidth($tmparr);
6652  unset($tmparr);
6653  if ($this->rtl) {
6654  $this->endlinex = $startx - $linew;
6655  } else {
6656  $this->endlinex = $startx + $linew;
6657  }
6658  $w = $linew;
6659  $tmpcellpadding = $this->cell_padding;
6660  if ($maxh == 0) {
6661  $this->SetCellPadding(0);
6662  }
6663  }
6664  if ($firstblock AND $this->isRTLTextDir()) {
6665  $tmpstr = $this->stringRightTrim($tmpstr);
6666  }
6667  $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
6668  unset($tmpstr);
6669  if ($firstline) {
6670  $this->cell_padding = $tmpcellpadding;
6671  return (TCPDF_FONTS::UniArrSubString($uchars, $nb));
6672  }
6673  ++$nl;
6674  }
6675  if ($firstline) {
6676  return '';
6677  }
6678  return $nl;
6679  }
6680 
6686  protected function getRemainingWidth() {
6687  list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y);
6688  if ($this->rtl) {
6689  return ($this->x - $this->lMargin);
6690  } else {
6691  return ($this->w - $this->rMargin - $this->x);
6692  }
6693  }
6694 
6706  protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
6707  if ($w <= 0) {
6708  // set maximum width
6709  $w = ($this->w - $this->lMargin - $this->rMargin);
6710  if ($w <= 0) {
6711  $w = 1;
6712  }
6713  }
6714  if ($h <= 0) {
6715  // set maximum height
6716  $h = ($this->PageBreakTrigger - $this->tMargin);
6717  if ($h <= 0) {
6718  $h = 1;
6719  }
6720  }
6721  // resize the block to be vertically contained on a single page or single column
6722  if ($fitonpage OR $this->AutoPageBreak) {
6723  $ratio_wh = ($w / $h);
6724  if ($h > ($this->PageBreakTrigger - $this->tMargin)) {
6725  $h = $this->PageBreakTrigger - $this->tMargin;
6726  $w = ($h * $ratio_wh);
6727  }
6728  // resize the block to be horizontally contained on a single page or single column
6729  if ($fitonpage) {
6730  $maxw = ($this->w - $this->lMargin - $this->rMargin);
6731  if ($w > $maxw) {
6732  $w = $maxw;
6733  $h = ($w / $ratio_wh);
6734  }
6735  }
6736  }
6737  // Check whether we need a new page or new column first as this does not fit
6738  $prev_x = $this->x;
6739  $prev_y = $this->y;
6740  if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) {
6741  $y = $this->y;
6742  if ($this->rtl) {
6743  $x += ($prev_x - $this->x);
6744  } else {
6745  $x += ($this->x - $prev_x);
6746  }
6747  $this->newline = true;
6748  }
6749  // resize the block to be contained on the remaining available page or column space
6750  if ($fitonpage) {
6751  $ratio_wh = ($w / $h);
6752  if (($y + $h) > $this->PageBreakTrigger) {
6753  $h = $this->PageBreakTrigger - $y;
6754  $w = ($h * $ratio_wh);
6755  }
6756  if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
6757  $w = $this->w - $this->rMargin - $x;
6758  $h = ($w / $ratio_wh);
6759  } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
6760  $w = $x - $this->lMargin;
6761  $h = ($w / $ratio_wh);
6762  }
6763  }
6764  return array($w, $h, $x, $y);
6765  }
6766 
6801  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()) {
6802  if ($this->state != 2) {
6803  return;
6804  }
6805  if ($x === '') {
6806  $x = $this->x;
6807  }
6808  if ($y === '') {
6809  $y = $this->y;
6810  }
6811  // check page for no-write regions and adapt page margins if necessary
6812  list($x, $y) = $this->checkPageRegions($h, $x, $y);
6813  $exurl = ''; // external streams
6814  $imsize = FALSE;
6815  // check if we are passing an image as file or string
6816  if ($file[0] === '@') {
6817  // image from string
6818  $imgdata = substr($file, 1);
6819  } else { // image file
6820  if ($file{0} === '*') {
6821  // image as external stream
6822  $file = substr($file, 1);
6823  $exurl = $file;
6824  }
6825  // check if is local file
6826  if (!@file_exists($file)) {
6827  // encode spaces on filename (file is probably an URL)
6828  $file = str_replace(' ', '%20', $file);
6829  }
6830  if (@file_exists($file)) {
6831  // get image dimensions
6832  $imsize = @getimagesize($file);
6833  }
6834  if ($imsize === FALSE) {
6835  $imgdata = TCPDF_STATIC::fileGetContents($file);
6836  }
6837  }
6838  if (isset($imgdata) AND ($imgdata !== FALSE) AND (strpos($file, '__tcpdf_img') === FALSE)) {
6839  // copy image to cache
6840  $original_file = $file;
6841  $file = TCPDF_STATIC::getObjFilename('img');
6842  $fp = fopen($file, 'w');
6843  fwrite($fp, $imgdata);
6844  fclose($fp);
6845  unset($imgdata);
6846  $imsize = @getimagesize($file);
6847  if ($imsize === FALSE) {
6848  unlink($file);
6849  $file = $original_file;
6850  } else {
6851  $this->cached_files[] = $file;
6852  }
6853  }
6854  if ($imsize === FALSE) {
6855  if (($w > 0) AND ($h > 0)) {
6856  // get measures from specified data
6857  $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6858  $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6859  $imsize = array($pw, $ph);
6860  } else {
6861  $this->Error('[Image] Unable to get the size of the image: '.$file);
6862  }
6863  }
6864  // file hash
6865  $filehash = md5($this->file_id.$file);
6866  // get original image width and height in pixels
6867  list($pixw, $pixh) = $imsize;
6868  // calculate image width and height on document
6869  if (($w <= 0) AND ($h <= 0)) {
6870  // convert image size to document unit
6871  $w = $this->pixelsToUnits($pixw);
6872  $h = $this->pixelsToUnits($pixh);
6873  } elseif ($w <= 0) {
6874  $w = $h * $pixw / $pixh;
6875  } elseif ($h <= 0) {
6876  $h = $w * $pixh / $pixw;
6877  } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
6878  if (strlen($fitbox) !== 2) {
6879  // set default alignment
6880  $fitbox = '--';
6881  }
6882  // scale image dimensions proportionally to fit within the ($w, $h) box
6883  if ((($w * $pixh) / ($h * $pixw)) < 1) {
6884  // store current height
6885  $oldh = $h;
6886  // calculate new height
6887  $h = $w * $pixh / $pixw;
6888  // height difference
6889  $hdiff = ($oldh - $h);
6890  // vertical alignment
6891  switch (strtoupper($fitbox{1})) {
6892  case 'T': {
6893  break;
6894  }
6895  case 'M': {
6896  $y += ($hdiff / 2);
6897  break;
6898  }
6899  case 'B': {
6900  $y += $hdiff;
6901  break;
6902  }
6903  }
6904  } else {
6905  // store current width
6906  $oldw = $w;
6907  // calculate new width
6908  $w = $h * $pixw / $pixh;
6909  // width difference
6910  $wdiff = ($oldw - $w);
6911  // horizontal alignment
6912  switch (strtoupper($fitbox{0})) {
6913  case 'L': {
6914  if ($this->rtl) {
6915  $x -= $wdiff;
6916  }
6917  break;
6918  }
6919  case 'C': {
6920  if ($this->rtl) {
6921  $x -= ($wdiff / 2);
6922  } else {
6923  $x += ($wdiff / 2);
6924  }
6925  break;
6926  }
6927  case 'R': {
6928  if (!$this->rtl) {
6929  $x += $wdiff;
6930  }
6931  break;
6932  }
6933  }
6934  }
6935  }
6936  // fit the image on available space
6937  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
6938  // calculate new minimum dimensions in pixels
6939  $neww = round($w * $this->k * $dpi / $this->dpi);
6940  $newh = round($h * $this->k * $dpi / $this->dpi);
6941  // check if resize is necessary (resize is used only to reduce the image)
6942  $newsize = ($neww * $newh);
6943  $pixsize = ($pixw * $pixh);
6944  if (intval($resize) == 2) {
6945  $resize = true;
6946  } elseif ($newsize >= $pixsize) {
6947  $resize = false;
6948  }
6949  // check if image has been already added on document
6950  $newimage = true;
6951  if (in_array($file, $this->imagekeys)) {
6952  $newimage = false;
6953  // get existing image data
6954  $info = $this->getImageBuffer($file);
6955  if (strpos($file, '__tcpdf_imgmask_') === FALSE) {
6956  // check if the newer image is larger
6957  $oldsize = ($info['w'] * $info['h']);
6958  if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
6959  $newimage = true;
6960  }
6961  }
6962  } elseif (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_imgmask_') === FALSE)) {
6963  // create temp image file (without alpha channel)
6964  $tempfile_plain = K_PATH_CACHE.'__tcpdf_imgmask_plain_'.$filehash;
6965  // create temp alpha file
6966  $tempfile_alpha = K_PATH_CACHE.'__tcpdf_imgmask_alpha_'.$filehash;
6967  // check for cached images
6968  if (in_array($tempfile_plain, $this->imagekeys)) {
6969  // get existing image data
6970  $info = $this->getImageBuffer($tempfile_plain);
6971  // check if the newer image is larger
6972  $oldsize = ($info['w'] * $info['h']);
6973  if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
6974  $newimage = true;
6975  } else {
6976  $newimage = false;
6977  // embed mask image
6978  $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
6979  // embed image, masked with previously embedded mask
6980  return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
6981  }
6982  }
6983  }
6984  if ($newimage) {
6985  //First use of image, get info
6986  $type = strtolower($type);
6987  if ($type == '') {
6988  $type = TCPDF_IMAGES::getImageFileType($file, $imsize);
6989  } elseif ($type == 'jpg') {
6990  $type = 'jpeg';
6991  }
6992  $mqr = TCPDF_STATIC::get_mqr();
6993  TCPDF_STATIC::set_mqr(false);
6994  // Specific image handlers (defined on TCPDF_IMAGES CLASS)
6995  $mtd = '_parse'.$type;
6996  // GD image handler function
6997  $gdfunction = 'imagecreatefrom'.$type;
6998  $info = false;
6999  if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
7000  // TCPDF image functions
7001  $info = TCPDF_IMAGES::$mtd($file);
7002  if (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_imgmask_') === FALSE)
7003  AND (($info === 'pngalpha') OR (isset($info['trns']) AND !empty($info['trns'])))) {
7004  return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
7005  }
7006  }
7007  if (($info === false) AND function_exists($gdfunction)) {
7008  try {
7009  // GD library
7010  $img = $gdfunction($file);
7011  if ($img !== false) {
7012  if ($resize) {
7013  $imgr = imagecreatetruecolor($neww, $newh);
7014  if (($type == 'gif') OR ($type == 'png')) {
7015  $imgr = TCPDF_IMAGES::setGDImageTransparency($imgr, $img);
7016  }
7017  imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
7018  if (($type == 'gif') OR ($type == 'png')) {
7019  $info = TCPDF_IMAGES::_toPNG($imgr);
7020  } else {
7021  $info = TCPDF_IMAGES::_toJPEG($imgr, $this->jpeg_quality);
7022  }
7023  } else {
7024  if (($type == 'gif') OR ($type == 'png')) {
7025  $info = TCPDF_IMAGES::_toPNG($img);
7026  } else {
7027  $info = TCPDF_IMAGES::_toJPEG($img, $this->jpeg_quality);
7028  }
7029  }
7030  }
7031  } catch(Exception $e) {
7032  $info = false;
7033  }
7034  }
7035  if (($info === false) AND extension_loaded('imagick')) {
7036  try {
7037  // ImageMagick library
7038  $img = new Imagick();
7039  if ($type == 'SVG') {
7040  // get SVG file content
7041  $svgimg = TCPDF_STATIC::fileGetContents($file);
7042  if ($svgimg !== FALSE) {
7043  // get width and height
7044  $regs = array();
7045  if (preg_match('/<svg([^>]*)>/si', $svgimg, $regs)) {
7046  $svgtag = $regs[1];
7047  $tmp = array();
7048  if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7049  $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7050  $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit;
7051  $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
7052  } else {
7053  $ow = $w;
7054  }
7055  $tmp = array();
7056  if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7057  $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7058  $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit;
7059  $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
7060  } else {
7061  $oh = $h;
7062  }
7063  $tmp = array();
7064  if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
7065  $vbw = ($ow * $this->imgscale * $this->k);
7066  $vbh = ($oh * $this->imgscale * $this->k);
7067  $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh);
7068  $svgtag = $vbox.$svgtag;
7069  }
7070  $svgimg = preg_replace('/<svg([^>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
7071  }
7072  $img->readImageBlob($svgimg);
7073  }
7074  } else {
7075  $img->readImage($file);
7076  }
7077  if ($resize) {
7078  $img->resizeImage($neww, $newh, 10, 1, false);
7079  }
7080  $img->setCompressionQuality($this->jpeg_quality);
7081  $img->setImageFormat('jpeg');
7082  $tempname = TCPDF_STATIC::getObjFilename('img');
7083  $img->writeImage($tempname);
7084  $info = TCPDF_IMAGES::_parsejpeg($tempname);
7085  unlink($tempname);
7086  $img->destroy();
7087  } catch(Exception $e) {
7088  $info = false;
7089  }
7090  }
7091  if ($info === false) {
7092  // unable to process image
7093  return;
7094  }
7095  TCPDF_STATIC::set_mqr($mqr);
7096  if ($ismask) {
7097  // force grayscale
7098  $info['cs'] = 'DeviceGray';
7099  }
7100  if ($imgmask !== false) {
7101  $info['masked'] = $imgmask;
7102  }
7103  if (!empty($exurl)) {
7104  $info['exurl'] = $exurl;
7105  }
7106  // array of alternative images
7107  $info['altimgs'] = $altimgs;
7108  // add image to document
7109  $info['i'] = $this->setImageBuffer($file, $info);
7110  }
7111  // set alignment
7112  $this->img_rb_y = $y + $h;
7113  // set alignment
7114  if ($this->rtl) {
7115  if ($palign == 'L') {
7116  $ximg = $this->lMargin;
7117  } elseif ($palign == 'C') {
7118  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7119  } elseif ($palign == 'R') {
7120  $ximg = $this->w - $this->rMargin - $w;
7121  } else {
7122  $ximg = $x - $w;
7123  }
7124  $this->img_rb_x = $ximg;
7125  } else {
7126  if ($palign == 'L') {
7127  $ximg = $this->lMargin;
7128  } elseif ($palign == 'C') {
7129  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7130  } elseif ($palign == 'R') {
7131  $ximg = $this->w - $this->rMargin - $w;
7132  } else {
7133  $ximg = $x;
7134  }
7135  $this->img_rb_x = $ximg + $w;
7136  }
7137  if ($ismask OR $hidden) {
7138  // image is not displayed
7139  return $info['i'];
7140  }
7141  $xkimg = $ximg * $this->k;
7142  if (!$alt) {
7143  // only non-alternative immages will be set
7144  $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']));
7145  }
7146  if (!empty($border)) {
7147  $bx = $this->x;
7148  $by = $this->y;
7149  $this->x = $ximg;
7150  if ($this->rtl) {
7151  $this->x += $w;
7152  }
7153  $this->y = $y;
7154  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
7155  $this->x = $bx;
7156  $this->y = $by;
7157  }
7158  if ($link) {
7159  $this->Link($ximg, $y, $w, $h, $link, 0);
7160  }
7161  // set pointer to align the next text/objects
7162  switch($align) {
7163  case 'T': {
7164  $this->y = $y;
7165  $this->x = $this->img_rb_x;
7166  break;
7167  }
7168  case 'M': {
7169  $this->y = $y + round($h/2);
7170  $this->x = $this->img_rb_x;
7171  break;
7172  }
7173  case 'B': {
7174  $this->y = $this->img_rb_y;
7175  $this->x = $this->img_rb_x;
7176  break;
7177  }
7178  case 'N': {
7179  $this->SetY($this->img_rb_y);
7180  break;
7181  }
7182  default:{
7183  break;
7184  }
7185  }
7186  $this->endlinex = $this->img_rb_x;
7187  if ($this->inxobj) {
7188  // we are inside an XObject template
7189  $this->xobjects[$this->xobjid]['images'][] = $info['i'];
7190  }
7191  return $info['i'];
7192  }
7193 
7215  protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
7216  // create temp images
7217  if (empty($filehash)) {
7218  $filehash = md5($this->file_id.$file);
7219  }
7220  // create temp image file (without alpha channel)
7221  $tempfile_plain = K_PATH_CACHE.'__tcpdf_imgmask_plain_'.$filehash;
7222  // create temp alpha file
7223  $tempfile_alpha = K_PATH_CACHE.'__tcpdf_imgmask_alpha_'.$filehash;
7224  $parsed = false;
7225  $parse_error = '';
7226  // ImageMagick extension
7227  if (($parsed === false) AND extension_loaded('imagick')) {
7228  try {
7229  // ImageMagick library
7230  $img = new Imagick();
7231  $img->readImage($file);
7232  // clone image object
7233  $imga = TCPDF_STATIC::objclone($img);
7234  // extract alpha channel
7235  if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) {
7236  $img->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT);
7237  } else {
7238  $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
7239  $img->negateImage(true);
7240  }
7241  $img->setImageFormat('png');
7242  $img->writeImage($tempfile_alpha);
7243  // remove alpha channel
7244  if (method_exists($imga, 'setImageMatte')) {
7245  $imga->setImageMatte(false);
7246  } else {
7247  $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
7248  }
7249  $imga->setImageFormat('png');
7250  $imga->writeImage($tempfile_plain);
7251  $parsed = true;
7252  } catch (Exception $e) {
7253  // Imagemagick fails, try with GD
7254  $parse_error = 'Imagick library error: '.$e->getMessage();
7255  }
7256  }
7257  // GD extension
7258  if (($parsed === false) AND function_exists('imagecreatefrompng')) {
7259  try {
7260  // generate images
7261  $img = imagecreatefrompng($file);
7262  $imgalpha = imagecreate($wpx, $hpx);
7263  // generate gray scale palette (0 -> 255)
7264  for ($c = 0; $c < 256; ++$c) {
7265  ImageColorAllocate($imgalpha, $c, $c, $c);
7266  }
7267  // extract alpha channel
7268  for ($xpx = 0; $xpx < $wpx; ++$xpx) {
7269  for ($ypx = 0; $ypx < $hpx; ++$ypx) {
7270  $color = imagecolorat($img, $xpx, $ypx);
7271  // get and correct gamma color
7272  $alpha = $this->getGDgamma($img, $color);
7273  imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
7274  }
7275  }
7276  imagepng($imgalpha, $tempfile_alpha);
7277  imagedestroy($imgalpha);
7278  // extract image without alpha channel
7279  $imgplain = imagecreatetruecolor($wpx, $hpx);
7280  imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
7281  imagepng($imgplain, $tempfile_plain);
7282  imagedestroy($imgplain);
7283  $parsed = true;
7284  } catch (Exception $e) {
7285  // GD fails
7286  $parse_error = 'GD library error: '.$e->getMessage();
7287  }
7288  }
7289  if ($parsed === false) {
7290  if (empty($parse_error)) {
7291  $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
7292  } else {
7293  $this->Error($parse_error);
7294  }
7295  }
7296  // embed mask image
7297  $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7298  // embed image, masked with previously embedded mask
7299  $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7300  // remove temp files
7301  unlink($tempfile_alpha);
7302  unlink($tempfile_plain);
7303  }
7304 
7312  protected function getGDgamma($img, $c) {
7313  if (!isset($this->gdgammacache['#'.$c])) {
7314  $colors = imagecolorsforindex($img, $c);
7315  // GD alpha is only 7 bit (0 -> 127)
7316  $this->gdgammacache['#'.$c] = (((127 - $colors['alpha']) / 127) * 255);
7317  // correct gamma
7318  $this->gdgammacache['#'.$c] = (pow(($this->gdgammacache['#'.$c] / 255), 2.2) * 255);
7319  // store the latest values on cache to improve performances
7320  if (count($this->gdgammacache) > 8) {
7321  // remove one element from the cache array
7322  array_shift($this->gdgammacache);
7323  }
7324  }
7325  return $this->gdgammacache['#'.$c];
7326  }
7327 
7337  public function Ln($h='', $cell=false) {
7338  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'])) {
7339  // revove vertical space from the top of the column
7340  return;
7341  }
7342  if ($cell) {
7343  if ($this->rtl) {
7344  $cellpadding = $this->cell_padding['R'];
7345  } else {
7346  $cellpadding = $this->cell_padding['L'];
7347  }
7348  } else {
7349  $cellpadding = 0;
7350  }
7351  if ($this->rtl) {
7352  $this->x = $this->w - $this->rMargin - $cellpadding;
7353  } else {
7354  $this->x = $this->lMargin + $cellpadding;
7355  }
7356  if (is_string($h)) {
7357  $this->y += $this->lasth;
7358  } else {
7359  $this->y += $h;
7360  }
7361  $this->newline = true;
7362  }
7363 
7372  public function GetX() {
7373  //Get x position
7374  if ($this->rtl) {
7375  return ($this->w - $this->x);
7376  } else {
7377  return $this->x;
7378  }
7379  }
7380 
7388  public function GetAbsX() {
7389  return $this->x;
7390  }
7391 
7399  public function GetY() {
7400  return $this->y;
7401  }
7402 
7412  public function SetX($x, $rtloff=false) {
7413  $x = floatval($x);
7414  if (!$rtloff AND $this->rtl) {
7415  if ($x >= 0) {
7416  $this->x = $this->w - $x;
7417  } else {
7418  $this->x = abs($x);
7419  }
7420  } else {
7421  if ($x >= 0) {
7422  $this->x = $x;
7423  } else {
7424  $this->x = $this->w + $x;
7425  }
7426  }
7427  if ($this->x < 0) {
7428  $this->x = 0;
7429  }
7430  if ($this->x > $this->w) {
7431  $this->x = $this->w;
7432  }
7433  }
7434 
7445  public function SetY($y, $resetx=true, $rtloff=false) {
7446  $y = floatval($y);
7447  if ($resetx) {
7448  //reset x
7449  if (!$rtloff AND $this->rtl) {
7450  $this->x = $this->w - $this->rMargin;
7451  } else {
7452  $this->x = $this->lMargin;
7453  }
7454  }
7455  if ($y >= 0) {
7456  $this->y = $y;
7457  } else {
7458  $this->y = $this->h + $y;
7459  }
7460  if ($this->y < 0) {
7461  $this->y = 0;
7462  }
7463  if ($this->y > $this->h) {
7464  $this->y = $this->h;
7465  }
7466  }
7467 
7478  public function SetXY($x, $y, $rtloff=false) {
7479  $this->SetY($y, false, $rtloff);
7480  $this->SetX($x, $rtloff);
7481  }
7482 
7490  public function SetAbsX($x) {
7491  $this->x = floatval($x);
7492  }
7493 
7501  public function SetAbsY($y) {
7502  $this->y = floatval($y);
7503  }
7504 
7513  public function SetAbsXY($x, $y) {
7514  $this->SetAbsX($x);
7515  $this->SetAbsY($y);
7516  }
7517 
7528  public function Output($name='doc.pdf', $dest='I') {
7529  //Output PDF to some destination
7530  //Finish document if necessary
7531  if ($this->state < 3) {
7532  $this->Close();
7533  }
7534  //Normalize parameters
7535  if (is_bool($dest)) {
7536  $dest = $dest ? 'D' : 'F';
7537  }
7538  $dest = strtoupper($dest);
7539  if ($dest{0} != 'F') {
7540  $name = preg_replace('/[\s]+/', '_', $name);
7541  $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
7542  }
7543  if ($this->sign) {
7544  // *** apply digital signature to the document ***
7545  // get the document content
7546  $pdfdoc = $this->getBuffer();
7547  // remove last newline
7548  $pdfdoc = substr($pdfdoc, 0, -1);
7549  // Remove the original buffer
7550  if (isset($this->diskcache) AND $this->diskcache) {
7551  // remove buffer file from cache
7552  unlink($this->buffer);
7553  }
7554  unset($this->buffer);
7555  // remove filler space
7556  $byterange_string_len = strlen(TCPDF_STATIC::$byterange_string);
7557  // define the ByteRange
7558  $byte_range = array();
7559  $byte_range[0] = 0;
7560  $byte_range[1] = strpos($pdfdoc, TCPDF_STATIC::$byterange_string) + $byterange_string_len + 10;
7561  $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
7562  $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
7563  $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
7564  // replace the ByteRange
7565  $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
7566  $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
7567  $pdfdoc = str_replace(TCPDF_STATIC::$byterange_string, $byterange, $pdfdoc);
7568  // write the document to a temporary folder
7569  $tempdoc = TCPDF_STATIC::getObjFilename('doc');
7570  $f = fopen($tempdoc, 'wb');
7571  if (!$f) {
7572  $this->Error('Unable to create temporary file: '.$tempdoc);
7573  }
7574  $pdfdoc_length = strlen($pdfdoc);
7575  fwrite($f, $pdfdoc, $pdfdoc_length);
7576  fclose($f);
7577  // get digital signature via openssl library
7578  $tempsign = TCPDF_STATIC::getObjFilename('sig');
7579  if (empty($this->signature_data['extracerts'])) {
7580  openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
7581  } else {
7582  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']);
7583  }
7584  unlink($tempdoc);
7585  // read signature
7586  $signature = file_get_contents($tempsign);
7587  unlink($tempsign);
7588  // extract signature
7589  $signature = substr($signature, $pdfdoc_length);
7590  $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
7591  $tmparr = explode("\n\n", $signature);
7592  $signature = $tmparr[1];
7593  unset($tmparr);
7594  // decode signature
7595  $signature = base64_decode(trim($signature));
7596  // convert signature to hex
7597  $signature = current(unpack('H*', $signature));
7598  $signature = str_pad($signature, $this->signature_max_length, '0');
7599  // disable disk caching
7600  $this->diskcache = false;
7601  // Add signature to the document
7602  $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
7603  $this->bufferlen = strlen($this->buffer);
7604  }
7605  switch($dest) {
7606  case 'I': {
7607  // Send PDF to the standard output
7608  if (ob_get_contents()) {
7609  $this->Error('Some data has already been output, can\'t send PDF file');
7610  }
7611  if (php_sapi_name() != 'cli') {
7612  // send output to a browser
7613  header('Content-Type: application/pdf');
7614  if (headers_sent()) {
7615  $this->Error('Some data has already been output to browser, can\'t send PDF file');
7616  }
7617  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7618  //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7619  header('Pragma: public');
7620  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7621  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7622  header('Content-Disposition: inline; filename="'.basename($name).'"');
7623  TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7624  } else {
7625  echo $this->getBuffer();
7626  }
7627  break;
7628  }
7629  case 'D': {
7630  // download PDF as file
7631  if (ob_get_contents()) {
7632  $this->Error('Some data has already been output, can\'t send PDF file');
7633  }
7634  header('Content-Description: File Transfer');
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  // force download dialog
7644  if (strpos(php_sapi_name(), 'cgi') === false) {
7645  header('Content-Type: application/force-download');
7646  header('Content-Type: application/octet-stream', false);
7647  header('Content-Type: application/download', false);
7648  header('Content-Type: application/pdf', false);
7649  } else {
7650  header('Content-Type: application/pdf');
7651  }
7652  // use the Content-Disposition header to supply a recommended filename
7653  header('Content-Disposition: attachment; filename="'.basename($name).'"');
7654  header('Content-Transfer-Encoding: binary');
7655  TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7656  break;
7657  }
7658  case 'F':
7659  case 'FI':
7660  case 'FD': {
7661  // save PDF to a local file
7662  if ($this->diskcache) {
7663  copy($this->buffer, $name);
7664  } else {
7665  $f = fopen($name, 'wb');
7666  if (!$f) {
7667  $this->Error('Unable to create output file: '.$name);
7668  }
7669  fwrite($f, $this->getBuffer(), $this->bufferlen);
7670  fclose($f);
7671  }
7672  if ($dest == 'FI') {
7673  // send headers to browser
7674  header('Content-Type: application/pdf');
7675  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7676  //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7677  header('Pragma: public');
7678  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7679  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7680  header('Content-Disposition: inline; filename="'.basename($name).'"');
7681  TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7682  } elseif ($dest == 'FD') {
7683  // send headers to browser
7684  if (ob_get_contents()) {
7685  $this->Error('Some data has already been output, can\'t send PDF file');
7686  }
7687  header('Content-Description: File Transfer');
7688  if (headers_sent()) {
7689  $this->Error('Some data has already been output to browser, can\'t send PDF file');
7690  }
7691  header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7692  header('Pragma: public');
7693  header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7694  header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7695  // force download dialog
7696  if (strpos(php_sapi_name(), 'cgi') === false) {
7697  header('Content-Type: application/force-download');
7698  header('Content-Type: application/octet-stream', false);
7699  header('Content-Type: application/download', false);
7700  header('Content-Type: application/pdf', false);
7701  } else {
7702  header('Content-Type: application/pdf');
7703  }
7704  // use the Content-Disposition header to supply a recommended filename
7705  header('Content-Disposition: attachment; filename="'.basename($name).'"');
7706  header('Content-Transfer-Encoding: binary');
7707  TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7708  }
7709  break;
7710  }
7711  case 'E': {
7712  // return PDF as base64 mime multi-part email attachment (RFC 2045)
7713  $retval = 'Content-Type: application/pdf;'."\r\n";
7714  $retval .= ' name="'.$name.'"'."\r\n";
7715  $retval .= 'Content-Transfer-Encoding: base64'."\r\n";
7716  $retval .= 'Content-Disposition: attachment;'."\r\n";
7717  $retval .= ' filename="'.$name.'"'."\r\n\r\n";
7718  $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
7719  return $retval;
7720  }
7721  case 'S': {
7722  // returns PDF as a string
7723  return $this->getBuffer();
7724  }
7725  default: {
7726  $this->Error('Incorrect output destination: '.$dest);
7727  }
7728  }
7729  return '';
7730  }
7731 
7739  public function _destroy($destroyall=false, $preserve_objcopy=false) {
7740  if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!TCPDF_STATIC::empty_string($this->buffer))) {
7741  // remove buffer file from cache
7742  unlink($this->buffer);
7743  }
7744  if ($destroyall AND !empty($this->cached_files)) {
7745  // remove cached files
7746  foreach ($this->cached_files as $cachefile) {
7747  if (is_file($cachefile)) {
7748  unlink($cachefile);
7749  }
7750  }
7751  unset($this->cached_files);
7752  }
7753  foreach (array_keys(get_object_vars($this)) as $val) {
7754  if ($destroyall OR (
7755  ($val != 'internal_encoding')
7756  AND ($val != 'state')
7757  AND ($val != 'bufferlen')
7758  AND ($val != 'buffer')
7759  AND ($val != 'diskcache')
7760  AND ($val != 'cached_files')
7761  AND ($val != 'sign')
7762  AND ($val != 'signature_data')
7763  AND ($val != 'signature_max_length')
7764  AND ($val != 'byterange_string')
7765  )) {
7766  if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) {
7767  unset($this->$val);
7768  }
7769  }
7770  }
7771  }
7772 
7777  protected function _dochecks() {
7778  //Check for locale-related bug
7779  if (1.1 == 1) {
7780  $this->Error('Don\'t alter the locale before including class file');
7781  }
7782  //Check for decimal separator
7783  if (sprintf('%.1F', 1.0) != '1.0') {
7784  setlocale(LC_NUMERIC, 'C');
7785  }
7786  }
7787 
7794  protected function getInternalPageNumberAliases($a= '') {
7795  $alias = array();
7796  // build array of Unicode + ASCII variants (the order is important)
7797  $alias = array('u' => array(), 'a' => array());
7798  $u = '{'.$a.'}';
7799  $alias['u'][] = TCPDF_STATIC::_escape($u);
7800  if ($this->isunicode) {
7801  $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($u, $this->isunicode, $this->CurrentFont));
7802  $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($u, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7803  $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($a, $this->isunicode, $this->CurrentFont));
7804  $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($a, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7805  }
7806  $alias['a'][] = TCPDF_STATIC::_escape($a);
7807  return $alias;
7808  }
7809 
7815  protected function getAllInternalPageNumberAliases() {
7817  $pnalias = array();
7818  foreach($basic_alias as $k => $a) {
7819  $pnalias[$k] = $this->getInternalPageNumberAliases($a);
7820  }
7821  return $pnalias;
7822  }
7823 
7833  protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
7834  foreach ($aliases as $type => $alias) {
7835  foreach ($alias as $a) {
7836  // find position of compensation factor
7837  $startnum = (strpos($a, ':') + 1);
7838  $a = substr($a, 0, $startnum);
7839  if (($pos = strpos($page, $a)) !== false) {
7840  // end of alias
7841  $endnum = strpos($page, '}', $pos);
7842  // string to be replaced
7843  $aa = substr($page, $pos, ($endnum - $pos + 1));
7844  // get compensation factor
7845  $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum));
7846  $ratio = preg_replace('/[^0-9\.]/', '', $ratio);
7847  $ratio = floatval($ratio);
7848  if ($type == 'u') {
7849  $chrdiff = floor(($diff + 12) * $ratio);
7850  $shift = str_repeat(' ', $chrdiff);
7851  $shift = TCPDF_FONTS::UTF8ToUTF16BE($shift, false, $this->isunicode, $this->CurrentFont);
7852  } else {
7853  $chrdiff = floor(($diff + 11) * $ratio);
7854  $shift = str_repeat(' ', $chrdiff);
7855  }
7856  $page = str_replace($aa, $shift, $page);
7857  }
7858  }
7859  }
7860  return $page;
7861  }
7862 
7868  protected function setPageBoxTypes($boxes) {
7869  $this->page_boxes = array();
7870  foreach ($boxes as $box) {
7871  if (in_array($box, TCPDF_STATIC::$pageboxes)) {
7872  $this->page_boxes[] = $box;
7873  }
7874  }
7875  }
7876 
7881  protected function _putpages() {
7882  $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
7883  // get internal aliases for page numbers
7884  $pnalias = $this->getAllInternalPageNumberAliases();
7885  $num_pages = $this->numpages;
7886  $ptpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $num_pages - 1));
7887  $ptpu = TCPDF_FONTS::UTF8ToUTF16BE($ptpa, false, $this->isunicode, $this->CurrentFont);
7888  $ptp_num_chars = $this->GetNumChars($ptpa);
7889  $pagegroupnum = 0;
7890  $groupnum = 0;
7891  $ptgu = 1;
7892  $ptga = 1;
7893  $ptg_num_chars = 1;
7894  for ($n = 1; $n <= $num_pages; ++$n) {
7895  // get current page
7896  $temppage = $this->getPageBuffer($n);
7897  $pagelen = strlen($temppage);
7898  // set replacements for total pages number
7899  $pnpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $n - 1));
7900  $pnpu = TCPDF_FONTS::UTF8ToUTF16BE($pnpa, false, $this->isunicode, $this->CurrentFont);
7901  $pnp_num_chars = $this->GetNumChars($pnpa);
7902  $pdiff = 0; // difference used for right shift alignment of page numbers
7903  $gdiff = 0; // difference used for right shift alignment of page group numbers
7904  if (!empty($this->pagegroups)) {
7905  if (isset($this->newpagegroup[$n])) {
7906  $pagegroupnum = 0;
7907  ++$groupnum;
7908  $ptga = TCPDF_STATIC::formatPageNumber($this->pagegroups[$groupnum]);
7909  $ptgu = TCPDF_FONTS::UTF8ToUTF16BE($ptga, false, $this->isunicode, $this->CurrentFont);
7910  $ptg_num_chars = $this->GetNumChars($ptga);
7911  }
7912  ++$pagegroupnum;
7913  $pnga = TCPDF_STATIC::formatPageNumber($pagegroupnum);
7914  $pngu = TCPDF_FONTS::UTF8ToUTF16BE($pnga, false, $this->isunicode, $this->CurrentFont);
7915  $png_num_chars = $this->GetNumChars($pnga);
7916  // replace page numbers
7917  $replace = array();
7918  $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
7919  $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
7920  $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
7921  $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
7922  list($temppage, $gdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $gdiff);
7923  }
7924  // replace page numbers
7925  $replace = array();
7926  $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
7927  $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
7928  $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
7929  $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
7930  list($temppage, $pdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $pdiff);
7931  // replace right shift alias
7932  $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
7933  // replace EPS marker
7934  $temppage = str_replace($this->epsmarker, '', $temppage);
7935  //Page
7936  $this->page_obj_id[$n] = $this->_newobj();
7937  $out = '<<';
7938  $out .= ' /Type /Page';
7939  $out .= ' /Parent 1 0 R';
7940  $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp);
7941  $out .= ' /Resources 2 0 R';
7942  foreach ($this->page_boxes as $box) {
7943  $out .= ' /'.$box;
7944  $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']);
7945  }
7946  if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
7947  $out .= ' /BoxColorInfo <<';
7948  foreach ($this->page_boxes as $box) {
7949  if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
7950  $out .= ' /'.$box.' <<';
7951  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
7952  $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
7953  $out .= ' /C [';
7954  $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
7955  $out .= ' ]';
7956  }
7957  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
7958  $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
7959  }
7960  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
7961  $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
7962  }
7963  if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
7964  $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
7965  $out .= ' /D [';
7966  foreach ($dashes as $dash) {
7967  $out .= sprintf(' %F', ($dash * $this->k));
7968  }
7969  $out .= ' ]';
7970  }
7971  $out .= ' >>';
7972  }
7973  }
7974  $out .= ' >>';
7975  }
7976  $out .= ' /Contents '.($this->n + 1).' 0 R';
7977  $out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
7978  if (!$this->pdfa_mode) {
7979  $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
7980  }
7981  if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
7982  // page transitions
7983  if (isset($this->pagedim[$n]['trans']['Dur'])) {
7984  $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
7985  }
7986  $out .= ' /Trans <<';
7987  $out .= ' /Type /Trans';
7988  if (isset($this->pagedim[$n]['trans']['S'])) {
7989  $out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
7990  }
7991  if (isset($this->pagedim[$n]['trans']['D'])) {
7992  $out .= ' /D '.$this->pagedim[$n]['trans']['D'];
7993  }
7994  if (isset($this->pagedim[$n]['trans']['Dm'])) {
7995  $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
7996  }
7997  if (isset($this->pagedim[$n]['trans']['M'])) {
7998  $out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
7999  }
8000  if (isset($this->pagedim[$n]['trans']['Di'])) {
8001  $out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
8002  }
8003  if (isset($this->pagedim[$n]['trans']['SS'])) {
8004  $out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
8005  }
8006  if (isset($this->pagedim[$n]['trans']['B'])) {
8007  $out .= ' /B '.$this->pagedim[$n]['trans']['B'];
8008  }
8009  $out .= ' >>';
8010  }
8011  $out .= $this->_getannotsrefs($n);
8012  $out .= ' /PZ '.$this->pagedim[$n]['PZ'];
8013  $out .= ' >>';
8014  $out .= "\n".'endobj';
8015  $this->_out($out);
8016  //Page content
8017  $p = ($this->compress) ? gzcompress($temppage) : $temppage;
8018  $this->_newobj();
8019  $p = $this->_getrawstream($p);
8020  $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
8021  if ($this->diskcache) {
8022  // remove temporary files
8023  unlink($this->pages[$n]);
8024  }
8025  }
8026  //Pages root
8027  $out = $this->_getobj(1)."\n";
8028  $out .= '<< /Type /Pages /Kids [';
8029  foreach($this->page_obj_id as $page_obj) {
8030  $out .= ' '.$page_obj.' 0 R';
8031  }
8032  $out .= ' ] /Count '.$num_pages.' >>';
8033  $out .= "\n".'endobj';
8034  $this->_out($out);
8035  }
8036 
8045  protected function _putannotsrefs($n) {
8046  $this->_out($this->_getannotsrefs($n));
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  // external URI link
8386  $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
8387  }
8388  } elseif (isset($this->links[$pl['txt']])) {
8389  // internal link ID
8390  $l = $this->links[$pl['txt']];
8391  if (isset($this->page_obj_id[($l[0])])) {
8392  $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
8393  }
8394  }
8395  $hmodes = array('N', 'I', 'O', 'P');
8396  if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
8397  $annots .= ' /H /'.$pl['opt']['h'];
8398  } else {
8399  $annots .= ' /H /I';
8400  }
8401  //$annots .= ' /PA ';
8402  //$annots .= ' /Quadpoints ';
8403  break;
8404  }
8405  case 'freetext': {
8406  if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8407  $annots .= ' /DA ('.$pl['opt']['da'].')';
8408  }
8409  if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8410  $annots .= ' /Q '.intval($pl['opt']['q']);
8411  }
8412  if (isset($pl['opt']['rc'])) {
8413  $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8414  }
8415  if (isset($pl['opt']['ds'])) {
8416  $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
8417  }
8418  if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
8419  $annots .= ' /CL [';
8420  foreach ($pl['opt']['cl'] as $cl) {
8421  $annots .= sprintf('%F ', $cl * $this->k);
8422  }
8423  $annots .= ']';
8424  }
8425  $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
8426  if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
8427  $annots .= ' /IT /'.$pl['opt']['it'];
8428  }
8429  if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
8430  $l = $pl['opt']['rd'][0] * $this->k;
8431  $r = $pl['opt']['rd'][1] * $this->k;
8432  $t = $pl['opt']['rd'][2] * $this->k;
8433  $b = $pl['opt']['rd'][3] * $this->k;
8434  $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']';
8435  }
8436  if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
8437  $annots .= ' /LE /'.$pl['opt']['le'];
8438  }
8439  break;
8440  }
8441  case 'line': {
8442  break;
8443  }
8444  case 'square': {
8445  break;
8446  }
8447  case 'circle': {
8448  break;
8449  }
8450  case 'polygon': {
8451  break;
8452  }
8453  case 'polyline': {
8454  break;
8455  }
8456  case 'highlight': {
8457  break;
8458  }
8459  case 'underline': {
8460  break;
8461  }
8462  case 'squiggly': {
8463  break;
8464  }
8465  case 'strikeout': {
8466  break;
8467  }
8468  case 'stamp': {
8469  break;
8470  }
8471  case 'caret': {
8472  break;
8473  }
8474  case 'ink': {
8475  break;
8476  }
8477  case 'popup': {
8478  break;
8479  }
8480  case 'fileattachment': {
8481  if ($this->pdfa_mode) {
8482  // embedded files are not allowed in PDF/A mode
8483  break;
8484  }
8485  if (!isset($pl['opt']['fs'])) {
8486  break;
8487  }
8488  $filename = basename($pl['opt']['fs']);
8489  if (isset($this->embeddedfiles[$filename]['f'])) {
8490  $annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R';
8491  $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
8492  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8493  $annots .= ' /Name /'.$pl['opt']['name'];
8494  } else {
8495  $annots .= ' /Name /PushPin';
8496  }
8497  // index (zero-based) of the annotation in the Annots array of this page
8498  $this->embeddedfiles[$filename]['a'] = $key;
8499  }
8500  break;
8501  }
8502  case 'sound': {
8503  if (!isset($pl['opt']['fs'])) {
8504  break;
8505  }
8506  $filename = basename($pl['opt']['fs']);
8507  if (isset($this->embeddedfiles[$filename]['f'])) {
8508  // ... TO BE COMPLETED ...
8509  // /R /C /B /E /CO /CP
8510  $annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R';
8511  $iconsapp = array('Speaker', 'Mic');
8512  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8513  $annots .= ' /Name /'.$pl['opt']['name'];
8514  } else {
8515  $annots .= ' /Name /Speaker';
8516  }
8517  }
8518  break;
8519  }
8520  case 'movie': {
8521  break;
8522  }
8523  case 'widget': {
8524  $hmode = array('N', 'I', 'O', 'P', 'T');
8525  if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
8526  $annots .= ' /H /'.$pl['opt']['h'];
8527  }
8528  if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
8529  $annots .= ' /MK <<';
8530  if (isset($pl['opt']['mk']['r'])) {
8531  $annots .= ' /R '.$pl['opt']['mk']['r'];
8532  }
8533  if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
8534  $annots .= ' /BC '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bc']);
8535  }
8536  if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
8537  $annots .= ' /BG '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bg']);
8538  }
8539  if (isset($pl['opt']['mk']['ca'])) {
8540  $annots .= ' /CA '.$pl['opt']['mk']['ca'];
8541  }
8542  if (isset($pl['opt']['mk']['rc'])) {
8543  $annots .= ' /RC '.$pl['opt']['mk']['rc'];
8544  }
8545  if (isset($pl['opt']['mk']['ac'])) {
8546  $annots .= ' /AC '.$pl['opt']['mk']['ac'];
8547  }
8548  if (isset($pl['opt']['mk']['i'])) {
8549  $info = $this->getImageBuffer($pl['opt']['mk']['i']);
8550  if ($info !== false) {
8551  $annots .= ' /I '.$info['n'].' 0 R';
8552  }
8553  }
8554  if (isset($pl['opt']['mk']['ri'])) {
8555  $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
8556  if ($info !== false) {
8557  $annots .= ' /RI '.$info['n'].' 0 R';
8558  }
8559  }
8560  if (isset($pl['opt']['mk']['ix'])) {
8561  $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
8562  if ($info !== false) {
8563  $annots .= ' /IX '.$info['n'].' 0 R';
8564  }
8565  }
8566  if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
8567  $annots .= ' /IF <<';
8568  $if_sw = array('A', 'B', 'S', 'N');
8569  if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
8570  $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
8571  }
8572  $if_s = array('A', 'P');
8573  if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
8574  $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
8575  }
8576  if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
8577  $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
8578  }
8579  if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
8580  $annots .= ' /FB true';
8581  }
8582  $annots .= '>>';
8583  }
8584  if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
8585  $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
8586  }
8587  $annots .= '>>';
8588  } // end MK
8589  // --- Entries for field dictionaries ---
8590  if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
8591  // set parent
8592  $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
8593  }
8594  if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8595  $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
8596  }
8597  if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8598  $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
8599  }
8600  if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
8601  $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
8602  }
8603  if (isset($pl['opt']['ff'])) {
8604  if (is_array($pl['opt']['ff'])) {
8605  // array of bit settings
8606  $flag = 0;
8607  foreach($pl['opt']['ff'] as $val) {
8608  $flag += 1 << ($val - 1);
8609  }
8610  } else {
8611  $flag = intval($pl['opt']['ff']);
8612  }
8613  $annots .= ' /Ff '.$flag;
8614  }
8615  if (isset($pl['opt']['maxlen'])) {
8616  $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
8617  }
8618  if (isset($pl['opt']['v'])) {
8619  $annots .= ' /V';
8620  if (is_array($pl['opt']['v'])) {
8621  foreach ($pl['opt']['v'] AS $optval) {
8622  if (is_float($optval)) {
8623  $optval = sprintf('%F', $optval);
8624  }
8625  $annots .= ' '.$optval;
8626  }
8627  } else {
8628  $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
8629  }
8630  }
8631  if (isset($pl['opt']['dv'])) {
8632  $annots .= ' /DV';
8633  if (is_array($pl['opt']['dv'])) {
8634  foreach ($pl['opt']['dv'] AS $optval) {
8635  if (is_float($optval)) {
8636  $optval = sprintf('%F', $optval);
8637  }
8638  $annots .= ' '.$optval;
8639  }
8640  } else {
8641  $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
8642  }
8643  }
8644  if (isset($pl['opt']['rv'])) {
8645  $annots .= ' /RV';
8646  if (is_array($pl['opt']['rv'])) {
8647  foreach ($pl['opt']['rv'] AS $optval) {
8648  if (is_float($optval)) {
8649  $optval = sprintf('%F', $optval);
8650  }
8651  $annots .= ' '.$optval;
8652  }
8653  } else {
8654  $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
8655  }
8656  }
8657  if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
8658  $annots .= ' /A << '.$pl['opt']['a'].' >>';
8659  }
8660  if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
8661  $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
8662  }
8663  if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8664  $annots .= ' /DA ('.$pl['opt']['da'].')';
8665  }
8666  if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8667  $annots .= ' /Q '.intval($pl['opt']['q']);
8668  }
8669  if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
8670  $annots .= ' /Opt [';
8671  foreach($pl['opt']['opt'] AS $copt) {
8672  if (is_array($copt)) {
8673  $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
8674  } else {
8675  $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
8676  }
8677  }
8678  $annots .= ']';
8679  }
8680  if (isset($pl['opt']['ti'])) {
8681  $annots .= ' /TI '.intval($pl['opt']['ti']);
8682  }
8683  if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
8684  $annots .= ' /I [';
8685  foreach($pl['opt']['i'] AS $copt) {
8686  $annots .= intval($copt).' ';
8687  }
8688  $annots .= ']';
8689  }
8690  break;
8691  }
8692  case 'screen': {
8693  break;
8694  }
8695  case 'printermark': {
8696  break;
8697  }
8698  case 'trapnet': {
8699  break;
8700  }
8701  case 'watermark': {
8702  break;
8703  }
8704  case '3d': {
8705  break;
8706  }
8707  default: {
8708  break;
8709  }
8710  }
8711  $annots .= '>>';
8712  // create new annotation object
8713  $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
8714  if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
8715  // store reference of form object
8716  $this->form_obj_id[] = $annot_obj_id;
8717  }
8718  }
8719  }
8720  } // end for each page
8721  }
8722 
8732  protected function _putAPXObject($w=0, $h=0, $stream='') {
8733  $stream = trim($stream);
8734  $out = $this->_getobj()."\n";
8735  $this->xobjects['AX'.$this->n] = array('n' => $this->n);
8736  $out .= '<<';
8737  $out .= ' /Type /XObject';
8738  $out .= ' /Subtype /Form';
8739  $out .= ' /FormType 1';
8740  if ($this->compress) {
8741  $stream = gzcompress($stream);
8742  $out .= ' /Filter /FlateDecode';
8743  }
8744  $rect = sprintf('%F %F', $w, $h);
8745  $out .= ' /BBox [0 0 '.$rect.']';
8746  $out .= ' /Matrix [1 0 0 1 0 0]';
8747  $out .= ' /Resources 2 0 R';
8748  $stream = $this->_getrawstream($stream);
8749  $out .= ' /Length '.strlen($stream);
8750  $out .= ' >>';
8751  $out .= ' stream'."\n".$stream."\n".'endstream';
8752  $out .= "\n".'endobj';
8753  $this->_out($out);
8754  return $this->n;
8755  }
8756 
8762  protected function _putfonts() {
8763  $nf = $this->n;
8764  foreach ($this->diffs as $diff) {
8765  //Encodings
8766  $this->_newobj();
8767  $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
8768  }
8769  $mqr = TCPDF_STATIC::get_mqr();
8770  TCPDF_STATIC::set_mqr(false);
8771  foreach ($this->FontFiles as $file => $info) {
8772  // search and get font file to embedd
8773  $fontfile = TCPDF_FONTS::getFontFullPath($file, $info['fontdir']);
8774  if (!TCPDF_STATIC::empty_string($fontfile)) {
8775  $font = file_get_contents($fontfile);
8776  $compressed = (substr($file, -2) == '.z');
8777  if ((!$compressed) AND (isset($info['length2']))) {
8778  $header = (ord($font{0}) == 128);
8779  if ($header) {
8780  // strip first binary header
8781  $font = substr($font, 6);
8782  }
8783  if ($header AND (ord($font[$info['length1']]) == 128)) {
8784  // strip second binary header
8785  $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
8786  }
8787  } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
8788  if ($compressed) {
8789  // uncompress font
8790  $font = gzuncompress($font);
8791  }
8792  // merge subset characters
8793  $subsetchars = array(); // used chars
8794  foreach ($info['fontkeys'] as $fontkey) {
8795  $fontinfo = $this->getFontBuffer($fontkey);
8796  $subsetchars += $fontinfo['subsetchars'];
8797  }
8798  // rebuild a font subset
8799  $font = TCPDF_FONTS::_getTrueTypeFontSubset($font, $subsetchars);
8800  // calculate new font length
8801  $info['length1'] = strlen($font);
8802  if ($compressed) {
8803  // recompress font
8804  $font = gzcompress($font);
8805  }
8806  }
8807  $this->_newobj();
8808  $this->FontFiles[$file]['n'] = $this->n;
8809  $stream = $this->_getrawstream($font);
8810  $out = '<< /Length '.strlen($stream);
8811  if ($compressed) {
8812  $out .= ' /Filter /FlateDecode';
8813  }
8814  $out .= ' /Length1 '.$info['length1'];
8815  if (isset($info['length2'])) {
8816  $out .= ' /Length2 '.$info['length2'].' /Length3 0';
8817  }
8818  $out .= ' >>';
8819  $out .= ' stream'."\n".$stream."\n".'endstream';
8820  $out .= "\n".'endobj';
8821  $this->_out($out);
8822  }
8823  }
8824  TCPDF_STATIC::set_mqr($mqr);
8825  foreach ($this->fontkeys as $k) {
8826  //Font objects
8827  $font = $this->getFontBuffer($k);
8828  $type = $font['type'];
8829  $name = $font['name'];
8830  if ($type == 'core') {
8831  // standard core font
8832  $out = $this->_getobj($this->font_obj_ids[$k])."\n";
8833  $out .= '<</Type /Font';
8834  $out .= ' /Subtype /Type1';
8835  $out .= ' /BaseFont /'.$name;
8836  $out .= ' /Name /F'.$font['i'];
8837  if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
8838  $out .= ' /Encoding /WinAnsiEncoding';
8839  }
8840  if ($k == 'helvetica') {
8841  // add default font for annotations
8842  $this->annotation_fonts[$k] = $font['i'];
8843  }
8844  $out .= ' >>';
8845  $out .= "\n".'endobj';
8846  $this->_out($out);
8847  } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
8848  // additional Type1 or TrueType font
8849  $out = $this->_getobj($this->font_obj_ids[$k])."\n";
8850  $out .= '<</Type /Font';
8851  $out .= ' /Subtype /'.$type;
8852  $out .= ' /BaseFont /'.$name;
8853  $out .= ' /Name /F'.$font['i'];
8854  $out .= ' /FirstChar 32 /LastChar 255';
8855  $out .= ' /Widths '.($this->n + 1).' 0 R';
8856  $out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
8857  if ($font['enc']) {
8858  if (isset($font['diff'])) {
8859  $out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
8860  } else {
8861  $out .= ' /Encoding /WinAnsiEncoding';
8862  }
8863  }
8864  $out .= ' >>';
8865  $out .= "\n".'endobj';
8866  $this->_out($out);
8867  // Widths
8868  $this->_newobj();
8869  $s = '[';
8870  for ($i = 32; $i < 256; ++$i) {
8871  if (isset($font['cw'][$i])) {
8872  $s .= $font['cw'][$i].' ';
8873  } else {
8874  $s .= $font['dw'].' ';
8875  }
8876  }
8877  $s .= ']';
8878  $s .= "\n".'endobj';
8879  $this->_out($s);
8880  //Descriptor
8881  $this->_newobj();
8882  $s = '<</Type /FontDescriptor /FontName /'.$name;
8883  foreach ($font['desc'] as $fdk => $fdv) {
8884  if (is_float($fdv)) {
8885  $fdv = sprintf('%F', $fdv);
8886  }
8887  $s .= ' /'.$fdk.' '.$fdv.'';
8888  }
8889  if (!TCPDF_STATIC::empty_string($font['file'])) {
8890  $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
8891  }
8892  $s .= '>>';
8893  $s .= "\n".'endobj';
8894  $this->_out($s);
8895  } else {
8896  // additional types
8897  $mtd = '_put'.strtolower($type);
8898  if (!method_exists($this, $mtd)) {
8899  $this->Error('Unsupported font type: '.$type);
8900  }
8901  $this->$mtd($font);
8902  }
8903  }
8904  }
8905 
8914  protected function _puttruetypeunicode($font) {
8915  $fontname = '';
8916  if ($font['subset']) {
8917  // change name for font subsetting
8918  $subtag = sprintf('%06u', $font['i']);
8919  $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
8920  $fontname .= $subtag.'+';
8921  }
8922  $fontname .= $font['name'];
8923  // Type0 Font
8924  // A composite font composed of other fonts, organized hierarchically
8925  $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
8926  $out .= '<< /Type /Font';
8927  $out .= ' /Subtype /Type0';
8928  $out .= ' /BaseFont /'.$fontname;
8929  $out .= ' /Name /F'.$font['i'];
8930  $out .= ' /Encoding /'.$font['enc'];
8931  $out .= ' /ToUnicode '.($this->n + 1).' 0 R';
8932  $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
8933  $out .= ' >>';
8934  $out .= "\n".'endobj';
8935  $this->_out($out);
8936  // ToUnicode map for Identity-H
8938  // ToUnicode Object
8939  $this->_newobj();
8940  $stream = ($this->compress) ? gzcompress($stream) : $stream;
8941  $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
8942  $stream = $this->_getrawstream($stream);
8943  $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
8944  // CIDFontType2
8945  // A CIDFont whose glyph descriptions are based on TrueType font technology
8946  $oid = $this->_newobj();
8947  $out = '<< /Type /Font';
8948  $out .= ' /Subtype /CIDFontType2';
8949  $out .= ' /BaseFont /'.$fontname;
8950  // A dictionary containing entries that define the character collection of the CIDFont.
8951  $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
8952  $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
8953  $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
8954  $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
8955  $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
8956  $out .= ' /DW '.$font['dw']; // default width
8957  $out .= "\n".TCPDF_FONTS::_putfontwidths($font, 0);
8958  if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
8959  $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
8960  }
8961  $out .= ' >>';
8962  $out .= "\n".'endobj';
8963  $this->_out($out);
8964  // Font descriptor
8965  // A font descriptor describing the CIDFont default metrics other than its glyph widths
8966  $this->_newobj();
8967  $out = '<< /Type /FontDescriptor';
8968  $out .= ' /FontName /'.$fontname;
8969  foreach ($font['desc'] as $key => $value) {
8970  if (is_float($value)) {
8971  $value = sprintf('%F', $value);
8972  }
8973  $out .= ' /'.$key.' '.$value;
8974  }
8975  $fontdir = false;
8976  if (!TCPDF_STATIC::empty_string($font['file'])) {
8977  // A stream containing a TrueType font
8978  $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
8979  $fontdir = $this->FontFiles[$font['file']]['fontdir'];
8980  }
8981  $out .= ' >>';
8982  $out .= "\n".'endobj';
8983  $this->_out($out);
8984  if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
8985  $this->_newobj();
8986  // Embed CIDToGIDMap
8987  // A specification of the mapping from CIDs to glyph indices
8988  // search and get CTG font file to embedd
8989  $ctgfile = strtolower($font['ctg']);
8990  // search and get ctg font file to embedd
8991  $fontfile = TCPDF_FONTS::getFontFullPath($ctgfile, $fontdir);
8992  if (TCPDF_STATIC::empty_string($fontfile)) {
8993  $this->Error('Font file not found: '.$ctgfile);
8994  }
8995  $stream = $this->_getrawstream(file_get_contents($fontfile));
8996  $out = '<< /Length '.strlen($stream).'';
8997  if (substr($fontfile, -2) == '.z') { // check file extension
8998  // Decompresses data encoded using the public-domain
8999  // zlib/deflate compression method, reproducing the
9000  // original text or binary data
9001  $out .= ' /Filter /FlateDecode';
9002  }
9003  $out .= ' >>';
9004  $out .= ' stream'."\n".$stream."\n".'endstream';
9005  $out .= "\n".'endobj';
9006  $this->_out($out);
9007  }
9008  }
9009 
9018  protected function _putcidfont0($font) {
9019  $cidoffset = 0;
9020  if (!isset($font['cw'][1])) {
9021  $cidoffset = 31;
9022  }
9023  if (isset($font['cidinfo']['uni2cid'])) {
9024  // convert unicode to cid.
9025  $uni2cid = $font['cidinfo']['uni2cid'];
9026  $cw = array();
9027  foreach ($font['cw'] as $uni => $width) {
9028  if (isset($uni2cid[$uni])) {
9029  $cw[($uni2cid[$uni] + $cidoffset)] = $width;
9030  } elseif ($uni < 256) {
9031  $cw[$uni] = $width;
9032  } // else unknown character
9033  }
9034  $font = array_merge($font, array('cw' => $cw));
9035  }
9036  $name = $font['name'];
9037  $enc = $font['enc'];
9038  if ($enc) {
9039  $longname = $name.'-'.$enc;
9040  } else {
9041  $longname = $name;
9042  }
9043  $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
9044  $out .= '<</Type /Font';
9045  $out .= ' /Subtype /Type0';
9046  $out .= ' /BaseFont /'.$longname;
9047  $out .= ' /Name /F'.$font['i'];
9048  if ($enc) {
9049  $out .= ' /Encoding /'.$enc;
9050  }
9051  $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
9052  $out .= ' >>';
9053  $out .= "\n".'endobj';
9054  $this->_out($out);
9055  $oid = $this->_newobj();
9056  $out = '<</Type /Font';
9057  $out .= ' /Subtype /CIDFontType0';
9058  $out .= ' /BaseFont /'.$name;
9059  $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9060  $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9061  $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9062  $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
9063  $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
9064  $out .= ' /DW '.$font['dw'];
9065  $out .= "\n".TCPDF_FONTS::_putfontwidths($font, $cidoffset);
9066  $out .= ' >>';
9067  $out .= "\n".'endobj';
9068  $this->_out($out);
9069  $this->_newobj();
9070  $s = '<</Type /FontDescriptor /FontName /'.$name;
9071  foreach ($font['desc'] as $k => $v) {
9072  if ($k != 'Style') {
9073  if (is_float($v)) {
9074  $v = sprintf('%F', $v);
9075  }
9076  $s .= ' /'.$k.' '.$v.'';
9077  }
9078  }
9079  $s .= '>>';
9080  $s .= "\n".'endobj';
9081  $this->_out($s);
9082  }
9083 
9088  protected function _putimages() {
9089  $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9090  foreach ($this->imagekeys as $file) {
9091  $info = $this->getImageBuffer($file);
9092  // set object for alternate images array
9093  if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
9094  $altoid = $this->_newobj();
9095  $out = '[';
9096  foreach ($info['altimgs'] as $altimage) {
9097  if (isset($this->xobjects['I'.$altimage[0]]['n'])) {
9098  $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R';
9099  $out .= ' /DefaultForPrinting';
9100  if ($altimage[1] === true) {
9101  $out .= ' true';
9102  } else {
9103  $out .= ' false';
9104  }
9105  $out .= ' >>';
9106  }
9107  }
9108  $out .= ' ]';
9109  $out .= "\n".'endobj';
9110  $this->_out($out);
9111  }
9112  // set image object
9113  $oid = $this->_newobj();
9114  $this->xobjects['I'.$info['i']] = array('n' => $oid);
9115  $this->setImageSubBuffer($file, 'n', $this->n);
9116  $out = '<</Type /XObject';
9117  $out .= ' /Subtype /Image';
9118  $out .= ' /Width '.$info['w'];
9119  $out .= ' /Height '.$info['h'];
9120  if (array_key_exists('masked', $info)) {
9121  $out .= ' /SMask '.($this->n - 1).' 0 R';
9122  }
9123  // set color space
9124  $icc = false;
9125  if (isset($info['icc']) AND ($info['icc'] !== false)) {
9126  // ICC Colour Space
9127  $icc = true;
9128  $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]';
9129  } elseif ($info['cs'] == 'Indexed') {
9130  // Indexed Colour Space
9131  $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
9132  } else {
9133  // Device Colour Space
9134  $out .= ' /ColorSpace /'.$info['cs'];
9135  }
9136  if ($info['cs'] == 'DeviceCMYK') {
9137  $out .= ' /Decode [1 0 1 0 1 0 1 0]';
9138  }
9139  $out .= ' /BitsPerComponent '.$info['bpc'];
9140  if (isset($altoid) AND ($altoid > 0)) {
9141  // reference to alternate images dictionary
9142  $out .= ' /Alternates '.$altoid.' 0 R';
9143  }
9144  if (isset($info['exurl']) AND !empty($info['exurl'])) {
9145  // external stream
9146  $out .= ' /Length 0';
9147  $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
9148  if (isset($info['f'])) {
9149  $out .= ' /FFilter /'.$info['f'];
9150  }
9151  $out .= ' >>';
9152  $out .= ' stream'."\n".'endstream';
9153  } else {
9154  if (isset($info['f'])) {
9155  $out .= ' /Filter /'.$info['f'];
9156  }
9157  if (isset($info['parms'])) {
9158  $out .= ' '.$info['parms'];
9159  }
9160  if (isset($info['trns']) AND is_array($info['trns'])) {
9161  $trns = '';
9162  $count_info = count($info['trns']);
9163  for ($i=0; $i < $count_info; ++$i) {
9164  $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
9165  }
9166  $out .= ' /Mask ['.$trns.']';
9167  }
9168  $stream = $this->_getrawstream($info['data']);
9169  $out .= ' /Length '.strlen($stream).' >>';
9170  $out .= ' stream'."\n".$stream."\n".'endstream';
9171  }
9172  $out .= "\n".'endobj';
9173  $this->_out($out);
9174  if ($icc) {
9175  // ICC colour profile
9176  $this->_newobj();
9177  $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
9178  $icc = $this->_getrawstream($icc);
9179  $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9180  } elseif ($info['cs'] == 'Indexed') {
9181  // colour palette
9182  $this->_newobj();
9183  $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
9184  $pal = $this->_getrawstream($pal);
9185  $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
9186  }
9187  }
9188  }
9189 
9197  protected function _putxobjects() {
9198  foreach ($this->xobjects as $key => $data) {
9199  if (isset($data['outdata'])) {
9200  $stream = str_replace($this->epsmarker, '', trim($data['outdata']));
9201  $out = $this->_getobj($data['n'])."\n";
9202  $out .= '<<';
9203  $out .= ' /Type /XObject';
9204  $out .= ' /Subtype /Form';
9205  $out .= ' /FormType 1';
9206  if ($this->compress) {
9207  $stream = gzcompress($stream);
9208  $out .= ' /Filter /FlateDecode';
9209  }
9210  $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));
9211  $out .= ' /Matrix [1 0 0 1 0 0]';
9212  $out .= ' /Resources <<';
9213  $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9214  if (!$this->pdfa_mode) {
9215  // transparency
9216  if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
9217  $out .= ' /ExtGState <<';
9218  foreach ($data['extgstates'] as $k => $extgstate) {
9219  if (isset($this->extgstates[$k]['name'])) {
9220  $out .= ' /'.$this->extgstates[$k]['name'];
9221  } else {
9222  $out .= ' /GS'.$k;
9223  }
9224  $out .= ' '.$this->extgstates[$k]['n'].' 0 R';
9225  }
9226  $out .= ' >>';
9227  }
9228  if (isset($data['gradients']) AND !empty($data['gradients'])) {
9229  $gp = '';
9230  $gs = '';
9231  foreach ($data['gradients'] as $id => $grad) {
9232  // gradient patterns
9233  $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R';
9234  // gradient shadings
9235  $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R';
9236  }
9237  $out .= ' /Pattern <<'.$gp.' >>';
9238  $out .= ' /Shading <<'.$gs.' >>';
9239  }
9240  }
9241  // spot colors
9242  if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
9243  $out .= ' /ColorSpace <<';
9244  foreach ($data['spot_colors'] as $name => $color) {
9245  $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R';
9246  }
9247  $out .= ' >>';
9248  }
9249  // fonts
9250  if (!empty($data['fonts'])) {
9251  $out .= ' /Font <<';
9252  foreach ($data['fonts'] as $fontkey => $fontid) {
9253  $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9254  }
9255  $out .= ' >>';
9256  }
9257  // images or nested xobjects
9258  if (!empty($data['images']) OR !empty($data['xobjects'])) {
9259  $out .= ' /XObject <<';
9260  foreach ($data['images'] as $imgid) {
9261  $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
9262  }
9263  foreach ($data['xobjects'] as $sub_id => $sub_objid) {
9264  $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
9265  }
9266  $out .= ' >>';
9267  }
9268  $out .= ' >>'; //end resources
9269  if (isset($data['group']) AND ($data['group'] !== false)) {
9270  // set transparency group
9271  $out .= ' /Group << /Type /Group /S /Transparency';
9272  if (is_array($data['group'])) {
9273  if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
9274  $out .= ' /CS /'.$data['group']['CS'];
9275  }
9276  if (isset($data['group']['I'])) {
9277  $out .= ' /I /'.($data['group']['I']===true?'true':'false');
9278  }
9279  if (isset($data['group']['K'])) {
9280  $out .= ' /K /'.($data['group']['K']===true?'true':'false');
9281  }
9282  }
9283  $out .= ' >>';
9284  }
9285  $stream = $this->_getrawstream($stream, $data['n']);
9286  $out .= ' /Length '.strlen($stream);
9287  $out .= ' >>';
9288  $out .= ' stream'."\n".$stream."\n".'endstream';
9289  $out .= "\n".'endobj';
9290  $this->_out($out);
9291  }
9292  }
9293  }
9294 
9300  protected function _putspotcolors() {
9301  foreach ($this->spot_colors as $name => $color) {
9302  $this->_newobj();
9303  $this->spot_colors[$name]['n'] = $this->n;
9304  $out = '[/Separation /'.str_replace(' ', '#20', $name);
9305  $out .= ' /DeviceCMYK <<';
9306  $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
9307  $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
9308  $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
9309  $out .= "\n".'endobj';
9310  $this->_out($out);
9311  }
9312  }
9313 
9320  protected function _getxobjectdict() {
9321  $out = '';
9322  foreach ($this->xobjects as $id => $objid) {
9323  $out .= ' /'.$id.' '.$objid['n'].' 0 R';
9324  }
9325  return $out;
9326  }
9327 
9332  protected function _putresourcedict() {
9333  $out = $this->_getobj(2)."\n";
9334  $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9335  $out .= ' /Font <<';
9336  foreach ($this->fontkeys as $fontkey) {
9337  $font = $this->getFontBuffer($fontkey);
9338  $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
9339  }
9340  $out .= ' >>';
9341  $out .= ' /XObject <<';
9342  $out .= $this->_getxobjectdict();
9343  $out .= ' >>';
9344  // layers
9345  if (!empty($this->pdflayers)) {
9346  $out .= ' /Properties <<';
9347  foreach ($this->pdflayers as $layer) {
9348  $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
9349  }
9350  $out .= ' >>';
9351  }
9352  if (!$this->pdfa_mode) {
9353  // transparency
9354  if (isset($this->extgstates) AND !empty($this->extgstates)) {
9355  $out .= ' /ExtGState <<';
9356  foreach ($this->extgstates as $k => $extgstate) {
9357  if (isset($extgstate['name'])) {
9358  $out .= ' /'.$extgstate['name'];
9359  } else {
9360  $out .= ' /GS'.$k;
9361  }
9362  $out .= ' '.$extgstate['n'].' 0 R';
9363  }
9364  $out .= ' >>';
9365  }
9366  if (isset($this->gradients) AND !empty($this->gradients)) {
9367  $gp = '';
9368  $gs = '';
9369  foreach ($this->gradients as $id => $grad) {
9370  // gradient patterns
9371  $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
9372  // gradient shadings
9373  $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
9374  }
9375  $out .= ' /Pattern <<'.$gp.' >>';
9376  $out .= ' /Shading <<'.$gs.' >>';
9377  }
9378  }
9379  // spot colors
9380  if (isset($this->spot_colors) AND !empty($this->spot_colors)) {
9381  $out .= ' /ColorSpace <<';
9382  foreach ($this->spot_colors as $color) {
9383  $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
9384  }
9385  $out .= ' >>';
9386  }
9387  $out .= ' >>';
9388  $out .= "\n".'endobj';
9389  $this->_out($out);
9390  }
9391 
9396  protected function _putresources() {
9397  $this->_putextgstates();
9398  $this->_putocg();
9399  $this->_putfonts();
9400  $this->_putimages();
9401  $this->_putspotcolors();
9402  $this->_putshaders();
9403  $this->_putxobjects();
9404  $this->_putresourcedict();
9405  $this->_putdests();
9406  $this->_putEmbeddedFiles();
9407  $this->_putannotsobjs();
9408  $this->_putjavascript();
9409  $this->_putbookmarks();
9410  $this->_putencryption();
9411  }
9412 
9419  protected function _putinfo() {
9420  $oid = $this->_newobj();
9421  $out = '<<';
9422  // store current isunicode value
9423  $prev_isunicode = $this->isunicode;
9424  if ($this->docinfounicode) {
9425  $this->isunicode = true;
9426  }
9427  if (!TCPDF_STATIC::empty_string($this->title)) {
9428  // The document's title.
9429  $out .= ' /Title '.$this->_textstring($this->title, $oid);
9430  }
9431  if (!TCPDF_STATIC::empty_string($this->author)) {
9432  // The name of the person who created the document.
9433  $out .= ' /Author '.$this->_textstring($this->author, $oid);
9434  }
9435  if (!TCPDF_STATIC::empty_string($this->subject)) {
9436  // The subject of the document.
9437  $out .= ' /Subject '.$this->_textstring($this->subject, $oid);
9438  }
9439  if (!TCPDF_STATIC::empty_string($this->keywords)) {
9440  // Keywords associated with the document.
9441  $out .= ' /Keywords '.$this->_textstring($this->keywords, $oid);
9442  }
9443  if (!TCPDF_STATIC::empty_string($this->creator)) {
9444  // 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.
9445  $out .= ' /Creator '.$this->_textstring($this->creator, $oid);
9446  }
9447  // restore previous isunicode value
9448  $this->isunicode = $prev_isunicode;
9449  // default producer
9450  $out .= ' /Producer '.$this->_textstring(TCPDF_STATIC::getTCPDFProducer(), $oid);
9451  // The date and time the document was created, in human-readable form
9452  $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp);
9453  // The date and time the document was most recently modified, in human-readable form
9454  $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp);
9455  // A name object indicating whether the document has been modified to include trapping information
9456  $out .= ' /Trapped /False';
9457  $out .= ' >>';
9458  $out .= "\n".'endobj';
9459  $this->_out($out);
9460  return $oid;
9461  }
9462 
9470  public function setExtraXMP($xmp) {
9471  $this->custom_xmp = $xmp;
9472  }
9473 
9480  protected function _putXMP() {
9481  $oid = $this->_newobj();
9482  // store current isunicode value
9483  $prev_isunicode = $this->isunicode;
9484  $this->isunicode = true;
9485  $prev_encrypted = $this->encrypted;
9486  $this->encrypted = false;
9487  // set XMP data
9488  $xmp = '<?xpacket begin="'.TCPDF_FONTS::unichr(0xfeff, $this->isunicode).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
9489  $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";
9490  $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
9491  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
9492  $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
9493  $xmp .= "\t\t\t".'<dc:title>'."\n";
9494  $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9495  $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->title).'</rdf:li>'."\n";
9496  $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9497  $xmp .= "\t\t\t".'</dc:title>'."\n";
9498  $xmp .= "\t\t\t".'<dc:creator>'."\n";
9499  $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
9500  $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->author).'</rdf:li>'."\n";
9501  $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
9502  $xmp .= "\t\t\t".'</dc:creator>'."\n";
9503  $xmp .= "\t\t\t".'<dc:description>'."\n";
9504  $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9505  $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->subject).'</rdf:li>'."\n";
9506  $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9507  $xmp .= "\t\t\t".'</dc:description>'."\n";
9508  $xmp .= "\t\t\t".'<dc:subject>'."\n";
9509  $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9510  $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->keywords).'</rdf:li>'."\n";
9511  $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9512  $xmp .= "\t\t\t".'</dc:subject>'."\n";
9513  $xmp .= "\t\t".'</rdf:Description>'."\n";
9514  // convert doc creation date format
9515  $dcdate = TCPDF_STATIC::getFormattedDate($this->doc_creation_timestamp);
9516  $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2);
9517  $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2);
9518  $doccreationdate .= '+'.substr($dcdate, 15, 2).':'.substr($dcdate, 18, 2);
9519  $doccreationdate = TCPDF_STATIC::_escapeXML($doccreationdate);
9520  // convert doc modification date format
9521  $dmdate = TCPDF_STATIC::getFormattedDate($this->doc_modification_timestamp);
9522  $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2);
9523  $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2);
9524  $docmoddate .= '+'.substr($dmdate, 15, 2).':'.substr($dmdate, 18, 2);
9525  $docmoddate = TCPDF_STATIC::_escapeXML($docmoddate);
9526  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
9527  $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n";
9528  $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n";
9529  $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n";
9530  $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n";
9531  $xmp .= "\t\t".'</rdf:Description>'."\n";
9532  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
9533  $xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC::_escapeXML($this->keywords).'</pdf:Keywords>'."\n";
9534  $xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC::_escapeXML(TCPDF_STATIC::getTCPDFProducer()).'</pdf:Producer>'."\n";
9535  $xmp .= "\t\t".'</rdf:Description>'."\n";
9536  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
9537  $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);
9538  $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
9539  $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
9540  $xmp .= "\t\t".'</rdf:Description>'."\n";
9541  if ($this->pdfa_mode) {
9542  $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
9543  $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n";
9544  $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
9545  $xmp .= "\t\t".'</rdf:Description>'."\n";
9546  }
9547  // XMP extension schemas
9548  $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";
9549  $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
9550  $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9551  $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9552  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
9553  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
9554  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
9555  $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9556  $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9557  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
9558  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
9559  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
9560  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9561  $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9562  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9563  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9564  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
9565  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9566  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9567  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9568  $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9569  $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9570  $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9571  $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9572  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
9573  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
9574  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
9575  $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9576  $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9577  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9578  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9579  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
9580  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
9581  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
9582  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9583  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9584  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9585  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
9586  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
9587  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9588  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9589  $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9590  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9591  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
9592  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
9593  $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9594  $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9595  $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9596  $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9597  $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9598  $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9599  $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
9600  $xmp .= "\t\t".'</rdf:Description>'."\n";
9601  $xmp .= "\t".'</rdf:RDF>'."\n";
9602  $xmp .= $this->custom_xmp;
9603  $xmp .= '</x:xmpmeta>'."\n";
9604  $xmp .= '<?xpacket end="w"?>';
9605  $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
9606  // restore previous isunicode value
9607  $this->isunicode = $prev_isunicode;
9608  $this->encrypted = $prev_encrypted;
9609  $this->_out($out);
9610  return $oid;
9611  }
9612 
9618  protected function _putcatalog() {
9619  // put XMP
9620  $xmpobj = $this->_putXMP();
9621  // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile
9622  if ($this->pdfa_mode OR $this->force_srgb) {
9623  $iccobj = $this->_newobj();
9624  $icc = file_get_contents(dirname(__FILE__).'/include/sRGB.icc');
9625  $filter = '';
9626  if ($this->compress) {
9627  $filter = ' /Filter /FlateDecode';
9628  $icc = gzcompress($icc);
9629  }
9630  $icc = $this->_getrawstream($icc);
9631  $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9632  }
9633  // start catalog
9634  $oid = $this->_newobj();
9635  $out = '<< /Type /Catalog';
9636  $out .= ' /Version /'.$this->PDFVersion;
9637  //$out .= ' /Extensions <<>>';
9638  $out .= ' /Pages 1 0 R';
9639  //$out .= ' /PageLabels ' //...;
9640  $out .= ' /Names <<';
9641  if ((!$this->pdfa_mode) AND !empty($this->n_js)) {
9642  $out .= ' /JavaScript '.$this->n_js;
9643  }
9644  if (!empty($this->efnames)) {
9645  $out .= ' /EmbeddedFiles <</Names [';
9646  foreach ($this->efnames AS $fn => $fref) {
9647  $out .= ' '.$this->_datastring($fn).' '.$fref;
9648  }
9649  $out .= ' ]>>';
9650  }
9651  $out .= ' >>';
9652  if (!empty($this->dests)) {
9653  $out .= ' /Dests '.($this->n_dests).' 0 R';
9654  }
9655  $out .= $this->_putviewerpreferences();
9656  if (isset($this->LayoutMode) AND (!TCPDF_STATIC::empty_string($this->LayoutMode))) {
9657  $out .= ' /PageLayout /'.$this->LayoutMode;
9658  }
9659  if (isset($this->PageMode) AND (!TCPDF_STATIC::empty_string($this->PageMode))) {
9660  $out .= ' /PageMode /'.$this->PageMode;
9661  }
9662  if (count($this->outlines) > 0) {
9663  $out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
9664  $out .= ' /PageMode /UseOutlines';
9665  }
9666  //$out .= ' /Threads []';
9667  if ($this->ZoomMode == 'fullpage') {
9668  $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
9669  } elseif ($this->ZoomMode == 'fullwidth') {
9670  $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
9671  } elseif ($this->ZoomMode == 'real') {
9672  $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
9673  } elseif (!is_string($this->ZoomMode)) {
9674  $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100));
9675  }
9676  //$out .= ' /AA <<>>';
9677  //$out .= ' /URI <<>>';
9678  $out .= ' /Metadata '.$xmpobj.' 0 R';
9679  //$out .= ' /StructTreeRoot <<>>';
9680  //$out .= ' /MarkInfo <<>>';
9681  if (isset($this->l['a_meta_language'])) {
9682  $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
9683  }
9684  //$out .= ' /SpiderInfo <<>>';
9685  // set OutputIntent to sRGB IEC61966-2.1 if required
9686  if ($this->pdfa_mode OR $this->force_srgb) {
9687  $out .= ' /OutputIntents [<<';
9688  $out .= ' /Type /OutputIntent';
9689  $out .= ' /S /GTS_PDFA1';
9690  $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9691  $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9692  $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
9693  $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9694  $out .= ' /DestOutputProfile '.$iccobj.' 0 R';
9695  $out .= ' >>]';
9696  }
9697  //$out .= ' /PieceInfo <<>>';
9698  if (!empty($this->pdflayers)) {
9699  $lyrobjs = '';
9700  $lyrobjs_print = '';
9701  $lyrobjs_view = '';
9702  foreach ($this->pdflayers as $layer) {
9703  $lyrobjs .= ' '.$layer['objid'].' 0 R';
9704  if ($layer['print']) {
9705  $lyrobjs_print .= ' '.$layer['objid'].' 0 R';
9706  }
9707  if ($layer['view']) {
9708  $lyrobjs_view .= ' '.$layer['objid'].' 0 R';
9709  }
9710  }
9711  $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
9712  $out .= ' /D <<';
9713  $out .= ' /Name '.$this->_textstring('Layers', $oid);
9714  $out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
9715  $out .= ' /BaseState /ON';
9716  $out .= ' /ON ['.$lyrobjs_print.']';
9717  $out .= ' /OFF ['.$lyrobjs_view.']';
9718  $out .= ' /Intent /View';
9719  $out .= ' /AS [';
9720  $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
9721  $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
9722  $out .= ' ]';
9723  $out .= ' /Order ['.$lyrobjs.']';
9724  $out .= ' /ListMode /AllPages';
9725  //$out .= ' /RBGroups ['..']';
9726  //$out .= ' /Locked ['..']';
9727  $out .= ' >>';
9728  $out .= ' >>';
9729  }
9730  // AcroForm
9731  if (!empty($this->form_obj_id)
9732  OR ($this->sign AND isset($this->signature_data['cert_type']))
9733  OR !empty($this->empty_signature_appearance)) {
9734  $out .= ' /AcroForm <<';
9735  $objrefs = '';
9736  if ($this->sign AND isset($this->signature_data['cert_type'])) {
9737  // set reference for signature object
9738  $objrefs .= $this->sig_obj_id.' 0 R';
9739  }
9740  if (!empty($this->empty_signature_appearance)) {
9741  foreach ($this->empty_signature_appearance as $esa) {
9742  // set reference for empty signature objects
9743  $objrefs .= ' '.$esa['objid'].' 0 R';
9744  }
9745  }
9746  if (!empty($this->form_obj_id)) {
9747  foreach($this->form_obj_id as $objid) {
9748  $objrefs .= ' '.$objid.' 0 R';
9749  }
9750  }
9751  $out .= ' /Fields ['.$objrefs.']';
9752  // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
9753  $out .= ' /NeedAppearances false';
9754  if ($this->sign AND isset($this->signature_data['cert_type'])) {
9755  if ($this->signature_data['cert_type'] > 0) {
9756  $out .= ' /SigFlags 3';
9757  } else {
9758  $out .= ' /SigFlags 1';
9759  }
9760  }
9761  //$out .= ' /CO ';
9762  if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
9763  $out .= ' /DR <<';
9764  $out .= ' /Font <<';
9765  foreach ($this->annotation_fonts as $fontkey => $fontid) {
9766  $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9767  }
9768  $out .= ' >> >>';
9769  }
9770  $font = $this->getFontBuffer('helvetica');
9771  $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
9772  $out .= ' /Q '.(($this->rtl)?'2':'0');
9773  //$out .= ' /XFA ';
9774  $out .= ' >>';
9775  // signatures
9776  if ($this->sign AND isset($this->signature_data['cert_type'])) {
9777  if ($this->signature_data['cert_type'] > 0) {
9778  $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
9779  } else {
9780  $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
9781  }
9782  }
9783  }
9784  //$out .= ' /Legal <<>>';
9785  //$out .= ' /Requirements []';
9786  //$out .= ' /Collection <<>>';
9787  //$out .= ' /NeedsRendering true';
9788  $out .= ' >>';
9789  $out .= "\n".'endobj';
9790  $this->_out($out);
9791  return $oid;
9792  }
9793 
9801  protected function _putviewerpreferences() {
9803  $out = ' /ViewerPreferences <<';
9804  if ($this->rtl) {
9805  $out .= ' /Direction /R2L';
9806  } else {
9807  $out .= ' /Direction /L2R';
9808  }
9809  if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) {
9810  $out .= ' /HideToolbar true';
9811  }
9812  if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) {
9813  $out .= ' /HideMenubar true';
9814  }
9815  if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) {
9816  $out .= ' /HideWindowUI true';
9817  }
9818  if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) {
9819  $out .= ' /FitWindow true';
9820  }
9821  if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) {
9822  $out .= ' /CenterWindow true';
9823  }
9824  if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) {
9825  $out .= ' /DisplayDocTitle true';
9826  }
9827  if (isset($vp['NonFullScreenPageMode'])) {
9828  $out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode'];
9829  }
9830  if (isset($vp['ViewArea'])) {
9831  $out .= ' /ViewArea /'.$vp['ViewArea'];
9832  }
9833  if (isset($vp['ViewClip'])) {
9834  $out .= ' /ViewClip /'.$vp['ViewClip'];
9835  }
9836  if (isset($vp['PrintArea'])) {
9837  $out .= ' /PrintArea /'.$vp['PrintArea'];
9838  }
9839  if (isset($vp['PrintClip'])) {
9840  $out .= ' /PrintClip /'.$vp['PrintClip'];
9841  }
9842  if (isset($vp['PrintScaling'])) {
9843  $out .= ' /PrintScaling /'.$vp['PrintScaling'];
9844  }
9845  if (isset($vp['Duplex']) AND (!TCPDF_STATIC::empty_string($vp['Duplex']))) {
9846  $out .= ' /Duplex /'.$vp['Duplex'];
9847  }
9848  if (isset($vp['PickTrayByPDFSize'])) {
9849  if ($vp['PickTrayByPDFSize']) {
9850  $out .= ' /PickTrayByPDFSize true';
9851  } else {
9852  $out .= ' /PickTrayByPDFSize false';
9853  }
9854  }
9855  if (isset($vp['PrintPageRange'])) {
9856  $PrintPageRangeNum = '';
9857  foreach ($vp['PrintPageRange'] as $k => $v) {
9858  $PrintPageRangeNum .= ' '.($v - 1).'';
9859  }
9860  $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
9861  }
9862  if (isset($vp['NumCopies'])) {
9863  $out .= ' /NumCopies '.intval($vp['NumCopies']);
9864  }
9865  $out .= ' >>';
9866  return $out;
9867  }
9868 
9873  protected function _putheader() {
9874  $this->_out('%PDF-'.$this->PDFVersion);
9875  $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
9876  }
9877 
9882  protected function _enddoc() {
9883  if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
9884  // save subset chars of the previous font
9885  $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
9886  }
9887  $this->state = 1;
9888  $this->_putheader();
9889  $this->_putpages();
9890  $this->_putresources();
9891  // empty signature fields
9892  if (!empty($this->empty_signature_appearance)) {
9893  foreach ($this->empty_signature_appearance as $key => $esa) {
9894  // widget annotation for empty signature
9895  $out = $this->_getobj($esa['objid'])."\n";
9896  $out .= '<< /Type /Annot';
9897  $out .= ' /Subtype /Widget';
9898  $out .= ' /Rect ['.$esa['rect'].']';
9899  $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page
9900  $out .= ' /F 4';
9901  $out .= ' /FT /Sig';
9902  $signame = $esa['name'].sprintf(' [%03d]', ($key + 1));
9903  $out .= ' /T '.$this->_textstring($signame, $esa['objid']);
9904  $out .= ' /Ff 0';
9905  $out .= ' >>';
9906  $out .= "\n".'endobj';
9907  $this->_out($out);
9908  }
9909  }
9910  // Signature
9911  if ($this->sign AND isset($this->signature_data['cert_type'])) {
9912  // widget annotation for signature
9913  $out = $this->_getobj($this->sig_obj_id)."\n";
9914  $out .= '<< /Type /Annot';
9915  $out .= ' /Subtype /Widget';
9916  $out .= ' /Rect ['.$this->signature_appearance['rect'].']';
9917  $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
9918  $out .= ' /F 4';
9919  $out .= ' /FT /Sig';
9920  $out .= ' /T '.$this->_textstring($this->signature_appearance['name'], $this->sig_obj_id);
9921  $out .= ' /Ff 0';
9922  $out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
9923  $out .= ' >>';
9924  $out .= "\n".'endobj';
9925  $this->_out($out);
9926  // signature
9927  $this->_putsignature();
9928  }
9929  // Info
9930  $objid_info = $this->_putinfo();
9931  // Catalog
9932  $objid_catalog = $this->_putcatalog();
9933  // Cross-ref
9934  $o = $this->bufferlen;
9935  // XREF section
9936  $this->_out('xref');
9937  $this->_out('0 '.($this->n + 1));
9938  $this->_out('0000000000 65535 f ');
9939  $freegen = ($this->n + 2);
9940  for ($i=1; $i <= $this->n; ++$i) {
9941  if (!isset($this->offsets[$i]) AND ($i > 1)) {
9942  $this->_out(sprintf('0000000000 %05d f ', $freegen));
9943  ++$freegen;
9944  } else {
9945  $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
9946  }
9947  }
9948  // TRAILER
9949  $out = 'trailer'."\n";
9950  $out .= '<<';
9951  $out .= ' /Size '.($this->n + 1);
9952  $out .= ' /Root '.$objid_catalog.' 0 R';
9953  $out .= ' /Info '.$objid_info.' 0 R';
9954  if ($this->encrypted) {
9955  $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
9956  }
9957  $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
9958  $out .= ' >>';
9959  $this->_out($out);
9960  $this->_out('startxref');
9961  $this->_out($o);
9962  $this->_out('%%EOF');
9963  $this->state = 3; // end-of-doc
9964  if ($this->diskcache) {
9965  // remove temporary files used for images
9966  foreach ($this->imagekeys as $key) {
9967  // remove temporary files
9968  unlink($this->images[$key]);
9969  }
9970  foreach ($this->fontkeys as $key) {
9971  // remove temporary files
9972  unlink($this->fonts[$key]);
9973  }
9974  }
9975  }
9976 
9984  protected function _beginpage($orientation='', $format='') {
9985  ++$this->page;
9986  $this->pageobjects[$this->page] = array();
9987  $this->setPageBuffer($this->page, '');
9988  // initialize array for graphics tranformation positions inside a page buffer
9989  $this->transfmrk[$this->page] = array();
9990  $this->state = 2;
9991  if (TCPDF_STATIC::empty_string($orientation)) {
9992  if (isset($this->CurOrientation)) {
9993  $orientation = $this->CurOrientation;
9994  } elseif ($this->fwPt > $this->fhPt) {
9995  // landscape
9996  $orientation = 'L';
9997  } else {
9998  // portrait
9999  $orientation = 'P';
10000  }
10001  }
10002  if (TCPDF_STATIC::empty_string($format)) {
10003  $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
10004  $this->setPageOrientation($orientation);
10005  } else {
10006  $this->setPageFormat($format, $orientation);
10007  }
10008  if ($this->rtl) {
10009  $this->x = $this->w - $this->rMargin;
10010  } else {
10011  $this->x = $this->lMargin;
10012  }
10013  $this->y = $this->tMargin;
10014  if (isset($this->newpagegroup[$this->page])) {
10015  // start a new group
10016  $this->currpagegroup = $this->newpagegroup[$this->page];
10017  $this->pagegroups[$this->currpagegroup] = 1;
10018  } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
10019  ++$this->pagegroups[$this->currpagegroup];
10020  }
10021  }
10022 
10027  protected function _endpage() {
10028  $this->setVisibility('all');
10029  $this->state = 1;
10030  }
10031 
10037  protected function _newobj() {
10038  $this->_out($this->_getobj());
10039  return $this->n;
10040  }
10041 
10049  protected function _getobj($objid='') {
10050  if ($objid === '') {
10051  ++$this->n;
10052  $objid = $this->n;
10053  }
10054  $this->offsets[$objid] = $this->bufferlen;
10055  $this->pageobjects[$this->page][] = $objid;
10056  return $objid.' 0 obj';
10057  }
10058 
10066  protected function _dounderline($x, $y, $txt) {
10067  $w = $this->GetStringWidth($txt);
10068  return $this->_dounderlinew($x, $y, $w);
10069  }
10070 
10079  protected function _dounderlinew($x, $y, $w) {
10080  $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10081  return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
10082  }
10083 
10091  protected function _dolinethrough($x, $y, $txt) {
10092  $w = $this->GetStringWidth($txt);
10093  return $this->_dolinethroughw($x, $y, $w);
10094  }
10095 
10104  protected function _dolinethroughw($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 + ($this->FontSizePt / 3)), $w * $this->k, $linew);
10107  }
10108 
10117  protected function _dooverline($x, $y, $txt) {
10118  $w = $this->GetStringWidth($txt);
10119  return $this->_dooverlinew($x, $y, $w);
10120  }
10121 
10130  protected function _dooverlinew($x, $y, $w) {
10131  $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10132  return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
10133 
10134  }
10135 
10143  protected function _datastring($s, $n=0) {
10144  if ($n == 0) {
10145  $n = $this->n;
10146  }
10147  $s = $this->_encrypt_data($n, $s);
10148  return '('. TCPDF_STATIC::_escape($s).')';
10149  }
10150 
10157  public function setDocCreationTimestamp($time) {
10158  if (is_string($time)) {
10159  $time = TCPDF_STATIC::getTimestamp($time);
10160  }
10161  $this->doc_creation_timestamp = intval($time);
10162  }
10163 
10170  public function setDocModificationTimestamp($time) {
10171  if (is_string($time)) {
10172  $time = TCPDF_STATIC::getTimestamp($time);
10173  }
10174  $this->doc_modification_timestamp = intval($time);
10175  }
10176 
10183  public function getDocCreationTimestamp() {
10185  }
10186 
10193  public function getDocModificationTimestamp() {
10195  }
10196 
10205  protected function _datestring($n=0, $timestamp=0) {
10206  if ((empty($timestamp)) OR ($timestamp < 0)) {
10207  $timestamp = $this->doc_creation_timestamp;
10208  }
10209  return $this->_datastring('D:'.TCPDF_STATIC::getFormattedDate($timestamp), $n);
10210  }
10211 
10219  protected function _textstring($s, $n=0) {
10220  if ($this->isunicode) {
10221  //Convert string to UTF-16BE
10222  $s = TCPDF_FONTS::UTF8ToUTF16BE($s, true, $this->isunicode, $this->CurrentFont);
10223  }
10224  return $this->_datastring($s, $n);
10225  }
10226 
10235  protected function _escapetext($s) {
10236  if ($this->isunicode) {
10237  if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
10238  $s = TCPDF_FONTS::UTF8ToLatin1($s, $this->isunicode, $this->CurrentFont);
10239  } else {
10240  //Convert string to UTF-16BE and reverse RTL language
10241  $s = TCPDF_FONTS::utf8StrRev($s, false, $this->tmprtl, $this->isunicode, $this->CurrentFont);
10242  }
10243  }
10244  return TCPDF_STATIC::_escape($s);
10245  }
10246 
10255  protected function _getrawstream($s, $n=0) {
10256  if ($n <= 0) {
10257  // default to current object
10258  $n = $this->n;
10259  }
10260  return $this->_encrypt_data($n, $s);
10261  }
10262 
10270  protected function _getstream($s, $n=0) {
10271  return 'stream'."\n".$this->_getrawstream($s, $n)."\n".'endstream';
10272  }
10273 
10281  protected function _putstream($s, $n=0) {
10282  $this->_out($this->_getstream($s, $n));
10283  }
10284 
10290  protected function _out($s) {
10291  if ($this->state == 2) {
10292  if ($this->inxobj) {
10293  // we are inside an XObject template
10294  $this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
10295  } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
10296  // puts data before page footer
10297  $pagebuff = $this->getPageBuffer($this->page);
10298  $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
10299  $footer = substr($pagebuff, -$this->footerlen[$this->page]);
10300  $this->setPageBuffer($this->page, $page.$s."\n".$footer);
10301  // update footer position
10302  $this->footerpos[$this->page] += strlen($s."\n");
10303  } else {
10304  // set page data
10305  $this->setPageBuffer($this->page, $s."\n", true);
10306  }
10307  } elseif ($this->state > 0) {
10308  // set general data
10309  $this->setBuffer($s."\n");
10310  }
10311  }
10312 
10319  public function setHeaderFont($font) {
10320  $this->header_font = $font;
10321  }
10322 
10329  public function getHeaderFont() {
10330  return $this->header_font;
10331  }
10332 
10339  public function setFooterFont($font) {
10340  $this->footer_font = $font;
10341  }
10342 
10349  public function getFooterFont() {
10350  return $this->footer_font;
10351  }
10352 
10359  public function setLanguageArray($language) {
10360  $this->l = $language;
10361  if (isset($this->l['a_meta_dir'])) {
10362  $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
10363  } else {
10364  $this->rtl = false;
10365  }
10366  }
10367 
10372  public function getPDFData() {
10373  if ($this->state < 3) {
10374  $this->Close();
10375  }
10376  return $this->buffer;
10377  }
10378 
10391  public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
10392  if (isset($url[1]) AND ($url[0] == '#') AND is_numeric($url[1])) {
10393  // convert url to internal link
10394  $lnkdata = explode(',', $url);
10395  if (isset($lnkdata[0])) {
10396  $page = intval(substr($lnkdata[0], 1));
10397  if (empty($page) OR ($page <= 0)) {
10398  $page = $this->page;
10399  }
10400  if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
10401  $lnky = floatval($lnkdata[1]);
10402  } else {
10403  $lnky = 0;
10404  }
10405  $url = $this->AddLink();
10406  $this->SetLink($url, $lnky, $page);
10407  }
10408  }
10409  // store current settings
10410  $prevcolor = $this->fgcolor;
10411  $prevstyle = $this->FontStyle;
10412  if (empty($color)) {
10413  $this->SetTextColorArray($this->htmlLinkColorArray);
10414  } else {
10415  $this->SetTextColorArray($color);
10416  }
10417  if ($style == -1) {
10418  $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
10419  } else {
10420  $this->SetFont('', $this->FontStyle.$style);
10421  }
10422  $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
10423  // restore settings
10424  $this->SetFont('', $prevstyle);
10425  $this->SetTextColorArray($prevcolor);
10426  return $ret;
10427  }
10428 
10436  public function pixelsToUnits($px) {
10437  return ($px / ($this->imgscale * $this->k));
10438  }
10439 
10447  public function unhtmlentities($text_to_convert) {
10448  return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
10449  }
10450 
10451  // ENCRYPTION METHODS ----------------------------------
10452 
10462  protected function _objectkey($n) {
10463  $objkey = $this->encryptdata['key'].pack('VXxx', $n);
10464  if ($this->encryptdata['mode'] == 2) { // AES-128
10465  // AES padding
10466  $objkey .= "\x73\x41\x6C\x54"; // sAlT
10467  }
10468  $objkey = substr(TCPDF_STATIC::_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
10469  $objkey = substr($objkey, 0, 16);
10470  return $objkey;
10471  }
10472 
10482  protected function _encrypt_data($n, $s) {
10483  if (!$this->encrypted) {
10484  return $s;
10485  }
10486  switch ($this->encryptdata['mode']) {
10487  case 0: // RC4-40
10488  case 1: { // RC4-128
10489  $s = TCPDF_STATIC::_RC4($this->_objectkey($n), $s, $this->last_enc_key, $this->last_enc_key_c);
10490  break;
10491  }
10492  case 2: { // AES-128
10493  $s = TCPDF_STATIC::_AES($this->_objectkey($n), $s);
10494  break;
10495  }
10496  case 3: { // AES-256
10497  $s = TCPDF_STATIC::_AES($this->encryptdata['key'], $s);
10498  break;
10499  }
10500  }
10501  return $s;
10502  }
10503 
10510  protected function _putencryption() {
10511  if (!$this->encrypted) {
10512  return;
10513  }
10514  $this->encryptdata['objid'] = $this->_newobj();
10515  $out = '<<';
10516  if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
10517  $this->encryptdata['Filter'] = 'Standard';
10518  }
10519  $out .= ' /Filter /'.$this->encryptdata['Filter'];
10520  if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
10521  $out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
10522  }
10523  if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
10524  $this->encryptdata['V'] = 1;
10525  }
10526  // V is a code specifying the algorithm to be used in encrypting and decrypting the document
10527  $out .= ' /V '.$this->encryptdata['V'];
10528  if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
10529  // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
10530  $out .= ' /Length '.$this->encryptdata['Length'];
10531  } else {
10532  $out .= ' /Length 40';
10533  }
10534  if ($this->encryptdata['V'] >= 4) {
10535  if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
10536  $this->encryptdata['StmF'] = 'Identity';
10537  }
10538  if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
10539  // The name of the crypt filter that shall be used when decrypting all strings in the document.
10540  $this->encryptdata['StrF'] = 'Identity';
10541  }
10542  // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
10543  if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
10544  $out .= ' /CF <<';
10545  $out .= ' /'.$this->encryptdata['StmF'].' <<';
10546  $out .= ' /Type /CryptFilter';
10547  if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
10548  // The method used
10549  $out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
10550  if ($this->encryptdata['pubkey']) {
10551  $out .= ' /Recipients [';
10552  foreach ($this->encryptdata['Recipients'] as $rec) {
10553  $out .= ' <'.$rec.'>';
10554  }
10555  $out .= ' ]';
10556  if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
10557  $out .= ' /EncryptMetadata false';
10558  } else {
10559  $out .= ' /EncryptMetadata true';
10560  }
10561  }
10562  } else {
10563  $out .= ' /CFM /None';
10564  }
10565  if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
10566  // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
10567  $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
10568  } else {
10569  $out .= ' /AuthEvent /DocOpen';
10570  }
10571  if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
10572  // The bit length of the encryption key.
10573  $out .= ' /Length '.$this->encryptdata['CF']['Length'];
10574  }
10575  $out .= ' >> >>';
10576  }
10577  // The name of the crypt filter that shall be used by default when decrypting streams.
10578  $out .= ' /StmF /'.$this->encryptdata['StmF'];
10579  // The name of the crypt filter that shall be used when decrypting all strings in the document.
10580  $out .= ' /StrF /'.$this->encryptdata['StrF'];
10581  if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
10582  // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
10583  $out .= ' /EFF /'.$this->encryptdata[''];
10584  }
10585  }
10586  // Additional encryption dictionary entries for the standard security handler
10587  if ($this->encryptdata['pubkey']) {
10588  if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
10589  $out .= ' /Recipients [';
10590  foreach ($this->encryptdata['Recipients'] as $rec) {
10591  $out .= ' <'.$rec.'>';
10592  }
10593  $out .= ' ]';
10594  }
10595  } else {
10596  $out .= ' /R';
10597  if ($this->encryptdata['V'] == 5) { // AES-256
10598  $out .= ' 5';
10599  $out .= ' /OE ('.TCPDF_STATIC::_escape($this->encryptdata['OE']).')';
10600  $out .= ' /UE ('.TCPDF_STATIC::_escape($this->encryptdata['UE']).')';
10601  $out .= ' /Perms ('.TCPDF_STATIC::_escape($this->encryptdata['perms']).')';
10602  } elseif ($this->encryptdata['V'] == 4) { // AES-128
10603  $out .= ' 4';
10604  } elseif ($this->encryptdata['V'] < 2) { // RC-40
10605  $out .= ' 2';
10606  } else { // RC-128
10607  $out .= ' 3';
10608  }
10609  $out .= ' /O ('.TCPDF_STATIC::_escape($this->encryptdata['O']).')';
10610  $out .= ' /U ('.TCPDF_STATIC::_escape($this->encryptdata['U']).')';
10611  $out .= ' /P '.$this->encryptdata['P'];
10612  if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
10613  $out .= ' /EncryptMetadata false';
10614  } else {
10615  $out .= ' /EncryptMetadata true';
10616  }
10617  }
10618  $out .= ' >>';
10619  $out .= "\n".'endobj';
10620  $this->_out($out);
10621  }
10622 
10630  protected function _Uvalue() {
10631  if ($this->encryptdata['mode'] == 0) { // RC4-40
10632  return TCPDF_STATIC::_RC4($this->encryptdata['key'], TCPDF_STATIC::$enc_padding, $this->last_enc_key, $this->last_enc_key_c);
10633  } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
10634  $tmp = TCPDF_STATIC::_md5_16(TCPDF_STATIC::$enc_padding.$this->encryptdata['fileid']);
10635  $enc = TCPDF_STATIC::_RC4($this->encryptdata['key'], $tmp, $this->last_enc_key, $this->last_enc_key_c);
10636  $len = strlen($tmp);
10637  for ($i = 1; $i <= 19; ++$i) {
10638  $ek = '';
10639  for ($j = 0; $j < $len; ++$j) {
10640  $ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i);
10641  }
10642  $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10643  }
10644  $enc .= str_repeat("\x00", 16);
10645  return substr($enc, 0, 32);
10646  } elseif ($this->encryptdata['mode'] == 3) { // AES-256
10648  // User Validation Salt
10649  $this->encryptdata['UVS'] = substr($seed, 0, 8);
10650  // User Key Salt
10651  $this->encryptdata['UKS'] = substr($seed, 8, 16);
10652  return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
10653  }
10654  }
10655 
10663  protected function _UEvalue() {
10664  $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
10665  $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
10666  return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
10667  }
10668 
10676  protected function _Ovalue() {
10677  if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
10678  $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['owner_password']);
10679  if ($this->encryptdata['mode'] > 0) {
10680  for ($i = 0; $i < 50; ++$i) {
10681  $tmp = TCPDF_STATIC::_md5_16($tmp);
10682  }
10683  }
10684  $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
10685  $enc = TCPDF_STATIC::_RC4($owner_key, $this->encryptdata['user_password'], $this->last_enc_key, $this->last_enc_key_c);
10686  if ($this->encryptdata['mode'] > 0) {
10687  $len = strlen($owner_key);
10688  for ($i = 1; $i <= 19; ++$i) {
10689  $ek = '';
10690  for ($j = 0; $j < $len; ++$j) {
10691  $ek .= chr(ord($owner_key[$j]) ^ $i);
10692  }
10693  $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10694  }
10695  }
10696  return $enc;
10697  } elseif ($this->encryptdata['mode'] == 3) { // AES-256
10699  // Owner Validation Salt
10700  $this->encryptdata['OVS'] = substr($seed, 0, 8);
10701  // Owner Key Salt
10702  $this->encryptdata['OKS'] = substr($seed, 8, 16);
10703  return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
10704  }
10705  }
10706 
10714  protected function _OEvalue() {
10715  $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
10716  $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC));
10717  return mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $hashkey, $this->encryptdata['key'], MCRYPT_MODE_CBC, $iv);
10718  }
10719 
10728  protected function _fixAES256Password($password) {
10729  $psw = ''; // password to be returned
10730  $psw_array = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($password, $this->isunicode, $this->CurrentFont), $password, $this->rtl, $this->isunicode, $this->CurrentFont);
10731  foreach ($psw_array as $c) {
10732  $psw .= TCPDF_FONTS::unichr($c, $this->isunicode);
10733  }
10734  return substr($psw, 0, 127);
10735  }
10736 
10743  protected function _generateencryptionkey() {
10744  $keybytelen = ($this->encryptdata['Length'] / 8);
10745  if (!$this->encryptdata['pubkey']) { // standard mode
10746  if ($this->encryptdata['mode'] == 3) { // AES-256
10747  // generate 256 bit random key
10748  $this->encryptdata['key'] = substr(hash('sha256', TCPDF_STATIC::getRandomSeed(), true), 0, $keybytelen);
10749  // truncate passwords
10750  $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
10751  $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
10752  // Compute U value
10753  $this->encryptdata['U'] = $this->_Uvalue();
10754  // Compute UE value
10755  $this->encryptdata['UE'] = $this->_UEvalue();
10756  // Compute O value
10757  $this->encryptdata['O'] = $this->_Ovalue();
10758  // Compute OE value
10759  $this->encryptdata['OE'] = $this->_OEvalue();
10760  // Compute P value
10761  $this->encryptdata['P'] = $this->encryptdata['protection'];
10762  // Computing the encryption dictionary's Perms (permissions) value
10763  $perms = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
10764  $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
10765  if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
10766  $perms .= 'F';
10767  } else {
10768  $perms .= 'T';
10769  }
10770  $perms .= 'adb'; // bytes 9-11
10771  $perms .= 'nick'; // bytes 12-15
10772  $iv = str_repeat("\x00", mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB));
10773  $this->encryptdata['perms'] = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->encryptdata['key'], $perms, MCRYPT_MODE_ECB, $iv);
10774  } else { // RC4-40, RC4-128, AES-128
10775  // Pad passwords
10776  $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10777  $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10778  // Compute O value
10779  $this->encryptdata['O'] = $this->_Ovalue();
10780  // get default permissions (reverse byte order)
10781  $permissions = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']);
10782  // Compute encryption key
10783  $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
10784  if ($this->encryptdata['mode'] > 0) {
10785  for ($i = 0; $i < 50; ++$i) {
10786  $tmp = TCPDF_STATIC::_md5_16(substr($tmp, 0, $keybytelen));
10787  }
10788  }
10789  $this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
10790  // Compute U value
10791  $this->encryptdata['U'] = $this->_Uvalue();
10792  // Compute P value
10793  $this->encryptdata['P'] = $this->encryptdata['protection'];
10794  }
10795  } else { // Public-Key mode
10796  // random 20-byte seed
10797  $seed = sha1(TCPDF_STATIC::getRandomSeed(), true);
10798  $recipient_bytes = '';
10799  foreach ($this->encryptdata['pubkeys'] as $pubkey) {
10800  // for each public certificate
10801  if (isset($pubkey['p'])) {
10802  $pkprotection = TCPDF_STATIC::getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
10803  } else {
10804  $pkprotection = $this->encryptdata['protection'];
10805  }
10806  // get default permissions (reverse byte order)
10807  $pkpermissions = TCPDF_STATIC::getEncPermissionsString($pkprotection);
10808  // envelope data
10809  $envelope = $seed.$pkpermissions;
10810  // write the envelope data to a temporary file
10811  $tempkeyfile = TCPDF_STATIC::getObjFilename('key');
10812  $f = fopen($tempkeyfile, 'wb');
10813  if (!$f) {
10814  $this->Error('Unable to create temporary key file: '.$tempkeyfile);
10815  }
10816  $envelope_length = strlen($envelope);
10817  fwrite($f, $envelope, $envelope_length);
10818  fclose($f);
10819  $tempencfile = TCPDF_STATIC::getObjFilename('enc');
10820  if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) {
10821  $this->Error('Unable to encrypt the file: '.$tempkeyfile);
10822  }
10823  unlink($tempkeyfile);
10824  // read encryption signature
10825  $signature = file_get_contents($tempencfile, false, null, $envelope_length);
10826  unlink($tempencfile);
10827  // extract signature
10828  $signature = substr($signature, strpos($signature, 'Content-Disposition'));
10829  $tmparr = explode("\n\n", $signature);
10830  $signature = trim($tmparr[1]);
10831  unset($tmparr);
10832  // decode signature
10833  $signature = base64_decode($signature);
10834  // convert signature to hex
10835  $hexsignature = current(unpack('H*', $signature));
10836  // store signature on recipients array
10837  $this->encryptdata['Recipients'][] = $hexsignature;
10838  // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
10839  $recipient_bytes .= $signature;
10840  }
10841  // calculate encryption key
10842  if ($this->encryptdata['mode'] == 3) { // AES-256
10843  $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
10844  } else { // RC4-40, RC4-128, AES-128
10845  $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
10846  }
10847  }
10848  }
10849 
10864  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) {
10865  if ($this->pdfa_mode) {
10866  // encryption is not allowed in PDF/A mode
10867  return;
10868  }
10869  $this->encryptdata['protection'] = TCPDF_STATIC::getUserPermissionCode($permissions, $mode);
10870  if (($pubkeys !== null) AND (is_array($pubkeys))) {
10871  // public-key mode
10872  $this->encryptdata['pubkeys'] = $pubkeys;
10873  if ($mode == 0) {
10874  // public-Key Security requires at least 128 bit
10875  $mode = 1;
10876  }
10877  if (!function_exists('openssl_pkcs7_encrypt')) {
10878  $this->Error('Public-Key Security requires openssl library.');
10879  }
10880  // Set Public-Key filter (availabe are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
10881  $this->encryptdata['pubkey'] = true;
10882  $this->encryptdata['Filter'] = 'Adobe.PubSec';
10883  $this->encryptdata['StmF'] = 'DefaultCryptFilter';
10884  $this->encryptdata['StrF'] = 'DefaultCryptFilter';
10885  } else {
10886  // standard mode (password mode)
10887  $this->encryptdata['pubkey'] = false;
10888  $this->encryptdata['Filter'] = 'Standard';
10889  $this->encryptdata['StmF'] = 'StdCF';
10890  $this->encryptdata['StrF'] = 'StdCF';
10891  }
10892  if ($mode > 1) { // AES
10893  if (!extension_loaded('mcrypt')) {
10894  $this->Error('AES encryption requires mcrypt library (http://www.php.net/manual/en/mcrypt.requirements.php).');
10895  }
10896  if (mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
10897  $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
10898  }
10899  if (($mode == 3) AND !function_exists('hash')) {
10900  // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
10901  $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
10902  }
10903  }
10904  if ($owner_pass === null) {
10905  $owner_pass = md5(TCPDF_STATIC::getRandomSeed());
10906  }
10907  $this->encryptdata['user_password'] = $user_pass;
10908  $this->encryptdata['owner_password'] = $owner_pass;
10909  $this->encryptdata['mode'] = $mode;
10910  switch ($mode) {
10911  case 0: { // RC4 40 bit
10912  $this->encryptdata['V'] = 1;
10913  $this->encryptdata['Length'] = 40;
10914  $this->encryptdata['CF']['CFM'] = 'V2';
10915  break;
10916  }
10917  case 1: { // RC4 128 bit
10918  $this->encryptdata['V'] = 2;
10919  $this->encryptdata['Length'] = 128;
10920  $this->encryptdata['CF']['CFM'] = 'V2';
10921  if ($this->encryptdata['pubkey']) {
10922  $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
10923  $this->encryptdata['Recipients'] = array();
10924  }
10925  break;
10926  }
10927  case 2: { // AES 128 bit
10928  $this->encryptdata['V'] = 4;
10929  $this->encryptdata['Length'] = 128;
10930  $this->encryptdata['CF']['CFM'] = 'AESV2';
10931  $this->encryptdata['CF']['Length'] = 128;
10932  if ($this->encryptdata['pubkey']) {
10933  $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
10934  $this->encryptdata['Recipients'] = array();
10935  }
10936  break;
10937  }
10938  case 3: { // AES 256 bit
10939  $this->encryptdata['V'] = 5;
10940  $this->encryptdata['Length'] = 256;
10941  $this->encryptdata['CF']['CFM'] = 'AESV3';
10942  $this->encryptdata['CF']['Length'] = 256;
10943  if ($this->encryptdata['pubkey']) {
10944  $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
10945  $this->encryptdata['Recipients'] = array();
10946  }
10947  break;
10948  }
10949  }
10950  $this->encrypted = true;
10951  $this->encryptdata['fileid'] = TCPDF_STATIC::convertHexStringToString($this->file_id);
10952  $this->_generateencryptionkey();
10953  }
10954 
10955  // END OF ENCRYPTION FUNCTIONS -------------------------
10956 
10957  // START TRANSFORMATIONS SECTION -----------------------
10958 
10967  public function StartTransform() {
10968  if ($this->state != 2) {
10969  return;
10970  }
10971  $this->_outSaveGraphicsState();
10972  if ($this->inxobj) {
10973  // we are inside an XObject template
10974  $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
10975  } else {
10976  $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
10977  }
10979  $this->transfmatrix[$this->transfmatrix_key] = array();
10980  }
10981 
10990  public function StopTransform() {
10991  if ($this->state != 2) {
10992  return;
10993  }
10994  $this->_outRestoreGraphicsState();
10995  if (isset($this->transfmatrix[$this->transfmatrix_key])) {
10996  array_pop($this->transfmatrix[$this->transfmatrix_key]);
10998  }
10999  if ($this->inxobj) {
11000  // we are inside an XObject template
11001  array_pop($this->xobjects[$this->xobjid]['transfmrk']);
11002  } else {
11003  array_pop($this->transfmrk[$this->page]);
11004  }
11005  }
11015  public function ScaleX($s_x, $x='', $y='') {
11016  $this->Scale($s_x, 100, $x, $y);
11017  }
11018 
11028  public function ScaleY($s_y, $x='', $y='') {
11029  $this->Scale(100, $s_y, $x, $y);
11030  }
11031 
11041  public function ScaleXY($s, $x='', $y='') {
11042  $this->Scale($s, $s, $x, $y);
11043  }
11044 
11055  public function Scale($s_x, $s_y, $x='', $y='') {
11056  if ($x === '') {
11057  $x = $this->x;
11058  }
11059  if ($y === '') {
11060  $y = $this->y;
11061  }
11062  if (($s_x == 0) OR ($s_y == 0)) {
11063  $this->Error('Please do not use values equal to zero for scaling');
11064  }
11065  $y = ($this->h - $y) * $this->k;
11066  $x *= $this->k;
11067  //calculate elements of transformation matrix
11068  $s_x /= 100;
11069  $s_y /= 100;
11070  $tm = array();
11071  $tm[0] = $s_x;
11072  $tm[1] = 0;
11073  $tm[2] = 0;
11074  $tm[3] = $s_y;
11075  $tm[4] = $x * (1 - $s_x);
11076  $tm[5] = $y * (1 - $s_y);
11077  //scale the coordinate system
11078  $this->Transform($tm);
11079  }
11080 
11088  public function MirrorH($x='') {
11089  $this->Scale(-100, 100, $x);
11090  }
11091 
11099  public function MirrorV($y='') {
11100  $this->Scale(100, -100, '', $y);
11101  }
11102 
11111  public function MirrorP($x='',$y='') {
11112  $this->Scale(-100, -100, $x, $y);
11113  }
11114 
11124  public function MirrorL($angle=0, $x='',$y='') {
11125  $this->Scale(-100, 100, $x, $y);
11126  $this->Rotate(-2*($angle-90), $x, $y);
11127  }
11128 
11136  public function TranslateX($t_x) {
11137  $this->Translate($t_x, 0);
11138  }
11139 
11147  public function TranslateY($t_y) {
11148  $this->Translate(0, $t_y);
11149  }
11150 
11159  public function Translate($t_x, $t_y) {
11160  //calculate elements of transformation matrix
11161  $tm = array();
11162  $tm[0] = 1;
11163  $tm[1] = 0;
11164  $tm[2] = 0;
11165  $tm[3] = 1;
11166  $tm[4] = $t_x * $this->k;
11167  $tm[5] = -$t_y * $this->k;
11168  //translate the coordinate system
11169  $this->Transform($tm);
11170  }
11171 
11181  public function Rotate($angle, $x='', $y='') {
11182  if ($x === '') {
11183  $x = $this->x;
11184  }
11185  if ($y === '') {
11186  $y = $this->y;
11187  }
11188  $y = ($this->h - $y) * $this->k;
11189  $x *= $this->k;
11190  //calculate elements of transformation matrix
11191  $tm = array();
11192  $tm[0] = cos(deg2rad($angle));
11193  $tm[1] = sin(deg2rad($angle));
11194  $tm[2] = -$tm[1];
11195  $tm[3] = $tm[0];
11196  $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
11197  $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
11198  //rotate the coordinate system around ($x,$y)
11199  $this->Transform($tm);
11200  }
11201 
11211  public function SkewX($angle_x, $x='', $y='') {
11212  $this->Skew($angle_x, 0, $x, $y);
11213  }
11214 
11224  public function SkewY($angle_y, $x='', $y='') {
11225  $this->Skew(0, $angle_y, $x, $y);
11226  }
11227 
11238  public function Skew($angle_x, $angle_y, $x='', $y='') {
11239  if ($x === '') {
11240  $x = $this->x;
11241  }
11242  if ($y === '') {
11243  $y = $this->y;
11244  }
11245  if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
11246  $this->Error('Please use values between -90 and +90 degrees for Skewing.');
11247  }
11248  $x *= $this->k;
11249  $y = ($this->h - $y) * $this->k;
11250  //calculate elements of transformation matrix
11251  $tm = array();
11252  $tm[0] = 1;
11253  $tm[1] = tan(deg2rad($angle_y));
11254  $tm[2] = tan(deg2rad($angle_x));
11255  $tm[3] = 1;
11256  $tm[4] = -$tm[2] * $y;
11257  $tm[5] = -$tm[1] * $x;
11258  //skew the coordinate system
11259  $this->Transform($tm);
11260  }
11261 
11269  protected function Transform($tm) {
11270  if ($this->state != 2) {
11271  return;
11272  }
11273  $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
11274  // add tranformation matrix
11275  $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
11276  // update transformation mark
11277  if ($this->inxobj) {
11278  // we are inside an XObject template
11279  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
11280  $key = key($this->xobjects[$this->xobjid]['transfmrk']);
11281  $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
11282  }
11283  } elseif (end($this->transfmrk[$this->page]) !== false) {
11284  $key = key($this->transfmrk[$this->page]);
11285  $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
11286  }
11287  }
11288 
11289  // END TRANSFORMATIONS SECTION -------------------------
11290 
11291  // START GRAPHIC FUNCTIONS SECTION ---------------------
11292  // The following section is based on the code provided by David Hernandez Sanz
11293 
11301  public function SetLineWidth($width) {
11302  //Set line width
11303  $this->LineWidth = $width;
11304  $this->linestyleWidth = sprintf('%F w', ($width * $this->k));
11305  if ($this->state == 2) {
11306  $this->_out($this->linestyleWidth);
11307  }
11308  }
11309 
11317  public function GetLineWidth() {
11318  return $this->LineWidth;
11319  }
11320 
11344  public function SetLineStyle($style, $ret=false) {
11345  $s = ''; // string to be returned
11346  if (!is_array($style)) {
11347  return;
11348  }
11349  if (isset($style['width'])) {
11350  $this->LineWidth = $style['width'];
11351  $this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k));
11352  $s .= $this->linestyleWidth.' ';
11353  }
11354  if (isset($style['cap'])) {
11355  $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
11356  if (isset($ca[$style['cap']])) {
11357  $this->linestyleCap = $ca[$style['cap']].' J';
11358  $s .= $this->linestyleCap.' ';
11359  }
11360  }
11361  if (isset($style['join'])) {
11362  $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
11363  if (isset($ja[$style['join']])) {
11364  $this->linestyleJoin = $ja[$style['join']].' j';
11365  $s .= $this->linestyleJoin.' ';
11366  }
11367  }
11368  if (isset($style['dash'])) {
11369  $dash_string = '';
11370  if ($style['dash']) {
11371  if (preg_match('/^.+,/', $style['dash']) > 0) {
11372  $tab = explode(',', $style['dash']);
11373  } else {
11374  $tab = array($style['dash']);
11375  }
11376  $dash_string = '';
11377  foreach ($tab as $i => $v) {
11378  if ($i) {
11379  $dash_string .= ' ';
11380  }
11381  $dash_string .= sprintf('%F', $v);
11382  }
11383  }
11384  if (!isset($style['phase']) OR !$style['dash']) {
11385  $style['phase'] = 0;
11386  }
11387  $this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']);
11388  $s .= $this->linestyleDash.' ';
11389  }
11390  if (isset($style['color'])) {
11391  $s .= $this->SetDrawColorArray($style['color'], true).' ';
11392  }
11393  if (!$ret AND ($this->state == 2)) {
11394  $this->_out($s);
11395  }
11396  return $s;
11397  }
11398 
11406  protected function _outPoint($x, $y) {
11407  if ($this->state == 2) {
11408  $this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k)));
11409  }
11410  }
11411 
11420  protected function _outLine($x, $y) {
11421  if ($this->state == 2) {
11422  $this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k)));
11423  }
11424  }
11425 
11436  protected function _outRect($x, $y, $w, $h, $op) {
11437  if ($this->state == 2) {
11438  $this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k), (($this->h - $y) * $this->k), ($w * $this->k), (-$h * $this->k), $op));
11439  }
11440  }
11441 
11454  protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
11455  if ($this->state == 2) {
11456  $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)));
11457  }
11458  }
11459 
11470  protected function _outCurveV($x2, $y2, $x3, $y3) {
11471  if ($this->state == 2) {
11472  $this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11473  }
11474  }
11475 
11486  protected function _outCurveY($x1, $y1, $x3, $y3) {
11487  if ($this->state == 2) {
11488  $this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11489  }
11490  }
11491 
11503  public function Line($x1, $y1, $x2, $y2, $style=array()) {
11504  if ($this->state != 2) {
11505  return;
11506  }
11507  if (is_array($style)) {
11508  $this->SetLineStyle($style);
11509  }
11510  $this->_outPoint($x1, $y1);
11511  $this->_outLine($x2, $y2);
11512  $this->_out('S');
11513  }
11514 
11533  public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
11534  if ($this->state != 2) {
11535  return;
11536  }
11537  if (empty($style)) {
11538  $style = 'S';
11539  }
11540  if (!(strpos($style, 'F') === false) AND !empty($fill_color)) {
11541  // set background color
11542  $this->SetFillColorArray($fill_color);
11543  }
11544  if (!empty($border_style)) {
11545  if (isset($border_style['all']) AND !empty($border_style['all'])) {
11546  //set global style for border
11547  $this->SetLineStyle($border_style['all']);
11548  $border_style = array();
11549  } else {
11550  // remove stroke operator from style
11551  $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*' );
11552  if (isset($opnostroke[$style])) {
11553  $style = $opnostroke[$style];
11554  }
11555  }
11556  }
11557  if (!empty($style)) {
11558  $op = TCPDF_STATIC::getPathPaintOperator($style);
11559  $this->_outRect($x, $y, $w, $h, $op);
11560  }
11561  if (!empty($border_style)) {
11562  $border_style2 = array();
11563  foreach ($border_style as $line => $value) {
11564  $length = strlen($line);
11565  for ($i = 0; $i < $length; ++$i) {
11566  $border_style2[$line[$i]] = $value;
11567  }
11568  }
11569  $border_style = $border_style2;
11570  if (isset($border_style['L']) AND $border_style['L']) {
11571  $this->Line($x, $y, $x, $y + $h, $border_style['L']);
11572  }
11573  if (isset($border_style['T']) AND $border_style['T']) {
11574  $this->Line($x, $y, $x + $w, $y, $border_style['T']);
11575  }
11576  if (isset($border_style['R']) AND $border_style['R']) {
11577  $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
11578  }
11579  if (isset($border_style['B']) AND $border_style['B']) {
11580  $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
11581  }
11582  }
11583  }
11584 
11604  public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
11605  if ($this->state != 2) {
11606  return;
11607  }
11608  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11609  $this->SetFillColorArray($fill_color);
11610  }
11611  $op = TCPDF_STATIC::getPathPaintOperator($style);
11612  if ($line_style) {
11613  $this->SetLineStyle($line_style);
11614  }
11615  $this->_outPoint($x0, $y0);
11616  $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11617  $this->_out($op);
11618  }
11619 
11634  public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
11635  if ($this->state != 2) {
11636  return;
11637  }
11638  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11639  $this->SetFillColorArray($fill_color);
11640  }
11641  $op = TCPDF_STATIC::getPathPaintOperator($style);
11642  if ($op == 'f') {
11643  $line_style = array();
11644  }
11645  if ($line_style) {
11646  $this->SetLineStyle($line_style);
11647  }
11648  $this->_outPoint($x0, $y0);
11649  foreach ($segments as $segment) {
11650  list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
11651  $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11652  }
11653  $this->_out($op);
11654  }
11655 
11674  public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11675  if ($this->state != 2) {
11676  return;
11677  }
11678  if (TCPDF_STATIC::empty_string($ry) OR ($ry == 0)) {
11679  $ry = $rx;
11680  }
11681  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11682  $this->SetFillColorArray($fill_color);
11683  }
11684  $op = TCPDF_STATIC::getPathPaintOperator($style);
11685  if ($op == 'f') {
11686  $line_style = array();
11687  }
11688  if ($line_style) {
11689  $this->SetLineStyle($line_style);
11690  }
11691  $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
11692  $this->_out($op);
11693  }
11694 
11715  protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
11716  if (($rx <= 0) OR ($ry < 0)) {
11717  return;
11718  }
11719  $k = $this->k;
11720  if ($nc < 2) {
11721  $nc = 2;
11722  }
11723  $xmin = 2147483647;
11724  $ymin = 2147483647;
11725  $xmax = 0;
11726  $ymax = 0;
11727  if ($pie) {
11728  // center of the arc
11729  $this->_outPoint($xc, $yc);
11730  }
11731  $xang = deg2rad((float) $xang);
11732  $angs = deg2rad((float) $angs);
11733  $angf = deg2rad((float) $angf);
11734  if ($svg) {
11735  $as = $angs;
11736  $af = $angf;
11737  } else {
11738  $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
11739  $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
11740  }
11741  if ($as < 0) {
11742  $as += (2 * M_PI);
11743  }
11744  if ($af < 0) {
11745  $af += (2 * M_PI);
11746  }
11747  if ($ccw AND ($as > $af)) {
11748  // reverse rotation
11749  $as -= (2 * M_PI);
11750  } elseif (!$ccw AND ($as < $af)) {
11751  // reverse rotation
11752  $af -= (2 * M_PI);
11753  }
11754  $total_angle = ($af - $as);
11755  if ($nc < 2) {
11756  $nc = 2;
11757  }
11758  // total arcs to draw
11759  $nc *= (2 * abs($total_angle) / M_PI);
11760  $nc = round($nc) + 1;
11761  // angle of each arc
11762  $arcang = ($total_angle / $nc);
11763  // center point in PDF coordinates
11764  $x0 = $xc;
11765  $y0 = ($this->h - $yc);
11766  // starting angle
11767  $ang = $as;
11768  $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
11769  $cos_xang = cos($xang);
11770  $sin_xang = sin($xang);
11771  $cos_ang = cos($ang);
11772  $sin_ang = sin($ang);
11773  // first arc point
11774  $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11775  $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11776  // first Bezier control point
11777  $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11778  $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11779  if ($pie) {
11780  // line from center to arc starting point
11781  $this->_outLine($px1, $this->h - $py1);
11782  } elseif ($startpoint) {
11783  // arc starting point
11784  $this->_outPoint($px1, $this->h - $py1);
11785  }
11786  // draw arcs
11787  for ($i = 1; $i <= $nc; ++$i) {
11788  // starting angle
11789  $ang = $as + ($i * $arcang);
11790  if ($i == $nc) {
11791  $ang = $af;
11792  }
11793  $cos_ang = cos($ang);
11794  $sin_ang = sin($ang);
11795  // second arc point
11796  $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11797  $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11798  // second Bezier control point
11799  $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11800  $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11801  // draw arc
11802  $cx1 = ($px1 + $qx1);
11803  $cy1 = ($this->h - ($py1 + $qy1));
11804  $cx2 = ($px2 - $qx2);
11805  $cy2 = ($this->h - ($py2 - $qy2));
11806  $cx3 = $px2;
11807  $cy3 = ($this->h - $py2);
11808  $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
11809  // get bounding box coordinates
11810  $xmin = min($xmin, $cx1, $cx2, $cx3);
11811  $ymin = min($ymin, $cy1, $cy2, $cy3);
11812  $xmax = max($xmax, $cx1, $cx2, $cx3);
11813  $ymax = max($ymax, $cy1, $cy2, $cy3);
11814  // move to next point
11815  $px1 = $px2;
11816  $py1 = $py2;
11817  $qx1 = $qx2;
11818  $qy1 = $qy2;
11819  }
11820  if ($pie) {
11821  $this->_outLine($xc, $yc);
11822  // get bounding box coordinates
11823  $xmin = min($xmin, $xc);
11824  $ymin = min($ymin, $yc);
11825  $xmax = max($xmax, $xc);
11826  $ymax = max($ymax, $yc);
11827  }
11828  return array($xmin, $ymin, $xmax, $ymax);
11829  }
11830 
11846  public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11847  $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
11848  }
11849 
11864  public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
11865  $this->Polygon($p, $style, $line_style, $fill_color, false);
11866  }
11867 
11883  public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
11884  if ($this->state != 2) {
11885  return;
11886  }
11887  $nc = count($p); // number of coordinates
11888  $np = $nc / 2; // number of points
11889  if ($closed) {
11890  // close polygon by adding the first 2 points at the end (one line)
11891  for ($i = 0; $i < 4; ++$i) {
11892  $p[$nc + $i] = $p[$i];
11893  }
11894  // copy style for the last added line
11895  if (isset($line_style[0])) {
11896  $line_style[$np] = $line_style[0];
11897  }
11898  $nc += 4;
11899  }
11900  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11901  $this->SetFillColorArray($fill_color);
11902  }
11903  $op = TCPDF_STATIC::getPathPaintOperator($style);
11904  if ($op == 'f') {
11905  $line_style = array();
11906  }
11907  $draw = true;
11908  if ($line_style) {
11909  if (isset($line_style['all'])) {
11910  $this->SetLineStyle($line_style['all']);
11911  } else {
11912  $draw = false;
11913  if ($op == 'B') {
11914  // draw fill
11915  $op = 'f';
11916  $this->_outPoint($p[0], $p[1]);
11917  for ($i = 2; $i < $nc; $i = $i + 2) {
11918  $this->_outLine($p[$i], $p[$i + 1]);
11919  }
11920  $this->_out($op);
11921  }
11922  // draw outline
11923  $this->_outPoint($p[0], $p[1]);
11924  for ($i = 2; $i < $nc; $i = $i + 2) {
11925  $line_num = ($i / 2) - 1;
11926  if (isset($line_style[$line_num])) {
11927  if ($line_style[$line_num] != 0) {
11928  if (is_array($line_style[$line_num])) {
11929  $this->_out('S');
11930  $this->SetLineStyle($line_style[$line_num]);
11931  $this->_outPoint($p[$i - 2], $p[$i - 1]);
11932  $this->_outLine($p[$i], $p[$i + 1]);
11933  $this->_out('S');
11934  $this->_outPoint($p[$i], $p[$i + 1]);
11935  } else {
11936  $this->_outLine($p[$i], $p[$i + 1]);
11937  }
11938  }
11939  } else {
11940  $this->_outLine($p[$i], $p[$i + 1]);
11941  }
11942  }
11943  $this->_out($op);
11944  }
11945  }
11946  if ($draw) {
11947  $this->_outPoint($p[0], $p[1]);
11948  for ($i = 2; $i < $nc; $i = $i + 2) {
11949  $this->_outLine($p[$i], $p[$i + 1]);
11950  }
11951  $this->_out($op);
11952  }
11953  }
11954 
11984  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()) {
11985  if (3 > $ns) {
11986  $ns = 3;
11987  }
11988  if ($draw_circle) {
11989  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
11990  }
11991  $p = array();
11992  for ($i = 0; $i < $ns; ++$i) {
11993  $a = $angle + ($i * 360 / $ns);
11994  $a_rad = deg2rad((float) $a);
11995  $p[] = $x0 + ($r * sin($a_rad));
11996  $p[] = $y0 + ($r * cos($a_rad));
11997  }
11998  $this->Polygon($p, $style, $line_style, $fill_color);
11999  }
12000 
12032  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()) {
12033  if ($nv < 2) {
12034  $nv = 2;
12035  }
12036  if ($draw_circle) {
12037  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12038  }
12039  $p2 = array();
12040  $visited = array();
12041  for ($i = 0; $i < $nv; ++$i) {
12042  $a = $angle + ($i * 360 / $nv);
12043  $a_rad = deg2rad((float) $a);
12044  $p2[] = $x0 + ($r * sin($a_rad));
12045  $p2[] = $y0 + ($r * cos($a_rad));
12046  $visited[] = false;
12047  }
12048  $p = array();
12049  $i = 0;
12050  do {
12051  $p[] = $p2[$i * 2];
12052  $p[] = $p2[($i * 2) + 1];
12053  $visited[$i] = true;
12054  $i += $ng;
12055  $i %= $nv;
12056  } while (!$visited[$i]);
12057  $this->Polygon($p, $style, $line_style, $fill_color);
12058  }
12059 
12074  public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12075  $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
12076  }
12077 
12093  public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12094  if ($this->state != 2) {
12095  return;
12096  }
12097  if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
12098  // Not rounded
12099  $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
12100  return;
12101  }
12102  // Rounded
12103  if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12104  $this->SetFillColorArray($fill_color);
12105  }
12106  $op = TCPDF_STATIC::getPathPaintOperator($style);
12107  if ($op == 'f') {
12108  $border_style = array();
12109  }
12110  if ($border_style) {
12111  $this->SetLineStyle($border_style);
12112  }
12113  $MyArc = 4 / 3 * (sqrt(2) - 1);
12114  $this->_outPoint($x + $rx, $y);
12115  $xc = $x + $w - $rx;
12116  $yc = $y + $ry;
12117  $this->_outLine($xc, $y);
12118  if ($round_corner[0]) {
12119  $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
12120  } else {
12121  $this->_outLine($x + $w, $y);
12122  }
12123  $xc = $x + $w - $rx;
12124  $yc = $y + $h - $ry;
12125  $this->_outLine($x + $w, $yc);
12126  if ($round_corner[1]) {
12127  $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
12128  } else {
12129  $this->_outLine($x + $w, $y + $h);
12130  }
12131  $xc = $x + $rx;
12132  $yc = $y + $h - $ry;
12133  $this->_outLine($xc, $y + $h);
12134  if ($round_corner[2]) {
12135  $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
12136  } else {
12137  $this->_outLine($x, $y + $h);
12138  }
12139  $xc = $x + $rx;
12140  $yc = $y + $ry;
12141  $this->_outLine($x, $yc);
12142  if ($round_corner[3]) {
12143  $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
12144  } else {
12145  $this->_outLine($x, $y);
12146  $this->_outLine($x + $rx, $y);
12147  }
12148  $this->_out($op);
12149  }
12150 
12163  public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
12164  // getting arrow direction angle
12165  // 0 deg angle is when both arms go along X axis. angle grows clockwise.
12166  $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
12167  if ($dir_angle < 0) {
12168  $dir_angle += (2 * M_PI);
12169  }
12170  $arm_angle = deg2rad($arm_angle);
12171  $sx1 = $x1;
12172  $sy1 = $y1;
12173  if ($head_style > 0) {
12174  // calculate the stopping point for the arrow shaft
12175  $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
12176  $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
12177  }
12178  // main arrow line / shaft
12179  $this->Line($x0, $y0, $sx1, $sy1);
12180  // left arrowhead arm tip
12181  $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
12182  $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
12183  // right arrowhead arm tip
12184  $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
12185  $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
12186  $mode = 'D';
12187  $style = array();
12188  switch ($head_style) {
12189  case 0: {
12190  // draw only arrowhead arms
12191  $mode = 'D';
12192  $style = array(1, 1, 0);
12193  break;
12194  }
12195  case 1: {
12196  // draw closed arrowhead, but no fill
12197  $mode = 'D';
12198  break;
12199  }
12200  case 2: {
12201  // closed and filled arrowhead
12202  $mode = 'DF';
12203  break;
12204  }
12205  case 3: {
12206  // filled arrowhead
12207  $mode = 'F';
12208  break;
12209  }
12210  }
12211  $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
12212  }
12213 
12214  // END GRAPHIC FUNCTIONS SECTION -----------------------
12215 
12228  public function setDestination($name, $y=-1, $page='', $x=-1) {
12229  // remove unsupported characters
12230  $name = TCPDF_STATIC::encodeNameObject($name);
12231  if (TCPDF_STATIC::empty_string($name)) {
12232  return false;
12233  }
12234  if ($y == -1) {
12235  $y = $this->GetY();
12236  } elseif ($y < 0) {
12237  $y = 0;
12238  } elseif ($y > $this->h) {
12239  $y = $this->h;
12240  }
12241  if ($x == -1) {
12242  $x = $this->GetX();
12243  } elseif ($x < 0) {
12244  $x = 0;
12245  } elseif ($x > $this->w) {
12246  $x = $this->w;
12247  }
12248  if (empty($page)) {
12249  $page = $this->PageNo();
12250  if (empty($page)) {
12251  return;
12252  }
12253  }
12254  $this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page);
12255  return $name;
12256  }
12257 
12265  public function getDestination() {
12266  return $this->dests;
12267  }
12268 
12275  protected function _putdests() {
12276  if (empty($this->dests)) {
12277  return;
12278  }
12279  $this->n_dests = $this->_newobj();
12280  $out = ' <<';
12281  foreach($this->dests as $name => $o) {
12282  $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)));
12283  }
12284  $out .= ' >>';
12285  $out .= "\n".'endobj';
12286  $this->_out($out);
12287  }
12288 
12301  public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12302  $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link);
12303  }
12304 
12318  public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12319  if ($level < 0) {
12320  $level = 0;
12321  }
12322  if (isset($this->outlines[0])) {
12323  $lastoutline = end($this->outlines);
12324  $maxlevel = $lastoutline['l'] + 1;
12325  } else {
12326  $maxlevel = 0;
12327  }
12328  if ($level > $maxlevel) {
12329  $level = $maxlevel;
12330  }
12331  if ($y == -1) {
12332  $y = $this->GetY();
12333  } elseif ($y < 0) {
12334  $y = 0;
12335  } elseif ($y > $this->h) {
12336  $y = $this->h;
12337  }
12338  if ($x == -1) {
12339  $x = $this->GetX();
12340  } elseif ($x < 0) {
12341  $x = 0;
12342  } elseif ($x > $this->w) {
12343  $x = $this->w;
12344  }
12345  if (empty($page)) {
12346  $page = $this->PageNo();
12347  if (empty($page)) {
12348  return;
12349  }
12350  }
12351  $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 's' => strtoupper($style), 'c' => $color, 'u' => $link);
12352  }
12353 
12359  protected function sortBookmarks() {
12360  // get sorting columns
12361  $outline_p = array();
12362  $outline_y = array();
12363  foreach ($this->outlines as $key => $row) {
12364  $outline_p[$key] = $row['p'];
12365  $outline_k[$key] = $key;
12366  }
12367  // sort outlines by page and original position
12368  array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
12369  }
12370 
12377  protected function _putbookmarks() {
12378  $nb = count($this->outlines);
12379  if ($nb == 0) {
12380  return;
12381  }
12382  // sort bookmarks
12383  $this->sortBookmarks();
12384  $lru = array();
12385  $level = 0;
12386  foreach ($this->outlines as $i => $o) {
12387  if ($o['l'] > 0) {
12388  $parent = $lru[($o['l'] - 1)];
12389  //Set parent and last pointers
12390  $this->outlines[$i]['parent'] = $parent;
12391  $this->outlines[$parent]['last'] = $i;
12392  if ($o['l'] > $level) {
12393  //Level increasing: set first pointer
12394  $this->outlines[$parent]['first'] = $i;
12395  }
12396  } else {
12397  $this->outlines[$i]['parent'] = $nb;
12398  }
12399  if (($o['l'] <= $level) AND ($i > 0)) {
12400  //Set prev and next pointers
12401  $prev = $lru[$o['l']];
12402  $this->outlines[$prev]['next'] = $i;
12403  $this->outlines[$i]['prev'] = $prev;
12404  }
12405  $lru[$o['l']] = $i;
12406  $level = $o['l'];
12407  }
12408  //Outline items
12409  $n = $this->n + 1;
12410  $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';
12411  foreach ($this->outlines as $i => $o) {
12412  $oid = $this->_newobj();
12413  // covert HTML title to string
12414  $title = preg_replace($nltags, "\n", $o['t']);
12415  $title = preg_replace("/[\r]+/si", '', $title);
12416  $title = preg_replace("/[\n]+/si", "\n", $title);
12417  $title = strip_tags($title);
12418  $title = $this->stringTrim($title);
12419  $out = '<</Title '.$this->_textstring($title, $oid);
12420  $out .= ' /Parent '.($n + $o['parent']).' 0 R';
12421  if (isset($o['prev'])) {
12422  $out .= ' /Prev '.($n + $o['prev']).' 0 R';
12423  }
12424  if (isset($o['next'])) {
12425  $out .= ' /Next '.($n + $o['next']).' 0 R';
12426  }
12427  if (isset($o['first'])) {
12428  $out .= ' /First '.($n + $o['first']).' 0 R';
12429  }
12430  if (isset($o['last'])) {
12431  $out .= ' /Last '.($n + $o['last']).' 0 R';
12432  }
12433  if (isset($o['u']) AND !empty($o['u'])) {
12434  // link
12435  if (is_string($o['u'])) {
12436  if ($o['u'][0] == '#') {
12437  // internal destination
12438  $out .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($o['u'], 1));
12439  } elseif ($o['u'][0] == '%') {
12440  // embedded PDF file
12441  $filename = basename(substr($o['u'], 1));
12442  $out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
12443  } elseif ($o['u'][0] == '*') {
12444  // embedded generic file
12445  $filename = basename(substr($o['u'], 1));
12446  $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});';
12447  $out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>';
12448  } else {
12449  // external URI link
12450  $out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>';
12451  }
12452  } elseif (isset($this->links[$o['u']])) {
12453  // internal link ID
12454  $l = $this->links[$o['u']];
12455  if (isset($this->page_obj_id[($l[0])])) {
12456  $out .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l[0])], ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
12457  }
12458  }
12459  } elseif (isset($this->page_obj_id[($o['p'])])) {
12460  // link to a page
12461  $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)));
12462  }
12463  // set font style
12464  $style = 0;
12465  if (!empty($o['s'])) {
12466  // bold
12467  if (strpos($o['s'], 'B') !== false) {
12468  $style |= 2;
12469  }
12470  // oblique
12471  if (strpos($o['s'], 'I') !== false) {
12472  $style |= 1;
12473  }
12474  }
12475  $out .= sprintf(' /F %d', $style);
12476  // set bookmark color
12477  if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
12478  $color = array_values($o['c']);
12479  $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
12480  } else {
12481  // black
12482  $out .= ' /C [0.0 0.0 0.0]';
12483  }
12484  $out .= ' /Count 0'; // normally closed item
12485  $out .= ' >>';
12486  $out .= "\n".'endobj';
12487  $this->_out($out);
12488  }
12489  //Outline root
12490  $this->OutlineRoot = $this->_newobj();
12491  $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj');
12492  }
12493 
12494  // --- JAVASCRIPT ------------------------------------------------------
12495 
12503  public function IncludeJS($script) {
12504  $this->javascript .= $script;
12505  }
12506 
12516  public function addJavascriptObject($script, $onload=false) {
12517  if ($this->pdfa_mode) {
12518  // javascript is not allowed in PDF/A mode
12519  return false;
12520  }
12521  ++$this->n;
12522  $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload);
12523  return $this->n;
12524  }
12525 
12532  protected function _putjavascript() {
12533  if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) {
12534  return;
12535  }
12536  if (strpos($this->javascript, 'this.addField') > 0) {
12537  if (!$this->ur['enabled']) {
12538  //$this->setUserRights();
12539  }
12540  // the following two lines are used to avoid form fields duplication after saving
12541  // The addField method only works when releasing user rights (UR3)
12542  $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
12543  $jsb = "getField('tcpdfdocsaved').value='saved';";
12544  $this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
12545  }
12546  // name tree for javascript
12547  $this->n_js = '<< /Names [';
12548  if (!empty($this->javascript)) {
12549  $this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
12550  }
12551  if (!empty($this->js_objects)) {
12552  foreach ($this->js_objects as $key => $val) {
12553  if ($val['onload']) {
12554  $this->n_js .= ' (JS'.$key.') '.$key.' 0 R';
12555  }
12556  }
12557  }
12558  $this->n_js .= ' ] >>';
12559  // default Javascript object
12560  if (!empty($this->javascript)) {
12561  $obj_id = $this->_newobj();
12562  $out = '<< /S /JavaScript';
12563  $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id);
12564  $out .= ' >>';
12565  $out .= "\n".'endobj';
12566  $this->_out($out);
12567  }
12568  // additional Javascript objects
12569  if (!empty($this->js_objects)) {
12570  foreach ($this->js_objects as $key => $val) {
12571  $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
12572  $this->_out($out);
12573  }
12574  }
12575  }
12576 
12590  protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
12591  if ($this->rtl) {
12592  $x = $x - $w;
12593  }
12594  // the followind avoid fields duplication after saving the document
12595  $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {";
12596  $k = $this->k;
12597  $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";
12598  $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
12599  while (list($key, $val) = each($prop)) {
12600  if (strcmp(substr($key, -5), 'Color') == 0) {
12601  $val = TCPDF_COLORS::_JScolor($val);
12602  } else {
12603  $val = "'".$val."'";
12604  }
12605  $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
12606  }
12607  if ($this->rtl) {
12608  $this->x -= $w;
12609  } else {
12610  $this->x += $w;
12611  }
12612  $this->javascript .= '}';
12613  }
12614 
12615  // --- FORM FIELDS -----------------------------------------------------
12616 
12617 
12618 
12626  public function setFormDefaultProp($prop=array()) {
12627  $this->default_form_prop = $prop;
12628  }
12629 
12637  public function getFormDefaultProp() {
12638  return $this->default_form_prop;
12639  }
12640 
12655  public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12656  if ($x === '') {
12657  $x = $this->x;
12658  }
12659  if ($y === '') {
12660  $y = $this->y;
12661  }
12662  // check page for no-write regions and adapt page margins if necessary
12663  list($x, $y) = $this->checkPageRegions($h, $x, $y);
12664  if ($js) {
12665  $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
12666  return;
12667  }
12668  // get default style
12669  $prop = array_merge($this->getFormDefaultProp(), $prop);
12670  // get annotation data
12671  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12672  // set default appearance stream
12673  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12674  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12675  $popt['da'] = $fontstyle;
12676  // build appearance stream
12677  $popt['ap'] = array();
12678  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12679  $text = '';
12680  if (isset($prop['value']) AND !empty($prop['value'])) {
12681  $text = $prop['value'];
12682  } elseif (isset($opt['v']) AND !empty($opt['v'])) {
12683  $text = $opt['v'];
12684  }
12685  $tmpid = $this->startTemplate($w, $h, false);
12686  $align = '';
12687  if (isset($popt['q'])) {
12688  switch ($popt['q']) {
12689  case 0: {
12690  $align = 'L';
12691  break;
12692  }
12693  case 1: {
12694  $align = 'C';
12695  break;
12696  }
12697  case 2: {
12698  $align = 'R';
12699  break;
12700  }
12701  default: {
12702  $align = '';
12703  break;
12704  }
12705  }
12706  }
12707  $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12708  $this->endTemplate();
12709  --$this->n;
12710  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12711  unset($this->xobjects[$tmpid]);
12712  $popt['ap']['n'] .= 'Q EMC';
12713  // merge options
12714  $opt = array_merge($popt, $opt);
12715  // remove some conflicting options
12716  unset($opt['bs']);
12717  // set remaining annotation data
12718  $opt['Subtype'] = 'Widget';
12719  $opt['ft'] = 'Tx';
12720  $opt['t'] = $name;
12721  // Additional annotation's parameters (check _putannotsobj() method):
12722  //$opt['f']
12723  //$opt['as']
12724  //$opt['bs']
12725  //$opt['be']
12726  //$opt['c']
12727  //$opt['border']
12728  //$opt['h']
12729  //$opt['mk'];
12730  //$opt['mk']['r']
12731  //$opt['mk']['bc'];
12732  //$opt['mk']['bg'];
12733  unset($opt['mk']['ca']);
12734  unset($opt['mk']['rc']);
12735  unset($opt['mk']['ac']);
12736  unset($opt['mk']['i']);
12737  unset($opt['mk']['ri']);
12738  unset($opt['mk']['ix']);
12739  unset($opt['mk']['if']);
12740  //$opt['mk']['if']['sw'];
12741  //$opt['mk']['if']['s'];
12742  //$opt['mk']['if']['a'];
12743  //$opt['mk']['if']['fb'];
12744  unset($opt['mk']['tp']);
12745  //$opt['tu']
12746  //$opt['tm']
12747  //$opt['ff']
12748  //$opt['v']
12749  //$opt['dv']
12750  //$opt['a']
12751  //$opt['aa']
12752  //$opt['q']
12753  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12754  if ($this->rtl) {
12755  $this->x -= $w;
12756  } else {
12757  $this->x += $w;
12758  }
12759  }
12760 
12776  public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
12777  if ($x === '') {
12778  $x = $this->x;
12779  }
12780  if ($y === '') {
12781  $y = $this->y;
12782  }
12783  // check page for no-write regions and adapt page margins if necessary
12784  list($x, $y) = $this->checkPageRegions($w, $x, $y);
12785  if ($js) {
12786  $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
12787  return;
12788  }
12789  if (TCPDF_STATIC::empty_string($onvalue)) {
12790  $onvalue = 'On';
12791  }
12792  if ($checked) {
12793  $defval = $onvalue;
12794  } else {
12795  $defval = 'Off';
12796  }
12797  // set font
12798  $font = 'zapfdingbats';
12799  if ($this->pdfa_mode) {
12800  // all fonts must be embedded
12801  $font = 'pdfa'.$font;
12802  }
12803  $this->AddFont($font);
12804  $tmpfont = $this->getFontBuffer($font);
12805  // set data for parent group
12806  if (!isset($this->radiobutton_groups[$this->page])) {
12807  $this->radiobutton_groups[$this->page] = array();
12808  }
12809  if (!isset($this->radiobutton_groups[$this->page][$name])) {
12810  $this->radiobutton_groups[$this->page][$name] = array();
12811  ++$this->n;
12812  $this->radiobutton_groups[$this->page][$name]['n'] = $this->n;
12813  $this->radio_groups[] = $this->n;
12814  }
12815  $kid = ($this->n + 1);
12816  // save object ID to be added on Kids entry on parent object
12817  $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval);
12818  // get default style
12819  $prop = array_merge($this->getFormDefaultProp(), $prop);
12820  $prop['NoToggleToOff'] = 'true';
12821  $prop['Radio'] = 'true';
12822  $prop['borderStyle'] = 'inset';
12823  // get annotation data
12824  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12825  // set additional default options
12826  $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
12827  $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
12828  $popt['da'] = $fontstyle;
12829  // build appearance stream
12830  $popt['ap'] = array();
12831  $popt['ap']['n'] = array();
12832  $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k);
12833  $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
12834  $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);
12835  $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);
12836  if (!isset($popt['mk'])) {
12837  $popt['mk'] = array();
12838  }
12839  $popt['mk']['ca'] = '(l)';
12840  // merge options
12841  $opt = array_merge($popt, $opt);
12842  // set remaining annotation data
12843  $opt['Subtype'] = 'Widget';
12844  $opt['ft'] = 'Btn';
12845  if ($checked) {
12846  $opt['v'] = array('/'.$onvalue);
12847  $opt['as'] = $onvalue;
12848  } else {
12849  $opt['as'] = 'Off';
12850  }
12851  // store readonly flag
12852  if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) {
12853  $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false;
12854  }
12855  $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64);
12856  $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
12857  if ($this->rtl) {
12858  $this->x -= $w;
12859  } else {
12860  $this->x += $w;
12861  }
12862  }
12863 
12879  public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12880  if ($x === '') {
12881  $x = $this->x;
12882  }
12883  if ($y === '') {
12884  $y = $this->y;
12885  }
12886  // check page for no-write regions and adapt page margins if necessary
12887  list($x, $y) = $this->checkPageRegions($h, $x, $y);
12888  if ($js) {
12889  $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
12890  $s = '';
12891  foreach ($values as $value) {
12892  if (is_array($value)) {
12893  $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12894  } else {
12895  $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12896  }
12897  }
12898  $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12899  return;
12900  }
12901  // get default style
12902  $prop = array_merge($this->getFormDefaultProp(), $prop);
12903  // get annotation data
12904  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12905  // set additional default values
12906  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12907  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12908  $popt['da'] = $fontstyle;
12909  // build appearance stream
12910  $popt['ap'] = array();
12911  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12912  $text = '';
12913  foreach($values as $item) {
12914  if (is_array($item)) {
12915  $text .= $item[1]."\n";
12916  } else {
12917  $text .= $item."\n";
12918  }
12919  }
12920  $tmpid = $this->startTemplate($w, $h, false);
12921  $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12922  $this->endTemplate();
12923  --$this->n;
12924  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12925  unset($this->xobjects[$tmpid]);
12926  $popt['ap']['n'] .= 'Q EMC';
12927  // merge options
12928  $opt = array_merge($popt, $opt);
12929  // set remaining annotation data
12930  $opt['Subtype'] = 'Widget';
12931  $opt['ft'] = 'Ch';
12932  $opt['t'] = $name;
12933  $opt['opt'] = $values;
12934  unset($opt['mk']['ca']);
12935  unset($opt['mk']['rc']);
12936  unset($opt['mk']['ac']);
12937  unset($opt['mk']['i']);
12938  unset($opt['mk']['ri']);
12939  unset($opt['mk']['ix']);
12940  unset($opt['mk']['if']);
12941  unset($opt['mk']['tp']);
12942  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12943  if ($this->rtl) {
12944  $this->x -= $w;
12945  } else {
12946  $this->x += $w;
12947  }
12948  }
12949 
12965  public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12966  if ($x === '') {
12967  $x = $this->x;
12968  }
12969  if ($y === '') {
12970  $y = $this->y;
12971  }
12972  // check page for no-write regions and adapt page margins if necessary
12973  list($x, $y) = $this->checkPageRegions($h, $x, $y);
12974  if ($js) {
12975  $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
12976  $s = '';
12977  foreach ($values as $value) {
12978  if (is_array($value)) {
12979  $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12980  } else {
12981  $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12982  }
12983  }
12984  $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12985  return;
12986  }
12987  // get default style
12988  $prop = array_merge($this->getFormDefaultProp(), $prop);
12989  $prop['Combo'] = true;
12990  // get annotation data
12991  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12992  // set additional default options
12993  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12994  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12995  $popt['da'] = $fontstyle;
12996  // build appearance stream
12997  $popt['ap'] = array();
12998  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12999  $text = '';
13000  foreach($values as $item) {
13001  if (is_array($item)) {
13002  $text .= $item[1]."\n";
13003  } else {
13004  $text .= $item."\n";
13005  }
13006  }
13007  $tmpid = $this->startTemplate($w, $h, false);
13008  $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13009  $this->endTemplate();
13010  --$this->n;
13011  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13012  unset($this->xobjects[$tmpid]);
13013  $popt['ap']['n'] .= 'Q EMC';
13014  // merge options
13015  $opt = array_merge($popt, $opt);
13016  // set remaining annotation data
13017  $opt['Subtype'] = 'Widget';
13018  $opt['ft'] = 'Ch';
13019  $opt['t'] = $name;
13020  $opt['opt'] = $values;
13021  unset($opt['mk']['ca']);
13022  unset($opt['mk']['rc']);
13023  unset($opt['mk']['ac']);
13024  unset($opt['mk']['i']);
13025  unset($opt['mk']['ri']);
13026  unset($opt['mk']['ix']);
13027  unset($opt['mk']['if']);
13028  unset($opt['mk']['tp']);
13029  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13030  if ($this->rtl) {
13031  $this->x -= $w;
13032  } else {
13033  $this->x += $w;
13034  }
13035  }
13036 
13052  public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
13053  if ($x === '') {
13054  $x = $this->x;
13055  }
13056  if ($y === '') {
13057  $y = $this->y;
13058  }
13059  // check page for no-write regions and adapt page margins if necessary
13060  list($x, $y) = $this->checkPageRegions($w, $x, $y);
13061  if ($js) {
13062  $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
13063  return;
13064  }
13065  if (!isset($prop['value'])) {
13066  $prop['value'] = array('Yes');
13067  }
13068  // get default style
13069  $prop = array_merge($this->getFormDefaultProp(), $prop);
13070  $prop['borderStyle'] = 'inset';
13071  // get annotation data
13072  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13073  // set additional default options
13074  $font = 'zapfdingbats';
13075  if ($this->pdfa_mode) {
13076  // all fonts must be embedded
13077  $font = 'pdfa'.$font;
13078  }
13079  $this->AddFont($font);
13080  $tmpfont = $this->getFontBuffer($font);
13081  $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
13082  $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
13083  $popt['da'] = $fontstyle;
13084  // build appearance stream
13085  $popt['ap'] = array();
13086  $popt['ap']['n'] = array();
13087  $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k);
13088  $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
13089  $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);
13090  $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);
13091  // merge options
13092  $opt = array_merge($popt, $opt);
13093  // set remaining annotation data
13094  $opt['Subtype'] = 'Widget';
13095  $opt['ft'] = 'Btn';
13096  $opt['t'] = $name;
13097  if (TCPDF_STATIC::empty_string($onvalue)) {
13098  $onvalue = 'Yes';
13099  }
13100  $opt['opt'] = array($onvalue);
13101  if ($checked) {
13102  $opt['v'] = array('/Yes');
13103  $opt['as'] = 'Yes';
13104  } else {
13105  $opt['v'] = array('/Off');
13106  $opt['as'] = 'Off';
13107  }
13108  $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
13109  if ($this->rtl) {
13110  $this->x -= $w;
13111  } else {
13112  $this->x += $w;
13113  }
13114  }
13115 
13132  public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13133  if ($x === '') {
13134  $x = $this->x;
13135  }
13136  if ($y === '') {
13137  $y = $this->y;
13138  }
13139  // check page for no-write regions and adapt page margins if necessary
13140  list($x, $y) = $this->checkPageRegions($h, $x, $y);
13141  if ($js) {
13142  $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
13143  $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
13144  $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
13145  $this->javascript .= 'f'.$name.".highlight='push';\n";
13146  $this->javascript .= 'f'.$name.".print=false;\n";
13147  return;
13148  }
13149  // get default style
13150  $prop = array_merge($this->getFormDefaultProp(), $prop);
13151  $prop['Pushbutton'] = 'true';
13152  $prop['highlight'] = 'push';
13153  $prop['display'] = 'display.noPrint';
13154  // get annotation data
13155  $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13156  $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13157  $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13158  $popt['da'] = $fontstyle;
13159  // build appearance stream
13160  $popt['ap'] = array();
13161  $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13162  $tmpid = $this->startTemplate($w, $h, false);
13163  $bw = (2 / $this->k); // border width
13164  $border = array(
13165  'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13166  'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
13167  'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13168  'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
13169  $this->SetFillColor(204);
13170  $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
13171  $this->endTemplate();
13172  --$this->n;
13173  $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13174  unset($this->xobjects[$tmpid]);
13175  $popt['ap']['n'] .= 'Q EMC';
13176  // set additional default options
13177  if (!isset($popt['mk'])) {
13178  $popt['mk'] = array();
13179  }
13180  $ann_obj_id = ($this->n + 1);
13181  if (!empty($action) AND !is_array($action)) {
13182  $ann_obj_id = ($this->n + 2);
13183  }
13184  $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
13185  $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
13186  $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
13187  // merge options
13188  $opt = array_merge($popt, $opt);
13189  // set remaining annotation data
13190  $opt['Subtype'] = 'Widget';
13191  $opt['ft'] = 'Btn';
13192  $opt['t'] = $caption;
13193  $opt['v'] = $name;
13194  if (!empty($action)) {
13195  if (is_array($action)) {
13196  // form action options as on section 12.7.5 of PDF32000_2008.
13197  $opt['aa'] = '/D <<';
13198  $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
13199  foreach ($action AS $key => $val) {
13200  if (($key == 'S') AND in_array($val, $bmode)) {
13201  $opt['aa'] .= ' /S /'.$val;
13202  } elseif (($key == 'F') AND (!empty($val))) {
13203  $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
13204  } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
13205  $opt['aa'] .= ' /Fields [';
13206  foreach ($val AS $field) {
13207  $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
13208  }
13209  $opt['aa'] .= ']';
13210  } elseif (($key == 'Flags')) {
13211  $ff = 0;
13212  if (is_array($val)) {
13213  foreach ($val AS $flag) {
13214  switch ($flag) {
13215  case 'Include/Exclude': {
13216  $ff += 1 << 0;
13217  break;
13218  }
13219  case 'IncludeNoValueFields': {
13220  $ff += 1 << 1;
13221  break;
13222  }
13223  case 'ExportFormat': {
13224  $ff += 1 << 2;
13225  break;
13226  }
13227  case 'GetMethod': {
13228  $ff += 1 << 3;
13229  break;
13230  }
13231  case 'SubmitCoordinates': {
13232  $ff += 1 << 4;
13233  break;
13234  }
13235  case 'XFDF': {
13236  $ff += 1 << 5;
13237  break;
13238  }
13239  case 'IncludeAppendSaves': {
13240  $ff += 1 << 6;
13241  break;
13242  }
13243  case 'IncludeAnnotations': {
13244  $ff += 1 << 7;
13245  break;
13246  }
13247  case 'SubmitPDF': {
13248  $ff += 1 << 8;
13249  break;
13250  }
13251  case 'CanonicalFormat': {
13252  $ff += 1 << 9;
13253  break;
13254  }
13255  case 'ExclNonUserAnnots': {
13256  $ff += 1 << 10;
13257  break;
13258  }
13259  case 'ExclFKey': {
13260  $ff += 1 << 11;
13261  break;
13262  }
13263  case 'EmbedForm': {
13264  $ff += 1 << 13;
13265  break;
13266  }
13267  }
13268  }
13269  } else {
13270  $ff = intval($val);
13271  }
13272  $opt['aa'] .= ' /Flags '.$ff;
13273  }
13274  }
13275  $opt['aa'] .= ' >>';
13276  } else {
13277  // Javascript action or raw action command
13278  $js_obj_id = $this->addJavascriptObject($action);
13279  $opt['aa'] = '/D '.$js_obj_id.' 0 R';
13280  }
13281  }
13282  $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13283  if ($this->rtl) {
13284  $this->x -= $w;
13285  } else {
13286  $this->x += $w;
13287  }
13288  }
13289 
13290  // --- END FORMS FIELDS ------------------------------------------------
13291 
13299  protected function _putsignature() {
13300  if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
13301  return;
13302  }
13303  $sigobjid = ($this->sig_obj_id + 1);
13304  $out = $this->_getobj($sigobjid)."\n";
13305  $out .= '<< /Type /Sig';
13306  $out .= ' /Filter /Adobe.PPKLite';
13307  $out .= ' /SubFilter /adbe.pkcs7.detached';
13308  $out .= ' '.TCPDF_STATIC::$byterange_string;
13309  $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>';
13310  $out .= ' /Reference ['; // array of signature reference dictionaries
13311  $out .= ' << /Type /SigRef';
13312  if ($this->signature_data['cert_type'] > 0) {
13313  $out .= ' /TransformMethod /DocMDP';
13314  $out .= ' /TransformParams <<';
13315  $out .= ' /Type /TransformParams';
13316  $out .= ' /P '.$this->signature_data['cert_type'];
13317  $out .= ' /V /1.2';
13318  } else {
13319  $out .= ' /TransformMethod /UR3';
13320  $out .= ' /TransformParams <<';
13321  $out .= ' /Type /TransformParams';
13322  $out .= ' /V /2.2';
13323  if (!TCPDF_STATIC::empty_string($this->ur['document'])) {
13324  $out .= ' /Document['.$this->ur['document'].']';
13325  }
13326  if (!TCPDF_STATIC::empty_string($this->ur['form'])) {
13327  $out .= ' /Form['.$this->ur['form'].']';
13328  }
13329  if (!TCPDF_STATIC::empty_string($this->ur['signature'])) {
13330  $out .= ' /Signature['.$this->ur['signature'].']';
13331  }
13332  if (!TCPDF_STATIC::empty_string($this->ur['annots'])) {
13333  $out .= ' /Annots['.$this->ur['annots'].']';
13334  }
13335  if (!TCPDF_STATIC::empty_string($this->ur['ef'])) {
13336  $out .= ' /EF['.$this->ur['ef'].']';
13337  }
13338  if (!TCPDF_STATIC::empty_string($this->ur['formex'])) {
13339  $out .= ' /FormEX['.$this->ur['formex'].']';
13340  }
13341  }
13342  $out .= ' >>'; // close TransformParams
13343  // optional digest data (values must be calculated and replaced later)
13344  //$out .= ' /Data ********** 0 R';
13345  //$out .= ' /DigestMethod/MD5';
13346  //$out .= ' /DigestLocation[********** 34]';
13347  //$out .= ' /DigestValue<********************************>';
13348  $out .= ' >>';
13349  $out .= ' ]'; // end of reference
13350  if (isset($this->signature_data['info']['Name']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Name'])) {
13351  $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid);
13352  }
13353  if (isset($this->signature_data['info']['Location']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Location'])) {
13354  $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid);
13355  }
13356  if (isset($this->signature_data['info']['Reason']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Reason'])) {
13357  $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid);
13358  }
13359  if (isset($this->signature_data['info']['ContactInfo']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['ContactInfo'])) {
13360  $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid);
13361  }
13362  $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp);
13363  $out .= ' >>';
13364  $out .= "\n".'endobj';
13365  $this->_out($out);
13366  }
13367 
13385  public function setUserRights(
13386  $enable=true,
13387  $document='/FullSave',
13388  $annots='/Create/Delete/Modify/Copy/Import/Export',
13389  $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
13390  $signature='/Modify',
13391  $ef='/Create/Delete/Modify/Import',
13392  $formex='') {
13393  $this->ur['enabled'] = $enable;
13394  $this->ur['document'] = $document;
13395  $this->ur['annots'] = $annots;
13396  $this->ur['form'] = $form;
13397  $this->ur['signature'] = $signature;
13398  $this->ur['ef'] = $ef;
13399  $this->ur['formex'] = $formex;
13400  if (!$this->sign) {
13401  $this->setSignature('', '', '', '', 0, array());
13402  }
13403  }
13404 
13421  public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
13422  // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13423  // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13424  // to convert pfx certificate to pem: openssl
13425  // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
13426  $this->sign = true;
13427  ++$this->n;
13428  $this->sig_obj_id = $this->n; // signature widget
13429  ++$this->n; // signature object ($this->sig_obj_id + 1)
13430  $this->signature_data = array();
13431  if (strlen($signing_cert) == 0) {
13432  $this->Error('Please provide a certificate file and password!');
13433  }
13434  if (strlen($private_key) == 0) {
13435  $private_key = $signing_cert;
13436  }
13437  $this->signature_data['signcert'] = $signing_cert;
13438  $this->signature_data['privkey'] = $private_key;
13439  $this->signature_data['password'] = $private_key_password;
13440  $this->signature_data['extracerts'] = $extracerts;
13441  $this->signature_data['cert_type'] = $cert_type;
13442  $this->signature_data['info'] = $info;
13443  }
13444 
13457  public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13458  $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13459  }
13460 
13473  public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13474  ++$this->n;
13475  $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13476  }
13477 
13491  protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13492  $sigapp = array();
13493  if (($page < 1) OR ($page > $this->numpages)) {
13494  $sigapp['page'] = $this->page;
13495  } else {
13496  $sigapp['page'] = intval($page);
13497  }
13498  if (empty($name)) {
13499  $sigapp['name'] = 'Signature';
13500  } else {
13501  $sigapp['name'] = $name;
13502  }
13503  $a = $x * $this->k;
13504  $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k);
13505  $c = $w * $this->k;
13506  $d = $h * $this->k;
13507  $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d));
13508  return $sigapp;
13509  }
13510 
13518  public function startPageGroup($page='') {
13519  if (empty($page)) {
13520  $page = $this->page + 1;
13521  }
13522  $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1;
13523  }
13524 
13531  public function setStartingPageNumber($num=1) {
13532  $this->starting_page_number = max(0, intval($num));
13533  }
13534 
13542  public function getAliasRightShift() {
13543  // calculate aproximatively the ratio between widths of aliases and replacements.
13544  $ref = '{'.TCPDF_STATIC::$alias_right_shift.'}{'.TCPDF_STATIC::$alias_tot_pages.'}{'.TCPDF_STATIC::$alias_num_page.'}';
13545  $rep = str_repeat(' ', $this->GetNumChars($ref));
13546  $wrep = $this->GetStringWidth($rep);
13547  if ($wrep > 0) {
13548  $wdiff = max(1, ($this->GetStringWidth($ref) / $wrep));
13549  } else {
13550  $wdiff = 1;
13551  }
13552  $sdiff = sprintf('%F', $wdiff);
13553  $alias = TCPDF_STATIC::$alias_right_shift.$sdiff.'}';
13554  if ($this->isUnicodeFont()) {
13555  $alias = '{'.$alias;
13556  }
13557  return $alias;
13558  }
13559 
13568  public function getAliasNbPages() {
13569  if ($this->isUnicodeFont()) {
13570  return '{'.TCPDF_STATIC::$alias_tot_pages.'}';
13571  }
13573  }
13574 
13583  public function getAliasNumPage() {
13584  if ($this->isUnicodeFont()) {
13585  return '{'.TCPDF_STATIC::$alias_num_page.'}';
13586  }
13588  }
13589 
13598  public function getPageGroupAlias() {
13599  if ($this->isUnicodeFont()) {
13600  return '{'.TCPDF_STATIC::$alias_group_tot_pages.'}';
13601  }
13603  }
13604 
13613  public function getPageNumGroupAlias() {
13614  if ($this->isUnicodeFont()) {
13615  return '{'.TCPDF_STATIC::$alias_group_num_page.'}';
13616  }
13618  }
13619 
13626  public function getGroupPageNo() {
13627  return $this->pagegroups[$this->currpagegroup];
13628  }
13629 
13636  public function getGroupPageNoFormatted() {
13638  }
13639 
13646  public function PageNoFormatted() {
13647  return TCPDF_STATIC::formatPageNumber($this->PageNo());
13648  }
13649 
13655  protected function _putocg() {
13656  if (empty($this->pdflayers)) {
13657  return;
13658  }
13659  foreach ($this->pdflayers as $key => $layer) {
13660  $this->pdflayers[$key]['objid'] = $this->_newobj();
13661  $out = '<< /Type /OCG';
13662  $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']);
13663  $out .= ' /Usage <<';
13664  if (isset($layer['print']) AND ($layer['print'] !== NULL)) {
13665  $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>';
13666  }
13667  $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>';
13668  $out .= ' >> >>';
13669  $out .= "\n".'endobj';
13670  $this->_out($out);
13671  }
13672  }
13673 
13682  public function startLayer($name='', $print=true, $view=true) {
13683  if ($this->state != 2) {
13684  return;
13685  }
13686  $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1));
13687  if (empty($name)) {
13688  $name = $layer;
13689  } else {
13690  $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
13691  }
13692  $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view);
13693  $this->openMarkedContent = true;
13694  $this->_out('/OC /'.$layer.' BDC');
13695  }
13696 
13702  public function endLayer() {
13703  if ($this->state != 2) {
13704  return;
13705  }
13706  if ($this->openMarkedContent) {
13707  // close existing open marked-content layer
13708  $this->_out('EMC');
13709  $this->openMarkedContent = false;
13710  }
13711  }
13712 
13721  public function setVisibility($v) {
13722  if ($this->state != 2) {
13723  return;
13724  }
13725  $this->endLayer();
13726  switch($v) {
13727  case 'print': {
13728  $this->startLayer('Print', true, false);
13729  break;
13730  }
13731  case 'view':
13732  case 'screen': {
13733  $this->startLayer('View', false, true);
13734  break;
13735  }
13736  case 'all': {
13737  $this->_out('');
13738  break;
13739  }
13740  default: {
13741  $this->Error('Incorrect visibility: '.$v);
13742  break;
13743  }
13744  }
13745  }
13746 
13754  protected function addExtGState($parms) {
13755  if ($this->pdfa_mode) {
13756  // transparencies are not allowed in PDF/A mode
13757  return;
13758  }
13759  // check if this ExtGState already exist
13760  foreach ($this->extgstates as $i => $ext) {
13761  if ($ext['parms'] == $parms) {
13762  if ($this->inxobj) {
13763  // we are inside an XObject template
13764  $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext;
13765  }
13766  // return reference to existing ExtGState
13767  return $i;
13768  }
13769  }
13770  $n = (count($this->extgstates) + 1);
13771  $this->extgstates[$n] = array('parms' => $parms);
13772  if ($this->inxobj) {
13773  // we are inside an XObject template
13774  $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n];
13775  }
13776  return $n;
13777  }
13778 
13785  protected function setExtGState($gs) {
13786  if ($this->pdfa_mode OR ($this->state != 2)) {
13787  // transparency is not allowed in PDF/A mode
13788  return;
13789  }
13790  $this->_out(sprintf('/GS%d gs', $gs));
13791  }
13792 
13798  protected function _putextgstates() {
13799  foreach ($this->extgstates as $i => $ext) {
13800  $this->extgstates[$i]['n'] = $this->_newobj();
13801  $out = '<< /Type /ExtGState';
13802  foreach ($ext['parms'] as $k => $v) {
13803  if (is_float($v)) {
13804  $v = sprintf('%F', $v);
13805  } elseif ($v === true) {
13806  $v = 'true';
13807  } elseif ($v === false) {
13808  $v = 'false';
13809  }
13810  $out .= ' /'.$k.' '.$v;
13811  }
13812  $out .= ' >>';
13813  $out .= "\n".'endobj';
13814  $this->_out($out);
13815  }
13816  }
13817 
13827  public function setOverprint($stroking=true, $nonstroking='', $mode=0) {
13828  if ($this->state != 2) {
13829  return;
13830  }
13831  $stroking = $stroking ? true : false;
13832  if (TCPDF_STATIC::empty_string($nonstroking)) {
13833  // default value if not set
13834  $nonstroking = $stroking;
13835  } else {
13836  $nonstroking = $nonstroking ? true : false;
13837  }
13838  if (($mode != 0) AND ($mode != 1)) {
13839  $mode = 0;
13840  }
13841  $this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode);
13842  $gs = $this->addExtGState($this->overprint);
13843  $this->setExtGState($gs);
13844  }
13845 
13853  public function getOverprint() {
13854  return $this->overprint;
13855  }
13856 
13866  public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) {
13867  if ($this->pdfa_mode) {
13868  // transparency is not allowed in PDF/A mode
13869  return;
13870  }
13871  $stroking = floatval($stroking);
13872  if (TCPDF_STATIC::empty_string($nonstroking)) {
13873  // default value if not set
13874  $nonstroking = $stroking;
13875  } else {
13876  $nonstroking = floatval($nonstroking);
13877  }
13878  if ($bm[0] == '/') {
13879  // remove trailing slash
13880  $bm = substr($bm, 1);
13881  }
13882  if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
13883  $bm = 'Normal';
13884  }
13885  $ais = $ais ? true : false;
13886  $this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais);
13887  $gs = $this->addExtGState($this->alpha);
13888  $this->setExtGState($gs);
13889  }
13890 
13898  public function getAlpha() {
13899  return $this->alpha;
13900  }
13901 
13908  public function setJPEGQuality($quality) {
13909  if (($quality < 1) OR ($quality > 100)) {
13910  $quality = 75;
13911  }
13912  $this->jpeg_quality = intval($quality);
13913  }
13914 
13921  public function setDefaultTableColumns($cols=4) {
13922  $this->default_table_columns = intval($cols);
13923  }
13924 
13931  public function setCellHeightRatio($h) {
13932  $this->cell_height_ratio = $h;
13933  }
13934 
13940  public function getCellHeightRatio() {
13941  return $this->cell_height_ratio;
13942  }
13943 
13950  public function setPDFVersion($version='1.7') {
13951  if ($this->pdfa_mode) {
13952  // PDF/A mode
13953  $this->PDFVersion = '1.4';
13954  } else {
13955  $this->PDFVersion = $version;
13956  }
13957  }
13958 
13968  public function setViewerPreferences($preferences) {
13969  $this->viewer_preferences = $preferences;
13970  }
13971 
13985  public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
13986  if (strpos($colors, 'ALLSPOT') !== false) {
13987  // expand spot colors
13988  $spot_colors = '';
13989  foreach ($this->spot_colors as $spot_color_name => $v) {
13990  $spot_colors .= ','.$spot_color_name;
13991  }
13992  if (!empty($spot_colors)) {
13993  $spot_colors = substr($spot_colors, 1);
13994  $colors = str_replace('ALLSPOT', $spot_colors, $colors);
13995  } else {
13996  $colors = str_replace('ALLSPOT', 'NONE', $colors);
13997  }
13998  }
13999  $bars = explode(',', $colors);
14000  $numbars = count($bars); // number of bars to print
14001  if ($numbars <= 0) {
14002  return;
14003  }
14004  // set bar measures
14005  if ($vertical) {
14006  $coords = array(0, 0, 0, 1);
14007  $wb = $w / $numbars; // bar width
14008  $hb = $h; // bar height
14009  $xd = $wb; // delta x
14010  $yd = 0; // delta y
14011  } else {
14012  $coords = array(1, 0, 0, 0);
14013  $wb = $w; // bar width
14014  $hb = $h / $numbars; // bar height
14015  $xd = 0; // delta x
14016  $yd = $hb; // delta y
14017  }
14018  $xb = $x;
14019  $yb = $y;
14020  foreach ($bars as $col) {
14021  switch ($col) {
14022  // set transition colors
14023  case 'A': { // BLACK (GRAYSCALE)
14024  $col_a = array(255);
14025  $col_b = array(0);
14026  break;
14027  }
14028  case 'W': { // WHITE (GRAYSCALE)
14029  $col_a = array(0);
14030  $col_b = array(255);
14031  break;
14032  }
14033  case 'R': { // RED (RGB)
14034  $col_a = array(255,255,255);
14035  $col_b = array(255,0,0);
14036  break;
14037  }
14038  case 'G': { // GREEN (RGB)
14039  $col_a = array(255,255,255);
14040  $col_b = array(0,255,0);
14041  break;
14042  }
14043  case 'B': { // BLUE (RGB)
14044  $col_a = array(255,255,255);
14045  $col_b = array(0,0,255);
14046  break;
14047  }
14048  case 'C': { // CYAN (CMYK)
14049  $col_a = array(0,0,0,0);
14050  $col_b = array(100,0,0,0);
14051  break;
14052  }
14053  case 'M': { // MAGENTA (CMYK)
14054  $col_a = array(0,0,0,0);
14055  $col_b = array(0,100,0,0);
14056  break;
14057  }
14058  case 'Y': { // YELLOW (CMYK)
14059  $col_a = array(0,0,0,0);
14060  $col_b = array(0,0,100,0);
14061  break;
14062  }
14063  case 'K': { // KEY - BLACK (CMYK)
14064  $col_a = array(0,0,0,0);
14065  $col_b = array(0,0,0,100);
14066  break;
14067  }
14068  case 'RGB': { // BLACK REGISTRATION (RGB)
14069  $col_a = array(255,255,255);
14070  $col_b = array(0,0,0);
14071  break;
14072  }
14073  case 'CMYK': { // BLACK REGISTRATION (CMYK)
14074  $col_a = array(0,0,0,0);
14075  $col_b = array(100,100,100,100);
14076  break;
14077  }
14078  case 'ALL': { // SPOT COLOR REGISTRATION
14079  $col_a = array(0,0,0,0,'None');
14080  $col_b = array(100,100,100,100,'All');
14081  break;
14082  }
14083  case 'NONE': { // SKIP THIS COLOR
14084  $col_a = array(0,0,0,0,'None');
14085  $col_b = array(0,0,0,0,'None');
14086  break;
14087  }
14088  default: { // SPECIFIC SPOT COLOR NAME
14089  $col_a = array(0,0,0,0,'None');
14090  $col_b = TCPDF_COLORS::getSpotColor($col, $this->spot_colors);
14091  if ($col_b === false) {
14092  // in case of error defaults to the registration color
14093  $col_b = array(100,100,100,100,'All');
14094  }
14095  break;
14096  }
14097  }
14098  if ($col != 'NONE') {
14099  if ($transition) {
14100  // color gradient
14101  $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
14102  } else {
14103  $this->SetFillColorArray($col_b);
14104  // colored rectangle
14105  $this->Rect($xb, $yb, $wb, $hb, 'F', array());
14106  }
14107  $xb += $xd;
14108  $yb += $yd;
14109  }
14110  }
14111  }
14112 
14125  public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(100,100,100,100,'All')) {
14126  $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
14127  $type = strtoupper($type);
14128  $type = preg_replace('/[^A-Z\-\,]*/', '', $type);
14129  // split type in single components
14130  $type = str_replace('-', ',', $type);
14131  $type = str_replace('TL', 'T,L', $type);
14132  $type = str_replace('TR', 'T,R', $type);
14133  $type = str_replace('BL', 'F,L', $type);
14134  $type = str_replace('BR', 'F,R', $type);
14135  $type = str_replace('A', 'T,L', $type);
14136  $type = str_replace('B', 'T,R', $type);
14137  $type = str_replace('T,RO', 'BO', $type);
14138  $type = str_replace('C', 'F,L', $type);
14139  $type = str_replace('D', 'F,R', $type);
14140  $crops = explode(',', strtoupper($type));
14141  // remove duplicates
14142  $crops = array_unique($crops);
14143  $dw = ($w / 4); // horizontal space to leave before the intersection point
14144  $dh = ($h / 4); // vertical space to leave before the intersection point
14145  foreach ($crops as $crop) {
14146  switch ($crop) {
14147  case 'T':
14148  case 'TOP': {
14149  $x1 = $x;
14150  $y1 = ($y - $h);
14151  $x2 = $x;
14152  $y2 = ($y - $dh);
14153  break;
14154  }
14155  case 'F':
14156  case 'BOTTOM': {
14157  $x1 = $x;
14158  $y1 = ($y + $dh);
14159  $x2 = $x;
14160  $y2 = ($y + $h);
14161  break;
14162  }
14163  case 'L':
14164  case 'LEFT': {
14165  $x1 = ($x - $w);
14166  $y1 = $y;
14167  $x2 = ($x - $dw);
14168  $y2 = $y;
14169  break;
14170  }
14171  case 'R':
14172  case 'RIGHT': {
14173  $x1 = ($x + $dw);
14174  $y1 = $y;
14175  $x2 = ($x + $w);
14176  $y2 = $y;
14177  break;
14178  }
14179  }
14180  $this->Line($x1, $y1, $x2, $y2);
14181  }
14182  }
14183 
14196  public function registrationMark($x, $y, $r, $double=false, $cola=array(100,100,100,100,'All'), $colb=array(0,0,0,0,'None')) {
14197  $line_style = array('width' => max((0.5 / $this->k),($r / 30)), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
14198  $this->SetFillColorArray($cola);
14199  $this->PieSector($x, $y, $r, 90, 180, 'F');
14200  $this->PieSector($x, $y, $r, 270, 360, 'F');
14201  $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14202  if ($double) {
14203  $ri = $r * 0.5;
14204  $this->SetFillColorArray($colb);
14205  $this->PieSector($x, $y, $ri, 90, 180, 'F');
14206  $this->PieSector($x, $y, $ri, 270, 360, 'F');
14207  $this->SetFillColorArray($cola);
14208  $this->PieSector($x, $y, $ri, 0, 90, 'F');
14209  $this->PieSector($x, $y, $ri, 180, 270, 'F');
14210  $this->Circle($x, $y, $ri, 0, 360, 'C', $line_style, array(), 8);
14211  }
14212  }
14213 
14223  public function registrationMarkCMYK($x, $y, $r) {
14224  // line width
14225  $lw = max((0.5 / $this->k),($r / 8));
14226  // internal radius
14227  $ri = ($r * 0.6);
14228  // external radius
14229  $re = ($r * 1.3);
14230  // Cyan
14231  $this->SetFillColorArray(array(100,0,0,0));
14232  $this->PieSector($x, $y, $ri, 270, 360, 'F');
14233  // Magenta
14234  $this->SetFillColorArray(array(0,100,0,0));
14235  $this->PieSector($x, $y, $ri, 0, 90, 'F');
14236  // Yellow
14237  $this->SetFillColorArray(array(0,0,100,0));
14238  $this->PieSector($x, $y, $ri, 90, 180, 'F');
14239  // Key - black
14240  $this->SetFillColorArray(array(0,0,0,100));
14241  $this->PieSector($x, $y, $ri, 180, 270, 'F');
14242  // registration color
14243  $line_style = array('width' => $lw, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(100,100,100,100,'All'));
14244  $this->SetFillColorArray(array(100,100,100,100,'All'));
14245  // external circle
14246  $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14247  // cross lines
14248  $this->Line($x, ($y - $re), $x, ($y - $ri));
14249  $this->Line($x, ($y + $ri), $x, ($y + $re));
14250  $this->Line(($x - $re), $y, ($x - $ri), $y);
14251  $this->Line(($x + $ri), $y, ($x + $re), $y);
14252  }
14253 
14267  public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
14268  $this->Clip($x, $y, $w, $h);
14269  $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14270  }
14271 
14285  public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
14286  $this->Clip($x, $y, $w, $h);
14287  $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14288  }
14289 
14308  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) {
14309  if ($this->pdfa_mode OR ($this->state != 2)) {
14310  return;
14311  }
14312  $this->Clip($x, $y, $w, $h);
14313  $n = count($this->gradients) + 1;
14314  $this->gradients[$n] = array();
14315  $this->gradients[$n]['type'] = 6; //coons patch mesh
14316  $this->gradients[$n]['coords'] = array();
14317  $this->gradients[$n]['antialias'] = $antialias;
14318  $this->gradients[$n]['colors'] = array();
14319  $this->gradients[$n]['transparency'] = false;
14320  //check the coords array if it is the simple array or the multi patch array
14321  if (!isset($coords[0]['f'])) {
14322  //simple array -> convert to multi patch array
14323  if (!isset($col1[1])) {
14324  $col1[1] = $col1[2] = $col1[0];
14325  }
14326  if (!isset($col2[1])) {
14327  $col2[1] = $col2[2] = $col2[0];
14328  }
14329  if (!isset($col3[1])) {
14330  $col3[1] = $col3[2] = $col3[0];
14331  }
14332  if (!isset($col4[1])) {
14333  $col4[1] = $col4[2] = $col4[0];
14334  }
14335  $patch_array[0]['f'] = 0;
14336  $patch_array[0]['points'] = $coords;
14337  $patch_array[0]['colors'][0]['r'] = $col1[0];
14338  $patch_array[0]['colors'][0]['g'] = $col1[1];
14339  $patch_array[0]['colors'][0]['b'] = $col1[2];
14340  $patch_array[0]['colors'][1]['r'] = $col2[0];
14341  $patch_array[0]['colors'][1]['g'] = $col2[1];
14342  $patch_array[0]['colors'][1]['b'] = $col2[2];
14343  $patch_array[0]['colors'][2]['r'] = $col3[0];
14344  $patch_array[0]['colors'][2]['g'] = $col3[1];
14345  $patch_array[0]['colors'][2]['b'] = $col3[2];
14346  $patch_array[0]['colors'][3]['r'] = $col4[0];
14347  $patch_array[0]['colors'][3]['g'] = $col4[1];
14348  $patch_array[0]['colors'][3]['b'] = $col4[2];
14349  } else {
14350  //multi patch array
14351  $patch_array = $coords;
14352  }
14353  $bpcd = 65535; //16 bits per coordinate
14354  //build the data stream
14355  $this->gradients[$n]['stream'] = '';
14356  $count_patch = count($patch_array);
14357  for ($i=0; $i < $count_patch; ++$i) {
14358  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
14359  $count_points = count($patch_array[$i]['points']);
14360  for ($j=0; $j < $count_points; ++$j) {
14361  //each point as 16 bit
14362  $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
14363  if ($patch_array[$i]['points'][$j] < 0) {
14364  $patch_array[$i]['points'][$j] = 0;
14365  }
14366  if ($patch_array[$i]['points'][$j] > $bpcd) {
14367  $patch_array[$i]['points'][$j] = $bpcd;
14368  }
14369  $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
14370  $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
14371  }
14372  $count_cols = count($patch_array[$i]['colors']);
14373  for ($j=0; $j < $count_cols; ++$j) {
14374  //each color component as 8 bit
14375  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
14376  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
14377  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
14378  }
14379  }
14380  //paint the gradient
14381  $this->_out('/Sh'.$n.' sh');
14382  //restore previous Graphic State
14383  $this->_outRestoreGraphicsState();
14384  if ($this->inxobj) {
14385  // we are inside an XObject template
14386  $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14387  }
14388  }
14389 
14400  protected function Clip($x, $y, $w, $h) {
14401  if ($this->state != 2) {
14402  return;
14403  }
14404  if ($this->rtl) {
14405  $x = $this->w - $x - $w;
14406  }
14407  //save current Graphic State
14408  $s = 'q';
14409  //set clipping area
14410  $s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
14411  //set up transformation matrix for gradient
14412  $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
14413  $this->_out($s);
14414  }
14415 
14427  public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
14428  if ($this->pdfa_mode OR ($this->state != 2)) {
14429  return;
14430  }
14431  $n = count($this->gradients) + 1;
14432  $this->gradients[$n] = array();
14433  $this->gradients[$n]['type'] = $type;
14434  $this->gradients[$n]['coords'] = $coords;
14435  $this->gradients[$n]['antialias'] = $antialias;
14436  $this->gradients[$n]['colors'] = array();
14437  $this->gradients[$n]['transparency'] = false;
14438  // color space
14439  $numcolspace = count($stops[0]['color']);
14440  $bcolor = array_values($background);
14441  switch($numcolspace) {
14442  case 5: // SPOT
14443  case 4: { // CMYK
14444  $this->gradients[$n]['colspace'] = 'DeviceCMYK';
14445  if (!empty($background)) {
14446  $this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
14447  }
14448  break;
14449  }
14450  case 3: { // RGB
14451  $this->gradients[$n]['colspace'] = 'DeviceRGB';
14452  if (!empty($background)) {
14453  $this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
14454  }
14455  break;
14456  }
14457  case 1: { // GRAY SCALE
14458  $this->gradients[$n]['colspace'] = 'DeviceGray';
14459  if (!empty($background)) {
14460  $this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255);
14461  }
14462  break;
14463  }
14464  }
14465  $num_stops = count($stops);
14466  $last_stop_id = $num_stops - 1;
14467  foreach ($stops as $key => $stop) {
14468  $this->gradients[$n]['colors'][$key] = array();
14469  // offset represents a location along the gradient vector
14470  if (isset($stop['offset'])) {
14471  $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
14472  } else {
14473  if ($key == 0) {
14474  $this->gradients[$n]['colors'][$key]['offset'] = 0;
14475  } elseif ($key == $last_stop_id) {
14476  $this->gradients[$n]['colors'][$key]['offset'] = 1;
14477  } else {
14478  $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
14479  $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
14480  }
14481  }
14482  if (isset($stop['opacity'])) {
14483  $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
14484  if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) {
14485  $this->gradients[$n]['transparency'] = true;
14486  }
14487  } else {
14488  $this->gradients[$n]['colors'][$key]['opacity'] = 1;
14489  }
14490  // exponent for the exponential interpolation function
14491  if (isset($stop['exponent'])) {
14492  $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
14493  } else {
14494  $this->gradients[$n]['colors'][$key]['exponent'] = 1;
14495  }
14496  // set colors
14497  $color = array_values($stop['color']);
14498  switch($numcolspace) {
14499  case 5: // SPOT
14500  case 4: { // CMYK
14501  $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
14502  break;
14503  }
14504  case 3: { // RGB
14505  $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255);
14506  break;
14507  }
14508  case 1: { // GRAY SCALE
14509  $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255);
14510  break;
14511  }
14512  }
14513  }
14514  if ($this->gradients[$n]['transparency']) {
14515  // paint luminosity gradient
14516  $this->_out('/TGS'.$n.' gs');
14517  }
14518  //paint the gradient
14519  $this->_out('/Sh'.$n.' sh');
14520  //restore previous Graphic State
14521  $this->_outRestoreGraphicsState();
14522  if ($this->inxobj) {
14523  // we are inside an XObject template
14524  $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14525  }
14526  }
14527 
14534  function _putshaders() {
14535  if ($this->pdfa_mode) {
14536  return;
14537  }
14538  $idt = count($this->gradients); //index for transparency gradients
14539  foreach ($this->gradients as $id => $grad) {
14540  if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
14541  $fc = $this->_newobj();
14542  $out = '<<';
14543  $out .= ' /FunctionType 3';
14544  $out .= ' /Domain [0 1]';
14545  $functions = '';
14546  $bounds = '';
14547  $encode = '';
14548  $i = 1;
14549  $num_cols = count($grad['colors']);
14550  $lastcols = $num_cols - 1;
14551  for ($i = 1; $i < $num_cols; ++$i) {
14552  $functions .= ($fc + $i).' 0 R ';
14553  if ($i < $lastcols) {
14554  $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']);
14555  }
14556  $encode .= '0 1 ';
14557  }
14558  $out .= ' /Functions ['.trim($functions).']';
14559  $out .= ' /Bounds ['.trim($bounds).']';
14560  $out .= ' /Encode ['.trim($encode).']';
14561  $out .= ' >>';
14562  $out .= "\n".'endobj';
14563  $this->_out($out);
14564  for ($i = 1; $i < $num_cols; ++$i) {
14565  $this->_newobj();
14566  $out = '<<';
14567  $out .= ' /FunctionType 2';
14568  $out .= ' /Domain [0 1]';
14569  $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
14570  $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
14571  $out .= ' /N '.$grad['colors'][$i]['exponent'];
14572  $out .= ' >>';
14573  $out .= "\n".'endobj';
14574  $this->_out($out);
14575  }
14576  // set transparency fuctions
14577  if ($grad['transparency']) {
14578  $ft = $this->_newobj();
14579  $out = '<<';
14580  $out .= ' /FunctionType 3';
14581  $out .= ' /Domain [0 1]';
14582  $functions = '';
14583  $i = 1;
14584  $num_cols = count($grad['colors']);
14585  for ($i = 1; $i < $num_cols; ++$i) {
14586  $functions .= ($ft + $i).' 0 R ';
14587  }
14588  $out .= ' /Functions ['.trim($functions).']';
14589  $out .= ' /Bounds ['.trim($bounds).']';
14590  $out .= ' /Encode ['.trim($encode).']';
14591  $out .= ' >>';
14592  $out .= "\n".'endobj';
14593  $this->_out($out);
14594  for ($i = 1; $i < $num_cols; ++$i) {
14595  $this->_newobj();
14596  $out = '<<';
14597  $out .= ' /FunctionType 2';
14598  $out .= ' /Domain [0 1]';
14599  $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
14600  $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
14601  $out .= ' /N '.$grad['colors'][$i]['exponent'];
14602  $out .= ' >>';
14603  $out .= "\n".'endobj';
14604  $this->_out($out);
14605  }
14606  }
14607  }
14608  // set shading object
14609  $this->_newobj();
14610  $out = '<< /ShadingType '.$grad['type'];
14611  if (isset($grad['colspace'])) {
14612  $out .= ' /ColorSpace /'.$grad['colspace'];
14613  } else {
14614  $out .= ' /ColorSpace /DeviceRGB';
14615  }
14616  if (isset($grad['background']) AND !empty($grad['background'])) {
14617  $out .= ' /Background ['.$grad['background'].']';
14618  }
14619  if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
14620  $out .= ' /AntiAlias true';
14621  }
14622  if ($grad['type'] == 2) {
14623  $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
14624  $out .= ' /Domain [0 1]';
14625  $out .= ' /Function '.$fc.' 0 R';
14626  $out .= ' /Extend [true true]';
14627  $out .= ' >>';
14628  } elseif ($grad['type'] == 3) {
14629  //x0, y0, r0, x1, y1, r1
14630  //at this this time radius of inner circle is 0
14631  $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
14632  $out .= ' /Domain [0 1]';
14633  $out .= ' /Function '.$fc.' 0 R';
14634  $out .= ' /Extend [true true]';
14635  $out .= ' >>';
14636  } elseif ($grad['type'] == 6) {
14637  $out .= ' /BitsPerCoordinate 16';
14638  $out .= ' /BitsPerComponent 8';
14639  $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
14640  $out .= ' /BitsPerFlag 8';
14641  $stream = $this->_getrawstream($grad['stream']);
14642  $out .= ' /Length '.strlen($stream);
14643  $out .= ' >>';
14644  $out .= ' stream'."\n".$stream."\n".'endstream';
14645  }
14646  $out .= "\n".'endobj';
14647  $this->_out($out);
14648  if ($grad['transparency']) {
14649  $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
14650  $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
14651  }
14652  $this->gradients[$id]['id'] = $this->n;
14653  // set pattern object
14654  $this->_newobj();
14655  $out = '<< /Type /Pattern /PatternType 2';
14656  $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
14657  $out .= ' >>';
14658  $out .= "\n".'endobj';
14659  $this->_out($out);
14660  $this->gradients[$id]['pattern'] = $this->n;
14661  // set shading and pattern for transparency mask
14662  if ($grad['transparency']) {
14663  // luminosity pattern
14664  $idgs = $id + $idt;
14665  $this->_newobj();
14666  $this->_out($shading_transparency);
14667  $this->gradients[$idgs]['id'] = $this->n;
14668  $this->_newobj();
14669  $out = '<< /Type /Pattern /PatternType 2';
14670  $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
14671  $out .= ' >>';
14672  $out .= "\n".'endobj';
14673  $this->_out($out);
14674  $this->gradients[$idgs]['pattern'] = $this->n;
14675  // luminosity XObject
14676  $oid = $this->_newobj();
14677  $this->xobjects['LX'.$oid] = array('n' => $oid);
14678  $filter = '';
14679  $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
14680  if ($this->compress) {
14681  $filter = ' /Filter /FlateDecode';
14682  $stream = gzcompress($stream);
14683  }
14684  $stream = $this->_getrawstream($stream);
14685  $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
14686  $out .= ' /Length '.strlen($stream);
14687  $rect = sprintf('%F %F', $this->wPt, $this->hPt);
14688  $out .= ' /BBox [0 0 '.$rect.']';
14689  $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
14690  $out .= ' /Resources <<';
14691  $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
14692  $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
14693  $out .= ' >>';
14694  $out .= ' >> ';
14695  $out .= ' stream'."\n".$stream."\n".'endstream';
14696  $out .= "\n".'endobj';
14697  $this->_out($out);
14698  // SMask
14699  $this->_newobj();
14700  $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj';
14701  $this->_out($out);
14702  // ExtGState
14703  $this->_newobj();
14704  $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj';
14705  $this->_out($out);
14706  $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
14707  }
14708  }
14709  }
14710 
14726  public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
14727  $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
14728  }
14729 
14747  public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
14748  if ($this->state != 2) {
14749  return;
14750  }
14751  if ($this->rtl) {
14752  $xc = ($this->w - $xc);
14753  }
14754  $op = TCPDF_STATIC::getPathPaintOperator($style);
14755  if ($op == 'f') {
14756  $line_style = array();
14757  }
14758  if ($cw) {
14759  $d = $b;
14760  $b = (360 - $a + $o);
14761  $a = (360 - $d + $o);
14762  } else {
14763  $b += $o;
14764  $a += $o;
14765  }
14766  $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
14767  $this->_out($op);
14768  }
14769 
14791  public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
14792  if ($this->state != 2) {
14793  return;
14794  }
14795  if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
14796  // convert EPS to raster image using GD or ImageMagick libraries
14797  return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14798  }
14799  if ($x === '') {
14800  $x = $this->x;
14801  }
14802  if ($y === '') {
14803  $y = $this->y;
14804  }
14805  // check page for no-write regions and adapt page margins if necessary
14806  list($x, $y) = $this->checkPageRegions($h, $x, $y);
14807  $k = $this->k;
14808  if ($file{0} === '@') { // image from string
14809  $data = substr($file, 1);
14810  } else { // EPS/AI file
14811  $data = TCPDF_STATIC::fileGetContents($file);
14812  }
14813  if ($data === FALSE) {
14814  $this->Error('EPS file not found: '.$file);
14815  }
14816  $regs = array();
14817  // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
14818  preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
14819  if (count($regs) > 1) {
14820  $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
14821  if (strpos($version_str, 'Adobe Illustrator') !== false) {
14822  $versexp = explode(' ', $version_str);
14823  $version = (float)array_pop($versexp);
14824  if ($version >= 9) {
14825  $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
14826  }
14827  }
14828  }
14829  // strip binary bytes in front of PS-header
14830  $start = strpos($data, '%!PS-Adobe');
14831  if ($start > 0) {
14832  $data = substr($data, $start);
14833  }
14834  // find BoundingBox params
14835  preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
14836  if (count($regs) > 1) {
14837  list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
14838  } else {
14839  $this->Error('No BoundingBox found in EPS/AI file: '.$file);
14840  }
14841  $start = strpos($data, '%%EndSetup');
14842  if ($start === false) {
14843  $start = strpos($data, '%%EndProlog');
14844  }
14845  if ($start === false) {
14846  $start = strpos($data, '%%BoundingBox');
14847  }
14848  $data = substr($data, $start);
14849  $end = strpos($data, '%%PageTrailer');
14850  if ($end===false) {
14851  $end = strpos($data, 'showpage');
14852  }
14853  if ($end) {
14854  $data = substr($data, 0, $end);
14855  }
14856  // calculate image width and height on document
14857  if (($w <= 0) AND ($h <= 0)) {
14858  $w = ($x2 - $x1) / $k;
14859  $h = ($y2 - $y1) / $k;
14860  } elseif ($w <= 0) {
14861  $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
14862  } elseif ($h <= 0) {
14863  $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
14864  }
14865  // fit the image on available space
14866  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
14867  if ($this->rasterize_vector_images) {
14868  // convert EPS to raster image using GD or ImageMagick libraries
14869  return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14870  }
14871  // set scaling factors
14872  $scale_x = $w / (($x2 - $x1) / $k);
14873  $scale_y = $h / (($y2 - $y1) / $k);
14874  // set alignment
14875  $this->img_rb_y = $y + $h;
14876  // set alignment
14877  if ($this->rtl) {
14878  if ($palign == 'L') {
14879  $ximg = $this->lMargin;
14880  } elseif ($palign == 'C') {
14881  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
14882  } elseif ($palign == 'R') {
14883  $ximg = $this->w - $this->rMargin - $w;
14884  } else {
14885  $ximg = $x - $w;
14886  }
14887  $this->img_rb_x = $ximg;
14888  } else {
14889  if ($palign == 'L') {
14890  $ximg = $this->lMargin;
14891  } elseif ($palign == 'C') {
14892  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
14893  } elseif ($palign == 'R') {
14894  $ximg = $this->w - $this->rMargin - $w;
14895  } else {
14896  $ximg = $x;
14897  }
14898  $this->img_rb_x = $ximg + $w;
14899  }
14900  if ($useBoundingBox) {
14901  $dx = $ximg * $k - $x1;
14902  $dy = $y * $k - $y1;
14903  } else {
14904  $dx = $ximg * $k;
14905  $dy = $y * $k;
14906  }
14907  // save the current graphic state
14908  $this->_out('q'.$this->epsmarker);
14909  // translate
14910  $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
14911  // scale
14912  if (isset($scale_x)) {
14913  $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
14914  }
14915  // handle pc/unix/mac line endings
14916  $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY);
14917  $u=0;
14918  $cnt = count($lines);
14919  for ($i=0; $i < $cnt; ++$i) {
14920  $line = $lines[$i];
14921  if (($line == '') OR ($line{0} == '%')) {
14922  continue;
14923  }
14924  $len = strlen($line);
14925  // check for spot color names
14926  $color_name = '';
14927  if (strcasecmp('x', substr(trim($line), -1)) == 0) {
14928  if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) {
14929  // extract spot color name
14930  $color_name = $matches[0];
14931  // remove color name from string
14932  $line = str_replace(' '.$color_name, '', $line);
14933  // remove pharentesis from color name
14934  $color_name = substr($color_name, 1, -1);
14935  }
14936  }
14937  $chunks = explode(' ', $line);
14938  $cmd = trim(array_pop($chunks));
14939  // RGB
14940  if (($cmd == 'Xa') OR ($cmd == 'XA')) {
14941  $b = array_pop($chunks);
14942  $g = array_pop($chunks);
14943  $r = array_pop($chunks);
14944  $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!
14945  continue;
14946  }
14947  $skip = false;
14948  if ($fixoutvals) {
14949  // check for values outside the bounding box
14950  switch ($cmd) {
14951  case 'm':
14952  case 'l':
14953  case 'L': {
14954  // skip values outside bounding box
14955  foreach ($chunks as $key => $val) {
14956  if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) {
14957  $skip = true;
14958  } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) {
14959  $skip = true;
14960  }
14961  }
14962  }
14963  }
14964  }
14965  switch ($cmd) {
14966  case 'm':
14967  case 'l':
14968  case 'v':
14969  case 'y':
14970  case 'c':
14971  case 'k':
14972  case 'K':
14973  case 'g':
14974  case 'G':
14975  case 's':
14976  case 'S':
14977  case 'J':
14978  case 'j':
14979  case 'w':
14980  case 'M':
14981  case 'd':
14982  case 'n': {
14983  if ($skip) {
14984  break;
14985  }
14986  $this->_out($line);
14987  break;
14988  }
14989  case 'x': {// custom fill color
14990  if (empty($color_name)) {
14991  // CMYK color
14992  list($col_c, $col_m, $col_y, $col_k) = $chunks;
14993  $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
14994  } else {
14995  // Spot Color (CMYK + tint)
14996  list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
14997  $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
14998  $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t));
14999  $this->_out($color_cmd);
15000  }
15001  break;
15002  }
15003  case 'X': { // custom stroke color
15004  if (empty($color_name)) {
15005  // CMYK color
15006  list($col_c, $col_m, $col_y, $col_k) = $chunks;
15007  $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
15008  } else {
15009  // Spot Color (CMYK + tint)
15010  list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15011  $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15012  $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15013  $this->_out($color_cmd);
15014  }
15015  break;
15016  }
15017  case 'Y':
15018  case 'N':
15019  case 'V':
15020  case 'L':
15021  case 'C': {
15022  if ($skip) {
15023  break;
15024  }
15025  $line[($len - 1)] = strtolower($cmd);
15026  $this->_out($line);
15027  break;
15028  }
15029  case 'b':
15030  case 'B': {
15031  $this->_out($cmd . '*');
15032  break;
15033  }
15034  case 'f':
15035  case 'F': {
15036  if ($u > 0) {
15037  $isU = false;
15038  $max = min(($i + 5), $cnt);
15039  for ($j = ($i + 1); $j < $max; ++$j) {
15040  $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
15041  }
15042  if ($isU) {
15043  $this->_out('f*');
15044  }
15045  } else {
15046  $this->_out('f*');
15047  }
15048  break;
15049  }
15050  case '*u': {
15051  ++$u;
15052  break;
15053  }
15054  case '*U': {
15055  --$u;
15056  break;
15057  }
15058  }
15059  }
15060  // restore previous graphic state
15061  $this->_out($this->epsmarker.'Q');
15062  if (!empty($border)) {
15063  $bx = $this->x;
15064  $by = $this->y;
15065  $this->x = $ximg;
15066  if ($this->rtl) {
15067  $this->x += $w;
15068  }
15069  $this->y = $y;
15070  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
15071  $this->x = $bx;
15072  $this->y = $by;
15073  }
15074  if ($link) {
15075  $this->Link($ximg, $y, $w, $h, $link, 0);
15076  }
15077  // set pointer to align the next text/objects
15078  switch($align) {
15079  case 'T':{
15080  $this->y = $y;
15081  $this->x = $this->img_rb_x;
15082  break;
15083  }
15084  case 'M':{
15085  $this->y = $y + round($h/2);
15086  $this->x = $this->img_rb_x;
15087  break;
15088  }
15089  case 'B':{
15090  $this->y = $this->img_rb_y;
15091  $this->x = $this->img_rb_x;
15092  break;
15093  }
15094  case 'N':{
15095  $this->SetY($this->img_rb_y);
15096  break;
15097  }
15098  default:{
15099  break;
15100  }
15101  }
15102  $this->endlinex = $this->img_rb_x;
15103  }
15104 
15110  public function setBarcode($bc='') {
15111  $this->barcode = $bc;
15112  }
15113 
15120  public function getBarcode() {
15121  return $this->barcode;
15122  }
15123 
15154  public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
15155  if (TCPDF_STATIC::empty_string(trim($code))) {
15156  return;
15157  }
15158  require_once(dirname(__FILE__).'/tcpdf_barcodes_1d.php');
15159  // save current graphic settings
15160  $gvars = $this->getGraphicVars();
15161  // create new barcode object
15162  $barcodeobj = new TCPDFBarcode($code, $type);
15163  $arrcode = $barcodeobj->getBarcodeArray();
15164  if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] <= 0)) {
15165  $this->Error('Error in 1D barcode string');
15166  }
15167  if ($arrcode['maxh'] <= 0) {
15168  $arrcode['maxh'] = 1;
15169  }
15170  // set default values
15171  if (!isset($style['position'])) {
15172  $style['position'] = '';
15173  } elseif ($style['position'] == 'S') {
15174  // keep this for backward compatibility
15175  $style['position'] = '';
15176  $style['stretch'] = true;
15177  }
15178  if (!isset($style['fitwidth'])) {
15179  if (!isset($style['stretch'])) {
15180  $style['fitwidth'] = true;
15181  } else {
15182  $style['fitwidth'] = false;
15183  }
15184  }
15185  if ($style['fitwidth']) {
15186  // disable stretch
15187  $style['stretch'] = false;
15188  }
15189  if (!isset($style['stretch'])) {
15190  if (($w === '') OR ($w <= 0)) {
15191  $style['stretch'] = false;
15192  } else {
15193  $style['stretch'] = true;
15194  }
15195  }
15196  if (!isset($style['fgcolor'])) {
15197  $style['fgcolor'] = array(0,0,0); // default black
15198  }
15199  if (!isset($style['bgcolor'])) {
15200  $style['bgcolor'] = false; // default transparent
15201  }
15202  if (!isset($style['border'])) {
15203  $style['border'] = false;
15204  }
15205  $fontsize = 0;
15206  if (!isset($style['text'])) {
15207  $style['text'] = false;
15208  }
15209  if ($style['text'] AND isset($style['font'])) {
15210  if (isset($style['fontsize'])) {
15211  $fontsize = $style['fontsize'];
15212  }
15213  $this->SetFont($style['font'], '', $fontsize);
15214  }
15215  if (!isset($style['stretchtext'])) {
15216  $style['stretchtext'] = 4;
15217  }
15218  if ($x === '') {
15219  $x = $this->x;
15220  }
15221  if ($y === '') {
15222  $y = $this->y;
15223  }
15224  // check page for no-write regions and adapt page margins if necessary
15225  list($x, $y) = $this->checkPageRegions($h, $x, $y);
15226  if (($w === '') OR ($w <= 0)) {
15227  if ($this->rtl) {
15228  $w = $x - $this->lMargin;
15229  } else {
15230  $w = $this->w - $this->rMargin - $x;
15231  }
15232  }
15233  // padding
15234  if (!isset($style['padding'])) {
15235  $padding = 0;
15236  } elseif ($style['padding'] === 'auto') {
15237  $padding = 10 * ($w / ($arrcode['maxw'] + 20));
15238  } else {
15239  $padding = floatval($style['padding']);
15240  }
15241  // horizontal padding
15242  if (!isset($style['hpadding'])) {
15243  $hpadding = $padding;
15244  } elseif ($style['hpadding'] === 'auto') {
15245  $hpadding = 10 * ($w / ($arrcode['maxw'] + 20));
15246  } else {
15247  $hpadding = floatval($style['hpadding']);
15248  }
15249  // vertical padding
15250  if (!isset($style['vpadding'])) {
15251  $vpadding = $padding;
15252  } elseif ($style['vpadding'] === 'auto') {
15253  $vpadding = ($hpadding / 2);
15254  } else {
15255  $vpadding = floatval($style['vpadding']);
15256  }
15257  // calculate xres (single bar width)
15258  $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
15259  if ($style['stretch']) {
15260  $xres = $max_xres;
15261  } else {
15262  if (TCPDF_STATIC::empty_string($xres)) {
15263  $xres = (0.141 * $this->k); // default bar width = 0.4 mm
15264  }
15265  if ($xres > $max_xres) {
15266  // correct xres to fit on $w
15267  $xres = $max_xres;
15268  }
15269  if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
15270  OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
15271  $hpadding = 10 * $xres;
15272  if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
15273  $vpadding = ($hpadding / 2);
15274  }
15275  }
15276  }
15277  if ($style['fitwidth']) {
15278  $wold = $w;
15279  $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding));
15280  if (isset($style['cellfitalign'])) {
15281  switch ($style['cellfitalign']) {
15282  case 'L': {
15283  if ($this->rtl) {
15284  $x -= ($wold - $w);
15285  }
15286  break;
15287  }
15288  case 'R': {
15289  if (!$this->rtl) {
15290  $x += ($wold - $w);
15291  }
15292  break;
15293  }
15294  case 'C': {
15295  if ($this->rtl) {
15296  $x -= (($wold - $w) / 2);
15297  } else {
15298  $x += (($wold - $w) / 2);
15299  }
15300  break;
15301  }
15302  default : {
15303  break;
15304  }
15305  }
15306  }
15307  }
15308  $text_height = $this->getCellHeight($fontsize / $this->k);
15309  // height
15310  if (($h === '') OR ($h <= 0)) {
15311  // set default height
15312  $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height;
15313  }
15314  $barh = $h - $text_height - (2 * $vpadding);
15315  if ($barh <=0) {
15316  // try to reduce font or padding to fit barcode on available height
15317  if ($text_height > $h) {
15318  $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio));
15319  $text_height = $this->getCellHeight($fontsize / $this->k);
15320  $this->SetFont($style['font'], '', $fontsize);
15321  }
15322  if ($vpadding > 0) {
15323  $vpadding = (($h - $text_height) / 4);
15324  }
15325  $barh = $h - $text_height - (2 * $vpadding);
15326  }
15327  // fit the barcode on available space
15328  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15329  // set alignment
15330  $this->img_rb_y = $y + $h;
15331  // set alignment
15332  if ($this->rtl) {
15333  if ($style['position'] == 'L') {
15334  $xpos = $this->lMargin;
15335  } elseif ($style['position'] == 'C') {
15336  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15337  } elseif ($style['position'] == 'R') {
15338  $xpos = $this->w - $this->rMargin - $w;
15339  } else {
15340  $xpos = $x - $w;
15341  }
15342  $this->img_rb_x = $xpos;
15343  } else {
15344  if ($style['position'] == 'L') {
15345  $xpos = $this->lMargin;
15346  } elseif ($style['position'] == 'C') {
15347  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15348  } elseif ($style['position'] == 'R') {
15349  $xpos = $this->w - $this->rMargin - $w;
15350  } else {
15351  $xpos = $x;
15352  }
15353  $this->img_rb_x = $xpos + $w;
15354  }
15355  $xpos_rect = $xpos;
15356  if (!isset($style['align'])) {
15357  $style['align'] = 'C';
15358  }
15359  switch ($style['align']) {
15360  case 'L': {
15361  $xpos = $xpos_rect + $hpadding;
15362  break;
15363  }
15364  case 'R': {
15365  $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding;
15366  break;
15367  }
15368  case 'C':
15369  default : {
15370  $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2);
15371  break;
15372  }
15373  }
15374  $xpos_text = $xpos;
15375  // barcode is always printed in LTR direction
15376  $tempRTL = $this->rtl;
15377  $this->rtl = false;
15378  // print background color
15379  if ($style['bgcolor']) {
15380  $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15381  } elseif ($style['border']) {
15382  $this->Rect($xpos_rect, $y, $w, $h, 'D');
15383  }
15384  // set foreground color
15385  $this->SetDrawColorArray($style['fgcolor']);
15386  $this->SetTextColorArray($style['fgcolor']);
15387  // print bars
15388  foreach ($arrcode['bcode'] as $k => $v) {
15389  $bw = ($v['w'] * $xres);
15390  if ($v['t']) {
15391  // draw a vertical bar
15392  $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']);
15393  $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
15394  }
15395  $xpos += $bw;
15396  }
15397  // print text
15398  if ($style['text']) {
15399  if (isset($style['label']) AND !TCPDF_STATIC::empty_string($style['label'])) {
15400  $label = $style['label'];
15401  } else {
15402  $label = $code;
15403  }
15404  $txtwidth = ($arrcode['maxw'] * $xres);
15405  if ($this->GetStringWidth($label) > $txtwidth) {
15406  $style['stretchtext'] = 2;
15407  }
15408  // print text
15409  $this->x = $xpos_text;
15410  $this->y = $y + $vpadding + $barh;
15411  $cellpadding = $this->cell_padding;
15412  $this->SetCellPadding(0);
15413  $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
15414  $this->cell_padding = $cellpadding;
15415  }
15416  // restore original direction
15417  $this->rtl = $tempRTL;
15418  // restore previous settings
15419  $this->setGraphicVars($gvars);
15420  // set pointer to align the next text/objects
15421  switch($align) {
15422  case 'T':{
15423  $this->y = $y;
15424  $this->x = $this->img_rb_x;
15425  break;
15426  }
15427  case 'M':{
15428  $this->y = $y + round($h / 2);
15429  $this->x = $this->img_rb_x;
15430  break;
15431  }
15432  case 'B':{
15433  $this->y = $this->img_rb_y;
15434  $this->x = $this->img_rb_x;
15435  break;
15436  }
15437  case 'N':{
15438  $this->SetY($this->img_rb_y);
15439  break;
15440  }
15441  default:{
15442  break;
15443  }
15444  }
15445  $this->endlinex = $this->img_rb_x;
15446  }
15447 
15473  public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) {
15474  if (TCPDF_STATIC::empty_string(trim($code))) {
15475  return;
15476  }
15477  require_once(dirname(__FILE__).'/tcpdf_barcodes_2d.php');
15478  // save current graphic settings
15479  $gvars = $this->getGraphicVars();
15480  // create new barcode object
15481  $barcodeobj = new TCPDF2DBarcode($code, $type);
15482  $arrcode = $barcodeobj->getBarcodeArray();
15483  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)) {
15484  $this->Error('Error in 2D barcode string');
15485  }
15486  // set default values
15487  if (!isset($style['position'])) {
15488  $style['position'] = '';
15489  }
15490  if (!isset($style['fgcolor'])) {
15491  $style['fgcolor'] = array(0,0,0); // default black
15492  }
15493  if (!isset($style['bgcolor'])) {
15494  $style['bgcolor'] = false; // default transparent
15495  }
15496  if (!isset($style['border'])) {
15497  $style['border'] = false;
15498  }
15499  // padding
15500  if (!isset($style['padding'])) {
15501  $style['padding'] = 0;
15502  } elseif ($style['padding'] === 'auto') {
15503  $style['padding'] = 4;
15504  }
15505  if (!isset($style['hpadding'])) {
15506  $style['hpadding'] = $style['padding'];
15507  } elseif ($style['hpadding'] === 'auto') {
15508  $style['hpadding'] = 4;
15509  }
15510  if (!isset($style['vpadding'])) {
15511  $style['vpadding'] = $style['padding'];
15512  } elseif ($style['vpadding'] === 'auto') {
15513  $style['vpadding'] = 4;
15514  }
15515  $hpad = (2 * $style['hpadding']);
15516  $vpad = (2 * $style['vpadding']);
15517  // cell (module) dimension
15518  if (!isset($style['module_width'])) {
15519  $style['module_width'] = 1; // width of a single module in points
15520  }
15521  if (!isset($style['module_height'])) {
15522  $style['module_height'] = 1; // height of a single module in points
15523  }
15524  if ($x === '') {
15525  $x = $this->x;
15526  }
15527  if ($y === '') {
15528  $y = $this->y;
15529  }
15530  // check page for no-write regions and adapt page margins if necessary
15531  list($x, $y) = $this->checkPageRegions($h, $x, $y);
15532  // number of barcode columns and rows
15533  $rows = $arrcode['num_rows'];
15534  $cols = $arrcode['num_cols'];
15535  if (($rows <= 0) || ($cols <= 0)){
15536  $this->Error('Error in 2D barcode string');
15537  }
15538  // module width and height
15539  $mw = $style['module_width'];
15540  $mh = $style['module_height'];
15541  if (($mw <= 0) OR ($mh <= 0)) {
15542  $this->Error('Error in 2D barcode string');
15543  }
15544  // get max dimensions
15545  if ($this->rtl) {
15546  $maxw = $x - $this->lMargin;
15547  } else {
15548  $maxw = $this->w - $this->rMargin - $x;
15549  }
15550  $maxh = ($this->h - $this->tMargin - $this->bMargin);
15551  $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad));
15552  $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad));
15553  if (!$distort) {
15554  if (($maxw * $ratioHW) > $maxh) {
15555  $maxw = $maxh * $ratioWH;
15556  }
15557  if (($maxh * $ratioWH) > $maxw) {
15558  $maxh = $maxw * $ratioHW;
15559  }
15560  }
15561  // set maximum dimesions
15562  if ($w > $maxw) {
15563  $w = $maxw;
15564  }
15565  if ($h > $maxh) {
15566  $h = $maxh;
15567  }
15568  // set dimensions
15569  if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
15570  $w = ($cols + $hpad) * ($mw / $this->k);
15571  $h = ($rows + $vpad) * ($mh / $this->k);
15572  } elseif (($w === '') OR ($w <= 0)) {
15573  $w = $h * $ratioWH;
15574  } elseif (($h === '') OR ($h <= 0)) {
15575  $h = $w * $ratioHW;
15576  }
15577  // barcode size (excluding padding)
15578  $bw = ($w * $cols) / ($cols + $hpad);
15579  $bh = ($h * $rows) / ($rows + $vpad);
15580  // dimension of single barcode cell unit
15581  $cw = $bw / $cols;
15582  $ch = $bh / $rows;
15583  if (!$distort) {
15584  if (($cw / $ch) > ($mw / $mh)) {
15585  // correct horizontal distortion
15586  $cw = $ch * $mw / $mh;
15587  $bw = $cw * $cols;
15588  $style['hpadding'] = ($w - $bw) / (2 * $cw);
15589  } else {
15590  // correct vertical distortion
15591  $ch = $cw * $mh / $mw;
15592  $bh = $ch * $rows;
15593  $style['vpadding'] = ($h - $bh) / (2 * $ch);
15594  }
15595  }
15596  // fit the barcode on available space
15597  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15598  // set alignment
15599  $this->img_rb_y = $y + $h;
15600  // set alignment
15601  if ($this->rtl) {
15602  if ($style['position'] == 'L') {
15603  $xpos = $this->lMargin;
15604  } elseif ($style['position'] == 'C') {
15605  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15606  } elseif ($style['position'] == 'R') {
15607  $xpos = $this->w - $this->rMargin - $w;
15608  } else {
15609  $xpos = $x - $w;
15610  }
15611  $this->img_rb_x = $xpos;
15612  } else {
15613  if ($style['position'] == 'L') {
15614  $xpos = $this->lMargin;
15615  } elseif ($style['position'] == 'C') {
15616  $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15617  } elseif ($style['position'] == 'R') {
15618  $xpos = $this->w - $this->rMargin - $w;
15619  } else {
15620  $xpos = $x;
15621  }
15622  $this->img_rb_x = $xpos + $w;
15623  }
15624  $xstart = $xpos + ($style['hpadding'] * $cw);
15625  $ystart = $y + ($style['vpadding'] * $ch);
15626  // barcode is always printed in LTR direction
15627  $tempRTL = $this->rtl;
15628  $this->rtl = false;
15629  // print background color
15630  if ($style['bgcolor']) {
15631  $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15632  } elseif ($style['border']) {
15633  $this->Rect($xpos, $y, $w, $h, 'D');
15634  }
15635  // set foreground color
15636  $this->SetDrawColorArray($style['fgcolor']);
15637  // print barcode cells
15638  // for each row
15639  for ($r = 0; $r < $rows; ++$r) {
15640  $xr = $xstart;
15641  // for each column
15642  for ($c = 0; $c < $cols; ++$c) {
15643  if ($arrcode['bcode'][$r][$c] == 1) {
15644  // draw a single barcode cell
15645  $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
15646  }
15647  $xr += $cw;
15648  }
15649  $ystart += $ch;
15650  }
15651  // restore original direction
15652  $this->rtl = $tempRTL;
15653  // restore previous settings
15654  $this->setGraphicVars($gvars);
15655  // set pointer to align the next text/objects
15656  switch($align) {
15657  case 'T':{
15658  $this->y = $y;
15659  $this->x = $this->img_rb_x;
15660  break;
15661  }
15662  case 'M':{
15663  $this->y = $y + round($h/2);
15664  $this->x = $this->img_rb_x;
15665  break;
15666  }
15667  case 'B':{
15668  $this->y = $this->img_rb_y;
15669  $this->x = $this->img_rb_x;
15670  break;
15671  }
15672  case 'N':{
15673  $this->SetY($this->img_rb_y);
15674  break;
15675  }
15676  default:{
15677  break;
15678  }
15679  }
15680  $this->endlinex = $this->img_rb_x;
15681  }
15682 
15702  public function getMargins() {
15703  $ret = array(
15704  'left' => $this->lMargin,
15705  'right' => $this->rMargin,
15706  'top' => $this->tMargin,
15707  'bottom' => $this->bMargin,
15708  'header' => $this->header_margin,
15709  'footer' => $this->footer_margin,
15710  'cell' => $this->cell_padding,
15711  'padding_left' => $this->cell_padding['L'],
15712  'padding_top' => $this->cell_padding['T'],
15713  'padding_right' => $this->cell_padding['R'],
15714  'padding_bottom' => $this->cell_padding['B']
15715  );
15716  return $ret;
15717  }
15718 
15729  public function getOriginalMargins() {
15730  $ret = array(
15731  'left' => $this->original_lMargin,
15732  'right' => $this->original_rMargin
15733  );
15734  return $ret;
15735  }
15736 
15743  public function getFontSize() {
15744  return $this->FontSize;
15745  }
15746 
15753  public function getFontSizePt() {
15754  return $this->FontSizePt;
15755  }
15756 
15763  public function getFontFamily() {
15764  return $this->FontFamily;
15765  }
15766 
15773  public function getFontStyle() {
15774  return $this->FontStyle;
15775  }
15776 
15789  public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
15790  return TCPDF_STATIC::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces);
15791  }
15792 
15800  protected function getCSSBorderWidth($width) {
15801  if ($width == 'thin') {
15802  $width = (2 / $this->k);
15803  } elseif ($width == 'medium') {
15804  $width = (4 / $this->k);
15805  } elseif ($width == 'thick') {
15806  $width = (6 / $this->k);
15807  } else {
15808  $width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
15809  }
15810  return $width;
15811  }
15812 
15820  protected function getCSSBorderDashStyle($style) {
15821  switch (strtolower($style)) {
15822  case 'none':
15823  case 'hidden': {
15824  $dash = -1;
15825  break;
15826  }
15827  case 'dotted': {
15828  $dash = 1;
15829  break;
15830  }
15831  case 'dashed': {
15832  $dash = 3;
15833  break;
15834  }
15835  case 'double':
15836  case 'groove':
15837  case 'ridge':
15838  case 'inset':
15839  case 'outset':
15840  case 'solid':
15841  default: {
15842  $dash = 0;
15843  break;
15844  }
15845  }
15846  return $dash;
15847  }
15848 
15856  protected function getCSSBorderStyle($cssborder) {
15857  $bprop = preg_split('/[\s]+/', trim($cssborder));
15858  $border = array(); // value to be returned
15859  switch (count($bprop)) {
15860  case 3: {
15861  $width = $bprop[0];
15862  $style = $bprop[1];
15863  $color = $bprop[2];
15864  break;
15865  }
15866  case 2: {
15867  $width = 'medium';
15868  $style = $bprop[0];
15869  $color = $bprop[1];
15870  break;
15871  }
15872  case 1: {
15873  $width = 'medium';
15874  $style = $bprop[0];
15875  $color = 'black';
15876  break;
15877  }
15878  default: {
15879  $width = 'medium';
15880  $style = 'solid';
15881  $color = 'black';
15882  break;
15883  }
15884  }
15885  if ($style == 'none') {
15886  return array();
15887  }
15888  $border['cap'] = 'square';
15889  $border['join'] = 'miter';
15890  $border['dash'] = $this->getCSSBorderDashStyle($style);
15891  if ($border['dash'] < 0) {
15892  return array();
15893  }
15894  $border['width'] = $this->getCSSBorderWidth($width);
15895  $border['color'] = TCPDF_COLORS::convertHTMLColorToDec($color, $this->spot_colors);
15896  return $border;
15897  }
15898 
15907  public function getCSSPadding($csspadding, $width=0) {
15908  $padding = preg_split('/[\s]+/', trim($csspadding));
15909  $cell_padding = array(); // value to be returned
15910  switch (count($padding)) {
15911  case 4: {
15912  $cell_padding['T'] = $padding[0];
15913  $cell_padding['R'] = $padding[1];
15914  $cell_padding['B'] = $padding[2];
15915  $cell_padding['L'] = $padding[3];
15916  break;
15917  }
15918  case 3: {
15919  $cell_padding['T'] = $padding[0];
15920  $cell_padding['R'] = $padding[1];
15921  $cell_padding['B'] = $padding[2];
15922  $cell_padding['L'] = $padding[1];
15923  break;
15924  }
15925  case 2: {
15926  $cell_padding['T'] = $padding[0];
15927  $cell_padding['R'] = $padding[1];
15928  $cell_padding['B'] = $padding[0];
15929  $cell_padding['L'] = $padding[1];
15930  break;
15931  }
15932  case 1: {
15933  $cell_padding['T'] = $padding[0];
15934  $cell_padding['R'] = $padding[0];
15935  $cell_padding['B'] = $padding[0];
15936  $cell_padding['L'] = $padding[0];
15937  break;
15938  }
15939  default: {
15940  return $this->cell_padding;
15941  }
15942  }
15943  if ($width == 0) {
15944  $width = $this->w - $this->lMargin - $this->rMargin;
15945  }
15946  $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
15947  $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
15948  $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
15949  $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
15950  return $cell_padding;
15951  }
15952 
15961  public function getCSSMargin($cssmargin, $width=0) {
15962  $margin = preg_split('/[\s]+/', trim($cssmargin));
15963  $cell_margin = array(); // value to be returned
15964  switch (count($margin)) {
15965  case 4: {
15966  $cell_margin['T'] = $margin[0];
15967  $cell_margin['R'] = $margin[1];
15968  $cell_margin['B'] = $margin[2];
15969  $cell_margin['L'] = $margin[3];
15970  break;
15971  }
15972  case 3: {
15973  $cell_margin['T'] = $margin[0];
15974  $cell_margin['R'] = $margin[1];
15975  $cell_margin['B'] = $margin[2];
15976  $cell_margin['L'] = $margin[1];
15977  break;
15978  }
15979  case 2: {
15980  $cell_margin['T'] = $margin[0];
15981  $cell_margin['R'] = $margin[1];
15982  $cell_margin['B'] = $margin[0];
15983  $cell_margin['L'] = $margin[1];
15984  break;
15985  }
15986  case 1: {
15987  $cell_margin['T'] = $margin[0];
15988  $cell_margin['R'] = $margin[0];
15989  $cell_margin['B'] = $margin[0];
15990  $cell_margin['L'] = $margin[0];
15991  break;
15992  }
15993  default: {
15994  return $this->cell_margin;
15995  }
15996  }
15997  if ($width == 0) {
15998  $width = $this->w - $this->lMargin - $this->rMargin;
15999  }
16000  $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
16001  $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
16002  $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
16003  $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
16004  return $cell_margin;
16005  }
16006 
16015  public function getCSSBorderMargin($cssbspace, $width=0) {
16016  $space = preg_split('/[\s]+/', trim($cssbspace));
16017  $border_spacing = array(); // value to be returned
16018  switch (count($space)) {
16019  case 2: {
16020  $border_spacing['H'] = $space[0];
16021  $border_spacing['V'] = $space[1];
16022  break;
16023  }
16024  case 1: {
16025  $border_spacing['H'] = $space[0];
16026  $border_spacing['V'] = $space[0];
16027  break;
16028  }
16029  default: {
16030  return array('H' => 0, 'V' => 0);
16031  }
16032  }
16033  if ($width == 0) {
16034  $width = $this->w - $this->lMargin - $this->rMargin;
16035  }
16036  $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
16037  $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
16038  return $border_spacing;
16039  }
16040 
16049  protected function getCSSFontSpacing($spacing, $parent=0) {
16050  $val = 0; // value to be returned
16051  $spacing = trim($spacing);
16052  switch ($spacing) {
16053  case 'normal': {
16054  $val = 0;
16055  break;
16056  }
16057  case 'inherit': {
16058  if ($parent == 'normal') {
16059  $val = 0;
16060  } else {
16061  $val = $parent;
16062  }
16063  break;
16064  }
16065  default: {
16066  $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
16067  }
16068  }
16069  return $val;
16070  }
16071 
16080  protected function getCSSFontStretching($stretch, $parent=100) {
16081  $val = 100; // value to be returned
16082  $stretch = trim($stretch);
16083  switch ($stretch) {
16084  case 'ultra-condensed': {
16085  $val = 40;
16086  break;
16087  }
16088  case 'extra-condensed': {
16089  $val = 55;
16090  break;
16091  }
16092  case 'condensed': {
16093  $val = 70;
16094  break;
16095  }
16096  case 'semi-condensed': {
16097  $val = 85;
16098  break;
16099  }
16100  case 'normal': {
16101  $val = 100;
16102  break;
16103  }
16104  case 'semi-expanded': {
16105  $val = 115;
16106  break;
16107  }
16108  case 'expanded': {
16109  $val = 130;
16110  break;
16111  }
16112  case 'extra-expanded': {
16113  $val = 145;
16114  break;
16115  }
16116  case 'ultra-expanded': {
16117  $val = 160;
16118  break;
16119  }
16120  case 'wider': {
16121  $val = ($parent + 10);
16122  break;
16123  }
16124  case 'narrower': {
16125  $val = ($parent - 10);
16126  break;
16127  }
16128  case 'inherit': {
16129  if ($parent == 'normal') {
16130  $val = 100;
16131  } else {
16132  $val = $parent;
16133  }
16134  break;
16135  }
16136  default: {
16137  $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
16138  }
16139  }
16140  return $val;
16141  }
16142 
16152  public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') {
16153  $refsize = TCPDF_FONTS::getFontRefSize($refsize);
16154  $parent_size = TCPDF_FONTS::getFontRefSize($parent_size, $refsize);
16155  switch ($val) {
16156  case 'xx-small': {
16157  $size = ($refsize - 4);
16158  break;
16159  }
16160  case 'x-small': {
16161  $size = ($refsize - 3);
16162  break;
16163  }
16164  case 'small': {
16165  $size = ($refsize - 2);
16166  break;
16167  }
16168  case 'medium': {
16169  $size = $refsize;
16170  break;
16171  }
16172  case 'large': {
16173  $size = ($refsize + 2);
16174  break;
16175  }
16176  case 'x-large': {
16177  $size = ($refsize + 4);
16178  break;
16179  }
16180  case 'xx-large': {
16181  $size = ($refsize + 6);
16182  break;
16183  }
16184  case 'smaller': {
16185  $size = ($parent_size - 3);
16186  break;
16187  }
16188  case 'larger': {
16189  $size = ($parent_size + 3);
16190  break;
16191  }
16192  default: {
16193  $size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true);
16194  }
16195  }
16196  return $size;
16197  }
16198 
16206  protected function getHtmlDomArray($html) {
16207  // array of CSS styles ( selector => properties).
16208  $css = array();
16209  // get CSS array defined at previous call
16210  $matches = array();
16211  if (preg_match_all('/<cssarray>([^<]*)<\/cssarray>/isU', $html, $matches) > 0) {
16212  if (isset($matches[1][0])) {
16213  $css = array_merge($css, unserialize($this->unhtmlentities($matches[1][0])));
16214  }
16215  $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
16216  }
16217  // extract external CSS files
16218  $matches = array();
16219  if (preg_match_all('/<link([^>]*)>/isU', $html, $matches) > 0) {
16220  foreach ($matches[1] as $key => $link) {
16221  $type = array();
16222  if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
16223  $type = array();
16224  preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
16225  // get 'all' and 'print' media, other media types are discarded
16226  // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16227  if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16228  $type = array();
16229  if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
16230  // read CSS data file
16231  $cssdata = TCPDF_STATIC::fileGetContents(trim($type[1]));
16232  if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) {
16233  $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16234  }
16235  }
16236  }
16237  }
16238  }
16239  }
16240  // extract style tags
16241  $matches = array();
16242  if (preg_match_all('/<style([^>]*)>([^<]*)<\/style>/isU', $html, $matches) > 0) {
16243  foreach ($matches[1] as $key => $media) {
16244  $type = array();
16245  preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
16246  // get 'all' and 'print' media, other media types are discarded
16247  // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16248  if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16249  $cssdata = $matches[2][$key];
16250  $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16251  }
16252  }
16253  }
16254  // create a special tag to contain the CSS array (used for table content)
16255  $csstagarray = '<cssarray>'.htmlentities(serialize($css)).'</cssarray>';
16256  // remove head and style blocks
16257  $html = preg_replace('/<head([^>]*)>(.*?)<\/head>/siU', '', $html);
16258  $html = preg_replace('/<style([^>]*)>([^<]*)<\/style>/isU', '', $html);
16259  // define block tags
16260  $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
16261  // define self-closing tags
16262  $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
16263  // remove all unsupported tags (the line below lists all supported tags)
16264  $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>');
16265  //replace some blank characters
16266  $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
16267  $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);
16268  $html = preg_replace('@(\r\n|\r)@', "\n", $html);
16269  $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
16270  $html = strtr($html, $repTable);
16271  $offset = 0;
16272  while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
16273  $html_a = substr($html, 0, $offset);
16274  $html_b = substr($html, $offset, ($pos - $offset + 6));
16275  while (preg_match("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
16276  // preserve newlines on <pre> tag
16277  $html_b = preg_replace("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
16278  }
16279  while (preg_match("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) {
16280  // preserve spaces on <pre> tag
16281  $html_b = preg_replace("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2&nbsp;\\3</pre>", $html_b);
16282  }
16283  $html = $html_a.$html_b.substr($html, $pos + 6);
16284  $offset = strlen($html_a.$html_b);
16285  }
16286  $offset = 0;
16287  while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
16288  $html_a = substr($html, 0, $offset);
16289  $html_b = substr($html, $offset, ($pos - $offset + 11));
16290  while (preg_match("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
16291  // preserve newlines on <textarea> tag
16292  $html_b = preg_replace("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
16293  $html_b = preg_replace("'<textarea([^>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
16294  }
16295  $html = $html_a.$html_b.substr($html, $pos + 11);
16296  $offset = strlen($html_a.$html_b);
16297  }
16298  $html = preg_replace('/([\s]*)<option/si', '<option', $html);
16299  $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
16300  $offset = 0;
16301  while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
16302  $html_a = substr($html, 0, $offset);
16303  $html_b = substr($html, $offset, ($pos - $offset + 9));
16304  while (preg_match("'<option([^>]*)>(.*?)</option>'si", $html_b)) {
16305  $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
16306  $html_b = preg_replace("'<option([^>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
16307  }
16308  $html = $html_a.$html_b.substr($html, $pos + 9);
16309  $offset = strlen($html_a.$html_b);
16310  }
16311  if (preg_match("'</select'si", $html)) {
16312  $html = preg_replace("'<select([^>]*)>'si", "<select\\1 opt=\"", $html);
16313  $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
16314  }
16315  $html = str_replace("\n", ' ', $html);
16316  // restore textarea newlines
16317  $html = str_replace('<TBR>', "\n", $html);
16318  // remove extra spaces from code
16319  $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
16320  $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html);
16321  $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
16322  $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html);
16323  $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);
16324  $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
16325  $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
16326  $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html);
16327  $html = preg_replace('/<img([^>]*)>[\s]+([^<])/xi', '<img\\1>&nbsp;\\2', $html);
16328  $html = preg_replace('/<img([^>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
16329  $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
16330  $html = preg_replace('/<textarea([^>]*)>([^<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
16331  $html = preg_replace('/<li([^>]*)><\/li>/', '<li\\1>&nbsp;</li>', $html);
16332  $html = preg_replace('/<li([^>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1">&nbsp;</font><img', $html);
16333  $html = preg_replace('/<([^>\/]*)>[\s]/', '<\\1>&nbsp;', $html); // preserve some spaces
16334  $html = preg_replace('/[\s]<\/([^>]*)>/', '&nbsp;</\\1>', $html); // preserve some spaces
16335  $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment
16336  $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment
16337  $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space
16338  // trim string
16339  $html = $this->stringTrim($html);
16340  // fix br tag after li
16341  $html = preg_replace('/<li><br([^>]*)>/', '<li> <br\\1>', $html);
16342  // fix first image tag alignment
16343  $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
16344  // pattern for generic tag
16345  $tagpattern = '/(<[^>]+>)/';
16346  // explodes the string
16347  $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
16348  // count elements
16349  $maxel = count($a);
16350  $elkey = 0;
16351  $key = 0;
16352  // create an array of elements
16353  $dom = array();
16354  $dom[$key] = array();
16355  // set inheritable properties fot the first void element
16356  // 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
16357  $dom[$key]['tag'] = false;
16358  $dom[$key]['block'] = false;
16359  $dom[$key]['value'] = '';
16360  $dom[$key]['parent'] = 0;
16361  $dom[$key]['hide'] = false;
16362  $dom[$key]['fontname'] = $this->FontFamily;
16363  $dom[$key]['fontstyle'] = $this->FontStyle;
16364  $dom[$key]['fontsize'] = $this->FontSizePt;
16365  $dom[$key]['font-stretch'] = $this->font_stretching;
16366  $dom[$key]['letter-spacing'] = $this->font_spacing;
16367  $dom[$key]['stroke'] = $this->textstrokewidth;
16368  $dom[$key]['fill'] = (($this->textrendermode % 2) == 0);
16369  $dom[$key]['clip'] = ($this->textrendermode > 3);
16370  $dom[$key]['line-height'] = $this->cell_height_ratio;
16371  $dom[$key]['bgcolor'] = false;
16372  $dom[$key]['fgcolor'] = $this->fgcolor; // color
16373  $dom[$key]['strokecolor'] = $this->strokecolor;
16374  $dom[$key]['align'] = '';
16375  $dom[$key]['listtype'] = '';
16376  $dom[$key]['text-indent'] = 0;
16377  $dom[$key]['text-transform'] = '';
16378  $dom[$key]['border'] = array();
16379  $dom[$key]['dir'] = $this->rtl?'rtl':'ltr';
16380  $thead = false; // true when we are inside the THEAD tag
16381  ++$key;
16382  $level = array();
16383  array_push($level, 0); // root
16384  while ($elkey < $maxel) {
16385  $dom[$key] = array();
16386  $element = $a[$elkey];
16387  $dom[$key]['elkey'] = $elkey;
16388  if (preg_match($tagpattern, $element)) {
16389  // html tag
16390  $element = substr($element, 1, -1);
16391  // get tag name
16392  preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
16393  $tagname = strtolower($tag[1]);
16394  // check if we are inside a table header
16395  if ($tagname == 'thead') {
16396  if ($element{0} == '/') {
16397  $thead = false;
16398  } else {
16399  $thead = true;
16400  }
16401  ++$elkey;
16402  continue;
16403  }
16404  $dom[$key]['tag'] = true;
16405  $dom[$key]['value'] = $tagname;
16406  if (in_array($dom[$key]['value'], $blocktags)) {
16407  $dom[$key]['block'] = true;
16408  } else {
16409  $dom[$key]['block'] = false;
16410  }
16411  if ($element{0} == '/') {
16412  // *** closing html tag
16413  $dom[$key]['opening'] = false;
16414  $dom[$key]['parent'] = end($level);
16415  array_pop($level);
16416  $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
16417  $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
16418  $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
16419  $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
16420  $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
16421  $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
16422  $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
16423  $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
16424  $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
16425  $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
16426  $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
16427  $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
16428  $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
16429  $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
16430  $dom[$key]['text-transform'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['text-transform'];
16431  $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
16432  if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
16433  $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
16434  }
16435  // set the number of columns in table tag
16436  if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
16437  $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
16438  }
16439  if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
16440  $dom[($dom[$key]['parent'])]['content'] = $csstagarray;
16441  for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
16442  $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
16443  }
16444  $key = $i;
16445  // mark nested tables
16446  $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
16447  // remove thead sections from nested tables
16448  $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
16449  $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
16450  }
16451  // store header rows on a new table
16452  if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
16453  if (TCPDF_STATIC::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
16454  $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
16455  }
16456  for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
16457  $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
16458  }
16459  if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
16460  $dom[($dom[$key]['parent'])]['attribute'] = array();
16461  }
16462  // header elements must be always contained in a single page
16463  $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
16464  }
16465  if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC::empty_string($dom[($dom[$key]['parent'])]['thead']))) {
16466  // remove the nobr attributes from the table header
16467  $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
16468  $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
16469  }
16470  } else {
16471  // *** opening or self-closing html tag
16472  $dom[$key]['opening'] = true;
16473  $dom[$key]['parent'] = end($level);
16474  if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
16475  // self-closing tag
16476  $dom[$key]['self'] = true;
16477  } else {
16478  // opening tag
16479  array_push($level, $key);
16480  $dom[$key]['self'] = false;
16481  }
16482  // copy some values from parent
16483  $parentkey = 0;
16484  if ($key > 0) {
16485  $parentkey = $dom[$key]['parent'];
16486  $dom[$key]['hide'] = $dom[$parentkey]['hide'];
16487  $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
16488  $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
16489  $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
16490  $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
16491  $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
16492  $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
16493  $dom[$key]['fill'] = $dom[$parentkey]['fill'];
16494  $dom[$key]['clip'] = $dom[$parentkey]['clip'];
16495  $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16496  $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
16497  $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
16498  $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
16499  $dom[$key]['align'] = $dom[$parentkey]['align'];
16500  $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16501  $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16502  $dom[$key]['text-transform'] = $dom[$parentkey]['text-transform'];
16503  $dom[$key]['border'] = array();
16504  $dom[$key]['dir'] = $dom[$parentkey]['dir'];
16505  }
16506  // get attributes
16507  preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER);
16508  $dom[$key]['attribute'] = array(); // reset attribute array
16509  while (list($id, $name) = each($attr_array[1])) {
16510  $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
16511  }
16512  if (!empty($css)) {
16513  // merge CSS style to current style
16514  list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC::getCSSdataArray($dom, $key, $css);
16515  $dom[$key]['attribute']['style'] = TCPDF_STATIC::getTagStyleFromCSSarray($dom[$key]['cssdata']);
16516  }
16517  // split style attributes
16518  if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
16519  // get style attributes
16520  preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
16521  $dom[$key]['style'] = array(); // reset style attribute array
16522  while (list($id, $name) = each($style_array[1])) {
16523  // in case of duplicate attribute the last replace the previous
16524  $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
16525  }
16526  // --- get some style attributes ---
16527  // text direction
16528  if (isset($dom[$key]['style']['direction'])) {
16529  $dom[$key]['dir'] = $dom[$key]['style']['direction'];
16530  }
16531  // display
16532  if (isset($dom[$key]['style']['display'])) {
16533  $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
16534  }
16535  // font family
16536  if (isset($dom[$key]['style']['font-family'])) {
16537  $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
16538  }
16539  // list-style-type
16540  if (isset($dom[$key]['style']['list-style-type'])) {
16541  $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
16542  if ($dom[$key]['listtype'] == 'inherit') {
16543  $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16544  }
16545  }
16546  // text-indent
16547  if (isset($dom[$key]['style']['text-indent'])) {
16548  $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
16549  if ($dom[$key]['text-indent'] == 'inherit') {
16550  $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16551  }
16552  }
16553  // text-transform
16554  if (isset($dom[$key]['style']['text-transform'])) {
16555  $dom[$key]['text-transform'] = $dom[$key]['style']['text-transform'];
16556  }
16557  // font size
16558  if (isset($dom[$key]['style']['font-size'])) {
16559  $fsize = trim($dom[$key]['style']['font-size']);
16560  $dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt');
16561  }
16562  // font-stretch
16563  if (isset($dom[$key]['style']['font-stretch'])) {
16564  $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
16565  }
16566  // letter-spacing
16567  if (isset($dom[$key]['style']['letter-spacing'])) {
16568  $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
16569  }
16570  // line-height (internally is the cell height ratio)
16571  if (isset($dom[$key]['style']['line-height'])) {
16572  $lineheight = trim($dom[$key]['style']['line-height']);
16573  switch ($lineheight) {
16574  // A normal line height. This is default
16575  case 'normal': {
16576  $dom[$key]['line-height'] = $dom[0]['line-height'];
16577  break;
16578  }
16579  case 'inherit': {
16580  $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16581  }
16582  default: {
16583  if (is_numeric($lineheight)) {
16584  // convert to percentage of font height
16585  $lineheight = ($lineheight * 100).'%';
16586  }
16587  $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
16588  if (substr($lineheight, -1) !== '%') {
16589  $dom[$key]['line-height'] = (($dom[$key]['line-height'] - $this->cell_padding['T'] - $this->cell_padding['B']) / $dom[$key]['fontsize']);
16590  }
16591  }
16592  }
16593  }
16594  // font style
16595  if (isset($dom[$key]['style']['font-weight'])) {
16596  if (strtolower($dom[$key]['style']['font-weight']{0}) == 'n') {
16597  if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
16598  $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
16599  }
16600  } elseif (strtolower($dom[$key]['style']['font-weight']{0}) == 'b') {
16601  $dom[$key]['fontstyle'] .= 'B';
16602  }
16603  }
16604  if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
16605  $dom[$key]['fontstyle'] .= 'I';
16606  }
16607  // font color
16608  if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['color']))) {
16609  $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors);
16610  } elseif ($dom[$key]['value'] == 'a') {
16611  $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16612  }
16613  // background color
16614  if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['background-color']))) {
16615  $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors);
16616  }
16617  // text-decoration
16618  if (isset($dom[$key]['style']['text-decoration'])) {
16619  $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
16620  foreach ($decors as $dec) {
16621  $dec = trim($dec);
16622  if (!TCPDF_STATIC::empty_string($dec)) {
16623  if ($dec{0} == 'u') {
16624  // underline
16625  $dom[$key]['fontstyle'] .= 'U';
16626  } elseif ($dec{0} == 'l') {
16627  // line-through
16628  $dom[$key]['fontstyle'] .= 'D';
16629  } elseif ($dec{0} == 'o') {
16630  // overline
16631  $dom[$key]['fontstyle'] .= 'O';
16632  }
16633  }
16634  }
16635  } elseif ($dom[$key]['value'] == 'a') {
16636  $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16637  }
16638  // check for width attribute
16639  if (isset($dom[$key]['style']['width'])) {
16640  $dom[$key]['width'] = $dom[$key]['style']['width'];
16641  }
16642  // check for height attribute
16643  if (isset($dom[$key]['style']['height'])) {
16644  $dom[$key]['height'] = $dom[$key]['style']['height'];
16645  }
16646  // check for text alignment
16647  if (isset($dom[$key]['style']['text-align'])) {
16648  $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
16649  }
16650  // check for CSS border properties
16651  if (isset($dom[$key]['style']['border'])) {
16652  $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
16653  if (!empty($borderstyle)) {
16654  $dom[$key]['border']['LTRB'] = $borderstyle;
16655  }
16656  }
16657  if (isset($dom[$key]['style']['border-color'])) {
16658  $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
16659  if (isset($brd_colors[3])) {
16660  $dom[$key]['border']['L']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[3], $this->spot_colors);
16661  }
16662  if (isset($brd_colors[1])) {
16663  $dom[$key]['border']['R']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[1], $this->spot_colors);
16664  }
16665  if (isset($brd_colors[0])) {
16666  $dom[$key]['border']['T']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[0], $this->spot_colors);
16667  }
16668  if (isset($brd_colors[2])) {
16669  $dom[$key]['border']['B']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[2], $this->spot_colors);
16670  }
16671  }
16672  if (isset($dom[$key]['style']['border-width'])) {
16673  $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
16674  if (isset($brd_widths[3])) {
16675  $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
16676  }
16677  if (isset($brd_widths[1])) {
16678  $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
16679  }
16680  if (isset($brd_widths[0])) {
16681  $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
16682  }
16683  if (isset($brd_widths[2])) {
16684  $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
16685  }
16686  }
16687  if (isset($dom[$key]['style']['border-style'])) {
16688  $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
16689  if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) {
16690  $dom[$key]['border']['L']['cap'] = 'square';
16691  $dom[$key]['border']['L']['join'] = 'miter';
16692  $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
16693  if ($dom[$key]['border']['L']['dash'] < 0) {
16694  $dom[$key]['border']['L'] = array();
16695  }
16696  }
16697  if (isset($brd_styles[1])) {
16698  $dom[$key]['border']['R']['cap'] = 'square';
16699  $dom[$key]['border']['R']['join'] = 'miter';
16700  $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
16701  if ($dom[$key]['border']['R']['dash'] < 0) {
16702  $dom[$key]['border']['R'] = array();
16703  }
16704  }
16705  if (isset($brd_styles[0])) {
16706  $dom[$key]['border']['T']['cap'] = 'square';
16707  $dom[$key]['border']['T']['join'] = 'miter';
16708  $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
16709  if ($dom[$key]['border']['T']['dash'] < 0) {
16710  $dom[$key]['border']['T'] = array();
16711  }
16712  }
16713  if (isset($brd_styles[2])) {
16714  $dom[$key]['border']['B']['cap'] = 'square';
16715  $dom[$key]['border']['B']['join'] = 'miter';
16716  $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
16717  if ($dom[$key]['border']['B']['dash'] < 0) {
16718  $dom[$key]['border']['B'] = array();
16719  }
16720  }
16721  }
16722  $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
16723  foreach ($cellside as $bsk => $bsv) {
16724  if (isset($dom[$key]['style']['border-'.$bsv])) {
16725  $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
16726  if (!empty($borderstyle)) {
16727  $dom[$key]['border'][$bsk] = $borderstyle;
16728  }
16729  }
16730  if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
16731  $dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors);
16732  }
16733  if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
16734  $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
16735  }
16736  if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
16737  $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
16738  if ($dom[$key]['border'][$bsk]['dash'] < 0) {
16739  $dom[$key]['border'][$bsk] = array();
16740  }
16741  }
16742  }
16743  // check for CSS padding properties
16744  if (isset($dom[$key]['style']['padding'])) {
16745  $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
16746  } else {
16747  $dom[$key]['padding'] = $this->cell_padding;
16748  }
16749  foreach ($cellside as $psk => $psv) {
16750  if (isset($dom[$key]['style']['padding-'.$psv])) {
16751  $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
16752  }
16753  }
16754  // check for CSS margin properties
16755  if (isset($dom[$key]['style']['margin'])) {
16756  $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
16757  } else {
16758  $dom[$key]['margin'] = $this->cell_margin;
16759  }
16760  foreach ($cellside as $psk => $psv) {
16761  if (isset($dom[$key]['style']['margin-'.$psv])) {
16762  $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
16763  }
16764  }
16765  // check for CSS border-spacing properties
16766  if (isset($dom[$key]['style']['border-spacing'])) {
16767  $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
16768  }
16769  // page-break-inside
16770  if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
16771  $dom[$key]['attribute']['nobr'] = 'true';
16772  }
16773  // page-break-before
16774  if (isset($dom[$key]['style']['page-break-before'])) {
16775  if ($dom[$key]['style']['page-break-before'] == 'always') {
16776  $dom[$key]['attribute']['pagebreak'] = 'true';
16777  } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
16778  $dom[$key]['attribute']['pagebreak'] = 'left';
16779  } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
16780  $dom[$key]['attribute']['pagebreak'] = 'right';
16781  }
16782  }
16783  // page-break-after
16784  if (isset($dom[$key]['style']['page-break-after'])) {
16785  if ($dom[$key]['style']['page-break-after'] == 'always') {
16786  $dom[$key]['attribute']['pagebreakafter'] = 'true';
16787  } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
16788  $dom[$key]['attribute']['pagebreakafter'] = 'left';
16789  } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
16790  $dom[$key]['attribute']['pagebreakafter'] = 'right';
16791  }
16792  }
16793  }
16794  if (isset($dom[$key]['attribute']['display'])) {
16795  $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
16796  }
16797  if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
16798  $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
16799  if (!empty($borderstyle)) {
16800  $dom[$key]['border']['LTRB'] = $borderstyle;
16801  }
16802  }
16803  // check for font tag
16804  if ($dom[$key]['value'] == 'font') {
16805  // font family
16806  if (isset($dom[$key]['attribute']['face'])) {
16807  $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
16808  }
16809  // font size
16810  if (isset($dom[$key]['attribute']['size'])) {
16811  if ($key > 0) {
16812  if ($dom[$key]['attribute']['size']{0} == '+') {
16813  $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
16814  } elseif ($dom[$key]['attribute']['size']{0} == '-') {
16815  $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
16816  } else {
16817  $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16818  }
16819  } else {
16820  $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16821  }
16822  }
16823  }
16824  // force natural alignment for lists
16825  if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
16826  AND (!isset($dom[$key]['align']) OR TCPDF_STATIC::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
16827  if ($this->rtl) {
16828  $dom[$key]['align'] = 'R';
16829  } else {
16830  $dom[$key]['align'] = 'L';
16831  }
16832  }
16833  if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
16834  if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16835  $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
16836  }
16837  }
16838  if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
16839  $dom[$key]['fontstyle'] .= 'B';
16840  }
16841  if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
16842  $dom[$key]['fontstyle'] .= 'I';
16843  }
16844  if ($dom[$key]['value'] == 'u') {
16845  $dom[$key]['fontstyle'] .= 'U';
16846  }
16847  if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
16848  $dom[$key]['fontstyle'] .= 'D';
16849  }
16850  if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
16851  $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16852  }
16853  if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
16854  $dom[$key]['fontname'] = $this->default_monospaced_font;
16855  }
16856  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)) {
16857  // headings h1, h2, h3, h4, h5, h6
16858  if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16859  $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
16860  $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
16861  }
16862  if (!isset($dom[$key]['style']['font-weight'])) {
16863  $dom[$key]['fontstyle'] .= 'B';
16864  }
16865  }
16866  if (($dom[$key]['value'] == 'table')) {
16867  $dom[$key]['rows'] = 0; // number of rows
16868  $dom[$key]['trids'] = array(); // IDs of TR elements
16869  $dom[$key]['thead'] = ''; // table header rows
16870  }
16871  if (($dom[$key]['value'] == 'tr')) {
16872  $dom[$key]['cols'] = 0;
16873  if ($thead) {
16874  $dom[$key]['thead'] = true;
16875  // rows on thead block are printed as a separate table
16876  } else {
16877  $dom[$key]['thead'] = false;
16878  // store the number of rows on table element
16879  ++$dom[($dom[$key]['parent'])]['rows'];
16880  // store the TR elements IDs on table element
16881  array_push($dom[($dom[$key]['parent'])]['trids'], $key);
16882  }
16883  }
16884  if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
16885  if (isset($dom[$key]['attribute']['colspan'])) {
16886  $colspan = intval($dom[$key]['attribute']['colspan']);
16887  } else {
16888  $colspan = 1;
16889  }
16890  $dom[$key]['attribute']['colspan'] = $colspan;
16891  $dom[($dom[$key]['parent'])]['cols'] += $colspan;
16892  }
16893  // text direction
16894  if (isset($dom[$key]['attribute']['dir'])) {
16895  $dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
16896  }
16897  // set foreground color attribute
16898  if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['color']))) {
16899  $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors);
16900  } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
16901  $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16902  }
16903  // set background color attribute
16904  if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['bgcolor']))) {
16905  $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors);
16906  }
16907  // set stroke color attribute
16908  if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['strokecolor']))) {
16909  $dom[$key]['strokecolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors);
16910  }
16911  // check for width attribute
16912  if (isset($dom[$key]['attribute']['width'])) {
16913  $dom[$key]['width'] = $dom[$key]['attribute']['width'];
16914  }
16915  // check for height attribute
16916  if (isset($dom[$key]['attribute']['height'])) {
16917  $dom[$key]['height'] = $dom[$key]['attribute']['height'];
16918  }
16919  // check for text alignment
16920  if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
16921  $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
16922  }
16923  // check for text rendering mode (the following attributes do not exist in HTML)
16924  if (isset($dom[$key]['attribute']['stroke'])) {
16925  // font stroke width
16926  $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
16927  }
16928  if (isset($dom[$key]['attribute']['fill'])) {
16929  // font fill
16930  if ($dom[$key]['attribute']['fill'] == 'true') {
16931  $dom[$key]['fill'] = true;
16932  } else {
16933  $dom[$key]['fill'] = false;
16934  }
16935  }
16936  if (isset($dom[$key]['attribute']['clip'])) {
16937  // clipping mode
16938  if ($dom[$key]['attribute']['clip'] == 'true') {
16939  $dom[$key]['clip'] = true;
16940  } else {
16941  $dom[$key]['clip'] = false;
16942  }
16943  }
16944  } // end opening tag
16945  } else {
16946  // text
16947  $dom[$key]['tag'] = false;
16948  $dom[$key]['block'] = false;
16949  $dom[$key]['parent'] = end($level);
16950  $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
16951  if (!empty($dom[$dom[$key]['parent']]['text-transform'])) {
16952  // text-transform for unicode requires mb_convert_case (Multibyte String Functions)
16953  if (function_exists('mb_convert_case')) {
16954  $ttm = array('capitalize' => MB_CASE_TITLE, 'uppercase' => MB_CASE_UPPER, 'lowercase' => MB_CASE_LOWER);
16955  if (isset($ttm[$dom[$dom[$key]['parent']]['text-transform']])) {
16956  $element = mb_convert_case($element, $ttm[$dom[$dom[$key]['parent']]['text-transform']], $this->encoding);
16957  }
16958  } elseif (!$this->isunicode) {
16959  switch ($dom[$dom[$key]['parent']]['text-transform']) {
16960  case 'capitalize': {
16961  $element = ucwords(strtolower($element));
16962  break;
16963  }
16964  case 'uppercase': {
16965  $element = strtoupper($element);
16966  break;
16967  }
16968  case 'lowercase': {
16969  $element = strtolower($element);
16970  break;
16971  }
16972  }
16973  }
16974  }
16975  $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
16976  }
16977  ++$elkey;
16978  ++$key;
16979  }
16980  return $dom;
16981  }
16982 
16990  protected function getSpaceString() {
16991  $spacestr = chr(32);
16992  if ($this->isUnicodeFont()) {
16993  $spacestr = chr(0).chr(32);
16994  }
16995  return $spacestr;
16996  }
16997 
17006  public function serializeTCPDFtagParameters($pararray) {
17008  }
17009 
17032  public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
17033  return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
17034  }
17035 
17049  public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
17050  $gvars = $this->getGraphicVars();
17051  // store current values
17052  $prev_cell_margin = $this->cell_margin;
17053  $prev_cell_padding = $this->cell_padding;
17054  $prevPage = $this->page;
17055  $prevlMargin = $this->lMargin;
17056  $prevrMargin = $this->rMargin;
17057  $curfontname = $this->FontFamily;
17058  $curfontstyle = $this->FontStyle;
17059  $curfontsize = $this->FontSizePt;
17060  $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
17061  $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
17062  $curfontstretcing = $this->font_stretching;
17063  $curfonttracking = $this->font_spacing;
17064  $this->newline = true;
17065  $newline = true;
17066  $startlinepage = $this->page;
17067  $minstartliney = $this->y;
17068  $maxbottomliney = 0;
17069  $startlinex = $this->x;
17070  $startliney = $this->y;
17071  $yshift = 0;
17072  $loop = 0;
17073  $curpos = 0;
17074  $this_method_vars = array();
17075  $undo = false;
17076  $fontaligned = false;
17077  $reverse_dir = false; // true when the text direction is reversed
17078  $this->premode = false;
17079  if ($this->inxobj) {
17080  // we are inside an XObject template
17081  $pask = count($this->xobjects[$this->xobjid]['annotations']);
17082  } elseif (isset($this->PageAnnots[$this->page])) {
17083  $pask = count($this->PageAnnots[$this->page]);
17084  } else {
17085  $pask = 0;
17086  }
17087  if ($this->inxobj) {
17088  // we are inside an XObject template
17089  $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17090  } elseif (!$this->InFooter) {
17091  if (isset($this->footerlen[$this->page])) {
17092  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17093  } else {
17094  $this->footerpos[$this->page] = $this->pagelen[$this->page];
17095  }
17096  $startlinepos = $this->footerpos[$this->page];
17097  } else {
17098  // we are inside the footer
17099  $startlinepos = $this->pagelen[$this->page];
17100  }
17101  $lalign = $align;
17102  $plalign = $align;
17103  if ($this->rtl) {
17104  $w = $this->x - $this->lMargin;
17105  } else {
17106  $w = $this->w - $this->rMargin - $this->x;
17107  }
17108  $w -= ($this->cell_padding['L'] + $this->cell_padding['R']);
17109  if ($cell) {
17110  if ($this->rtl) {
17111  $this->x -= $this->cell_padding['R'];
17112  $this->lMargin += $this->cell_padding['R'];
17113  } else {
17114  $this->x += $this->cell_padding['L'];
17115  $this->rMargin += $this->cell_padding['L'];
17116  }
17117  }
17118  if ($this->customlistindent >= 0) {
17119  $this->listindent = $this->customlistindent;
17120  } else {
17121  $this->listindent = $this->GetStringWidth('000000');
17122  }
17123  $this->listindentlevel = 0;
17124  // save previous states
17125  $prev_cell_height_ratio = $this->cell_height_ratio;
17126  $prev_listnum = $this->listnum;
17127  $prev_listordered = $this->listordered;
17128  $prev_listcount = $this->listcount;
17129  $prev_lispacer = $this->lispacer;
17130  $this->listnum = 0;
17131  $this->listordered = array();
17132  $this->listcount = array();
17133  $this->lispacer = '';
17134  if ((TCPDF_STATIC::empty_string($this->lasth)) OR ($reseth)) {
17135  // reset row height
17136  $this->resetLastH();
17137  }
17138  $dom = $this->getHtmlDomArray($html);
17139  $maxel = count($dom);
17140  $key = 0;
17141  while ($key < $maxel) {
17142  if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND $dom[$key]['hide']) {
17143  // store the node key
17144  $hidden_node_key = $key;
17145  if ($dom[$key]['self']) {
17146  // skip just this self-closing tag
17147  ++$key;
17148  } else {
17149  // skip this and all children tags
17150  while (($key < $maxel) AND (!$dom[$key]['tag'] OR $dom[$key]['opening'] OR ($dom[$key]['parent'] != $hidden_node_key))) {
17151  // skip hidden objects
17152  ++$key;
17153  }
17154  ++$key;
17155  }
17156  }
17157  if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
17158  // check for pagebreak
17159  if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
17160  // add a page (or trig AcceptPageBreak() for multicolumn mode)
17161  $this->checkPageBreak($this->PageBreakTrigger + 1);
17162  $this->htmlvspace = ($this->PageBreakTrigger + 1);
17163  }
17164  if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
17165  OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
17166  // add a page (or trig AcceptPageBreak() for multicolumn mode)
17167  $this->checkPageBreak($this->PageBreakTrigger + 1);
17168  $this->htmlvspace = ($this->PageBreakTrigger + 1);
17169  }
17170  }
17171  if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
17172  if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
17173  $dom[$key]['attribute']['nobr'] = false;
17174  } else {
17175  // store current object
17176  $this->startTransaction();
17177  // save this method vars
17178  $this_method_vars['html'] = $html;
17179  $this_method_vars['ln'] = $ln;
17180  $this_method_vars['fill'] = $fill;
17181  $this_method_vars['reseth'] = $reseth;
17182  $this_method_vars['cell'] = $cell;
17183  $this_method_vars['align'] = $align;
17184  $this_method_vars['gvars'] = $gvars;
17185  $this_method_vars['prevPage'] = $prevPage;
17186  $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
17187  $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
17188  $this_method_vars['prevlMargin'] = $prevlMargin;
17189  $this_method_vars['prevrMargin'] = $prevrMargin;
17190  $this_method_vars['curfontname'] = $curfontname;
17191  $this_method_vars['curfontstyle'] = $curfontstyle;
17192  $this_method_vars['curfontsize'] = $curfontsize;
17193  $this_method_vars['curfontascent'] = $curfontascent;
17194  $this_method_vars['curfontdescent'] = $curfontdescent;
17195  $this_method_vars['curfontstretcing'] = $curfontstretcing;
17196  $this_method_vars['curfonttracking'] = $curfonttracking;
17197  $this_method_vars['minstartliney'] = $minstartliney;
17198  $this_method_vars['maxbottomliney'] = $maxbottomliney;
17199  $this_method_vars['yshift'] = $yshift;
17200  $this_method_vars['startlinepage'] = $startlinepage;
17201  $this_method_vars['startlinepos'] = $startlinepos;
17202  $this_method_vars['startlinex'] = $startlinex;
17203  $this_method_vars['startliney'] = $startliney;
17204  $this_method_vars['newline'] = $newline;
17205  $this_method_vars['loop'] = $loop;
17206  $this_method_vars['curpos'] = $curpos;
17207  $this_method_vars['pask'] = $pask;
17208  $this_method_vars['lalign'] = $lalign;
17209  $this_method_vars['plalign'] = $plalign;
17210  $this_method_vars['w'] = $w;
17211  $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
17212  $this_method_vars['prev_listnum'] = $prev_listnum;
17213  $this_method_vars['prev_listordered'] = $prev_listordered;
17214  $this_method_vars['prev_listcount'] = $prev_listcount;
17215  $this_method_vars['prev_lispacer'] = $prev_lispacer;
17216  $this_method_vars['fontaligned'] = $fontaligned;
17217  $this_method_vars['key'] = $key;
17218  $this_method_vars['dom'] = $dom;
17219  }
17220  }
17221  // print THEAD block
17222  if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
17223  if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC::empty_string($dom[$dom[$key]['parent']]['thead'])) {
17224  $this->inthead = true;
17225  // print table header (thead)
17226  $this->writeHTML($this->thead, false, false, false, false, '');
17227  // check if we are on a new page or on a new column
17228  if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
17229  // we are on a new page or on a new column and the total object height is less than the available vertical space.
17230  // restore previous object
17231  $this->rollbackTransaction(true);
17232  // restore previous values
17233  foreach ($this_method_vars as $vkey => $vval) {
17234  $$vkey = $vval;
17235  }
17236  // disable table header
17237  $tmp_thead = $this->thead;
17238  $this->thead = '';
17239  // add a page (or trig AcceptPageBreak() for multicolumn mode)
17240  $pre_y = $this->y;
17241  if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
17242  // fix for multicolumn mode
17243  $startliney = $this->y;
17244  }
17245  $this->start_transaction_page = $this->page;
17246  $this->start_transaction_y = $this->y;
17247  // restore table header
17248  $this->thead = $tmp_thead;
17249  // fix table border properties
17250  if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
17251  $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
17252  } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
17253  $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
17254  } else {
17255  $tmp_cellspacing = 0;
17256  }
17257  $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
17258  $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
17259  $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
17260  $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']);
17261  $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
17262  $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
17263  // print table header (thead)
17264  $this->writeHTML($this->thead, false, false, false, false, '');
17265  }
17266  }
17267  // move $key index forward to skip THEAD block
17268  while ( ($key < $maxel) AND (!(
17269  ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
17270  OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
17271  ++$key;
17272  }
17273  }
17274  if ($dom[$key]['tag'] OR ($key == 0)) {
17275  if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
17276  $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
17277  }
17278  // vertically align image in line
17279  if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
17280  // get image height
17281  $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], $this->lasth, 'px');
17282  $autolinebreak = false;
17283  if (isset($dom[$key]['width']) AND ($dom[$key]['width'] > 0)) {
17284  $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], 1, 'px', false);
17285  if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R']))
17286  AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L'])))
17287  OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) {
17288  // add automatic line break
17289  $autolinebreak = true;
17290  $this->Ln('', $cell);
17291  if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
17292  // go back to evaluate this line break
17293  --$key;
17294  }
17295  }
17296  }
17297  if (!$autolinebreak) {
17298  if ($this->inPageBody()) {
17299  $pre_y = $this->y;
17300  // check for page break
17301  if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
17302  // fix for multicolumn mode
17303  $startliney = $this->y;
17304  }
17305  }
17306  if ($this->page > $startlinepage) {
17307  // fix line splitted over two pages
17308  if (isset($this->footerlen[$startlinepage])) {
17309  $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17310  }
17311  // line to be moved one page forward
17312  $pagebuff = $this->getPageBuffer($startlinepage);
17313  $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17314  $tstart = substr($pagebuff, 0, $startlinepos);
17315  $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17316  // remove line from previous page
17317  $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17318  $pagebuff = $this->getPageBuffer($this->page);
17319  $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17320  $tend = substr($pagebuff, $this->cntmrk[$this->page]);
17321  // add line start to current page
17322  $yshift = ($minstartliney - $this->y);
17323  if ($fontaligned) {
17324  $yshift += ($curfontsize / $this->k);
17325  }
17326  $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17327  $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17328  // shift the annotations and links
17329  if (isset($this->PageAnnots[$this->page])) {
17330  $next_pask = count($this->PageAnnots[$this->page]);
17331  } else {
17332  $next_pask = 0;
17333  }
17334  if (isset($this->PageAnnots[$startlinepage])) {
17335  foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17336  if ($pak >= $pask) {
17337  $this->PageAnnots[$this->page][] = $pac;
17338  unset($this->PageAnnots[$startlinepage][$pak]);
17339  $npak = count($this->PageAnnots[$this->page]) - 1;
17340  $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17341  }
17342  }
17343  }
17344  $pask = $next_pask;
17345  $startlinepos = $this->cntmrk[$this->page];
17346  $startlinepage = $this->page;
17347  $startliney = $this->y;
17348  $this->newline = false;
17349  }
17350  $this->y += (($this->getCellHeight($curfontsize / $this->k) + $curfontascent - $curfontdescent) / 2) - $imgh;
17351  $minstartliney = min($this->y, $minstartliney);
17352  $maxbottomliney = ($startliney + $this->getCellHeight($this->FontSize));
17353  }
17354  } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
17355  // account for different font size
17356  $pfontname = $curfontname;
17357  $pfontstyle = $curfontstyle;
17358  $pfontsize = $curfontsize;
17359  $fontname = (isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname);
17360  $fontstyle = (isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle);
17361  $fontsize = (isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize);
17362  $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
17363  $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
17364  if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
17365  OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17366  OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
17367  if (($key < ($maxel - 1)) AND (
17368  ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
17369  OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17370  OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize) AND ($fontsize >= 0) AND ($curfontsize >= 0) AND ($fontsize != $curfontsize))
17371  )) {
17372  if ($this->page > $startlinepage) {
17373  // fix lines splitted over two pages
17374  if (isset($this->footerlen[$startlinepage])) {
17375  $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17376  }
17377  // line to be moved one page forward
17378  $pagebuff = $this->getPageBuffer($startlinepage);
17379  $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17380  $tstart = substr($pagebuff, 0, $startlinepos);
17381  $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17382  // remove line start from previous page
17383  $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17384  $pagebuff = $this->getPageBuffer($this->page);
17385  $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17386  $tend = substr($pagebuff, $this->cntmrk[$this->page]);
17387  // add line start to current page
17388  $yshift = ($minstartliney - $this->y);
17389  $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17390  $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17391  // shift the annotations and links
17392  if (isset($this->PageAnnots[$this->page])) {
17393  $next_pask = count($this->PageAnnots[$this->page]);
17394  } else {
17395  $next_pask = 0;
17396  }
17397  if (isset($this->PageAnnots[$startlinepage])) {
17398  foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17399  if ($pak >= $pask) {
17400  $this->PageAnnots[$this->page][] = $pac;
17401  unset($this->PageAnnots[$startlinepage][$pak]);
17402  $npak = count($this->PageAnnots[$this->page]) - 1;
17403  $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17404  }
17405  }
17406  }
17407  $pask = $next_pask;
17408  $startlinepos = $this->cntmrk[$this->page];
17409  $startlinepage = $this->page;
17410  $startliney = $this->y;
17411  }
17412  if (!isset($dom[$key]['line-height'])) {
17413  $dom[$key]['line-height'] = $this->cell_height_ratio;
17414  }
17415  if (!$dom[$key]['block']) {
17416  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']))) {
17417  $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
17418  }
17419  if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
17420  $current_line_align_data = array($key, $minstartliney, $maxbottomliney);
17421  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)))) {
17422  $minstartliney = min($this->y, $line_align_data[1]);
17423  $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $line_align_data[2]);
17424  } else {
17425  $minstartliney = min($this->y, $minstartliney);
17426  $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $maxbottomliney);
17427  }
17428  $line_align_data = $current_line_align_data;
17429  }
17430  }
17431  $this->cell_height_ratio = $dom[$key]['line-height'];
17432  $fontaligned = true;
17433  }
17434  $this->SetFont($fontname, $fontstyle, $fontsize);
17435  // reset row height
17436  $this->resetLastH();
17437  $curfontname = $fontname;
17438  $curfontstyle = $fontstyle;
17439  $curfontsize = $fontsize;
17440  $curfontascent = $fontascent;
17441  $curfontdescent = $fontdescent;
17442  }
17443  }
17444  // set text rendering mode
17445  $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
17446  $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
17447  $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
17448  $this->setTextRenderingMode($textstroke, $textfill, $textclip);
17449  if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
17450  $this->setFontStretching($dom[$key]['font-stretch']);
17451  }
17452  if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
17453  $this->setFontSpacing($dom[$key]['letter-spacing']);
17454  }
17455  if (($plalign == 'J') AND $dom[$key]['block']) {
17456  $plalign = '';
17457  }
17458  // get current position on page buffer
17459  $curpos = $this->pagelen[$startlinepage];
17460  if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
17461  $this->SetFillColorArray($dom[$key]['bgcolor']);
17462  $wfill = true;
17463  } else {
17464  $wfill = $fill | false;
17465  }
17466  if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
17467  $this->SetTextColorArray($dom[$key]['fgcolor']);
17468  }
17469  if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
17470  $this->SetDrawColorArray($dom[$key]['strokecolor']);
17471  }
17472  if (isset($dom[$key]['align'])) {
17473  $lalign = $dom[$key]['align'];
17474  }
17475  if (TCPDF_STATIC::empty_string($lalign)) {
17476  $lalign = $align;
17477  }
17478  }
17479  // align lines
17480  if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
17481  $newline = true;
17482  $fontaligned = false;
17483  // we are at the beginning of a new line
17484  if (isset($startlinex)) {
17485  $yshift = ($minstartliney - $startliney);
17486  if (($yshift > 0) OR ($this->page > $startlinepage)) {
17487  $yshift = 0;
17488  }
17489  $t_x = 0;
17490  // the last line must be shifted to be aligned as requested
17491  $linew = abs($this->endlinex - $startlinex);
17492  if ($this->inxobj) {
17493  // we are inside an XObject template
17494  $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
17495  if (isset($opentagpos)) {
17496  $midpos = $opentagpos;
17497  } else {
17498  $midpos = 0;
17499  }
17500  if ($midpos > 0) {
17501  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
17502  $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
17503  } else {
17504  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
17505  $pend = '';
17506  }
17507  } else {
17508  $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
17509  if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17510  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17511  $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
17512  } elseif (isset($opentagpos)) {
17513  $midpos = $opentagpos;
17514  } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17515  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17516  $midpos = $this->footerpos[$startlinepage];
17517  } else {
17518  $midpos = 0;
17519  }
17520  if ($midpos > 0) {
17521  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
17522  $pend = substr($this->getPageBuffer($startlinepage), $midpos);
17523  } else {
17524  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
17525  $pend = '';
17526  }
17527  }
17528  if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
17529  // calculate shifting amount
17530  $tw = $w;
17531  if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
17532  $tw += $this->cell_padding['R'];
17533  }
17534  if ($this->lMargin != $prevlMargin) {
17535  $tw += ($prevlMargin - $this->lMargin);
17536  }
17537  if ($this->rMargin != $prevrMargin) {
17538  $tw += ($prevrMargin - $this->rMargin);
17539  }
17540  $one_space_width = $this->GetStringWidth(chr(32));
17541  $no = 0; // number of spaces on a line contained on a single block
17542  if ($this->isRTLTextDir()) { // RTL
17543  // remove left space if exist
17544  $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
17545  if ($pos1 > 0) {
17546  $pos1 = intval($pos1);
17547  if ($this->isUnicodeFont()) {
17548  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
17549  $spacelen = 2;
17550  } else {
17551  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
17552  $spacelen = 1;
17553  }
17554  if ($pos1 == $pos2) {
17555  $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
17556  if (substr($pmid, $pos1, 4) == '[()]') {
17557  $linew -= $one_space_width;
17558  } elseif ($pos1 == strpos($pmid, '[(')) {
17559  $no = 1;
17560  }
17561  }
17562  }
17563  } else { // LTR
17564  // remove right space if exist
17565  $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
17566  if ($pos1 > 0) {
17567  $pos1 = intval($pos1);
17568  if ($this->isUnicodeFont()) {
17569  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
17570  $spacelen = 2;
17571  } else {
17572  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
17573  $spacelen = 1;
17574  }
17575  if ($pos1 == $pos2) {
17576  $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
17577  $linew -= $one_space_width;
17578  }
17579  }
17580  }
17581  $mdiff = ($tw - $linew);
17582  if ($plalign == 'C') {
17583  if ($this->rtl) {
17584  $t_x = -($mdiff / 2);
17585  } else {
17586  $t_x = ($mdiff / 2);
17587  }
17588  } elseif ($plalign == 'R') {
17589  // right alignment on LTR document
17590  $t_x = $mdiff;
17591  } elseif ($plalign == 'L') {
17592  // left alignment on RTL document
17593  $t_x = -$mdiff;
17594  } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
17595  // Justification
17596  if ($this->isRTLTextDir()) {
17597  // align text on the left
17598  $t_x = -$mdiff;
17599  }
17600  $ns = 0; // number of spaces
17601  $pmidtemp = $pmid;
17602  // escape special characters
17603  $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
17604  $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
17605  // search spaces
17606  if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
17607  $spacestr = $this->getSpaceString();
17608  $maxkk = count($lnstring[1]) - 1;
17609  for ($kk=0; $kk <= $maxkk; ++$kk) {
17610  // restore special characters
17611  $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
17612  $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
17613  // store number of spaces on the strings
17614  $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
17615  // count total spaces on line
17616  $ns += $lnstring[2][$kk];
17617  $lnstring[3][$kk] = $ns;
17618  }
17619  if ($ns == 0) {
17620  $ns = 1;
17621  }
17622  // calculate additional space to add to each existing space
17623  $spacewidth = ($mdiff / ($ns - $no)) * $this->k;
17624  if ($this->FontSize <= 0) {
17625  $this->FontSize = 1;
17626  }
17627  $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize;
17628  if ($this->font_spacing != 0) {
17629  // fixed spacing mode
17630  $osw = -1000 * $this->font_spacing / $this->FontSize;
17631  $spacewidthu += $osw;
17632  }
17633  $nsmax = $ns;
17634  $ns = 0;
17635  reset($lnstring);
17636  $offset = 0;
17637  $strcount = 0;
17638  $prev_epsposbeg = 0;
17639  $textpos = 0;
17640  if ($this->isRTLTextDir()) {
17641  $textpos = $this->wPt;
17642  }
17643  while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
17644  // check if we are inside a string section '[( ... )]'
17645  $stroffset = strpos($pmid, '[(', $offset);
17646  if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
17647  // set offset to the end of string section
17648  $offset = strpos($pmid, ')]', $stroffset);
17649  while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
17650  $offset = strpos($pmid, ')]', ($offset + 1));
17651  }
17652  if ($offset === false) {
17653  $this->Error('HTML Justification: malformed PDF code.');
17654  }
17655  continue;
17656  }
17657  if ($this->isRTLTextDir()) {
17658  $spacew = ($spacewidth * ($nsmax - $ns));
17659  } else {
17660  $spacew = ($spacewidth * $ns);
17661  }
17662  $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
17663  $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
17664  $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
17665  if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
17666  OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
17667  // shift EPS images
17668  $trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
17669  $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
17670  $pmid_b = substr($pmid, 0, $epsposbeg);
17671  $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
17672  $pmid_e = substr($pmid, $epsposend);
17673  $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
17674  $offset = $epsposend;
17675  continue;
17676 
17677  }
17678  $prev_epsposbeg = $epsposbeg;
17679  $currentxpos = 0;
17680  // shift blocks of code
17681  switch ($strpiece[2][0]) {
17682  case 'Td':
17683  case 'cm':
17684  case 'm':
17685  case 'l': {
17686  // get current X position
17687  preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
17688  if (!isset($xmatches[1])) {
17689  break;
17690  }
17691  $currentxpos = $xmatches[1];
17692  $textpos = $currentxpos;
17693  if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
17694  $ns = $lnstring[3][$strcount];
17695  if ($this->isRTLTextDir()) {
17696  $spacew = ($spacewidth * ($nsmax - $ns));
17697  }
17698  ++$strcount;
17699  }
17700  // justify block
17701  if (preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $pmatch) == 1) {
17702  $newpmid = sprintf('%F',(floatval($pmatch[1]) + $spacew)).' '.$pmatch[2].' x*#!#*x'.$pmatch[3].$pmatch[4];
17703  $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17704  unset($pmatch, $newpmid);
17705  }
17706  break;
17707  }
17708  case 're': {
17709  // justify block
17710  if (!TCPDF_STATIC::empty_string($this->lispacer)) {
17711  $this->lispacer = '';
17712  continue;
17713  }
17714  preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
17715  if (!isset($xmatches[1])) {
17716  break;
17717  }
17718  $currentxpos = $xmatches[1];
17719  $x_diff = 0;
17720  $w_diff = 0;
17721  if ($this->isRTLTextDir()) { // RTL
17722  if ($currentxpos < $textpos) {
17723  $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
17724  $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17725  } else {
17726  if ($strcount > 0) {
17727  $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
17728  $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17729  }
17730  }
17731  } else { // LTR
17732  if ($currentxpos > $textpos) {
17733  if ($strcount > 0) {
17734  $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
17735  }
17736  $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17737  } else {
17738  if ($strcount > 1) {
17739  $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
17740  }
17741  if ($strcount > 0) {
17742  $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17743  }
17744  }
17745  }
17746  if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $pmatch) == 1) {
17747  $newx = sprintf('%F',(floatval($pmatch[1]) + $x_diff));
17748  $neww = sprintf('%F',(floatval($pmatch[3]) + $w_diff));
17749  $newpmid = $newx.' '.$pmatch[2].' '.$neww.' '.$pmatch[4].' x*#!#*x'.$pmatch[5].$pmatch[6];
17750  $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17751  unset($pmatch, $newpmid, $newx, $neww);
17752  }
17753  break;
17754  }
17755  case 'c': {
17756  // get current X position
17757  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);
17758  if (!isset($xmatches[1])) {
17759  break;
17760  }
17761  $currentxpos = $xmatches[1];
17762  // justify block
17763  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) {
17764  $newx1 = sprintf('%F',(floatval($pmatch[1]) + $spacew));
17765  $newx2 = sprintf('%F',(floatval($pmatch[3]) + $spacew));
17766  $newx3 = sprintf('%F',(floatval($pmatch[5]) + $spacew));
17767  $newpmid = $newx1.' '.$pmatch[2].' '.$newx2.' '.$pmatch[4].' '.$newx3.' '.$pmatch[6].' x*#!#*x'.$pmatch[7].$pmatch[8];
17768  $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17769  unset($pmatch, $newpmid, $newx1, $newx2, $newx3);
17770  }
17771  break;
17772  }
17773  }
17774  // shift the annotations and links
17775  $cxpos = ($currentxpos / $this->k);
17776  $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps);
17777  if ($this->inxobj) {
17778  // we are inside an XObject template
17779  foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17780  if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17781  if ($cxpos > $lmpos) {
17782  $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k);
17783  $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17784  } else {
17785  $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17786  }
17787  break;
17788  }
17789  }
17790  } elseif (isset($this->PageAnnots[$this->page])) {
17791  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17792  if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17793  if ($cxpos > $lmpos) {
17794  $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
17795  $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17796  } else {
17797  $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17798  }
17799  break;
17800  }
17801  }
17802  }
17803  } // end of while
17804  // remove markers
17805  $pmid = str_replace('x*#!#*x', '', $pmid);
17806  if ($this->isUnicodeFont()) {
17807  // multibyte characters
17808  $spacew = $spacewidthu;
17809  if ($this->font_stretching != 100) {
17810  // word spacing is affected by stretching
17811  $spacew /= ($this->font_stretching / 100);
17812  }
17813  // escape special characters
17814  $pos = 0;
17815  $pmid = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmid);
17816  $pmid = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmid);
17817  if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmid, $pamatch) > 0) {
17818  foreach($pamatch[0] as $pk => $pmatch) {
17819  $replace = $pamatch[1][$pk];
17820  $replace = str_replace('#!#OP#!#', '(', $replace);
17821  $replace = str_replace('#!#CP#!#', ')', $replace);
17822  $newpmid = '[('.str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacew).' (', $replace).')]';
17823  $pos = strpos($pmid, $pmatch, $pos);
17824  if ($pos !== FALSE) {
17825  $pmid = substr_replace($pmid, $newpmid, $pos, strlen($pmatch));
17826  }
17827  ++$pos;
17828  }
17829  unset($pamatch);
17830  }
17831  if ($this->inxobj) {
17832  // we are inside an XObject template
17833  $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend;
17834  } else {
17835  $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
17836  }
17837  $endlinepos = strlen($pstart."\n".$pmid."\n");
17838  } else {
17839  // non-unicode (single-byte characters)
17840  if ($this->font_stretching != 100) {
17841  // word spacing (Tw) is affected by stretching
17842  $spacewidth /= ($this->font_stretching / 100);
17843  }
17844  $rs = sprintf('%F Tw', $spacewidth);
17845  $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
17846  if ($this->inxobj) {
17847  // we are inside an XObject template
17848  $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
17849  } else {
17850  $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
17851  }
17852  $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
17853  }
17854  }
17855  } // end of J
17856  } // end if $startlinex
17857  if (($t_x != 0) OR ($yshift < 0)) {
17858  // shift the line
17859  $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
17860  $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
17861  $endlinepos = strlen($pstart);
17862  if ($this->inxobj) {
17863  // we are inside an XObject template
17864  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
17865  foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17866  if ($pak >= $pask) {
17867  $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
17868  $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
17869  }
17870  }
17871  } else {
17872  $this->setPageBuffer($startlinepage, $pstart.$pend);
17873  // shift the annotations and links
17874  if (isset($this->PageAnnots[$this->page])) {
17875  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17876  if ($pak >= $pask) {
17877  $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
17878  $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
17879  }
17880  }
17881  }
17882  }
17883  $this->y -= $yshift;
17884  }
17885  }
17886  $pbrk = $this->checkPageBreak($this->lasth);
17887  $this->newline = false;
17888  $startlinex = $this->x;
17889  $startliney = $this->y;
17890  if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
17891  $startliney -= ((0.3 * $this->FontSizePt) / $this->k);
17892  } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
17893  $startliney -= (($this->FontSizePt / 0.7) / $this->k);
17894  } else {
17895  $minstartliney = $startliney;
17896  $maxbottomliney = ($this->y + $this->getCellHeight($fontsize / $this->k));
17897  }
17898  $startlinepage = $this->page;
17899  if (isset($endlinepos) AND (!$pbrk)) {
17900  $startlinepos = $endlinepos;
17901  } else {
17902  if ($this->inxobj) {
17903  // we are inside an XObject template
17904  $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17905  } elseif (!$this->InFooter) {
17906  if (isset($this->footerlen[$this->page])) {
17907  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17908  } else {
17909  $this->footerpos[$this->page] = $this->pagelen[$this->page];
17910  }
17911  $startlinepos = $this->footerpos[$this->page];
17912  } else {
17913  $startlinepos = $this->pagelen[$this->page];
17914  }
17915  }
17916  unset($endlinepos);
17917  $plalign = $lalign;
17918  if (isset($this->PageAnnots[$this->page])) {
17919  $pask = count($this->PageAnnots[$this->page]);
17920  } else {
17921  $pask = 0;
17922  }
17923  if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
17924  AND (isset($this->emptypagemrk[$this->page]))
17925  AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) {
17926  $this->SetFont($fontname, $fontstyle, $fontsize);
17927  if ($wfill) {
17928  $this->SetFillColorArray($this->bgcolor);
17929  }
17930  }
17931  } // end newline
17932  if (isset($opentagpos)) {
17933  unset($opentagpos);
17934  }
17935  if ($dom[$key]['tag']) {
17936  if ($dom[$key]['opening']) {
17937  // get text indentation (if any)
17938  if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
17939  $this->textindent = $dom[$key]['text-indent'];
17940  $this->newline = true;
17941  }
17942  // table
17943  if (($dom[$key]['value'] == 'table') AND isset($dom[$key]['cols']) AND ($dom[$key]['cols'] > 0)) {
17944  // available page width
17945  if ($this->rtl) {
17946  $wtmp = $this->x - $this->lMargin;
17947  } else {
17948  $wtmp = $this->w - $this->rMargin - $this->x;
17949  }
17950  // get cell spacing
17951  if (isset($dom[$key]['attribute']['cellspacing'])) {
17952  $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
17953  $cellspacing = array('H' => $clsp, 'V' => $clsp);
17954  } elseif (isset($dom[$key]['border-spacing'])) {
17955  $cellspacing = $dom[$key]['border-spacing'];
17956  } else {
17957  $cellspacing = array('H' => 0, 'V' => 0);
17958  }
17959  // table width
17960  if (isset($dom[$key]['width'])) {
17961  $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
17962  } else {
17963  $table_width = $wtmp;
17964  }
17965  $table_width -= (2 * $cellspacing['H']);
17966  if (!$this->inthead) {
17967  $this->y += $cellspacing['V'];
17968  }
17969  if ($this->rtl) {
17970  $cellspacingx = -$cellspacing['H'];
17971  } else {
17972  $cellspacingx = $cellspacing['H'];
17973  }
17974  // total table width without cellspaces
17975  $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
17976  // minimum column width
17977  $table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
17978  // array of custom column widths
17979  $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
17980  }
17981  // table row
17982  if ($dom[$key]['value'] == 'tr') {
17983  // reset column counter
17984  $colid = 0;
17985  }
17986  // table cell
17987  if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
17988  $trid = $dom[$key]['parent'];
17989  $table_el = $dom[$trid]['parent'];
17990  if (!isset($dom[$table_el]['cols'])) {
17991  $dom[$table_el]['cols'] = $dom[$trid]['cols'];
17992  }
17993  // store border info
17994  $tdborder = 0;
17995  if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
17996  $tdborder = $dom[$key]['border'];
17997  }
17998  $colspan = intval($dom[$key]['attribute']['colspan']);
17999  if ($colspan <= 0) {
18000  $colspan = 1;
18001  }
18002  $old_cell_padding = $this->cell_padding;
18003  if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
18004  $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
18005  $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
18006  } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
18007  $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
18008  } else {
18009  $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
18010  }
18011  $this->cell_padding = $current_cell_padding;
18012  if (isset($dom[$key]['height'])) {
18013  // minimum cell height
18014  $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
18015  } else {
18016  $cellh = 0;
18017  }
18018  if (isset($dom[$key]['content'])) {
18019  $cell_content = stripslashes($dom[$key]['content']);
18020  } else {
18021  $cell_content = '&nbsp;';
18022  }
18023  $tagtype = $dom[$key]['value'];
18024  $parentid = $key;
18025  while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
18026  // move $key index forward
18027  ++$key;
18028  }
18029  if (!isset($dom[$trid]['startpage'])) {
18030  $dom[$trid]['startpage'] = $this->page;
18031  } else {
18032  $this->setPage($dom[$trid]['startpage']);
18033  }
18034  if (!isset($dom[$trid]['startcolumn'])) {
18035  $dom[$trid]['startcolumn'] = $this->current_column;
18036  } elseif ($this->current_column != $dom[$trid]['startcolumn']) {
18037  $tmpx = $this->x;
18038  $this->selectColumn($dom[$trid]['startcolumn']);
18039  $this->x = $tmpx;
18040  }
18041  if (!isset($dom[$trid]['starty'])) {
18042  $dom[$trid]['starty'] = $this->y;
18043  } else {
18044  $this->y = $dom[$trid]['starty'];
18045  }
18046  if (!isset($dom[$trid]['startx'])) {
18047  $dom[$trid]['startx'] = $this->x;
18048  $this->x += $cellspacingx;
18049  } else {
18050  $this->x += ($cellspacingx / 2);
18051  }
18052  if (isset($dom[$parentid]['attribute']['rowspan'])) {
18053  $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
18054  } else {
18055  $rowspan = 1;
18056  }
18057  // skip row-spanned cells started on the previous rows
18058  if (isset($dom[$table_el]['rowspans'])) {
18059  $rsk = 0;
18060  $rskmax = count($dom[$table_el]['rowspans']);
18061  while ($rsk < $rskmax) {
18062  $trwsp = $dom[$table_el]['rowspans'][$rsk];
18063  $rsstartx = $trwsp['startx'];
18064  $rsendx = $trwsp['endx'];
18065  // account for margin changes
18066  if ($trwsp['startpage'] < $this->page) {
18067  if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
18068  $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
18069  $rsstartx -= $dl;
18070  $rsendx -= $dl;
18071  } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
18072  $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
18073  $rsstartx += $dl;
18074  $rsendx += $dl;
18075  }
18076  }
18077  if (($trwsp['rowspan'] > 0)
18078  AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps))
18079  AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps))
18080  AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) {
18081  // set the starting X position of the current cell
18082  $this->x = $rsendx + $cellspacingx;
18083  // increment column indicator
18084  $colid += $trwsp['colspan'];
18085  if (($trwsp['rowspan'] == 1)
18086  AND (isset($dom[$trid]['endy']))
18087  AND (isset($dom[$trid]['endpage']))
18088  AND (isset($dom[$trid]['endcolumn']))
18089  AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
18090  AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18091  // set ending Y position for row
18092  $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18093  $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
18094  }
18095  $rsk = 0;
18096  } else {
18097  ++$rsk;
18098  }
18099  }
18100  }
18101  if (isset($dom[$parentid]['width'])) {
18102  // user specified width
18103  $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
18104  $tmpcw = ($cellw / $colspan);
18105  for ($i = 0; $i < $colspan; ++$i) {
18106  $table_colwidths[($colid + $i)] = $tmpcw;
18107  }
18108  } else {
18109  // inherit column width
18110  $cellw = 0;
18111  for ($i = 0; $i < $colspan; ++$i) {
18112  $cellw += $table_colwidths[($colid + $i)];
18113  }
18114  }
18115  $cellw += (($colspan - 1) * $cellspacing['H']);
18116  // increment column indicator
18117  $colid += $colspan;
18118  // add rowspan information to table element
18119  if ($rowspan > 1) {
18120  $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));
18121  }
18122  $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
18123  if ($rowspan > 1) {
18124  $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
18125  }
18126  // push background colors
18127  if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
18128  $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
18129  }
18130  // store border info
18131  if (isset($tdborder) AND !empty($tdborder)) {
18132  $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
18133  }
18134  $prevLastH = $this->lasth;
18135  // store some info for multicolumn mode
18136  if ($this->rtl) {
18137  $this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
18138  } else {
18139  $this->colxshift['x'] = $this->x - $this->lMargin;
18140  }
18141  $this->colxshift['s'] = $cellspacing;
18142  $this->colxshift['p'] = $current_cell_padding;
18143  // ****** write the cell content ******
18144  $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
18145  // restore some values
18146  $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
18147  $this->lasth = $prevLastH;
18148  $this->cell_padding = $old_cell_padding;
18149  $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
18150  // update the end of row position
18151  if ($rowspan <= 1) {
18152  if (isset($dom[$trid]['endy'])) {
18153  if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) {
18154  $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
18155  } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) {
18156  $dom[$trid]['endy'] = $this->y;
18157  }
18158  } else {
18159  $dom[$trid]['endy'] = $this->y;
18160  }
18161  if (isset($dom[$trid]['endpage'])) {
18162  $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
18163  } else {
18164  $dom[$trid]['endpage'] = $this->page;
18165  }
18166  if (isset($dom[$trid]['endcolumn'])) {
18167  $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
18168  } else {
18169  $dom[$trid]['endcolumn'] = $this->current_column;
18170  }
18171  } else {
18172  // account for row-spanned cells
18173  $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
18174  $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
18175  $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
18176  $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column;
18177  }
18178  if (isset($dom[$table_el]['rowspans'])) {
18179  // update endy and endpage on rowspanned cells
18180  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
18181  if ($trwsp['rowspan'] > 0) {
18182  if (isset($dom[$trid]['endpage'])) {
18183  if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18184  $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18185  } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
18186  $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
18187  $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
18188  $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
18189  } else {
18190  $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
18191  }
18192  }
18193  }
18194  }
18195  }
18196  $this->x += ($cellspacingx / 2);
18197  } else {
18198  // opening tag (or self-closing tag)
18199  if (!isset($opentagpos)) {
18200  if ($this->inxobj) {
18201  // we are inside an XObject template
18202  $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
18203  } elseif (!$this->InFooter) {
18204  if (isset($this->footerlen[$this->page])) {
18205  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
18206  } else {
18207  $this->footerpos[$this->page] = $this->pagelen[$this->page];
18208  }
18209  $opentagpos = $this->footerpos[$this->page];
18210  }
18211  }
18212  $dom = $this->openHTMLTagHandler($dom, $key, $cell);
18213  }
18214  } else { // closing tag
18215  $prev_numpages = $this->numpages;
18216  $old_bordermrk = $this->bordermrk[$this->page];
18217  $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
18218  if ($this->bordermrk[$this->page] > $old_bordermrk) {
18219  $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk);
18220  }
18221  if ($prev_numpages > $this->numpages) {
18222  $startlinepage = $this->page;
18223  }
18224  }
18225  } elseif (strlen($dom[$key]['value']) > 0) {
18226  // print list-item
18227  if (!TCPDF_STATIC::empty_string($this->lispacer) AND ($this->lispacer != '^')) {
18228  $this->SetFont($pfontname, $pfontstyle, $pfontsize);
18229  $this->resetLastH();
18230  $minstartliney = $this->y;
18231  $maxbottomliney = ($startliney + $this->getCellHeight($this->FontSize));
18232  if (is_numeric($pfontsize) AND ($pfontsize > 0)) {
18233  $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
18234  }
18235  $this->SetFont($curfontname, $curfontstyle, $curfontsize);
18236  $this->resetLastH();
18237  if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
18238  $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
18239  $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
18240  $this->y += ($this->getCellHeight(($pfontsize - $curfontsize) / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
18241  $minstartliney = min($this->y, $minstartliney);
18242  $maxbottomliney = max(($this->y + $this->getCellHeight($pfontsize / $this->k)), $maxbottomliney);
18243  }
18244  }
18245  // text
18246  $this->htmlvspace = 0;
18247  if ((!$this->premode) AND $this->isRTLTextDir()) {
18248  // reverse spaces order
18249  $lsp = ''; // left spaces
18250  $rsp = ''; // right spaces
18251  if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18252  $lsp = $matches[1];
18253  }
18254  if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18255  $rsp = $matches[1];
18256  }
18257  $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
18258  }
18259  if ($newline) {
18260  if (!$this->premode) {
18261  $prelen = strlen($dom[$key]['value']);
18262  if ($this->isRTLTextDir()) {
18263  // right trim except non-breaking space
18264  $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
18265  } else {
18266  // left trim except non-breaking space
18267  $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
18268  }
18269  $postlen = strlen($dom[$key]['value']);
18270  if (($postlen == 0) AND ($prelen > 0)) {
18271  $dom[$key]['trimmed_space'] = true;
18272  }
18273  }
18274  $newline = false;
18275  $firstblock = true;
18276  } else {
18277  $firstblock = false;
18278  // replace empty multiple spaces string with a single space
18279  $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']);
18280  }
18281  $strrest = '';
18282  if ($this->rtl) {
18283  $this->x -= $this->textindent;
18284  } else {
18285  $this->x += $this->textindent;
18286  }
18287  if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
18288  $strlinelen = $this->GetStringWidth($dom[$key]['value']);
18289  if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
18290  // HTML <a> Link
18291  $hrefcolor = '';
18292  if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
18293  $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
18294  }
18295  $hrefstyle = -1;
18296  if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
18297  $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
18298  }
18299  $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
18300  } else {
18301  $wadj = 0; // space to leave for block continuity
18302  if ($this->rtl) {
18303  $cwa = ($this->x - $this->lMargin);
18304  } else {
18305  $cwa = ($this->w - $this->rMargin - $this->x);
18306  }
18307  if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) {
18308  // check the next text blocks for continuity
18309  $nkey = ($key + 1);
18310  $write_block = true;
18311  $same_textdir = true;
18312  $tmp_fontname = $this->FontFamily;
18313  $tmp_fontstyle = $this->FontStyle;
18314  $tmp_fontsize = $this->FontSizePt;
18315  while ($write_block AND isset($dom[$nkey])) {
18316  if ($dom[$nkey]['tag']) {
18317  if ($dom[$nkey]['block']) {
18318  // end of block
18319  $write_block = false;
18320  }
18321  $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
18322  $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
18323  $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
18324  $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
18325  } else {
18326  $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'+/', $this->re_space['m'], $dom[$nkey]['value']);
18327  if (isset($nextstr[0]) AND $same_textdir) {
18328  $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
18329  if (isset($nextstr[1])) {
18330  $write_block = false;
18331  }
18332  }
18333  }
18334  ++$nkey;
18335  }
18336  }
18337  if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) {
18338  $wadj = 0;
18339  $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $dom[$key]['value']);
18340  $numblks = count($nextstr);
18341  if ($numblks > 1) {
18342  // try to split on blank spaces
18343  $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)]));
18344  } else {
18345  // set the entire block on new line
18346  $wadj = $this->GetStringWidth($nextstr[0]);
18347  }
18348  }
18349  // check for reversed text direction
18350  if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) {
18351  // LTR text on RTL direction or RTL text on LTR direction
18352  $reverse_dir = true;
18353  $this->rtl = !$this->rtl;
18354  $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems
18355  if ($this->rtl) {
18356  $this->x += $revshift;
18357  } else {
18358  $this->x -= $revshift;
18359  }
18360  $xws = $this->x;
18361  }
18362  // ****** write only until the end of the line and get the rest ******
18363  $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
18364  // restore default direction
18365  if ($reverse_dir AND ($wadj == 0)) {
18366  $this->x = $xws;
18367  $this->rtl = !$this->rtl;
18368  $reverse_dir = false;
18369  }
18370  }
18371  }
18372  $this->textindent = 0;
18373  if (strlen($strrest) > 0) {
18374  // store the remaining string on the previous $key position
18375  $this->newline = true;
18376  if ($strrest == $dom[$key]['value']) {
18377  // used to avoid infinite loop
18378  ++$loop;
18379  } else {
18380  $loop = 0;
18381  }
18382  $dom[$key]['value'] = $strrest;
18383  if ($cell) {
18384  if ($this->rtl) {
18385  $this->x -= $this->cell_padding['R'];
18386  } else {
18387  $this->x += $this->cell_padding['L'];
18388  }
18389  }
18390  if ($loop < 3) {
18391  --$key;
18392  }
18393  } else {
18394  $loop = 0;
18395  // add the positive font spacing of the last character (if any)
18396  if ($this->font_spacing > 0) {
18397  if ($this->rtl) {
18398  $this->x -= $this->font_spacing;
18399  } else {
18400  $this->x += $this->font_spacing;
18401  }
18402  }
18403  }
18404  }
18405  ++$key;
18406  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')) {
18407  // check if we are on a new page or on a new column
18408  if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) {
18409  // we are on a new page or on a new column and the total object height is less than the available vertical space.
18410  // restore previous object
18411  $this->rollbackTransaction(true);
18412  // restore previous values
18413  foreach ($this_method_vars as $vkey => $vval) {
18414  $$vkey = $vval;
18415  }
18416  // add a page (or trig AcceptPageBreak() for multicolumn mode)
18417  $pre_y = $this->y;
18418  if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
18419  $startliney = $this->y;
18420  }
18421  $undo = true; // avoid infinite loop
18422  } else {
18423  $undo = false;
18424  }
18425  }
18426  } // end for each $key
18427  // align the last line
18428  if (isset($startlinex)) {
18429  $yshift = ($minstartliney - $startliney);
18430  if (($yshift > 0) OR ($this->page > $startlinepage)) {
18431  $yshift = 0;
18432  }
18433  $t_x = 0;
18434  // the last line must be shifted to be aligned as requested
18435  $linew = abs($this->endlinex - $startlinex);
18436  if ($this->inxobj) {
18437  // we are inside an XObject template
18438  $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
18439  if (isset($opentagpos)) {
18440  $midpos = $opentagpos;
18441  } else {
18442  $midpos = 0;
18443  }
18444  if ($midpos > 0) {
18445  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
18446  $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
18447  } else {
18448  $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
18449  $pend = '';
18450  }
18451  } else {
18452  $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
18453  if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18454  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18455  $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
18456  } elseif (isset($opentagpos)) {
18457  $midpos = $opentagpos;
18458  } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18459  $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18460  $midpos = $this->footerpos[$startlinepage];
18461  } else {
18462  $midpos = 0;
18463  }
18464  if ($midpos > 0) {
18465  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
18466  $pend = substr($this->getPageBuffer($startlinepage), $midpos);
18467  } else {
18468  $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
18469  $pend = '';
18470  }
18471  }
18472  if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
18473  // calculate shifting amount
18474  $tw = $w;
18475  if ($this->lMargin != $prevlMargin) {
18476  $tw += ($prevlMargin - $this->lMargin);
18477  }
18478  if ($this->rMargin != $prevrMargin) {
18479  $tw += ($prevrMargin - $this->rMargin);
18480  }
18481  $one_space_width = $this->GetStringWidth(chr(32));
18482  $no = 0; // number of spaces on a line contained on a single block
18483  if ($this->isRTLTextDir()) { // RTL
18484  // remove left space if exist
18485  $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
18486  if ($pos1 > 0) {
18487  $pos1 = intval($pos1);
18488  if ($this->isUnicodeFont()) {
18489  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
18490  $spacelen = 2;
18491  } else {
18492  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
18493  $spacelen = 1;
18494  }
18495  if ($pos1 == $pos2) {
18496  $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
18497  if (substr($pmid, $pos1, 4) == '[()]') {
18498  $linew -= $one_space_width;
18499  } elseif ($pos1 == strpos($pmid, '[(')) {
18500  $no = 1;
18501  }
18502  }
18503  }
18504  } else { // LTR
18505  // remove right space if exist
18506  $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
18507  if ($pos1 > 0) {
18508  $pos1 = intval($pos1);
18509  if ($this->isUnicodeFont()) {
18510  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
18511  $spacelen = 2;
18512  } else {
18513  $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
18514  $spacelen = 1;
18515  }
18516  if ($pos1 == $pos2) {
18517  $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
18518  $linew -= $one_space_width;
18519  }
18520  }
18521  }
18522  $mdiff = ($tw - $linew);
18523  if ($plalign == 'C') {
18524  if ($this->rtl) {
18525  $t_x = -($mdiff / 2);
18526  } else {
18527  $t_x = ($mdiff / 2);
18528  }
18529  } elseif ($plalign == 'R') {
18530  // right alignment on LTR document
18531  $t_x = $mdiff;
18532  } elseif ($plalign == 'L') {
18533  // left alignment on RTL document
18534  $t_x = -$mdiff;
18535  }
18536  } // end if startlinex
18537  if (($t_x != 0) OR ($yshift < 0)) {
18538  // shift the line
18539  $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
18540  $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18541  $endlinepos = strlen($pstart);
18542  if ($this->inxobj) {
18543  // we are inside an XObject template
18544  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
18545  foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
18546  if ($pak >= $pask) {
18547  $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
18548  $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
18549  }
18550  }
18551  } else {
18552  $this->setPageBuffer($startlinepage, $pstart.$pend);
18553  // shift the annotations and links
18554  if (isset($this->PageAnnots[$this->page])) {
18555  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
18556  if ($pak >= $pask) {
18557  $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
18558  $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
18559  }
18560  }
18561  }
18562  }
18563  $this->y -= $yshift;
18564  $yshift = 0;
18565  }
18566  }
18567  // restore previous values
18568  $this->setGraphicVars($gvars);
18569  if ($this->num_columns > 1) {
18570  $this->selectColumn();
18571  } elseif ($this->page > $prevPage) {
18572  $this->lMargin = $this->pagedim[$this->page]['olm'];
18573  $this->rMargin = $this->pagedim[$this->page]['orm'];
18574  }
18575  // restore previous list state
18576  $this->cell_height_ratio = $prev_cell_height_ratio;
18577  $this->listnum = $prev_listnum;
18578  $this->listordered = $prev_listordered;
18579  $this->listcount = $prev_listcount;
18580  $this->lispacer = $prev_lispacer;
18581  if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
18582  $this->Ln($this->lasth);
18583  if ($this->y < $maxbottomliney) {
18584  $this->y = $maxbottomliney;
18585  }
18586  }
18587  unset($dom);
18588  }
18589 
18598  protected function openHTMLTagHandler($dom, $key, $cell) {
18599  $tag = $dom[$key];
18600  $parent = $dom[($dom[$key]['parent'])];
18601  $firsttag = ($key == 1);
18602  // check for text direction attribute
18603  if (isset($tag['dir'])) {
18604  $this->setTempRTL($tag['dir']);
18605  } else {
18606  $this->tmprtl = false;
18607  }
18608  if ($tag['block']) {
18609  $hbz = 0; // distance from y to line bottom
18610  $hb = 0; // vertical space between block tags
18611  // calculate vertical space for block tags
18612  if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
18613  $cur_h = $this->tagvspaces[$tag['value']][0]['h'];
18614  } elseif (isset($tag['fontsize'])) {
18615  $cur_h = $this->getCellHeight($tag['fontsize'] / $this->k);
18616  } else {
18617  $cur_h = $this->getCellHeight($this->FontSize);
18618  }
18619  if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
18620  $n = $this->tagvspaces[$tag['value']][0]['n'];
18621  } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18622  $n = 0.6;
18623  } else {
18624  $n = 1;
18625  }
18626  if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br')))) {
18627  $hb = 0;
18628  } else {
18629  $hb = ($n * $cur_h);
18630  }
18631  if (($this->htmlvspace <= 0) AND ($n > 0)) {
18632  if (isset($parent['fontsize'])) {
18633  $hbz = $this->getCellHeight($tag['fontsize'] / $this->k);
18634  } else {
18635  $hbz = $this->getCellHeight($this->FontSize);
18636  }
18637  }
18638  if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) {
18639  // fix vertical space after table
18640  $hbz = 0;
18641  }
18642  }
18643  // Opening tag
18644  switch($tag['value']) {
18645  case 'table': {
18646  $cp = 0;
18647  $cs = 0;
18648  $dom[$key]['rowspans'] = array();
18649  if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
18650  $this->htmlvspace = 0;
18651  // set table header
18652  if (!TCPDF_STATIC::empty_string($dom[$key]['thead'])) {
18653  // set table header
18654  $this->thead = $dom[$key]['thead'];
18655  if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
18656  $this->theadMargins = array();
18657  $this->theadMargins['cell_padding'] = $this->cell_padding;
18658  $this->theadMargins['lmargin'] = $this->lMargin;
18659  $this->theadMargins['rmargin'] = $this->rMargin;
18660  $this->theadMargins['page'] = $this->page;
18661  $this->theadMargins['cell'] = $cell;
18662  $this->theadMargins['gvars'] = $this->getGraphicVars();
18663  }
18664  }
18665  }
18666  // store current margins and page
18667  $dom[$key]['old_cell_padding'] = $this->cell_padding;
18668  if (isset($tag['attribute']['cellpadding'])) {
18669  $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
18670  $this->SetCellPadding($pad);
18671  } elseif (isset($tag['padding'])) {
18672  $this->cell_padding = $tag['padding'];
18673  }
18674  if (isset($tag['attribute']['cellspacing'])) {
18675  $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
18676  } elseif (isset($tag['border-spacing'])) {
18677  $cs = $tag['border-spacing']['V'];
18678  }
18679  $prev_y = $this->y;
18680  if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) {
18681  $this->inthead = true;
18682  // add a page (or trig AcceptPageBreak() for multicolumn mode)
18683  $this->checkPageBreak($this->PageBreakTrigger + 1);
18684  }
18685  break;
18686  }
18687  case 'tr': {
18688  // array of columns positions
18689  $dom[$key]['cellpos'] = array();
18690  break;
18691  }
18692  case 'hr': {
18693  if ((isset($tag['height'])) AND ($tag['height'] != '')) {
18694  $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
18695  } else {
18696  $hrHeight = $this->GetLineWidth();
18697  }
18698  $this->addHTMLVertSpace($hbz, ($hrHeight / 2), $cell, $firsttag);
18699  $x = $this->GetX();
18700  $y = $this->GetY();
18701  $wtmp = $this->w - $this->lMargin - $this->rMargin;
18702  if ($cell) {
18703  $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']);
18704  }
18705  if ((isset($tag['width'])) AND ($tag['width'] != '')) {
18706  $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
18707  } else {
18708  $hrWidth = $wtmp;
18709  }
18710  $prevlinewidth = $this->GetLineWidth();
18711  $this->SetLineWidth($hrHeight);
18712  $this->Line($x, $y, $x + $hrWidth, $y);
18713  $this->SetLineWidth($prevlinewidth);
18714  $this->addHTMLVertSpace(($hrHeight / 2), 0, $cell, !isset($dom[($key + 1)]));
18715  break;
18716  }
18717  case 'a': {
18718  if (array_key_exists('href', $tag['attribute'])) {
18719  $this->HREF['url'] = $tag['attribute']['href'];
18720  }
18721  break;
18722  }
18723  case 'img': {
18724  if (!empty($tag['attribute']['src'])) {
18725  if ($tag['attribute']['src']{0} === '@') {
18726  // data stream
18727  $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1));
18728  $type = '';
18729  } else {
18730  // get image type
18731  $type = TCPDF_IMAGES::getImageFileType($tag['attribute']['src']);
18732  }
18733  if (!isset($tag['width'])) {
18734  $tag['width'] = 0;
18735  }
18736  if (!isset($tag['height'])) {
18737  $tag['height'] = 0;
18738  }
18739  //if (!isset($tag['attribute']['align'])) {
18740  // the only alignment supported is "bottom"
18741  // further development is required for other modes.
18742  $tag['attribute']['align'] = 'bottom';
18743  //}
18744  switch($tag['attribute']['align']) {
18745  case 'top': {
18746  $align = 'T';
18747  break;
18748  }
18749  case 'middle': {
18750  $align = 'M';
18751  break;
18752  }
18753  case 'bottom': {
18754  $align = 'B';
18755  break;
18756  }
18757  default: {
18758  $align = 'B';
18759  break;
18760  }
18761  }
18762  $prevy = $this->y;
18763  $xpos = $this->x;
18764  $imglink = '';
18765  if (isset($this->HREF['url']) AND !TCPDF_STATIC::empty_string($this->HREF['url'])) {
18766  $imglink = $this->HREF['url'];
18767  if ($imglink{0} == '#') {
18768  // convert url to internal link
18769  $lnkdata = explode(',', $imglink);
18770  if (isset($lnkdata[0])) {
18771  $page = intval(substr($lnkdata[0], 1));
18772  if (empty($page) OR ($page <= 0)) {
18773  $page = $this->page;
18774  }
18775  if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
18776  $lnky = floatval($lnkdata[1]);
18777  } else {
18778  $lnky = 0;
18779  }
18780  $imglink = $this->AddLink();
18781  $this->SetLink($imglink, $lnky, $page);
18782  }
18783  }
18784  }
18785  $border = 0;
18786  if (isset($tag['border']) AND !empty($tag['border'])) {
18787  // currently only support 1 (frame) or a combination of 'LTRB'
18788  $border = $tag['border'];
18789  }
18790  $iw = '';
18791  if (isset($tag['width'])) {
18792  $iw = $this->getHTMLUnitToUnits($tag['width'], 1, 'px', false);
18793  }
18794  $ih = '';
18795  if (isset($tag['height'])) {
18796  $ih = $this->getHTMLUnitToUnits($tag['height'], 1, 'px', false);
18797  }
18798  if (($type == 'eps') OR ($type == 'ai')) {
18799  $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
18800  } elseif ($type == 'svg') {
18801  $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
18802  } else {
18803  $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
18804  }
18805  switch($align) {
18806  case 'T': {
18807  $this->y = $prevy;
18808  break;
18809  }
18810  case 'M': {
18811  $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
18812  break;
18813  }
18814  case 'B': {
18815  $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
18816  break;
18817  }
18818  }
18819  }
18820  break;
18821  }
18822  case 'dl': {
18823  ++$this->listnum;
18824  if ($this->listnum == 1) {
18825  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18826  } else {
18827  $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18828  }
18829  break;
18830  }
18831  case 'dt': {
18832  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18833  break;
18834  }
18835  case 'dd': {
18836  if ($this->rtl) {
18837  $this->rMargin += $this->listindent;
18838  } else {
18839  $this->lMargin += $this->listindent;
18840  }
18842  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18843  break;
18844  }
18845  case 'ul':
18846  case 'ol': {
18847  ++$this->listnum;
18848  if ($tag['value'] == 'ol') {
18849  $this->listordered[$this->listnum] = true;
18850  } else {
18851  $this->listordered[$this->listnum] = false;
18852  }
18853  if (isset($tag['attribute']['start'])) {
18854  $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
18855  } else {
18856  $this->listcount[$this->listnum] = 0;
18857  }
18858  if ($this->rtl) {
18859  $this->rMargin += $this->listindent;
18860  $this->x -= $this->listindent;
18861  } else {
18862  $this->lMargin += $this->listindent;
18863  $this->x += $this->listindent;
18864  }
18866  if ($this->listnum == 1) {
18867  if ($key > 1) {
18868  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18869  }
18870  } else {
18871  $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18872  }
18873  break;
18874  }
18875  case 'li': {
18876  if ($key > 2) {
18877  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18878  }
18879  if ($this->listordered[$this->listnum]) {
18880  // ordered item
18881  if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
18882  $this->lispacer = $parent['attribute']['type'];
18883  } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
18884  $this->lispacer = $parent['listtype'];
18885  } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
18886  $this->lispacer = $this->lisymbol;
18887  } else {
18888  $this->lispacer = '#';
18889  }
18890  ++$this->listcount[$this->listnum];
18891  if (isset($tag['attribute']['value'])) {
18892  $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
18893  }
18894  } else {
18895  // unordered item
18896  if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
18897  $this->lispacer = $parent['attribute']['type'];
18898  } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
18899  $this->lispacer = $parent['listtype'];
18900  } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
18901  $this->lispacer = $this->lisymbol;
18902  } else {
18903  $this->lispacer = '!';
18904  }
18905  }
18906  break;
18907  }
18908  case 'blockquote': {
18909  if ($this->rtl) {
18910  $this->rMargin += $this->listindent;
18911  } else {
18912  $this->lMargin += $this->listindent;
18913  }
18915  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18916  break;
18917  }
18918  case 'br': {
18919  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18920  break;
18921  }
18922  case 'div': {
18923  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18924  break;
18925  }
18926  case 'p': {
18927  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18928  break;
18929  }
18930  case 'pre': {
18931  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18932  $this->premode = true;
18933  break;
18934  }
18935  case 'sup': {
18936  $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
18937  break;
18938  }
18939  case 'sub': {
18940  $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
18941  break;
18942  }
18943  case 'h1':
18944  case 'h2':
18945  case 'h3':
18946  case 'h4':
18947  case 'h5':
18948  case 'h6': {
18949  $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18950  break;
18951  }
18952  // Form fields (since 4.8.000 - 2009-09-07)
18953  case 'form': {
18954  if (isset($tag['attribute']['action'])) {
18955  $this->form_action = $tag['attribute']['action'];
18956  } else {
18957  $this->Error('Please explicitly set action attribute path!');
18958  }
18959  if (isset($tag['attribute']['enctype'])) {
18960  $this->form_enctype = $tag['attribute']['enctype'];
18961  } else {
18962  $this->form_enctype = 'application/x-www-form-urlencoded';
18963  }
18964  if (isset($tag['attribute']['method'])) {
18965  $this->form_mode = $tag['attribute']['method'];
18966  } else {
18967  $this->form_mode = 'post';
18968  }
18969  break;
18970  }
18971  case 'input': {
18972  if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
18973  $name = $tag['attribute']['name'];
18974  } else {
18975  break;
18976  }
18977  $prop = array();
18978  $opt = array();
18979  if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
18980  $prop['readonly'] = true;
18981  }
18982  if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
18983  $value = $tag['attribute']['value'];
18984  }
18985  if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC::empty_string($tag['attribute']['maxlength'])) {
18986  $opt['maxlen'] = intval($tag['attribute']['maxlength']);
18987  }
18988  $h = $this->getCellHeight($this->FontSize);
18989  if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
18990  $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
18991  } else {
18992  $w = $h;
18993  }
18994  if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
18995  $checked = true;
18996  } else {
18997  $checked = false;
18998  }
18999  if (isset($tag['align'])) {
19000  switch ($tag['align']) {
19001  case 'C': {
19002  $opt['q'] = 1;
19003  break;
19004  }
19005  case 'R': {
19006  $opt['q'] = 2;
19007  break;
19008  }
19009  case 'L':
19010  default: {
19011  break;
19012  }
19013  }
19014  }
19015  switch ($tag['attribute']['type']) {
19016  case 'text': {
19017  if (isset($value)) {
19018  $opt['v'] = $value;
19019  }
19020  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19021  break;
19022  }
19023  case 'password': {
19024  if (isset($value)) {
19025  $opt['v'] = $value;
19026  }
19027  $prop['password'] = 'true';
19028  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19029  break;
19030  }
19031  case 'checkbox': {
19032  if (!isset($value)) {
19033  break;
19034  }
19035  $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
19036  break;
19037  }
19038  case 'radio': {
19039  if (!isset($value)) {
19040  break;
19041  }
19042  $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
19043  break;
19044  }
19045  case 'submit': {
19046  if (!isset($value)) {
19047  $value = 'submit';
19048  }
19049  $w = $this->GetStringWidth($value) * 1.5;
19050  $h *= 1.6;
19051  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19052  $action = array();
19053  $action['S'] = 'SubmitForm';
19054  $action['F'] = $this->form_action;
19055  if ($this->form_enctype != 'FDF') {
19056  $action['Flags'] = array('ExportFormat');
19057  }
19058  if ($this->form_mode == 'get') {
19059  $action['Flags'] = array('GetMethod');
19060  }
19061  $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
19062  break;
19063  }
19064  case 'reset': {
19065  if (!isset($value)) {
19066  $value = 'reset';
19067  }
19068  $w = $this->GetStringWidth($value) * 1.5;
19069  $h *= 1.6;
19070  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19071  $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
19072  break;
19073  }
19074  case 'file': {
19075  $prop['fileSelect'] = 'true';
19076  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19077  if (!isset($value)) {
19078  $value = '*';
19079  }
19080  $w = $this->GetStringWidth($value) * 2;
19081  $h *= 1.2;
19082  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19083  $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
19084  $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19085  break;
19086  }
19087  case 'hidden': {
19088  if (isset($value)) {
19089  $opt['v'] = $value;
19090  }
19091  $opt['f'] = array('invisible', 'hidden');
19092  $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
19093  break;
19094  }
19095  case 'image': {
19096  // THIS TYPE MUST BE FIXED
19097  if (isset($tag['attribute']['src']) AND !TCPDF_STATIC::empty_string($tag['attribute']['src'])) {
19098  $img = $tag['attribute']['src'];
19099  } else {
19100  break;
19101  }
19102  $value = 'img';
19103  //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
19104  if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19105  $jsaction = $tag['attribute']['onclick'];
19106  } else {
19107  $jsaction = '';
19108  }
19109  $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19110  break;
19111  }
19112  case 'button': {
19113  if (!isset($value)) {
19114  $value = ' ';
19115  }
19116  $w = $this->GetStringWidth($value) * 1.5;
19117  $h *= 1.6;
19118  $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19119  if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19120  $jsaction = $tag['attribute']['onclick'];
19121  } else {
19122  $jsaction = '';
19123  }
19124  $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19125  break;
19126  }
19127  }
19128  break;
19129  }
19130  case 'textarea': {
19131  $prop = array();
19132  $opt = array();
19133  if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19134  $prop['readonly'] = true;
19135  }
19136  if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19137  $name = $tag['attribute']['name'];
19138  } else {
19139  break;
19140  }
19141  if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19142  $opt['v'] = $tag['attribute']['value'];
19143  }
19144  if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC::empty_string($tag['attribute']['cols'])) {
19145  $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
19146  } else {
19147  $w = 40;
19148  }
19149  if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC::empty_string($tag['attribute']['rows'])) {
19150  $h = intval($tag['attribute']['rows']) * $this->getCellHeight($this->FontSize);
19151  } else {
19152  $h = 10;
19153  }
19154  $prop['multiline'] = 'true';
19155  $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19156  break;
19157  }
19158  case 'select': {
19159  $h = $this->getCellHeight($this->FontSize);
19160  if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19161  $h *= ($tag['attribute']['size'] + 1);
19162  }
19163  $prop = array();
19164  $opt = array();
19165  if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19166  $name = $tag['attribute']['name'];
19167  } else {
19168  break;
19169  }
19170  $w = 0;
19171  if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC::empty_string($tag['attribute']['opt'])) {
19172  $options = explode('#!NwL!#', $tag['attribute']['opt']);
19173  $values = array();
19174  foreach ($options as $val) {
19175  if (strpos($val, '#!TaB!#') !== false) {
19176  $opts = explode('#!TaB!#', $val);
19177  $values[] = $opts;
19178  $w = max($w, $this->GetStringWidth($opts[1]));
19179  } else {
19180  $values[] = $val;
19181  $w = max($w, $this->GetStringWidth($val));
19182  }
19183  }
19184  } else {
19185  break;
19186  }
19187  $w *= 2;
19188  if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
19189  $prop['multipleSelection'] = 'true';
19190  $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19191  } else {
19192  $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19193  }
19194  break;
19195  }
19196  case 'tcpdf': {
19197  if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
19198  // Special tag used to call TCPDF methods
19199  if (isset($tag['attribute']['method'])) {
19200  $tcpdf_method = $tag['attribute']['method'];
19201  if (method_exists($this, $tcpdf_method)) {
19202  if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
19203  $params = unserialize(urldecode($tag['attribute']['params']));
19204  call_user_func_array(array($this, $tcpdf_method), $params);
19205  } else {
19206  $this->$tcpdf_method();
19207  }
19208  $this->newline = true;
19209  }
19210  }
19211  }
19212  break;
19213  }
19214  default: {
19215  break;
19216  }
19217  }
19218  // define tags that support borders and background colors
19219  $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
19220  if (in_array($tag['value'], $bordertags)) {
19221  // set border
19222  $dom[$key]['borderposition'] = $this->getBorderStartPosition();
19223  }
19224  if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
19225  $pba = $dom[$key]['attribute']['pagebreakafter'];
19226  // check for pagebreak
19227  if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19228  // add a page (or trig AcceptPageBreak() for multicolumn mode)
19229  $this->checkPageBreak($this->PageBreakTrigger + 1);
19230  }
19231  if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19232  OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19233  // add a page (or trig AcceptPageBreak() for multicolumn mode)
19234  $this->checkPageBreak($this->PageBreakTrigger + 1);
19235  }
19236  }
19237  return $dom;
19238  }
19239 
19249  protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
19250  $tag = $dom[$key];
19251  $parent = $dom[($dom[$key]['parent'])];
19252  $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
19253  $in_table_head = false;
19254  // maximum x position (used to draw borders)
19255  if ($this->rtl) {
19256  $xmax = $this->w;
19257  } else {
19258  $xmax = 0;
19259  }
19260  if ($tag['block']) {
19261  $hbz = 0; // distance from y to line bottom
19262  $hb = 0; // vertical space between block tags
19263  // calculate vertical space for block tags
19264  if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
19265  $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
19266  } elseif (isset($parent['fontsize'])) {
19267  $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
19268  } else {
19269  $pre_h = $this->getCellHeight($this->FontSize);
19270  }
19271  if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
19272  $n = $this->tagvspaces[$tag['value']][1]['n'];
19273  } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
19274  $n = 0.6;
19275  } else {
19276  $n = 1;
19277  }
19278  if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) {
19279  $hb = 0;
19280  } else {
19281  $hb = ($n * $pre_h);
19282  }
19283  if ($maxbottomliney > $this->PageBreakTrigger) {
19284  $hbz = $this->getCellHeight($this->FontSize);
19285  } elseif ($this->y < $maxbottomliney) {
19286  $hbz = ($maxbottomliney - $this->y);
19287  }
19288  }
19289  // Closing tag
19290  switch($tag['value']) {
19291  case 'tr': {
19292  $table_el = $dom[($dom[$key]['parent'])]['parent'];
19293  if (!isset($parent['endy'])) {
19294  $dom[($dom[$key]['parent'])]['endy'] = $this->y;
19295  $parent['endy'] = $this->y;
19296  }
19297  if (!isset($parent['endpage'])) {
19298  $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
19299  $parent['endpage'] = $this->page;
19300  }
19301  if (!isset($parent['endcolumn'])) {
19302  $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column;
19303  $parent['endcolumn'] = $this->current_column;
19304  }
19305  // update row-spanned cells
19306  if (isset($dom[$table_el]['rowspans'])) {
19307  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19308  $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
19309  if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19310  if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
19311  $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
19312  } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
19313  $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19314  $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19315  $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19316  }
19317  }
19318  }
19319  // report new endy and endpage to the rowspanned cells
19320  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19321  if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19322  $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
19323  $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19324  $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
19325  $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19326  $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
19327  $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19328  }
19329  }
19330  // update remaining rowspanned cells
19331  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19332  if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19333  $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
19334  $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
19335  $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
19336  }
19337  }
19338  }
19339  $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
19340  if ($this->num_columns > 1) {
19341  $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
19342  }
19343  $this->y = $dom[($dom[$key]['parent'])]['endy'];
19344  if (isset($dom[$table_el]['attribute']['cellspacing'])) {
19345  $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
19346  } elseif (isset($dom[$table_el]['border-spacing'])) {
19347  $this->y += $dom[$table_el]['border-spacing']['V'];
19348  }
19349  $this->Ln(0, $cell);
19350  if ($this->current_column == $parent['startcolumn']) {
19351  $this->x = $parent['startx'];
19352  }
19353  // account for booklet mode
19354  if ($this->page > $parent['startpage']) {
19355  if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
19356  $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
19357  } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
19358  $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
19359  }
19360  }
19361  break;
19362  }
19363  case 'tablehead':
19364  // closing tag used for the thead part
19365  $in_table_head = true;
19366  $this->inthead = false;
19367  case 'table': {
19368  $table_el = $parent;
19369  // set default border
19370  if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
19371  // set default border
19372  $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
19373  } else {
19374  $border = 0;
19375  }
19376  $default_border = $border;
19377  // fix bottom line alignment of last line before page break
19378  foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
19379  // update row-spanned cells
19380  if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19381  foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19382  if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) {
19383  $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
19384  }
19385  if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) {
19386  $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
19387  }
19388  }
19389  }
19390  if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
19391  $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
19392  $dom[$prevtrkey]['endy'] = $pgendy;
19393  // update row-spanned cells
19394  if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19395  foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19396  if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] > 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
19397  $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
19398  $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
19399  }
19400  }
19401  }
19402  }
19403  $prevtrkey = $trkey;
19404  $table_el = $dom[($dom[$key]['parent'])];
19405  }
19406  // for each row
19407  if (count($table_el['trids']) > 0) {
19408  unset($xmax);
19409  }
19410  foreach ($table_el['trids'] as $j => $trkey) {
19411  $parent = $dom[$trkey];
19412  if (!isset($xmax)) {
19413  $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
19414  }
19415  // for each cell on the row
19416  foreach ($parent['cellpos'] as $k => $cellpos) {
19417  if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
19418  $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
19419  $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
19420  $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
19421  $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
19422  $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
19423  $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
19424  $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
19425  } else {
19426  $endy = $parent['endy'];
19427  $startpage = $parent['startpage'];
19428  $endpage = $parent['endpage'];
19429  $startcolumn = $parent['startcolumn'];
19430  $endcolumn = $parent['endcolumn'];
19431  }
19432  if ($this->num_columns == 0) {
19433  $this->num_columns = 1;
19434  }
19435  if (isset($cellpos['border'])) {
19436  $border = $cellpos['border'];
19437  }
19438  if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
19439  $this->SetFillColorArray($cellpos['bgcolor']);
19440  $fill = true;
19441  } else {
19442  $fill = false;
19443  }
19444  $x = $cellpos['startx'];
19445  $y = $parent['starty'];
19446  $starty = $y;
19447  $w = abs($cellpos['endx'] - $cellpos['startx']);
19448  // get border modes
19449  $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
19450  $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
19451  $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19452  // design borders around HTML cells.
19453  for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
19454  $ccode = '';
19455  $this->setPage($page);
19456  if ($this->num_columns < 2) {
19457  // single-column mode
19458  $this->x = $x;
19459  $this->y = $this->tMargin;
19460  }
19461  // account for margin changes
19462  if ($page > $startpage) {
19463  if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
19464  $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
19465  } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
19466  $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
19467  }
19468  }
19469  if ($startpage == $endpage) { // single page
19470  $deltacol = 0;
19471  $deltath = 0;
19472  for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
19473  $this->selectColumn($column);
19474  if ($startcolumn == $endcolumn) { // single column
19475  $cborder = $border;
19476  $h = $endy - $parent['starty'];
19477  $this->y = $y;
19478  $this->x = $x;
19479  } elseif ($column == $startcolumn) { // first column
19480  $cborder = $border_start;
19481  $this->y = $starty;
19482  $this->x = $x;
19483  $h = $this->h - $this->y - $this->bMargin;
19484  if ($this->rtl) {
19485  $deltacol = $this->x + $this->rMargin - $this->w;
19486  } else {
19487  $deltacol = $this->x - $this->lMargin;
19488  }
19489  } elseif ($column == $endcolumn) { // end column
19490  $cborder = $border_end;
19491  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19492  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19493  }
19494  $this->x += $deltacol;
19495  $h = $endy - $this->y;
19496  } else { // middle column
19497  $cborder = $border_middle;
19498  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19499  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19500  }
19501  $this->x += $deltacol;
19502  $h = $this->h - $this->y - $this->bMargin;
19503  }
19504  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19505  } // end for each column
19506  } elseif ($page == $startpage) { // first page
19507  $deltacol = 0;
19508  $deltath = 0;
19509  for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
19510  $this->selectColumn($column);
19511  if ($column == $startcolumn) { // first column
19512  $cborder = $border_start;
19513  $this->y = $starty;
19514  $this->x = $x;
19515  $h = $this->h - $this->y - $this->bMargin;
19516  if ($this->rtl) {
19517  $deltacol = $this->x + $this->rMargin - $this->w;
19518  } else {
19519  $deltacol = $this->x - $this->lMargin;
19520  }
19521  } else { // middle column
19522  $cborder = $border_middle;
19523  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19524  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19525  }
19526  $this->x += $deltacol;
19527  $h = $this->h - $this->y - $this->bMargin;
19528  }
19529  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19530  } // end for each column
19531  } elseif ($page == $endpage) { // last page
19532  $deltacol = 0;
19533  $deltath = 0;
19534  for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
19535  $this->selectColumn($column);
19536  if ($column == $endcolumn) { // end column
19537  $cborder = $border_end;
19538  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19539  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19540  }
19541  $this->x += $deltacol;
19542  $h = $endy - $this->y;
19543  } else { // middle column
19544  $cborder = $border_middle;
19545  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19546  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19547  }
19548  $this->x += $deltacol;
19549  $h = $this->h - $this->y - $this->bMargin;
19550  }
19551  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19552  } // end for each column
19553  } else { // middle page
19554  $deltacol = 0;
19555  $deltath = 0;
19556  for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
19557  $this->selectColumn($column);
19558  $cborder = $border_middle;
19559  if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19560  $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19561  }
19562  $this->x += $deltacol;
19563  $h = $this->h - $this->y - $this->bMargin;
19564  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19565  } // end for each column
19566  }
19567  if ($cborder OR $fill) {
19568  $offsetlen = strlen($ccode);
19569  // draw border and fill
19570  if ($this->inxobj) {
19571  // we are inside an XObject template
19572  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
19573  $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
19574  $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
19575  $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
19576  } else {
19577  $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
19578  $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
19579  }
19580  $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
19581  $pstart = substr($pagebuff, 0, $pagemark);
19582  $pend = substr($pagebuff, $pagemark);
19583  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
19584  } else {
19585  // draw border and fill
19586  if (end($this->transfmrk[$this->page]) !== false) {
19587  $pagemarkkey = key($this->transfmrk[$this->page]);
19588  $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
19589  } elseif ($this->InFooter) {
19590  $pagemark = $this->footerpos[$this->page];
19591  } else {
19592  $pagemark = $this->intmrk[$this->page];
19593  }
19594  $pagebuff = $this->getPageBuffer($this->page);
19595  $pstart = substr($pagebuff, 0, $pagemark);
19596  $pend = substr($pagebuff, $pagemark);
19597  $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
19598  }
19599  }
19600  } // end for each page
19601  // restore default border
19602  $border = $default_border;
19603  } // end for each cell on the row
19604  if (isset($table_el['attribute']['cellspacing'])) {
19605  $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
19606  } elseif (isset($table_el['border-spacing'])) {
19607  $this->y += $table_el['border-spacing']['V'];
19608  }
19609  $this->Ln(0, $cell);
19610  $this->x = $parent['startx'];
19611  if ($endpage > $startpage) {
19612  if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
19613  $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
19614  } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
19615  $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
19616  }
19617  }
19618  }
19619  if (!$in_table_head) { // we are not inside a thead section
19620  $this->cell_padding = $table_el['old_cell_padding'];
19621  // reset row height
19622  $this->resetLastH();
19623  if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) {
19624  $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]);
19625  if (($plendiff > 0) AND ($plendiff < 60)) {
19626  $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff);
19627  if (substr($pagediff, 0, 5) == 'BT /F') {
19628  // the difference is only a font setting
19629  $plendiff = 0;
19630  }
19631  }
19632  if ($plendiff == 0) {
19633  // remove last blank page
19634  $this->deletePage($this->numpages);
19635  }
19636  }
19637  if (isset($this->theadMargins['top'])) {
19638  // restore top margin
19639  $this->tMargin = $this->theadMargins['top'];
19640  }
19641  if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
19642  // reset main table header
19643  $this->thead = '';
19644  $this->theadMargins = array();
19645  $this->pagedim[$this->page]['tm'] = $this->tMargin;
19646  }
19647  }
19648  $parent = $table_el;
19649  break;
19650  }
19651  case 'a': {
19652  $this->HREF = '';
19653  break;
19654  }
19655  case 'sup': {
19656  $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
19657  break;
19658  }
19659  case 'sub': {
19660  $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k));
19661  break;
19662  }
19663  case 'div': {
19664  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19665  break;
19666  }
19667  case 'blockquote': {
19668  if ($this->rtl) {
19669  $this->rMargin -= $this->listindent;
19670  } else {
19671  $this->lMargin -= $this->listindent;
19672  }
19674  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19675  break;
19676  }
19677  case 'p': {
19678  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19679  break;
19680  }
19681  case 'pre': {
19682  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19683  $this->premode = false;
19684  break;
19685  }
19686  case 'dl': {
19687  --$this->listnum;
19688  if ($this->listnum <= 0) {
19689  $this->listnum = 0;
19690  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19691  } else {
19692  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19693  }
19694  $this->resetLastH();
19695  break;
19696  }
19697  case 'dt': {
19698  $this->lispacer = '';
19699  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19700  break;
19701  }
19702  case 'dd': {
19703  $this->lispacer = '';
19704  if ($this->rtl) {
19705  $this->rMargin -= $this->listindent;
19706  } else {
19707  $this->lMargin -= $this->listindent;
19708  }
19710  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19711  break;
19712  }
19713  case 'ul':
19714  case 'ol': {
19715  --$this->listnum;
19716  $this->lispacer = '';
19717  if ($this->rtl) {
19718  $this->rMargin -= $this->listindent;
19719  } else {
19720  $this->lMargin -= $this->listindent;
19721  }
19723  if ($this->listnum <= 0) {
19724  $this->listnum = 0;
19725  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19726  } else {
19727  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19728  }
19729  $this->resetLastH();
19730  break;
19731  }
19732  case 'li': {
19733  $this->lispacer = '';
19734  $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19735  break;
19736  }
19737  case 'h1':
19738  case 'h2':
19739  case 'h3':
19740  case 'h4':
19741  case 'h5':
19742  case 'h6': {
19743  $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19744  break;
19745  }
19746  // Form fields (since 4.8.000 - 2009-09-07)
19747  case 'form': {
19748  $this->form_action = '';
19749  $this->form_enctype = 'application/x-www-form-urlencoded';
19750  break;
19751  }
19752  default : {
19753  break;
19754  }
19755  }
19756  // draw border and background (if any)
19757  $this->drawHTMLTagBorder($parent, $xmax);
19758  if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
19759  $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
19760  // check for pagebreak
19761  if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19762  // add a page (or trig AcceptPageBreak() for multicolumn mode)
19763  $this->checkPageBreak($this->PageBreakTrigger + 1);
19764  }
19765  if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19766  OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19767  // add a page (or trig AcceptPageBreak() for multicolumn mode)
19768  $this->checkPageBreak($this->PageBreakTrigger + 1);
19769  }
19770  }
19771  $this->tmprtl = false;
19772  return $dom;
19773  }
19774 
19784  protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
19785  if ($firsttag) {
19786  $this->Ln(0, $cell);
19787  $this->htmlvspace = 0;
19788  return;
19789  }
19790  if ($lasttag) {
19791  $this->Ln($hbz, $cell);
19792  $this->htmlvspace = 0;
19793  return;
19794  }
19795  if ($hb < $this->htmlvspace) {
19796  $hd = 0;
19797  } else {
19798  $hd = $hb - $this->htmlvspace;
19799  $this->htmlvspace = $hb;
19800  }
19801  $this->Ln(($hbz + $hd), $cell);
19802  }
19803 
19810  protected function getBorderStartPosition() {
19811  if ($this->rtl) {
19812  $xmax = $this->lMargin;
19813  } else {
19814  $xmax = $this->w - $this->rMargin;
19815  }
19816  return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax);
19817  }
19818 
19826  protected function drawHTMLTagBorder($tag, $xmax) {
19827  if (!isset($tag['borderposition'])) {
19828  // nothing to draw
19829  return;
19830  }
19831  $prev_x = $this->x;
19832  $prev_y = $this->y;
19833  $prev_lasth = $this->lasth;
19834  $border = 0;
19835  $fill = false;
19836  $this->lasth = 0;
19837  if (isset($tag['border']) AND !empty($tag['border'])) {
19838  // get border style
19839  $border = $tag['border'];
19840  if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
19841  // border for table header
19842  $border = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19843  }
19844  }
19845  if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
19846  // get background color
19847  $old_bgcolor = $this->bgcolor;
19848  $this->SetFillColorArray($tag['bgcolor']);
19849  $fill = true;
19850  }
19851  if (!$border AND !$fill) {
19852  // nothing to draw
19853  return;
19854  }
19855  if (isset($tag['attribute']['cellspacing'])) {
19856  $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
19857  $cellspacing = array('H' => $clsp, 'V' => $clsp);
19858  } elseif (isset($tag['border-spacing'])) {
19859  $cellspacing = $tag['border-spacing'];
19860  } else {
19861  $cellspacing = array('H' => 0, 'V' => 0);
19862  }
19863  if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
19864  // draw the border externally respect the sqare edge.
19865  $border['mode'] = 'ext';
19866  }
19867  if ($this->rtl) {
19868  if ($xmax >= $tag['borderposition']['x']) {
19869  $xmax = $tag['borderposition']['xmax'];
19870  }
19871  $w = ($tag['borderposition']['x'] - $xmax);
19872  } else {
19873  if ($xmax <= $tag['borderposition']['x']) {
19874  $xmax = $tag['borderposition']['xmax'];
19875  }
19876  $w = ($xmax - $tag['borderposition']['x']);
19877  }
19878  if ($w <= 0) {
19879  return;
19880  }
19881  $w += $cellspacing['H'];
19882  $startpage = $tag['borderposition']['page'];
19883  $startcolumn = $tag['borderposition']['column'];
19884  $x = $tag['borderposition']['x'];
19885  $y = $tag['borderposition']['y'];
19886  $endpage = $this->page;
19887  $starty = $tag['borderposition']['y'] - $cellspacing['V'];
19888  $currentY = $this->y;
19889  $this->x = $x;
19890  // get latest column
19891  $endcolumn = $this->current_column;
19892  if ($this->num_columns == 0) {
19893  $this->num_columns = 1;
19894  }
19895  // get border modes
19896  $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
19897  $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
19898  $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19899  // temporary disable page regions
19900  $temp_page_regions = $this->page_regions;
19901  $this->page_regions = array();
19902  // design borders around HTML cells.
19903  for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
19904  $ccode = '';
19905  $this->setPage($page);
19906  if ($this->num_columns < 2) {
19907  // single-column mode
19908  $this->x = $x;
19909  $this->y = $this->tMargin;
19910  }
19911  // account for margin changes
19912  if ($page > $startpage) {
19913  if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
19914  $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
19915  } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
19916  $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
19917  }
19918  }
19919  if ($startpage == $endpage) {
19920  // single page
19921  for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
19922  $this->selectColumn($column);
19923  if ($startcolumn == $endcolumn) { // single column
19924  $cborder = $border;
19925  $h = ($currentY - $y) + $cellspacing['V'];
19926  $this->y = $starty;
19927  } elseif ($column == $startcolumn) { // first column
19928  $cborder = $border_start;
19929  $this->y = $starty;
19930  $h = $this->h - $this->y - $this->bMargin;
19931  } elseif ($column == $endcolumn) { // end column
19932  $cborder = $border_end;
19933  $h = $currentY - $this->y;
19934  } else { // middle column
19935  $cborder = $border_middle;
19936  $h = $this->h - $this->y - $this->bMargin;
19937  }
19938  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19939  } // end for each column
19940  } elseif ($page == $startpage) { // first page
19941  for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
19942  $this->selectColumn($column);
19943  if ($column == $startcolumn) { // first column
19944  $cborder = $border_start;
19945  $this->y = $starty;
19946  $h = $this->h - $this->y - $this->bMargin;
19947  } else { // middle column
19948  $cborder = $border_middle;
19949  $h = $this->h - $this->y - $this->bMargin;
19950  }
19951  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19952  } // end for each column
19953  } elseif ($page == $endpage) { // last page
19954  for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
19955  $this->selectColumn($column);
19956  if ($column == $endcolumn) {
19957  // end column
19958  $cborder = $border_end;
19959  $h = $currentY - $this->y;
19960  } else {
19961  // middle column
19962  $cborder = $border_middle;
19963  $h = $this->h - $this->y - $this->bMargin;
19964  }
19965  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19966  } // end for each column
19967  } else { // middle page
19968  for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
19969  $this->selectColumn($column);
19970  $cborder = $border_middle;
19971  $h = $this->h - $this->y - $this->bMargin;
19972  $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19973  } // end for each column
19974  }
19975  if ($cborder OR $fill) {
19976  $offsetlen = strlen($ccode);
19977  // draw border and fill
19978  if ($this->inxobj) {
19979  // we are inside an XObject template
19980  if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
19981  $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
19982  $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
19983  $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
19984  } else {
19985  $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
19986  $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
19987  }
19988  $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
19989  $pstart = substr($pagebuff, 0, $pagemark);
19990  $pend = substr($pagebuff, $pagemark);
19991  $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
19992  } else {
19993  if (end($this->transfmrk[$this->page]) !== false) {
19994  $pagemarkkey = key($this->transfmrk[$this->page]);
19995  $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
19996  } elseif ($this->InFooter) {
19997  $pagemark = $this->footerpos[$this->page];
19998  } else {
19999  $pagemark = $this->intmrk[$this->page];
20000  }
20001  $pagebuff = $this->getPageBuffer($this->page);
20002  $pstart = substr($pagebuff, 0, $pagemark);
20003  $pend = substr($pagebuff, $pagemark);
20004  $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
20005  $this->bordermrk[$this->page] += $offsetlen;
20006  $this->cntmrk[$this->page] += $offsetlen;
20007  }
20008  }
20009  } // end for each page
20010  // restore page regions
20011  $this->page_regions = $temp_page_regions;
20012  if (isset($old_bgcolor)) {
20013  // restore background color
20014  $this->SetFillColorArray($old_bgcolor);
20015  }
20016  // restore pointer position
20017  $this->x = $prev_x;
20018  $this->y = $prev_y;
20019  $this->lasth = $prev_lasth;
20020  }
20021 
20028  public function setLIsymbol($symbol='!') {
20029  // check for custom image symbol
20030  if (substr($symbol, 0, 4) == 'img|') {
20031  $this->lisymbol = $symbol;
20032  return;
20033  }
20034  $symbol = strtolower($symbol);
20035  $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');
20036  if (in_array($symbol, $valid_symbols)) {
20037  $this->lisymbol = $symbol;
20038  } else {
20039  $this->lisymbol = '';
20040  }
20041  }
20042 
20051  public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
20052  $this->booklet = $booklet;
20053  if ($inner >= 0) {
20054  $this->lMargin = $inner;
20055  }
20056  if ($outer >= 0) {
20057  $this->rMargin = $outer;
20058  }
20059  }
20060 
20067  protected function swapMargins($reverse=true) {
20068  if ($reverse) {
20069  // swap left and right margins
20070  $mtemp = $this->original_lMargin;
20071  $this->original_lMargin = $this->original_rMargin;
20072  $this->original_rMargin = $mtemp;
20073  $deltam = $this->original_lMargin - $this->original_rMargin;
20074  $this->lMargin += $deltam;
20075  $this->rMargin -= $deltam;
20076  }
20077  }
20078 
20091  public function setHtmlVSpace($tagvs) {
20092  $this->tagvspaces = $tagvs;
20093  }
20094 
20101  public function setListIndentWidth($width) {
20102  return $this->customlistindent = floatval($width);
20103  }
20104 
20111  public function setOpenCell($isopen) {
20112  $this->opencell = $isopen;
20113  }
20114 
20122  public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
20123  $this->htmlLinkColorArray = $color;
20124  $this->htmlLinkFontStyle = $fontstyle;
20125  }
20126 
20137  public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
20138  $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
20139  $retval = 0;
20140  $value = 0;
20141  $unit = 'px';
20142  if ($points) {
20143  $k = 1;
20144  } else {
20145  $k = $this->k;
20146  }
20147  if (in_array($defaultunit, $supportedunits)) {
20148  $unit = $defaultunit;
20149  }
20150  if (is_numeric($htmlval)) {
20151  $value = floatval($htmlval);
20152  } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
20153  $value = floatval($mnum[1]);
20154  if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
20155  if (in_array($munit[1], $supportedunits)) {
20156  $unit = $munit[1];
20157  }
20158  }
20159  }
20160  switch ($unit) {
20161  // percentage
20162  case '%': {
20163  $retval = (($value * $refsize) / 100);
20164  break;
20165  }
20166  // relative-size
20167  case 'em': {
20168  $retval = ($value * $refsize);
20169  break;
20170  }
20171  // height of lower case 'x' (about half the font-size)
20172  case 'ex': {
20173  $retval = ($value * ($refsize / 2));
20174  break;
20175  }
20176  // absolute-size
20177  case 'in': {
20178  $retval = (($value * $this->dpi) / $k);
20179  break;
20180  }
20181  // centimeters
20182  case 'cm': {
20183  $retval = (($value / 2.54 * $this->dpi) / $k);
20184  break;
20185  }
20186  // millimeters
20187  case 'mm': {
20188  $retval = (($value / 25.4 * $this->dpi) / $k);
20189  break;
20190  }
20191  // one pica is 12 points
20192  case 'pc': {
20193  $retval = (($value * 12) / $k);
20194  break;
20195  }
20196  // points
20197  case 'pt': {
20198  $retval = ($value / $k);
20199  break;
20200  }
20201  // pixels
20202  case 'px': {
20203  $retval = $this->pixelsToUnits($value);
20204  if ($points) {
20205  $retval *= $this->k;
20206  }
20207  break;
20208  }
20209  }
20210  return $retval;
20211  }
20212 
20221  protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
20222  if ($this->state != 2) {
20223  return;
20224  }
20225  $size /= $this->k;
20226  $fill = '';
20227  $bgcolor = $this->bgcolor;
20228  $color = $this->fgcolor;
20229  $strokecolor = $this->strokecolor;
20230  $width = 0;
20231  $textitem = '';
20232  $tmpx = $this->x;
20233  $lspace = $this->GetStringWidth(' ');
20234  if ($listtype == '^') {
20235  // special symbol used for avoid justification of rect bullet
20236  $this->lispacer = '';
20237  return;
20238  } elseif ($listtype == '!') {
20239  // set default list type for unordered list
20240  $deftypes = array('disc', 'circle', 'square');
20241  $listtype = $deftypes[($listdepth - 1) % 3];
20242  } elseif ($listtype == '#') {
20243  // set default list type for ordered list
20244  $listtype = 'decimal';
20245  } elseif (substr($listtype, 0, 4) == 'img|') {
20246  // custom image type ('img|type|width|height|image.ext')
20247  $img = explode('|', $listtype);
20248  $listtype = 'img';
20249  }
20250  switch ($listtype) {
20251  // unordered types
20252  case 'none': {
20253  break;
20254  }
20255  case 'disc': {
20256  $r = $size / 6;
20257  $lspace += (2 * $r);
20258  if ($this->rtl) {
20259  $this->x += $lspace;
20260  } else {
20261  $this->x -= $lspace;
20262  }
20263  $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8);
20264  break;
20265  }
20266  case 'circle': {
20267  $r = $size / 6;
20268  $lspace += (2 * $r);
20269  if ($this->rtl) {
20270  $this->x += $lspace;
20271  } else {
20272  $this->x -= $lspace;
20273  }
20274  $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor;
20275  $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
20276  $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
20277  $this->_out($prev_line_style); // restore line settings
20278  break;
20279  }
20280  case 'square': {
20281  $l = $size / 3;
20282  $lspace += $l;
20283  if ($this->rtl) {;
20284  $this->x += $lspace;
20285  } else {
20286  $this->x -= $lspace;
20287  }
20288  $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color);
20289  break;
20290  }
20291  case 'img': {
20292  // 1=>type, 2=>width, 3=>height, 4=>image.ext
20293  $lspace += $img[2];
20294  if ($this->rtl) {;
20295  $this->x += $lspace;
20296  } else {
20297  $this->x -= $lspace;
20298  }
20299  $imgtype = strtolower($img[1]);
20300  $prev_y = $this->y;
20301  switch ($imgtype) {
20302  case 'svg': {
20303  $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
20304  break;
20305  }
20306  case 'ai':
20307  case 'eps': {
20308  $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
20309  break;
20310  }
20311  default: {
20312  $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);
20313  break;
20314  }
20315  }
20316  $this->y = $prev_y;
20317  break;
20318  }
20319  // ordered types
20320  // $this->listcount[$this->listnum];
20321  // $textitem
20322  case '1':
20323  case 'decimal': {
20324  $textitem = $this->listcount[$this->listnum];
20325  break;
20326  }
20327  case 'decimal-leading-zero': {
20328  $textitem = sprintf('%02d', $this->listcount[$this->listnum]);
20329  break;
20330  }
20331  case 'i':
20332  case 'lower-roman': {
20333  $textitem = strtolower(TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]));
20334  break;
20335  }
20336  case 'I':
20337  case 'upper-roman': {
20338  $textitem = TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]);
20339  break;
20340  }
20341  case 'a':
20342  case 'lower-alpha':
20343  case 'lower-latin': {
20344  $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
20345  break;
20346  }
20347  case 'A':
20348  case 'upper-alpha':
20349  case 'upper-latin': {
20350  $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
20351  break;
20352  }
20353  case 'lower-greek': {
20354  $textitem = TCPDF_FONTS::unichr((945 + $this->listcount[$this->listnum] - 1), $this->isunicode);
20355  break;
20356  }
20357  /*
20358  // Types to be implemented (special handling)
20359  case 'hebrew': {
20360  break;
20361  }
20362  case 'armenian': {
20363  break;
20364  }
20365  case 'georgian': {
20366  break;
20367  }
20368  case 'cjk-ideographic': {
20369  break;
20370  }
20371  case 'hiragana': {
20372  break;
20373  }
20374  case 'katakana': {
20375  break;
20376  }
20377  case 'hiragana-iroha': {
20378  break;
20379  }
20380  case 'katakana-iroha': {
20381  break;
20382  }
20383  */
20384  default: {
20385  $textitem = $this->listcount[$this->listnum];
20386  }
20387  }
20388  if (!TCPDF_STATIC::empty_string($textitem)) {
20389  // Check whether we need a new page or new column
20390  $prev_y = $this->y;
20391  $h = $this->getCellHeight($this->FontSize);
20392  if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) {
20393  $tmpx = $this->x;
20394  }
20395  // print ordered item
20396  if ($this->rtl) {
20397  $textitem = '.'.$textitem;
20398  } else {
20399  $textitem = $textitem.'.';
20400  }
20401  $lspace += $this->GetStringWidth($textitem);
20402  if ($this->rtl) {
20403  $this->x += $lspace;
20404  } else {
20405  $this->x -= $lspace;
20406  }
20407  $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
20408  }
20409  $this->x = $tmpx;
20410  $this->lispacer = '^';
20411  // restore colors
20412  $this->SetFillColorArray($bgcolor);
20413  $this->SetDrawColorArray($strokecolor);
20414  $this->SettextColorArray($color);
20415  }
20416 
20423  protected function getGraphicVars() {
20424  $grapvars = array(
20425  'FontFamily' => $this->FontFamily,
20426  'FontStyle' => $this->FontStyle,
20427  'FontSizePt' => $this->FontSizePt,
20428  'rMargin' => $this->rMargin,
20429  'lMargin' => $this->lMargin,
20430  'cell_padding' => $this->cell_padding,
20431  'cell_margin' => $this->cell_margin,
20432  'LineWidth' => $this->LineWidth,
20433  'linestyleWidth' => $this->linestyleWidth,
20434  'linestyleCap' => $this->linestyleCap,
20435  'linestyleJoin' => $this->linestyleJoin,
20436  'linestyleDash' => $this->linestyleDash,
20437  'textrendermode' => $this->textrendermode,
20438  'textstrokewidth' => $this->textstrokewidth,
20439  'DrawColor' => $this->DrawColor,
20440  'FillColor' => $this->FillColor,
20441  'TextColor' => $this->TextColor,
20442  'ColorFlag' => $this->ColorFlag,
20443  'bgcolor' => $this->bgcolor,
20444  'fgcolor' => $this->fgcolor,
20445  'htmlvspace' => $this->htmlvspace,
20446  'listindent' => $this->listindent,
20447  'listindentlevel' => $this->listindentlevel,
20448  'listnum' => $this->listnum,
20449  'listordered' => $this->listordered,
20450  'listcount' => $this->listcount,
20451  'lispacer' => $this->lispacer,
20452  'cell_height_ratio' => $this->cell_height_ratio,
20453  'font_stretching' => $this->font_stretching,
20454  'font_spacing' => $this->font_spacing,
20455  'alpha' => $this->alpha,
20456  // extended
20457  'lasth' => $this->lasth,
20458  'tMargin' => $this->tMargin,
20459  'bMargin' => $this->bMargin,
20460  'AutoPageBreak' => $this->AutoPageBreak,
20461  'PageBreakTrigger' => $this->PageBreakTrigger,
20462  'x' => $this->x,
20463  'y' => $this->y,
20464  'w' => $this->w,
20465  'h' => $this->h,
20466  'wPt' => $this->wPt,
20467  'hPt' => $this->hPt,
20468  'fwPt' => $this->fwPt,
20469  'fhPt' => $this->fhPt,
20470  'page' => $this->page,
20471  'current_column' => $this->current_column,
20472  'num_columns' => $this->num_columns
20473  );
20474  return $grapvars;
20475  }
20476 
20484  protected function setGraphicVars($gvars, $extended=false) {
20485  if ($this->state != 2) {
20486  return;
20487  }
20488  $this->FontFamily = $gvars['FontFamily'];
20489  $this->FontStyle = $gvars['FontStyle'];
20490  $this->FontSizePt = $gvars['FontSizePt'];
20491  $this->rMargin = $gvars['rMargin'];
20492  $this->lMargin = $gvars['lMargin'];
20493  $this->cell_padding = $gvars['cell_padding'];
20494  $this->cell_margin = $gvars['cell_margin'];
20495  $this->LineWidth = $gvars['LineWidth'];
20496  $this->linestyleWidth = $gvars['linestyleWidth'];
20497  $this->linestyleCap = $gvars['linestyleCap'];
20498  $this->linestyleJoin = $gvars['linestyleJoin'];
20499  $this->linestyleDash = $gvars['linestyleDash'];
20500  $this->textrendermode = $gvars['textrendermode'];
20501  $this->textstrokewidth = $gvars['textstrokewidth'];
20502  $this->DrawColor = $gvars['DrawColor'];
20503  $this->FillColor = $gvars['FillColor'];
20504  $this->TextColor = $gvars['TextColor'];
20505  $this->ColorFlag = $gvars['ColorFlag'];
20506  $this->bgcolor = $gvars['bgcolor'];
20507  $this->fgcolor = $gvars['fgcolor'];
20508  $this->htmlvspace = $gvars['htmlvspace'];
20509  $this->listindent = $gvars['listindent'];
20510  $this->listindentlevel = $gvars['listindentlevel'];
20511  $this->listnum = $gvars['listnum'];
20512  $this->listordered = $gvars['listordered'];
20513  $this->listcount = $gvars['listcount'];
20514  $this->lispacer = $gvars['lispacer'];
20515  $this->cell_height_ratio = $gvars['cell_height_ratio'];
20516  $this->font_stretching = $gvars['font_stretching'];
20517  $this->font_spacing = $gvars['font_spacing'];
20518  $this->alpha = $gvars['alpha'];
20519  if ($extended) {
20520  // restore extended values
20521  $this->lasth = $gvars['lasth'];
20522  $this->tMargin = $gvars['tMargin'];
20523  $this->bMargin = $gvars['bMargin'];
20524  $this->AutoPageBreak = $gvars['AutoPageBreak'];
20525  $this->PageBreakTrigger = $gvars['PageBreakTrigger'];
20526  $this->x = $gvars['x'];
20527  $this->y = $gvars['y'];
20528  $this->w = $gvars['w'];
20529  $this->h = $gvars['h'];
20530  $this->wPt = $gvars['wPt'];
20531  $this->hPt = $gvars['hPt'];
20532  $this->fwPt = $gvars['fwPt'];
20533  $this->fhPt = $gvars['fhPt'];
20534  $this->page = $gvars['page'];
20535  $this->current_column = $gvars['current_column'];
20536  $this->num_columns = $gvars['num_columns'];
20537  }
20538  $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
20539  if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
20540  $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
20541  }
20542  }
20543 
20548  protected function _outSaveGraphicsState() {
20549  $this->_out('q');
20550  }
20551 
20556  protected function _outRestoreGraphicsState() {
20557  $this->_out('Q');
20558  }
20559 
20568  protected function writeDiskCache($filename, $data, $append=false) {
20569  if ($append) {
20570  $fmode = 'ab+';
20571  } else {
20572  $fmode = 'wb+';
20573  }
20574  $f = @fopen($filename, $fmode);
20575  if (!$f) {
20576  $this->Error('Unable to write cache file: '.$filename);
20577  } else {
20578  fwrite($f, $data);
20579  fclose($f);
20580  }
20581  // update file length (needed for transactions)
20582  if (!isset($this->cache_file_length['_'.$filename])) {
20583  $this->cache_file_length['_'.$filename] = strlen($data);
20584  } else {
20585  $this->cache_file_length['_'.$filename] += strlen($data);
20586  }
20587  }
20588 
20596  protected function readDiskCache($filename) {
20597  return file_get_contents($filename);
20598  }
20599 
20606  protected function setBuffer($data) {
20607  $this->bufferlen += strlen($data);
20608  if ($this->diskcache) {
20609  if (!isset($this->buffer) OR TCPDF_STATIC::empty_string($this->buffer)) {
20610  $this->buffer = TCPDF_STATIC::getObjFilename('buf');
20611  }
20612  $this->writeDiskCache($this->buffer, $data, true);
20613  } else {
20614  $this->buffer .= $data;
20615  }
20616  }
20617 
20624  protected function replaceBuffer($data) {
20625  $this->bufferlen = strlen($data);
20626  if ($this->diskcache) {
20627  if (!isset($this->buffer) OR TCPDF_STATIC::empty_string($this->buffer)) {
20628  $this->buffer = TCPDF_STATIC::getObjFilename('buf');
20629  }
20630  $this->writeDiskCache($this->buffer, $data, false);
20631  } else {
20632  $this->buffer = $data;
20633  }
20634  }
20635 
20642  protected function getBuffer() {
20643  if ($this->diskcache) {
20644  return $this->readDiskCache($this->buffer);
20645  } else {
20646  return $this->buffer;
20647  }
20648  }
20649 
20658  protected function setPageBuffer($page, $data, $append=false) {
20659  if ($this->diskcache) {
20660  if (!isset($this->pages[$page])) {
20661  $this->pages[$page] = TCPDF_STATIC::getObjFilename('page');
20662  }
20663  $this->writeDiskCache($this->pages[$page], $data, $append);
20664  } else {
20665  if ($append) {
20666  $this->pages[$page] .= $data;
20667  } else {
20668  $this->pages[$page] = $data;
20669  }
20670  }
20671  if ($append AND isset($this->pagelen[$page])) {
20672  $this->pagelen[$page] += strlen($data);
20673  } else {
20674  $this->pagelen[$page] = strlen($data);
20675  }
20676  }
20677 
20685  protected function getPageBuffer($page) {
20686  if ($this->diskcache) {
20687  return $this->readDiskCache($this->pages[$page]);
20688  } elseif (isset($this->pages[$page])) {
20689  return $this->pages[$page];
20690  }
20691  return false;
20692  }
20693 
20702  protected function setImageBuffer($image, $data) {
20703  if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) {
20704  $this->imagekeys[$this->numimages] = $image;
20705  $data['i'] = $this->numimages;
20706  ++$this->numimages;
20707  }
20708  if ($this->diskcache) {
20709  if (!isset($this->images[$image])) {
20710  $this->images[$image] = TCPDF_STATIC::getObjFilename('img');
20711  }
20712  $this->writeDiskCache($this->images[$image], serialize($data));
20713  } else {
20714  $this->images[$image] = $data;
20715  }
20716  return $data['i'];
20717  }
20718 
20727  protected function setImageSubBuffer($image, $key, $data) {
20728  if (!isset($this->images[$image])) {
20729  $this->setImageBuffer($image, array());
20730  }
20731  if ($this->diskcache) {
20732  $tmpimg = $this->getImageBuffer($image);
20733  $tmpimg[$key] = $data;
20734  $this->writeDiskCache($this->images[$image], serialize($tmpimg));
20735  } else {
20736  $this->images[$image][$key] = $data;
20737  }
20738  }
20739 
20747  protected function getImageBuffer($image) {
20748  if ($this->diskcache AND isset($this->images[$image])) {
20749  return unserialize($this->readDiskCache($this->images[$image]));
20750  } elseif (isset($this->images[$image])) {
20751  return $this->images[$image];
20752  }
20753  return false;
20754  }
20755 
20763  protected function setFontBuffer($font, $data) {
20764  if ($this->diskcache) {
20765  if (!isset($this->fonts[$font])) {
20766  $this->fonts[$font] = TCPDF_STATIC::getObjFilename('font');
20767  }
20768  $this->writeDiskCache($this->fonts[$font], serialize($data));
20769  } else {
20770  $this->fonts[$font] = $data;
20771  }
20772  if (!in_array($font, $this->fontkeys)) {
20773  $this->fontkeys[] = $font;
20774  // store object ID for current font
20775  ++$this->n;
20776  $this->font_obj_ids[$font] = $this->n;
20777  $this->setFontSubBuffer($font, 'n', $this->n);
20778  }
20779  }
20780 
20789  protected function setFontSubBuffer($font, $key, $data) {
20790  if (!isset($this->fonts[$font])) {
20791  $this->setFontBuffer($font, array());
20792  }
20793  if ($this->diskcache) {
20794  $tmpfont = $this->getFontBuffer($font);
20795  $tmpfont[$key] = $data;
20796  $this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
20797  } else {
20798  $this->fonts[$font][$key] = $data;
20799  }
20800  }
20801 
20809  protected function getFontBuffer($font) {
20810  if ($this->diskcache AND isset($this->fonts[$font])) {
20811  return unserialize($this->readDiskCache($this->fonts[$font]));
20812  } elseif (isset($this->fonts[$font])) {
20813  return $this->fonts[$font];
20814  }
20815  return false;
20816  }
20817 
20826  public function movePage($frompage, $topage) {
20827  if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
20828  return false;
20829  }
20830  if ($frompage == $this->page) {
20831  // close the page before moving it
20832  $this->endPage();
20833  }
20834  // move all page-related states
20835  $tmppage = $this->getPageBuffer($frompage);
20836  $tmppagedim = $this->pagedim[$frompage];
20837  $tmppagelen = $this->pagelen[$frompage];
20838  $tmpintmrk = $this->intmrk[$frompage];
20839  $tmpbordermrk = $this->bordermrk[$frompage];
20840  $tmpcntmrk = $this->cntmrk[$frompage];
20841  $tmppageobjects = $this->pageobjects[$frompage];
20842  if (isset($this->footerpos[$frompage])) {
20843  $tmpfooterpos = $this->footerpos[$frompage];
20844  }
20845  if (isset($this->footerlen[$frompage])) {
20846  $tmpfooterlen = $this->footerlen[$frompage];
20847  }
20848  if (isset($this->transfmrk[$frompage])) {
20849  $tmptransfmrk = $this->transfmrk[$frompage];
20850  }
20851  if (isset($this->PageAnnots[$frompage])) {
20852  $tmpannots = $this->PageAnnots[$frompage];
20853  }
20854  if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
20855  for ($i = $frompage; $i > $topage; --$i) {
20856  if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) {
20857  --$this->pagegroups[$this->newpagegroup[$i]];
20858  break;
20859  }
20860  }
20861  for ($i = $topage; $i > 0; --$i) {
20862  if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) {
20863  ++$this->pagegroups[$this->newpagegroup[$i]];
20864  break;
20865  }
20866  }
20867  }
20868  for ($i = $frompage; $i > $topage; --$i) {
20869  $j = $i - 1;
20870  // shift pages down
20871  $this->setPageBuffer($i, $this->getPageBuffer($j));
20872  $this->pagedim[$i] = $this->pagedim[$j];
20873  $this->pagelen[$i] = $this->pagelen[$j];
20874  $this->intmrk[$i] = $this->intmrk[$j];
20875  $this->bordermrk[$i] = $this->bordermrk[$j];
20876  $this->cntmrk[$i] = $this->cntmrk[$j];
20877  $this->pageobjects[$i] = $this->pageobjects[$j];
20878  if (isset($this->footerpos[$j])) {
20879  $this->footerpos[$i] = $this->footerpos[$j];
20880  } elseif (isset($this->footerpos[$i])) {
20881  unset($this->footerpos[$i]);
20882  }
20883  if (isset($this->footerlen[$j])) {
20884  $this->footerlen[$i] = $this->footerlen[$j];
20885  } elseif (isset($this->footerlen[$i])) {
20886  unset($this->footerlen[$i]);
20887  }
20888  if (isset($this->transfmrk[$j])) {
20889  $this->transfmrk[$i] = $this->transfmrk[$j];
20890  } elseif (isset($this->transfmrk[$i])) {
20891  unset($this->transfmrk[$i]);
20892  }
20893  if (isset($this->PageAnnots[$j])) {
20894  $this->PageAnnots[$i] = $this->PageAnnots[$j];
20895  } elseif (isset($this->PageAnnots[$i])) {
20896  unset($this->PageAnnots[$i]);
20897  }
20898  if (isset($this->newpagegroup[$j])) {
20899  $this->newpagegroup[$i] = $this->newpagegroup[$j];
20900  unset($this->newpagegroup[$j]);
20901  }
20902  if ($this->currpagegroup == $j) {
20903  $this->currpagegroup = $i;
20904  }
20905  }
20906  $this->setPageBuffer($topage, $tmppage);
20907  $this->pagedim[$topage] = $tmppagedim;
20908  $this->pagelen[$topage] = $tmppagelen;
20909  $this->intmrk[$topage] = $tmpintmrk;
20910  $this->bordermrk[$topage] = $tmpbordermrk;
20911  $this->cntmrk[$topage] = $tmpcntmrk;
20912  $this->pageobjects[$topage] = $tmppageobjects;
20913  if (isset($tmpfooterpos)) {
20914  $this->footerpos[$topage] = $tmpfooterpos;
20915  } elseif (isset($this->footerpos[$topage])) {
20916  unset($this->footerpos[$topage]);
20917  }
20918  if (isset($tmpfooterlen)) {
20919  $this->footerlen[$topage] = $tmpfooterlen;
20920  } elseif (isset($this->footerlen[$topage])) {
20921  unset($this->footerlen[$topage]);
20922  }
20923  if (isset($tmptransfmrk)) {
20924  $this->transfmrk[$topage] = $tmptransfmrk;
20925  } elseif (isset($this->transfmrk[$topage])) {
20926  unset($this->transfmrk[$topage]);
20927  }
20928  if (isset($tmpannots)) {
20929  $this->PageAnnots[$topage] = $tmpannots;
20930  } elseif (isset($this->PageAnnots[$topage])) {
20931  unset($this->PageAnnots[$topage]);
20932  }
20933  // adjust outlines
20934  $tmpoutlines = $this->outlines;
20935  foreach ($tmpoutlines as $key => $outline) {
20936  if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
20937  $this->outlines[$key]['p'] = ($outline['p'] + 1);
20938  } elseif ($outline['p'] == $frompage) {
20939  $this->outlines[$key]['p'] = $topage;
20940  }
20941  }
20942  // adjust dests
20943  $tmpdests = $this->dests;
20944  foreach ($tmpdests as $key => $dest) {
20945  if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
20946  $this->dests[$key]['p'] = ($dest['p'] + 1);
20947  } elseif ($dest['p'] == $frompage) {
20948  $this->dests[$key]['p'] = $topage;
20949  }
20950  }
20951  // adjust links
20952  $tmplinks = $this->links;
20953  foreach ($tmplinks as $key => $link) {
20954  if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
20955  $this->links[$key][0] = ($link[0] + 1);
20956  } elseif ($link[0] == $frompage) {
20957  $this->links[$key][0] = $topage;
20958  }
20959  }
20960  // adjust javascript
20961  $jfrompage = $frompage;
20962  $jtopage = $topage;
20963  if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) {
20964  foreach($pamatch[0] as $pk => $pmatch) {
20965  $pagenum = intval($pamatch[3][$pk]) + 1;
20966  if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
20967  $newpage = ($pagenum + 1);
20968  } elseif ($pagenum == $jfrompage) {
20969  $newpage = $jtopage;
20970  } else {
20971  $newpage = $pagenum;
20972  }
20973  --$newpage;
20974  $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
20975  $this->javascript = str_replace($pmatch, $newjs, $this->javascript);
20976  }
20977  unset($pamatch);
20978  }
20979  // return to last page
20980  $this->lastPage(true);
20981  return true;
20982  }
20983 
20991  public function deletePage($page) {
20992  if (($page < 1) OR ($page > $this->numpages)) {
20993  return false;
20994  }
20995  // delete current page
20996  unset($this->pages[$page]);
20997  unset($this->pagedim[$page]);
20998  unset($this->pagelen[$page]);
20999  unset($this->intmrk[$page]);
21000  unset($this->bordermrk[$page]);
21001  unset($this->cntmrk[$page]);
21002  foreach ($this->pageobjects[$page] as $oid) {
21003  if (isset($this->offsets[$oid])){
21004  unset($this->offsets[$oid]);
21005  }
21006  }
21007  unset($this->pageobjects[$page]);
21008  if (isset($this->footerpos[$page])) {
21009  unset($this->footerpos[$page]);
21010  }
21011  if (isset($this->footerlen[$page])) {
21012  unset($this->footerlen[$page]);
21013  }
21014  if (isset($this->transfmrk[$page])) {
21015  unset($this->transfmrk[$page]);
21016  }
21017  if (isset($this->PageAnnots[$page])) {
21018  unset($this->PageAnnots[$page]);
21019  }
21020  if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
21021  for ($i = $page; $i > 0; --$i) {
21022  if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) {
21023  --$this->pagegroups[$this->newpagegroup[$i]];
21024  break;
21025  }
21026  }
21027  }
21028  if (isset($this->pageopen[$page])) {
21029  unset($this->pageopen[$page]);
21030  }
21031  if ($page < $this->numpages) {
21032  // update remaining pages
21033  for ($i = $page; $i < $this->numpages; ++$i) {
21034  $j = $i + 1;
21035  // shift pages
21036  $this->setPageBuffer($i, $this->getPageBuffer($j));
21037  $this->pagedim[$i] = $this->pagedim[$j];
21038  $this->pagelen[$i] = $this->pagelen[$j];
21039  $this->intmrk[$i] = $this->intmrk[$j];
21040  $this->bordermrk[$i] = $this->bordermrk[$j];
21041  $this->cntmrk[$i] = $this->cntmrk[$j];
21042  $this->pageobjects[$i] = $this->pageobjects[$j];
21043  if (isset($this->footerpos[$j])) {
21044  $this->footerpos[$i] = $this->footerpos[$j];
21045  } elseif (isset($this->footerpos[$i])) {
21046  unset($this->footerpos[$i]);
21047  }
21048  if (isset($this->footerlen[$j])) {
21049  $this->footerlen[$i] = $this->footerlen[$j];
21050  } elseif (isset($this->footerlen[$i])) {
21051  unset($this->footerlen[$i]);
21052  }
21053  if (isset($this->transfmrk[$j])) {
21054  $this->transfmrk[$i] = $this->transfmrk[$j];
21055  } elseif (isset($this->transfmrk[$i])) {
21056  unset($this->transfmrk[$i]);
21057  }
21058  if (isset($this->PageAnnots[$j])) {
21059  $this->PageAnnots[$i] = $this->PageAnnots[$j];
21060  } elseif (isset($this->PageAnnots[$i])) {
21061  unset($this->PageAnnots[$i]);
21062  }
21063  if (isset($this->newpagegroup[$j])) {
21064  $this->newpagegroup[$i] = $this->newpagegroup[$j];
21065  unset($this->newpagegroup[$j]);
21066  }
21067  if ($this->currpagegroup == $j) {
21068  $this->currpagegroup = $i;
21069  }
21070  if (isset($this->pageopen[$j])) {
21071  $this->pageopen[$i] = $this->pageopen[$j];
21072  } elseif (isset($this->pageopen[$i])) {
21073  unset($this->pageopen[$i]);
21074  }
21075  }
21076  // remove last page
21077  unset($this->pages[$this->numpages]);
21078  unset($this->pagedim[$this->numpages]);
21079  unset($this->pagelen[$this->numpages]);
21080  unset($this->intmrk[$this->numpages]);
21081  unset($this->bordermrk[$this->numpages]);
21082  unset($this->cntmrk[$this->numpages]);
21083  foreach ($this->pageobjects[$this->numpages] as $oid) {
21084  if (isset($this->offsets[$oid])){
21085  unset($this->offsets[$oid]);
21086  }
21087  }
21088  unset($this->pageobjects[$this->numpages]);
21089  if (isset($this->footerpos[$this->numpages])) {
21090  unset($this->footerpos[$this->numpages]);
21091  }
21092  if (isset($this->footerlen[$this->numpages])) {
21093  unset($this->footerlen[$this->numpages]);
21094  }
21095  if (isset($this->transfmrk[$this->numpages])) {
21096  unset($this->transfmrk[$this->numpages]);
21097  }
21098  if (isset($this->PageAnnots[$this->numpages])) {
21099  unset($this->PageAnnots[$this->numpages]);
21100  }
21101  if (isset($this->newpagegroup[$this->numpages])) {
21102  unset($this->newpagegroup[$this->numpages]);
21103  }
21104  if ($this->currpagegroup == $this->numpages) {
21105  $this->currpagegroup = ($this->numpages - 1);
21106  }
21107  if (isset($this->pagegroups[$this->numpages])) {
21108  unset($this->pagegroups[$this->numpages]);
21109  }
21110  if (isset($this->pageopen[$this->numpages])) {
21111  unset($this->pageopen[$this->numpages]);
21112  }
21113  }
21114  --$this->numpages;
21115  $this->page = $this->numpages;
21116  // adjust outlines
21117  $tmpoutlines = $this->outlines;
21118  foreach ($tmpoutlines as $key => $outline) {
21119  if ($outline['p'] > $page) {
21120  $this->outlines[$key]['p'] = $outline['p'] - 1;
21121  } elseif ($outline['p'] == $page) {
21122  unset($this->outlines[$key]);
21123  }
21124  }
21125  // adjust dests
21126  $tmpdests = $this->dests;
21127  foreach ($tmpdests as $key => $dest) {
21128  if ($dest['p'] > $page) {
21129  $this->dests[$key]['p'] = $dest['p'] - 1;
21130  } elseif ($dest['p'] == $page) {
21131  unset($this->dests[$key]);
21132  }
21133  }
21134  // adjust links
21135  $tmplinks = $this->links;
21136  foreach ($tmplinks as $key => $link) {
21137  if ($link[0] > $page) {
21138  $this->links[$key][0] = $link[0] - 1;
21139  } elseif ($link[0] == $page) {
21140  unset($this->links[$key]);
21141  }
21142  }
21143  // adjust javascript
21144  $jpage = $page;
21145  if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) {
21146  foreach($pamatch[0] as $pk => $pmatch) {
21147  $pagenum = intval($pamatch[3][$pk]) + 1;
21148  if ($pagenum >= $jpage) {
21149  $newpage = ($pagenum - 1);
21150  } elseif ($pagenum == $jpage) {
21151  $newpage = 1;
21152  } else {
21153  $newpage = $pagenum;
21154  }
21155  --$newpage;
21156  $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21157  $this->javascript = str_replace($pmatch, $newjs, $this->javascript);
21158  }
21159  unset($pamatch);
21160  }
21161  // return to last page
21162  if ($this->numpages > 0) {
21163  $this->lastPage(true);
21164  }
21165  return true;
21166  }
21167 
21175  public function copyPage($page=0) {
21176  if ($page == 0) {
21177  // default value
21178  $page = $this->page;
21179  }
21180  if (($page < 1) OR ($page > $this->numpages)) {
21181  return false;
21182  }
21183  // close the last page
21184  $this->endPage();
21185  // copy all page-related states
21186  ++$this->numpages;
21187  $this->page = $this->numpages;
21188  $this->setPageBuffer($this->page, $this->getPageBuffer($page));
21189  $this->pagedim[$this->page] = $this->pagedim[$page];
21190  $this->pagelen[$this->page] = $this->pagelen[$page];
21191  $this->intmrk[$this->page] = $this->intmrk[$page];
21192  $this->bordermrk[$this->page] = $this->bordermrk[$page];
21193  $this->cntmrk[$this->page] = $this->cntmrk[$page];
21194  $this->pageobjects[$this->page] = $this->pageobjects[$page];
21195  $this->pageopen[$this->page] = false;
21196  if (isset($this->footerpos[$page])) {
21197  $this->footerpos[$this->page] = $this->footerpos[$page];
21198  }
21199  if (isset($this->footerlen[$page])) {
21200  $this->footerlen[$this->page] = $this->footerlen[$page];
21201  }
21202  if (isset($this->transfmrk[$page])) {
21203  $this->transfmrk[$this->page] = $this->transfmrk[$page];
21204  }
21205  if (isset($this->PageAnnots[$page])) {
21206  $this->PageAnnots[$this->page] = $this->PageAnnots[$page];
21207  }
21208  if (isset($this->newpagegroup[$page])) {
21209  // start a new group
21210  $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1;
21211  $this->currpagegroup = $this->newpagegroup[$this->page];
21212  $this->pagegroups[$this->currpagegroup] = 1;
21213  } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
21214  ++$this->pagegroups[$this->currpagegroup];
21215  }
21216  // copy outlines
21217  $tmpoutlines = $this->outlines;
21218  foreach ($tmpoutlines as $key => $outline) {
21219  if ($outline['p'] == $page) {
21220  $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'x' => $outline['x'], 'y' => $outline['y'], 'p' => $this->page, 's' => $outline['s'], 'c' => $outline['c']);
21221  }
21222  }
21223  // copy links
21224  $tmplinks = $this->links;
21225  foreach ($tmplinks as $key => $link) {
21226  if ($link[0] == $page) {
21227  $this->links[] = array($this->page, $link[1]);
21228  }
21229  }
21230  // return to last page
21231  $this->lastPage(true);
21232  return true;
21233  }
21234 
21252  public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
21253  $fontsize = $this->FontSizePt;
21254  $fontfamily = $this->FontFamily;
21255  $fontstyle = $this->FontStyle;
21256  $w = $this->w - $this->lMargin - $this->rMargin;
21257  $spacer = $this->GetStringWidth(chr(32)) * 4;
21258  $lmargin = $this->lMargin;
21259  $rmargin = $this->rMargin;
21260  $x_start = $this->GetX();
21261  $page_first = $this->page;
21262  $current_page = $this->page;
21263  $page_fill_start = false;
21264  $page_fill_end = false;
21265  $current_column = $this->current_column;
21266  if (TCPDF_STATIC::empty_string($numbersfont)) {
21267  $numbersfont = $this->default_monospaced_font;
21268  }
21269  if (TCPDF_STATIC::empty_string($filler)) {
21270  $filler = ' ';
21271  }
21272  if (TCPDF_STATIC::empty_string($page)) {
21273  $gap = ' ';
21274  } else {
21275  $gap = '';
21276  if ($page < 1) {
21277  $page = 1;
21278  }
21279  }
21280  $this->SetFont($numbersfont, $fontstyle, $fontsize);
21281  $numwidth = $this->GetStringWidth('00000');
21282  $maxpage = 0; //used for pages on attached documents
21283  foreach ($this->outlines as $key => $outline) {
21284  // check for extra pages (used for attachments)
21285  if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) {
21286  $outline['p'] += ($this->page - $page_first);
21287  }
21288  if ($this->rtl) {
21289  $aligntext = 'R';
21290  $alignnum = 'L';
21291  } else {
21292  $aligntext = 'L';
21293  $alignnum = 'R';
21294  }
21295  if ($outline['l'] == 0) {
21296  $this->SetFont($fontfamily, $outline['s'].'B', $fontsize);
21297  } else {
21298  $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']);
21299  }
21300  $this->SetTextColorArray($outline['c']);
21301  // check for page break
21302  $this->checkPageBreak(2 * $this->getCellHeight($this->FontSize));
21303  // set margins and X position
21304  if (($this->page == $current_page) AND ($this->current_column == $current_column)) {
21305  $this->lMargin = $lmargin;
21306  $this->rMargin = $rmargin;
21307  } else {
21308  if ($this->current_column != $current_column) {
21309  if ($this->rtl) {
21310  $x_start = $this->w - $this->columns[$this->current_column]['x'];
21311  } else {
21312  $x_start = $this->columns[$this->current_column]['x'];
21313  }
21314  }
21315  $lmargin = $this->lMargin;
21316  $rmargin = $this->rMargin;
21317  $current_page = $this->page;
21318  $current_column = $this->current_column;
21319  }
21320  $this->SetX($x_start);
21321  $indent = ($spacer * $outline['l']);
21322  if ($this->rtl) {
21323  $this->x -= $indent;
21324  $this->rMargin = $this->w - $this->x;
21325  } else {
21326  $this->x += $indent;
21327  $this->lMargin = $this->x;
21328  }
21329  $link = $this->AddLink();
21330  $this->SetLink($link, $outline['y'], $outline['p']);
21331  // write the text
21332  if ($this->rtl) {
21333  $txt = ' '.$outline['t'];
21334  } else {
21335  $txt = $outline['t'].' ';
21336  }
21337  $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
21338  if ($this->rtl) {
21339  $tw = $this->x - $this->lMargin;
21340  } else {
21341  $tw = $this->w - $this->rMargin - $this->x;
21342  }
21343  $this->SetFont($numbersfont, $fontstyle, $fontsize);
21344  if (TCPDF_STATIC::empty_string($page)) {
21345  $pagenum = $outline['p'];
21346  } else {
21347  // placemark to be replaced with the correct number
21348  $pagenum = '{#'.($outline['p']).'}';
21349  if ($this->isUnicodeFont()) {
21350  $pagenum = '{'.$pagenum.'}';
21351  }
21352  $maxpage = max($maxpage, $outline['p']);
21353  }
21354  $fw = ($tw - $this->GetStringWidth($pagenum.$filler));
21355  $wfiller = $this->GetStringWidth($filler);
21356  if ($wfiller > 0) {
21357  $numfills = floor($fw / $wfiller);
21358  } else {
21359  $numfills = 0;
21360  }
21361  if ($numfills > 0) {
21362  $rowfill = str_repeat($filler, $numfills);
21363  } else {
21364  $rowfill = '';
21365  }
21366  if ($this->rtl) {
21367  $pagenum = $pagenum.$gap.$rowfill;
21368  } else {
21369  $pagenum = $rowfill.$gap.$pagenum;
21370  }
21371  // write the number
21372  $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
21373  }
21374  $page_last = $this->getPage();
21375  $numpages = ($page_last - $page_first + 1);
21376  // account for booklet mode
21377  if ($this->booklet) {
21378  // check if a blank page is required before TOC
21379  $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21380  $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21381  if ($page_fill_start) {
21382  // add a page at the end (to be moved before TOC)
21383  $this->addPage();
21384  ++$page_last;
21385  ++$numpages;
21386  }
21387  if ($page_fill_end) {
21388  // add a page at the end
21389  $this->addPage();
21390  ++$page_last;
21391  ++$numpages;
21392  }
21393  }
21394  $maxpage = max($maxpage, $page_last);
21395  if (!TCPDF_STATIC::empty_string($page)) {
21396  for ($p = $page_first; $p <= $page_last; ++$p) {
21397  // get page data
21398  $temppage = $this->getPageBuffer($p);
21399  for ($n = 1; $n <= $maxpage; ++$n) {
21400  // update page numbers
21401  $a = '{#'.$n.'}';
21402  // get page number aliases
21403  $pnalias = $this->getInternalPageNumberAliases($a);
21404  // calculate replacement number
21405  if (($n >= $page) AND ($n <= $this->numpages)) {
21406  $np = $n + $numpages;
21407  } else {
21408  $np = $n;
21409  }
21410  $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21411  $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21412  // replace aliases with numbers
21413  foreach ($pnalias['u'] as $u) {
21414  $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
21415  if ($this->rtl) {
21416  $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21417  } else {
21418  $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21419  }
21420  $temppage = str_replace($u, $nr, $temppage);
21421  }
21422  foreach ($pnalias['a'] as $a) {
21423  $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
21424  if ($this->rtl) {
21425  $nr = $na.' '.$sfill;
21426  } else {
21427  $nr = $sfill.' '.$na;
21428  }
21429  $temppage = str_replace($a, $nr, $temppage);
21430  }
21431  }
21432  // save changes
21433  $this->setPageBuffer($p, $temppage);
21434  }
21435  // move pages
21436  $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21437  if ($page_fill_start) {
21438  $this->movePage($page_last, $page_first);
21439  }
21440  for ($i = 0; $i < $numpages; ++$i) {
21441  $this->movePage($page_last, $page);
21442  }
21443  }
21444  }
21445 
21462  public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
21463  $filler = ' ';
21464  $prev_htmlLinkColorArray = $this->htmlLinkColorArray;
21465  $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle;
21466  // set new style for link
21467  $this->htmlLinkColorArray = array();
21468  $this->htmlLinkFontStyle = '';
21469  $page_first = $this->getPage();
21470  $page_fill_start = false;
21471  $page_fill_end = false;
21472  // get the font type used for numbers in each template
21473  $current_font = $this->FontFamily;
21474  foreach ($templates as $level => $html) {
21475  $dom = $this->getHtmlDomArray($html);
21476  foreach ($dom as $key => $value) {
21477  if ($value['value'] == '#TOC_PAGE_NUMBER#') {
21478  $this->SetFont($dom[($key - 1)]['fontname']);
21479  $templates['F'.$level] = $this->isUnicodeFont();
21480  }
21481  }
21482  }
21483  $this->SetFont($current_font);
21484  $maxpage = 0; //used for pages on attached documents
21485  foreach ($this->outlines as $key => $outline) {
21486  // get HTML template
21487  $row = $templates[$outline['l']];
21488  if (TCPDF_STATIC::empty_string($page)) {
21489  $pagenum = $outline['p'];
21490  } else {
21491  // placemark to be replaced with the correct number
21492  $pagenum = '{#'.($outline['p']).'}';
21493  if ($templates['F'.$outline['l']]) {
21494  $pagenum = '{'.$pagenum.'}';
21495  }
21496  $maxpage = max($maxpage, $outline['p']);
21497  }
21498  // replace templates with current values
21499  $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
21500  $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
21501  // add link to page
21502  $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
21503  // write bookmark entry
21504  $this->writeHTML($row, false, false, true, false, '');
21505  }
21506  // restore link styles
21507  $this->htmlLinkColorArray = $prev_htmlLinkColorArray;
21508  $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle;
21509  // move TOC page and replace numbers
21510  $page_last = $this->getPage();
21511  $numpages = ($page_last - $page_first + 1);
21512  // account for booklet mode
21513  if ($this->booklet) {
21514  // check if a blank page is required before TOC
21515  $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21516  $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21517  if ($page_fill_start) {
21518  // add a page at the end (to be moved before TOC)
21519  $this->addPage();
21520  ++$page_last;
21521  ++$numpages;
21522  }
21523  if ($page_fill_end) {
21524  // add a page at the end
21525  $this->addPage();
21526  ++$page_last;
21527  ++$numpages;
21528  }
21529  }
21530  $maxpage = max($maxpage, $page_last);
21531  if (!TCPDF_STATIC::empty_string($page)) {
21532  for ($p = $page_first; $p <= $page_last; ++$p) {
21533  // get page data
21534  $temppage = $this->getPageBuffer($p);
21535  for ($n = 1; $n <= $maxpage; ++$n) {
21536  // update page numbers
21537  $a = '{#'.$n.'}';
21538  // get page number aliases
21539  $pnalias = $this->getInternalPageNumberAliases($a);
21540  // calculate replacement number
21541  if ($n >= $page) {
21542  $np = $n + $numpages;
21543  } else {
21544  $np = $n;
21545  }
21546  $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21547  $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21548  // replace aliases with numbers
21549  foreach ($pnalias['u'] as $u) {
21550  if ($correct_align) {
21551  $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
21552  if ($this->rtl) {
21553  $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21554  } else {
21555  $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21556  }
21557  } else {
21558  $nr = $nu;
21559  }
21560  $temppage = str_replace($u, $nr, $temppage);
21561  }
21562  foreach ($pnalias['a'] as $a) {
21563  if ($correct_align) {
21564  $sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
21565  if ($this->rtl) {
21566  $nr = $na.' '.$sfill;
21567  } else {
21568  $nr = $sfill.' '.$na;
21569  }
21570  } else {
21571  $nr = $na;
21572  }
21573  $temppage = str_replace($a, $nr, $temppage);
21574  }
21575  }
21576  // save changes
21577  $this->setPageBuffer($p, $temppage);
21578  }
21579  // move pages
21580  $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21581  if ($page_fill_start) {
21582  $this->movePage($page_last, $page_first);
21583  }
21584  for ($i = 0; $i < $numpages; ++$i) {
21585  $this->movePage($page_last, $page);
21586  }
21587  }
21588  }
21589 
21595  public function startTransaction() {
21596  if (isset($this->objcopy)) {
21597  // remove previous copy
21598  $this->commitTransaction();
21599  }
21600  // record current page number and Y position
21601  $this->start_transaction_page = $this->page;
21602  $this->start_transaction_y = $this->y;
21603  // clone current object
21604  $this->objcopy = TCPDF_STATIC::objclone($this);
21605  }
21606 
21612  public function commitTransaction() {
21613  if (isset($this->objcopy)) {
21614  $this->objcopy->_destroy(true, true);
21615  unset($this->objcopy);
21616  }
21617  }
21618 
21626  public function rollbackTransaction($self=false) {
21627  if (isset($this->objcopy)) {
21628  if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
21629  // truncate files to previous values
21630  foreach ($this->objcopy->cache_file_length as $file => $length) {
21631  $file = substr($file, 1);
21632  $handle = fopen($file, 'r+');
21633  ftruncate($handle, $length);
21634  }
21635  }
21636  $this->_destroy(true, true);
21637  if ($self) {
21638  $objvars = get_object_vars($this->objcopy);
21639  foreach ($objvars as $key => $value) {
21640  $this->$key = $value;
21641  }
21642  }
21643  return $this->objcopy;
21644  }
21645  return $this;
21646  }
21647 
21648  // --- MULTI COLUMNS METHODS -----------------------
21649 
21658  public function setEqualColumns($numcols=0, $width=0, $y='') {
21659  $this->columns = array();
21660  if ($numcols < 2) {
21661  $numcols = 0;
21662  $this->columns = array();
21663  } else {
21664  // maximum column width
21665  $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
21666  if (($width == 0) OR ($width > $maxwidth)) {
21667  $width = $maxwidth;
21668  }
21669  if (TCPDF_STATIC::empty_string($y)) {
21670  $y = $this->y;
21671  }
21672  // space between columns
21673  $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
21674  // fill the columns array (with, space, starting Y position)
21675  for ($i = 0; $i < $numcols; ++$i) {
21676  $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
21677  }
21678  }
21679  $this->num_columns = $numcols;
21680  $this->current_column = 0;
21681  $this->column_start_page = $this->page;
21682  $this->selectColumn(0);
21683  }
21684 
21690  public function resetColumns() {
21691  $this->lMargin = $this->original_lMargin;
21692  $this->rMargin = $this->original_rMargin;
21693  $this->setEqualColumns();
21694  }
21695 
21703  public function setColumnsArray($columns) {
21704  $this->columns = $columns;
21705  $this->num_columns = count($columns);
21706  $this->current_column = 0;
21707  $this->column_start_page = $this->page;
21708  $this->selectColumn(0);
21709  }
21710 
21717  public function selectColumn($col='') {
21718  if (is_string($col)) {
21719  $col = $this->current_column;
21720  } elseif ($col >= $this->num_columns) {
21721  $col = 0;
21722  }
21723  $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
21724  $enable_thead = false;
21725  if ($this->num_columns > 1) {
21726  if ($col != $this->current_column) {
21727  // move Y pointer at the top of the column
21728  if ($this->column_start_page == $this->page) {
21729  $this->y = $this->columns[$col]['y'];
21730  } else {
21731  $this->y = $this->tMargin;
21732  }
21733  // Avoid to write table headers more than once
21734  if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) {
21735  $enable_thead = true;
21736  $this->maxselcol['page'] = $this->page;
21737  $this->maxselcol['column'] = $col;
21738  }
21739  }
21740  $xshift = $this->colxshift;
21741  // set X position of the current column by case
21742  $listindent = ($this->listindentlevel * $this->listindent);
21743  // calculate column X position
21744  $colpos = 0;
21745  for ($i = 0; $i < $col; ++$i) {
21746  $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']);
21747  }
21748  if ($this->rtl) {
21749  $x = $this->w - $this->original_rMargin - $colpos;
21750  $this->rMargin = ($this->w - $x + $listindent);
21751  $this->lMargin = ($x - $this->columns[$col]['w']);
21752  $this->x = $x - $listindent;
21753  } else {
21754  $x = $this->original_lMargin + $colpos;
21755  $this->lMargin = ($x + $listindent);
21756  $this->rMargin = ($this->w - $x - $this->columns[$col]['w']);
21757  $this->x = $x + $listindent;
21758  }
21759  $this->columns[$col]['x'] = $x;
21760  }
21761  $this->current_column = $col;
21762  // fix for HTML mode
21763  $this->newline = true;
21764  // print HTML table header (if any)
21765  if ((!TCPDF_STATIC::empty_string($this->thead)) AND (!$this->inthead)) {
21766  if ($enable_thead) {
21767  // print table header
21768  $this->writeHTML($this->thead, false, false, false, false, '');
21769  $this->y += $xshift['s']['V'];
21770  // store end of header position
21771  if (!isset($this->columns[$col]['th'])) {
21772  $this->columns[$col]['th'] = array();
21773  }
21774  $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y;
21775  $this->lasth = 0;
21776  } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) {
21777  $this->y = $this->columns[$col]['th']['\''.$this->page.'\''];
21778  }
21779  }
21780  // account for an html table cell over multiple columns
21781  if ($this->rtl) {
21782  $this->rMargin += $xshift['x'];
21783  $this->x -= ($xshift['x'] + $xshift['p']['R']);
21784  } else {
21785  $this->lMargin += $xshift['x'];
21786  $this->x += $xshift['x'] + $xshift['p']['L'];
21787  }
21788  }
21789 
21796  public function getColumn() {
21797  return $this->current_column;
21798  }
21799 
21806  public function getNumberOfColumns() {
21807  return $this->num_columns;
21808  }
21809 
21818  public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
21819  // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
21820  // convert text rendering parameters
21821  if ($stroke < 0) {
21822  $stroke = 0;
21823  }
21824  if ($fill === true) {
21825  if ($stroke > 0) {
21826  if ($clip === true) {
21827  // Fill, then stroke text and add to path for clipping
21828  $textrendermode = 6;
21829  } else {
21830  // Fill, then stroke text
21831  $textrendermode = 2;
21832  }
21833  $textstrokewidth = $stroke;
21834  } else {
21835  if ($clip === true) {
21836  // Fill text and add to path for clipping
21837  $textrendermode = 4;
21838  } else {
21839  // Fill text
21840  $textrendermode = 0;
21841  }
21842  }
21843  } else {
21844  if ($stroke > 0) {
21845  if ($clip === true) {
21846  // Stroke text and add to path for clipping
21847  $textrendermode = 5;
21848  } else {
21849  // Stroke text
21850  $textrendermode = 1;
21851  }
21852  $textstrokewidth = $stroke;
21853  } else {
21854  if ($clip === true) {
21855  // Add text to path for clipping
21856  $textrendermode = 7;
21857  } else {
21858  // Neither fill nor stroke text (invisible)
21859  $textrendermode = 3;
21860  }
21861  }
21862  }
21863  $this->textrendermode = $textrendermode;
21864  $this->textstrokewidth = $stroke;
21865  }
21866 
21873  public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) {
21874  if (isset($params['enabled'])) {
21875  $this->txtshadow['enabled'] = $params['enabled']?true:false;
21876  } else {
21877  $this->txtshadow['enabled'] = false;
21878  }
21879  if (isset($params['depth_w'])) {
21880  $this->txtshadow['depth_w'] = floatval($params['depth_w']);
21881  } else {
21882  $this->txtshadow['depth_w'] = 0;
21883  }
21884  if (isset($params['depth_h'])) {
21885  $this->txtshadow['depth_h'] = floatval($params['depth_h']);
21886  } else {
21887  $this->txtshadow['depth_h'] = 0;
21888  }
21889  if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) {
21890  $this->txtshadow['color'] = $params['color'];
21891  } else {
21892  $this->txtshadow['color'] = $this->strokecolor;
21893  }
21894  if (isset($params['opacity'])) {
21895  $this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity'])));
21896  } else {
21897  $this->txtshadow['opacity'] = 1;
21898  }
21899  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'))) {
21900  $this->txtshadow['blend_mode'] = $params['blend_mode'];
21901  } else {
21902  $this->txtshadow['blend_mode'] = 'Normal';
21903  }
21904  if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) {
21905  $this->txtshadow['enabled'] = false;
21906  }
21907  }
21908 
21915  public function getTextShadow() {
21916  return $this->txtshadow;
21917  }
21918 
21933  protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
21934  $hyphenword = array(); // hyphens positions
21935  $numchars = count($word);
21936  if ($numchars <= $charmin) {
21937  return $word;
21938  }
21939  $word_string = TCPDF_FONTS::UTF8ArrSubString($word, '', '', $this->isunicode);
21940  // some words will be returned as-is
21941  $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})(\]?)$/';
21942  if (preg_match($pattern, $word_string) > 0) {
21943  // email
21944  return $word;
21945  }
21946  $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})(\]?)$/';
21947  if (preg_match($pattern, $word_string) > 0) {
21948  // URL
21949  return $word;
21950  }
21951  if (isset($dictionary[$word_string])) {
21952  return TCPDF_FONTS::UTF8StringToArray($dictionary[$word_string], $this->isunicode, $this->CurrentFont);
21953  }
21954  // suround word with '_' characters
21955  $tmpword = array_merge(array(95), $word, array(95));
21956  $tmpnumchars = $numchars + 2;
21957  $maxpos = $tmpnumchars - $charmin;
21958  for ($pos = 0; $pos < $maxpos; ++$pos) {
21959  $imax = min(($tmpnumchars - $pos), $charmax);
21960  for ($i = $charmin; $i <= $imax; ++$i) {
21961  $subword = strtolower(TCPDF_FONTS::UTF8ArrSubString($tmpword, $pos, ($pos + $i), $this->isunicode));
21962  if (isset($patterns[$subword])) {
21963  $pattern = TCPDF_FONTS::UTF8StringToArray($patterns[$subword], $this->isunicode, $this->CurrentFont);
21964  $pattern_length = count($pattern);
21965  $digits = 1;
21966  for ($j = 0; $j < $pattern_length; ++$j) {
21967  // check if $pattern[$j] is a number
21968  if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
21969  if ($j == 0) {
21970  $zero = $pos - 1;
21971  } else {
21972  $zero = $pos + $j - $digits;
21973  }
21974  if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] != $pattern[$j])) {
21975  $hyphenword[$zero] = TCPDF_FONTS::unichr($pattern[$j], $this->isunicode);
21976  }
21977  ++$digits;
21978  }
21979  }
21980  }
21981  }
21982  }
21983  $inserted = 0;
21984  $maxpos = $numchars - $rightmin;
21985  for ($i = $leftmin; $i <= $maxpos; ++$i) {
21986  if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
21987  // 173 = soft hyphen character
21988  array_splice($word, $i + $inserted, 0, 173);
21989  ++$inserted;
21990  }
21991  }
21992  return $word;
21993  }
21994 
22009  public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22010  $text = $this->unhtmlentities($text);
22011  $word = array(); // last word
22012  $txtarr = array(); // text to be returned
22013  $intag = false; // true if we are inside an HTML tag
22014  if (!is_array($patterns)) {
22015  $patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patterns);
22016  }
22017  // get array of characters
22018  $unichars = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
22019  // for each char
22020  foreach ($unichars as $char) {
22021  if ((!$intag) AND TCPDF_FONT_DATA::$uni_type[$char] == 'L') {
22022  // letter character
22023  $word[] = $char;
22024  } else {
22025  // other type of character
22026  if (!TCPDF_STATIC::empty_string($word)) {
22027  // hypenate the word
22028  $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22029  $word = array();
22030  }
22031  $txtarr[] = $char;
22032  if (chr($char) == '<') {
22033  // we are inside an HTML tag
22034  $intag = true;
22035  } elseif ($intag AND (chr($char) == '>')) {
22036  // end of HTML tag
22037  $intag = false;
22038  }
22039  }
22040  }
22041  if (!TCPDF_STATIC::empty_string($word)) {
22042  // hypenate the word
22043  $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22044  }
22045  // convert char array to string and return
22046  return TCPDF_FONTS::UTF8ArrSubString($txtarr, '', '', $this->isunicode);
22047  }
22048 
22055  public function setRasterizeVectorImages($mode) {
22056  $this->rasterize_vector_images = $mode;
22057  }
22058 
22066  public function setFontSubsetting($enable=true) {
22067  if ($this->pdfa_mode) {
22068  $this->font_subsetting = false;
22069  } else {
22070  $this->font_subsetting = $enable ? true : false;
22071  }
22072  }
22073 
22081  public function getFontSubsetting() {
22082  return $this->font_subsetting;
22083  }
22084 
22094  public function stringLeftTrim($str, $replace='') {
22095  return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str);
22096  }
22097 
22107  public function stringRightTrim($str, $replace='') {
22108  return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str);
22109  }
22110 
22120  public function stringTrim($str, $replace='') {
22121  $str = $this->stringLeftTrim($str, $replace);
22122  $str = $this->stringRightTrim($str, $replace);
22123  return $str;
22124  }
22125 
22133  public function isUnicodeFont() {
22134  return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0'));
22135  }
22136 
22145  public function getFontFamilyName($fontfamily) {
22146  // remove spaces and symbols
22147  $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily));
22148  // extract all font names
22149  $fontslist = preg_split('/[,]/', $fontfamily);
22150  // find first valid font name
22151  foreach ($fontslist as $font) {
22152  // replace font variations
22153  $font = preg_replace('/italic$/', 'I', $font);
22154  $font = preg_replace('/oblique$/', 'I', $font);
22155  $font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
22156  // replace common family names and core fonts
22157  $pattern = array();
22158  $replacement = array();
22159  $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
22160  $replacement[] = 'times';
22161  $pattern[] = '/^sansserif/';
22162  $replacement[] = 'helvetica';
22163  $pattern[] = '/^monospace/';
22164  $replacement[] = 'courier';
22165  $font = preg_replace($pattern, $replacement, $font);
22166  if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) {
22167  return $font;
22168  }
22169  }
22170  // return current font as default
22171  return $this->CurrentFont['fontkey'];
22172  }
22173 
22188  public function startTemplate($w=0, $h=0, $group=false) {
22189  if ($this->inxobj) {
22190  // we are already inside an XObject template
22191  return false;
22192  }
22193  $this->inxobj = true;
22194  ++$this->n;
22195  // XObject ID
22196  $this->xobjid = 'XT'.$this->n;
22197  // object ID
22198  $this->xobjects[$this->xobjid] = array('n' => $this->n);
22199  // store current graphic state
22200  $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars();
22201  // initialize data
22202  $this->xobjects[$this->xobjid]['intmrk'] = 0;
22203  $this->xobjects[$this->xobjid]['transfmrk'] = array();
22204  $this->xobjects[$this->xobjid]['outdata'] = '';
22205  $this->xobjects[$this->xobjid]['xobjects'] = array();
22206  $this->xobjects[$this->xobjid]['images'] = array();
22207  $this->xobjects[$this->xobjid]['fonts'] = array();
22208  $this->xobjects[$this->xobjid]['annotations'] = array();
22209  $this->xobjects[$this->xobjid]['extgstates'] = array();
22210  $this->xobjects[$this->xobjid]['gradients'] = array();
22211  $this->xobjects[$this->xobjid]['spot_colors'] = array();
22212  // set new environment
22213  $this->num_columns = 1;
22214  $this->current_column = 0;
22215  $this->SetAutoPageBreak(false);
22216  if (($w === '') OR ($w <= 0)) {
22217  $w = $this->w - $this->lMargin - $this->rMargin;
22218  }
22219  if (($h === '') OR ($h <= 0)) {
22220  $h = $this->h - $this->tMargin - $this->bMargin;
22221  }
22222  $this->xobjects[$this->xobjid]['x'] = 0;
22223  $this->xobjects[$this->xobjid]['y'] = 0;
22224  $this->xobjects[$this->xobjid]['w'] = $w;
22225  $this->xobjects[$this->xobjid]['h'] = $h;
22226  $this->w = $w;
22227  $this->h = $h;
22228  $this->wPt = $this->w * $this->k;
22229  $this->hPt = $this->h * $this->k;
22230  $this->fwPt = $this->wPt;
22231  $this->fhPt = $this->hPt;
22232  $this->x = 0;
22233  $this->y = 0;
22234  $this->lMargin = 0;
22235  $this->rMargin = 0;
22236  $this->tMargin = 0;
22237  $this->bMargin = 0;
22238  // set group mode
22239  $this->xobjects[$this->xobjid]['group'] = $group;
22240  return $this->xobjid;
22241  }
22242 
22253  public function endTemplate() {
22254  if (!$this->inxobj) {
22255  // we are not inside a template
22256  return false;
22257  }
22258  $this->inxobj = false;
22259  // restore previous graphic state
22260  $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true);
22261  return $this->xobjid;
22262  }
22263 
22282  public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
22283  if ($this->state != 2) {
22284  return;
22285  }
22286  if (!isset($this->xobjects[$id])) {
22287  $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
22288  }
22289  if ($this->inxobj) {
22290  if ($id == $this->xobjid) {
22291  // close current template
22292  $this->endTemplate();
22293  } else {
22294  // use the template as resource for the template currently opened
22295  $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id];
22296  }
22297  }
22298  // set default values
22299  if ($x === '') {
22300  $x = $this->x;
22301  }
22302  if ($y === '') {
22303  $y = $this->y;
22304  }
22305  // check page for no-write regions and adapt page margins if necessary
22306  list($x, $y) = $this->checkPageRegions($h, $x, $y);
22307  $ow = $this->xobjects[$id]['w'];
22308  if ($ow <= 0) {
22309  $ow = 1;
22310  }
22311  $oh = $this->xobjects[$id]['h'];
22312  if ($oh <= 0) {
22313  $oh = 1;
22314  }
22315  // calculate template width and height on document
22316  if (($w <= 0) AND ($h <= 0)) {
22317  $w = $ow;
22318  $h = $oh;
22319  } elseif ($w <= 0) {
22320  $w = $h * $ow / $oh;
22321  } elseif ($h <= 0) {
22322  $h = $w * $oh / $ow;
22323  }
22324  // fit the template on available space
22325  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22326  // set page alignment
22327  $rb_y = $y + $h;
22328  // set alignment
22329  if ($this->rtl) {
22330  if ($palign == 'L') {
22331  $xt = $this->lMargin;
22332  } elseif ($palign == 'C') {
22333  $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22334  } elseif ($palign == 'R') {
22335  $xt = $this->w - $this->rMargin - $w;
22336  } else {
22337  $xt = $x - $w;
22338  }
22339  $rb_x = $xt;
22340  } else {
22341  if ($palign == 'L') {
22342  $xt = $this->lMargin;
22343  } elseif ($palign == 'C') {
22344  $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22345  } elseif ($palign == 'R') {
22346  $xt = $this->w - $this->rMargin - $w;
22347  } else {
22348  $xt = $x;
22349  }
22350  $rb_x = $xt + $w;
22351  }
22352  // print XObject Template + Transformation matrix
22353  $this->StartTransform();
22354  // translate and scale
22355  $sx = ($w / $ow);
22356  $sy = ($h / $oh);
22357  $tm = array();
22358  $tm[0] = $sx;
22359  $tm[1] = 0;
22360  $tm[2] = 0;
22361  $tm[3] = $sy;
22362  $tm[4] = $xt * $this->k;
22363  $tm[5] = ($this->h - $h - $y) * $this->k;
22364  $this->Transform($tm);
22365  // set object
22366  $this->_out('/'.$id.' Do');
22367  $this->StopTransform();
22368  // add annotations
22369  if (!empty($this->xobjects[$id]['annotations'])) {
22370  foreach ($this->xobjects[$id]['annotations'] as $annot) {
22371  // transform original coordinates
22372  $coordlt = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k)));
22373  $ax = ($coordlt[4] / $this->k);
22374  $ay = ($this->h - $h - ($coordlt[5] / $this->k));
22375  $coordrb = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k)));
22376  $aw = ($coordrb[4] / $this->k) - $ax;
22377  $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay;
22378  $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
22379  }
22380  }
22381  // set pointer to align the next text/objects
22382  switch($align) {
22383  case 'T': {
22384  $this->y = $y;
22385  $this->x = $rb_x;
22386  break;
22387  }
22388  case 'M': {
22389  $this->y = $y + round($h/2);
22390  $this->x = $rb_x;
22391  break;
22392  }
22393  case 'B': {
22394  $this->y = $rb_y;
22395  $this->x = $rb_x;
22396  break;
22397  }
22398  case 'N': {
22399  $this->SetY($rb_y);
22400  break;
22401  }
22402  default:{
22403  break;
22404  }
22405  }
22406  }
22407 
22415  public function setFontStretching($perc=100) {
22416  $this->font_stretching = $perc;
22417  }
22418 
22426  public function getFontStretching() {
22427  return $this->font_stretching;
22428  }
22429 
22437  public function setFontSpacing($spacing=0) {
22438  $this->font_spacing = $spacing;
22439  }
22440 
22448  public function getFontSpacing() {
22449  return $this->font_spacing;
22450  }
22451 
22460  public function getPageRegions() {
22461  return $this->page_regions;
22462  }
22463 
22475  public function setPageRegions($regions=array()) {
22476  // empty current regions array
22477  $this->page_regions = array();
22478  // add regions
22479  foreach ($regions as $data) {
22480  $this->addPageRegion($data);
22481  }
22482  }
22483 
22495  public function addPageRegion($region) {
22496  if (!isset($region['page']) OR empty($region['page'])) {
22497  $region['page'] = $this->page;
22498  }
22499  if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
22500  AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
22501  AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
22502  $this->page_regions[] = $region;
22503  }
22504  }
22505 
22514  public function removePageRegion($key) {
22515  if (isset($this->page_regions[$key])) {
22516  unset($this->page_regions[$key]);
22517  }
22518  }
22519 
22532  protected function checkPageRegions($h, $x, $y) {
22533  // set default values
22534  if ($x === '') {
22535  $x = $this->x;
22536  }
22537  if ($y === '') {
22538  $y = $this->y;
22539  }
22540  if (!$this->check_page_regions OR empty($this->page_regions)) {
22541  // no page regions defined
22542  return array($x, $y);
22543  }
22544  if (empty($h)) {
22545  $h = $this->getCellHeight($this->FontSize);
22546  }
22547  // check for page break
22548  if ($this->checkPageBreak($h, $y)) {
22549  // the content will be printed on a new page
22550  $x = $this->x;
22551  $y = $this->y;
22552  }
22553  if ($this->num_columns > 1) {
22554  if ($this->rtl) {
22555  $this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22556  } else {
22557  $this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22558  }
22559  } else {
22560  if ($this->rtl) {
22561  $this->lMargin = max($this->clMargin, $this->original_lMargin);
22562  } else {
22563  $this->rMargin = max($this->crMargin, $this->original_rMargin);
22564  }
22565  }
22566  // adjust coordinates and page margins
22567  foreach ($this->page_regions as $regid => $regdata) {
22568  if ($regdata['page'] == $this->page) {
22569  // check region boundaries
22570  if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
22571  // Y is inside the region
22572  $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
22573  $yt = max($y, $regdata['yt']);
22574  $yb = min(($yt + $h), $regdata['yb']);
22575  $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt'];
22576  $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt'];
22577  if ($regdata['side'] == 'L') { // left side
22578  $new_margin = max($xt, $xb);
22579  if ($this->lMargin < $new_margin) {
22580  if ($this->rtl) {
22581  // adjust left page margin
22582  $this->lMargin = max(0, $new_margin);
22583  }
22584  if ($x < $new_margin) {
22585  // adjust x position
22586  $x = $new_margin;
22587  if ($new_margin > ($this->w - $this->rMargin)) {
22588  // adjust y position
22589  $y = $regdata['yb'] - $h;
22590  }
22591  }
22592  }
22593  } elseif ($regdata['side'] == 'R') { // right side
22594  $new_margin = min($xt, $xb);
22595  if (($this->w - $this->rMargin) > $new_margin) {
22596  if (!$this->rtl) {
22597  // adjust right page margin
22598  $this->rMargin = max(0, ($this->w - $new_margin));
22599  }
22600  if ($x > $new_margin) {
22601  // adjust x position
22602  $x = $new_margin;
22603  if ($new_margin > $this->lMargin) {
22604  // adjust y position
22605  $y = $regdata['yb'] - $h;
22606  }
22607  }
22608  }
22609  }
22610  }
22611  }
22612  }
22613  return array($x, $y);
22614  }
22615 
22616  // --- SVG METHODS ---------------------------------------------------------
22617 
22635  public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
22636  if ($this->state != 2) {
22637  return;
22638  }
22639  // reseet SVG vars
22640  $this->svggradients = array();
22641  $this->svggradientid = 0;
22642  $this->svgdefsmode = false;
22643  $this->svgdefs = array();
22644  $this->svgclipmode = false;
22645  $this->svgclippaths = array();
22646  $this->svgcliptm = array();
22647  $this->svgclipid = 0;
22648  $this->svgtext = '';
22649  $this->svgtextmode = array();
22650  if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
22651  // convert SVG to raster image using GD or ImageMagick libraries
22652  return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22653  }
22654  if ($file{0} === '@') { // image from string
22655  $this->svgdir = '';
22656  $svgdata = substr($file, 1);
22657  } else { // SVG file
22658  $this->svgdir = dirname($file);
22659  $svgdata = TCPDF_STATIC::fileGetContents($file);
22660  }
22661  if ($svgdata === FALSE) {
22662  $this->Error('SVG file not found: '.$file);
22663  }
22664  if ($x === '') {
22665  $x = $this->x;
22666  }
22667  if ($y === '') {
22668  $y = $this->y;
22669  }
22670  // check page for no-write regions and adapt page margins if necessary
22671  list($x, $y) = $this->checkPageRegions($h, $x, $y);
22672  $k = $this->k;
22673  $ox = 0;
22674  $oy = 0;
22675  $ow = $w;
22676  $oh = $h;
22677  $aspect_ratio_align = 'xMidYMid';
22678  $aspect_ratio_ms = 'meet';
22679  $regs = array();
22680  // get original image width and height
22681  preg_match('/<svg([^>]*)>/si', $svgdata, $regs);
22682  if (isset($regs[1]) AND !empty($regs[1])) {
22683  $tmp = array();
22684  if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22685  $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22686  }
22687  $tmp = array();
22688  if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22689  $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22690  }
22691  $tmp = array();
22692  if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22693  $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22694  }
22695  $tmp = array();
22696  if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22697  $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22698  }
22699  $tmp = array();
22700  $view_box = array();
22701  if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
22702  if (count($tmp) == 5) {
22703  array_shift($tmp);
22704  foreach ($tmp as $key => $val) {
22705  $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
22706  }
22707  $ox = $view_box[0];
22708  $oy = $view_box[1];
22709  }
22710  // get aspect ratio
22711  $tmp = array();
22712  if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22713  $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
22714  switch (count($aspect_ratio)) {
22715  case 3: {
22716  $aspect_ratio_align = $aspect_ratio[1];
22717  $aspect_ratio_ms = $aspect_ratio[2];
22718  break;
22719  }
22720  case 2: {
22721  $aspect_ratio_align = $aspect_ratio[0];
22722  $aspect_ratio_ms = $aspect_ratio[1];
22723  break;
22724  }
22725  case 1: {
22726  $aspect_ratio_align = $aspect_ratio[0];
22727  $aspect_ratio_ms = 'meet';
22728  break;
22729  }
22730  }
22731  }
22732  }
22733  }
22734  if ($ow <= 0) {
22735  $ow = 1;
22736  }
22737  if ($oh <= 0) {
22738  $oh = 1;
22739  }
22740  // calculate image width and height on document
22741  if (($w <= 0) AND ($h <= 0)) {
22742  // convert image size to document unit
22743  $w = $ow;
22744  $h = $oh;
22745  } elseif ($w <= 0) {
22746  $w = $h * $ow / $oh;
22747  } elseif ($h <= 0) {
22748  $h = $w * $oh / $ow;
22749  }
22750  // fit the image on available space
22751  list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22752  if ($this->rasterize_vector_images) {
22753  // convert SVG to raster image using GD or ImageMagick libraries
22754  return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22755  }
22756  // set alignment
22757  $this->img_rb_y = $y + $h;
22758  // set alignment
22759  if ($this->rtl) {
22760  if ($palign == 'L') {
22761  $ximg = $this->lMargin;
22762  } elseif ($palign == 'C') {
22763  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22764  } elseif ($palign == 'R') {
22765  $ximg = $this->w - $this->rMargin - $w;
22766  } else {
22767  $ximg = $x - $w;
22768  }
22769  $this->img_rb_x = $ximg;
22770  } else {
22771  if ($palign == 'L') {
22772  $ximg = $this->lMargin;
22773  } elseif ($palign == 'C') {
22774  $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22775  } elseif ($palign == 'R') {
22776  $ximg = $this->w - $this->rMargin - $w;
22777  } else {
22778  $ximg = $x;
22779  }
22780  $this->img_rb_x = $ximg + $w;
22781  }
22782  // store current graphic vars
22783  $gvars = $this->getGraphicVars();
22784  // store SVG position and scale factors
22785  $svgoffset_x = ($ximg - $ox) * $this->k;
22786  $svgoffset_y = -($y - $oy) * $this->k;
22787  if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
22788  $ow = $view_box[2];
22789  $oh = $view_box[3];
22790  } else {
22791  if ($ow <= 0) {
22792  $ow = $w;
22793  }
22794  if ($oh <= 0) {
22795  $oh = $h;
22796  }
22797  }
22798  $svgscale_x = $w / $ow;
22799  $svgscale_y = $h / $oh;
22800  // scaling and alignment
22801  if ($aspect_ratio_align != 'none') {
22802  // store current scaling values
22803  $svgscale_old_x = $svgscale_x;
22804  $svgscale_old_y = $svgscale_y;
22805  // force uniform scaling
22806  if ($aspect_ratio_ms == 'slice') {
22807  // the entire viewport is covered by the viewBox
22808  if ($svgscale_x > $svgscale_y) {
22809  $svgscale_y = $svgscale_x;
22810  } elseif ($svgscale_x < $svgscale_y) {
22811  $svgscale_x = $svgscale_y;
22812  }
22813  } else { // meet
22814  // the entire viewBox is visible within the viewport
22815  if ($svgscale_x < $svgscale_y) {
22816  $svgscale_y = $svgscale_x;
22817  } elseif ($svgscale_x > $svgscale_y) {
22818  $svgscale_x = $svgscale_y;
22819  }
22820  }
22821  // correct X alignment
22822  switch (substr($aspect_ratio_align, 1, 3)) {
22823  case 'Min': {
22824  // do nothing
22825  break;
22826  }
22827  case 'Max': {
22828  $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
22829  break;
22830  }
22831  default:
22832  case 'Mid': {
22833  $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
22834  break;
22835  }
22836  }
22837  // correct Y alignment
22838  switch (substr($aspect_ratio_align, 5)) {
22839  case 'Min': {
22840  // do nothing
22841  break;
22842  }
22843  case 'Max': {
22844  $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
22845  break;
22846  }
22847  default:
22848  case 'Mid': {
22849  $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
22850  break;
22851  }
22852  }
22853  }
22854  // store current page break mode
22855  $page_break_mode = $this->AutoPageBreak;
22856  $page_break_margin = $this->getBreakMargin();
22857  $cell_padding = $this->cell_padding;
22858  $this->SetCellPadding(0);
22859  $this->SetAutoPageBreak(false);
22860  // save the current graphic state
22861  $this->_out('q'.$this->epsmarker);
22862  // set initial clipping mask
22863  $this->Rect($x, $y, $w, $h, 'CNZ', array(), array());
22864  // scale and translate
22865  $e = $ox * $this->k * (1 - $svgscale_x);
22866  $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
22867  $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y)));
22868  // creates a new XML parser to be used by the other XML functions
22869  $this->parser = xml_parser_create('UTF-8');
22870  // the following function allows to use parser inside object
22871  xml_set_object($this->parser, $this);
22872  // disable case-folding for this XML parser
22873  xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
22874  // sets the element handler functions for the XML parser
22875  xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler');
22876  // sets the character data handler function for the XML parser
22877  xml_set_character_data_handler($this->parser, 'segSVGContentHandler');
22878  // start parsing an XML document
22879  if (!xml_parse($this->parser, $svgdata)) {
22880  $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));
22881  $this->Error($error_message);
22882  }
22883  // free this XML parser
22884  xml_parser_free($this->parser);
22885  // restore previous graphic state
22886  $this->_out($this->epsmarker.'Q');
22887  // restore graphic vars
22888  $this->setGraphicVars($gvars);
22889  $this->lasth = $gvars['lasth'];
22890  if (!empty($border)) {
22891  $bx = $this->x;
22892  $by = $this->y;
22893  $this->x = $ximg;
22894  if ($this->rtl) {
22895  $this->x += $w;
22896  }
22897  $this->y = $y;
22898  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
22899  $this->x = $bx;
22900  $this->y = $by;
22901  }
22902  if ($link) {
22903  $this->Link($ximg, $y, $w, $h, $link, 0);
22904  }
22905  // set pointer to align the next text/objects
22906  switch($align) {
22907  case 'T':{
22908  $this->y = $y;
22909  $this->x = $this->img_rb_x;
22910  break;
22911  }
22912  case 'M':{
22913  $this->y = $y + round($h/2);
22914  $this->x = $this->img_rb_x;
22915  break;
22916  }
22917  case 'B':{
22918  $this->y = $this->img_rb_y;
22919  $this->x = $this->img_rb_x;
22920  break;
22921  }
22922  case 'N':{
22923  $this->SetY($this->img_rb_y);
22924  break;
22925  }
22926  default:{
22927  // restore pointer to starting position
22928  $this->x = $gvars['x'];
22929  $this->y = $gvars['y'];
22930  $this->page = $gvars['page'];
22931  $this->current_column = $gvars['current_column'];
22932  $this->tMargin = $gvars['tMargin'];
22933  $this->bMargin = $gvars['bMargin'];
22934  $this->w = $gvars['w'];
22935  $this->h = $gvars['h'];
22936  $this->wPt = $gvars['wPt'];
22937  $this->hPt = $gvars['hPt'];
22938  $this->fwPt = $gvars['fwPt'];
22939  $this->fhPt = $gvars['fhPt'];
22940  break;
22941  }
22942  }
22943  $this->endlinex = $this->img_rb_x;
22944  // restore page break
22945  $this->SetAutoPageBreak($page_break_mode, $page_break_margin);
22946  $this->cell_padding = $cell_padding;
22947  }
22948 
22956  protected function convertSVGtMatrix($tm) {
22957  $a = $tm[0];
22958  $b = -$tm[1];
22959  $c = -$tm[2];
22960  $d = $tm[3];
22961  $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
22962  $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
22963  $x = 0;
22964  $y = $this->h * $this->k;
22965  $e = ($x * (1 - $a)) - ($y * $c) + $e;
22966  $f = ($y * (1 - $d)) - ($x * $b) + $f;
22967  return array($a, $b, $c, $d, $e, $f);
22968  }
22969 
22976  protected function SVGTransform($tm) {
22977  $this->Transform($this->convertSVGtMatrix($tm));
22978  }
22979 
22995  protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
22996  if ($this->state != 2) {
22997  return;
22998  }
22999  $objstyle = '';
23000  $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
23001  if (!isset($svgstyle['opacity'])) {
23002  return $objstyle;
23003  }
23004  // clip-path
23005  $regs = array();
23006  if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
23007  $clip_path = $this->svgclippaths[$regs[1]];
23008  foreach ($clip_path as $cp) {
23009  $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
23010  }
23011  }
23012  // opacity
23013  if ($svgstyle['opacity'] != 1) {
23014  $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false);
23015  }
23016  // color
23017  $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors);
23018  $this->SetFillColorArray($fill_color);
23019  // text color
23020  $text_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors);
23021  $this->SetTextColorArray($text_color);
23022  // clip
23023  if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
23024  $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
23025  $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
23026  $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
23027  $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
23028  $cx = $x + $left;
23029  $cy = $y + $top;
23030  $cw = $w - $left - $right;
23031  $ch = $h - $top - $bottom;
23032  if ($svgstyle['clip-rule'] == 'evenodd') {
23033  $clip_rule = 'CNZ';
23034  } else {
23035  $clip_rule = 'CEO';
23036  }
23037  $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
23038  }
23039  // fill
23040  $regs = array();
23041  if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
23042  // gradient
23043  $gradient = $this->svggradients[$regs[1]];
23044  if (isset($gradient['xref'])) {
23045  // reference to another gradient definition
23046  $newgradient = $this->svggradients[$gradient['xref']];
23047  $newgradient['coords'] = $gradient['coords'];
23048  $newgradient['mode'] = $gradient['mode'];
23049  $newgradient['gradientUnits'] = $gradient['gradientUnits'];
23050  if (isset($gradient['gradientTransform'])) {
23051  $newgradient['gradientTransform'] = $gradient['gradientTransform'];
23052  }
23053  $gradient = $newgradient;
23054  }
23055  //save current Graphic State
23056  $this->_outSaveGraphicsState();
23057  //set clipping area
23058  if (!empty($clip_function) AND method_exists($this, $clip_function)) {
23059  $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
23060  if (is_array($bbox) AND (count($bbox) == 4)) {
23061  list($x, $y, $w, $h) = $bbox;
23062  }
23063  }
23064  if ($gradient['mode'] == 'measure') {
23065  if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
23066  $gtm = $gradient['gradientTransform'];
23067  // apply transformation matrix
23068  $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
23069  $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
23070  $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
23071  $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
23072  if (isset($gradient['coords'][4])) {
23073  $gradient['coords'][4] = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
23074  }
23075  $gradient['coords'][0] = $xa;
23076  $gradient['coords'][1] = $ya;
23077  $gradient['coords'][2] = $xb;
23078  $gradient['coords'][3] = $yb;
23079  }
23080  // convert SVG coordinates to user units
23081  $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
23082  $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
23083  $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
23084  $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
23085  if (isset($gradient['coords'][4])) {
23086  $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
23087  }
23088  if ($w <= $minlen) {
23089  $w = $minlen;
23090  }
23091  if ($h <= $minlen) {
23092  $h = $minlen;
23093  }
23094  // shift units
23095  if ($gradient['gradientUnits'] == 'objectBoundingBox') {
23096  // convert to SVG coordinate system
23097  $gradient['coords'][0] += $x;
23098  $gradient['coords'][1] += $y;
23099  $gradient['coords'][2] += $x;
23100  $gradient['coords'][3] += $y;
23101  }
23102  // calculate percentages
23103  $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w);
23104  $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h);
23105  $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w);
23106  $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h);
23107  if (isset($gradient['coords'][4])) {
23108  $gradient['coords'][4] /= $w;
23109  }
23110  } elseif ($gradient['mode'] == 'percentage') {
23111  foreach($gradient['coords'] as $key => $val) {
23112  $gradient['coords'][$key] = (intval($val) / 100);
23113  if ($val < 0) {
23114  $gradient['coords'][$key] = 0;
23115  } elseif ($val > 1) {
23116  $gradient['coords'][$key] = 1;
23117  }
23118  }
23119  }
23120  if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
23121  // single color (no shading)
23122  $gradient['coords'][0] = 1;
23123  $gradient['coords'][1] = 0;
23124  $gradient['coords'][2] = 0.999;
23125  $gradient['coords'][3] = 0;
23126  }
23127  // swap Y coordinates
23128  $tmp = $gradient['coords'][1];
23129  $gradient['coords'][1] = $gradient['coords'][3];
23130  $gradient['coords'][3] = $tmp;
23131  // set transformation map for gradient
23132  if ($gradient['type'] == 3) {
23133  // circular gradient
23134  $cy = $this->h - $y - ($gradient['coords'][1] * ($w + $h));
23135  $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($w * $this->k), ($x * $this->k), ($cy * $this->k)));
23136  } else {
23137  $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), (($this->h - ($y + $h)) * $this->k)));
23138  }
23139  if (count($gradient['stops']) > 1) {
23140  $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
23141  }
23142  } elseif ($svgstyle['fill'] != 'none') {
23143  $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors);
23144  if ($svgstyle['fill-opacity'] != 1) {
23145  $this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false);
23146  }
23147  $this->SetFillColorArray($fill_color);
23148  if ($svgstyle['fill-rule'] == 'evenodd') {
23149  $objstyle .= 'F*';
23150  } else {
23151  $objstyle .= 'F';
23152  }
23153  }
23154  // stroke
23155  if ($svgstyle['stroke'] != 'none') {
23156  if ($svgstyle['stroke-opacity'] != 1) {
23157  $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false);
23158  }
23159  $stroke_style = array(
23160  'color' => TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors),
23161  'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
23162  'cap' => $svgstyle['stroke-linecap'],
23163  'join' => $svgstyle['stroke-linejoin']
23164  );
23165  if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
23166  $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
23167  }
23168  $this->SetLineStyle($stroke_style);
23169  $objstyle .= 'D';
23170  }
23171  // font
23172  $regs = array();
23173  if (!empty($svgstyle['font'])) {
23174  if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
23175  $font_family = $this->getFontFamilyName($regs[1]);
23176  } else {
23177  $font_family = $svgstyle['font-family'];
23178  }
23179  if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23180  $font_size = trim($regs[1]);
23181  } else {
23182  $font_size = $svgstyle['font-size'];
23183  }
23184  if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23185  $font_style = trim($regs[1]);
23186  } else {
23187  $font_style = $svgstyle['font-style'];
23188  }
23189  if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23190  $font_weight = trim($regs[1]);
23191  } else {
23192  $font_weight = $svgstyle['font-weight'];
23193  }
23194  if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23195  $font_stretch = trim($regs[1]);
23196  } else {
23197  $font_stretch = $svgstyle['font-stretch'];
23198  }
23199  if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23200  $font_spacing = trim($regs[1]);
23201  } else {
23202  $font_spacing = $svgstyle['letter-spacing'];
23203  }
23204  } else {
23205  $font_family = $this->getFontFamilyName($svgstyle['font-family']);
23206  $font_size = $svgstyle['font-size'];
23207  $font_style = $svgstyle['font-style'];
23208  $font_weight = $svgstyle['font-weight'];
23209  $font_stretch = $svgstyle['font-stretch'];
23210  $font_spacing = $svgstyle['letter-spacing'];
23211  }
23212  $font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit);
23213  $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
23214  $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
23215  switch ($font_style) {
23216  case 'italic': {
23217  $font_style = 'I';
23218  break;
23219  }
23220  case 'oblique': {
23221  $font_style = 'I';
23222  break;
23223  }
23224  default:
23225  case 'normal': {
23226  $font_style = '';
23227  break;
23228  }
23229  }
23230  switch ($font_weight) {
23231  case 'bold':
23232  case 'bolder': {
23233  $font_style .= 'B';
23234  break;
23235  }
23236  }
23237  switch ($svgstyle['text-decoration']) {
23238  case 'underline': {
23239  $font_style .= 'U';
23240  break;
23241  }
23242  case 'overline': {
23243  $font_style .= 'O';
23244  break;
23245  }
23246  case 'line-through': {
23247  $font_style .= 'D';
23248  break;
23249  }
23250  default:
23251  case 'none': {
23252  break;
23253  }
23254  }
23255  $this->SetFont($font_family, $font_style, $font_size);
23256  $this->setFontStretching($font_stretch);
23257  $this->setFontSpacing($font_spacing);
23258  return $objstyle;
23259  }
23260 
23279  protected function SVGPath($d, $style='') {
23280  if ($this->state != 2) {
23281  return;
23282  }
23283  // set fill/stroke style
23284  $op = TCPDF_STATIC::getPathPaintOperator($style, '');
23285  if (empty($op)) {
23286  return;
23287  }
23288  $paths = array();
23289  $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
23290  preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER);
23291  $x = 0;
23292  $y = 0;
23293  $x1 = 0;
23294  $y1 = 0;
23295  $x2 = 0;
23296  $y2 = 0;
23297  $xmin = 2147483647;
23298  $xmax = 0;
23299  $ymin = 2147483647;
23300  $ymax = 0;
23301  $relcoord = false;
23302  $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
23303  $firstcmd = true; // used to print first point
23304  // draw curve pieces
23305  foreach ($paths as $key => $val) {
23306  // get curve type
23307  $cmd = trim($val[1]);
23308  if (strtolower($cmd) == $cmd) {
23309  // use relative coordinated instead of absolute
23310  $relcoord = true;
23311  $xoffset = $x;
23312  $yoffset = $y;
23313  } else {
23314  $relcoord = false;
23315  $xoffset = 0;
23316  $yoffset = 0;
23317  }
23318  $params = array();
23319  if (isset($val[2])) {
23320  // get curve parameters
23321  $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
23322  $params = array();
23323  foreach ($rawparams as $ck => $cp) {
23324  $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
23325  if (abs($params[$ck]) < $minlen) {
23326  // aproximate little values to zero
23327  $params[$ck] = 0;
23328  }
23329  }
23330  }
23331  // store current origin point
23332  $x0 = $x;
23333  $y0 = $y;
23334  switch (strtoupper($cmd)) {
23335  case 'M': { // moveto
23336  foreach ($params as $ck => $cp) {
23337  if (($ck % 2) == 0) {
23338  $x = $cp + $xoffset;
23339  } else {
23340  $y = $cp + $yoffset;
23341  if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23342  if ($ck == 1) {
23343  $this->_outPoint($x, $y);
23344  $firstcmd = false;
23345  } else {
23346  $this->_outLine($x, $y);
23347  }
23348  $x0 = $x;
23349  $y0 = $y;
23350  }
23351  $xmin = min($xmin, $x);
23352  $ymin = min($ymin, $y);
23353  $xmax = max($xmax, $x);
23354  $ymax = max($ymax, $y);
23355  if ($relcoord) {
23356  $xoffset = $x;
23357  $yoffset = $y;
23358  }
23359  }
23360  }
23361  break;
23362  }
23363  case 'L': { // lineto
23364  foreach ($params as $ck => $cp) {
23365  if (($ck % 2) == 0) {
23366  $x = $cp + $xoffset;
23367  } else {
23368  $y = $cp + $yoffset;
23369  if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23370  $this->_outLine($x, $y);
23371  $x0 = $x;
23372  $y0 = $y;
23373  }
23374  $xmin = min($xmin, $x);
23375  $ymin = min($ymin, $y);
23376  $xmax = max($xmax, $x);
23377  $ymax = max($ymax, $y);
23378  if ($relcoord) {
23379  $xoffset = $x;
23380  $yoffset = $y;
23381  }
23382  }
23383  }
23384  break;
23385  }
23386  case 'H': { // horizontal lineto
23387  foreach ($params as $ck => $cp) {
23388  $x = $cp + $xoffset;
23389  if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23390  $this->_outLine($x, $y);
23391  $x0 = $x;
23392  $y0 = $y;
23393  }
23394  $xmin = min($xmin, $x);
23395  $xmax = max($xmax, $x);
23396  if ($relcoord) {
23397  $xoffset = $x;
23398  }
23399  }
23400  break;
23401  }
23402  case 'V': { // vertical lineto
23403  foreach ($params as $ck => $cp) {
23404  $y = $cp + $yoffset;
23405  if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23406  $this->_outLine($x, $y);
23407  $x0 = $x;
23408  $y0 = $y;
23409  }
23410  $ymin = min($ymin, $y);
23411  $ymax = max($ymax, $y);
23412  if ($relcoord) {
23413  $yoffset = $y;
23414  }
23415  }
23416  break;
23417  }
23418  case 'C': { // curveto
23419  foreach ($params as $ck => $cp) {
23420  $params[$ck] = $cp;
23421  if ((($ck + 1) % 6) == 0) {
23422  $x1 = $params[($ck - 5)] + $xoffset;
23423  $y1 = $params[($ck - 4)] + $yoffset;
23424  $x2 = $params[($ck - 3)] + $xoffset;
23425  $y2 = $params[($ck - 2)] + $yoffset;
23426  $x = $params[($ck - 1)] + $xoffset;
23427  $y = $params[($ck)] + $yoffset;
23428  $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23429  $xmin = min($xmin, $x, $x1, $x2);
23430  $ymin = min($ymin, $y, $y1, $y2);
23431  $xmax = max($xmax, $x, $x1, $x2);
23432  $ymax = max($ymax, $y, $y1, $y2);
23433  if ($relcoord) {
23434  $xoffset = $x;
23435  $yoffset = $y;
23436  }
23437  }
23438  }
23439  break;
23440  }
23441  case 'S': { // shorthand/smooth curveto
23442  foreach ($params as $ck => $cp) {
23443  $params[$ck] = $cp;
23444  if ((($ck + 1) % 4) == 0) {
23445  if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
23446  $x1 = (2 * $x) - $x2;
23447  $y1 = (2 * $y) - $y2;
23448  } else {
23449  $x1 = $x;
23450  $y1 = $y;
23451  }
23452  $x2 = $params[($ck - 3)] + $xoffset;
23453  $y2 = $params[($ck - 2)] + $yoffset;
23454  $x = $params[($ck - 1)] + $xoffset;
23455  $y = $params[($ck)] + $yoffset;
23456  $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23457  $xmin = min($xmin, $x, $x1, $x2);
23458  $ymin = min($ymin, $y, $y1, $y2);
23459  $xmax = max($xmax, $x, $x1, $x2);
23460  $ymax = max($ymax, $y, $y1, $y2);
23461  if ($relcoord) {
23462  $xoffset = $x;
23463  $yoffset = $y;
23464  }
23465  }
23466  }
23467  break;
23468  }
23469  case 'Q': { // quadratic Bezier curveto
23470  foreach ($params as $ck => $cp) {
23471  $params[$ck] = $cp;
23472  if ((($ck + 1) % 4) == 0) {
23473  // convert quadratic points to cubic points
23474  $x1 = $params[($ck - 3)] + $xoffset;
23475  $y1 = $params[($ck - 2)] + $yoffset;
23476  $xa = ($x + (2 * $x1)) / 3;
23477  $ya = ($y + (2 * $y1)) / 3;
23478  $x = $params[($ck - 1)] + $xoffset;
23479  $y = $params[($ck)] + $yoffset;
23480  $xb = ($x + (2 * $x1)) / 3;
23481  $yb = ($y + (2 * $y1)) / 3;
23482  $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23483  $xmin = min($xmin, $x, $xa, $xb);
23484  $ymin = min($ymin, $y, $ya, $yb);
23485  $xmax = max($xmax, $x, $xa, $xb);
23486  $ymax = max($ymax, $y, $ya, $yb);
23487  if ($relcoord) {
23488  $xoffset = $x;
23489  $yoffset = $y;
23490  }
23491  }
23492  }
23493  break;
23494  }
23495  case 'T': { // shorthand/smooth quadratic Bezier curveto
23496  foreach ($params as $ck => $cp) {
23497  $params[$ck] = $cp;
23498  if (($ck % 2) != 0) {
23499  if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
23500  $x1 = (2 * $x) - $x1;
23501  $y1 = (2 * $y) - $y1;
23502  } else {
23503  $x1 = $x;
23504  $y1 = $y;
23505  }
23506  // convert quadratic points to cubic points
23507  $xa = ($x + (2 * $x1)) / 3;
23508  $ya = ($y + (2 * $y1)) / 3;
23509  $x = $params[($ck - 1)] + $xoffset;
23510  $y = $params[($ck)] + $yoffset;
23511  $xb = ($x + (2 * $x1)) / 3;
23512  $yb = ($y + (2 * $y1)) / 3;
23513  $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23514  $xmin = min($xmin, $x, $xa, $xb);
23515  $ymin = min($ymin, $y, $ya, $yb);
23516  $xmax = max($xmax, $x, $xa, $xb);
23517  $ymax = max($ymax, $y, $ya, $yb);
23518  if ($relcoord) {
23519  $xoffset = $x;
23520  $yoffset = $y;
23521  }
23522  }
23523  }
23524  break;
23525  }
23526  case 'A': { // elliptical arc
23527  foreach ($params as $ck => $cp) {
23528  $params[$ck] = $cp;
23529  if ((($ck + 1) % 7) == 0) {
23530  $x0 = $x;
23531  $y0 = $y;
23532  $rx = abs($params[($ck - 6)]);
23533  $ry = abs($params[($ck - 5)]);
23534  $ang = -$rawparams[($ck - 4)];
23535  $angle = deg2rad($ang);
23536  $fa = $rawparams[($ck - 3)]; // large-arc-flag
23537  $fs = $rawparams[($ck - 2)]; // sweep-flag
23538  $x = $params[($ck - 1)] + $xoffset;
23539  $y = $params[$ck] + $yoffset;
23540  if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
23541  // endpoints are almost identical
23542  $xmin = min($xmin, $x);
23543  $ymin = min($ymin, $y);
23544  $xmax = max($xmax, $x);
23545  $ymax = max($ymax, $y);
23546  } else {
23547  $cos_ang = cos($angle);
23548  $sin_ang = sin($angle);
23549  $a = (($x0 - $x) / 2);
23550  $b = (($y0 - $y) / 2);
23551  $xa = ($a * $cos_ang) - ($b * $sin_ang);
23552  $ya = ($a * $sin_ang) + ($b * $cos_ang);
23553  $rx2 = $rx * $rx;
23554  $ry2 = $ry * $ry;
23555  $xa2 = $xa * $xa;
23556  $ya2 = $ya * $ya;
23557  $delta = ($xa2 / $rx2) + ($ya2 / $ry2);
23558  if ($delta > 1) {
23559  $rx *= sqrt($delta);
23560  $ry *= sqrt($delta);
23561  $rx2 = $rx * $rx;
23562  $ry2 = $ry * $ry;
23563  }
23564  $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
23565  if ($numerator < 0) {
23566  $root = 0;
23567  } else {
23568  $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
23569  }
23570  if ($fa == $fs){
23571  $root *= -1;
23572  }
23573  $cax = $root * (($rx * $ya) / $ry);
23574  $cay = -$root * (($ry * $xa) / $rx);
23575  // coordinates of ellipse center
23576  $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
23577  $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
23578  // get angles
23579  $angs = TCPDF_STATIC::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
23580  $dang = TCPDF_STATIC::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
23581  if (($fs == 0) AND ($dang > 0)) {
23582  $dang -= (2 * M_PI);
23583  } elseif (($fs == 1) AND ($dang < 0)) {
23584  $dang += (2 * M_PI);
23585  }
23586  $angf = $angs - $dang;
23587  if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
23588  // reverse angles
23589  $tmp = $angs;
23590  $angs = $angf;
23591  $angf = $tmp;
23592  }
23593  $angs = round(rad2deg($angs), 6);
23594  $angf = round(rad2deg($angf), 6);
23595  // covent angles to positive values
23596  if (($angs < 0) AND ($angf < 0)) {
23597  $angs += 360;
23598  $angf += 360;
23599  }
23600  $pie = false;
23601  if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
23602  $pie = true;
23603  }
23604  list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
23605  $xmin = min($xmin, $x, $axmin);
23606  $ymin = min($ymin, $y, $aymin);
23607  $xmax = max($xmax, $x, $axmax);
23608  $ymax = max($ymax, $y, $aymax);
23609  }
23610  if ($relcoord) {
23611  $xoffset = $x;
23612  $yoffset = $y;
23613  }
23614  }
23615  }
23616  break;
23617  }
23618  case 'Z': {
23619  $this->_out('h');
23620  break;
23621  }
23622  }
23623  $firstcmd = false;
23624  } // end foreach
23625  if (!empty($op)) {
23626  $this->_out($op);
23627  }
23628  return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
23629  }
23630 
23641  protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
23642  // check if we are in clip mode
23643  if ($this->svgclipmode) {
23644  $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]);
23645  return;
23646  }
23647  if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
23648  if (isset($attribs['id'])) {
23649  $attribs['child_elements'] = array();
23650  $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23651  return;
23652  }
23653  if (end($this->svgdefs) !== FALSE) {
23654  $last_svgdefs_id = key($this->svgdefs);
23655  if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
23656  $attribs['id'] = 'DF_'.(count($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements']) + 1);
23657  $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23658  return;
23659  }
23660  }
23661  return;
23662  }
23663  $clipping = false;
23664  if ($parser == 'clip-path') {
23665  // set clipping mode
23666  $clipping = true;
23667  }
23668  // get styling properties
23669  $prev_svgstyle = $this->svgstyles[max(0,(count($this->svgstyles) - 1))]; // previous style
23670  $svgstyle = $this->svgstyles[0]; // set default style
23671  if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) {
23672  // default fill attribute for clipping
23673  $attribs['fill'] = 'none';
23674  }
23675  if (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) {
23676  // fix style for regular expression
23677  $attribs['style'] = ';'.$attribs['style'];
23678  }
23679  foreach ($prev_svgstyle as $key => $val) {
23680  if (in_array($key, TCPDF_IMAGES::$svginheritprop)) {
23681  // inherit previous value
23682  $svgstyle[$key] = $val;
23683  }
23684  if (isset($attribs[$key]) AND !TCPDF_STATIC::empty_string($attribs[$key])) {
23685  // specific attribute settings
23686  if ($attribs[$key] == 'inherit') {
23687  $svgstyle[$key] = $val;
23688  } else {
23689  $svgstyle[$key] = $attribs[$key];
23690  }
23691  } elseif (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) {
23692  // CSS style syntax
23693  $attrval = array();
23694  if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
23695  if ($attrval[1] == 'inherit') {
23696  $svgstyle[$key] = $val;
23697  } else {
23698  $svgstyle[$key] = $attrval[1];
23699  }
23700  }
23701  }
23702  }
23703  // transformation matrix
23704  if (!empty($ctm)) {
23705  $tm = $ctm;
23706  } else {
23707  $tm = array(1,0,0,1,0,0);
23708  }
23709  if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
23711  }
23712  $svgstyle['transfmatrix'] = $tm;
23713  $invisible = false;
23714  if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
23715  // the current graphics element is invisible (nothing is painted)
23716  $invisible = true;
23717  }
23718  // process tag
23719  switch($name) {
23720  case 'defs': {
23721  $this->svgdefsmode = true;
23722  break;
23723  }
23724  // clipPath
23725  case 'clipPath': {
23726  if ($invisible) {
23727  break;
23728  }
23729  $this->svgclipmode = true;
23730  if (!isset($attribs['id'])) {
23731  $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1);
23732  }
23733  $this->svgclipid = $attribs['id'];
23734  $this->svgclippaths[$this->svgclipid] = array();
23735  $this->svgcliptm[$this->svgclipid] = $tm;
23736  break;
23737  }
23738  case 'svg': {
23739  // start of SVG object
23740  break;
23741  }
23742  case 'g': {
23743  // group together related graphics elements
23744  array_push($this->svgstyles, $svgstyle);
23745  $this->StartTransform();
23746  $x = (isset($attribs['x'])?$attribs['x']:0);
23747  $y = (isset($attribs['y'])?$attribs['y']:0);
23748  $w = 1;//(isset($attribs['width'])?$attribs['width']:1);
23749  $h = 1;//(isset($attribs['height'])?$attribs['height']:1);
23750  $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
23751  $this->SVGTransform($tm);
23752  $this->setSVGStyles($svgstyle, $prev_svgstyle);
23753  break;
23754  }
23755  case 'linearGradient': {
23756  if ($this->pdfa_mode) {
23757  break;
23758  }
23759  if (!isset($attribs['id'])) {
23760  $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
23761  }
23762  $this->svggradientid = $attribs['id'];
23763  $this->svggradients[$this->svggradientid] = array();
23764  $this->svggradients[$this->svggradientid]['type'] = 2;
23765  $this->svggradients[$this->svggradientid]['stops'] = array();
23766  if (isset($attribs['gradientUnits'])) {
23767  $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
23768  } else {
23769  $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
23770  }
23771  //$attribs['spreadMethod']
23772  if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2'])))
23773  OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%'))
23774  OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%'))
23775  OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%'))
23776  OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) {
23777  $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
23778  } else {
23779  $this->svggradients[$this->svggradientid]['mode'] = 'measure';
23780  }
23781  $x1 = (isset($attribs['x1'])?$attribs['x1']:'0');
23782  $y1 = (isset($attribs['y1'])?$attribs['y1']:'0');
23783  $x2 = (isset($attribs['x2'])?$attribs['x2']:'100');
23784  $y2 = (isset($attribs['y2'])?$attribs['y2']:'0');
23785  if (isset($attribs['gradientTransform'])) {
23786  $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
23787  }
23788  $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
23789  if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23790  // gradient is defined on another place
23791  $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
23792  }
23793  break;
23794  }
23795  case 'radialGradient': {
23796  if ($this->pdfa_mode) {
23797  break;
23798  }
23799  if (!isset($attribs['id'])) {
23800  $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
23801  }
23802  $this->svggradientid = $attribs['id'];
23803  $this->svggradients[$this->svggradientid] = array();
23804  $this->svggradients[$this->svggradientid]['type'] = 3;
23805  $this->svggradients[$this->svggradientid]['stops'] = array();
23806  if (isset($attribs['gradientUnits'])) {
23807  $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
23808  } else {
23809  $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
23810  }
23811  //$attribs['spreadMethod']
23812  if (((!isset($attribs['cx'])) AND (!isset($attribs['cy'])))
23813  OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%'))
23814  OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')) )) {
23815  $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
23816  } else {
23817  $this->svggradients[$this->svggradientid]['mode'] = 'measure';
23818  }
23819  $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5);
23820  $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5);
23821  $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx);
23822  $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy);
23823  $r = (isset($attribs['r']) ? $attribs['r'] : 0.5);
23824  if (isset($attribs['gradientTransform'])) {
23825  $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
23826  }
23827  $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
23828  if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23829  // gradient is defined on another place
23830  $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
23831  }
23832  break;
23833  }
23834  case 'stop': {
23835  // gradient stops
23836  if (substr($attribs['offset'], -1) == '%') {
23837  $offset = floatval(substr($attribs['offset'], -1)) / 100;
23838  } else {
23839  $offset = floatval($attribs['offset']);
23840  if ($offset > 1) {
23841  $offset /= 100;
23842  }
23843  }
23844  $stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors):'black';
23845  $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
23846  $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
23847  break;
23848  }
23849  // paths
23850  case 'path': {
23851  if ($invisible) {
23852  break;
23853  }
23854  if (isset($attribs['d'])) {
23855  $d = trim($attribs['d']);
23856  if (!empty($d)) {
23857  $x = (isset($attribs['x'])?$attribs['x']:0);
23858  $y = (isset($attribs['y'])?$attribs['y']:0);
23859  $w = (isset($attribs['width'])?$attribs['width']:1);
23860  $h = (isset($attribs['height'])?$attribs['height']:1);
23861  $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
23862  if ($clipping) {
23863  $this->SVGTransform($tm);
23864  $this->SVGPath($d, 'CNZ');
23865  } else {
23866  $this->StartTransform();
23867  $this->SVGTransform($tm);
23868  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ'));
23869  if (!empty($obstyle)) {
23870  $this->SVGPath($d, $obstyle);
23871  }
23872  $this->StopTransform();
23873  }
23874  }
23875  }
23876  break;
23877  }
23878  // shapes
23879  case 'rect': {
23880  if ($invisible) {
23881  break;
23882  }
23883  $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
23884  $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
23885  $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
23886  $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
23887  $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
23888  $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
23889  if ($clipping) {
23890  $this->SVGTransform($tm);
23891  $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
23892  } else {
23893  $this->StartTransform();
23894  $this->SVGTransform($tm);
23895  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
23896  if (!empty($obstyle)) {
23897  $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
23898  }
23899  $this->StopTransform();
23900  }
23901  break;
23902  }
23903  case 'circle': {
23904  if ($invisible) {
23905  break;
23906  }
23907  $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0);
23908  $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
23909  $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
23910  $x = ($cx - $r);
23911  $y = ($cy - $r);
23912  $w = (2 * $r);
23913  $h = $w;
23914  if ($clipping) {
23915  $this->SVGTransform($tm);
23916  $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
23917  } else {
23918  $this->StartTransform();
23919  $this->SVGTransform($tm);
23920  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
23921  if (!empty($obstyle)) {
23922  $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
23923  }
23924  $this->StopTransform();
23925  }
23926  break;
23927  }
23928  case 'ellipse': {
23929  if ($invisible) {
23930  break;
23931  }
23932  $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0);
23933  $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0);
23934  $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
23935  $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
23936  $x = ($cx - $rx);
23937  $y = ($cy - $ry);
23938  $w = (2 * $rx);
23939  $h = (2 * $ry);
23940  if ($clipping) {
23941  $this->SVGTransform($tm);
23942  $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
23943  } else {
23944  $this->StartTransform();
23945  $this->SVGTransform($tm);
23946  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
23947  if (!empty($obstyle)) {
23948  $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
23949  }
23950  $this->StopTransform();
23951  }
23952  break;
23953  }
23954  case 'line': {
23955  if ($invisible) {
23956  break;
23957  }
23958  $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
23959  $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
23960  $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
23961  $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
23962  $x = $x1;
23963  $y = $y1;
23964  $w = abs($x2 - $x1);
23965  $h = abs($y2 - $y1);
23966  if (!$clipping) {
23967  $this->StartTransform();
23968  $this->SVGTransform($tm);
23969  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
23970  $this->Line($x1, $y1, $x2, $y2);
23971  $this->StopTransform();
23972  }
23973  break;
23974  }
23975  case 'polyline':
23976  case 'polygon': {
23977  if ($invisible) {
23978  break;
23979  }
23980  $points = (isset($attribs['points'])?$attribs['points']:'0 0');
23981  $points = trim($points);
23982  // note that point may use a complex syntax not covered here
23983  $points = preg_split('/[\,\s]+/si', $points);
23984  if (count($points) < 4) {
23985  break;
23986  }
23987  $p = array();
23988  $xmin = 2147483647;
23989  $xmax = 0;
23990  $ymin = 2147483647;
23991  $ymax = 0;
23992  foreach ($points as $key => $val) {
23993  $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
23994  if (($key % 2) == 0) {
23995  // X coordinate
23996  $xmin = min($xmin, $p[$key]);
23997  $xmax = max($xmax, $p[$key]);
23998  } else {
23999  // Y coordinate
24000  $ymin = min($ymin, $p[$key]);
24001  $ymax = max($ymax, $p[$key]);
24002  }
24003  }
24004  $x = $xmin;
24005  $y = $ymin;
24006  $w = ($xmax - $xmin);
24007  $h = ($ymax - $ymin);
24008  if ($name == 'polyline') {
24009  $this->StartTransform();
24010  $this->SVGTransform($tm);
24011  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
24012  if (!empty($obstyle)) {
24013  $this->PolyLine($p, $obstyle, array(), array());
24014  }
24015  $this->StopTransform();
24016  } else { // polygon
24017  if ($clipping) {
24018  $this->SVGTransform($tm);
24019  $this->Polygon($p, 'CNZ', array(), array(), true);
24020  } else {
24021  $this->StartTransform();
24022  $this->SVGTransform($tm);
24023  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
24024  if (!empty($obstyle)) {
24025  $this->Polygon($p, $obstyle, array(), array(), true);
24026  }
24027  $this->StopTransform();
24028  }
24029  }
24030  break;
24031  }
24032  // image
24033  case 'image': {
24034  if ($invisible) {
24035  break;
24036  }
24037  if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
24038  break;
24039  }
24040  $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24041  $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24042  $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24043  $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24044  $img = $attribs['xlink:href'];
24045  if (!$clipping) {
24046  $this->StartTransform();
24047  $this->SVGTransform($tm);
24048  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
24049  if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
24050  // embedded image encoded as base64
24051  $img = '@'.base64_decode(substr($img, strlen($m[0])));
24052  } else {
24053  // fix image path
24054  if (!TCPDF_STATIC::empty_string($this->svgdir) AND (($img{0} == '.') OR (basename($img) == $img))) {
24055  // replace relative path with full server path
24056  $img = $this->svgdir.'/'.$img;
24057  }
24058  if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
24059  $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
24060  if (($findroot === false) OR ($findroot > 1)) {
24061  if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
24062  $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
24063  } else {
24064  $img = $_SERVER['DOCUMENT_ROOT'].$img;
24065  }
24066  }
24067  }
24068  $img = urldecode($img);
24069  $testscrtype = @parse_url($img);
24070  if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
24071  // convert URL to server path
24072  $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
24073  }
24074  }
24075  // get image type
24076  $imgtype = TCPDF_IMAGES::getImageFileType($img);
24077  if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
24078  $this->ImageEps($img, $x, $y, $w, $h);
24079  } elseif ($imgtype == 'svg') {
24080  $this->ImageSVG($img, $x, $y, $w, $h);
24081  } else {
24082  $this->Image($img, $x, $y, $w, $h);
24083  }
24084  $this->StopTransform();
24085  }
24086  break;
24087  }
24088  // text
24089  case 'text':
24090  case 'tspan': {
24091  // only basic support - advanced features must be implemented
24092  $this->svgtextmode['invisible'] = $invisible;
24093  if ($invisible) {
24094  break;
24095  }
24096  array_push($this->svgstyles, $svgstyle);
24097  if (isset($attribs['x'])) {
24098  $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false);
24099  } elseif ($name == 'tspan') {
24100  $x = $this->x;
24101  } else {
24102  $x = 0;
24103  }
24104  if (isset($attribs['dx'])) {
24105  $x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false);
24106  }
24107  if (isset($attribs['y'])) {
24108  $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false);
24109  } elseif ($name == 'tspan') {
24110  $y = $this->y;
24111  } else {
24112  $y = 0;
24113  }
24114  if (isset($attribs['dy'])) {
24115  $y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false);
24116  }
24117  $svgstyle['text-color'] = $svgstyle['fill'];
24118  $this->svgtext = '';
24119  if (isset($svgstyle['text-anchor'])) {
24120  $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor'];
24121  } else {
24122  $this->svgtextmode['text-anchor'] = 'start';
24123  }
24124  if (isset($svgstyle['direction'])) {
24125  if ($svgstyle['direction'] == 'rtl') {
24126  $this->svgtextmode['rtl'] = true;
24127  } else {
24128  $this->svgtextmode['rtl'] = false;
24129  }
24130  } else {
24131  $this->svgtextmode['rtl'] = false;
24132  }
24133  if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
24134  $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false);
24135  } else {
24136  $this->svgtextmode['stroke'] = false;
24137  }
24138  $this->StartTransform();
24139  $this->SVGTransform($tm);
24140  $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
24141  $this->x = $x;
24142  $this->y = $y;
24143  break;
24144  }
24145  // use
24146  case 'use': {
24147  if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24148  $svgdefid = substr($attribs['xlink:href'], 1);
24149  if (isset($this->svgdefs[$svgdefid])) {
24150  $use = $this->svgdefs[$svgdefid];
24151  if (isset($attribs['xlink:href'])) {
24152  unset($attribs['xlink:href']);
24153  }
24154  if (isset($attribs['id'])) {
24155  unset($attribs['id']);
24156  }
24157  if (isset($use['attribs']['x']) AND isset($attribs['x'])) {
24158  $attribs['x'] += $use['attribs']['x'];
24159  }
24160  if (isset($use['attribs']['y']) AND isset($attribs['y'])) {
24161  $attribs['y'] += $use['attribs']['y'];
24162  }
24163  if (empty($attribs['style'])) {
24164  $attribs['style'] = '';
24165  }
24166  if (!empty($use['attribs']['style'])) {
24167  // merge styles
24168  $attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']);
24169  }
24170  $attribs = array_merge($use['attribs'], $attribs);
24171  $this->startSVGElementHandler('use-tag', $use['name'], $attribs);
24172  return;
24173  }
24174  }
24175  break;
24176  }
24177  default: {
24178  break;
24179  }
24180  } // end of switch
24181  // process child elements
24182  if (!empty($attribs['child_elements'])) {
24183  $child_elements = $attribs['child_elements'];
24184  unset($attribs['child_elements']);
24185  foreach($child_elements as $child_element) {
24186  if (empty($child_element['attribs']['closing_tag'])) {
24187  $this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']);
24188  } else {
24189  if (isset($child_element['attribs']['content'])) {
24190  $this->svgtext = $child_element['attribs']['content'];
24191  }
24192  $this->endSVGElementHandler('child-tag', $child_element['name']);
24193  }
24194  }
24195  }
24196  }
24197 
24206  protected function endSVGElementHandler($parser, $name) {
24207  if ($this->svgdefsmode AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {;
24208  if (end($this->svgdefs) !== FALSE) {
24209  $last_svgdefs_id = key($this->svgdefs);
24210  if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
24211  foreach($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) {
24212  if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) {
24213  $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24214  return;
24215  }
24216  }
24217  if ($this->svgdefs[$last_svgdefs_id]['name'] == $name) {
24218  $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24219  return;
24220  }
24221  }
24222  }
24223  return;
24224  }
24225  switch($name) {
24226  case 'defs': {
24227  $this->svgdefsmode = false;
24228  break;
24229  }
24230  // clipPath
24231  case 'clipPath': {
24232  $this->svgclipmode = false;
24233  break;
24234  }
24235  case 'g': {
24236  // ungroup: remove last style from array
24237  array_pop($this->svgstyles);
24238  $this->StopTransform();
24239  break;
24240  }
24241  case 'text':
24242  case 'tspan': {
24243  if ($this->svgtextmode['invisible']) {
24244  // This implementation must be fixed to following the rule:
24245  // 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.
24246  break;
24247  }
24248  // print text
24249  $text = $this->svgtext;
24250  //$text = $this->stringTrim($text);
24251  $textlen = $this->GetStringWidth($text);
24252  if ($this->svgtextmode['text-anchor'] != 'start') {
24253  // check if string is RTL text
24254  if ($this->svgtextmode['text-anchor'] == 'end') {
24255  if ($this->svgtextmode['rtl']) {
24256  $this->x += $textlen;
24257  } else {
24258  $this->x -= $textlen;
24259  }
24260  } elseif ($this->svgtextmode['text-anchor'] == 'middle') {
24261  if ($this->svgtextmode['rtl']) {
24262  $this->x += ($textlen / 2);
24263  } else {
24264  $this->x -= ($textlen / 2);
24265  }
24266  }
24267  }
24268  $textrendermode = $this->textrendermode;
24269  $textstrokewidth = $this->textstrokewidth;
24270  $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false);
24271  if ($name == 'text') {
24272  // store current coordinates
24273  $tmpx = $this->x;
24274  $tmpy = $this->y;
24275  }
24276  $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
24277  if ($name == 'text') {
24278  // restore coordinates
24279  $this->x = $tmpx;
24280  $this->y = $tmpy;
24281  }
24282  // restore previous rendering mode
24283  $this->textrendermode = $textrendermode;
24284  $this->textstrokewidth = $textstrokewidth;
24285  $this->svgtext = '';
24286  $this->StopTransform();
24287  if (!$this->svgdefsmode) {
24288  array_pop($this->svgstyles);
24289  }
24290  break;
24291  }
24292  default: {
24293  break;
24294  }
24295  }
24296  }
24297 
24306  protected function segSVGContentHandler($parser, $data) {
24307  $this->svgtext .= $data;
24308  }
24309 
24310  // --- END SVG METHODS -----------------------------------------------------
24311 
24312 } // END OF TCPDF CLASS
24313 
24314 //============================================================+
24315 // END OF FILE
24316 //============================================================+




Korrekturen, Hinweise und Ergänzungen

Bitte scheuen Sie sich nicht und melden Sie, was auf dieser Seite sachlich falsch oder irreführend ist, was ergänzt werden sollte, was fehlt usw. Dazu bitte oben aus dem Menü Seite den Eintrag Support Forum wählen. Es ist eine kostenlose Anmeldung erforderlich, um Anmerkungen zu posten. Unpassende Postings, Spam usw. werden kommentarlos entfernt.