ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
tcpdf.php
Go to the documentation of this file.
1<?php
2//============================================================+
3// File name : tcpdf.php
4// Version : 6.2.12
5// Begin : 2002-08-03
6// Last Update : 2015-06-18
7// Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
8// License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
9// -------------------------------------------------------------------
10// Copyright (C) 2002-2015 Nicola Asuni - Tecnick.com LTD
11//
12// This file is part of TCPDF software library.
13//
14// TCPDF is free software: you can redistribute it and/or modify it
15// under the terms of the GNU Lesser General Public License as
16// published by the Free Software Foundation, either version 3 of the
17// License, or (at your option) any later version.
18//
19// TCPDF is distributed in the hope that it will be useful, but
20// WITHOUT ANY WARRANTY; without even the implied warranty of
21// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22// See the GNU Lesser General Public License for more details.
23//
24// You should have received a copy of the License
25// along with TCPDF. If not, see
26// <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>.
27//
28// See LICENSE.TXT file for more information.
29// -------------------------------------------------------------------
30//
31// Description :
32// This is a PHP class for generating PDF documents without requiring external extensions.
33//
34// NOTE:
35// This class was originally derived in 2002 from the Public
36// Domain FPDF class by Olivier Plathey (http://www.fpdf.org),
37// but now is almost entirely rewritten and contains thousands of
38// new lines of code and hundreds new features.
39//
40// Main features:
41// * no external libraries are required for the basic functions;
42// * all standard page formats, custom page formats, custom margins and units of measure;
43// * UTF-8 Unicode and Right-To-Left languages;
44// * TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;
45// * font subsetting;
46// * methods to publish some XHTML + CSS code, Javascript and Forms;
47// * images, graphic (geometric figures) and transformation methods;
48// * supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)
49// * 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extension, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417;
50// * JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;
51// * automatic page header and footer management;
52// * document encryption up to 256 bit and digital signature certifications;
53// * transactions to UNDO commands;
54// * PDF annotations, including links, text and file attachments;
55// * text rendering modes (fill, stroke and clipping);
56// * multiple columns mode;
57// * no-write page regions;
58// * bookmarks, named destinations and table of content;
59// * text hyphenation;
60// * text stretching and spacing (tracking);
61// * automatic page break, line break and text alignments including justification;
62// * automatic page numbering and page groups;
63// * move and delete pages;
64// * page compression (requires php-zlib extension);
65// * XOBject Templates;
66// * Layers and object visibility.
67// * PDF/A-1b support
68//============================================================+
69
110// TCPDF configuration
111require_once(dirname(__FILE__).'/tcpdf_autoconfig.php');
112// TCPDF static font methods and data
113require_once(dirname(__FILE__).'/include/tcpdf_font_data.php');
114// TCPDF static font methods and data
115require_once(dirname(__FILE__).'/include/tcpdf_fonts.php');
116// TCPDF static color methods and data
117require_once(dirname(__FILE__).'/include/tcpdf_colors.php');
118// TCPDF static image methods and data
119require_once(dirname(__FILE__).'/include/tcpdf_images.php');
120// TCPDF static methods and data
121require_once(dirname(__FILE__).'/include/tcpdf_static.php');
122
123// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
124
134class 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
191
196 protected $pagedim = array();
197
202 protected $k;
203
208 protected $fwPt;
209
214 protected $fhPt;
215
220 protected $wPt;
221
226 protected $hPt;
227
232 protected $w;
233
238 protected $h;
239
244 protected $lMargin;
245
250 protected $rMargin;
251
256 protected $clMargin;
257
262 protected $crMargin;
263
268 protected $tMargin;
269
274 protected $bMargin;
275
281 protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
282
288 protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
289
294 protected $x;
295
300 protected $y;
301
306 protected $lasth;
307
312 protected $LineWidth;
313
318 protected $CoreFonts;
319
324 protected $fonts = array();
325
330 protected $FontFiles = array();
331
336 protected $diffs = array();
337
342 protected $images = array();
343
348 protected $svg_tag_depth = 0;
349
354 protected $PageAnnots = array();
355
360 protected $links = array();
361
366 protected $FontFamily;
367
372 protected $FontStyle;
373
379 protected $FontAscent;
380
386 protected $FontDescent;
387
392 protected $underline;
393
398 protected $overline;
399
404 protected $CurrentFont;
405
410 protected $FontSizePt;
411
416 protected $FontSize;
417
422 protected $DrawColor;
423
428 protected $FillColor;
429
434 protected $TextColor;
435
440 protected $ColorFlag;
441
446 protected $AutoPageBreak;
447
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
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
589
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
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
823
829 protected $file_id;
830
831 // --- bookmark ---
832
838 protected $outlines = array();
839
845 protected $OutlineRoot;
846
847 // --- javascript and form ---
848
854 protected $javascript = '';
855
861 protected $n_js;
862
868 protected $linethrough;
869
875 protected $ur = array();
876
882 protected $dpi = 72;
883
889 protected $newpagegroup = array();
890
896 protected $pagegroups = array();
897
903 protected $currpagegroup = 0;
904
910 protected $extgstates;
911
917 protected $jpeg_quality;
918
925
932
938 protected $PageMode;
939
945 protected $gradients = array();
946
952 protected $intmrk = array();
953
959 protected $bordermrk = array();
960
966 protected $emptypagemrk = array();
967
973 protected $cntmrk = array();
974
980 protected $footerpos = array();
981
987 protected $footerlen = array();
988
994 protected $newline = true;
995
1001 protected $endlinex = 0;
1002
1008 protected $linestyleWidth = '';
1009
1015 protected $linestyleCap = '0 J';
1016
1022 protected $linestyleJoin = '0 j';
1023
1029 protected $linestyleDash = '[] 0 d';
1030
1036 protected $openMarkedContent = false;
1037
1043 protected $htmlvspace = 0;
1044
1050 protected $spot_colors = array();
1051
1057 protected $lisymbol = '';
1058
1064 protected $epsmarker = 'x#!#EPS#!#x';
1065
1071 protected $transfmatrix = array();
1072
1078 protected $transfmatrix_key = 0;
1079
1085 protected $booklet = false;
1086
1092 protected $feps = 0.005;
1093
1099 protected $tagvspaces = array();
1100
1106 protected $customlistindent = -1;
1107
1113 protected $opencell = true;
1114
1120 protected $embeddedfiles = array();
1121
1127 protected $premode = false;
1128
1135 protected $transfmrk = array();
1136
1142 protected $htmlLinkColorArray = array(0, 0, 255);
1143
1149 protected $htmlLinkFontStyle = 'U';
1150
1156 protected $numpages = 0;
1157
1163 protected $pagelen = array();
1164
1170 protected $numimages = 0;
1171
1177 protected $imagekeys = array();
1178
1184 protected $bufferlen = 0;
1185
1191 protected $numfonts = 0;
1192
1198 protected $fontkeys = array();
1199
1205 protected $font_obj_ids = array();
1206
1212 protected $pageopen = array();
1213
1219 protected $default_monospaced_font = 'courier';
1220
1226 protected $objcopy;
1227
1233 protected $cache_file_length = array();
1234
1240 protected $thead = '';
1241
1247 protected $theadMargins = array();
1248
1254 protected $sign = false;
1255
1261 protected $signature_data = array();
1262
1268 protected $signature_max_length = 11742;
1269
1275 protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
1276
1282 protected $empty_signature_appearance = array();
1283
1289 protected $tsa_timestamp = false;
1290
1296 protected $tsa_data = array();
1297
1303 protected $re_spaces = '/[^\S\xa0]/';
1304
1310 protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
1311
1317 protected $sig_obj_id = 0;
1318
1324 protected $page_obj_id = array();
1325
1331 protected $form_obj_id = array();
1332
1338 protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1339
1345 protected $js_objects = array();
1346
1352 protected $form_action = '';
1353
1359 protected $form_enctype = 'application/x-www-form-urlencoded';
1360
1366 protected $form_mode = 'post';
1367
1373 protected $annotation_fonts = array();
1374
1380 protected $radiobutton_groups = array();
1381
1387 protected $radio_groups = array();
1388
1394 protected $textindent = 0;
1395
1402
1409
1415 protected $inthead = false;
1416
1422 protected $columns = array();
1423
1429 protected $num_columns = 1;
1430
1436 protected $current_column = 0;
1437
1443 protected $column_start_page = 0;
1444
1450 protected $maxselcol = array('page' => 0, 'column' => 0);
1451
1457 protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
1458
1464 protected $textrendermode = 0;
1465
1471 protected $textstrokewidth = 0;
1472
1478 protected $strokecolor;
1479
1485 protected $pdfunit = 'mm';
1486
1491 protected $tocpage = false;
1492
1498 protected $rasterize_vector_images = false;
1499
1505 protected $font_subsetting = true;
1506
1512 protected $default_graphic_vars = array();
1513
1519 protected $xobjects = array();
1520
1526 protected $inxobj = false;
1527
1533 protected $xobjid = '';
1534
1540 protected $font_stretching = 100;
1541
1547 protected $font_spacing = 0;
1548
1555 protected $page_regions = array();
1556
1561 protected $check_page_regions = true;
1562
1568 protected $pdflayers = array();
1569
1575 protected $dests = array();
1576
1582 protected $n_dests;
1583
1589 protected $efnames = array();
1590
1596 protected $svgdir = '';
1597
1603 protected $svgunit = 'px';
1604
1610 protected $svggradients = array();
1611
1617 protected $svggradientid = 0;
1618
1624 protected $svgdefsmode = false;
1625
1631 protected $svgdefs = array();
1632
1638 protected $svgclipmode = false;
1639
1645 protected $svgclippaths = array();
1646
1652 protected $svgcliptm = array();
1653
1659 protected $svgclipid = 0;
1660
1666 protected $svgtext = '';
1667
1673 protected $svgtextmode = array();
1674
1680 protected $svgstyles = array(array(
1681 'alignment-baseline' => 'auto',
1682 'baseline-shift' => 'baseline',
1683 'clip' => 'auto',
1684 'clip-path' => 'none',
1685 'clip-rule' => 'nonzero',
1686 'color' => 'black',
1687 'color-interpolation' => 'sRGB',
1688 'color-interpolation-filters' => 'linearRGB',
1689 'color-profile' => 'auto',
1690 'color-rendering' => 'auto',
1691 'cursor' => 'auto',
1692 'direction' => 'ltr',
1693 'display' => 'inline',
1694 'dominant-baseline' => 'auto',
1695 'enable-background' => 'accumulate',
1696 'fill' => 'black',
1697 'fill-opacity' => 1,
1698 'fill-rule' => 'nonzero',
1699 'filter' => 'none',
1700 'flood-color' => 'black',
1701 'flood-opacity' => 1,
1702 'font' => '',
1703 'font-family' => 'helvetica',
1704 'font-size' => 'medium',
1705 'font-size-adjust' => 'none',
1706 'font-stretch' => 'normal',
1707 'font-style' => 'normal',
1708 'font-variant' => 'normal',
1709 'font-weight' => 'normal',
1710 'glyph-orientation-horizontal' => '0deg',
1711 'glyph-orientation-vertical' => 'auto',
1712 'image-rendering' => 'auto',
1713 'kerning' => 'auto',
1714 'letter-spacing' => 'normal',
1715 'lighting-color' => 'white',
1716 'marker' => '',
1717 'marker-end' => 'none',
1718 'marker-mid' => 'none',
1719 'marker-start' => 'none',
1720 'mask' => 'none',
1721 'opacity' => 1,
1722 'overflow' => 'auto',
1723 'pointer-events' => 'visiblePainted',
1724 'shape-rendering' => 'auto',
1725 'stop-color' => 'black',
1726 'stop-opacity' => 1,
1727 'stroke' => 'none',
1728 'stroke-dasharray' => 'none',
1729 'stroke-dashoffset' => 0,
1730 'stroke-linecap' => 'butt',
1731 'stroke-linejoin' => 'miter',
1732 'stroke-miterlimit' => 4,
1733 'stroke-opacity' => 1,
1734 'stroke-width' => 1,
1735 'text-anchor' => 'start',
1736 'text-decoration' => 'none',
1737 'text-rendering' => 'auto',
1738 'unicode-bidi' => 'normal',
1739 'visibility' => 'visible',
1740 'word-spacing' => 'normal',
1741 'writing-mode' => 'lr-tb',
1742 'text-color' => 'black',
1743 'transfmatrix' => array(1, 0, 0, 1, 0, 0)
1744 ));
1745
1751 protected $force_srgb = false;
1752
1758 protected $pdfa_mode = false;
1759
1766
1773
1779 protected $custom_xmp = '';
1780
1787 protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0);
1788
1795 protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false);
1796
1802 protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
1803
1809 protected $tcpdflink = true;
1810
1816 protected $gdgammacache = array();
1817
1818 //------------------------------------------------------------
1819 // METHODS
1820 //------------------------------------------------------------
1821
1838 public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) {
1839 /* Set internal character encoding to ASCII */
1840 if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1841 $this->internal_encoding = mb_internal_encoding();
1842 mb_internal_encoding('ASCII');
1843 }
1844 // set file ID for trailer
1845 $serformat = (is_array($format) ? json_encode($format) : $format);
1846 $this->file_id = md5(TCPDF_STATIC::getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding));
1847 $this->font_obj_ids = array();
1848 $this->page_obj_id = array();
1849 $this->form_obj_id = array();
1850 // set pdf/a mode
1851 $this->pdfa_mode = $pdfa;
1852 $this->force_srgb = false;
1853 // set language direction
1854 $this->rtl = false;
1855 $this->tmprtl = false;
1856 // some checks
1857 $this->_dochecks();
1858 // initialization of properties
1859 $this->isunicode = $unicode;
1860 $this->page = 0;
1861 $this->transfmrk[0] = array();
1862 $this->pagedim = array();
1863 $this->n = 2;
1864 $this->buffer = '';
1865 $this->pages = array();
1866 $this->state = 0;
1867 $this->fonts = array();
1868 $this->FontFiles = array();
1869 $this->diffs = array();
1870 $this->images = array();
1871 $this->links = array();
1872 $this->gradients = array();
1873 $this->InFooter = false;
1874 $this->lasth = 0;
1875 $this->FontFamily = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
1876 $this->FontStyle = '';
1877 $this->FontSizePt = 12;
1878 $this->underline = false;
1879 $this->overline = false;
1880 $this->linethrough = false;
1881 $this->DrawColor = '0 G';
1882 $this->FillColor = '0 g';
1883 $this->TextColor = '0 g';
1884 $this->ColorFlag = false;
1885 $this->pdflayers = array();
1886 // encryption values
1887 $this->encrypted = false;
1888 $this->last_enc_key = '';
1889 // standard Unicode fonts
1890 $this->CoreFonts = array(
1891 'courier'=>'Courier',
1892 'courierB'=>'Courier-Bold',
1893 'courierI'=>'Courier-Oblique',
1894 'courierBI'=>'Courier-BoldOblique',
1895 'helvetica'=>'Helvetica',
1896 'helveticaB'=>'Helvetica-Bold',
1897 'helveticaI'=>'Helvetica-Oblique',
1898 'helveticaBI'=>'Helvetica-BoldOblique',
1899 'times'=>'Times-Roman',
1900 'timesB'=>'Times-Bold',
1901 'timesI'=>'Times-Italic',
1902 'timesBI'=>'Times-BoldItalic',
1903 'symbol'=>'Symbol',
1904 'zapfdingbats'=>'ZapfDingbats'
1905 );
1906 // set scale factor
1907 $this->setPageUnit($unit);
1908 // set page format and orientation
1909 $this->setPageFormat($format, $orientation);
1910 // page margins (1 cm)
1911 $margin = 28.35 / $this->k;
1912 $this->SetMargins($margin, $margin);
1913 $this->clMargin = $this->lMargin;
1914 $this->crMargin = $this->rMargin;
1915 // internal cell padding
1916 $cpadding = $margin / 10;
1917 $this->setCellPaddings($cpadding, 0, $cpadding, 0);
1918 // cell margins
1919 $this->setCellMargins(0, 0, 0, 0);
1920 // line width (0.2 mm)
1921 $this->LineWidth = 0.57 / $this->k;
1922 $this->linestyleWidth = sprintf('%F w', ($this->LineWidth * $this->k));
1923 $this->linestyleCap = '0 J';
1924 $this->linestyleJoin = '0 j';
1925 $this->linestyleDash = '[] 0 d';
1926 // automatic page break
1927 $this->SetAutoPageBreak(true, (2 * $margin));
1928 // full width display mode
1929 $this->SetDisplayMode('fullwidth');
1930 // compression
1931 $this->SetCompression();
1932 // set default PDF version number
1933 $this->setPDFVersion();
1934 $this->tcpdflink = true;
1935 $this->encoding = $encoding;
1936 $this->HREF = array();
1937 $this->getFontsList();
1938 $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1939 $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0);
1940 $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1941 $this->extgstates = array();
1942 $this->setTextShadow();
1943 // signature
1944 $this->sign = false;
1945 $this->tsa_timestamp = false;
1946 $this->tsa_data = array();
1947 $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0', 'name' => 'Signature');
1948 $this->empty_signature_appearance = array();
1949 // user's rights
1950 $this->ur['enabled'] = false;
1951 $this->ur['document'] = '/FullSave';
1952 $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
1953 $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1954 $this->ur['signature'] = '/Modify';
1955 $this->ur['ef'] = '/Create/Delete/Modify/Import';
1956 $this->ur['formex'] = '';
1957 // set default JPEG quality
1958 $this->jpeg_quality = 75;
1959 // initialize some settings
1960 TCPDF_FONTS::utf8Bidi(array(''), '', false, $this->isunicode, $this->CurrentFont);
1961 // set default font
1962 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
1963 $this->setHeaderFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt));
1964 $this->setFooterFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt));
1965 // check if PCRE Unicode support is enabled
1966 if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
1967 // PCRE unicode support is turned ON
1968 // \s : any whitespace character
1969 // \p{Z} : any separator
1970 // \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words.
1971 // \xa0 : Unicode Character 'NO-BREAK SPACE' (U+00A0)
1972 //$this->setSpacesRE('/(?!\xa0)[\s\p{Z}\p{Lo}]/u');
1973 $this->setSpacesRE('/(?!\xa0)[\s\p{Z}]/u');
1974 } else {
1975 // PCRE unicode support is turned OFF
1976 $this->setSpacesRE('/[^\S\xa0]/');
1977 }
1978 $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1979 // set document creation and modification timestamp
1980 $this->doc_creation_timestamp = time();
1981 $this->doc_modification_timestamp = $this->doc_creation_timestamp;
1982 // get default graphic vars
1983 $this->default_graphic_vars = $this->getGraphicVars();
1984 $this->header_xobj_autoreset = false;
1985 $this->custom_xmp = '';
1986 // Call cleanup method after script execution finishes or exit() is called.
1987 // NOTE: This will not be executed if the process is killed with a SIGTERM or SIGKILL signal.
1988 register_shutdown_function(array($this, '_destroy'), true);
1989 }
1990
1996 public function __destruct() {
1997 // restore internal encoding
1998 if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1999 mb_internal_encoding($this->internal_encoding);
2000 }
2001 // cleanup
2002 $this->_destroy(true);
2003 }
2004
2011 public function setPageUnit($unit) {
2012 $unit = strtolower($unit);
2013 //Set scale factor
2014 switch ($unit) {
2015 // points
2016 case 'px':
2017 case 'pt': {
2018 $this->k = 1;
2019 break;
2020 }
2021 // millimeters
2022 case 'mm': {
2023 $this->k = $this->dpi / 25.4;
2024 break;
2025 }
2026 // centimeters
2027 case 'cm': {
2028 $this->k = $this->dpi / 2.54;
2029 break;
2030 }
2031 // inches
2032 case 'in': {
2033 $this->k = $this->dpi;
2034 break;
2035 }
2036 // unsupported unit
2037 default : {
2038 $this->Error('Incorrect unit: '.$unit);
2039 break;
2040 }
2041 }
2042 $this->pdfunit = $unit;
2043 if (isset($this->CurOrientation)) {
2044 $this->setPageOrientation($this->CurOrientation);
2045 }
2046 }
2047
2103 protected function setPageFormat($format, $orientation='P') {
2104 if (!empty($format) AND isset($this->pagedim[$this->page])) {
2105 // remove inherited values
2106 unset($this->pagedim[$this->page]);
2107 }
2108 if (is_string($format)) {
2109 // get page measures from format name
2111 $this->fwPt = $pf[0];
2112 $this->fhPt = $pf[1];
2113 } else {
2114 // the boundaries of the physical medium on which the page shall be displayed or printed
2115 if (isset($format['MediaBox'])) {
2116 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', $format['MediaBox']['llx'], $format['MediaBox']['lly'], $format['MediaBox']['urx'], $format['MediaBox']['ury'], false, $this->k, $this->pagedim);
2117 $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k);
2118 $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k);
2119 } else {
2120 if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) {
2121 $pf = array(($format[0] * $this->k), ($format[1] * $this->k));
2122 } else {
2123 if (!isset($format['format'])) {
2124 // default value
2125 $format['format'] = 'A4';
2126 }
2128 }
2129 $this->fwPt = $pf[0];
2130 $this->fhPt = $pf[1];
2131 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2132 }
2133 // the visible region of default user space
2134 if (isset($format['CropBox'])) {
2135 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'CropBox', $format['CropBox']['llx'], $format['CropBox']['lly'], $format['CropBox']['urx'], $format['CropBox']['ury'], false, $this->k, $this->pagedim);
2136 }
2137 // the region to which the contents of the page shall be clipped when output in a production environment
2138 if (isset($format['BleedBox'])) {
2139 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'BleedBox', $format['BleedBox']['llx'], $format['BleedBox']['lly'], $format['BleedBox']['urx'], $format['BleedBox']['ury'], false, $this->k, $this->pagedim);
2140 }
2141 // the intended dimensions of the finished page after trimming
2142 if (isset($format['TrimBox'])) {
2143 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'TrimBox', $format['TrimBox']['llx'], $format['TrimBox']['lly'], $format['TrimBox']['urx'], $format['TrimBox']['ury'], false, $this->k, $this->pagedim);
2144 }
2145 // the page's meaningful content (including potential white space)
2146 if (isset($format['ArtBox'])) {
2147 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'ArtBox', $format['ArtBox']['llx'], $format['ArtBox']['lly'], $format['ArtBox']['urx'], $format['ArtBox']['ury'], false, $this->k, $this->pagedim);
2148 }
2149 // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
2150 if (isset($format['BoxColorInfo'])) {
2151 $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo'];
2152 }
2153 if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) {
2154 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2155 $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']);
2156 }
2157 if (isset($format['PZ'])) {
2158 // The page's preferred zoom (magnification) factor
2159 $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']);
2160 }
2161 if (isset($format['trans'])) {
2162 // The style and duration of the visual transition to use when moving from another page to the given page during a presentation
2163 if (isset($format['trans']['Dur'])) {
2164 // The page's display duration
2165 $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']);
2166 }
2167 $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
2168 if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
2169 // The transition style that shall be used when moving to this page from another during a presentation
2170 $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S'];
2171 $valid_effect = array('Split', 'Blinds');
2172 $valid_vals = array('H', 'V');
2173 if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
2174 $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm'];
2175 }
2176 $valid_effect = array('Split', 'Box', 'Fly');
2177 $valid_vals = array('I', 'O');
2178 if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
2179 $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M'];
2180 }
2181 $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
2182 if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
2183 if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
2184 OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
2185 OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
2186 $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']);
2187 }
2188 }
2189 if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
2190 $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']);
2191 }
2192 if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
2193 $this->pagedim[$this->page]['trans']['B'] = 'true';
2194 }
2195 } else {
2196 $this->pagedim[$this->page]['trans']['S'] = 'R';
2197 }
2198 if (isset($format['trans']['D'])) {
2199 // The duration of the transition effect, in seconds
2200 $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']);
2201 } else {
2202 $this->pagedim[$this->page]['trans']['D'] = 1;
2203 }
2204 }
2205 }
2206 $this->setPageOrientation($orientation);
2207 }
2208
2217 public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
2218 if (!isset($this->pagedim[$this->page]['MediaBox'])) {
2219 // the boundaries of the physical medium on which the page shall be displayed or printed
2220 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2221 }
2222 if (!isset($this->pagedim[$this->page]['CropBox'])) {
2223 // the visible region of default user space
2224 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'CropBox', $this->pagedim[$this->page]['MediaBox']['llx'], $this->pagedim[$this->page]['MediaBox']['lly'], $this->pagedim[$this->page]['MediaBox']['urx'], $this->pagedim[$this->page]['MediaBox']['ury'], true, $this->k, $this->pagedim);
2225 }
2226 if (!isset($this->pagedim[$this->page]['BleedBox'])) {
2227 // the region to which the contents of the page shall be clipped when output in a production environment
2228 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'BleedBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2229 }
2230 if (!isset($this->pagedim[$this->page]['TrimBox'])) {
2231 // the intended dimensions of the finished page after trimming
2232 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'TrimBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2233 }
2234 if (!isset($this->pagedim[$this->page]['ArtBox'])) {
2235 // the page's meaningful content (including potential white space)
2236 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'ArtBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2237 }
2238 if (!isset($this->pagedim[$this->page]['Rotate'])) {
2239 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2240 $this->pagedim[$this->page]['Rotate'] = 0;
2241 }
2242 if (!isset($this->pagedim[$this->page]['PZ'])) {
2243 // The page's preferred zoom (magnification) factor
2244 $this->pagedim[$this->page]['PZ'] = 1;
2245 }
2246 if ($this->fwPt > $this->fhPt) {
2247 // landscape
2248 $default_orientation = 'L';
2249 } else {
2250 // portrait
2251 $default_orientation = 'P';
2252 }
2253 $valid_orientations = array('P', 'L');
2254 if (empty($orientation)) {
2255 $orientation = $default_orientation;
2256 } else {
2257 $orientation = strtoupper($orientation[0]);
2258 }
2259 if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
2260 $this->CurOrientation = $orientation;
2261 $this->wPt = $this->fhPt;
2262 $this->hPt = $this->fwPt;
2263 } else {
2264 $this->CurOrientation = $default_orientation;
2265 $this->wPt = $this->fwPt;
2266 $this->hPt = $this->fhPt;
2267 }
2268 if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
2269 // swap X and Y coordinates (change page orientation)
2270 $this->pagedim = TCPDF_STATIC::swapPageBoxCoordinates($this->page, $this->pagedim);
2271 }
2272 $this->w = ($this->wPt / $this->k);
2273 $this->h = ($this->hPt / $this->k);
2274 if (TCPDF_STATIC::empty_string($autopagebreak)) {
2275 if (isset($this->AutoPageBreak)) {
2276 $autopagebreak = $this->AutoPageBreak;
2277 } else {
2278 $autopagebreak = true;
2279 }
2280 }
2281 if (TCPDF_STATIC::empty_string($bottommargin)) {
2282 if (isset($this->bMargin)) {
2283 $bottommargin = $this->bMargin;
2284 } else {
2285 // default value = 2 cm
2286 $bottommargin = 2 * 28.35 / $this->k;
2287 }
2288 }
2289 $this->SetAutoPageBreak($autopagebreak, $bottommargin);
2290 // store page dimensions
2291 $this->pagedim[$this->page]['w'] = $this->wPt;
2292 $this->pagedim[$this->page]['h'] = $this->hPt;
2293 $this->pagedim[$this->page]['wk'] = $this->w;
2294 $this->pagedim[$this->page]['hk'] = $this->h;
2295 $this->pagedim[$this->page]['tm'] = $this->tMargin;
2296 $this->pagedim[$this->page]['bm'] = $bottommargin;
2297 $this->pagedim[$this->page]['lm'] = $this->lMargin;
2298 $this->pagedim[$this->page]['rm'] = $this->rMargin;
2299 $this->pagedim[$this->page]['pb'] = $autopagebreak;
2300 $this->pagedim[$this->page]['or'] = $this->CurOrientation;
2301 $this->pagedim[$this->page]['olm'] = $this->original_lMargin;
2302 $this->pagedim[$this->page]['orm'] = $this->original_rMargin;
2303 }
2304
2323 public function setSpacesRE($re='/[^\S\xa0]/') {
2324 $this->re_spaces = $re;
2325 $re_parts = explode('/', $re);
2326 // get pattern parts
2327 $this->re_space = array();
2328 if (isset($re_parts[1]) AND !empty($re_parts[1])) {
2329 $this->re_space['p'] = $re_parts[1];
2330 } else {
2331 $this->re_space['p'] = '[\s]';
2332 }
2333 // set pattern modifiers
2334 if (isset($re_parts[2]) AND !empty($re_parts[2])) {
2335 $this->re_space['m'] = $re_parts[2];
2336 } else {
2337 $this->re_space['m'] = '';
2338 }
2339 }
2340
2348 public function setRTL($enable, $resetx=true) {
2349 $enable = $enable ? true : false;
2350 $resetx = ($resetx AND ($enable != $this->rtl));
2351 $this->rtl = $enable;
2352 $this->tmprtl = false;
2353 if ($resetx) {
2354 $this->Ln(0);
2355 }
2356 }
2357
2364 public function getRTL() {
2365 return $this->rtl;
2366 }
2367
2374 public function setTempRTL($mode) {
2375 $newmode = false;
2376 switch (strtoupper($mode)) {
2377 case 'LTR':
2378 case 'L': {
2379 if ($this->rtl) {
2380 $newmode = 'L';
2381 }
2382 break;
2383 }
2384 case 'RTL':
2385 case 'R': {
2386 if (!$this->rtl) {
2387 $newmode = 'R';
2388 }
2389 break;
2390 }
2391 case false:
2392 default: {
2393 $newmode = false;
2394 break;
2395 }
2396 }
2397 $this->tmprtl = $newmode;
2398 }
2399
2406 public function isRTLTextDir() {
2407 return ($this->rtl OR ($this->tmprtl == 'R'));
2408 }
2409
2417 public function setLastH($h) {
2418 $this->lasth = $h;
2419 }
2420
2427 public function getCellHeight($fontsize, $padding=TRUE) {
2428 $height = ($fontsize * $this->cell_height_ratio);
2429 if ($padding) {
2430 $height += ($this->cell_padding['T'] + $this->cell_padding['B']);
2431 }
2432 return round($height, 6);
2433 }
2434
2440 public function resetLastH() {
2441 $this->lasth = $this->getCellHeight($this->FontSize);
2442 }
2443
2450 public function getLastH() {
2451 return $this->lasth;
2452 }
2453
2461 public function setImageScale($scale) {
2462 $this->imgscale = $scale;
2463 }
2464
2472 public function getImageScale() {
2473 return $this->imgscale;
2474 }
2475
2485 public function getPageDimensions($pagenum='') {
2486 if (empty($pagenum)) {
2487 $pagenum = $this->page;
2488 }
2489 return $this->pagedim[$pagenum];
2490 }
2491
2501 public function getPageWidth($pagenum='') {
2502 if (empty($pagenum)) {
2503 return $this->w;
2504 }
2505 return $this->pagedim[$pagenum]['w'];
2506 }
2507
2517 public function getPageHeight($pagenum='') {
2518 if (empty($pagenum)) {
2519 return $this->h;
2520 }
2521 return $this->pagedim[$pagenum]['h'];
2522 }
2523
2533 public function getBreakMargin($pagenum='') {
2534 if (empty($pagenum)) {
2535 return $this->bMargin;
2536 }
2537 return $this->pagedim[$pagenum]['bm'];
2538 }
2539
2547 public function getScaleFactor() {
2548 return $this->k;
2549 }
2550
2561 public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
2562 //Set left, top and right margins
2563 $this->lMargin = $left;
2564 $this->tMargin = $top;
2565 if ($right == -1) {
2566 $right = $left;
2567 }
2568 $this->rMargin = $right;
2569 if ($keepmargins) {
2570 // overwrite original values
2571 $this->original_lMargin = $this->lMargin;
2572 $this->original_rMargin = $this->rMargin;
2573 }
2574 }
2575
2583 public function SetLeftMargin($margin) {
2584 //Set left margin
2585 $this->lMargin = $margin;
2586 if (($this->page > 0) AND ($this->x < $margin)) {
2587 $this->x = $margin;
2588 }
2589 }
2590
2598 public function SetTopMargin($margin) {
2599 //Set top margin
2600 $this->tMargin = $margin;
2601 if (($this->page > 0) AND ($this->y < $margin)) {
2602 $this->y = $margin;
2603 }
2604 }
2605
2613 public function SetRightMargin($margin) {
2614 $this->rMargin = $margin;
2615 if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
2616 $this->x = $this->w - $margin;
2617 }
2618 }
2619
2627 public function SetCellPadding($pad) {
2628 if ($pad >= 0) {
2629 $this->cell_padding['L'] = $pad;
2630 $this->cell_padding['T'] = $pad;
2631 $this->cell_padding['R'] = $pad;
2632 $this->cell_padding['B'] = $pad;
2633 }
2634 }
2635
2646 public function setCellPaddings($left='', $top='', $right='', $bottom='') {
2647 if (($left !== '') AND ($left >= 0)) {
2648 $this->cell_padding['L'] = $left;
2649 }
2650 if (($top !== '') AND ($top >= 0)) {
2651 $this->cell_padding['T'] = $top;
2652 }
2653 if (($right !== '') AND ($right >= 0)) {
2654 $this->cell_padding['R'] = $right;
2655 }
2656 if (($bottom !== '') AND ($bottom >= 0)) {
2657 $this->cell_padding['B'] = $bottom;
2658 }
2659 }
2660
2668 public function getCellPaddings() {
2669 return $this->cell_padding;
2670 }
2671
2682 public function setCellMargins($left='', $top='', $right='', $bottom='') {
2683 if (($left !== '') AND ($left >= 0)) {
2684 $this->cell_margin['L'] = $left;
2685 }
2686 if (($top !== '') AND ($top >= 0)) {
2687 $this->cell_margin['T'] = $top;
2688 }
2689 if (($right !== '') AND ($right >= 0)) {
2690 $this->cell_margin['R'] = $right;
2691 }
2692 if (($bottom !== '') AND ($bottom >= 0)) {
2693 $this->cell_margin['B'] = $bottom;
2694 }
2695 }
2696
2704 public function getCellMargins() {
2705 return $this->cell_margin;
2706 }
2707
2715 protected function adjustCellPadding($brd=0) {
2716 if (empty($brd)) {
2717 return;
2718 }
2719 if (is_string($brd)) {
2720 // convert string to array
2721 $slen = strlen($brd);
2722 $newbrd = array();
2723 for ($i = 0; $i < $slen; ++$i) {
2724 $newbrd[$brd[$i]] = true;
2725 }
2726 $brd = $newbrd;
2727 } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) {
2728 $brd = array('LRTB' => true);
2729 }
2730 if (!is_array($brd)) {
2731 return;
2732 }
2733 // store current cell padding
2734 $cp = $this->cell_padding;
2735 // select border mode
2736 if (isset($brd['mode'])) {
2737 $mode = $brd['mode'];
2738 unset($brd['mode']);
2739 } else {
2740 $mode = 'normal';
2741 }
2742 // process borders
2743 foreach ($brd as $border => $style) {
2744 $line_width = $this->LineWidth;
2745 if (is_array($style) AND isset($style['width'])) {
2746 // get border width
2747 $line_width = $style['width'];
2748 }
2749 $adj = 0; // line width inside the cell
2750 switch ($mode) {
2751 case 'ext': {
2752 $adj = 0;
2753 break;
2754 }
2755 case 'int': {
2756 $adj = $line_width;
2757 break;
2758 }
2759 case 'normal':
2760 default: {
2761 $adj = ($line_width / 2);
2762 break;
2763 }
2764 }
2765 // correct internal cell padding if required to avoid overlap between text and lines
2766 if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) {
2767 $this->cell_padding['T'] = $adj;
2768 }
2769 if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) {
2770 $this->cell_padding['R'] = $adj;
2771 }
2772 if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) {
2773 $this->cell_padding['B'] = $adj;
2774 }
2775 if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) {
2776 $this->cell_padding['L'] = $adj;
2777 }
2778 }
2779 return array('T' => ($this->cell_padding['T'] - $cp['T']), 'R' => ($this->cell_padding['R'] - $cp['R']), 'B' => ($this->cell_padding['B'] - $cp['B']), 'L' => ($this->cell_padding['L'] - $cp['L']));
2780 }
2781
2790 public function SetAutoPageBreak($auto, $margin=0) {
2791 $this->AutoPageBreak = $auto ? true : false;
2792 $this->bMargin = $margin;
2793 $this->PageBreakTrigger = $this->h - $margin;
2794 }
2795
2802 public function getAutoPageBreak() {
2803 return $this->AutoPageBreak;
2804 }
2805
2814 public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
2815 if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
2816 $this->ZoomMode = $zoom;
2817 } else {
2818 $this->Error('Incorrect zoom display mode: '.$zoom);
2819 }
2820 $this->LayoutMode = TCPDF_STATIC::getPageLayoutMode($layout);
2821 $this->PageMode = TCPDF_STATIC::getPageMode($mode);
2822 }
2823
2831 public function SetCompression($compress=true) {
2832 if (function_exists('gzcompress')) {
2833 $this->compress = $compress ? true : false;
2834 } else {
2835 $this->compress = false;
2836 }
2837 }
2838
2845 public function setSRGBmode($mode=false) {
2846 $this->force_srgb = $mode ? true : false;
2847 }
2848
2856 public function SetDocInfoUnicode($unicode=true) {
2857 $this->docinfounicode = $unicode ? true : false;
2858 }
2859
2867 public function SetTitle($title) {
2868 $this->title = $title;
2869 }
2870
2878 public function SetSubject($subject) {
2879 $this->subject = $subject;
2880 }
2881
2889 public function SetAuthor($author) {
2890 $this->author = $author;
2891 }
2892
2900 public function SetKeywords($keywords) {
2901 $this->keywords = $keywords;
2902 }
2903
2911 public function SetCreator($creator) {
2912 $this->creator = $creator;
2913 }
2914
2921 public function Error($msg) {
2922 // unset all class variables
2923 $this->_destroy(true);
2924 if (defined('K_TCPDF_THROW_EXCEPTION_ERROR') AND !K_TCPDF_THROW_EXCEPTION_ERROR) {
2925 die('<strong>TCPDF ERROR: </strong>'.$msg);
2926 } else {
2927 throw new Exception('TCPDF ERROR: '.$msg);
2928 }
2929 }
2930
2939 public function Open() {
2940 $this->state = 1;
2941 }
2942
2951 public function Close() {
2952 if ($this->state == 3) {
2953 return;
2954 }
2955 if ($this->page == 0) {
2956 $this->AddPage();
2957 }
2958 $this->endLayer();
2959 if ($this->tcpdflink) {
2960 // save current graphic settings
2961 $gvars = $this->getGraphicVars();
2962 $this->setEqualColumns();
2963 $this->lastpage(true);
2964 $this->SetAutoPageBreak(false);
2965 $this->x = 0;
2966 $this->y = $this->h - (1 / $this->k);
2967 $this->lMargin = 0;
2968 $this->_outSaveGraphicsState();
2969 $font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
2970 $this->SetFont($font, '', 1);
2971 $this->setTextRenderingMode(0, false, false);
2972 $msg = "\x50\x6f\x77\x65\x72\x65\x64\x20\x62\x79\x20\x54\x43\x50\x44\x46\x20\x28\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29";
2973 $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
2974 $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
2975 $this->_outRestoreGraphicsState();
2976 // restore graphic settings
2977 $this->setGraphicVars($gvars);
2978 }
2979 // close page
2980 $this->endPage();
2981 // close document
2982 $this->_enddoc();
2983 // unset all class variables (except critical ones)
2984 $this->_destroy(false);
2985 }
2986
2995 public function setPage($pnum, $resetmargins=false) {
2996 if (($pnum == $this->page) AND ($this->state == 2)) {
2997 return;
2998 }
2999 if (($pnum > 0) AND ($pnum <= $this->numpages)) {
3000 $this->state = 2;
3001 // save current graphic settings
3002 //$gvars = $this->getGraphicVars();
3003 $oldpage = $this->page;
3004 $this->page = $pnum;
3005 $this->wPt = $this->pagedim[$this->page]['w'];
3006 $this->hPt = $this->pagedim[$this->page]['h'];
3007 $this->w = $this->pagedim[$this->page]['wk'];
3008 $this->h = $this->pagedim[$this->page]['hk'];
3009 $this->tMargin = $this->pagedim[$this->page]['tm'];
3010 $this->bMargin = $this->pagedim[$this->page]['bm'];
3011 $this->original_lMargin = $this->pagedim[$this->page]['olm'];
3012 $this->original_rMargin = $this->pagedim[$this->page]['orm'];
3013 $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
3014 $this->CurOrientation = $this->pagedim[$this->page]['or'];
3015 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
3016 // restore graphic settings
3017 //$this->setGraphicVars($gvars);
3018 if ($resetmargins) {
3019 $this->lMargin = $this->pagedim[$this->page]['olm'];
3020 $this->rMargin = $this->pagedim[$this->page]['orm'];
3021 $this->SetY($this->tMargin);
3022 } else {
3023 // account for booklet mode
3024 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3025 $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
3026 $this->lMargin += $deltam;
3027 $this->rMargin -= $deltam;
3028 }
3029 }
3030 } else {
3031 $this->Error('Wrong page number on setPage() function: '.$pnum);
3032 }
3033 }
3034
3042 public function lastPage($resetmargins=false) {
3043 $this->setPage($this->getNumPages(), $resetmargins);
3044 }
3045
3053 public function getPage() {
3054 return $this->page;
3055 }
3056
3064 public function getNumPages() {
3065 return $this->numpages;
3066 }
3067
3077 public function addTOCPage($orientation='', $format='', $keepmargins=false) {
3078 $this->AddPage($orientation, $format, $keepmargins, true);
3079 }
3080
3087 public function endTOCPage() {
3088 $this->endPage(true);
3089 }
3090
3102 public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
3103 if ($this->inxobj) {
3104 // we are inside an XObject template
3105 return;
3106 }
3107 if (!isset($this->original_lMargin) OR $keepmargins) {
3108 $this->original_lMargin = $this->lMargin;
3109 }
3110 if (!isset($this->original_rMargin) OR $keepmargins) {
3111 $this->original_rMargin = $this->rMargin;
3112 }
3113 // terminate previous page
3114 $this->endPage();
3115 // start new page
3116 $this->startPage($orientation, $format, $tocpage);
3117 }
3118
3126 public function endPage($tocpage=false) {
3127 // check if page is already closed
3128 if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
3129 return;
3130 }
3131 // print page footer
3132 $this->setFooter();
3133 // close page
3134 $this->_endpage();
3135 // mark page as closed
3136 $this->pageopen[$this->page] = false;
3137 if ($tocpage) {
3138 $this->tocpage = false;
3139 }
3140 }
3141
3152 public function startPage($orientation='', $format='', $tocpage=false) {
3153 if ($tocpage) {
3154 $this->tocpage = true;
3155 }
3156 // move page numbers of documents to be attached
3157 if ($this->tocpage) {
3158 // move reference to unexistent pages (used for page attachments)
3159 // adjust outlines
3160 $tmpoutlines = $this->outlines;
3161 foreach ($tmpoutlines as $key => $outline) {
3162 if (!$outline['f'] AND ($outline['p'] > $this->numpages)) {
3163 $this->outlines[$key]['p'] = ($outline['p'] + 1);
3164 }
3165 }
3166 // adjust dests
3167 $tmpdests = $this->dests;
3168 foreach ($tmpdests as $key => $dest) {
3169 if (!$dest['f'] AND ($dest['p'] > $this->numpages)) {
3170 $this->dests[$key]['p'] = ($dest['p'] + 1);
3171 }
3172 }
3173 // adjust links
3174 $tmplinks = $this->links;
3175 foreach ($tmplinks as $key => $link) {
3176 if (!$link['f'] AND ($link['p'] > $this->numpages)) {
3177 $this->links[$key]['p'] = ($link['p'] + 1);
3178 }
3179 }
3180 }
3181 if ($this->numpages > $this->page) {
3182 // this page has been already added
3183 $this->setPage($this->page + 1);
3184 $this->SetY($this->tMargin);
3185 return;
3186 }
3187 // start a new page
3188 if ($this->state == 0) {
3189 $this->Open();
3190 }
3192 $this->swapMargins($this->booklet);
3193 // save current graphic settings
3194 $gvars = $this->getGraphicVars();
3195 // start new page
3196 $this->_beginpage($orientation, $format);
3197 // mark page as open
3198 $this->pageopen[$this->page] = true;
3199 // restore graphic settings
3200 $this->setGraphicVars($gvars);
3201 // mark this point
3202 $this->setPageMark();
3203 // print page header
3204 $this->setHeader();
3205 // restore graphic settings
3206 $this->setGraphicVars($gvars);
3207 // mark this point
3208 $this->setPageMark();
3209 // print table header (if any)
3210 $this->setTableHeader();
3211 // set mark for empty page check
3212 $this->emptypagemrk[$this->page]= $this->pagelen[$this->page];
3213 }
3214
3223 public function setPageMark() {
3224 $this->intmrk[$this->page] = $this->pagelen[$this->page];
3225 $this->bordermrk[$this->page] = $this->intmrk[$this->page];
3226 $this->setContentMark();
3227 }
3228
3236 protected function setContentMark($page=0) {
3237 if ($page <= 0) {
3239 }
3240 if (isset($this->footerlen[$page])) {
3241 $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
3242 } else {
3243 $this->cntmrk[$page] = $this->pagelen[$page];
3244 }
3245 }
3246
3257 public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) {
3258 $this->header_logo = $ln;
3259 $this->header_logo_width = $lw;
3260 $this->header_title = $ht;
3261 $this->header_string = $hs;
3262 $this->header_text_color = $tc;
3263 $this->header_line_color = $lc;
3264 }
3265
3272 public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) {
3273 $this->footer_text_color = $tc;
3274 $this->footer_line_color = $lc;
3275 }
3276
3284 public function getHeaderData() {
3285 $ret = array();
3286 $ret['logo'] = $this->header_logo;
3287 $ret['logo_width'] = $this->header_logo_width;
3288 $ret['title'] = $this->header_title;
3289 $ret['string'] = $this->header_string;
3290 $ret['text_color'] = $this->header_text_color;
3291 $ret['line_color'] = $this->header_line_color;
3292 return $ret;
3293 }
3294
3301 public function setHeaderMargin($hm=10) {
3302 $this->header_margin = $hm;
3303 }
3304
3311 public function getHeaderMargin() {
3312 return $this->header_margin;
3313 }
3314
3321 public function setFooterMargin($fm=10) {
3322 $this->footer_margin = $fm;
3323 }
3324
3331 public function getFooterMargin() {
3332 return $this->footer_margin;
3333 }
3339 public function setPrintHeader($val=true) {
3340 $this->print_header = $val ? true : false;
3341 }
3342
3348 public function setPrintFooter($val=true) {
3349 $this->print_footer = $val ? true : false;
3350 }
3351
3357 public function getImageRBX() {
3358 return $this->img_rb_x;
3359 }
3360
3366 public function getImageRBY() {
3367 return $this->img_rb_y;
3368 }
3369
3374 public function resetHeaderTemplate() {
3375 $this->header_xobjid = false;
3376 }
3377
3383 public function setHeaderTemplateAutoreset($val=true) {
3384 $this->header_xobj_autoreset = $val ? true : false;
3385 }
3386
3392 public function Header() {
3393 if ($this->header_xobjid === false) {
3394 // start a new XObject Template
3395 $this->header_xobjid = $this->startTemplate($this->w, $this->tMargin);
3396 $headerfont = $this->getHeaderFont();
3397 $headerdata = $this->getHeaderData();
3398 $this->y = $this->header_margin;
3399 if ($this->rtl) {
3400 $this->x = $this->w - $this->original_rMargin;
3401 } else {
3402 $this->x = $this->original_lMargin;
3403 }
3404 if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
3405 $imgtype = TCPDF_IMAGES::getImageFileType(K_PATH_IMAGES.$headerdata['logo']);
3406 if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
3407 $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3408 } elseif ($imgtype == 'svg') {
3409 $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3410 } else {
3411 $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3412 }
3413 $imgy = $this->getImageRBY();
3414 } else {
3415 $imgy = $this->y;
3416 }
3417 $cell_height = $this->getCellHeight($headerfont[2] / $this->k);
3418 // set starting margin for text data cell
3419 if ($this->getRTL()) {
3420 $header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1);
3421 } else {
3422 $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1);
3423 }
3424 $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1);
3425 $this->SetTextColorArray($this->header_text_color);
3426 // header title
3427 $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
3428 $this->SetX($header_x);
3429 $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
3430 // header string
3431 $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
3432 $this->SetX($header_x);
3433 $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false);
3434 // print an ending header line
3435 $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color']));
3436 $this->SetY((2.835 / $this->k) + max($imgy, $this->y));
3437 if ($this->rtl) {
3438 $this->SetX($this->original_rMargin);
3439 } else {
3440 $this->SetX($this->original_lMargin);
3441 }
3442 $this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C');
3443 $this->endTemplate();
3444 }
3445 // print header template
3446 $x = 0;
3447 $dx = 0;
3448 if (!$this->header_xobj_autoreset AND $this->booklet AND (($this->page % 2) == 0)) {
3449 // adjust margins for booklet mode
3450 $dx = ($this->original_lMargin - $this->original_rMargin);
3451 }
3452 if ($this->rtl) {
3453 $x = $this->w + $dx;
3454 } else {
3455 $x = 0 + $dx;
3456 }
3457 $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false);
3458 if ($this->header_xobj_autoreset) {
3459 // reset header xobject template at each page
3460 $this->header_xobjid = false;
3461 }
3462 }
3463
3469 public function Footer() {
3470 $cur_y = $this->y;
3471 $this->SetTextColorArray($this->footer_text_color);
3472 //set style for cell border
3473 $line_width = (0.85 / $this->k);
3474 $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color));
3475 //print document barcode
3476 $barcode = $this->getBarcode();
3477 if (!empty($barcode)) {
3478 $this->Ln($line_width);
3479 $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3);
3480 $style = array(
3481 'position' => $this->rtl?'R':'L',
3482 'align' => $this->rtl?'R':'L',
3483 'stretch' => false,
3484 'fitwidth' => true,
3485 'cellfitalign' => '',
3486 'border' => false,
3487 'padding' => 0,
3488 'fgcolor' => array(0,0,0),
3489 'bgcolor' => false,
3490 'text' => false
3491 );
3492 $this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, '');
3493 }
3494 $w_page = isset($this->l['w_page']) ? $this->l['w_page'].' ' : '';
3495 if (empty($this->pagegroups)) {
3496 $pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
3497 } else {
3498 $pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
3499 }
3500 $this->SetY($cur_y);
3501 //Print page number
3502 if ($this->getRTL()) {
3503 $this->SetX($this->original_rMargin);
3504 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
3505 } else {
3506 $this->SetX($this->original_lMargin);
3507 $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R');
3508 }
3509 }
3510
3516 protected function setHeader() {
3517 if (!$this->print_header OR ($this->state != 2)) {
3518 return;
3519 }
3520 $this->InHeader = true;
3521 $this->setGraphicVars($this->default_graphic_vars);
3522 $temp_thead = $this->thead;
3523 $temp_theadMargins = $this->theadMargins;
3526 $this->_outSaveGraphicsState();
3527 $this->rMargin = $this->original_rMargin;
3528 $this->lMargin = $this->original_lMargin;
3529 $this->SetCellPadding(0);
3530 //set current position
3531 if ($this->rtl) {
3532 $this->SetXY($this->original_rMargin, $this->header_margin);
3533 } else {
3534 $this->SetXY($this->original_lMargin, $this->header_margin);
3535 }
3536 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
3537 $this->Header();
3538 //restore position
3539 if ($this->rtl) {
3540 $this->SetXY($this->original_rMargin, $this->tMargin);
3541 } else {
3542 $this->SetXY($this->original_lMargin, $this->tMargin);
3543 }
3544 $this->_outRestoreGraphicsState();
3545 $this->lasth = $lasth;
3546 $this->thead = $temp_thead;
3547 $this->theadMargins = $temp_theadMargins;
3548 $this->newline = $newline;
3549 $this->InHeader = false;
3550 }
3551
3557 protected function setFooter() {
3558 if ($this->state != 2) {
3559 return;
3560 }
3561 $this->InFooter = true;
3562 // save current graphic settings
3563 $gvars = $this->getGraphicVars();
3564 // mark this point
3565 $this->footerpos[$this->page] = $this->pagelen[$this->page];
3566 $this->_out("\n");
3567 if ($this->print_footer) {
3568 $this->setGraphicVars($this->default_graphic_vars);
3569 $this->current_column = 0;
3570 $this->num_columns = 1;
3571 $temp_thead = $this->thead;
3572 $temp_theadMargins = $this->theadMargins;
3574 $this->_outSaveGraphicsState();
3575 $this->rMargin = $this->original_rMargin;
3576 $this->lMargin = $this->original_lMargin;
3577 $this->SetCellPadding(0);
3578 //set current position
3579 $footer_y = $this->h - $this->footer_margin;
3580 if ($this->rtl) {
3581 $this->SetXY($this->original_rMargin, $footer_y);
3582 } else {
3583 $this->SetXY($this->original_lMargin, $footer_y);
3584 }
3585 $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
3586 $this->Footer();
3587 //restore position
3588 if ($this->rtl) {
3589 $this->SetXY($this->original_rMargin, $this->tMargin);
3590 } else {
3591 $this->SetXY($this->original_lMargin, $this->tMargin);
3592 }
3593 $this->_outRestoreGraphicsState();
3594 $this->lasth = $lasth;
3595 $this->thead = $temp_thead;
3596 $this->theadMargins = $temp_theadMargins;
3597 }
3598 // restore graphic settings
3599 $this->setGraphicVars($gvars);
3600 $this->current_column = $gvars['current_column'];
3601 $this->num_columns = $gvars['num_columns'];
3602 // calculate footer length
3603 $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
3604 $this->InFooter = false;
3605 }
3606
3613 protected function inPageBody() {
3614 return (($this->InHeader === false) AND ($this->InFooter === false));
3615 }
3616
3622 protected function setTableHeader() {
3623 if ($this->num_columns > 1) {
3624 // multi column mode
3625 return;
3626 }
3627 if (isset($this->theadMargins['top'])) {
3628 // restore the original top-margin
3629 $this->tMargin = $this->theadMargins['top'];
3630 $this->pagedim[$this->page]['tm'] = $this->tMargin;
3631 $this->y = $this->tMargin;
3632 }
3633 if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
3634 // set margins
3635 $prev_lMargin = $this->lMargin;
3636 $prev_rMargin = $this->rMargin;
3637 $prev_cell_padding = $this->cell_padding;
3638 $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']);
3639 $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']);
3640 $this->cell_padding = $this->theadMargins['cell_padding'];
3641 if ($this->rtl) {
3642 $this->x = $this->w - $this->rMargin;
3643 } else {
3644 $this->x = $this->lMargin;
3645 }
3646 // account for special "cell" mode
3647 if ($this->theadMargins['cell']) {
3648 if ($this->rtl) {
3649 $this->x -= $this->cell_padding['R'];
3650 } else {
3651 $this->x += $this->cell_padding['L'];
3652 }
3653 }
3654 $gvars = $this->getGraphicVars();
3655 if (!empty($this->theadMargins['gvars'])) {
3656 // set the correct graphic style
3657 $this->setGraphicVars($this->theadMargins['gvars']);
3658 $this->rMargin = $gvars['rMargin'];
3659 $this->lMargin = $gvars['lMargin'];
3660 }
3661 // print table header
3662 $this->writeHTML($this->thead, false, false, false, false, '');
3663 $this->setGraphicVars($gvars);
3664 // set new top margin to skip the table headers
3665 if (!isset($this->theadMargins['top'])) {
3666 $this->theadMargins['top'] = $this->tMargin;
3667 }
3668 // store end of header position
3669 if (!isset($this->columns[0]['th'])) {
3670 $this->columns[0]['th'] = array();
3671 }
3672 $this->columns[0]['th']['\''.$this->page.'\''] = $this->y;
3673 $this->tMargin = $this->y;
3674 $this->pagedim[$this->page]['tm'] = $this->tMargin;
3675 $this->lasth = 0;
3676 $this->lMargin = $prev_lMargin;
3677 $this->rMargin = $prev_rMargin;
3678 $this->cell_padding = $prev_cell_padding;
3679 }
3680 }
3681
3689 public function PageNo() {
3690 return $this->page;
3691 }
3692
3699 public function getAllSpotColors() {
3700 return $this->spot_colors;
3701 }
3702
3716 public function AddSpotColor($name, $c, $m, $y, $k) {
3717 if (!isset($this->spot_colors[$name])) {
3718 $i = (1 + count($this->spot_colors));
3719 $this->spot_colors[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i);
3720 }
3721 }
3722
3732 public function setSpotColor($type, $name, $tint=100) {
3733 $spotcolor = TCPDF_COLORS::getSpotColor($name, $this->spot_colors);
3734 if ($spotcolor === false) {
3735 $this->Error('Undefined spot color: '.$name.', you must add it using the AddSpotColor() method.');
3736 }
3737 $tint = (max(0, min(100, $tint)) / 100);
3738 $pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']);
3739 switch ($type) {
3740 case 'draw': {
3741 $pdfcolor .= sprintf('CS %F SCN', $tint);
3742 $this->DrawColor = $pdfcolor;
3743 $this->strokecolor = $spotcolor;
3744 break;
3745 }
3746 case 'fill': {
3747 $pdfcolor .= sprintf('cs %F scn', $tint);
3748 $this->FillColor = $pdfcolor;
3749 $this->bgcolor = $spotcolor;
3750 break;
3751 }
3752 case 'text': {
3753 $pdfcolor .= sprintf('cs %F scn', $tint);
3754 $this->TextColor = $pdfcolor;
3755 $this->fgcolor = $spotcolor;
3756 break;
3757 }
3758 }
3759 $this->ColorFlag = ($this->FillColor != $this->TextColor);
3760 if ($this->state == 2) {
3761 $this->_out($pdfcolor);
3762 }
3763 if ($this->inxobj) {
3764 // we are inside an XObject template
3765 $this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name];
3766 }
3767 return $pdfcolor;
3768 }
3769
3778 public function SetDrawSpotColor($name, $tint=100) {
3779 $this->setSpotColor('draw', $name, $tint);
3780 }
3781
3790 public function SetFillSpotColor($name, $tint=100) {
3791 $this->setSpotColor('fill', $name, $tint);
3792 }
3793
3802 public function SetTextSpotColor($name, $tint=100) {
3803 $this->setSpotColor('text', $name, $tint);
3804 }
3805
3817 public function setColorArray($type, $color, $ret=false) {
3818 if (is_array($color)) {
3819 $color = array_values($color);
3820 // component: grey, RGB red or CMYK cyan
3821 $c = isset($color[0]) ? $color[0] : -1;
3822 // component: RGB green or CMYK magenta
3823 $m = isset($color[1]) ? $color[1] : -1;
3824 // component: RGB blue or CMYK yellow
3825 $y = isset($color[2]) ? $color[2] : -1;
3826 // component: CMYK black
3827 $k = isset($color[3]) ? $color[3] : -1;
3828 // color name
3829 $name = isset($color[4]) ? $color[4] : '';
3830 if ($c >= 0) {
3831 return $this->setColor($type, $c, $m, $y, $k, $ret, $name);
3832 }
3833 }
3834 return '';
3835 }
3836
3848 public function SetDrawColorArray($color, $ret=false) {
3849 return $this->setColorArray('draw', $color, $ret);
3850 }
3851
3862 public function SetFillColorArray($color, $ret=false) {
3863 return $this->setColorArray('fill', $color, $ret);
3864 }
3865
3875 public function SetTextColorArray($color, $ret=false) {
3876 return $this->setColorArray('text', $color, $ret);
3877 }
3878
3892 public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3893 // set default values
3894 if (!is_numeric($col1)) {
3895 $col1 = 0;
3896 }
3897 if (!is_numeric($col2)) {
3898 $col2 = -1;
3899 }
3900 if (!is_numeric($col3)) {
3901 $col3 = -1;
3902 }
3903 if (!is_numeric($col4)) {
3904 $col4 = -1;
3905 }
3906 // set color by case
3907 $suffix = '';
3908 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
3909 // Grey scale
3910 $col1 = max(0, min(255, $col1));
3911 $intcolor = array('G' => $col1);
3912 $pdfcolor = sprintf('%F ', ($col1 / 255));
3913 $suffix = 'g';
3914 } elseif ($col4 == -1) {
3915 // RGB
3916 $col1 = max(0, min(255, $col1));
3917 $col2 = max(0, min(255, $col2));
3918 $col3 = max(0, min(255, $col3));
3919 $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
3920 $pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255));
3921 $suffix = 'rg';
3922 } else {
3923 $col1 = max(0, min(100, $col1));
3924 $col2 = max(0, min(100, $col2));
3925 $col3 = max(0, min(100, $col3));
3926 $col4 = max(0, min(100, $col4));
3927 if (empty($name)) {
3928 // CMYK
3929 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
3930 $pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
3931 $suffix = 'k';
3932 } else {
3933 // SPOT COLOR
3934 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
3935 $this->AddSpotColor($name, $col1, $col2, $col3, $col4);
3936 $pdfcolor = $this->setSpotColor($type, $name, 100);
3937 }
3938 }
3939 switch ($type) {
3940 case 'draw': {
3941 $pdfcolor .= strtoupper($suffix);
3942 $this->DrawColor = $pdfcolor;
3943 $this->strokecolor = $intcolor;
3944 break;
3945 }
3946 case 'fill': {
3947 $pdfcolor .= $suffix;
3948 $this->FillColor = $pdfcolor;
3949 $this->bgcolor = $intcolor;
3950 break;
3951 }
3952 case 'text': {
3953 $pdfcolor .= $suffix;
3954 $this->TextColor = $pdfcolor;
3955 $this->fgcolor = $intcolor;
3956 break;
3957 }
3958 }
3959 $this->ColorFlag = ($this->FillColor != $this->TextColor);
3960 if (($type != 'text') AND ($this->state == 2)) {
3961 if (!$ret) {
3962 $this->_out($pdfcolor);
3963 }
3964 return $pdfcolor;
3965 }
3966 return '';
3967 }
3968
3982 public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3983 return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name);
3984 }
3985
3999 public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4000 return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name);
4001 }
4002
4016 public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4017 return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name);
4018 }
4019
4032 public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4033 return $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont), $s, $this->tmprtl, $this->isunicode, $this->CurrentFont), $fontname, $fontstyle, $fontsize, $getarray);
4034 }
4035
4048 public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4049 // store current values
4050 if (!TCPDF_STATIC::empty_string($fontname)) {
4051 $prev_FontFamily = $this->FontFamily;
4052 $prev_FontStyle = $this->FontStyle;
4053 $prev_FontSizePt = $this->FontSizePt;
4054 $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false);
4055 }
4056 // convert UTF-8 array to Latin1 if required
4057 if ($this->isunicode AND (!$this->isUnicodeFont())) {
4059 }
4060 $w = 0; // total width
4061 $wa = array(); // array of characters widths
4062 foreach ($sa as $ck => $char) {
4063 // character width
4064 $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)]));
4065 $wa[] = $cw;
4066 $w += $cw;
4067 }
4068 // restore previous values
4069 if (!TCPDF_STATIC::empty_string($fontname)) {
4070 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
4071 }
4072 if ($getarray) {
4073 return $wa;
4074 }
4075 return $w;
4076 }
4077
4087 public function GetCharWidth($char, $notlast=true) {
4088 // get raw width
4089 $chw = $this->getRawCharWidth($char);
4090 if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) {
4091 // increase/decrease font spacing
4092 $chw += $this->font_spacing;
4093 }
4094 if ($this->font_stretching != 100) {
4095 // fixed stretching mode
4096 $chw *= ($this->font_stretching / 100);
4097 }
4098 return $chw;
4099 }
4100
4109 public function getRawCharWidth($char) {
4110 if ($char == 173) {
4111 // SHY character will not be printed
4112 return (0);
4113 }
4114 if (isset($this->CurrentFont['cw'][$char])) {
4115 $w = $this->CurrentFont['cw'][$char];
4116 } elseif (isset($this->CurrentFont['dw'])) {
4117 // default width
4118 $w = $this->CurrentFont['dw'];
4119 } elseif (isset($this->CurrentFont['cw'][32])) {
4120 // default width
4121 $w = $this->CurrentFont['cw'][32];
4122 } else {
4123 $w = 600;
4124 }
4125 return $this->getAbsFontMeasure($w);
4126 }
4127
4135 public function GetNumChars($s) {
4136 if ($this->isUnicodeFont()) {
4137 return count(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont));
4138 }
4139 return strlen($s);
4140 }
4141
4147 protected function getFontsList() {
4148 if (($fontsdir = opendir(TCPDF_FONTS::_getfontpath())) !== false) {
4149 while (($file = readdir($fontsdir)) !== false) {
4150 if (substr($file, -4) == '.php') {
4151 array_push($this->fontlist, strtolower(basename($file, '.php')));
4152 }
4153 }
4154 closedir($fontsdir);
4155 }
4156 }
4157
4171 public function AddFont($family, $style='', $fontfile='', $subset='default') {
4172 if ($subset === 'default') {
4173 $subset = $this->font_subsetting;
4174 }
4175 if ($this->pdfa_mode) {
4176 $subset = false;
4177 }
4178 if (TCPDF_STATIC::empty_string($family)) {
4179 if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
4180 $family = $this->FontFamily;
4181 } else {
4182 $this->Error('Empty font family');
4183 }
4184 }
4185 // move embedded styles on $style
4186 if (substr($family, -1) == 'I') {
4187 $style .= 'I';
4188 $family = substr($family, 0, -1);
4189 }
4190 if (substr($family, -1) == 'B') {
4191 $style .= 'B';
4192 $family = substr($family, 0, -1);
4193 }
4194 // normalize family name
4195 $family = strtolower($family);
4196 if ((!$this->isunicode) AND ($family == 'arial')) {
4197 $family = 'helvetica';
4198 }
4199 if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
4200 $style = '';
4201 }
4202 if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) {
4203 // all fonts must be embedded
4204 $family = 'pdfa'.$family;
4205 }
4206 $tempstyle = strtoupper($style);
4207 $style = '';
4208 // underline
4209 if (strpos($tempstyle, 'U') !== false) {
4210 $this->underline = true;
4211 } else {
4212 $this->underline = false;
4213 }
4214 // line-through (deleted)
4215 if (strpos($tempstyle, 'D') !== false) {
4216 $this->linethrough = true;
4217 } else {
4218 $this->linethrough = false;
4219 }
4220 // overline
4221 if (strpos($tempstyle, 'O') !== false) {
4222 $this->overline = true;
4223 } else {
4224 $this->overline = false;
4225 }
4226 // bold
4227 if (strpos($tempstyle, 'B') !== false) {
4228 $style .= 'B';
4229 }
4230 // oblique
4231 if (strpos($tempstyle, 'I') !== false) {
4232 $style .= 'I';
4233 }
4234 $bistyle = $style;
4235 $fontkey = $family.$style;
4236 $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : '');
4237 $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
4238 // check if the font has been already added
4239 $fb = $this->getFontBuffer($fontkey);
4240 if ($fb !== false) {
4241 if ($this->inxobj) {
4242 // we are inside an XObject template
4243 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i'];
4244 }
4245 return $fontdata;
4246 }
4247 // get specified font directory (if any)
4248 $fontdir = false;
4249 if (!TCPDF_STATIC::empty_string($fontfile)) {
4250 $fontdir = dirname($fontfile);
4251 if (TCPDF_STATIC::empty_string($fontdir) OR ($fontdir == '.')) {
4252 $fontdir = '';
4253 } else {
4254 $fontdir .= '/';
4255 }
4256 }
4257 // true when the font style variation is missing
4258 $missing_style = false;
4259 // search and include font file
4260 if (TCPDF_STATIC::empty_string($fontfile) OR (!@file_exists($fontfile))) {
4261 // build a standard filenames for specified font
4262 $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
4263 $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);
4264 if (TCPDF_STATIC::empty_string($fontfile)) {
4265 $missing_style = true;
4266 // try to remove the style part
4267 $tmp_fontfile = str_replace(' ', '', $family).'.php';
4268 $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);
4269 }
4270 }
4271 // include font file
4272 if (!TCPDF_STATIC::empty_string($fontfile) AND (@file_exists($fontfile))) {
4273 include($fontfile);
4274 } else {
4275 $this->Error('Could not include font definition file: '.$family.'');
4276 }
4277 // check font parameters
4278 if ((!isset($type)) OR (!isset($cw))) {
4279 $this->Error('The font definition file has a bad format: '.$fontfile.'');
4280 }
4281 // SET default parameters
4282 if (!isset($file) OR TCPDF_STATIC::empty_string($file)) {
4283 $file = '';
4284 }
4285 if (!isset($enc) OR TCPDF_STATIC::empty_string($enc)) {
4286 $enc = '';
4287 }
4288 if (!isset($cidinfo) OR TCPDF_STATIC::empty_string($cidinfo)) {
4289 $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0);
4290 $cidinfo['uni2cid'] = array();
4291 }
4292 if (!isset($ctg) OR TCPDF_STATIC::empty_string($ctg)) {
4293 $ctg = '';
4294 }
4295 if (!isset($desc) OR TCPDF_STATIC::empty_string($desc)) {
4296 $desc = array();
4297 }
4298 if (!isset($up) OR TCPDF_STATIC::empty_string($up)) {
4299 $up = -100;
4300 }
4301 if (!isset($ut) OR TCPDF_STATIC::empty_string($ut)) {
4302 $ut = 50;
4303 }
4304 if (!isset($cw) OR TCPDF_STATIC::empty_string($cw)) {
4305 $cw = array();
4306 }
4307 if (!isset($dw) OR TCPDF_STATIC::empty_string($dw)) {
4308 // set default width
4309 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
4310 $dw = $desc['MissingWidth'];
4311 } elseif (isset($cw[32])) {
4312 $dw = $cw[32];
4313 } else {
4314 $dw = 600;
4315 }
4316 }
4318 if ($type == 'core') {
4319 $name = $this->CoreFonts[$fontkey];
4320 $subset = false;
4321 } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
4322 $subset = false;
4323 } elseif ($type == 'TrueTypeUnicode') {
4324 $enc = 'Identity-H';
4325 } elseif ($type == 'cidfont0') {
4326 if ($this->pdfa_mode) {
4327 $this->Error('All fonts must be embedded in PDF/A mode!');
4328 }
4329 } else {
4330 $this->Error('Unknow font type: '.$type.'');
4331 }
4332 // set name if unset
4333 if (!isset($name) OR empty($name)) {
4334 $name = $fontkey;
4335 }
4336 // create artificial font style variations if missing (only works with non-embedded fonts)
4337 if (($type != 'core') AND $missing_style) {
4338 // style variations
4339 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
4340 $name .= $styles[$bistyle];
4341 // artificial bold
4342 if (strpos($bistyle, 'B') !== false) {
4343 if (isset($desc['StemV'])) {
4344 // from normal to bold
4345 $desc['StemV'] = round($desc['StemV'] * 1.75);
4346 } else {
4347 // bold
4348 $desc['StemV'] = 123;
4349 }
4350 }
4351 // artificial italic
4352 if (strpos($bistyle, 'I') !== false) {
4353 if (isset($desc['ItalicAngle'])) {
4354 $desc['ItalicAngle'] -= 11;
4355 } else {
4356 $desc['ItalicAngle'] = -11;
4357 }
4358 if (isset($desc['Flags'])) {
4359 $desc['Flags'] |= 64; //bit 7
4360 } else {
4361 $desc['Flags'] = 64;
4362 }
4363 }
4364 }
4365 // check if the array of characters bounding boxes is defined
4366 if (!isset($cbbox)) {
4367 $cbbox = array();
4368 }
4369 // initialize subsetchars
4370 $subsetchars = array_fill(0, 255, true);
4371 $this->setFontBuffer($fontkey, array('fontkey' => $fontkey, 'i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'cbbox' => $cbbox, 'dw' => $dw, 'enc' => $enc, 'cidinfo' => $cidinfo, 'file' => $file, 'ctg' => $ctg, 'subset' => $subset, 'subsetchars' => $subsetchars));
4372 if ($this->inxobj) {
4373 // we are inside an XObject template
4374 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts;
4375 }
4376 if (isset($diff) AND (!empty($diff))) {
4377 //Search existing encodings
4378 $d = 0;
4379 $nb = count($this->diffs);
4380 for ($i=1; $i <= $nb; ++$i) {
4381 if ($this->diffs[$i] == $diff) {
4382 $d = $i;
4383 break;
4384 }
4385 }
4386 if ($d == 0) {
4387 $d = $nb + 1;
4388 $this->diffs[$d] = $diff;
4389 }
4390 $this->setFontSubBuffer($fontkey, 'diff', $d);
4391 }
4393 if (!isset($this->FontFiles[$file])) {
4394 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
4395 $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4396 } elseif ($type != 'core') {
4397 $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4398 }
4399 } else {
4400 // update fontkeys that are sharing this font file
4401 $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset);
4402 if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) {
4403 $this->FontFiles[$file]['fontkeys'][] = $fontkey;
4404 }
4405 }
4406 }
4407 return $fontdata;
4408 }
4409
4427 public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) {
4428 //Select a font; size given in points
4429 if ($size === null) {
4431 }
4432 if ($size < 0) {
4433 $size = 0;
4434 }
4435 // try to add font (if not already added)
4436 $fontdata = $this->AddFont($family, $style, $fontfile, $subset);
4437 $this->FontFamily = $fontdata['family'];
4438 $this->FontStyle = $fontdata['style'];
4439 if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
4440 // save subset chars of the previous font
4441 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
4442 }
4443 $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
4444 $this->SetFontSize($size, $out);
4445 }
4446
4455 public function SetFontSize($size, $out=true) {
4456 // font size in points
4457 $this->FontSizePt = $size;
4458 // font size in user units
4459 $this->FontSize = $size / $this->k;
4460 // calculate some font metrics
4461 if (isset($this->CurrentFont['desc']['FontBBox'])) {
4462 $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4463 $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
4464 } else {
4465 $font_height = $size * 1.219;
4466 }
4467 if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
4468 $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000);
4469 }
4470 if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) {
4471 $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000);
4472 }
4473 if (!isset($font_ascent) AND !isset($font_descent)) {
4474 // core font
4475 $font_ascent = 0.76 * $font_height;
4476 $font_descent = $font_height - $font_ascent;
4477 } elseif (!isset($font_descent)) {
4478 $font_descent = $font_height - $font_ascent;
4479 } elseif (!isset($font_ascent)) {
4480 $font_ascent = $font_height - $font_descent;
4481 }
4482 $this->FontAscent = ($font_ascent / $this->k);
4483 $this->FontDescent = ($font_descent / $this->k);
4484 if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i'])) AND ($this->state == 2)) {
4485 $this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4486 }
4487 }
4488
4495 public function getFontBBox() {
4496 $fbbox = array();
4497 if (isset($this->CurrentFont['desc']['FontBBox'])) {
4498 $tmpbbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4499 $fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox);
4500 } else {
4501 // Find max width
4502 if (isset($this->CurrentFont['desc']['MaxWidth'])) {
4503 $maxw = $this->getAbsFontMeasure(intval($this->CurrentFont['desc']['MaxWidth']));
4504 } else {
4505 $maxw = 0;
4506 if (isset($this->CurrentFont['desc']['MissingWidth'])) {
4507 $maxw = max($maxw, $this->CurrentFont['desc']['MissingWidth']);
4508 }
4509 if (isset($this->CurrentFont['desc']['AvgWidth'])) {
4510 $maxw = max($maxw, $this->CurrentFont['desc']['AvgWidth']);
4511 }
4512 if (isset($this->CurrentFont['dw'])) {
4513 $maxw = max($maxw, $this->CurrentFont['dw']);
4514 }
4515 foreach ($this->CurrentFont['cw'] as $char => $w) {
4516 $maxw = max($maxw, $w);
4517 }
4518 if ($maxw == 0) {
4519 $maxw = 600;
4520 }
4521 $maxw = $this->getAbsFontMeasure($maxw);
4522 }
4523 $fbbox = array(0, (0 - $this->FontDescent), $maxw, $this->FontAscent);
4524 }
4525 return $fbbox;
4526 }
4527
4534 public function getAbsFontMeasure($s) {
4535 return ($s * $this->FontSize / 1000);
4536 }
4537
4544 public function getCharBBox($char) {
4545 $c = intval($char);
4546 if (isset($this->CurrentFont['cw'][$c])) {
4547 // glyph is defined ... use zero width & height for glyphs without outlines
4548 $result = array(0,0,0,0);
4549 if (isset($this->CurrentFont['cbbox'][$c])) {
4550 $result = $this->CurrentFont['cbbox'][$c];
4551 }
4552 return array_map(array($this,'getAbsFontMeasure'), $result);
4553 }
4554 return false;
4555 }
4556
4567 public function getFontDescent($font, $style='', $size=0) {
4568 $fontdata = $this->AddFont($font, $style);
4569 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4570 if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
4571 $descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
4572 } else {
4573 $descent = (1.219 * 0.24 * $size);
4574 }
4575 return ($descent / $this->k);
4576 }
4577
4588 public function getFontAscent($font, $style='', $size=0) {
4589 $fontdata = $this->AddFont($font, $style);
4590 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4591 if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
4592 $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
4593 } else {
4594 $ascent = 1.219 * 0.76 * $size;
4595 }
4596 return ($ascent / $this->k);
4597 }
4598
4608 public function isCharDefined($char, $font='', $style='') {
4609 if (is_string($char)) {
4610 // get character code
4611 $char = TCPDF_FONTS::UTF8StringToArray($char, $this->isunicode, $this->CurrentFont);
4612 $char = $char[0];
4613 }
4614 if (TCPDF_STATIC::empty_string($font)) {
4616 return (isset($this->CurrentFont['cw'][intval($char)]));
4617 }
4618 $font = $this->FontFamily;
4619 }
4620 $fontdata = $this->AddFont($font, $style);
4621 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4622 return (isset($fontinfo['cw'][intval($char)]));
4623 }
4624
4635 public function replaceMissingChars($text, $font='', $style='', $subs=array()) {
4636 if (empty($subs)) {
4637 return $text;
4638 }
4639 if (TCPDF_STATIC::empty_string($font)) {
4640 $font = $this->FontFamily;
4641 }
4642 $fontdata = $this->AddFont($font, $style);
4643 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4644 $uniarr = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
4645 foreach ($uniarr as $k => $chr) {
4646 if (!isset($fontinfo['cw'][$chr])) {
4647 // this character is missing on the selected font
4648 if (isset($subs[$chr])) {
4649 // we have available substitutions
4650 if (is_array($subs[$chr])) {
4651 foreach($subs[$chr] as $s) {
4652 if (isset($fontinfo['cw'][$s])) {
4653 $uniarr[$k] = $s;
4654 break;
4655 }
4656 }
4657 } elseif (isset($fontinfo['cw'][$subs[$chr]])) {
4658 $uniarr[$k] = $subs[$chr];
4659 }
4660 }
4661 }
4662 }
4663 return TCPDF_FONTS::UniArrSubString(TCPDF_FONTS::UTF8ArrayToUniArray($uniarr, $this->isunicode));
4664 }
4665
4672 public function SetDefaultMonospacedFont($font) {
4673 $this->default_monospaced_font = $font;
4674 }
4675
4683 public function AddLink() {
4684 // create a new internal link
4685 $n = count($this->links) + 1;
4686 $this->links[$n] = array('p' => 0, 'y' => 0, 'f' => false);
4687 return $n;
4688 }
4689
4699 public function SetLink($link, $y=0, $page=-1) {
4700 $fixed = false;
4701 if (!empty($page) AND ($page[0] == '*')) {
4702 $page = intval(substr($page, 1));
4703 // this page number will not be changed when moving/add/deleting pages
4704 $fixed = true;
4705 }
4706 if ($page < 0) {
4708 }
4709 if ($y == -1) {
4710 $y = $this->y;
4711 }
4712 $this->links[$link] = array('p' => $page, 'y' => $y, 'f' => $fixed);
4713 }
4714
4728 public function Link($x, $y, $w, $h, $link, $spaces=0) {
4729 $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
4730 }
4731
4745 public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
4746 if ($this->inxobj) {
4747 // store parameters for later use on template
4748 $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
4749 return;
4750 }
4751 if ($x === '') {
4752 $x = $this->x;
4753 }
4754 if ($y === '') {
4755 $y = $this->y;
4756 }
4757 // check page for no-write regions and adapt page margins if necessary
4758 list($x, $y) = $this->checkPageRegions($h, $x, $y);
4759 // recalculate coordinates to account for graphic transformations
4760 if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) {
4761 for ($i=$this->transfmatrix_key; $i > 0; --$i) {
4762 $maxid = count($this->transfmatrix[$i]) - 1;
4763 for ($j=$maxid; $j >= 0; --$j) {
4764 $ctm = $this->transfmatrix[$i][$j];
4765 if (isset($ctm['a'])) {
4766 $x = $x * $this->k;
4767 $y = ($this->h - $y) * $this->k;
4768 $w = $w * $this->k;
4769 $h = $h * $this->k;
4770 // top left
4771 $xt = $x;
4772 $yt = $y;
4773 $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4774 $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4775 // top right
4776 $xt = $x + $w;
4777 $yt = $y;
4778 $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4779 $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4780 // bottom left
4781 $xt = $x;
4782 $yt = $y - $h;
4783 $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4784 $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4785 // bottom right
4786 $xt = $x + $w;
4787 $yt = $y - $h;
4788 $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4789 $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4790 // new coordinates (rectangle area)
4791 $x = min($x1, $x2, $x3, $x4);
4792 $y = max($y1, $y2, $y3, $y4);
4793 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
4794 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
4795 $x = $x / $this->k;
4796 $y = $this->h - ($y / $this->k);
4797 }
4798 }
4799 }
4800 }
4801 if ($this->page <= 0) {
4802 $page = 1;
4803 } else {
4805 }
4806 if (!isset($this->PageAnnots[$page])) {
4807 $this->PageAnnots[$page] = array();
4808 }
4809 $this->PageAnnots[$page][] = array('n' => ++$this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
4810 if (!$this->pdfa_mode) {
4811 if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!TCPDF_STATIC::empty_string($opt['FS']))
4812 AND (@file_exists($opt['FS']) OR TCPDF_STATIC::isValidURL($opt['FS']))
4813 AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
4814 $this->embeddedfiles[basename($opt['FS'])] = array('f' => ++$this->n, 'n' => ++$this->n, 'file' => $opt['FS']);
4815 }
4816 }
4817 // Add widgets annotation's icons
4818 if (isset($opt['mk']['i']) AND @file_exists($opt['mk']['i'])) {
4819 $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
4820 }
4821 if (isset($opt['mk']['ri']) AND @file_exists($opt['mk']['ri'])) {
4822 $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4823 }
4824 if (isset($opt['mk']['ix']) AND @file_exists($opt['mk']['ix'])) {
4825 $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4826 }
4827 }
4828
4835 protected function _putEmbeddedFiles() {
4836 if ($this->pdfa_mode) {
4837 // embedded files are not allowed in PDF/A mode
4838 return;
4839 }
4840 reset($this->embeddedfiles);
4841 foreach ($this->embeddedfiles as $filename => $filedata) {
4842 $data = TCPDF_STATIC::fileGetContents($filedata['file']);
4843 if ($data !== FALSE) {
4844 $rawsize = strlen($data);
4845 if ($rawsize > 0) {
4846 // update name tree
4847 $this->efnames[$filename] = $filedata['f'].' 0 R';
4848 // embedded file specification object
4849 $out = $this->_getobj($filedata['f'])."\n";
4850 $out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']).' /EF <</F '.$filedata['n'].' 0 R>> >>';
4851 $out .= "\n".'endobj';
4852 $this->_out($out);
4853 // embedded file object
4854 $filter = '';
4855 if ($this->compress) {
4856 $data = gzcompress($data);
4857 $filter = ' /Filter /FlateDecode';
4858 }
4859 $stream = $this->_getrawstream($data, $filedata['n']);
4860 $out = $this->_getobj($filedata['n'])."\n";
4861 $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>';
4862 $out .= ' stream'."\n".$stream."\n".'endstream';
4863 $out .= "\n".'endobj';
4864 $this->_out($out);
4865 }
4866 }
4867 }
4868 }
4869
4893 public function Text($x, $y, $txt, $fstroke=false, $fclip=false, $ffill=true, $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M', $rtloff=false) {
4896 $this->setTextRenderingMode($fstroke, $ffill, $fclip);
4897 $this->SetXY($x, $y, $rtloff);
4898 $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
4899 // restore previous rendering mode
4900 $this->textrendermode = $textrendermode;
4901 $this->textstrokewidth = $textstrokewidth;
4902 }
4903
4913 public function AcceptPageBreak() {
4914 if ($this->num_columns > 1) {
4915 // multi column mode
4916 if ($this->current_column < ($this->num_columns - 1)) {
4917 // go to next column
4918 $this->selectColumn($this->current_column + 1);
4919 } elseif ($this->AutoPageBreak) {
4920 // add a new page
4921 $this->AddPage();
4922 // set first column
4923 $this->selectColumn(0);
4924 }
4925 // avoid page breaking from checkPageBreak()
4926 return false;
4927 }
4928 return $this->AutoPageBreak;
4929 }
4930
4940 protected function checkPageBreak($h=0, $y='', $addpage=true) {
4942 $y = $this->y;
4943 }
4944 $current_page = $this->page;
4945 if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) {
4946 if ($addpage) {
4947 //Automatic page break
4948 $x = $this->x;
4949 $this->AddPage($this->CurOrientation);
4950 $this->y = $this->tMargin;
4951 $oldpage = $this->page - 1;
4952 if ($this->rtl) {
4953 if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
4954 $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
4955 } else {
4956 $this->x = $x;
4957 }
4958 } else {
4959 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
4960 $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
4961 } else {
4962 $this->x = $x;
4963 }
4964 }
4965 }
4966 return true;
4967 }
4968 if ($current_page != $this->page) {
4969 // account for columns mode
4970 return true;
4971 }
4972 return false;
4973 }
4974
4994 public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') {
4995 $prev_cell_margin = $this->cell_margin;
4996 $prev_cell_padding = $this->cell_padding;
4997 $this->adjustCellPadding($border);
4998 if (!$ignore_min_height) {
4999 $min_cell_height = $this->getCellHeight($this->FontSize);
5000 if ($h < $min_cell_height) {
5001 $h = $min_cell_height;
5002 }
5003 }
5004 $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']);
5005 // apply text shadow if enabled
5006 if ($this->txtshadow['enabled']) {
5007 // save data
5008 $x = $this->x;
5009 $y = $this->y;
5010 $bc = $this->bgcolor;
5011 $fc = $this->fgcolor;
5014 // print shadow
5015 $this->x += $this->txtshadow['depth_w'];
5016 $this->y += $this->txtshadow['depth_h'];
5017 $this->SetFillColorArray($this->txtshadow['color']);
5018 $this->SetTextColorArray($this->txtshadow['color']);
5019 $this->SetDrawColorArray($this->txtshadow['color']);
5020 if ($this->txtshadow['opacity'] != $alpha['CA']) {
5021 $this->setAlpha($this->txtshadow['opacity'], $this->txtshadow['blend_mode']);
5022 }
5023 if ($this->state == 2) {
5024 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5025 }
5026 //restore data
5027 $this->x = $x;
5028 $this->y = $y;
5029 $this->SetFillColorArray($bc);
5030 $this->SetTextColorArray($fc);
5031 $this->SetDrawColorArray($sc);
5032 if ($this->txtshadow['opacity'] != $alpha['CA']) {
5033 $this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']);
5034 }
5035 }
5036 if ($this->state == 2) {
5037 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5038 }
5039 $this->cell_padding = $prev_cell_padding;
5040 $this->cell_margin = $prev_cell_margin;
5041 }
5042
5063 protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') {
5064 // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space
5065 $txt = str_replace(TCPDF_FONTS::unichr(160, $this->isunicode), ' ', $txt);
5066 $prev_cell_margin = $this->cell_margin;
5067 $prev_cell_padding = $this->cell_padding;
5068 $txt = TCPDF_STATIC::removeSHY($txt, $this->isunicode);
5069 $rs = ''; //string to be returned
5070 $this->adjustCellPadding($border);
5071 if (!$ignore_min_height) {
5072 $min_cell_height = $this->getCellHeight($this->FontSize);
5073 if ($h < $min_cell_height) {
5074 $h = $min_cell_height;
5075 }
5076 }
5077 $k = $this->k;
5078 // check page for no-write regions and adapt page margins if necessary
5079 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
5080 if ($this->rtl) {
5081 $x = $this->x - $this->cell_margin['R'];
5082 } else {
5083 $x = $this->x + $this->cell_margin['L'];
5084 }
5085 $y = $this->y + $this->cell_margin['T'];
5086 $prev_font_stretching = $this->font_stretching;
5087 $prev_font_spacing = $this->font_spacing;
5088 // cell vertical alignment
5089 switch ($calign) {
5090 case 'A': {
5091 // font top
5092 switch ($valign) {
5093 case 'T': {
5094 // top
5095 $y -= $this->cell_padding['T'];
5096 break;
5097 }
5098 case 'B': {
5099 // bottom
5100 $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent);
5101 break;
5102 }
5103 default:
5104 case 'C':
5105 case 'M': {
5106 // center
5107 $y -= (($h - $this->FontAscent - $this->FontDescent) / 2);
5108 break;
5109 }
5110 }
5111 break;
5112 }
5113 case 'L': {
5114 // font baseline
5115 switch ($valign) {
5116 case 'T': {
5117 // top
5118 $y -= ($this->cell_padding['T'] + $this->FontAscent);
5119 break;
5120 }
5121 case 'B': {
5122 // bottom
5123 $y -= ($h - $this->cell_padding['B'] - $this->FontDescent);
5124 break;
5125 }
5126 default:
5127 case 'C':
5128 case 'M': {
5129 // center
5130 $y -= (($h + $this->FontAscent - $this->FontDescent) / 2);
5131 break;
5132 }
5133 }
5134 break;
5135 }
5136 case 'D': {
5137 // font bottom
5138 switch ($valign) {
5139 case 'T': {
5140 // top
5141 $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent);
5142 break;
5143 }
5144 case 'B': {
5145 // bottom
5146 $y -= ($h - $this->cell_padding['B']);
5147 break;
5148 }
5149 default:
5150 case 'C':
5151 case 'M': {
5152 // center
5153 $y -= (($h + $this->FontAscent + $this->FontDescent) / 2);
5154 break;
5155 }
5156 }
5157 break;
5158 }
5159 case 'B': {
5160 // cell bottom
5161 $y -= $h;
5162 break;
5163 }
5164 case 'C':
5165 case 'M': {
5166 // cell center
5167 $y -= ($h / 2);
5168 break;
5169 }
5170 default:
5171 case 'T': {
5172 // cell top
5173 break;
5174 }
5175 }
5176 // text vertical alignment
5177 switch ($valign) {
5178 case 'T': {
5179 // top
5180 $yt = $y + $this->cell_padding['T'];
5181 break;
5182 }
5183 case 'B': {
5184 // bottom
5185 $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent;
5186 break;
5187 }
5188 default:
5189 case 'C':
5190 case 'M': {
5191 // center
5192 $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2);
5193 break;
5194 }
5195 }
5196 $basefonty = $yt + $this->FontAscent;
5197 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5198 if ($this->rtl) {
5199 $w = $x - $this->lMargin;
5200 } else {
5201 $w = $this->w - $this->rMargin - $x;
5202 }
5203 }
5204 $s = '';
5205 // fill and borders
5206 if (is_string($border) AND (strlen($border) == 4)) {
5207 // full border
5208 $border = 1;
5209 }
5210 if ($fill OR ($border == 1)) {
5211 if ($fill) {
5212 $op = ($border == 1) ? 'B' : 'f';
5213 } else {
5214 $op = 'S';
5215 }
5216 if ($this->rtl) {
5217 $xk = (($x - $w) * $k);
5218 } else {
5219 $xk = ($x * $k);
5220 }
5221 $s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op);
5222 }
5223 // draw borders
5224 $s .= $this->getCellBorder($x, $y, $w, $h, $border);
5225 if ($txt != '') {
5226 $txt2 = $txt;
5227 if ($this->isunicode) {
5228 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5229 $txt2 = TCPDF_FONTS::UTF8ToLatin1($txt2, $this->isunicode, $this->CurrentFont);
5230 } else {
5231 $unicode = TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont); // array of UTF-8 unicode values
5232 $unicode = TCPDF_FONTS::utf8Bidi($unicode, '', $this->tmprtl, $this->isunicode, $this->CurrentFont);
5233 // replace thai chars (if any)
5234 if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) {
5235 // number of chars
5236 $numchars = count($unicode);
5237 // po pla, for far, for fan
5238 $longtail = array(0x0e1b, 0x0e1d, 0x0e1f);
5239 // do chada, to patak
5240 $lowtail = array(0x0e0e, 0x0e0f);
5241 // mai hun arkad, sara i, sara ii, sara ue, sara uee
5242 $upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37);
5243 // mai ek, mai tho, mai tri, mai chattawa, karan
5244 $tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c);
5245 // sara u, sara uu, pinthu
5246 $lowvowel = array(0x0e38, 0x0e39, 0x0e3a);
5247 $output = array();
5248 for ($i = 0; $i < $numchars; $i++) {
5249 if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) {
5250 $ch0 = $unicode[$i];
5251 $ch1 = ($i > 0) ? $unicode[($i - 1)] : 0;
5252 $ch2 = ($i > 1) ? $unicode[($i - 2)] : 0;
5253 $chn = ($i < ($numchars - 1)) ? $unicode[($i + 1)] : 0;
5254 if (in_array($ch0, $tonemark)) {
5255 if ($chn == 0x0e33) {
5256 // sara um
5257 if (in_array($ch1, $longtail)) {
5258 // tonemark at upper left
5259 $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5260 } else {
5261 // tonemark at upper right (normal position)
5262 $output[] = $ch0;
5263 }
5264 } elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) {
5265 // tonemark at lower left
5266 $output[] = $this->replaceChar($ch0, (0xf705 + $ch0 - 0x0e48));
5267 } elseif (in_array($ch1, $upvowel)) {
5268 if (in_array($ch2, $longtail)) {
5269 // tonemark at upper left
5270 $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5271 } else {
5272 // tonemark at upper right (normal position)
5273 $output[] = $ch0;
5274 }
5275 } else {
5276 // tonemark at lower right
5277 $output[] = $this->replaceChar($ch0, (0xf70a + $ch0 - 0x0e48));
5278 }
5279 } elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) {
5280 // add lower left nikhahit and sara aa
5281 if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) {
5282 $output[] = 0xf711;
5283 $this->CurrentFont['subsetchars'][0xf711] = true;
5284 $output[] = 0x0e32;
5285 $this->CurrentFont['subsetchars'][0x0e32] = true;
5286 } else {
5287 $output[] = $ch0;
5288 }
5289 } elseif (in_array($ch1, $longtail)) {
5290 if ($ch0 == 0x0e31) {
5291 // lower left mai hun arkad
5292 $output[] = $this->replaceChar($ch0, 0xf710);
5293 } elseif (in_array($ch0, $upvowel)) {
5294 // lower left
5295 $output[] = $this->replaceChar($ch0, (0xf701 + $ch0 - 0x0e34));
5296 } elseif ($ch0 == 0x0e47) {
5297 // lower left mai tai koo
5298 $output[] = $this->replaceChar($ch0, 0xf712);
5299 } else {
5300 // normal character
5301 $output[] = $ch0;
5302 }
5303 } elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) {
5304 // lower vowel
5305 $output[] = $this->replaceChar($ch0, (0xf718 + $ch0 - 0x0e38));
5306 } elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) {
5307 // yo ying without lower part
5308 $output[] = $this->replaceChar($ch0, 0xf70f);
5309 } elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) {
5310 // tho santan without lower part
5311 $output[] = $this->replaceChar($ch0, 0xf700);
5312 } else {
5313 $output[] = $ch0;
5314 }
5315 } else {
5316 // non-thai character
5317 $output[] = $unicode[$i];
5318 }
5319 }
5320 $unicode = $output;
5321 // update font subsetchars
5322 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
5323 } // end of K_THAI_TOPCHARS
5324 $txt2 = TCPDF_FONTS::arrUTF8ToUTF16BE($unicode, false);
5325 }
5326 }
5327 $txt2 = TCPDF_STATIC::_escape($txt2);
5328 // get current text width (considering general font stretching and spacing)
5329 $txwidth = $this->GetStringWidth($txt);
5330 $width = $txwidth;
5331 // check for stretch mode
5332 if ($stretch > 0) {
5333 // calculate ratio between cell width and text width
5334 if ($width <= 0) {
5335 $ratio = 1;
5336 } else {
5337 $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width);
5338 }
5339 // check if stretching is required
5340 if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) {
5341 // the text will be stretched to fit cell width
5342 if ($stretch > 2) {
5343 // set new character spacing
5344 $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100));
5345 } else {
5346 // set new horizontal stretching
5347 $this->font_stretching *= $ratio;
5348 }
5349 // recalculate text width (the text fills the entire cell)
5350 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5351 // reset alignment
5352 $align = '';
5353 }
5354 }
5355 if ($this->font_stretching != 100) {
5356 // apply font stretching
5357 $rs .= sprintf('BT %F Tz ET ', $this->font_stretching);
5358 }
5359 if ($this->font_spacing != 0) {
5360 // increase/decrease font spacing
5361 $rs .= sprintf('BT %F Tc ET ', ($this->font_spacing * $this->k));
5362 }
5363 if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5364 $s .= 'q '.$this->TextColor.' ';
5365 }
5366 // rendering mode
5367 $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, ($this->textstrokewidth * $this->k));
5368 // count number of spaces
5369 $ns = substr_count($txt, chr(32));
5370 // Justification
5371 $spacewidth = 0;
5372 if (($align == 'J') AND ($ns > 0)) {
5373 if ($this->isUnicodeFont()) {
5374 // get string width without spaces
5375 $width = $this->GetStringWidth(str_replace(' ', '', $txt));
5376 // calculate average space width
5377 $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / ($this->FontSize?$this->FontSize:1);
5378 if ($this->font_stretching != 100) {
5379 // word spacing is affected by stretching
5380 $spacewidth /= ($this->font_stretching / 100);
5381 }
5382 // set word position to be used with TJ operator
5383 $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2);
5384 $unicode_justification = true;
5385 } else {
5386 // get string width
5387 $width = $txwidth;
5388 // new space width
5389 $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k;
5390 if ($this->font_stretching != 100) {
5391 // word spacing (Tw) is affected by stretching
5392 $spacewidth /= ($this->font_stretching / 100);
5393 }
5394 // set word spacing
5395 $rs .= sprintf('BT %F Tw ET ', $spacewidth);
5396 }
5397 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5398 }
5399 // replace carriage return characters
5400 $txt2 = str_replace("\r", ' ', $txt2);
5401 switch ($align) {
5402 case 'C': {
5403 $dx = ($w - $width) / 2;
5404 break;
5405 }
5406 case 'R': {
5407 if ($this->rtl) {
5408 $dx = $this->cell_padding['R'];
5409 } else {
5410 $dx = $w - $width - $this->cell_padding['R'];
5411 }
5412 break;
5413 }
5414 case 'L': {
5415 if ($this->rtl) {
5416 $dx = $w - $width - $this->cell_padding['L'];
5417 } else {
5418 $dx = $this->cell_padding['L'];
5419 }
5420 break;
5421 }
5422 case 'J':
5423 default: {
5424 if ($this->rtl) {
5425 $dx = $this->cell_padding['R'];
5426 } else {
5427 $dx = $this->cell_padding['L'];
5428 }
5429 break;
5430 }
5431 }
5432 if ($this->rtl) {
5433 $xdx = $x - $dx - $width;
5434 } else {
5435 $xdx = $x + $dx;
5436 }
5437 $xdk = $xdx * $k;
5438 // print text
5439 $s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
5440 if (isset($uniblock)) {
5441 // print overlapping characters as separate string
5442 $xshift = 0; // horizontal shift
5443 $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k);
5444 $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1));
5445 foreach ($uniblock as $uk => $uniarr) {
5446 if (($uk % 2) == 0) {
5447 // x space to skip
5448 if ($spacewidth != 0) {
5449 // justification shift
5450 $xshift += (count(array_keys($uniarr, 32)) * $spw);
5451 }
5452 $xshift += $this->GetArrStringWidth($uniarr); // + shift justification
5453 } else {
5454 // character to print
5455 $topchr = TCPDF_FONTS::arrUTF8ToUTF16BE($uniarr, false);
5456 $topchr = TCPDF_STATIC::_escape($topchr);
5457 $s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr);
5458 }
5459 }
5460 }
5461 if ($this->underline) {
5462 $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
5463 }
5464 if ($this->linethrough) {
5465 $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
5466 }
5467 if ($this->overline) {
5468 $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
5469 }
5470 if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5471 $s .= ' Q';
5472 }
5473 if ($link) {
5474 $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns);
5475 }
5476 }
5477 // output cell
5478 if ($s) {
5479 // output cell
5480 $rs .= $s;
5481 if ($this->font_spacing != 0) {
5482 // reset font spacing mode
5483 $rs .= ' BT 0 Tc ET';
5484 }
5485 if ($this->font_stretching != 100) {
5486 // reset font stretching mode
5487 $rs .= ' BT 100 Tz ET';
5488 }
5489 }
5490 // reset word spacing
5491 if (!$this->isUnicodeFont() AND ($align == 'J')) {
5492 $rs .= ' BT 0 Tw ET';
5493 }
5494 // reset stretching and spacing
5495 $this->font_stretching = $prev_font_stretching;
5496 $this->font_spacing = $prev_font_spacing;
5497 $this->lasth = $h;
5498 if ($ln > 0) {
5499 //Go to the beginning of the next line
5500 $this->y = $y + $h + $this->cell_margin['B'];
5501 if ($ln == 1) {
5502 if ($this->rtl) {
5503 $this->x = $this->w - $this->rMargin;
5504 } else {
5505 $this->x = $this->lMargin;
5506 }
5507 }
5508 } else {
5509 // go left or right by case
5510 if ($this->rtl) {
5511 $this->x = $x - $w - $this->cell_margin['L'];
5512 } else {
5513 $this->x = $x + $w + $this->cell_margin['R'];
5514 }
5515 }
5516 $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
5517 $rs = $gstyles.$rs;
5518 $this->cell_padding = $prev_cell_padding;
5519 $this->cell_margin = $prev_cell_margin;
5520 return $rs;
5521 }
5522
5531 protected function replaceChar($oldchar, $newchar) {
5532 if ($this->isCharDefined($newchar)) {
5533 // add the new char on the subset list
5534 $this->CurrentFont['subsetchars'][$newchar] = true;
5535 // return the new character
5536 return $newchar;
5537 }
5538 // return the old char
5539 return $oldchar;
5540 }
5541
5554 protected function getCellBorder($x, $y, $w, $h, $brd) {
5555 $s = ''; // string to be returned
5556 if (empty($brd)) {
5557 return $s;
5558 }
5559 if ($brd == 1) {
5560 $brd = array('LRTB' => true);
5561 }
5562 // calculate coordinates for border
5563 $k = $this->k;
5564 if ($this->rtl) {
5565 $xeL = ($x - $w) * $k;
5566 $xeR = $x * $k;
5567 } else {
5568 $xeL = $x * $k;
5569 $xeR = ($x + $w) * $k;
5570 }
5571 $yeL = (($this->h - ($y + $h)) * $k);
5572 $yeT = (($this->h - $y) * $k);
5573 $xeT = $xeL;
5574 $xeB = $xeR;
5575 $yeR = $yeT;
5576 $yeB = $yeL;
5577 if (is_string($brd)) {
5578 // convert string to array
5579 $slen = strlen($brd);
5580 $newbrd = array();
5581 for ($i = 0; $i < $slen; ++$i) {
5582 $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
5583 }
5584 $brd = $newbrd;
5585 }
5586 if (isset($brd['mode'])) {
5587 $mode = $brd['mode'];
5588 unset($brd['mode']);
5589 } else {
5590 $mode = 'normal';
5591 }
5592 foreach ($brd as $border => $style) {
5593 if (is_array($style) AND !empty($style)) {
5594 // apply border style
5595 $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' ';
5596 $s .= $this->SetLineStyle($style, true)."\n";
5597 }
5598 switch ($mode) {
5599 case 'ext': {
5600 $off = (($this->LineWidth / 2) * $k);
5601 $xL = $xeL - $off;
5602 $xR = $xeR + $off;
5603 $yT = $yeT + $off;
5604 $yL = $yeL - $off;
5605 $xT = $xL;
5606 $xB = $xR;
5607 $yR = $yT;
5608 $yB = $yL;
5611 break;
5612 }
5613 case 'int': {
5614 $off = ($this->LineWidth / 2) * $k;
5615 $xL = $xeL + $off;
5616 $xR = $xeR - $off;
5617 $yT = $yeT - $off;
5618 $yL = $yeL + $off;
5619 $xT = $xL;
5620 $xB = $xR;
5621 $yR = $yT;
5622 $yB = $yL;
5625 break;
5626 }
5627 case 'normal':
5628 default: {
5629 $xL = $xeL;
5630 $xT = $xeT;
5631 $xB = $xeB;
5632 $xR = $xeR;
5633 $yL = $yeL;
5634 $yT = $yeT;
5635 $yB = $yeB;
5636 $yR = $yeR;
5637 break;
5638 }
5639 }
5640 // draw borders by case
5641 if (strlen($border) == 4) {
5642 $s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k));
5643 } elseif (strlen($border) == 3) {
5644 if (strpos($border,'B') === false) { // LTR
5645 $s .= sprintf('%F %F m ', $xL, $yL);
5646 $s .= sprintf('%F %F l ', $xT, $yT);
5647 $s .= sprintf('%F %F l ', $xR, $yR);
5648 $s .= sprintf('%F %F l ', $xB, $yB);
5649 $s .= 'S ';
5650 } elseif (strpos($border,'L') === false) { // TRB
5651 $s .= sprintf('%F %F m ', $xT, $yT);
5652 $s .= sprintf('%F %F l ', $xR, $yR);
5653 $s .= sprintf('%F %F l ', $xB, $yB);
5654 $s .= sprintf('%F %F l ', $xL, $yL);
5655 $s .= 'S ';
5656 } elseif (strpos($border,'T') === false) { // RBL
5657 $s .= sprintf('%F %F m ', $xR, $yR);
5658 $s .= sprintf('%F %F l ', $xB, $yB);
5659 $s .= sprintf('%F %F l ', $xL, $yL);
5660 $s .= sprintf('%F %F l ', $xT, $yT);
5661 $s .= 'S ';
5662 } elseif (strpos($border,'R') === false) { // BLT
5663 $s .= sprintf('%F %F m ', $xB, $yB);
5664 $s .= sprintf('%F %F l ', $xL, $yL);
5665 $s .= sprintf('%F %F l ', $xT, $yT);
5666 $s .= sprintf('%F %F l ', $xR, $yR);
5667 $s .= 'S ';
5668 }
5669 } elseif (strlen($border) == 2) {
5670 if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
5671 $s .= sprintf('%F %F m ', $xL, $yL);
5672 $s .= sprintf('%F %F l ', $xT, $yT);
5673 $s .= sprintf('%F %F l ', $xR, $yR);
5674 $s .= 'S ';
5675 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
5676 $s .= sprintf('%F %F m ', $xT, $yT);
5677 $s .= sprintf('%F %F l ', $xR, $yR);
5678 $s .= sprintf('%F %F l ', $xB, $yB);
5679 $s .= 'S ';
5680 } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
5681 $s .= sprintf('%F %F m ', $xR, $yR);
5682 $s .= sprintf('%F %F l ', $xB, $yB);
5683 $s .= sprintf('%F %F l ', $xL, $yL);
5684 $s .= 'S ';
5685 } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
5686 $s .= sprintf('%F %F m ', $xB, $yB);
5687 $s .= sprintf('%F %F l ', $xL, $yL);
5688 $s .= sprintf('%F %F l ', $xT, $yT);
5689 $s .= 'S ';
5690 } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
5691 $s .= sprintf('%F %F m ', $xL, $yL);
5692 $s .= sprintf('%F %F l ', $xT, $yT);
5693 $s .= 'S ';
5694 $s .= sprintf('%F %F m ', $xR, $yR);
5695 $s .= sprintf('%F %F l ', $xB, $yB);
5696 $s .= 'S ';
5697 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
5698 $s .= sprintf('%F %F m ', $xT, $yT);
5699 $s .= sprintf('%F %F l ', $xR, $yR);
5700 $s .= 'S ';
5701 $s .= sprintf('%F %F m ', $xB, $yB);
5702 $s .= sprintf('%F %F l ', $xL, $yL);
5703 $s .= 'S ';
5704 }
5705 } else { // strlen($border) == 1
5706 if (strpos($border,'L') !== false) { // L
5707 $s .= sprintf('%F %F m ', $xL, $yL);
5708 $s .= sprintf('%F %F l ', $xT, $yT);
5709 $s .= 'S ';
5710 } elseif (strpos($border,'T') !== false) { // T
5711 $s .= sprintf('%F %F m ', $xT, $yT);
5712 $s .= sprintf('%F %F l ', $xR, $yR);
5713 $s .= 'S ';
5714 } elseif (strpos($border,'R') !== false) { // R
5715 $s .= sprintf('%F %F m ', $xR, $yR);
5716 $s .= sprintf('%F %F l ', $xB, $yB);
5717 $s .= 'S ';
5718 } elseif (strpos($border,'B') !== false) { // B
5719 $s .= sprintf('%F %F m ', $xB, $yB);
5720 $s .= sprintf('%F %F l ', $xL, $yL);
5721 $s .= 'S ';
5722 }
5723 }
5724 if (is_array($style) AND !empty($style)) {
5725 // reset border style to previous value
5726 $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n";
5727 }
5728 }
5729 return $s;
5730 }
5731
5757 public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $valign='T', $fitcell=false) {
5758 $prev_cell_margin = $this->cell_margin;
5759 $prev_cell_padding = $this->cell_padding;
5760 // adjust internal padding
5761 $this->adjustCellPadding($border);
5762 $mc_padding = $this->cell_padding;
5763 $mc_margin = $this->cell_margin;
5764 $this->cell_padding['T'] = 0;
5765 $this->cell_padding['B'] = 0;
5766 $this->setCellMargins(0, 0, 0, 0);
5767 if (TCPDF_STATIC::empty_string($this->lasth) OR $reseth) {
5768 // reset row height
5769 $this->resetLastH();
5770 }
5772 $this->SetY($y);
5773 } else {
5774 $y = $this->GetY();
5775 }
5776 $resth = 0;
5777 if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) {
5778 // spit cell in more pages/columns
5779 $newh = ($this->PageBreakTrigger - $y);
5780 $resth = ($h - $newh); // cell to be printed on the next page/column
5781 $h = $newh;
5782 }
5783 // get current page number
5784 $startpage = $this->page;
5785 // get current column
5786 $startcolumn = $this->current_column;
5788 $this->SetX($x);
5789 } else {
5790 $x = $this->GetX();
5791 }
5792 // check page for no-write regions and adapt page margins if necessary
5793 list($x, $y) = $this->checkPageRegions(0, $x, $y);
5794 // apply margins
5795 $oy = $y + $mc_margin['T'];
5796 if ($this->rtl) {
5797 $ox = ($this->w - $x - $mc_margin['R']);
5798 } else {
5799 $ox = ($x + $mc_margin['L']);
5800 }
5801 $this->x = $ox;
5802 $this->y = $oy;
5803 // set width
5804 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5805 if ($this->rtl) {
5806 $w = ($this->x - $this->lMargin - $mc_margin['L']);
5807 } else {
5808 $w = ($this->w - $this->x - $this->rMargin - $mc_margin['R']);
5809 }
5810 }
5811 // store original margin values
5814 if ($this->rtl) {
5815 $this->rMargin = ($this->w - $this->x);
5816 $this->lMargin = ($this->x - $w);
5817 } else {
5818 $this->lMargin = ($this->x);
5819 $this->rMargin = ($this->w - $this->x - $w);
5820 }
5821 $this->clMargin = $this->lMargin;
5822 $this->crMargin = $this->rMargin;
5823 if ($autopadding) {
5824 // add top padding
5825 $this->y += $mc_padding['T'];
5826 }
5827 if ($ishtml) { // ******* Write HTML text
5828 $this->writeHTML($txt, true, false, $reseth, true, $align);
5829 $nl = 1;
5830 } else { // ******* Write simple text
5831 $prev_FontSizePt = $this->FontSizePt;
5832 if ($fitcell) {
5833 // ajust height values
5834 $tobottom = ($this->h - $this->y - $this->bMargin - $this->cell_padding['T'] - $this->cell_padding['B']);
5835 $h = $maxh = max(min($h, $tobottom), min($maxh, $tobottom));
5836 }
5837 // vertical alignment
5838 if ($maxh > 0) {
5839 // get text height
5840 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5841 if ($fitcell AND ($text_height > $maxh) AND ($this->FontSizePt > 1)) {
5842 // try to reduce font size to fit text on cell (use a quick search algorithm)
5843 $fmin = 1;
5844 $fmax = $this->FontSizePt;
5845 $diff_epsilon = (1 / $this->k); // one point (min resolution)
5846 $maxit = (2 * min(100, max(10, intval($fmax)))); // max number of iterations
5847 while ($maxit >= 0) {
5848 $fmid = (($fmax + $fmin) / 2);
5849 $this->SetFontSize($fmid, false);
5850 $this->resetLastH();
5851 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5852 $diff = ($maxh - $text_height);
5853 if ($diff >= 0) {
5854 if ($diff <= $diff_epsilon) {
5855 break;
5856 }
5857 $fmin = $fmid;
5858 } else {
5859 $fmax = $fmid;
5860 }
5861 --$maxit;
5862 }
5863 if ($maxit < 0) {
5864 // premature exit, we get the minimum font value to fit the cell
5865 $this->SetFontSize($fmin);
5866 $this->resetLastH();
5867 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5868 } else {
5869 $this->SetFontSize($fmid);
5870 $this->resetLastH();
5871 }
5872 }
5873 if ($text_height < $maxh) {
5874 if ($valign == 'M') {
5875 // text vertically centered
5876 $this->y += (($maxh - $text_height) / 2);
5877 } elseif ($valign == 'B') {
5878 // text vertically aligned on bottom
5879 $this->y += ($maxh - $text_height);
5880 }
5881 }
5882 }
5883 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
5884 if ($fitcell) {
5885 // restore font size
5886 $this->SetFontSize($prev_FontSizePt);
5887 }
5888 }
5889 if ($autopadding) {
5890 // add bottom padding
5891 $this->y += $mc_padding['B'];
5892 }
5893 // Get end-of-text Y position
5894 $currentY = $this->y;
5895 // get latest page number
5896 $endpage = $this->page;
5897 if ($resth > 0) {
5898 $skip = ($endpage - $startpage);
5899 $tmpresth = $resth;
5900 while ($tmpresth > 0) {
5901 if ($skip <= 0) {
5902 // add a page (or trig AcceptPageBreak() for multicolumn mode)
5903 $this->checkPageBreak($this->PageBreakTrigger + 1);
5904 }
5905 if ($this->num_columns > 1) {
5906 $tmpresth -= ($this->h - $this->y - $this->bMargin);
5907 } else {
5908 $tmpresth -= ($this->h - $this->tMargin - $this->bMargin);
5909 }
5910 --$skip;
5911 }
5912 $currentY = $this->y;
5913 $endpage = $this->page;
5914 }
5915 // get latest column
5916 $endcolumn = $this->current_column;
5917 if ($this->num_columns == 0) {
5918 $this->num_columns = 1;
5919 }
5920 // disable page regions check
5922 $this->check_page_regions = false;
5923 // get border modes
5924 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
5925 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
5926 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
5927 // design borders around HTML cells.
5928 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
5929 $ccode = '';
5930 $this->setPage($page);
5931 if ($this->num_columns < 2) {
5932 // single-column mode
5933 $this->SetX($x);
5934 $this->y = $this->tMargin;
5935 }
5936 // account for margin changes
5937 if ($page > $startpage) {
5938 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
5939 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
5940 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
5941 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
5942 }
5943 }
5944 if ($startpage == $endpage) {
5945 // single page
5946 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
5947 $this->selectColumn($column);
5948 if ($this->rtl) {
5949 $this->x -= $mc_margin['R'];
5950 } else {
5951 $this->x += $mc_margin['L'];
5952 }
5953 if ($startcolumn == $endcolumn) { // single column
5954 $cborder = $border;
5955 $h = max($h, ($currentY - $oy));
5956 $this->y = $oy;
5957 } elseif ($column == $startcolumn) { // first column
5958 $cborder = $border_start;
5959 $this->y = $oy;
5960 $h = $this->h - $this->y - $this->bMargin;
5961 } elseif ($column == $endcolumn) { // end column
5962 $cborder = $border_end;
5963 $h = $currentY - $this->y;
5964 if ($resth > $h) {
5965 $h = $resth;
5966 }
5967 } else { // middle column
5968 $cborder = $border_middle;
5969 $h = $this->h - $this->y - $this->bMargin;
5970 $resth -= $h;
5971 }
5972 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
5973 } // end for each column
5974 } elseif ($page == $startpage) { // first page
5975 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
5976 $this->selectColumn($column);
5977 if ($this->rtl) {
5978 $this->x -= $mc_margin['R'];
5979 } else {
5980 $this->x += $mc_margin['L'];
5981 }
5982 if ($column == $startcolumn) { // first column
5983 $cborder = $border_start;
5984 $this->y = $oy;
5985 $h = $this->h - $this->y - $this->bMargin;
5986 } else { // middle column
5987 $cborder = $border_middle;
5988 $h = $this->h - $this->y - $this->bMargin;
5989 $resth -= $h;
5990 }
5991 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
5992 } // end for each column
5993 } elseif ($page == $endpage) { // last page
5994 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
5995 $this->selectColumn($column);
5996 if ($this->rtl) {
5997 $this->x -= $mc_margin['R'];
5998 } else {
5999 $this->x += $mc_margin['L'];
6000 }
6001 if ($column == $endcolumn) {
6002 // end column
6003 $cborder = $border_end;
6004 $h = $currentY - $this->y;
6005 if ($resth > $h) {
6006 $h = $resth;
6007 }
6008 } else {
6009 // middle column
6010 $cborder = $border_middle;
6011 $h = $this->h - $this->y - $this->bMargin;
6012 $resth -= $h;
6013 }
6014 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6015 } // end for each column
6016 } else { // middle page
6017 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
6018 $this->selectColumn($column);
6019 if ($this->rtl) {
6020 $this->x -= $mc_margin['R'];
6021 } else {
6022 $this->x += $mc_margin['L'];
6023 }
6024 $cborder = $border_middle;
6025 $h = $this->h - $this->y - $this->bMargin;
6026 $resth -= $h;
6027 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6028 } // end for each column
6029 }
6030 if ($cborder OR $fill) {
6031 $offsetlen = strlen($ccode);
6032 // draw border and fill
6033 if ($this->inxobj) {
6034 // we are inside an XObject template
6035 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
6036 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
6037 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
6038 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
6039 } else {
6040 $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
6041 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
6042 }
6043 $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
6044 $pstart = substr($pagebuff, 0, $pagemark);
6045 $pend = substr($pagebuff, $pagemark);
6046 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
6047 } else {
6048 if (end($this->transfmrk[$this->page]) !== false) {
6049 $pagemarkkey = key($this->transfmrk[$this->page]);
6050 $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
6051 $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
6052 } elseif ($this->InFooter) {
6053 $pagemark = $this->footerpos[$this->page];
6054 $this->footerpos[$this->page] += $offsetlen;
6055 } else {
6056 $pagemark = $this->intmrk[$this->page];
6057 $this->intmrk[$this->page] += $offsetlen;
6058 }
6059 $pagebuff = $this->getPageBuffer($this->page);
6060 $pstart = substr($pagebuff, 0, $pagemark);
6061 $pend = substr($pagebuff, $pagemark);
6062 $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
6063 }
6064 }
6065 } // end for each page
6066 // restore page regions check
6067 $this->check_page_regions = $check_page_regions;
6068 // Get end-of-cell Y position
6069 $currentY = $this->GetY();
6070 // restore previous values
6071 if ($this->num_columns > 1) {
6072 $this->selectColumn();
6073 } else {
6074 // restore original margins
6075 $this->lMargin = $lMargin;
6076 $this->rMargin = $rMargin;
6077 if ($this->page > $startpage) {
6078 // check for margin variations between pages (i.e. booklet mode)
6079 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']);
6080 $dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']);
6081 if (($dl != 0) OR ($dr != 0)) {
6082 $this->lMargin += $dl;
6083 $this->rMargin += $dr;
6084 }
6085 }
6086 }
6087 if ($ln > 0) {
6088 //Go to the beginning of the next line
6089 $this->SetY($currentY + $mc_margin['B']);
6090 if ($ln == 2) {
6091 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6092 }
6093 } else {
6094 // go left or right by case
6095 $this->setPage($startpage);
6096 $this->y = $y;
6097 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6098 }
6099 $this->setContentMark();
6100 $this->cell_padding = $prev_cell_padding;
6101 $this->cell_margin = $prev_cell_margin;
6102 $this->clMargin = $this->lMargin;
6103 $this->crMargin = $this->rMargin;
6104 return $nl;
6105 }
6106
6120 public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6121 if ($txt === NULL) {
6122 return 0;
6123 }
6124 if ($txt === '') {
6125 // empty string
6126 return 1;
6127 }
6128 // adjust internal padding
6129 $prev_cell_padding = $this->cell_padding;
6130 $prev_lasth = $this->lasth;
6131 if (is_array($cellpadding)) {
6132 $this->cell_padding = $cellpadding;
6133 }
6134 $this->adjustCellPadding($border);
6135 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
6136 if ($this->rtl) {
6137 $w = $this->x - $this->lMargin;
6138 } else {
6139 $w = $this->w - $this->rMargin - $this->x;
6140 }
6141 }
6142 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6143 if ($reseth) {
6144 // reset row height
6145 $this->resetLastH();
6146 }
6147 $lines = 1;
6148 $sum = 0;
6149 $chars = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont), $txt, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6150 $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
6151 $length = count($chars);
6152 $lastSeparator = -1;
6153 for ($i = 0; $i < $length; ++$i) {
6154 $c = $chars[$i];
6155 $charWidth = $charsWidth[$i];
6156 if (($c != 160)
6157 AND (($c == 173)
6158 OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6159 OR (($c == 45)
6160 AND ($i > 0) AND ($i < ($length - 1))
6161 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i - 1)], $this->isunicode))
6162 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6163 )
6164 )
6165 ) {
6166 $lastSeparator = $i;
6167 }
6168 if ((($sum + $charWidth) > $wmax) OR ($c == 10)) {
6169 ++$lines;
6170 if ($c == 10) {
6171 $lastSeparator = -1;
6172 $sum = 0;
6173 } elseif ($lastSeparator != -1) {
6174 $i = $lastSeparator;
6175 $lastSeparator = -1;
6176 $sum = 0;
6177 } else {
6178 $sum = $charWidth;
6179 }
6180 } else {
6181 $sum += $charWidth;
6182 }
6183 }
6184 if ($chars[($length - 1)] == 10) {
6185 --$lines;
6186 }
6187 $this->cell_padding = $prev_cell_padding;
6188 $this->lasth = $prev_lasth;
6189 return $lines;
6190 }
6191
6239 public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6240 // adjust internal padding
6241 $prev_cell_padding = $this->cell_padding;
6242 $prev_lasth = $this->lasth;
6243 if (is_array($cellpadding)) {
6244 $this->cell_padding = $cellpadding;
6245 }
6246 $this->adjustCellPadding($border);
6247 $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
6248 $height = $this->getCellHeight(($lines * $this->FontSize), $autopadding);
6249 $this->cell_padding = $prev_cell_padding;
6250 $this->lasth = $prev_lasth;
6251 return $height;
6252 }
6253
6272 public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
6273 // check page for no-write regions and adapt page margins if necessary
6274 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
6275 if (strlen($txt) == 0) {
6276 // fix empty text
6277 $txt = ' ';
6278 }
6279 if ($margin === '') {
6280 // set default margins
6281 $margin = $this->cell_margin;
6282 }
6283 // remove carriage returns
6284 $s = str_replace("\r", '', $txt);
6285 // check if string contains arabic text
6287 $arabic = true;
6288 } else {
6289 $arabic = false;
6290 }
6291 // check if string contains RTL text
6292 if ($arabic OR ($this->tmprtl == 'R') OR preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $s)) {
6293 $rtlmode = true;
6294 } else {
6295 $rtlmode = false;
6296 }
6297 // get a char width
6298 $chrwidth = $this->GetCharWidth(46); // dot character
6299 // get array of unicode values
6300 $chars = TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont);
6301 // calculate maximum width for a single character on string
6302 $chrw = $this->GetArrStringWidth($chars, '', '', 0, true);
6303 array_walk($chrw, array($this, 'getRawCharWidth'));
6304 $maxchwidth = max($chrw);
6305 // get array of chars
6306 $uchars = TCPDF_FONTS::UTF8ArrayToUniArray($chars, $this->isunicode);
6307 // get the number of characters
6308 $nb = count($chars);
6309 // replacement for SHY character (minus symbol)
6310 $shy_replacement = 45;
6311 $shy_replacement_char = TCPDF_FONTS::unichr($shy_replacement, $this->isunicode);
6312 // widht for SHY replacement
6313 $shy_replacement_width = $this->GetCharWidth($shy_replacement);
6314 // page width
6315 $pw = $w = $this->w - $this->lMargin - $this->rMargin;
6316 // calculate remaining line width ($w)
6317 if ($this->rtl) {
6318 $w = $this->x - $this->lMargin;
6319 } else {
6320 $w = $this->w - $this->rMargin - $this->x;
6321 }
6322 // max column width
6323 $wmax = ($w - $wadj);
6324 if (!$firstline) {
6325 $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']);
6326 }
6327 if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) {
6328 // the maximum width character do not fit on column
6329 return '';
6330 }
6331 // minimum row height
6332 $row_height = max($h, $this->getCellHeight($this->FontSize));
6333 // max Y
6334 $maxy = $this->y + $maxh - max($row_height, $h);
6335 $start_page = $this->page;
6336 $i = 0; // character position
6337 $j = 0; // current starting position
6338 $sep = -1; // position of the last blank space
6339 $prevsep = $sep; // previous separator
6340 $shy = false; // true if the last blank is a soft hypen (SHY)
6341 $prevshy = $shy; // previous shy mode
6342 $l = 0; // current string length
6343 $nl = 0; //number of lines
6344 $linebreak = false;
6345 $pc = 0; // previous character
6346 // for each character
6347 while ($i < $nb) {
6348 if (($maxh > 0) AND ($this->y > $maxy) ) {
6349 break;
6350 }
6351 //Get the current character
6352 $c = $chars[$i];
6353 if ($c == 10) { // 10 = "\n" = new line
6354 //Explicit line break
6355 if ($align == 'J') {
6356 if ($this->rtl) {
6357 $talign = 'R';
6358 } else {
6359 $talign = 'L';
6360 }
6361 } else {
6362 $talign = $align;
6363 }
6364 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6365 if ($firstline) {
6366 $startx = $this->x;
6367 $tmparr = array_slice($chars, $j, ($i - $j));
6368 if ($rtlmode) {
6369 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6370 }
6371 $linew = $this->GetArrStringWidth($tmparr);
6372 unset($tmparr);
6373 if ($this->rtl) {
6374 $this->endlinex = $startx - $linew;
6375 } else {
6376 $this->endlinex = $startx + $linew;
6377 }
6378 $w = $linew;
6379 $tmpcellpadding = $this->cell_padding;
6380 if ($maxh == 0) {
6381 $this->SetCellPadding(0);
6382 }
6383 }
6384 if ($firstblock AND $this->isRTLTextDir()) {
6385 $tmpstr = $this->stringRightTrim($tmpstr);
6386 }
6387 // Skip newlines at the beginning of a page or column
6388 if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) {
6389 $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
6390 }
6391 unset($tmpstr);
6392 if ($firstline) {
6393 $this->cell_padding = $tmpcellpadding;
6394 return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6395 }
6396 ++$nl;
6397 $j = $i + 1;
6398 $l = 0;
6399 $sep = -1;
6400 $prevsep = $sep;
6401 $shy = false;
6402 // account for margin changes
6403 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6404 $this->AcceptPageBreak();
6405 if ($this->rtl) {
6406 $this->x -= $margin['R'];
6407 } else {
6408 $this->x += $margin['L'];
6409 }
6410 $this->lMargin += $margin['L'];
6411 $this->rMargin += $margin['R'];
6412 }
6413 $w = $this->getRemainingWidth();
6414 $wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']);
6415 } else {
6416 // 160 is the non-breaking space.
6417 // 173 is SHY (Soft Hypen).
6418 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
6419 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
6420 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
6421 if (($c != 160)
6422 AND (($c == 173)
6423 OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6424 OR (($c == 45)
6425 AND ($i < ($nb - 1))
6426 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($pc, $this->isunicode))
6427 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6428 )
6429 )
6430 ) {
6431 // update last blank space position
6432 $prevsep = $sep;
6433 $sep = $i;
6434 // check if is a SHY
6435 if (($c == 173) OR ($c == 45)) {
6436 $prevshy = $shy;
6437 $shy = true;
6438 if ($pc == 45) {
6439 $tmp_shy_replacement_width = 0;
6440 $tmp_shy_replacement_char = '';
6441 } else {
6442 $tmp_shy_replacement_width = $shy_replacement_width;
6443 $tmp_shy_replacement_char = $shy_replacement_char;
6444 }
6445 } else {
6446 $shy = false;
6447 }
6448 }
6449 // update string length
6450 if ($this->isUnicodeFont() AND ($arabic)) {
6451 // with bidirectional algorithm some chars may be changed affecting the line length
6452 // *** very slow ***
6453 $l = $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl, $this->isunicode, $this->CurrentFont));
6454 } else {
6455 $l += $this->GetCharWidth($c);
6456 }
6457 if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) >= $wmax))) {
6458 if (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) {
6459 $sep = $prevsep;
6460 $shy = $prevshy;
6461 }
6462 // we have reached the end of column
6463 if ($sep == -1) {
6464 // check if the line was already started
6465 if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth)))
6466 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) {
6467 // print a void cell and go to next line
6468 $this->Cell($w, $h, '', 0, 1);
6469 $linebreak = true;
6470 if ($firstline) {
6471 return (TCPDF_FONTS::UniArrSubString($uchars, $j));
6472 }
6473 } else {
6474 // truncate the word because do not fit on column
6475 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6476 if ($firstline) {
6477 $startx = $this->x;
6478 $tmparr = array_slice($chars, $j, ($i - $j));
6479 if ($rtlmode) {
6480 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6481 }
6482 $linew = $this->GetArrStringWidth($tmparr);
6483 unset($tmparr);
6484 if ($this->rtl) {
6485 $this->endlinex = $startx - $linew;
6486 } else {
6487 $this->endlinex = $startx + $linew;
6488 }
6489 $w = $linew;
6490 $tmpcellpadding = $this->cell_padding;
6491 if ($maxh == 0) {
6492 $this->SetCellPadding(0);
6493 }
6494 }
6495 if ($firstblock AND $this->isRTLTextDir()) {
6496 $tmpstr = $this->stringRightTrim($tmpstr);
6497 }
6498 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6499 unset($tmpstr);
6500 if ($firstline) {
6501 $this->cell_padding = $tmpcellpadding;
6502 return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6503 }
6504 $j = $i;
6505 --$i;
6506 }
6507 } else {
6508 // word wrapping
6509 if ($this->rtl AND (!$firstblock) AND ($sep < $i)) {
6510 $endspace = 1;
6511 } else {
6512 $endspace = 0;
6513 }
6514 // check the length of the next string
6515 $strrest = TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace));
6516 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $this->stringTrim($strrest));
6517 if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
6518 // truncate the word because do not fit on a full page width
6519 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6520 if ($firstline) {
6521 $startx = $this->x;
6522 $tmparr = array_slice($chars, $j, ($i - $j));
6523 if ($rtlmode) {
6524 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6525 }
6526 $linew = $this->GetArrStringWidth($tmparr);
6527 unset($tmparr);
6528 if ($this->rtl) {
6529 $this->endlinex = ($startx - $linew);
6530 } else {
6531 $this->endlinex = ($startx + $linew);
6532 }
6533 $w = $linew;
6534 $tmpcellpadding = $this->cell_padding;
6535 if ($maxh == 0) {
6536 $this->SetCellPadding(0);
6537 }
6538 }
6539 if ($firstblock AND $this->isRTLTextDir()) {
6540 $tmpstr = $this->stringRightTrim($tmpstr);
6541 }
6542 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6543 unset($tmpstr);
6544 if ($firstline) {
6545 $this->cell_padding = $tmpcellpadding;
6546 return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6547 }
6548 $j = $i;
6549 --$i;
6550 } else {
6551 // word wrapping
6552 if ($shy) {
6553 // add hypen (minus symbol) at the end of the line
6554 $shy_width = $tmp_shy_replacement_width;
6555 if ($this->rtl) {
6556 $shy_char_left = $tmp_shy_replacement_char;
6557 $shy_char_right = '';
6558 } else {
6559 $shy_char_left = '';
6560 $shy_char_right = $tmp_shy_replacement_char;
6561 }
6562 } else {
6563 $shy_width = 0;
6564 $shy_char_left = '';
6565 $shy_char_right = '';
6566 }
6567 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, ($sep + $endspace));
6568 if ($firstline) {
6569 $startx = $this->x;
6570 $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
6571 if ($rtlmode) {
6572 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6573 }
6574 $linew = $this->GetArrStringWidth($tmparr);
6575 unset($tmparr);
6576 if ($this->rtl) {
6577 $this->endlinex = $startx - $linew - $shy_width;
6578 } else {
6579 $this->endlinex = $startx + $linew + $shy_width;
6580 }
6581 $w = $linew;
6582 $tmpcellpadding = $this->cell_padding;
6583 if ($maxh == 0) {
6584 $this->SetCellPadding(0);
6585 }
6586 }
6587 // print the line
6588 if ($firstblock AND $this->isRTLTextDir()) {
6589 $tmpstr = $this->stringRightTrim($tmpstr);
6590 }
6591 $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
6592 unset($tmpstr);
6593 if ($firstline) {
6594 if ($chars[$sep] == 45) {
6595 $endspace += 1;
6596 }
6597 // return the remaining text
6598 $this->cell_padding = $tmpcellpadding;
6599 return (TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace)));
6600 }
6601 $i = $sep;
6602 $sep = -1;
6603 $shy = false;
6604 $j = ($i + 1);
6605 }
6606 }
6607 // account for margin changes
6608 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6609 $this->AcceptPageBreak();
6610 if ($this->rtl) {
6611 $this->x -= $margin['R'];
6612 } else {
6613 $this->x += $margin['L'];
6614 }
6615 $this->lMargin += $margin['L'];
6616 $this->rMargin += $margin['R'];
6617 }
6618 $w = $this->getRemainingWidth();
6619 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6620 if ($linebreak) {
6621 $linebreak = false;
6622 } else {
6623 ++$nl;
6624 $l = 0;
6625 }
6626 }
6627 }
6628 // save last character
6629 $pc = $c;
6630 ++$i;
6631 } // end while i < nb
6632 // print last substring (if any)
6633 if ($l > 0) {
6634 switch ($align) {
6635 case 'J':
6636 case 'C': {
6637 $w = $w;
6638 break;
6639 }
6640 case 'L': {
6641 if ($this->rtl) {
6642 $w = $w;
6643 } else {
6644 $w = $l;
6645 }
6646 break;
6647 }
6648 case 'R': {
6649 if ($this->rtl) {
6650 $w = $l;
6651 } else {
6652 $w = $w;
6653 }
6654 break;
6655 }
6656 default: {
6657 $w = $l;
6658 break;
6659 }
6660 }
6661 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $nb);
6662 if ($firstline) {
6663 $startx = $this->x;
6664 $tmparr = array_slice($chars, $j, ($nb - $j));
6665 if ($rtlmode) {
6666 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6667 }
6668 $linew = $this->GetArrStringWidth($tmparr);
6669 unset($tmparr);
6670 if ($this->rtl) {
6671 $this->endlinex = $startx - $linew;
6672 } else {
6673 $this->endlinex = $startx + $linew;
6674 }
6675 $w = $linew;
6676 $tmpcellpadding = $this->cell_padding;
6677 if ($maxh == 0) {
6678 $this->SetCellPadding(0);
6679 }
6680 }
6681 if ($firstblock AND $this->isRTLTextDir()) {
6682 $tmpstr = $this->stringRightTrim($tmpstr);
6683 }
6684 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
6685 unset($tmpstr);
6686 if ($firstline) {
6687 $this->cell_padding = $tmpcellpadding;
6688 return (TCPDF_FONTS::UniArrSubString($uchars, $nb));
6689 }
6690 ++$nl;
6691 }
6692 if ($firstline) {
6693 return '';
6694 }
6695 return $nl;
6696 }
6697
6703 protected function getRemainingWidth() {
6704 list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y);
6705 if ($this->rtl) {
6706 return ($this->x - $this->lMargin);
6707 } else {
6708 return ($this->w - $this->rMargin - $this->x);
6709 }
6710 }
6711
6723 protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
6724 if ($w <= 0) {
6725 // set maximum width
6726 $w = ($this->w - $this->lMargin - $this->rMargin);
6727 if ($w <= 0) {
6728 $w = 1;
6729 }
6730 }
6731 if ($h <= 0) {
6732 // set maximum height
6733 $h = ($this->PageBreakTrigger - $this->tMargin);
6734 if ($h <= 0) {
6735 $h = 1;
6736 }
6737 }
6738 // resize the block to be vertically contained on a single page or single column
6739 if ($fitonpage OR $this->AutoPageBreak) {
6740 $ratio_wh = ($w / $h);
6741 if ($h > ($this->PageBreakTrigger - $this->tMargin)) {
6742 $h = $this->PageBreakTrigger - $this->tMargin;
6743 $w = ($h * $ratio_wh);
6744 }
6745 // resize the block to be horizontally contained on a single page or single column
6746 if ($fitonpage) {
6747 $maxw = ($this->w - $this->lMargin - $this->rMargin);
6748 if ($w > $maxw) {
6749 $w = $maxw;
6750 $h = ($w / $ratio_wh);
6751 }
6752 }
6753 }
6754 // Check whether we need a new page or new column first as this does not fit
6755 $prev_x = $this->x;
6756 $prev_y = $this->y;
6757 if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) {
6758 $y = $this->y;
6759 if ($this->rtl) {
6760 $x += ($prev_x - $this->x);
6761 } else {
6762 $x += ($this->x - $prev_x);
6763 }
6764 $this->newline = true;
6765 }
6766 // resize the block to be contained on the remaining available page or column space
6767 if ($fitonpage) {
6768 // tcpdf-patch: begin
6769 // 2016-06-02 mbecker will create a pull request to make this patch
6770 if($h)
6771 {
6772 $ratio_wh = ($w / $h);
6773 }
6774 else
6775 {
6776 $ratio_wh = 1;
6777 }
6778 // tcpdf-patch: end
6779 if (($y + $h) > $this->PageBreakTrigger) {
6780 $h = $this->PageBreakTrigger - $y;
6781 $w = ($h * $ratio_wh);
6782 }
6783 if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
6784 $w = $this->w - $this->rMargin - $x;
6785 $h = ($w / $ratio_wh);
6786 } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
6787 $w = $x - $this->lMargin;
6788 $h = ($w / $ratio_wh);
6789 }
6790 }
6791 return array($w, $h, $x, $y);
6792 }
6793
6828 public function Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false, $hidden=false, $fitonpage=false, $alt=false, $altimgs=array()) {
6829 if ($this->state != 2) {
6830 return;
6831 }
6832 if (strcmp($x, '') === 0) {
6833 $x = $this->x;
6834 }
6835 if (strcmp($y, '') === 0) {
6836 $y = $this->y;
6837 }
6838 // check page for no-write regions and adapt page margins if necessary
6839 list($x, $y) = $this->checkPageRegions($h, $x, $y);
6840 $exurl = ''; // external streams
6841 $imsize = FALSE;
6842 // check if we are passing an image as file or string
6843 if ($file[0] === '@') {
6844 // image from string
6845 $imgdata = substr($file, 1);
6846 } else { // image file
6847 if ($file[0] === '*') {
6848 // image as external stream
6849 $file = substr($file, 1);
6850 $exurl = $file;
6851 }
6852 // check if is a local file
6853 if (!@file_exists($file)) {
6854 // try to encode spaces on filename
6855 $tfile = str_replace(' ', '%20', $file);
6856 if (@file_exists($tfile)) {
6857 $file = $tfile;
6858 }
6859 }
6860 if (($imsize = @getimagesize($file)) === FALSE) {
6861 if (in_array($file, $this->imagekeys)) {
6862 // get existing image data
6863 $info = $this->getImageBuffer($file);
6864 $imsize = array($info['w'], $info['h']);
6865 } elseif (strpos($file, '__tcpdf_'.$this->file_id.'_img') === FALSE) {
6867 }
6868 }
6869 }
6870 if (!empty($imgdata)) {
6871 // copy image to cache
6872 $original_file = $file;
6873 $file = TCPDF_STATIC::getObjFilename('img', $this->file_id);
6874 $fp = TCPDF_STATIC::fopenLocal($file, 'w');
6875 if (!$fp) {
6876 $this->Error('Unable to write file: '.$file);
6877 }
6878 fwrite($fp, $imgdata);
6879 fclose($fp);
6880 unset($imgdata);
6881 $imsize = @getimagesize($file);
6882 if ($imsize === FALSE) {
6883 unlink($file);
6884 $file = $original_file;
6885 }
6886 }
6887 if ($imsize === FALSE) {
6888 if (($w > 0) AND ($h > 0)) {
6889 // get measures from specified data
6890 $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6891 $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6892 $imsize = array($pw, $ph);
6893 } else {
6894 $this->Error('[Image] Unable to get the size of the image: '.$file);
6895 }
6896 }
6897 // file hash
6898 $filehash = md5($file);
6899 // get original image width and height in pixels
6900 list($pixw, $pixh) = $imsize;
6901 // calculate image width and height on document
6902 if (($w <= 0) AND ($h <= 0)) {
6903 // convert image size to document unit
6904 $w = $this->pixelsToUnits($pixw);
6905 $h = $this->pixelsToUnits($pixh);
6906 } elseif ($w <= 0) {
6907 $w = $h * $pixw / $pixh;
6908 } elseif ($h <= 0) {
6909 $h = $w * $pixh / $pixw;
6910 } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
6911 if (strlen($fitbox) !== 2) {
6912 // set default alignment
6913 $fitbox = '--';
6914 }
6915 // scale image dimensions proportionally to fit within the ($w, $h) box
6916 if ((($w * $pixh) / ($h * $pixw)) < 1) {
6917 // store current height
6918 $oldh = $h;
6919 // calculate new height
6920 $h = $w * $pixh / $pixw;
6921 // height difference
6922 $hdiff = ($oldh - $h);
6923 // vertical alignment
6924 switch (strtoupper($fitbox[1])) {
6925 case 'T': {
6926 break;
6927 }
6928 case 'M': {
6929 $y += ($hdiff / 2);
6930 break;
6931 }
6932 case 'B': {
6933 $y += $hdiff;
6934 break;
6935 }
6936 }
6937 } else {
6938 // store current width
6939 $oldw = $w;
6940 // calculate new width
6941 $w = $h * $pixw / $pixh;
6942 // width difference
6943 $wdiff = ($oldw - $w);
6944 // horizontal alignment
6945 switch (strtoupper($fitbox[0])) {
6946 case 'L': {
6947 if ($this->rtl) {
6948 $x -= $wdiff;
6949 }
6950 break;
6951 }
6952 case 'C': {
6953 if ($this->rtl) {
6954 $x -= ($wdiff / 2);
6955 } else {
6956 $x += ($wdiff / 2);
6957 }
6958 break;
6959 }
6960 case 'R': {
6961 if (!$this->rtl) {
6962 $x += $wdiff;
6963 }
6964 break;
6965 }
6966 }
6967 }
6968 }
6969 // fit the image on available space
6970 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
6971 // calculate new minimum dimensions in pixels
6972 $neww = round($w * $this->k * $dpi / $this->dpi);
6973 $newh = round($h * $this->k * $dpi / $this->dpi);
6974 // check if resize is necessary (resize is used only to reduce the image)
6975 $newsize = ($neww * $newh);
6976 $pixsize = ($pixw * $pixh);
6977 if (intval($resize) == 2) {
6978 $resize = true;
6979 } elseif ($newsize >= $pixsize) {
6980 $resize = false;
6981 }
6982 // check if image has been already added on document
6983 $newimage = true;
6984 if (in_array($file, $this->imagekeys)) {
6985 $newimage = false;
6986 // get existing image data
6987 $info = $this->getImageBuffer($file);
6988 if (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE) {
6989 // check if the newer image is larger
6990 $oldsize = ($info['w'] * $info['h']);
6991 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
6992 $newimage = true;
6993 }
6994 }
6995 } elseif (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE)) {
6996 // create temp image file (without alpha channel)
6997 $tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash;
6998 // create temp alpha file
6999 $tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash;
7000 // check for cached images
7001 if (in_array($tempfile_plain, $this->imagekeys)) {
7002 // get existing image data
7003 $info = $this->getImageBuffer($tempfile_plain);
7004 // check if the newer image is larger
7005 $oldsize = ($info['w'] * $info['h']);
7006 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7007 $newimage = true;
7008 } else {
7009 $newimage = false;
7010 // embed mask image
7011 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7012 // embed image, masked with previously embedded mask
7013 return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7014 }
7015 }
7016 }
7017 if ($newimage) {
7018 //First use of image, get info
7019 $type = strtolower($type);
7020 if ($type == '') {
7022 } elseif ($type == 'jpg') {
7023 $type = 'jpeg';
7024 }
7025 $mqr = TCPDF_STATIC::get_mqr();
7026 TCPDF_STATIC::set_mqr(false);
7027 // Specific image handlers (defined on TCPDF_IMAGES CLASS)
7028 $mtd = '_parse'.$type;
7029 // GD image handler function
7030 $gdfunction = 'imagecreatefrom'.$type;
7031 $info = false;
7032 if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
7033 // TCPDF image functions
7034 $info = TCPDF_IMAGES::$mtd($file);
7035 if (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE)
7036 AND (($info === 'pngalpha') OR (isset($info['trns']) AND !empty($info['trns'])))) {
7037 return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
7038 }
7039 }
7040 if (($info === false) AND function_exists($gdfunction)) {
7041 try {
7042 // GD library
7043 $img = $gdfunction($file);
7044 if ($img !== false) {
7045 if ($resize) {
7046 $imgr = imagecreatetruecolor($neww, $newh);
7047 if (($type == 'gif') OR ($type == 'png')) {
7049 }
7050 imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
7051 $img = $imgr;
7052 }
7053 if (($type == 'gif') OR ($type == 'png')) {
7055 } else {
7056 $info = TCPDF_IMAGES::_toJPEG($img, $this->jpeg_quality, TCPDF_STATIC::getObjFilename('img', $this->file_id));
7057 }
7058 }
7059 } catch(Exception $e) {
7060 $info = false;
7061 }
7062 }
7063 if (($info === false) AND extension_loaded('imagick')) {
7064 try {
7065 // ImageMagick library
7066 $img = new Imagick();
7067 if ($type == 'svg') {
7068 if ($file[0] === '@') {
7069 // image from string
7070 $svgimg = substr($file, 1);
7071 } else {
7072 // get SVG file content
7074 }
7075 if ($svgimg !== FALSE) {
7076 // get width and height
7077 $regs = array();
7078 if (preg_match('/<svg([^>]*)>/si', $svgimg, $regs)) {
7079 $svgtag = $regs[1];
7080 $tmp = array();
7081 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7082 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7083 $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit;
7084 $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
7085 } else {
7086 $ow = $w;
7087 }
7088 $tmp = array();
7089 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7090 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7091 $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit;
7092 $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
7093 } else {
7094 $oh = $h;
7095 }
7096 $tmp = array();
7097 if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
7098 $vbw = ($ow * $this->imgscale * $this->k);
7099 $vbh = ($oh * $this->imgscale * $this->k);
7100 $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh);
7101 $svgtag = $vbox.$svgtag;
7102 }
7103 $svgimg = preg_replace('/<svg([^>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
7104 }
7105 $img->readImageBlob($svgimg);
7106 }
7107 } else {
7108 $img->readImage($file);
7109 }
7110 if ($resize) {
7111 $img->resizeImage($neww, $newh, 10, 1, false);
7112 }
7113 $img->setCompressionQuality($this->jpeg_quality);
7114 $img->setImageFormat('jpeg');
7115 $tempname = TCPDF_STATIC::getObjFilename('img', $this->file_id);
7116 $img->writeImage($tempname);
7117 $info = TCPDF_IMAGES::_parsejpeg($tempname);
7118 unlink($tempname);
7119 $img->destroy();
7120 } catch(Exception $e) {
7121 $info = false;
7122 }
7123 }
7124 if ($info === false) {
7125 // unable to process image
7126 return;
7127 }
7129 if ($ismask) {
7130 // force grayscale
7131 $info['cs'] = 'DeviceGray';
7132 }
7133 if ($imgmask !== false) {
7134 $info['masked'] = $imgmask;
7135 }
7136 if (!empty($exurl)) {
7137 $info['exurl'] = $exurl;
7138 }
7139 // array of alternative images
7140 $info['altimgs'] = $altimgs;
7141 // add image to document
7142 $info['i'] = $this->setImageBuffer($file, $info);
7143 }
7144 // set alignment
7145 $this->img_rb_y = $y + $h;
7146 // set alignment
7147 if ($this->rtl) {
7148 if ($palign == 'L') {
7149 $ximg = $this->lMargin;
7150 } elseif ($palign == 'C') {
7151 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7152 } elseif ($palign == 'R') {
7153 $ximg = $this->w - $this->rMargin - $w;
7154 } else {
7155 $ximg = $x - $w;
7156 }
7157 $this->img_rb_x = $ximg;
7158 } else {
7159 if ($palign == 'L') {
7160 $ximg = $this->lMargin;
7161 } elseif ($palign == 'C') {
7162 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7163 } elseif ($palign == 'R') {
7164 $ximg = $this->w - $this->rMargin - $w;
7165 } else {
7166 $ximg = $x;
7167 }
7168 $this->img_rb_x = $ximg + $w;
7169 }
7170 if ($ismask OR $hidden) {
7171 // image is not displayed
7172 return $info['i'];
7173 }
7174 $xkimg = $ximg * $this->k;
7175 if (!$alt) {
7176 // only non-alternative immages will be set
7177 $this->_out(sprintf('q %F 0 0 %F %F %F cm /I%u Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i']));
7178 }
7179 if (!empty($border)) {
7180 $bx = $this->x;
7181 $by = $this->y;
7182 $this->x = $ximg;
7183 if ($this->rtl) {
7184 $this->x += $w;
7185 }
7186 $this->y = $y;
7187 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
7188 $this->x = $bx;
7189 $this->y = $by;
7190 }
7191 if ($link) {
7192 $this->Link($ximg, $y, $w, $h, $link, 0);
7193 }
7194 // set pointer to align the next text/objects
7195 switch($align) {
7196 case 'T': {
7197 $this->y = $y;
7198 $this->x = $this->img_rb_x;
7199 break;
7200 }
7201 case 'M': {
7202 $this->y = $y + round($h/2);
7203 $this->x = $this->img_rb_x;
7204 break;
7205 }
7206 case 'B': {
7207 $this->y = $this->img_rb_y;
7208 $this->x = $this->img_rb_x;
7209 break;
7210 }
7211 case 'N': {
7212 $this->SetY($this->img_rb_y);
7213 break;
7214 }
7215 default:{
7216 break;
7217 }
7218 }
7219 $this->endlinex = $this->img_rb_x;
7220 if ($this->inxobj) {
7221 // we are inside an XObject template
7222 $this->xobjects[$this->xobjid]['images'][] = $info['i'];
7223 }
7224 return $info['i'];
7225 }
7226
7248 protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
7249 // create temp images
7250 if (empty($filehash)) {
7251 $filehash = md5($file);
7252 }
7253 // create temp image file (without alpha channel)
7254 $tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash;
7255 // create temp alpha file
7256 $tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash;
7257 $parsed = false;
7258 $parse_error = '';
7259 // ImageMagick extension
7260 if (($parsed === false) AND extension_loaded('imagick')) {
7261 try {
7262 // ImageMagick library
7263 $img = new Imagick();
7264 $img->readImage($file);
7265 // clone image object
7267 // extract alpha channel
7268 if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) {
7269 $img->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT);
7270 } else {
7271 $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
7272 $img->negateImage(true);
7273 }
7274 $img->setImageFormat('png');
7275 $img->writeImage($tempfile_alpha);
7276 // remove alpha channel
7277 if (method_exists($imga, 'setImageMatte')) {
7278 $imga->setImageMatte(false);
7279 } else {
7280 $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
7281 }
7282 $imga->setImageFormat('png');
7283 $imga->writeImage($tempfile_plain);
7284 $parsed = true;
7285 } catch (Exception $e) {
7286 // Imagemagick fails, try with GD
7287 $parse_error = 'Imagick library error: '.$e->getMessage();
7288 }
7289 }
7290 // GD extension
7291 if (($parsed === false) AND function_exists('imagecreatefrompng')) {
7292 try {
7293 // generate images
7294 $img = imagecreatefrompng($file);
7295 $imgalpha = imagecreate($wpx, $hpx);
7296 // generate gray scale palette (0 -> 255)
7297 for ($c = 0; $c < 256; ++$c) {
7298 ImageColorAllocate($imgalpha, $c, $c, $c);
7299 }
7300 // extract alpha channel
7301 for ($xpx = 0; $xpx < $wpx; ++$xpx) {
7302 for ($ypx = 0; $ypx < $hpx; ++$ypx) {
7303 $color = imagecolorat($img, $xpx, $ypx);
7304 // get and correct gamma color
7305 $alpha = $this->getGDgamma($img, $color);
7306 imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
7307 }
7308 }
7309 imagepng($imgalpha, $tempfile_alpha);
7310 imagedestroy($imgalpha);
7311 // extract image without alpha channel
7312 $imgplain = imagecreatetruecolor($wpx, $hpx);
7313 imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
7314 imagepng($imgplain, $tempfile_plain);
7315 imagedestroy($imgplain);
7316 $parsed = true;
7317 } catch (Exception $e) {
7318 // GD fails
7319 $parse_error = 'GD library error: '.$e->getMessage();
7320 }
7321 }
7322 if ($parsed === false) {
7323 if (empty($parse_error)) {
7324 $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
7325 } else {
7326 $this->Error($parse_error);
7327 }
7328 }
7329 // embed mask image
7330 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7331 // embed image, masked with previously embedded mask
7332 $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7333 }
7334
7342 protected function getGDgamma($img, $c) {
7343 if (!isset($this->gdgammacache['#'.$c])) {
7344 $colors = imagecolorsforindex($img, $c);
7345 // GD alpha is only 7 bit (0 -> 127)
7346 $this->gdgammacache['#'.$c] = (((127 - $colors['alpha']) / 127) * 255);
7347 // correct gamma
7348 $this->gdgammacache['#'.$c] = (pow(($this->gdgammacache['#'.$c] / 255), 2.2) * 255);
7349 // store the latest values on cache to improve performances
7350 if (count($this->gdgammacache) > 8) {
7351 // remove one element from the cache array
7352 array_shift($this->gdgammacache);
7353 }
7354 }
7355 return $this->gdgammacache['#'.$c];
7356 }
7357
7367 public function Ln($h='', $cell=false) {
7368 if (($this->num_columns > 1) AND ($this->y == $this->columns[$this->current_column]['y']) AND isset($this->columns[$this->current_column]['x']) AND ($this->x == $this->columns[$this->current_column]['x'])) {
7369 // revove vertical space from the top of the column
7370 return;
7371 }
7372 if ($cell) {
7373 if ($this->rtl) {
7374 $cellpadding = $this->cell_padding['R'];
7375 } else {
7376 $cellpadding = $this->cell_padding['L'];
7377 }
7378 } else {
7379 $cellpadding = 0;
7380 }
7381 if ($this->rtl) {
7382 $this->x = $this->w - $this->rMargin - $cellpadding;
7383 } else {
7384 $this->x = $this->lMargin + $cellpadding;
7385 }
7386 if (is_string($h)) {
7387 $h = $this->lasth;
7388 }
7389 $this->y += $h;
7390 $this->newline = true;
7391 }
7392
7401 public function GetX() {
7402 //Get x position
7403 if ($this->rtl) {
7404 return ($this->w - $this->x);
7405 } else {
7406 return $this->x;
7407 }
7408 }
7409
7417 public function GetAbsX() {
7418 return $this->x;
7419 }
7420
7428 public function GetY() {
7429 return $this->y;
7430 }
7431
7441 public function SetX($x, $rtloff=false) {
7442 $x = floatval($x);
7443 if (!$rtloff AND $this->rtl) {
7444 if ($x >= 0) {
7445 $this->x = $this->w - $x;
7446 } else {
7447 $this->x = abs($x);
7448 }
7449 } else {
7450 if ($x >= 0) {
7451 $this->x = $x;
7452 } else {
7453 $this->x = $this->w + $x;
7454 }
7455 }
7456 if ($this->x < 0) {
7457 $this->x = 0;
7458 }
7459 if ($this->x > $this->w) {
7460 $this->x = $this->w;
7461 }
7462 }
7463
7474 public function SetY($y, $resetx=true, $rtloff=false) {
7475 $y = floatval($y);
7476 if ($resetx) {
7477 //reset x
7478 if (!$rtloff AND $this->rtl) {
7479 $this->x = $this->w - $this->rMargin;
7480 } else {
7481 $this->x = $this->lMargin;
7482 }
7483 }
7484 if ($y >= 0) {
7485 $this->y = $y;
7486 } else {
7487 $this->y = $this->h + $y;
7488 }
7489 if ($this->y < 0) {
7490 $this->y = 0;
7491 }
7492 if ($this->y > $this->h) {
7493 $this->y = $this->h;
7494 }
7495 }
7496
7507 public function SetXY($x, $y, $rtloff=false) {
7508 $this->SetY($y, false, $rtloff);
7509 $this->SetX($x, $rtloff);
7510 }
7511
7519 public function SetAbsX($x) {
7520 $this->x = floatval($x);
7521 }
7522
7530 public function SetAbsY($y) {
7531 $this->y = floatval($y);
7532 }
7533
7542 public function SetAbsXY($x, $y) {
7543 $this->SetAbsX($x);
7544 $this->SetAbsY($y);
7545 }
7546
7558 public function Output($name='doc.pdf', $dest='I') {
7559 //Output PDF to some destination
7560 //Finish document if necessary
7561 if ($this->state < 3) {
7562 $this->Close();
7563 }
7564 //Normalize parameters
7565 if (is_bool($dest)) {
7566 $dest = $dest ? 'D' : 'F';
7567 }
7568 $dest = strtoupper($dest);
7569 if ($dest[0] != 'F') {
7570 $name = preg_replace('/[\s]+/', '_', $name);
7571 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
7572 }
7573 if ($this->sign) {
7574 // *** apply digital signature to the document ***
7575 // get the document content
7576 $pdfdoc = $this->getBuffer();
7577 // remove last newline
7578 $pdfdoc = substr($pdfdoc, 0, -1);
7579 // remove filler space
7580 $byterange_string_len = strlen(TCPDF_STATIC::$byterange_string);
7581 // define the ByteRange
7582 $byte_range = array();
7583 $byte_range[0] = 0;
7584 $byte_range[1] = strpos($pdfdoc, TCPDF_STATIC::$byterange_string) + $byterange_string_len + 10;
7585 $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
7586 $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
7587 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
7588 // replace the ByteRange
7589 $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
7590 $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
7591 $pdfdoc = str_replace(TCPDF_STATIC::$byterange_string, $byterange, $pdfdoc);
7592 // write the document to a temporary folder
7593 $tempdoc = TCPDF_STATIC::getObjFilename('doc', $this->file_id);
7594 $f = TCPDF_STATIC::fopenLocal($tempdoc, 'wb');
7595 if (!$f) {
7596 $this->Error('Unable to create temporary file: '.$tempdoc);
7597 }
7598 $pdfdoc_length = strlen($pdfdoc);
7599 fwrite($f, $pdfdoc, $pdfdoc_length);
7600 fclose($f);
7601 // get digital signature via openssl library
7602 $tempsign = TCPDF_STATIC::getObjFilename('sig', $this->file_id);
7603 if (empty($this->signature_data['extracerts'])) {
7604 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
7605 } else {
7606 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED, $this->signature_data['extracerts']);
7607 }
7608 // read signature
7609 $signature = file_get_contents($tempsign);
7610 // extract signature
7611 $signature = substr($signature, $pdfdoc_length);
7612 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
7613 $tmparr = explode("\n\n", $signature);
7614 $signature = $tmparr[1];
7615 // decode signature
7616 $signature = base64_decode(trim($signature));
7617 // add TSA timestamp to signature
7618 $signature = $this->applyTSA($signature);
7619 // convert signature to hex
7620 $signature = current(unpack('H*', $signature));
7621 $signature = str_pad($signature, $this->signature_max_length, '0');
7622 // Add signature to the document
7623 $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
7624 $this->bufferlen = strlen($this->buffer);
7625 }
7626 switch($dest) {
7627 case 'I': {
7628 // Send PDF to the standard output
7629 if (ob_get_contents()) {
7630 $this->Error('Some data has already been output, can\'t send PDF file');
7631 }
7632 if (php_sapi_name() != 'cli') {
7633 // send output to a browser
7634 header('Content-Type: application/pdf');
7635 if (headers_sent()) {
7636 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7637 }
7638 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7639 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7640 header('Pragma: public');
7641 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7642 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7643 header('Content-Disposition: inline; filename="'.basename($name).'"');
7644 TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7645 } else {
7646 echo $this->getBuffer();
7647 }
7648 break;
7649 }
7650 case 'D': {
7651 // download PDF as file
7652 if (ob_get_contents()) {
7653 $this->Error('Some data has already been output, can\'t send PDF file');
7654 }
7655 header('Content-Description: File Transfer');
7656 if (headers_sent()) {
7657 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7658 }
7659 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7660 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7661 header('Pragma: public');
7662 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7663 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7664 // force download dialog
7665 if (strpos(php_sapi_name(), 'cgi') === false) {
7666 header('Content-Type: application/force-download');
7667 header('Content-Type: application/octet-stream', false);
7668 header('Content-Type: application/download', false);
7669 header('Content-Type: application/pdf', false);
7670 } else {
7671 header('Content-Type: application/pdf');
7672 }
7673 // use the Content-Disposition header to supply a recommended filename
7674 header('Content-Disposition: attachment; filename="'.basename($name).'"');
7675 header('Content-Transfer-Encoding: binary');
7676 TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7677 break;
7678 }
7679 case 'F':
7680 case 'FI':
7681 case 'FD': {
7682 // save PDF to a local file
7683 $f = TCPDF_STATIC::fopenLocal($name, 'wb');
7684 if (!$f) {
7685 $this->Error('Unable to create output file: '.$name);
7686 }
7687 fwrite($f, $this->getBuffer(), $this->bufferlen);
7688 fclose($f);
7689 if ($dest == 'FI') {
7690 // send headers to browser
7691 header('Content-Type: application/pdf');
7692 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7693 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7694 header('Pragma: public');
7695 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7696 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7697 header('Content-Disposition: inline; filename="'.basename($name).'"');
7698 TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7699 } elseif ($dest == 'FD') {
7700 // send headers to browser
7701 if (ob_get_contents()) {
7702 $this->Error('Some data has already been output, can\'t send PDF file');
7703 }
7704 header('Content-Description: File Transfer');
7705 if (headers_sent()) {
7706 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7707 }
7708 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7709 header('Pragma: public');
7710 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7711 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7712 // force download dialog
7713 if (strpos(php_sapi_name(), 'cgi') === false) {
7714 header('Content-Type: application/force-download');
7715 header('Content-Type: application/octet-stream', false);
7716 header('Content-Type: application/download', false);
7717 header('Content-Type: application/pdf', false);
7718 } else {
7719 header('Content-Type: application/pdf');
7720 }
7721 // use the Content-Disposition header to supply a recommended filename
7722 header('Content-Disposition: attachment; filename="'.basename($name).'"');
7723 header('Content-Transfer-Encoding: binary');
7724 TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7725 }
7726 break;
7727 }
7728 case 'E': {
7729 // return PDF as base64 mime multi-part email attachment (RFC 2045)
7730 $retval = 'Content-Type: application/pdf;'."\r\n";
7731 $retval .= ' name="'.$name.'"'."\r\n";
7732 $retval .= 'Content-Transfer-Encoding: base64'."\r\n";
7733 $retval .= 'Content-Disposition: attachment;'."\r\n";
7734 $retval .= ' filename="'.$name.'"'."\r\n\r\n";
7735 $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
7736 return $retval;
7737 }
7738 case 'S': {
7739 // returns PDF as a string
7740 return $this->getBuffer();
7741 }
7742 default: {
7743 $this->Error('Incorrect output destination: '.$dest);
7744 }
7745 }
7746 return '';
7747 }
7748
7756 public function _destroy($destroyall=false, $preserve_objcopy=false) {
7757 if ($destroyall AND !$preserve_objcopy) {
7758 // remove all temporary files
7759 $tmpfiles = glob(K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_*');
7760 if (!empty($tmpfiles)) {
7761 array_map('unlink', $tmpfiles);
7762 }
7763 }
7764 $preserve = array(
7765 'file_id',
7766 'internal_encoding',
7767 'state',
7768 'bufferlen',
7769 'buffer',
7770 'cached_files',
7771 'sign',
7772 'signature_data',
7773 'signature_max_length',
7774 'byterange_string',
7775 'tsa_timestamp',
7776 'tsa_data'
7777 );
7778 foreach (array_keys(get_object_vars($this)) as $val) {
7779 if ($destroyall OR !in_array($val, $preserve)) {
7780 if ((!$preserve_objcopy OR ($val != 'objcopy')) AND ($val != 'file_id') AND isset($this->$val)) {
7781 unset($this->$val);
7782 }
7783 }
7784 }
7785 }
7786
7791 protected function _dochecks() {
7792 //Check for locale-related bug
7793 if (1.1 == 1) {
7794 $this->Error('Don\'t alter the locale before including class file');
7795 }
7796 //Check for decimal separator
7797 if (sprintf('%.1F', 1.0) != '1.0') {
7798 setlocale(LC_NUMERIC, 'C');
7799 }
7800 }
7801
7808 protected function getInternalPageNumberAliases($a= '') {
7809 $alias = array();
7810 // build array of Unicode + ASCII variants (the order is important)
7811 $alias = array('u' => array(), 'a' => array());
7812 $u = '{'.$a.'}';
7813 $alias['u'][] = TCPDF_STATIC::_escape($u);
7814 if ($this->isunicode) {
7815 $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($u, $this->isunicode, $this->CurrentFont));
7816 $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($u, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7817 $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($a, $this->isunicode, $this->CurrentFont));
7818 $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($a, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7819 }
7820 $alias['a'][] = TCPDF_STATIC::_escape($a);
7821 return $alias;
7822 }
7823
7829 protected function getAllInternalPageNumberAliases() {
7831 $pnalias = array();
7832 foreach($basic_alias as $k => $a) {
7833 $pnalias[$k] = $this->getInternalPageNumberAliases($a);
7834 }
7835 return $pnalias;
7836 }
7837
7847 protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
7848 foreach ($aliases as $type => $alias) {
7849 foreach ($alias as $a) {
7850 // find position of compensation factor
7851 $startnum = (strpos($a, ':') + 1);
7852 $a = substr($a, 0, $startnum);
7853 if (($pos = strpos($page, $a)) !== false) {
7854 // end of alias
7855 $endnum = strpos($page, '}', $pos);
7856 // string to be replaced
7857 $aa = substr($page, $pos, ($endnum - $pos + 1));
7858 // get compensation factor
7859 $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum));
7860 $ratio = preg_replace('/[^0-9\.]/', '', $ratio);
7861 $ratio = floatval($ratio);
7862 if ($type == 'u') {
7863 $chrdiff = floor(($diff + 12) * $ratio);
7864 $shift = str_repeat(' ', $chrdiff);
7865 $shift = TCPDF_FONTS::UTF8ToUTF16BE($shift, false, $this->isunicode, $this->CurrentFont);
7866 } else {
7867 $chrdiff = floor(($diff + 11) * $ratio);
7868 $shift = str_repeat(' ', $chrdiff);
7869 }
7870 $page = str_replace($aa, $shift, $page);
7871 }
7872 }
7873 }
7874 return $page;
7875 }
7876
7882 protected function setPageBoxTypes($boxes) {
7883 $this->page_boxes = array();
7884 foreach ($boxes as $box) {
7885 if (in_array($box, TCPDF_STATIC::$pageboxes)) {
7886 $this->page_boxes[] = $box;
7887 }
7888 }
7889 }
7890
7895 protected function _putpages() {
7896 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
7897 // get internal aliases for page numbers
7898 $pnalias = $this->getAllInternalPageNumberAliases();
7899 $num_pages = $this->numpages;
7900 $ptpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $num_pages - 1));
7901 $ptpu = TCPDF_FONTS::UTF8ToUTF16BE($ptpa, false, $this->isunicode, $this->CurrentFont);
7902 $ptp_num_chars = $this->GetNumChars($ptpa);
7903 $pagegroupnum = 0;
7904 $groupnum = 0;
7905 $ptgu = 1;
7906 $ptga = 1;
7907 $ptg_num_chars = 1;
7908 for ($n = 1; $n <= $num_pages; ++$n) {
7909 // get current page
7910 $temppage = $this->getPageBuffer($n);
7911 $pagelen = strlen($temppage);
7912 // set replacements for total pages number
7913 $pnpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $n - 1));
7914 $pnpu = TCPDF_FONTS::UTF8ToUTF16BE($pnpa, false, $this->isunicode, $this->CurrentFont);
7915 $pnp_num_chars = $this->GetNumChars($pnpa);
7916 $pdiff = 0; // difference used for right shift alignment of page numbers
7917 $gdiff = 0; // difference used for right shift alignment of page group numbers
7918 if (!empty($this->pagegroups)) {
7919 if (isset($this->newpagegroup[$n])) {
7920 $pagegroupnum = 0;
7921 ++$groupnum;
7922 $ptga = TCPDF_STATIC::formatPageNumber($this->pagegroups[$groupnum]);
7923 $ptgu = TCPDF_FONTS::UTF8ToUTF16BE($ptga, false, $this->isunicode, $this->CurrentFont);
7924 $ptg_num_chars = $this->GetNumChars($ptga);
7925 }
7926 ++$pagegroupnum;
7927 $pnga = TCPDF_STATIC::formatPageNumber($pagegroupnum);
7928 $pngu = TCPDF_FONTS::UTF8ToUTF16BE($pnga, false, $this->isunicode, $this->CurrentFont);
7929 $png_num_chars = $this->GetNumChars($pnga);
7930 // replace page numbers
7931 $replace = array();
7932 $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
7933 $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
7934 $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
7935 $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
7936 list($temppage, $gdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $gdiff);
7937 }
7938 // replace page numbers
7939 $replace = array();
7940 $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
7941 $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
7942 $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
7943 $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
7944 list($temppage, $pdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $pdiff);
7945 // replace right shift alias
7946 $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
7947 // replace EPS marker
7948 $temppage = str_replace($this->epsmarker, '', $temppage);
7949 //Page
7950 $this->page_obj_id[$n] = $this->_newobj();
7951 $out = '<<';
7952 $out .= ' /Type /Page';
7953 $out .= ' /Parent 1 0 R';
7954 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
7955 $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp);
7956 }
7957 $out .= ' /Resources 2 0 R';
7958 foreach ($this->page_boxes as $box) {
7959 $out .= ' /'.$box;
7960 $out .= sprintf(' [%F %F %F %F]', $this->pagedim[$n][$box]['llx'], $this->pagedim[$n][$box]['lly'], $this->pagedim[$n][$box]['urx'], $this->pagedim[$n][$box]['ury']);
7961 }
7962 if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
7963 $out .= ' /BoxColorInfo <<';
7964 foreach ($this->page_boxes as $box) {
7965 if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
7966 $out .= ' /'.$box.' <<';
7967 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
7968 $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
7969 $out .= ' /C [';
7970 $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
7971 $out .= ' ]';
7972 }
7973 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
7974 $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
7975 }
7976 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
7977 $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
7978 }
7979 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
7980 $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
7981 $out .= ' /D [';
7982 foreach ($dashes as $dash) {
7983 $out .= sprintf(' %F', ($dash * $this->k));
7984 }
7985 $out .= ' ]';
7986 }
7987 $out .= ' >>';
7988 }
7989 }
7990 $out .= ' >>';
7991 }
7992 $out .= ' /Contents '.($this->n + 1).' 0 R';
7993 $out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
7994 if (!$this->pdfa_mode) {
7995 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
7996 }
7997 if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
7998 // page transitions
7999 if (isset($this->pagedim[$n]['trans']['Dur'])) {
8000 $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
8001 }
8002 $out .= ' /Trans <<';
8003 $out .= ' /Type /Trans';
8004 if (isset($this->pagedim[$n]['trans']['S'])) {
8005 $out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
8006 }
8007 if (isset($this->pagedim[$n]['trans']['D'])) {
8008 $out .= ' /D '.$this->pagedim[$n]['trans']['D'];
8009 }
8010 if (isset($this->pagedim[$n]['trans']['Dm'])) {
8011 $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
8012 }
8013 if (isset($this->pagedim[$n]['trans']['M'])) {
8014 $out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
8015 }
8016 if (isset($this->pagedim[$n]['trans']['Di'])) {
8017 $out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
8018 }
8019 if (isset($this->pagedim[$n]['trans']['SS'])) {
8020 $out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
8021 }
8022 if (isset($this->pagedim[$n]['trans']['B'])) {
8023 $out .= ' /B '.$this->pagedim[$n]['trans']['B'];
8024 }
8025 $out .= ' >>';
8026 }
8027 $out .= $this->_getannotsrefs($n);
8028 $out .= ' /PZ '.$this->pagedim[$n]['PZ'];
8029 $out .= ' >>';
8030 $out .= "\n".'endobj';
8031 $this->_out($out);
8032 //Page content
8033 $p = ($this->compress) ? gzcompress($temppage) : $temppage;
8034 $this->_newobj();
8035 $p = $this->_getrawstream($p);
8036 $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
8037 }
8038 //Pages root
8039 $out = $this->_getobj(1)."\n";
8040 $out .= '<< /Type /Pages /Kids [';
8041 foreach($this->page_obj_id as $page_obj) {
8042 $out .= ' '.$page_obj.' 0 R';
8043 }
8044 $out .= ' ] /Count '.$num_pages.' >>';
8045 $out .= "\n".'endobj';
8046 $this->_out($out);
8047 }
8048
8057 protected function _getannotsrefs($n) {
8058 if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
8059 return '';
8060 }
8061 $out = ' /Annots [';
8062 if (isset($this->PageAnnots[$n])) {
8063 foreach ($this->PageAnnots[$n] as $key => $val) {
8064 if (!in_array($val['n'], $this->radio_groups)) {
8065 $out .= ' '.$val['n'].' 0 R';
8066 }
8067 }
8068 // add radiobutton groups
8069 if (isset($this->radiobutton_groups[$n])) {
8070 foreach ($this->radiobutton_groups[$n] as $key => $data) {
8071 if (isset($data['n'])) {
8072 $out .= ' '.$data['n'].' 0 R';
8073 }
8074 }
8075 }
8076 }
8077 if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
8078 // set reference for signature object
8079 $out .= ' '.$this->sig_obj_id.' 0 R';
8080 }
8081 if (!empty($this->empty_signature_appearance)) {
8082 foreach ($this->empty_signature_appearance as $esa) {
8083 if ($esa['page'] == $n) {
8084 // set reference for empty signature objects
8085 $out .= ' '.$esa['objid'].' 0 R';
8086 }
8087 }
8088 }
8089 $out .= ' ]';
8090 return $out;
8091 }
8092
8101 protected function _putannotsobjs() {
8102 // reset object counter
8103 for ($n=1; $n <= $this->numpages; ++$n) {
8104 if (isset($this->PageAnnots[$n])) {
8105 // set page annotations
8106 foreach ($this->PageAnnots[$n] as $key => $pl) {
8107 $annot_obj_id = $this->PageAnnots[$n][$key]['n'];
8108 // create annotation object for grouping radiobuttons
8109 if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
8110 $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n'];
8111 $annots = '<<';
8112 $annots .= ' /Type /Annot';
8113 $annots .= ' /Subtype /Widget';
8114 $annots .= ' /Rect [0 0 0 0]';
8115 if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) {
8116 // read only
8117 $annots .= ' /F 68';
8118 $annots .= ' /Ff 49153';
8119 } else {
8120 $annots .= ' /F 4'; // default print for PDF/A
8121 $annots .= ' /Ff 49152';
8122 }
8123 $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
8124 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8125 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id);
8126 }
8127 $annots .= ' /FT /Btn';
8128 $annots .= ' /Kids [';
8129 $defval = '';
8130 foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) {
8131 if (isset($data['kid'])) {
8132 $annots .= ' '.$data['kid'].' 0 R';
8133 if ($data['def'] !== 'Off') {
8134 $defval = $data['def'];
8135 }
8136 }
8137 }
8138 $annots .= ' ]';
8139 if (!empty($defval)) {
8140 $annots .= ' /V /'.$defval;
8141 }
8142 $annots .= ' >>';
8143 $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
8144 $this->form_obj_id[] = $radio_button_obj_id;
8145 // store object id to be used on Parent entry of Kids
8146 $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id;
8147 }
8148 $formfield = false;
8149 $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
8150 $a = $pl['x'] * $this->k;
8151 $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k);
8152 $c = $pl['w'] * $this->k;
8153 $d = $pl['h'] * $this->k;
8154 $rect = sprintf('%F %F %F %F', $a, $b, $a+$c, $b+$d);
8155 // create new annotation object
8156 $annots = '<</Type /Annot';
8157 $annots .= ' /Subtype /'.$pl['opt']['subtype'];
8158 $annots .= ' /Rect ['.$rect.']';
8159 $ft = array('Btn', 'Tx', 'Ch', 'Sig');
8160 if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
8161 $annots .= ' /FT /'.$pl['opt']['ft'];
8162 $formfield = true;
8163 }
8164 $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
8165 $annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
8166 $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
8167 $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp);
8168 if (isset($pl['opt']['f'])) {
8169 $fval = 0;
8170 if (is_array($pl['opt']['f'])) {
8171 foreach ($pl['opt']['f'] as $f) {
8172 switch (strtolower($f)) {
8173 case 'invisible': {
8174 $fval += 1 << 0;
8175 break;
8176 }
8177 case 'hidden': {
8178 $fval += 1 << 1;
8179 break;
8180 }
8181 case 'print': {
8182 $fval += 1 << 2;
8183 break;
8184 }
8185 case 'nozoom': {
8186 $fval += 1 << 3;
8187 break;
8188 }
8189 case 'norotate': {
8190 $fval += 1 << 4;
8191 break;
8192 }
8193 case 'noview': {
8194 $fval += 1 << 5;
8195 break;
8196 }
8197 case 'readonly': {
8198 $fval += 1 << 6;
8199 break;
8200 }
8201 case 'locked': {
8202 $fval += 1 << 8;
8203 break;
8204 }
8205 case 'togglenoview': {
8206 $fval += 1 << 9;
8207 break;
8208 }
8209 case 'lockedcontents': {
8210 $fval += 1 << 10;
8211 break;
8212 }
8213 default: {
8214 break;
8215 }
8216 }
8217 }
8218 } else {
8219 $fval = intval($pl['opt']['f']);
8220 }
8221 } else {
8222 $fval = 4;
8223 }
8224 if ($this->pdfa_mode) {
8225 // force print flag for PDF/A mode
8226 $fval |= 4;
8227 }
8228 $annots .= ' /F '.intval($fval);
8229 if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
8230 $annots .= ' /AS /'.$pl['opt']['as'];
8231 }
8232 if (isset($pl['opt']['ap'])) {
8233 // appearance stream
8234 $annots .= ' /AP <<';
8235 if (is_array($pl['opt']['ap'])) {
8236 foreach ($pl['opt']['ap'] as $apmode => $apdef) {
8237 // $apmode can be: n = normal; r = rollover; d = down;
8238 $annots .= ' /'.strtoupper($apmode);
8239 if (is_array($apdef)) {
8240 $annots .= ' <<';
8241 foreach ($apdef as $apstate => $stream) {
8242 // reference to XObject that define the appearance for this mode-state
8243 $apsobjid = $this->_putAPXObject($c, $d, $stream);
8244 $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
8245 }
8246 $annots .= ' >>';
8247 } else {
8248 // reference to XObject that define the appearance for this mode
8249 $apsobjid = $this->_putAPXObject($c, $d, $apdef);
8250 $annots .= ' '.$apsobjid.' 0 R';
8251 }
8252 }
8253 } else {
8254 $annots .= $pl['opt']['ap'];
8255 }
8256 $annots .= ' >>';
8257 }
8258 if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
8259 $annots .= ' /BS <<';
8260 $annots .= ' /Type /Border';
8261 if (isset($pl['opt']['bs']['w'])) {
8262 $annots .= ' /W '.intval($pl['opt']['bs']['w']);
8263 }
8264 $bstyles = array('S', 'D', 'B', 'I', 'U');
8265 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
8266 $annots .= ' /S /'.$pl['opt']['bs']['s'];
8267 }
8268 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
8269 $annots .= ' /D [';
8270 foreach ($pl['opt']['bs']['d'] as $cord) {
8271 $annots .= ' '.intval($cord);
8272 }
8273 $annots .= ']';
8274 }
8275 $annots .= ' >>';
8276 } else {
8277 $annots .= ' /Border [';
8278 if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
8279 $annots .= intval($pl['opt']['border'][0]).' ';
8280 $annots .= intval($pl['opt']['border'][1]).' ';
8281 $annots .= intval($pl['opt']['border'][2]);
8282 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
8283 $annots .= ' [';
8284 foreach ($pl['opt']['border'][3] as $dash) {
8285 $annots .= intval($dash).' ';
8286 }
8287 $annots .= ']';
8288 }
8289 } else {
8290 $annots .= '0 0 0';
8291 }
8292 $annots .= ']';
8293 }
8294 if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
8295 $annots .= ' /BE <<';
8296 $bstyles = array('S', 'C');
8297 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) {
8298 $annots .= ' /S /'.$pl['opt']['bs']['s'];
8299 } else {
8300 $annots .= ' /S /S';
8301 }
8302 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
8303 $annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']);
8304 }
8305 $annots .= '>>';
8306 }
8307 if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
8308 $annots .= ' /C '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['c']);
8309 }
8310 //$annots .= ' /StructParent ';
8311 //$annots .= ' /OC ';
8312 $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
8313 if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
8314 // this is a markup type
8315 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8316 $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
8317 }
8318 //$annots .= ' /Popup ';
8319 if (isset($pl['opt']['ca'])) {
8320 $annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca']));
8321 }
8322 if (isset($pl['opt']['rc'])) {
8323 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8324 }
8325 $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp);
8326 //$annots .= ' /IRT ';
8327 if (isset($pl['opt']['subj'])) {
8328 $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
8329 }
8330 //$annots .= ' /RT ';
8331 //$annots .= ' /IT ';
8332 //$annots .= ' /ExData ';
8333 }
8334 $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
8335 // Annotation types
8336 switch (strtolower($pl['opt']['subtype'])) {
8337 case 'text': {
8338 if (isset($pl['opt']['open'])) {
8339 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
8340 }
8341 $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
8342 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8343 $annots .= ' /Name /'.$pl['opt']['name'];
8344 } else {
8345 $annots .= ' /Name /Note';
8346 }
8347 $statemodels = array('Marked', 'Review');
8348 if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
8349 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8350 } else {
8351 $pl['opt']['statemodel'] = 'Marked';
8352 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8353 }
8354 if ($pl['opt']['statemodel'] == 'Marked') {
8355 $states = array('Accepted', 'Unmarked');
8356 } else {
8357 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
8358 }
8359 if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
8360 $annots .= ' /State /'.$pl['opt']['state'];
8361 } else {
8362 if ($pl['opt']['statemodel'] == 'Marked') {
8363 $annots .= ' /State /Unmarked';
8364 } else {
8365 $annots .= ' /State /None';
8366 }
8367 }
8368 break;
8369 }
8370 case 'link': {
8371 if (is_string($pl['txt'])) {
8372 if ($pl['txt'][0] == '#') {
8373 // internal destination
8374 $annots .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($pl['txt'], 1));
8375 } elseif ($pl['txt'][0] == '%') {
8376 // embedded PDF file
8377 $filename = basename(substr($pl['txt'], 1));
8378 $annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
8379 } elseif ($pl['txt'][0] == '*') {
8380 // embedded generic file
8381 $filename = basename(substr($pl['txt'], 1));
8382 $jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});';
8383 $annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>';
8384 } else {
8385 $parsedUrl = parse_url($pl['txt']);
8386 if (empty($parsedUrl['scheme']) AND (strtolower(substr($parsedUrl['path'], -4)) == '.pdf')) {
8387 // relative link to a PDF file
8388 $dest = '[0 /Fit]'; // default page 0
8389 if (!empty($parsedUrl['fragment'])) {
8390 // check for named destination
8391 $tmp = explode('=', $parsedUrl['fragment']);
8392 $dest = '('.((count($tmp) == 2) ? $tmp[1] : $tmp[0]).')';
8393 }
8394 $annots .= ' /A <</S /GoToR /D '.$dest.' /F '.$this->_datastring($this->unhtmlentities($parsedUrl['path']), $annot_obj_id).' /NewWindow true>>';
8395 } else {
8396 // external URI link
8397 $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
8398 }
8399 }
8400 } elseif (isset($this->links[$pl['txt']])) {
8401 // internal link ID
8402 $l = $this->links[$pl['txt']];
8403 if (isset($this->page_obj_id[($l['p'])])) {
8404 $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l['p'])], ($this->pagedim[$l['p']]['h'] - ($l['y'] * $this->k)));
8405 }
8406 }
8407 $hmodes = array('N', 'I', 'O', 'P');
8408 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
8409 $annots .= ' /H /'.$pl['opt']['h'];
8410 } else {
8411 $annots .= ' /H /I';
8412 }
8413 //$annots .= ' /PA ';
8414 //$annots .= ' /Quadpoints ';
8415 break;
8416 }
8417 case 'freetext': {
8418 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8419 $annots .= ' /DA ('.$pl['opt']['da'].')';
8420 }
8421 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8422 $annots .= ' /Q '.intval($pl['opt']['q']);
8423 }
8424 if (isset($pl['opt']['rc'])) {
8425 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8426 }
8427 if (isset($pl['opt']['ds'])) {
8428 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
8429 }
8430 if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
8431 $annots .= ' /CL [';
8432 foreach ($pl['opt']['cl'] as $cl) {
8433 $annots .= sprintf('%F ', $cl * $this->k);
8434 }
8435 $annots .= ']';
8436 }
8437 $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
8438 if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
8439 $annots .= ' /IT /'.$pl['opt']['it'];
8440 }
8441 if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
8442 $l = $pl['opt']['rd'][0] * $this->k;
8443 $r = $pl['opt']['rd'][1] * $this->k;
8444 $t = $pl['opt']['rd'][2] * $this->k;
8445 $b = $pl['opt']['rd'][3] * $this->k;
8446 $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']';
8447 }
8448 if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
8449 $annots .= ' /LE /'.$pl['opt']['le'];
8450 }
8451 break;
8452 }
8453 case 'line': {
8454 break;
8455 }
8456 case 'square': {
8457 break;
8458 }
8459 case 'circle': {
8460 break;
8461 }
8462 case 'polygon': {
8463 break;
8464 }
8465 case 'polyline': {
8466 break;
8467 }
8468 case 'highlight': {
8469 break;
8470 }
8471 case 'underline': {
8472 break;
8473 }
8474 case 'squiggly': {
8475 break;
8476 }
8477 case 'strikeout': {
8478 break;
8479 }
8480 case 'stamp': {
8481 break;
8482 }
8483 case 'caret': {
8484 break;
8485 }
8486 case 'ink': {
8487 break;
8488 }
8489 case 'popup': {
8490 break;
8491 }
8492 case 'fileattachment': {
8493 if ($this->pdfa_mode) {
8494 // embedded files are not allowed in PDF/A mode
8495 break;
8496 }
8497 if (!isset($pl['opt']['fs'])) {
8498 break;
8499 }
8500 $filename = basename($pl['opt']['fs']);
8501 if (isset($this->embeddedfiles[$filename]['f'])) {
8502 $annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R';
8503 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
8504 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8505 $annots .= ' /Name /'.$pl['opt']['name'];
8506 } else {
8507 $annots .= ' /Name /PushPin';
8508 }
8509 // index (zero-based) of the annotation in the Annots array of this page
8510 $this->embeddedfiles[$filename]['a'] = $key;
8511 }
8512 break;
8513 }
8514 case 'sound': {
8515 if (!isset($pl['opt']['fs'])) {
8516 break;
8517 }
8518 $filename = basename($pl['opt']['fs']);
8519 if (isset($this->embeddedfiles[$filename]['f'])) {
8520 // ... TO BE COMPLETED ...
8521 // /R /C /B /E /CO /CP
8522 $annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R';
8523 $iconsapp = array('Speaker', 'Mic');
8524 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8525 $annots .= ' /Name /'.$pl['opt']['name'];
8526 } else {
8527 $annots .= ' /Name /Speaker';
8528 }
8529 }
8530 break;
8531 }
8532 case 'movie': {
8533 break;
8534 }
8535 case 'widget': {
8536 $hmode = array('N', 'I', 'O', 'P', 'T');
8537 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
8538 $annots .= ' /H /'.$pl['opt']['h'];
8539 }
8540 if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
8541 $annots .= ' /MK <<';
8542 if (isset($pl['opt']['mk']['r'])) {
8543 $annots .= ' /R '.$pl['opt']['mk']['r'];
8544 }
8545 if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
8546 $annots .= ' /BC '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bc']);
8547 }
8548 if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
8549 $annots .= ' /BG '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bg']);
8550 }
8551 if (isset($pl['opt']['mk']['ca'])) {
8552 $annots .= ' /CA '.$pl['opt']['mk']['ca'];
8553 }
8554 if (isset($pl['opt']['mk']['rc'])) {
8555 $annots .= ' /RC '.$pl['opt']['mk']['rc'];
8556 }
8557 if (isset($pl['opt']['mk']['ac'])) {
8558 $annots .= ' /AC '.$pl['opt']['mk']['ac'];
8559 }
8560 if (isset($pl['opt']['mk']['i'])) {
8561 $info = $this->getImageBuffer($pl['opt']['mk']['i']);
8562 if ($info !== false) {
8563 $annots .= ' /I '.$info['n'].' 0 R';
8564 }
8565 }
8566 if (isset($pl['opt']['mk']['ri'])) {
8567 $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
8568 if ($info !== false) {
8569 $annots .= ' /RI '.$info['n'].' 0 R';
8570 }
8571 }
8572 if (isset($pl['opt']['mk']['ix'])) {
8573 $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
8574 if ($info !== false) {
8575 $annots .= ' /IX '.$info['n'].' 0 R';
8576 }
8577 }
8578 if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
8579 $annots .= ' /IF <<';
8580 $if_sw = array('A', 'B', 'S', 'N');
8581 if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
8582 $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
8583 }
8584 $if_s = array('A', 'P');
8585 if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
8586 $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
8587 }
8588 if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
8589 $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
8590 }
8591 if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
8592 $annots .= ' /FB true';
8593 }
8594 $annots .= '>>';
8595 }
8596 if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
8597 $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
8598 }
8599 $annots .= '>>';
8600 } // end MK
8601 // --- Entries for field dictionaries ---
8602 if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
8603 // set parent
8604 $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
8605 }
8606 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8607 $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
8608 }
8609 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8610 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
8611 }
8612 if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
8613 $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
8614 }
8615 if (isset($pl['opt']['ff'])) {
8616 if (is_array($pl['opt']['ff'])) {
8617 // array of bit settings
8618 $flag = 0;
8619 foreach($pl['opt']['ff'] as $val) {
8620 $flag += 1 << ($val - 1);
8621 }
8622 } else {
8623 $flag = intval($pl['opt']['ff']);
8624 }
8625 $annots .= ' /Ff '.$flag;
8626 }
8627 if (isset($pl['opt']['maxlen'])) {
8628 $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
8629 }
8630 if (isset($pl['opt']['v'])) {
8631 $annots .= ' /V';
8632 if (is_array($pl['opt']['v'])) {
8633 foreach ($pl['opt']['v'] AS $optval) {
8634 if (is_float($optval)) {
8635 $optval = sprintf('%F', $optval);
8636 }
8637 $annots .= ' '.$optval;
8638 }
8639 } else {
8640 $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
8641 }
8642 }
8643 if (isset($pl['opt']['dv'])) {
8644 $annots .= ' /DV';
8645 if (is_array($pl['opt']['dv'])) {
8646 foreach ($pl['opt']['dv'] AS $optval) {
8647 if (is_float($optval)) {
8648 $optval = sprintf('%F', $optval);
8649 }
8650 $annots .= ' '.$optval;
8651 }
8652 } else {
8653 $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
8654 }
8655 }
8656 if (isset($pl['opt']['rv'])) {
8657 $annots .= ' /RV';
8658 if (is_array($pl['opt']['rv'])) {
8659 foreach ($pl['opt']['rv'] AS $optval) {
8660 if (is_float($optval)) {
8661 $optval = sprintf('%F', $optval);
8662 }
8663 $annots .= ' '.$optval;
8664 }
8665 } else {
8666 $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
8667 }
8668 }
8669 if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
8670 $annots .= ' /A << '.$pl['opt']['a'].' >>';
8671 }
8672 if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
8673 $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
8674 }
8675 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8676 $annots .= ' /DA ('.$pl['opt']['da'].')';
8677 }
8678 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8679 $annots .= ' /Q '.intval($pl['opt']['q']);
8680 }
8681 if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
8682 $annots .= ' /Opt [';
8683 foreach($pl['opt']['opt'] AS $copt) {
8684 if (is_array($copt)) {
8685 $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
8686 } else {
8687 $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
8688 }
8689 }
8690 $annots .= ']';
8691 }
8692 if (isset($pl['opt']['ti'])) {
8693 $annots .= ' /TI '.intval($pl['opt']['ti']);
8694 }
8695 if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
8696 $annots .= ' /I [';
8697 foreach($pl['opt']['i'] AS $copt) {
8698 $annots .= intval($copt).' ';
8699 }
8700 $annots .= ']';
8701 }
8702 break;
8703 }
8704 case 'screen': {
8705 break;
8706 }
8707 case 'printermark': {
8708 break;
8709 }
8710 case 'trapnet': {
8711 break;
8712 }
8713 case 'watermark': {
8714 break;
8715 }
8716 case '3d': {
8717 break;
8718 }
8719 default: {
8720 break;
8721 }
8722 }
8723 $annots .= '>>';
8724 // create new annotation object
8725 $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
8726 if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
8727 // store reference of form object
8728 $this->form_obj_id[] = $annot_obj_id;
8729 }
8730 }
8731 }
8732 } // end for each page
8733 }
8734
8744 protected function _putAPXObject($w=0, $h=0, $stream='') {
8745 $stream = trim($stream);
8746 $out = $this->_getobj()."\n";
8747 $this->xobjects['AX'.$this->n] = array('n' => $this->n);
8748 $out .= '<<';
8749 $out .= ' /Type /XObject';
8750 $out .= ' /Subtype /Form';
8751 $out .= ' /FormType 1';
8752 if ($this->compress) {
8753 $stream = gzcompress($stream);
8754 $out .= ' /Filter /FlateDecode';
8755 }
8756 $rect = sprintf('%F %F', $w, $h);
8757 $out .= ' /BBox [0 0 '.$rect.']';
8758 $out .= ' /Matrix [1 0 0 1 0 0]';
8759 $out .= ' /Resources 2 0 R';
8760 $stream = $this->_getrawstream($stream);
8761 $out .= ' /Length '.strlen($stream);
8762 $out .= ' >>';
8763 $out .= ' stream'."\n".$stream."\n".'endstream';
8764 $out .= "\n".'endobj';
8765 $this->_out($out);
8766 return $this->n;
8767 }
8768
8774 protected function _putfonts() {
8775 $nf = $this->n;
8776 foreach ($this->diffs as $diff) {
8777 //Encodings
8778 $this->_newobj();
8779 $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
8780 }
8781 $mqr = TCPDF_STATIC::get_mqr();
8782 TCPDF_STATIC::set_mqr(false);
8783 foreach ($this->FontFiles as $file => $info) {
8784 // search and get font file to embedd
8785 $fontfile = TCPDF_FONTS::getFontFullPath($file, $info['fontdir']);
8786 if (!TCPDF_STATIC::empty_string($fontfile)) {
8787 $font = file_get_contents($fontfile);
8788 $compressed = (substr($file, -2) == '.z');
8789 if ((!$compressed) AND (isset($info['length2']))) {
8790 $header = (ord($font[0]) == 128);
8791 if ($header) {
8792 // strip first binary header
8793 $font = substr($font, 6);
8794 }
8795 if ($header AND (ord($font[$info['length1']]) == 128)) {
8796 // strip second binary header
8797 $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
8798 }
8799 } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
8800 if ($compressed) {
8801 // uncompress font
8802 $font = gzuncompress($font);
8803 }
8804 // merge subset characters
8805 $subsetchars = array(); // used chars
8806 foreach ($info['fontkeys'] as $fontkey) {
8807 $fontinfo = $this->getFontBuffer($fontkey);
8808 $subsetchars += $fontinfo['subsetchars'];
8809 }
8810 // rebuild a font subset
8811 $font = TCPDF_FONTS::_getTrueTypeFontSubset($font, $subsetchars);
8812 // calculate new font length
8813 $info['length1'] = strlen($font);
8814 if ($compressed) {
8815 // recompress font
8816 $font = gzcompress($font);
8817 }
8818 }
8819 $this->_newobj();
8820 $this->FontFiles[$file]['n'] = $this->n;
8821 $stream = $this->_getrawstream($font);
8822 $out = '<< /Length '.strlen($stream);
8823 if ($compressed) {
8824 $out .= ' /Filter /FlateDecode';
8825 }
8826 $out .= ' /Length1 '.$info['length1'];
8827 if (isset($info['length2'])) {
8828 $out .= ' /Length2 '.$info['length2'].' /Length3 0';
8829 }
8830 $out .= ' >>';
8831 $out .= ' stream'."\n".$stream."\n".'endstream';
8832 $out .= "\n".'endobj';
8833 $this->_out($out);
8834 }
8835 }
8837 foreach ($this->fontkeys as $k) {
8838 //Font objects
8839 $font = $this->getFontBuffer($k);
8840 $type = $font['type'];
8841 $name = $font['name'];
8842 if ($type == 'core') {
8843 // standard core font
8844 $out = $this->_getobj($this->font_obj_ids[$k])."\n";
8845 $out .= '<</Type /Font';
8846 $out .= ' /Subtype /Type1';
8847 $out .= ' /BaseFont /'.$name;
8848 $out .= ' /Name /F'.$font['i'];
8849 if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
8850 $out .= ' /Encoding /WinAnsiEncoding';
8851 }
8852 if ($k == 'helvetica') {
8853 // add default font for annotations
8854 $this->annotation_fonts[$k] = $font['i'];
8855 }
8856 $out .= ' >>';
8857 $out .= "\n".'endobj';
8858 $this->_out($out);
8859 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
8860 // additional Type1 or TrueType font
8861 $out = $this->_getobj($this->font_obj_ids[$k])."\n";
8862 $out .= '<</Type /Font';
8863 $out .= ' /Subtype /'.$type;
8864 $out .= ' /BaseFont /'.$name;
8865 $out .= ' /Name /F'.$font['i'];
8866 $out .= ' /FirstChar 32 /LastChar 255';
8867 $out .= ' /Widths '.($this->n + 1).' 0 R';
8868 $out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
8869 if ($font['enc']) {
8870 if (isset($font['diff'])) {
8871 $out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
8872 } else {
8873 $out .= ' /Encoding /WinAnsiEncoding';
8874 }
8875 }
8876 $out .= ' >>';
8877 $out .= "\n".'endobj';
8878 $this->_out($out);
8879 // Widths
8880 $this->_newobj();
8881 $s = '[';
8882 for ($i = 32; $i < 256; ++$i) {
8883 if (isset($font['cw'][$i])) {
8884 $s .= $font['cw'][$i].' ';
8885 } else {
8886 $s .= $font['dw'].' ';
8887 }
8888 }
8889 $s .= ']';
8890 $s .= "\n".'endobj';
8891 $this->_out($s);
8892 //Descriptor
8893 $this->_newobj();
8894 $s = '<</Type /FontDescriptor /FontName /'.$name;
8895 foreach ($font['desc'] as $fdk => $fdv) {
8896 if (is_float($fdv)) {
8897 $fdv = sprintf('%F', $fdv);
8898 }
8899 $s .= ' /'.$fdk.' '.$fdv.'';
8900 }
8901 if (!TCPDF_STATIC::empty_string($font['file'])) {
8902 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
8903 }
8904 $s .= '>>';
8905 $s .= "\n".'endobj';
8906 $this->_out($s);
8907 } else {
8908 // additional types
8909 $mtd = '_put'.strtolower($type);
8910 if (!method_exists($this, $mtd)) {
8911 $this->Error('Unsupported font type: '.$type);
8912 }
8913 $this->$mtd($font);
8914 }
8915 }
8916 }
8917
8926 protected function _puttruetypeunicode($font) {
8927 $fontname = '';
8928 if ($font['subset']) {
8929 // change name for font subsetting
8930 $subtag = sprintf('%06u', $font['i']);
8931 $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
8932 $fontname .= $subtag.'+';
8933 }
8934 $fontname .= $font['name'];
8935 // Type0 Font
8936 // A composite font composed of other fonts, organized hierarchically
8937 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
8938 $out .= '<< /Type /Font';
8939 $out .= ' /Subtype /Type0';
8940 $out .= ' /BaseFont /'.$fontname;
8941 $out .= ' /Name /F'.$font['i'];
8942 $out .= ' /Encoding /'.$font['enc'];
8943 $out .= ' /ToUnicode '.($this->n + 1).' 0 R';
8944 $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
8945 $out .= ' >>';
8946 $out .= "\n".'endobj';
8947 $this->_out($out);
8948 // ToUnicode map for Identity-H
8950 // ToUnicode Object
8951 $this->_newobj();
8952 $stream = ($this->compress) ? gzcompress($stream) : $stream;
8953 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
8954 $stream = $this->_getrawstream($stream);
8955 $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
8956 // CIDFontType2
8957 // A CIDFont whose glyph descriptions are based on TrueType font technology
8958 $oid = $this->_newobj();
8959 $out = '<< /Type /Font';
8960 $out .= ' /Subtype /CIDFontType2';
8961 $out .= ' /BaseFont /'.$fontname;
8962 // A dictionary containing entries that define the character collection of the CIDFont.
8963 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
8964 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
8965 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
8966 $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
8967 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
8968 $out .= ' /DW '.$font['dw']; // default width
8969 $out .= "\n".TCPDF_FONTS::_putfontwidths($font, 0);
8970 if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
8971 $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
8972 }
8973 $out .= ' >>';
8974 $out .= "\n".'endobj';
8975 $this->_out($out);
8976 // Font descriptor
8977 // A font descriptor describing the CIDFont default metrics other than its glyph widths
8978 $this->_newobj();
8979 $out = '<< /Type /FontDescriptor';
8980 $out .= ' /FontName /'.$fontname;
8981 foreach ($font['desc'] as $key => $value) {
8982 if (is_float($value)) {
8983 $value = sprintf('%F', $value);
8984 }
8985 $out .= ' /'.$key.' '.$value;
8986 }
8987 $fontdir = false;
8988 if (!TCPDF_STATIC::empty_string($font['file'])) {
8989 // A stream containing a TrueType font
8990 $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
8991 $fontdir = $this->FontFiles[$font['file']]['fontdir'];
8992 }
8993 $out .= ' >>';
8994 $out .= "\n".'endobj';
8995 $this->_out($out);
8996 if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
8997 $this->_newobj();
8998 // Embed CIDToGIDMap
8999 // A specification of the mapping from CIDs to glyph indices
9000 // search and get CTG font file to embedd
9001 $ctgfile = strtolower($font['ctg']);
9002 // search and get ctg font file to embedd
9003 $fontfile = TCPDF_FONTS::getFontFullPath($ctgfile, $fontdir);
9004 if (TCPDF_STATIC::empty_string($fontfile)) {
9005 $this->Error('Font file not found: '.$ctgfile);
9006 }
9007 $stream = $this->_getrawstream(file_get_contents($fontfile));
9008 $out = '<< /Length '.strlen($stream).'';
9009 if (substr($fontfile, -2) == '.z') { // check file extension
9010 // Decompresses data encoded using the public-domain
9011 // zlib/deflate compression method, reproducing the
9012 // original text or binary data
9013 $out .= ' /Filter /FlateDecode';
9014 }
9015 $out .= ' >>';
9016 $out .= ' stream'."\n".$stream."\n".'endstream';
9017 $out .= "\n".'endobj';
9018 $this->_out($out);
9019 }
9020 }
9021
9030 protected function _putcidfont0($font) {
9031 $cidoffset = 0;
9032 if (!isset($font['cw'][1])) {
9033 $cidoffset = 31;
9034 }
9035 if (isset($font['cidinfo']['uni2cid'])) {
9036 // convert unicode to cid.
9037 $uni2cid = $font['cidinfo']['uni2cid'];
9038 $cw = array();
9039 foreach ($font['cw'] as $uni => $width) {
9040 if (isset($uni2cid[$uni])) {
9041 $cw[($uni2cid[$uni] + $cidoffset)] = $width;
9042 } elseif ($uni < 256) {
9043 $cw[$uni] = $width;
9044 } // else unknown character
9045 }
9046 $font = array_merge($font, array('cw' => $cw));
9047 }
9048 $name = $font['name'];
9049 $enc = $font['enc'];
9050 if ($enc) {
9051 $longname = $name.'-'.$enc;
9052 } else {
9053 $longname = $name;
9054 }
9055 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
9056 $out .= '<</Type /Font';
9057 $out .= ' /Subtype /Type0';
9058 $out .= ' /BaseFont /'.$longname;
9059 $out .= ' /Name /F'.$font['i'];
9060 if ($enc) {
9061 $out .= ' /Encoding /'.$enc;
9062 }
9063 $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
9064 $out .= ' >>';
9065 $out .= "\n".'endobj';
9066 $this->_out($out);
9067 $oid = $this->_newobj();
9068 $out = '<</Type /Font';
9069 $out .= ' /Subtype /CIDFontType0';
9070 $out .= ' /BaseFont /'.$name;
9071 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9072 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9073 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9074 $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
9075 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
9076 $out .= ' /DW '.$font['dw'];
9077 $out .= "\n".TCPDF_FONTS::_putfontwidths($font, $cidoffset);
9078 $out .= ' >>';
9079 $out .= "\n".'endobj';
9080 $this->_out($out);
9081 $this->_newobj();
9082 $s = '<</Type /FontDescriptor /FontName /'.$name;
9083 foreach ($font['desc'] as $k => $v) {
9084 if ($k != 'Style') {
9085 if (is_float($v)) {
9086 $v = sprintf('%F', $v);
9087 }
9088 $s .= ' /'.$k.' '.$v.'';
9089 }
9090 }
9091 $s .= '>>';
9092 $s .= "\n".'endobj';
9093 $this->_out($s);
9094 }
9095
9100 protected function _putimages() {
9101 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9102 foreach ($this->imagekeys as $file) {
9103 $info = $this->getImageBuffer($file);
9104 // set object for alternate images array
9105 if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
9106 $altoid = $this->_newobj();
9107 $out = '[';
9108 foreach ($info['altimgs'] as $altimage) {
9109 if (isset($this->xobjects['I'.$altimage[0]]['n'])) {
9110 $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R';
9111 $out .= ' /DefaultForPrinting';
9112 if ($altimage[1] === true) {
9113 $out .= ' true';
9114 } else {
9115 $out .= ' false';
9116 }
9117 $out .= ' >>';
9118 }
9119 }
9120 $out .= ' ]';
9121 $out .= "\n".'endobj';
9122 $this->_out($out);
9123 }
9124 // set image object
9125 $oid = $this->_newobj();
9126 $this->xobjects['I'.$info['i']] = array('n' => $oid);
9127 $this->setImageSubBuffer($file, 'n', $this->n);
9128 $out = '<</Type /XObject';
9129 $out .= ' /Subtype /Image';
9130 $out .= ' /Width '.$info['w'];
9131 $out .= ' /Height '.$info['h'];
9132 if (array_key_exists('masked', $info)) {
9133 $out .= ' /SMask '.($this->n - 1).' 0 R';
9134 }
9135 // set color space
9136 $icc = false;
9137 if (isset($info['icc']) AND ($info['icc'] !== false)) {
9138 // ICC Colour Space
9139 $icc = true;
9140 $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]';
9141 } elseif ($info['cs'] == 'Indexed') {
9142 // Indexed Colour Space
9143 $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
9144 } else {
9145 // Device Colour Space
9146 $out .= ' /ColorSpace /'.$info['cs'];
9147 }
9148 if ($info['cs'] == 'DeviceCMYK') {
9149 $out .= ' /Decode [1 0 1 0 1 0 1 0]';
9150 }
9151 $out .= ' /BitsPerComponent '.$info['bpc'];
9152 if (isset($altoid) AND ($altoid > 0)) {
9153 // reference to alternate images dictionary
9154 $out .= ' /Alternates '.$altoid.' 0 R';
9155 }
9156 if (isset($info['exurl']) AND !empty($info['exurl'])) {
9157 // external stream
9158 $out .= ' /Length 0';
9159 $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
9160 if (isset($info['f'])) {
9161 $out .= ' /FFilter /'.$info['f'];
9162 }
9163 $out .= ' >>';
9164 $out .= ' stream'."\n".'endstream';
9165 } else {
9166 if (isset($info['f'])) {
9167 $out .= ' /Filter /'.$info['f'];
9168 }
9169 if (isset($info['parms'])) {
9170 $out .= ' '.$info['parms'];
9171 }
9172 if (isset($info['trns']) AND is_array($info['trns'])) {
9173 $trns = '';
9174 $count_info = count($info['trns']);
9175 if ($info['cs'] == 'Indexed') {
9176 $maxval =(pow(2, $info['bpc']) - 1);
9177 for ($i = 0; $i < $count_info; ++$i) {
9178 if (($info['trns'][$i] != 0) AND ($info['trns'][$i] != $maxval)) {
9179 // this is not a binary type mask @TODO: create a SMask
9180 $trns = '';
9181 break;
9182 } elseif (empty($trns) AND ($info['trns'][$i] == 0)) {
9183 // store the first fully transparent value
9184 $trns .= $i.' '.$i.' ';
9185 }
9186 }
9187 } else {
9188 // grayscale or RGB
9189 for ($i = 0; $i < $count_info; ++$i) {
9190 if ($info['trns'][$i] == 0) {
9191 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
9192 }
9193 }
9194 }
9195 // Colour Key Masking
9196 if (!empty($trns)) {
9197 $out .= ' /Mask ['.$trns.']';
9198 }
9199 }
9200 $stream = $this->_getrawstream($info['data']);
9201 $out .= ' /Length '.strlen($stream).' >>';
9202 $out .= ' stream'."\n".$stream."\n".'endstream';
9203 }
9204 $out .= "\n".'endobj';
9205 $this->_out($out);
9206 if ($icc) {
9207 // ICC colour profile
9208 $this->_newobj();
9209 $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
9210 $icc = $this->_getrawstream($icc);
9211 $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9212 } elseif ($info['cs'] == 'Indexed') {
9213 // colour palette
9214 $this->_newobj();
9215 $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
9216 $pal = $this->_getrawstream($pal);
9217 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
9218 }
9219 }
9220 }
9221
9229 protected function _putxobjects() {
9230 foreach ($this->xobjects as $key => $data) {
9231 if (isset($data['outdata'])) {
9232 $stream = str_replace($this->epsmarker, '', trim($data['outdata']));
9233 $out = $this->_getobj($data['n'])."\n";
9234 $out .= '<<';
9235 $out .= ' /Type /XObject';
9236 $out .= ' /Subtype /Form';
9237 $out .= ' /FormType 1';
9238 if ($this->compress) {
9239 $stream = gzcompress($stream);
9240 $out .= ' /Filter /FlateDecode';
9241 }
9242 $out .= sprintf(' /BBox [%F %F %F %F]', ($data['x'] * $this->k), (-$data['y'] * $this->k), (($data['w'] + $data['x']) * $this->k), (($data['h'] - $data['y']) * $this->k));
9243 $out .= ' /Matrix [1 0 0 1 0 0]';
9244 $out .= ' /Resources <<';
9245 $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9246 if (!$this->pdfa_mode) {
9247 // transparency
9248 if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
9249 $out .= ' /ExtGState <<';
9250 foreach ($data['extgstates'] as $k => $extgstate) {
9251 if (isset($this->extgstates[$k]['name'])) {
9252 $out .= ' /'.$this->extgstates[$k]['name'];
9253 } else {
9254 $out .= ' /GS'.$k;
9255 }
9256 $out .= ' '.$this->extgstates[$k]['n'].' 0 R';
9257 }
9258 $out .= ' >>';
9259 }
9260 if (isset($data['gradients']) AND !empty($data['gradients'])) {
9261 $gp = '';
9262 $gs = '';
9263 foreach ($data['gradients'] as $id => $grad) {
9264 // gradient patterns
9265 $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R';
9266 // gradient shadings
9267 $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R';
9268 }
9269 $out .= ' /Pattern <<'.$gp.' >>';
9270 $out .= ' /Shading <<'.$gs.' >>';
9271 }
9272 }
9273 // spot colors
9274 if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
9275 $out .= ' /ColorSpace <<';
9276 foreach ($data['spot_colors'] as $name => $color) {
9277 $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R';
9278 }
9279 $out .= ' >>';
9280 }
9281 // fonts
9282 if (!empty($data['fonts'])) {
9283 $out .= ' /Font <<';
9284 foreach ($data['fonts'] as $fontkey => $fontid) {
9285 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9286 }
9287 $out .= ' >>';
9288 }
9289 // images or nested xobjects
9290 if (!empty($data['images']) OR !empty($data['xobjects'])) {
9291 $out .= ' /XObject <<';
9292 foreach ($data['images'] as $imgid) {
9293 $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
9294 }
9295 foreach ($data['xobjects'] as $sub_id => $sub_objid) {
9296 $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
9297 }
9298 $out .= ' >>';
9299 }
9300 $out .= ' >>'; //end resources
9301 if (isset($data['group']) AND ($data['group'] !== false)) {
9302 // set transparency group
9303 $out .= ' /Group << /Type /Group /S /Transparency';
9304 if (is_array($data['group'])) {
9305 if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
9306 $out .= ' /CS /'.$data['group']['CS'];
9307 }
9308 if (isset($data['group']['I'])) {
9309 $out .= ' /I /'.($data['group']['I']===true?'true':'false');
9310 }
9311 if (isset($data['group']['K'])) {
9312 $out .= ' /K /'.($data['group']['K']===true?'true':'false');
9313 }
9314 }
9315 $out .= ' >>';
9316 }
9317 $stream = $this->_getrawstream($stream, $data['n']);
9318 $out .= ' /Length '.strlen($stream);
9319 $out .= ' >>';
9320 $out .= ' stream'."\n".$stream."\n".'endstream';
9321 $out .= "\n".'endobj';
9322 $this->_out($out);
9323 }
9324 }
9325 }
9326
9332 protected function _putspotcolors() {
9333 foreach ($this->spot_colors as $name => $color) {
9334 $this->_newobj();
9335 $this->spot_colors[$name]['n'] = $this->n;
9336 $out = '[/Separation /'.str_replace(' ', '#20', $name);
9337 $out .= ' /DeviceCMYK <<';
9338 $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
9339 $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
9340 $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
9341 $out .= "\n".'endobj';
9342 $this->_out($out);
9343 }
9344 }
9345
9352 protected function _getxobjectdict() {
9353 $out = '';
9354 foreach ($this->xobjects as $id => $objid) {
9355 $out .= ' /'.$id.' '.$objid['n'].' 0 R';
9356 }
9357 return $out;
9358 }
9359
9364 protected function _putresourcedict() {
9365 $out = $this->_getobj(2)."\n";
9366 $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9367 $out .= ' /Font <<';
9368 foreach ($this->fontkeys as $fontkey) {
9369 $font = $this->getFontBuffer($fontkey);
9370 $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
9371 }
9372 $out .= ' >>';
9373 $out .= ' /XObject <<';
9374 $out .= $this->_getxobjectdict();
9375 $out .= ' >>';
9376 // layers
9377 if (!empty($this->pdflayers)) {
9378 $out .= ' /Properties <<';
9379 foreach ($this->pdflayers as $layer) {
9380 $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
9381 }
9382 $out .= ' >>';
9383 }
9384 if (!$this->pdfa_mode) {
9385 // transparency
9386 if (isset($this->extgstates) AND !empty($this->extgstates)) {
9387 $out .= ' /ExtGState <<';
9388 foreach ($this->extgstates as $k => $extgstate) {
9389 if (isset($extgstate['name'])) {
9390 $out .= ' /'.$extgstate['name'];
9391 } else {
9392 $out .= ' /GS'.$k;
9393 }
9394 $out .= ' '.$extgstate['n'].' 0 R';
9395 }
9396 $out .= ' >>';
9397 }
9398 if (isset($this->gradients) AND !empty($this->gradients)) {
9399 $gp = '';
9400 $gs = '';
9401 foreach ($this->gradients as $id => $grad) {
9402 // gradient patterns
9403 $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
9404 // gradient shadings
9405 $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
9406 }
9407 $out .= ' /Pattern <<'.$gp.' >>';
9408 $out .= ' /Shading <<'.$gs.' >>';
9409 }
9410 }
9411 // spot colors
9412 if (isset($this->spot_colors) AND !empty($this->spot_colors)) {
9413 $out .= ' /ColorSpace <<';
9414 foreach ($this->spot_colors as $color) {
9415 $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
9416 }
9417 $out .= ' >>';
9418 }
9419 $out .= ' >>';
9420 $out .= "\n".'endobj';
9421 $this->_out($out);
9422 }
9423
9428 protected function _putresources() {
9429 $this->_putextgstates();
9430 $this->_putocg();
9431 $this->_putfonts();
9432 $this->_putimages();
9433 $this->_putspotcolors();
9434 $this->_putshaders();
9435 $this->_putxobjects();
9436 $this->_putresourcedict();
9437 $this->_putdests();
9438 $this->_putEmbeddedFiles();
9439 $this->_putannotsobjs();
9440 $this->_putjavascript();
9441 $this->_putbookmarks();
9442 $this->_putencryption();
9443 }
9444
9451 protected function _putinfo() {
9452 $oid = $this->_newobj();
9453 $out = '<<';
9454 // store current isunicode value
9455 $prev_isunicode = $this->isunicode;
9456 if ($this->docinfounicode) {
9457 $this->isunicode = true;
9458 }
9459 if (!TCPDF_STATIC::empty_string($this->title)) {
9460 // The document's title.
9461 $out .= ' /Title '.$this->_textstring($this->title, $oid);
9462 }
9463 if (!TCPDF_STATIC::empty_string($this->author)) {
9464 // The name of the person who created the document.
9465 $out .= ' /Author '.$this->_textstring($this->author, $oid);
9466 }
9467 if (!TCPDF_STATIC::empty_string($this->subject)) {
9468 // The subject of the document.
9469 $out .= ' /Subject '.$this->_textstring($this->subject, $oid);
9470 }
9471 if (!TCPDF_STATIC::empty_string($this->keywords)) {
9472 // Keywords associated with the document.
9473 $out .= ' /Keywords '.$this->_textstring($this->keywords, $oid);
9474 }
9475 if (!TCPDF_STATIC::empty_string($this->creator)) {
9476 // If the document was converted to PDF from another format, the name of the conforming product that created the original document from which it was converted.
9477 $out .= ' /Creator '.$this->_textstring($this->creator, $oid);
9478 }
9479 // restore previous isunicode value
9480 $this->isunicode = $prev_isunicode;
9481 // default producer
9482 $out .= ' /Producer '.$this->_textstring(TCPDF_STATIC::getTCPDFProducer(), $oid);
9483 // The date and time the document was created, in human-readable form
9484 $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp);
9485 // The date and time the document was most recently modified, in human-readable form
9486 $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp);
9487 // A name object indicating whether the document has been modified to include trapping information
9488 $out .= ' /Trapped /False';
9489 $out .= ' >>';
9490 $out .= "\n".'endobj';
9491 $this->_out($out);
9492 return $oid;
9493 }
9494
9502 public function setExtraXMP($xmp) {
9503 $this->custom_xmp = $xmp;
9504 }
9505
9512 protected function _putXMP() {
9513 $oid = $this->_newobj();
9514 // store current isunicode value
9515 $prev_isunicode = $this->isunicode;
9516 $this->isunicode = true;
9517 $prev_encrypted = $this->encrypted;
9518 $this->encrypted = false;
9519 // set XMP data
9520 $xmp = '<?xpacket begin="'.TCPDF_FONTS::unichr(0xfeff, $this->isunicode).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
9521 $xmp .= '<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.1-c043 52.372728, 2009/01/18-15:08:04">'."\n";
9522 $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
9523 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
9524 $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
9525 $xmp .= "\t\t\t".'<dc:title>'."\n";
9526 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9527 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->title).'</rdf:li>'."\n";
9528 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9529 $xmp .= "\t\t\t".'</dc:title>'."\n";
9530 $xmp .= "\t\t\t".'<dc:creator>'."\n";
9531 $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
9532 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->author).'</rdf:li>'."\n";
9533 $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
9534 $xmp .= "\t\t\t".'</dc:creator>'."\n";
9535 $xmp .= "\t\t\t".'<dc:description>'."\n";
9536 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9537 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->subject).'</rdf:li>'."\n";
9538 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9539 $xmp .= "\t\t\t".'</dc:description>'."\n";
9540 $xmp .= "\t\t\t".'<dc:subject>'."\n";
9541 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9542 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->keywords).'</rdf:li>'."\n";
9543 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9544 $xmp .= "\t\t\t".'</dc:subject>'."\n";
9545 $xmp .= "\t\t".'</rdf:Description>'."\n";
9546 // convert doc creation date format
9547 $dcdate = TCPDF_STATIC::getFormattedDate($this->doc_creation_timestamp);
9548 $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2);
9549 $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2);
9550 $doccreationdate .= substr($dcdate, 14, 3).':'.substr($dcdate, 18, 2);
9551 $doccreationdate = TCPDF_STATIC::_escapeXML($doccreationdate);
9552 // convert doc modification date format
9553 $dmdate = TCPDF_STATIC::getFormattedDate($this->doc_modification_timestamp);
9554 $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2);
9555 $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2);
9556 $docmoddate .= substr($dmdate, 14, 3).':'.substr($dmdate, 18, 2);
9557 $docmoddate = TCPDF_STATIC::_escapeXML($docmoddate);
9558 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
9559 $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n";
9560 $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n";
9561 $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n";
9562 $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n";
9563 $xmp .= "\t\t".'</rdf:Description>'."\n";
9564 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
9565 $xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC::_escapeXML($this->keywords).'</pdf:Keywords>'."\n";
9566 $xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC::_escapeXML(TCPDF_STATIC::getTCPDFProducer()).'</pdf:Producer>'."\n";
9567 $xmp .= "\t\t".'</rdf:Description>'."\n";
9568 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
9569 $uuid = 'uuid:'.substr($this->file_id, 0, 8).'-'.substr($this->file_id, 8, 4).'-'.substr($this->file_id, 12, 4).'-'.substr($this->file_id, 16, 4).'-'.substr($this->file_id, 20, 12);
9570 $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
9571 $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
9572 $xmp .= "\t\t".'</rdf:Description>'."\n";
9573 if ($this->pdfa_mode) {
9574 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
9575 $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n";
9576 $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
9577 $xmp .= "\t\t".'</rdf:Description>'."\n";
9578 }
9579 // XMP extension schemas
9580 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/" xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#" xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">'."\n";
9581 $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
9582 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9583 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9584 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
9585 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
9586 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
9587 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9588 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9589 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
9590 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
9591 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
9592 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9593 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9594 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9595 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9596 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
9597 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9598 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9599 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9600 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9601 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9602 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9603 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9604 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
9605 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
9606 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
9607 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9608 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9609 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9610 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9611 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
9612 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
9613 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
9614 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9615 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9616 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9617 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
9618 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
9619 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9620 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9621 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9622 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9623 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
9624 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
9625 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9626 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9627 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9628 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9629 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9630 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9631 $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
9632 $xmp .= "\t\t".'</rdf:Description>'."\n";
9633 $xmp .= "\t".'</rdf:RDF>'."\n";
9634 $xmp .= $this->custom_xmp;
9635 $xmp .= '</x:xmpmeta>'."\n";
9636 $xmp .= '<?xpacket end="w"?>';
9637 $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
9638 // restore previous isunicode value
9639 $this->isunicode = $prev_isunicode;
9640 $this->encrypted = $prev_encrypted;
9641 $this->_out($out);
9642 return $oid;
9643 }
9644
9650 protected function _putcatalog() {
9651 // put XMP
9652 $xmpobj = $this->_putXMP();
9653 // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile
9654 if ($this->pdfa_mode OR $this->force_srgb) {
9655 $iccobj = $this->_newobj();
9656 $icc = file_get_contents(dirname(__FILE__).'/include/sRGB.icc');
9657 $filter = '';
9658 if ($this->compress) {
9659 $filter = ' /Filter /FlateDecode';
9660 $icc = gzcompress($icc);
9661 }
9662 $icc = $this->_getrawstream($icc);
9663 $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9664 }
9665 // start catalog
9666 $oid = $this->_newobj();
9667 $out = '<< /Type /Catalog';
9668 $out .= ' /Version /'.$this->PDFVersion;
9669 //$out .= ' /Extensions <<>>';
9670 $out .= ' /Pages 1 0 R';
9671 //$out .= ' /PageLabels ' //...;
9672 $out .= ' /Names <<';
9673 if ((!$this->pdfa_mode) AND !empty($this->n_js)) {
9674 $out .= ' /JavaScript '.$this->n_js;
9675 }
9676 if (!empty($this->efnames)) {
9677 $out .= ' /EmbeddedFiles <</Names [';
9678 foreach ($this->efnames AS $fn => $fref) {
9679 $out .= ' '.$this->_datastring($fn).' '.$fref;
9680 }
9681 $out .= ' ]>>';
9682 }
9683 $out .= ' >>';
9684 if (!empty($this->dests)) {
9685 $out .= ' /Dests '.($this->n_dests).' 0 R';
9686 }
9687 $out .= $this->_putviewerpreferences();
9688 if (isset($this->LayoutMode) AND (!TCPDF_STATIC::empty_string($this->LayoutMode))) {
9689 $out .= ' /PageLayout /'.$this->LayoutMode;
9690 }
9691 if (isset($this->PageMode) AND (!TCPDF_STATIC::empty_string($this->PageMode))) {
9692 $out .= ' /PageMode /'.$this->PageMode;
9693 }
9694 if (count($this->outlines) > 0) {
9695 $out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
9696 $out .= ' /PageMode /UseOutlines';
9697 }
9698 //$out .= ' /Threads []';
9699 if ($this->ZoomMode == 'fullpage') {
9700 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
9701 } elseif ($this->ZoomMode == 'fullwidth') {
9702 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
9703 } elseif ($this->ZoomMode == 'real') {
9704 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
9705 } elseif (!is_string($this->ZoomMode)) {
9706 $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100));
9707 }
9708 //$out .= ' /AA <<>>';
9709 //$out .= ' /URI <<>>';
9710 $out .= ' /Metadata '.$xmpobj.' 0 R';
9711 //$out .= ' /StructTreeRoot <<>>';
9712 //$out .= ' /MarkInfo <<>>';
9713 if (isset($this->l['a_meta_language'])) {
9714 $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
9715 }
9716 //$out .= ' /SpiderInfo <<>>';
9717 // set OutputIntent to sRGB IEC61966-2.1 if required
9718 if ($this->pdfa_mode OR $this->force_srgb) {
9719 $out .= ' /OutputIntents [<<';
9720 $out .= ' /Type /OutputIntent';
9721 $out .= ' /S /GTS_PDFA1';
9722 $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9723 $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9724 $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
9725 $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9726 $out .= ' /DestOutputProfile '.$iccobj.' 0 R';
9727 $out .= ' >>]';
9728 }
9729 //$out .= ' /PieceInfo <<>>';
9730 if (!empty($this->pdflayers)) {
9731 $lyrobjs = '';
9732 $lyrobjs_off = '';
9733 $lyrobjs_lock = '';
9734 foreach ($this->pdflayers as $layer) {
9735 $layer_obj_ref = ' '.$layer['objid'].' 0 R';
9736 $lyrobjs .= $layer_obj_ref;
9737 if ($layer['view'] === false) {
9738 $lyrobjs_off .= $layer_obj_ref;
9739 }
9740 if ($layer['lock']) {
9741 $lyrobjs_lock .= $layer_obj_ref;
9742 }
9743 }
9744 $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
9745 $out .= ' /D <<';
9746 $out .= ' /Name '.$this->_textstring('Layers', $oid);
9747 $out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
9748 $out .= ' /BaseState /ON';
9749 $out .= ' /OFF ['.$lyrobjs_off.']';
9750 $out .= ' /Locked ['.$lyrobjs_lock.']';
9751 $out .= ' /Intent /View';
9752 $out .= ' /AS [';
9753 $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
9754 $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
9755 $out .= ' ]';
9756 $out .= ' /Order ['.$lyrobjs.']';
9757 $out .= ' /ListMode /AllPages';
9758 //$out .= ' /RBGroups ['..']';
9759 //$out .= ' /Locked ['..']';
9760 $out .= ' >>';
9761 $out .= ' >>';
9762 }
9763 // AcroForm
9764 if (!empty($this->form_obj_id)
9765 OR ($this->sign AND isset($this->signature_data['cert_type']))
9766 OR !empty($this->empty_signature_appearance)) {
9767 $out .= ' /AcroForm <<';
9768 $objrefs = '';
9769 if ($this->sign AND isset($this->signature_data['cert_type'])) {
9770 // set reference for signature object
9771 $objrefs .= $this->sig_obj_id.' 0 R';
9772 }
9773 if (!empty($this->empty_signature_appearance)) {
9774 foreach ($this->empty_signature_appearance as $esa) {
9775 // set reference for empty signature objects
9776 $objrefs .= ' '.$esa['objid'].' 0 R';
9777 }
9778 }
9779 if (!empty($this->form_obj_id)) {
9780 foreach($this->form_obj_id as $objid) {
9781 $objrefs .= ' '.$objid.' 0 R';
9782 }
9783 }
9784 $out .= ' /Fields ['.$objrefs.']';
9785 // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
9786 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
9787 $out .= ' /NeedAppearances false';
9788 }
9789 if ($this->sign AND isset($this->signature_data['cert_type'])) {
9790 if ($this->signature_data['cert_type'] > 0) {
9791 $out .= ' /SigFlags 3';
9792 } else {
9793 $out .= ' /SigFlags 1';
9794 }
9795 }
9796 //$out .= ' /CO ';
9797 if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
9798 $out .= ' /DR <<';
9799 $out .= ' /Font <<';
9800 foreach ($this->annotation_fonts as $fontkey => $fontid) {
9801 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9802 }
9803 $out .= ' >> >>';
9804 }
9805 $font = $this->getFontBuffer('helvetica');
9806 $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
9807 $out .= ' /Q '.(($this->rtl)?'2':'0');
9808 //$out .= ' /XFA ';
9809 $out .= ' >>';
9810 // signatures
9811 if ($this->sign AND isset($this->signature_data['cert_type'])
9812 AND (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A'))) {
9813 if ($this->signature_data['cert_type'] > 0) {
9814 $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
9815 } else {
9816 $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
9817 }
9818 }
9819 }
9820 //$out .= ' /Legal <<>>';
9821 //$out .= ' /Requirements []';
9822 //$out .= ' /Collection <<>>';
9823 //$out .= ' /NeedsRendering true';
9824 $out .= ' >>';
9825 $out .= "\n".'endobj';
9826 $this->_out($out);
9827 return $oid;
9828 }
9829
9837 protected function _putviewerpreferences() {
9839 $out = ' /ViewerPreferences <<';
9840 if ($this->rtl) {
9841 $out .= ' /Direction /R2L';
9842 } else {
9843 $out .= ' /Direction /L2R';
9844 }
9845 if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) {
9846 $out .= ' /HideToolbar true';
9847 }
9848 if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) {
9849 $out .= ' /HideMenubar true';
9850 }
9851 if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) {
9852 $out .= ' /HideWindowUI true';
9853 }
9854 if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) {
9855 $out .= ' /FitWindow true';
9856 }
9857 if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) {
9858 $out .= ' /CenterWindow true';
9859 }
9860 if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) {
9861 $out .= ' /DisplayDocTitle true';
9862 }
9863 if (isset($vp['NonFullScreenPageMode'])) {
9864 $out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode'];
9865 }
9866 if (isset($vp['ViewArea'])) {
9867 $out .= ' /ViewArea /'.$vp['ViewArea'];
9868 }
9869 if (isset($vp['ViewClip'])) {
9870 $out .= ' /ViewClip /'.$vp['ViewClip'];
9871 }
9872 if (isset($vp['PrintArea'])) {
9873 $out .= ' /PrintArea /'.$vp['PrintArea'];
9874 }
9875 if (isset($vp['PrintClip'])) {
9876 $out .= ' /PrintClip /'.$vp['PrintClip'];
9877 }
9878 if (isset($vp['PrintScaling'])) {
9879 $out .= ' /PrintScaling /'.$vp['PrintScaling'];
9880 }
9881 if (isset($vp['Duplex']) AND (!TCPDF_STATIC::empty_string($vp['Duplex']))) {
9882 $out .= ' /Duplex /'.$vp['Duplex'];
9883 }
9884 if (isset($vp['PickTrayByPDFSize'])) {
9885 if ($vp['PickTrayByPDFSize']) {
9886 $out .= ' /PickTrayByPDFSize true';
9887 } else {
9888 $out .= ' /PickTrayByPDFSize false';
9889 }
9890 }
9891 if (isset($vp['PrintPageRange'])) {
9892 $PrintPageRangeNum = '';
9893 foreach ($vp['PrintPageRange'] as $k => $v) {
9894 $PrintPageRangeNum .= ' '.($v - 1).'';
9895 }
9896 $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
9897 }
9898 if (isset($vp['NumCopies'])) {
9899 $out .= ' /NumCopies '.intval($vp['NumCopies']);
9900 }
9901 $out .= ' >>';
9902 return $out;
9903 }
9904
9909 protected function _putheader() {
9910 $this->_out('%PDF-'.$this->PDFVersion);
9911 $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
9912 }
9913
9918 protected function _enddoc() {
9919 if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
9920 // save subset chars of the previous font
9921 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
9922 }
9923 $this->state = 1;
9924 $this->_putheader();
9925 $this->_putpages();
9926 $this->_putresources();
9927 // empty signature fields
9928 if (!empty($this->empty_signature_appearance)) {
9929 foreach ($this->empty_signature_appearance as $key => $esa) {
9930 // widget annotation for empty signature
9931 $out = $this->_getobj($esa['objid'])."\n";
9932 $out .= '<< /Type /Annot';
9933 $out .= ' /Subtype /Widget';
9934 $out .= ' /Rect ['.$esa['rect'].']';
9935 $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page
9936 $out .= ' /F 4';
9937 $out .= ' /FT /Sig';
9938 $signame = $esa['name'].sprintf(' [%03d]', ($key + 1));
9939 $out .= ' /T '.$this->_textstring($signame, $esa['objid']);
9940 $out .= ' /Ff 0';
9941 $out .= ' >>';
9942 $out .= "\n".'endobj';
9943 $this->_out($out);
9944 }
9945 }
9946 // Signature
9947 if ($this->sign AND isset($this->signature_data['cert_type'])) {
9948 // widget annotation for signature
9949 $out = $this->_getobj($this->sig_obj_id)."\n";
9950 $out .= '<< /Type /Annot';
9951 $out .= ' /Subtype /Widget';
9952 $out .= ' /Rect ['.$this->signature_appearance['rect'].']';
9953 $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
9954 $out .= ' /F 4';
9955 $out .= ' /FT /Sig';
9956 $out .= ' /T '.$this->_textstring($this->signature_appearance['name'], $this->sig_obj_id);
9957 $out .= ' /Ff 0';
9958 $out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
9959 $out .= ' >>';
9960 $out .= "\n".'endobj';
9961 $this->_out($out);
9962 // signature
9963 $this->_putsignature();
9964 }
9965 // Info
9966 $objid_info = $this->_putinfo();
9967 // Catalog
9968 $objid_catalog = $this->_putcatalog();
9969 // Cross-ref
9970 $o = $this->bufferlen;
9971 // XREF section
9972 $this->_out('xref');
9973 $this->_out('0 '.($this->n + 1));
9974 $this->_out('0000000000 65535 f ');
9975 $freegen = ($this->n + 2);
9976 for ($i=1; $i <= $this->n; ++$i) {
9977 if (!isset($this->offsets[$i]) AND ($i > 1)) {
9978 $this->_out(sprintf('0000000000 %05d f ', $freegen));
9979 ++$freegen;
9980 } else {
9981 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
9982 }
9983 }
9984 // TRAILER
9985 $out = 'trailer'."\n";
9986 $out .= '<<';
9987 $out .= ' /Size '.($this->n + 1);
9988 $out .= ' /Root '.$objid_catalog.' 0 R';
9989 $out .= ' /Info '.$objid_info.' 0 R';
9990 if ($this->encrypted) {
9991 $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
9992 }
9993 $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
9994 $out .= ' >>';
9995 $this->_out($out);
9996 $this->_out('startxref');
9997 $this->_out($o);
9998 $this->_out('%%EOF');
9999 $this->state = 3; // end-of-doc
10000 }
10001
10009 protected function _beginpage($orientation='', $format='') {
10010 ++$this->page;
10011 $this->pageobjects[$this->page] = array();
10012 $this->setPageBuffer($this->page, '');
10013 // initialize array for graphics tranformation positions inside a page buffer
10014 $this->transfmrk[$this->page] = array();
10015 $this->state = 2;
10016 if (TCPDF_STATIC::empty_string($orientation)) {
10017 if (isset($this->CurOrientation)) {
10018 $orientation = $this->CurOrientation;
10019 } elseif ($this->fwPt > $this->fhPt) {
10020 // landscape
10021 $orientation = 'L';
10022 } else {
10023 // portrait
10024 $orientation = 'P';
10025 }
10026 }
10028 $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
10029 $this->setPageOrientation($orientation);
10030 } else {
10031 $this->setPageFormat($format, $orientation);
10032 }
10033 if ($this->rtl) {
10034 $this->x = $this->w - $this->rMargin;
10035 } else {
10036 $this->x = $this->lMargin;
10037 }
10038 $this->y = $this->tMargin;
10039 if (isset($this->newpagegroup[$this->page])) {
10040 // start a new group
10041 $this->currpagegroup = $this->newpagegroup[$this->page];
10042 $this->pagegroups[$this->currpagegroup] = 1;
10043 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
10044 ++$this->pagegroups[$this->currpagegroup];
10045 }
10046 }
10047
10052 protected function _endpage() {
10053 $this->setVisibility('all');
10054 $this->state = 1;
10055 }
10056
10062 protected function _newobj() {
10063 $this->_out($this->_getobj());
10064 return $this->n;
10065 }
10066
10074 protected function _getobj($objid='') {
10075 if ($objid === '') {
10076 ++$this->n;
10077 $objid = $this->n;
10078 }
10079 $this->offsets[$objid] = $this->bufferlen;
10080 $this->pageobjects[$this->page][] = $objid;
10081 return $objid.' 0 obj';
10082 }
10083
10091 protected function _dounderline($x, $y, $txt) {
10092 $w = $this->GetStringWidth($txt);
10093 return $this->_dounderlinew($x, $y, $w);
10094 }
10095
10104 protected function _dounderlinew($x, $y, $w) {
10105 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10106 return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
10107 }
10108
10116 protected function _dolinethrough($x, $y, $txt) {
10117 $w = $this->GetStringWidth($txt);
10118 return $this->_dolinethroughw($x, $y, $w);
10119 }
10120
10129 protected function _dolinethroughw($x, $y, $w) {
10130 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10131 return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew);
10132 }
10133
10142 protected function _dooverline($x, $y, $txt) {
10143 $w = $this->GetStringWidth($txt);
10144 return $this->_dooverlinew($x, $y, $w);
10145 }
10146
10155 protected function _dooverlinew($x, $y, $w) {
10156 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10157 return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
10158
10159 }
10160
10168 protected function _datastring($s, $n=0) {
10169 if ($n == 0) {
10170 $n = $this->n;
10171 }
10172 $s = $this->_encrypt_data($n, $s);
10173 return '('. TCPDF_STATIC::_escape($s).')';
10174 }
10175
10183 if (is_string($time)) {
10185 }
10186 $this->doc_creation_timestamp = intval($time);
10187 }
10188
10196 if (is_string($time)) {
10198 }
10199 $this->doc_modification_timestamp = intval($time);
10200 }
10201
10208 public function getDocCreationTimestamp() {
10210 }
10211
10220 }
10221
10230 protected function _datestring($n=0, $timestamp=0) {
10231 if ((empty($timestamp)) OR ($timestamp < 0)) {
10233 }
10235 }
10236
10244 protected function _textstring($s, $n=0) {
10245 if ($this->isunicode) {
10246 //Convert string to UTF-16BE
10247 $s = TCPDF_FONTS::UTF8ToUTF16BE($s, true, $this->isunicode, $this->CurrentFont);
10248 }
10249 return $this->_datastring($s, $n);
10250 }
10251
10260 protected function _getrawstream($s, $n=0) {
10261 if ($n <= 0) {
10262 // default to current object
10263 $n = $this->n;
10264 }
10265 return $this->_encrypt_data($n, $s);
10266 }
10267
10273 protected function _out($s) {
10274 if ($this->state == 2) {
10275 if ($this->inxobj) {
10276 // we are inside an XObject template
10277 $this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
10278 } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
10279 // puts data before page footer
10280 $pagebuff = $this->getPageBuffer($this->page);
10281 $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
10282 $footer = substr($pagebuff, -$this->footerlen[$this->page]);
10283 $this->setPageBuffer($this->page, $page.$s."\n".$footer);
10284 // update footer position
10285 $this->footerpos[$this->page] += strlen($s."\n");
10286 } else {
10287 // set page data
10288 $this->setPageBuffer($this->page, $s."\n", true);
10289 }
10290 } elseif ($this->state > 0) {
10291 // set general data
10292 $this->setBuffer($s."\n");
10293 }
10294 }
10295
10302 public function setHeaderFont($font) {
10303 $this->header_font = $font;
10304 }
10305
10312 public function getHeaderFont() {
10313 return $this->header_font;
10314 }
10315
10322 public function setFooterFont($font) {
10323 $this->footer_font = $font;
10324 }
10325
10332 public function getFooterFont() {
10333 return $this->footer_font;
10334 }
10335
10342 public function setLanguageArray($language) {
10343 $this->l = $language;
10344 if (isset($this->l['a_meta_dir'])) {
10345 $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
10346 } else {
10347 $this->rtl = false;
10348 }
10349 }
10350
10355 public function getPDFData() {
10356 if ($this->state < 3) {
10357 $this->Close();
10358 }
10359 return $this->buffer;
10360 }
10361
10374 public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
10375 if (isset($url[1]) AND ($url[0] == '#') AND is_numeric($url[1])) {
10376 // convert url to internal link
10377 $lnkdata = explode(',', $url);
10378 if (isset($lnkdata[0]) ) {
10379 $page = substr($lnkdata[0], 1);
10380 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
10381 $lnky = floatval($lnkdata[1]);
10382 } else {
10383 $lnky = 0;
10384 }
10385 $url = $this->AddLink();
10386 $this->SetLink($url, $lnky, $page);
10387 }
10388 }
10389 // store current settings
10390 $prevcolor = $this->fgcolor;
10391 $prevstyle = $this->FontStyle;
10392 if (empty($color)) {
10393 $this->SetTextColorArray($this->htmlLinkColorArray);
10394 } else {
10395 $this->SetTextColorArray($color);
10396 }
10397 if ($style == -1) {
10398 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
10399 } else {
10400 $this->SetFont('', $this->FontStyle.$style);
10401 }
10402 $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
10403 // restore settings
10404 $this->SetFont('', $prevstyle);
10405 $this->SetTextColorArray($prevcolor);
10406 return $ret;
10407 }
10408
10416 public function pixelsToUnits($px) {
10417 return ($px / ($this->imgscale * $this->k));
10418 }
10419
10427 public function unhtmlentities($text_to_convert) {
10428 return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
10429 }
10430
10431 // ENCRYPTION METHODS ----------------------------------
10432
10442 protected function _objectkey($n) {
10443 $objkey = $this->encryptdata['key'].pack('VXxx', $n);
10444 if ($this->encryptdata['mode'] == 2) { // AES-128
10445 // AES padding
10446 $objkey .= "\x73\x41\x6C\x54"; // sAlT
10447 }
10448 $objkey = substr(TCPDF_STATIC::_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
10449 $objkey = substr($objkey, 0, 16);
10450 return $objkey;
10451 }
10452
10462 protected function _encrypt_data($n, $s) {
10463 if (!$this->encrypted) {
10464 return $s;
10465 }
10466 switch ($this->encryptdata['mode']) {
10467 case 0: // RC4-40
10468 case 1: { // RC4-128
10469 $s = TCPDF_STATIC::_RC4($this->_objectkey($n), $s, $this->last_enc_key, $this->last_enc_key_c);
10470 break;
10471 }
10472 case 2: { // AES-128
10473 $s = TCPDF_STATIC::_AES($this->_objectkey($n), $s);
10474 break;
10475 }
10476 case 3: { // AES-256
10477 $s = TCPDF_STATIC::_AES($this->encryptdata['key'], $s);
10478 break;
10479 }
10480 }
10481 return $s;
10482 }
10483
10490 protected function _putencryption() {
10491 if (!$this->encrypted) {
10492 return;
10493 }
10494 $this->encryptdata['objid'] = $this->_newobj();
10495 $out = '<<';
10496 if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
10497 $this->encryptdata['Filter'] = 'Standard';
10498 }
10499 $out .= ' /Filter /'.$this->encryptdata['Filter'];
10500 if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
10501 $out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
10502 }
10503 if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
10504 $this->encryptdata['V'] = 1;
10505 }
10506 // V is a code specifying the algorithm to be used in encrypting and decrypting the document
10507 $out .= ' /V '.$this->encryptdata['V'];
10508 if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
10509 // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
10510 $out .= ' /Length '.$this->encryptdata['Length'];
10511 } else {
10512 $out .= ' /Length 40';
10513 }
10514 if ($this->encryptdata['V'] >= 4) {
10515 if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
10516 $this->encryptdata['StmF'] = 'Identity';
10517 }
10518 if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
10519 // The name of the crypt filter that shall be used when decrypting all strings in the document.
10520 $this->encryptdata['StrF'] = 'Identity';
10521 }
10522 // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
10523 if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
10524 $out .= ' /CF <<';
10525 $out .= ' /'.$this->encryptdata['StmF'].' <<';
10526 $out .= ' /Type /CryptFilter';
10527 if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
10528 // The method used
10529 $out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
10530 if ($this->encryptdata['pubkey']) {
10531 $out .= ' /Recipients [';
10532 foreach ($this->encryptdata['Recipients'] as $rec) {
10533 $out .= ' <'.$rec.'>';
10534 }
10535 $out .= ' ]';
10536 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
10537 $out .= ' /EncryptMetadata false';
10538 } else {
10539 $out .= ' /EncryptMetadata true';
10540 }
10541 }
10542 } else {
10543 $out .= ' /CFM /None';
10544 }
10545 if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
10546 // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
10547 $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
10548 } else {
10549 $out .= ' /AuthEvent /DocOpen';
10550 }
10551 if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
10552 // The bit length of the encryption key.
10553 $out .= ' /Length '.$this->encryptdata['CF']['Length'];
10554 }
10555 $out .= ' >> >>';
10556 }
10557 // The name of the crypt filter that shall be used by default when decrypting streams.
10558 $out .= ' /StmF /'.$this->encryptdata['StmF'];
10559 // The name of the crypt filter that shall be used when decrypting all strings in the document.
10560 $out .= ' /StrF /'.$this->encryptdata['StrF'];
10561 if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
10562 // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
10563 $out .= ' /EFF /'.$this->encryptdata[''];
10564 }
10565 }
10566 // Additional encryption dictionary entries for the standard security handler
10567 if ($this->encryptdata['pubkey']) {
10568 if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
10569 $out .= ' /Recipients [';
10570 foreach ($this->encryptdata['Recipients'] as $rec) {
10571 $out .= ' <'.$rec.'>';
10572 }
10573 $out .= ' ]';
10574 }
10575 } else {
10576 $out .= ' /R';
10577 if ($this->encryptdata['V'] == 5) { // AES-256
10578 $out .= ' 5';
10579 $out .= ' /OE ('.TCPDF_STATIC::_escape($this->encryptdata['OE']).')';
10580 $out .= ' /UE ('.TCPDF_STATIC::_escape($this->encryptdata['UE']).')';
10581 $out .= ' /Perms ('.TCPDF_STATIC::_escape($this->encryptdata['perms']).')';
10582 } elseif ($this->encryptdata['V'] == 4) { // AES-128
10583 $out .= ' 4';
10584 } elseif ($this->encryptdata['V'] < 2) { // RC-40
10585 $out .= ' 2';
10586 } else { // RC-128
10587 $out .= ' 3';
10588 }
10589 $out .= ' /O ('.TCPDF_STATIC::_escape($this->encryptdata['O']).')';
10590 $out .= ' /U ('.TCPDF_STATIC::_escape($this->encryptdata['U']).')';
10591 $out .= ' /P '.$this->encryptdata['P'];
10592 if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
10593 $out .= ' /EncryptMetadata false';
10594 } else {
10595 $out .= ' /EncryptMetadata true';
10596 }
10597 }
10598 $out .= ' >>';
10599 $out .= "\n".'endobj';
10600 $this->_out($out);
10601 }
10602
10610 protected function _Uvalue() {
10611 if ($this->encryptdata['mode'] == 0) { // RC4-40
10612 return TCPDF_STATIC::_RC4($this->encryptdata['key'], TCPDF_STATIC::$enc_padding, $this->last_enc_key, $this->last_enc_key_c);
10613 } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
10614 $tmp = TCPDF_STATIC::_md5_16(TCPDF_STATIC::$enc_padding.$this->encryptdata['fileid']);
10615 $enc = TCPDF_STATIC::_RC4($this->encryptdata['key'], $tmp, $this->last_enc_key, $this->last_enc_key_c);
10616 $len = strlen($tmp);
10617 for ($i = 1; $i <= 19; ++$i) {
10618 $ek = '';
10619 for ($j = 0; $j < $len; ++$j) {
10620 $ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i);
10621 }
10622 $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10623 }
10624 $enc .= str_repeat("\x00", 16);
10625 return substr($enc, 0, 32);
10626 } elseif ($this->encryptdata['mode'] == 3) { // AES-256
10628 // User Validation Salt
10629 $this->encryptdata['UVS'] = substr($seed, 0, 8);
10630 // User Key Salt
10631 $this->encryptdata['UKS'] = substr($seed, 8, 16);
10632 return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
10633 }
10634 }
10635
10643 protected function _UEvalue() {
10644 $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
10645 return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']);
10646 }
10647
10655 protected function _Ovalue() {
10656 if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
10657 $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['owner_password']);
10658 if ($this->encryptdata['mode'] > 0) {
10659 for ($i = 0; $i < 50; ++$i) {
10660 $tmp = TCPDF_STATIC::_md5_16($tmp);
10661 }
10662 }
10663 $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
10664 $enc = TCPDF_STATIC::_RC4($owner_key, $this->encryptdata['user_password'], $this->last_enc_key, $this->last_enc_key_c);
10665 if ($this->encryptdata['mode'] > 0) {
10666 $len = strlen($owner_key);
10667 for ($i = 1; $i <= 19; ++$i) {
10668 $ek = '';
10669 for ($j = 0; $j < $len; ++$j) {
10670 $ek .= chr(ord($owner_key[$j]) ^ $i);
10671 }
10672 $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10673 }
10674 }
10675 return $enc;
10676 } elseif ($this->encryptdata['mode'] == 3) { // AES-256
10678 // Owner Validation Salt
10679 $this->encryptdata['OVS'] = substr($seed, 0, 8);
10680 // Owner Key Salt
10681 $this->encryptdata['OKS'] = substr($seed, 8, 16);
10682 return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
10683 }
10684 }
10685
10693 protected function _OEvalue() {
10694 $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
10695 return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']);
10696 }
10697
10706 protected function _fixAES256Password($password) {
10707 $psw = ''; // password to be returned
10708 $psw_array = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($password, $this->isunicode, $this->CurrentFont), $password, $this->rtl, $this->isunicode, $this->CurrentFont);
10709 foreach ($psw_array as $c) {
10710 $psw .= TCPDF_FONTS::unichr($c, $this->isunicode);
10711 }
10712 return substr($psw, 0, 127);
10713 }
10714
10721 protected function _generateencryptionkey() {
10722 $keybytelen = ($this->encryptdata['Length'] / 8);
10723 if (!$this->encryptdata['pubkey']) { // standard mode
10724 if ($this->encryptdata['mode'] == 3) { // AES-256
10725 // generate 256 bit random key
10726 $this->encryptdata['key'] = substr(hash('sha256', TCPDF_STATIC::getRandomSeed(), true), 0, $keybytelen);
10727 // truncate passwords
10728 $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
10729 $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
10730 // Compute U value
10731 $this->encryptdata['U'] = $this->_Uvalue();
10732 // Compute UE value
10733 $this->encryptdata['UE'] = $this->_UEvalue();
10734 // Compute O value
10735 $this->encryptdata['O'] = $this->_Ovalue();
10736 // Compute OE value
10737 $this->encryptdata['OE'] = $this->_OEvalue();
10738 // Compute P value
10739 $this->encryptdata['P'] = $this->encryptdata['protection'];
10740 // Computing the encryption dictionary's Perms (permissions) value
10741 $perms = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
10742 $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
10743 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
10744 $perms .= 'F';
10745 } else {
10746 $perms .= 'T';
10747 }
10748 $perms .= 'adb'; // bytes 9-11
10749 $perms .= 'nick'; // bytes 12-15
10750 $this->encryptdata['perms'] = TCPDF_STATIC::_AESnopad($this->encryptdata['key'], $perms);
10751 } else { // RC4-40, RC4-128, AES-128
10752 // Pad passwords
10753 $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10754 $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10755 // Compute O value
10756 $this->encryptdata['O'] = $this->_Ovalue();
10757 // get default permissions (reverse byte order)
10758 $permissions = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']);
10759 // Compute encryption key
10760 $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
10761 if ($this->encryptdata['mode'] > 0) {
10762 for ($i = 0; $i < 50; ++$i) {
10763 $tmp = TCPDF_STATIC::_md5_16(substr($tmp, 0, $keybytelen));
10764 }
10765 }
10766 $this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
10767 // Compute U value
10768 $this->encryptdata['U'] = $this->_Uvalue();
10769 // Compute P value
10770 $this->encryptdata['P'] = $this->encryptdata['protection'];
10771 }
10772 } else { // Public-Key mode
10773 // random 20-byte seed
10774 $seed = sha1(TCPDF_STATIC::getRandomSeed(), true);
10775 $recipient_bytes = '';
10776 foreach ($this->encryptdata['pubkeys'] as $pubkey) {
10777 // for each public certificate
10778 if (isset($pubkey['p'])) {
10779 $pkprotection = TCPDF_STATIC::getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
10780 } else {
10781 $pkprotection = $this->encryptdata['protection'];
10782 }
10783 // get default permissions (reverse byte order)
10784 $pkpermissions = TCPDF_STATIC::getEncPermissionsString($pkprotection);
10785 // envelope data
10786 $envelope = $seed.$pkpermissions;
10787 // write the envelope data to a temporary file
10788 $tempkeyfile = TCPDF_STATIC::getObjFilename('key', $this->file_id);
10789 $f = TCPDF_STATIC::fopenLocal($tempkeyfile, 'wb');
10790 if (!$f) {
10791 $this->Error('Unable to create temporary key file: '.$tempkeyfile);
10792 }
10793 $envelope_length = strlen($envelope);
10794 fwrite($f, $envelope, $envelope_length);
10795 fclose($f);
10796 $tempencfile = TCPDF_STATIC::getObjFilename('enc', $this->file_id);
10797 if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) {
10798 $this->Error('Unable to encrypt the file: '.$tempkeyfile);
10799 }
10800 // read encryption signature
10801 $signature = file_get_contents($tempencfile, false, null, $envelope_length);
10802 // extract signature
10803 $signature = substr($signature, strpos($signature, 'Content-Disposition'));
10804 $tmparr = explode("\n\n", $signature);
10805 $signature = trim($tmparr[1]);
10806 unset($tmparr);
10807 // decode signature
10808 $signature = base64_decode($signature);
10809 // convert signature to hex
10810 $hexsignature = current(unpack('H*', $signature));
10811 // store signature on recipients array
10812 $this->encryptdata['Recipients'][] = $hexsignature;
10813 // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
10814 $recipient_bytes .= $signature;
10815 }
10816 // calculate encryption key
10817 if ($this->encryptdata['mode'] == 3) { // AES-256
10818 $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
10819 } else { // RC4-40, RC4-128, AES-128
10820 $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
10821 }
10822 }
10823 }
10824
10839 public function SetProtection($permissions=array('print', 'modify', 'copy', 'annot-forms', 'fill-forms', 'extract', 'assemble', 'print-high'), $user_pass='', $owner_pass=null, $mode=0, $pubkeys=null) {
10840 if ($this->pdfa_mode) {
10841 // encryption is not allowed in PDF/A mode
10842 return;
10843 }
10844 $this->encryptdata['protection'] = TCPDF_STATIC::getUserPermissionCode($permissions, $mode);
10845 if (($pubkeys !== null) AND (is_array($pubkeys))) {
10846 // public-key mode
10847 $this->encryptdata['pubkeys'] = $pubkeys;
10848 if ($mode == 0) {
10849 // public-Key Security requires at least 128 bit
10850 $mode = 1;
10851 }
10852 if (!function_exists('openssl_pkcs7_encrypt')) {
10853 $this->Error('Public-Key Security requires openssl library.');
10854 }
10855 // Set Public-Key filter (available are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
10856 $this->encryptdata['pubkey'] = true;
10857 $this->encryptdata['Filter'] = 'Adobe.PubSec';
10858 $this->encryptdata['StmF'] = 'DefaultCryptFilter';
10859 $this->encryptdata['StrF'] = 'DefaultCryptFilter';
10860 } else {
10861 // standard mode (password mode)
10862 $this->encryptdata['pubkey'] = false;
10863 $this->encryptdata['Filter'] = 'Standard';
10864 $this->encryptdata['StmF'] = 'StdCF';
10865 $this->encryptdata['StrF'] = 'StdCF';
10866 }
10867 if ($mode > 1) { // AES
10868 if (!extension_loaded('openssl') && !extension_loaded('mcrypt')) {
10869 $this->Error('AES encryption requires openssl or mcrypt extension (http://www.php.net/manual/en/mcrypt.requirements.php).');
10870 }
10871 if (extension_loaded('openssl') && !in_array('aes-256-cbc', openssl_get_cipher_methods())) {
10872 $this->Error('AES encryption requires openssl/aes-256-cbc cypher.');
10873 }
10874 if (extension_loaded('mcrypt') && mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
10875 $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
10876 }
10877 if (($mode == 3) AND !function_exists('hash')) {
10878 // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
10879 $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
10880 }
10881 }
10882 if ($owner_pass === null) {
10883 $owner_pass = md5(TCPDF_STATIC::getRandomSeed());
10884 }
10885 $this->encryptdata['user_password'] = $user_pass;
10886 $this->encryptdata['owner_password'] = $owner_pass;
10887 $this->encryptdata['mode'] = $mode;
10888 switch ($mode) {
10889 case 0: { // RC4 40 bit
10890 $this->encryptdata['V'] = 1;
10891 $this->encryptdata['Length'] = 40;
10892 $this->encryptdata['CF']['CFM'] = 'V2';
10893 break;
10894 }
10895 case 1: { // RC4 128 bit
10896 $this->encryptdata['V'] = 2;
10897 $this->encryptdata['Length'] = 128;
10898 $this->encryptdata['CF']['CFM'] = 'V2';
10899 if ($this->encryptdata['pubkey']) {
10900 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
10901 $this->encryptdata['Recipients'] = array();
10902 }
10903 break;
10904 }
10905 case 2: { // AES 128 bit
10906 $this->encryptdata['V'] = 4;
10907 $this->encryptdata['Length'] = 128;
10908 $this->encryptdata['CF']['CFM'] = 'AESV2';
10909 $this->encryptdata['CF']['Length'] = 128;
10910 if ($this->encryptdata['pubkey']) {
10911 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
10912 $this->encryptdata['Recipients'] = array();
10913 }
10914 break;
10915 }
10916 case 3: { // AES 256 bit
10917 $this->encryptdata['V'] = 5;
10918 $this->encryptdata['Length'] = 256;
10919 $this->encryptdata['CF']['CFM'] = 'AESV3';
10920 $this->encryptdata['CF']['Length'] = 256;
10921 if ($this->encryptdata['pubkey']) {
10922 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
10923 $this->encryptdata['Recipients'] = array();
10924 }
10925 break;
10926 }
10927 }
10928 $this->encrypted = true;
10929 $this->encryptdata['fileid'] = TCPDF_STATIC::convertHexStringToString($this->file_id);
10930 $this->_generateencryptionkey();
10931 }
10932
10933 // END OF ENCRYPTION FUNCTIONS -------------------------
10934
10935 // START TRANSFORMATIONS SECTION -----------------------
10936
10945 public function StartTransform() {
10946 if ($this->state != 2) {
10947 return;
10948 }
10949 $this->_outSaveGraphicsState();
10950 if ($this->inxobj) {
10951 // we are inside an XObject template
10952 $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
10953 } else {
10954 $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
10955 }
10957 $this->transfmatrix[$this->transfmatrix_key] = array();
10958 }
10959
10968 public function StopTransform() {
10969 if ($this->state != 2) {
10970 return;
10971 }
10972 $this->_outRestoreGraphicsState();
10973 if (isset($this->transfmatrix[$this->transfmatrix_key])) {
10974 array_pop($this->transfmatrix[$this->transfmatrix_key]);
10976 }
10977 if ($this->inxobj) {
10978 // we are inside an XObject template
10979 array_pop($this->xobjects[$this->xobjid]['transfmrk']);
10980 } else {
10981 array_pop($this->transfmrk[$this->page]);
10982 }
10983 }
10993 public function ScaleX($s_x, $x='', $y='') {
10994 $this->Scale($s_x, 100, $x, $y);
10995 }
10996
11006 public function ScaleY($s_y, $x='', $y='') {
11007 $this->Scale(100, $s_y, $x, $y);
11008 }
11009
11019 public function ScaleXY($s, $x='', $y='') {
11020 $this->Scale($s, $s, $x, $y);
11021 }
11022
11033 public function Scale($s_x, $s_y, $x='', $y='') {
11034 if ($x === '') {
11035 $x = $this->x;
11036 }
11037 if ($y === '') {
11038 $y = $this->y;
11039 }
11040 if (($s_x == 0) OR ($s_y == 0)) {
11041 $this->Error('Please do not use values equal to zero for scaling');
11042 }
11043 $y = ($this->h - $y) * $this->k;
11044 $x *= $this->k;
11045 //calculate elements of transformation matrix
11046 $s_x /= 100;
11047 $s_y /= 100;
11048 $tm = array();
11049 $tm[0] = $s_x;
11050 $tm[1] = 0;
11051 $tm[2] = 0;
11052 $tm[3] = $s_y;
11053 $tm[4] = $x * (1 - $s_x);
11054 $tm[5] = $y * (1 - $s_y);
11055 //scale the coordinate system
11056 $this->Transform($tm);
11057 }
11058
11066 public function MirrorH($x='') {
11067 $this->Scale(-100, 100, $x);
11068 }
11069
11077 public function MirrorV($y='') {
11078 $this->Scale(100, -100, '', $y);
11079 }
11080
11089 public function MirrorP($x='',$y='') {
11090 $this->Scale(-100, -100, $x, $y);
11091 }
11092
11102 public function MirrorL($angle=0, $x='',$y='') {
11103 $this->Scale(-100, 100, $x, $y);
11104 $this->Rotate(-2*($angle-90), $x, $y);
11105 }
11106
11114 public function TranslateX($t_x) {
11115 $this->Translate($t_x, 0);
11116 }
11117
11125 public function TranslateY($t_y) {
11126 $this->Translate(0, $t_y);
11127 }
11128
11137 public function Translate($t_x, $t_y) {
11138 //calculate elements of transformation matrix
11139 $tm = array();
11140 $tm[0] = 1;
11141 $tm[1] = 0;
11142 $tm[2] = 0;
11143 $tm[3] = 1;
11144 $tm[4] = $t_x * $this->k;
11145 $tm[5] = -$t_y * $this->k;
11146 //translate the coordinate system
11147 $this->Transform($tm);
11148 }
11149
11159 public function Rotate($angle, $x='', $y='') {
11160 if ($x === '') {
11161 $x = $this->x;
11162 }
11163 if ($y === '') {
11164 $y = $this->y;
11165 }
11166 $y = ($this->h - $y) * $this->k;
11167 $x *= $this->k;
11168 //calculate elements of transformation matrix
11169 $tm = array();
11170 $tm[0] = cos(deg2rad($angle));
11171 $tm[1] = sin(deg2rad($angle));
11172 $tm[2] = -$tm[1];
11173 $tm[3] = $tm[0];
11174 $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
11175 $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
11176 //rotate the coordinate system around ($x,$y)
11177 $this->Transform($tm);
11178 }
11179
11189 public function SkewX($angle_x, $x='', $y='') {
11190 $this->Skew($angle_x, 0, $x, $y);
11191 }
11192
11202 public function SkewY($angle_y, $x='', $y='') {
11203 $this->Skew(0, $angle_y, $x, $y);
11204 }
11205
11216 public function Skew($angle_x, $angle_y, $x='', $y='') {
11217 if ($x === '') {
11218 $x = $this->x;
11219 }
11220 if ($y === '') {
11221 $y = $this->y;
11222 }
11223 if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
11224 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
11225 }
11226 $x *= $this->k;
11227 $y = ($this->h - $y) * $this->k;
11228 //calculate elements of transformation matrix
11229 $tm = array();
11230 $tm[0] = 1;
11231 $tm[1] = tan(deg2rad($angle_y));
11232 $tm[2] = tan(deg2rad($angle_x));
11233 $tm[3] = 1;
11234 $tm[4] = -$tm[2] * $y;
11235 $tm[5] = -$tm[1] * $x;
11236 //skew the coordinate system
11237 $this->Transform($tm);
11238 }
11239
11247 protected function Transform($tm) {
11248 if ($this->state != 2) {
11249 return;
11250 }
11251 $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
11252 // add tranformation matrix
11253 $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
11254 // update transformation mark
11255 if ($this->inxobj) {
11256 // we are inside an XObject template
11257 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
11258 $key = key($this->xobjects[$this->xobjid]['transfmrk']);
11259 $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
11260 }
11261 } elseif (end($this->transfmrk[$this->page]) !== false) {
11262 $key = key($this->transfmrk[$this->page]);
11263 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
11264 }
11265 }
11266
11267 // END TRANSFORMATIONS SECTION -------------------------
11268
11269 // START GRAPHIC FUNCTIONS SECTION ---------------------
11270 // The following section is based on the code provided by David Hernandez Sanz
11271
11279 public function SetLineWidth($width) {
11280 //Set line width
11281 $this->LineWidth = $width;
11282 $this->linestyleWidth = sprintf('%F w', ($width * $this->k));
11283 if ($this->state == 2) {
11284 $this->_out($this->linestyleWidth);
11285 }
11286 }
11287
11295 public function GetLineWidth() {
11296 return $this->LineWidth;
11297 }
11298
11322 public function SetLineStyle($style, $ret=false) {
11323 $s = ''; // string to be returned
11324 if (!is_array($style)) {
11325 return;
11326 }
11327 if (isset($style['width'])) {
11328 $this->LineWidth = $style['width'];
11329 $this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k));
11330 $s .= $this->linestyleWidth.' ';
11331 }
11332 if (isset($style['cap'])) {
11333 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
11334 if (isset($ca[$style['cap']])) {
11335 $this->linestyleCap = $ca[$style['cap']].' J';
11336 $s .= $this->linestyleCap.' ';
11337 }
11338 }
11339 if (isset($style['join'])) {
11340 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
11341 if (isset($ja[$style['join']])) {
11342 $this->linestyleJoin = $ja[$style['join']].' j';
11343 $s .= $this->linestyleJoin.' ';
11344 }
11345 }
11346 if (isset($style['dash'])) {
11347 $dash_string = '';
11348 if ($style['dash']) {
11349 if (preg_match('/^.+,/', $style['dash']) > 0) {
11350 $tab = explode(',', $style['dash']);
11351 } else {
11352 $tab = array($style['dash']);
11353 }
11354 $dash_string = '';
11355 foreach ($tab as $i => $v) {
11356 if ($i) {
11357 $dash_string .= ' ';
11358 }
11359 $dash_string .= sprintf('%F', $v);
11360 }
11361 }
11362 if (!isset($style['phase']) OR !$style['dash']) {
11363 $style['phase'] = 0;
11364 }
11365 $this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']);
11366 $s .= $this->linestyleDash.' ';
11367 }
11368 if (isset($style['color'])) {
11369 $s .= $this->SetDrawColorArray($style['color'], true).' ';
11370 }
11371 if (!$ret AND ($this->state == 2)) {
11372 $this->_out($s);
11373 }
11374 return $s;
11375 }
11376
11384 protected function _outPoint($x, $y) {
11385 if ($this->state == 2) {
11386 $this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k)));
11387 }
11388 }
11389
11398 protected function _outLine($x, $y) {
11399 if ($this->state == 2) {
11400 $this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k)));
11401 }
11402 }
11403
11414 protected function _outRect($x, $y, $w, $h, $op) {
11415 if ($this->state == 2) {
11416 $this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k), (($this->h - $y) * $this->k), ($w * $this->k), (-$h * $this->k), $op));
11417 }
11418 }
11419
11432 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
11433 if ($this->state == 2) {
11434 $this->_out(sprintf('%F %F %F %F %F %F c', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11435 }
11436 }
11437
11448 protected function _outCurveV($x2, $y2, $x3, $y3) {
11449 if ($this->state == 2) {
11450 $this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11451 }
11452 }
11453
11464 protected function _outCurveY($x1, $y1, $x3, $y3) {
11465 if ($this->state == 2) {
11466 $this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11467 }
11468 }
11469
11481 public function Line($x1, $y1, $x2, $y2, $style=array()) {
11482 if ($this->state != 2) {
11483 return;
11484 }
11485 if (is_array($style)) {
11486 $this->SetLineStyle($style);
11487 }
11488 $this->_outPoint($x1, $y1);
11489 $this->_outLine($x2, $y2);
11490 $this->_out('S');
11491 }
11492
11511 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
11512 if ($this->state != 2) {
11513 return;
11514 }
11515 if (empty($style)) {
11516 $style = 'S';
11517 }
11518 if (!(strpos($style, 'F') === false) AND !empty($fill_color)) {
11519 // set background color
11520 $this->SetFillColorArray($fill_color);
11521 }
11522 if (!empty($border_style)) {
11523 if (isset($border_style['all']) AND !empty($border_style['all'])) {
11524 //set global style for border
11525 $this->SetLineStyle($border_style['all']);
11526 $border_style = array();
11527 } else {
11528 // remove stroke operator from style
11529 $opnostroke = array('S' => '', 'D' => '', 's' => '', 'd' => '', 'B' => 'F', 'FD' => 'F', 'DF' => 'F', 'B*' => 'F*', 'F*D' => 'F*', 'DF*' => 'F*', 'b' => 'f', 'fd' => 'f', 'df' => 'f', 'b*' => 'f*', 'f*d' => 'f*', 'df*' => 'f*' );
11530 if (isset($opnostroke[$style])) {
11531 $style = $opnostroke[$style];
11532 }
11533 }
11534 }
11535 if (!empty($style)) {
11537 $this->_outRect($x, $y, $w, $h, $op);
11538 }
11539 if (!empty($border_style)) {
11540 $border_style2 = array();
11541 foreach ($border_style as $line => $value) {
11542 $length = strlen($line);
11543 for ($i = 0; $i < $length; ++$i) {
11544 $border_style2[$line[$i]] = $value;
11545 }
11546 }
11547 $border_style = $border_style2;
11548 if (isset($border_style['L']) AND $border_style['L']) {
11549 $this->Line($x, $y, $x, $y + $h, $border_style['L']);
11550 }
11551 if (isset($border_style['T']) AND $border_style['T']) {
11552 $this->Line($x, $y, $x + $w, $y, $border_style['T']);
11553 }
11554 if (isset($border_style['R']) AND $border_style['R']) {
11555 $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
11556 }
11557 if (isset($border_style['B']) AND $border_style['B']) {
11558 $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
11559 }
11560 }
11561 }
11562
11582 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
11583 if ($this->state != 2) {
11584 return;
11585 }
11586 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11587 $this->SetFillColorArray($fill_color);
11588 }
11590 if ($line_style) {
11591 $this->SetLineStyle($line_style);
11592 }
11593 $this->_outPoint($x0, $y0);
11594 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11595 $this->_out($op);
11596 }
11597
11612 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
11613 if ($this->state != 2) {
11614 return;
11615 }
11616 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11617 $this->SetFillColorArray($fill_color);
11618 }
11620 if ($op == 'f') {
11621 $line_style = array();
11622 }
11623 if ($line_style) {
11624 $this->SetLineStyle($line_style);
11625 }
11626 $this->_outPoint($x0, $y0);
11627 foreach ($segments as $segment) {
11628 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
11629 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11630 }
11631 $this->_out($op);
11632 }
11633
11652 public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11653 if ($this->state != 2) {
11654 return;
11655 }
11656 if (TCPDF_STATIC::empty_string($ry) OR ($ry == 0)) {
11657 $ry = $rx;
11658 }
11659 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11660 $this->SetFillColorArray($fill_color);
11661 }
11663 if ($op == 'f') {
11664 $line_style = array();
11665 }
11666 if ($line_style) {
11667 $this->SetLineStyle($line_style);
11668 }
11669 $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
11670 $this->_out($op);
11671 }
11672
11693 protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
11694 if (($rx <= 0) OR ($ry < 0)) {
11695 return;
11696 }
11697 $k = $this->k;
11698 if ($nc < 2) {
11699 $nc = 2;
11700 }
11701 $xmin = 2147483647;
11702 $ymin = 2147483647;
11703 $xmax = 0;
11704 $ymax = 0;
11705 if ($pie) {
11706 // center of the arc
11707 $this->_outPoint($xc, $yc);
11708 }
11709 $xang = deg2rad((float) $xang);
11710 $angs = deg2rad((float) $angs);
11711 $angf = deg2rad((float) $angf);
11712 if ($svg) {
11713 $as = $angs;
11714 $af = $angf;
11715 } else {
11716 $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
11717 $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
11718 }
11719 if ($as < 0) {
11720 $as += (2 * M_PI);
11721 }
11722 if ($af < 0) {
11723 $af += (2 * M_PI);
11724 }
11725 if ($ccw AND ($as > $af)) {
11726 // reverse rotation
11727 $as -= (2 * M_PI);
11728 } elseif (!$ccw AND ($as < $af)) {
11729 // reverse rotation
11730 $af -= (2 * M_PI);
11731 }
11732 $total_angle = ($af - $as);
11733 if ($nc < 2) {
11734 $nc = 2;
11735 }
11736 // total arcs to draw
11737 $nc *= (2 * abs($total_angle) / M_PI);
11738 $nc = round($nc) + 1;
11739 // angle of each arc
11740 $arcang = ($total_angle / $nc);
11741 // center point in PDF coordinates
11742 $x0 = $xc;
11743 $y0 = ($this->h - $yc);
11744 // starting angle
11745 $ang = $as;
11746 $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
11747 $cos_xang = cos($xang);
11748 $sin_xang = sin($xang);
11749 $cos_ang = cos($ang);
11750 $sin_ang = sin($ang);
11751 // first arc point
11752 $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11753 $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11754 // first Bezier control point
11755 $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11756 $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11757 if ($pie) {
11758 // line from center to arc starting point
11759 $this->_outLine($px1, $this->h - $py1);
11760 } elseif ($startpoint) {
11761 // arc starting point
11762 $this->_outPoint($px1, $this->h - $py1);
11763 }
11764 // draw arcs
11765 for ($i = 1; $i <= $nc; ++$i) {
11766 // starting angle
11767 $ang = $as + ($i * $arcang);
11768 if ($i == $nc) {
11769 $ang = $af;
11770 }
11771 $cos_ang = cos($ang);
11772 $sin_ang = sin($ang);
11773 // second arc point
11774 $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11775 $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11776 // second Bezier control point
11777 $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11778 $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11779 // draw arc
11780 $cx1 = ($px1 + $qx1);
11781 $cy1 = ($this->h - ($py1 + $qy1));
11782 $cx2 = ($px2 - $qx2);
11783 $cy2 = ($this->h - ($py2 - $qy2));
11784 $cx3 = $px2;
11785 $cy3 = ($this->h - $py2);
11786 $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
11787 // get bounding box coordinates
11788 $xmin = min($xmin, $cx1, $cx2, $cx3);
11789 $ymin = min($ymin, $cy1, $cy2, $cy3);
11790 $xmax = max($xmax, $cx1, $cx2, $cx3);
11791 $ymax = max($ymax, $cy1, $cy2, $cy3);
11792 // move to next point
11793 $px1 = $px2;
11794 $py1 = $py2;
11795 $qx1 = $qx2;
11796 $qy1 = $qy2;
11797 }
11798 if ($pie) {
11799 $this->_outLine($xc, $yc);
11800 // get bounding box coordinates
11801 $xmin = min($xmin, $xc);
11802 $ymin = min($ymin, $yc);
11803 $xmax = max($xmax, $xc);
11804 $ymax = max($ymax, $yc);
11805 }
11806 return array($xmin, $ymin, $xmax, $ymax);
11807 }
11808
11824 public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11825 $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
11826 }
11827
11842 public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
11843 $this->Polygon($p, $style, $line_style, $fill_color, false);
11844 }
11845
11861 public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
11862 if ($this->state != 2) {
11863 return;
11864 }
11865 $nc = count($p); // number of coordinates
11866 $np = $nc / 2; // number of points
11867 if ($closed) {
11868 // close polygon by adding the first 2 points at the end (one line)
11869 for ($i = 0; $i < 4; ++$i) {
11870 $p[$nc + $i] = $p[$i];
11871 }
11872 // copy style for the last added line
11873 if (isset($line_style[0])) {
11874 $line_style[$np] = $line_style[0];
11875 }
11876 $nc += 4;
11877 }
11878 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11879 $this->SetFillColorArray($fill_color);
11880 }
11882 if ($op == 'f') {
11883 $line_style = array();
11884 }
11885 $draw = true;
11886 if ($line_style) {
11887 if (isset($line_style['all'])) {
11888 $this->SetLineStyle($line_style['all']);
11889 } else {
11890 $draw = false;
11891 if ($op == 'B') {
11892 // draw fill
11893 $op = 'f';
11894 $this->_outPoint($p[0], $p[1]);
11895 for ($i = 2; $i < $nc; $i = $i + 2) {
11896 $this->_outLine($p[$i], $p[$i + 1]);
11897 }
11898 $this->_out($op);
11899 }
11900 // draw outline
11901 $this->_outPoint($p[0], $p[1]);
11902 for ($i = 2; $i < $nc; $i = $i + 2) {
11903 $line_num = ($i / 2) - 1;
11904 if (isset($line_style[$line_num])) {
11905 if ($line_style[$line_num] != 0) {
11906 if (is_array($line_style[$line_num])) {
11907 $this->_out('S');
11908 $this->SetLineStyle($line_style[$line_num]);
11909 $this->_outPoint($p[$i - 2], $p[$i - 1]);
11910 $this->_outLine($p[$i], $p[$i + 1]);
11911 $this->_out('S');
11912 $this->_outPoint($p[$i], $p[$i + 1]);
11913 } else {
11914 $this->_outLine($p[$i], $p[$i + 1]);
11915 }
11916 }
11917 } else {
11918 $this->_outLine($p[$i], $p[$i + 1]);
11919 }
11920 }
11921 $this->_out($op);
11922 }
11923 }
11924 if ($draw) {
11925 $this->_outPoint($p[0], $p[1]);
11926 for ($i = 2; $i < $nc; $i = $i + 2) {
11927 $this->_outLine($p[$i], $p[$i + 1]);
11928 }
11929 $this->_out($op);
11930 }
11931 }
11932
11962 public function RegularPolygon($x0, $y0, $r, $ns, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
11963 if (3 > $ns) {
11964 $ns = 3;
11965 }
11966 if ($draw_circle) {
11967 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
11968 }
11969 $p = array();
11970 for ($i = 0; $i < $ns; ++$i) {
11971 $a = $angle + ($i * 360 / $ns);
11972 $a_rad = deg2rad((float) $a);
11973 $p[] = $x0 + ($r * sin($a_rad));
11974 $p[] = $y0 + ($r * cos($a_rad));
11975 }
11976 $this->Polygon($p, $style, $line_style, $fill_color);
11977 }
11978
12010 public function StarPolygon($x0, $y0, $r, $nv, $ng, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
12011 if ($nv < 2) {
12012 $nv = 2;
12013 }
12014 if ($draw_circle) {
12015 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12016 }
12017 $p2 = array();
12018 $visited = array();
12019 for ($i = 0; $i < $nv; ++$i) {
12020 $a = $angle + ($i * 360 / $nv);
12021 $a_rad = deg2rad((float) $a);
12022 $p2[] = $x0 + ($r * sin($a_rad));
12023 $p2[] = $y0 + ($r * cos($a_rad));
12024 $visited[] = false;
12025 }
12026 $p = array();
12027 $i = 0;
12028 do {
12029 $p[] = $p2[$i * 2];
12030 $p[] = $p2[($i * 2) + 1];
12031 $visited[$i] = true;
12032 $i += $ng;
12033 $i %= $nv;
12034 } while (!$visited[$i]);
12035 $this->Polygon($p, $style, $line_style, $fill_color);
12036 }
12037
12052 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12053 $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
12054 }
12055
12071 public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12072 if ($this->state != 2) {
12073 return;
12074 }
12075 if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
12076 // Not rounded
12077 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
12078 return;
12079 }
12080 // Rounded
12081 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12082 $this->SetFillColorArray($fill_color);
12083 }
12085 if ($op == 'f') {
12086 $border_style = array();
12087 }
12088 if ($border_style) {
12090 }
12091 $MyArc = 4 / 3 * (sqrt(2) - 1);
12092 $this->_outPoint($x + $rx, $y);
12093 $xc = $x + $w - $rx;
12094 $yc = $y + $ry;
12095 $this->_outLine($xc, $y);
12096 if ($round_corner[0]) {
12097 $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
12098 } else {
12099 $this->_outLine($x + $w, $y);
12100 }
12101 $xc = $x + $w - $rx;
12102 $yc = $y + $h - $ry;
12103 $this->_outLine($x + $w, $yc);
12104 if ($round_corner[1]) {
12105 $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
12106 } else {
12107 $this->_outLine($x + $w, $y + $h);
12108 }
12109 $xc = $x + $rx;
12110 $yc = $y + $h - $ry;
12111 $this->_outLine($xc, $y + $h);
12112 if ($round_corner[2]) {
12113 $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
12114 } else {
12115 $this->_outLine($x, $y + $h);
12116 }
12117 $xc = $x + $rx;
12118 $yc = $y + $ry;
12119 $this->_outLine($x, $yc);
12120 if ($round_corner[3]) {
12121 $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
12122 } else {
12123 $this->_outLine($x, $y);
12124 $this->_outLine($x + $rx, $y);
12125 }
12126 $this->_out($op);
12127 }
12128
12141 public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
12142 // getting arrow direction angle
12143 // 0 deg angle is when both arms go along X axis. angle grows clockwise.
12144 $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
12145 if ($dir_angle < 0) {
12146 $dir_angle += (2 * M_PI);
12147 }
12148 $arm_angle = deg2rad($arm_angle);
12149 $sx1 = $x1;
12150 $sy1 = $y1;
12151 if ($head_style > 0) {
12152 // calculate the stopping point for the arrow shaft
12153 $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
12154 $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
12155 }
12156 // main arrow line / shaft
12157 $this->Line($x0, $y0, $sx1, $sy1);
12158 // left arrowhead arm tip
12159 $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
12160 $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
12161 // right arrowhead arm tip
12162 $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
12163 $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
12164 $mode = 'D';
12165 $style = array();
12166 switch ($head_style) {
12167 case 0: {
12168 // draw only arrowhead arms
12169 $mode = 'D';
12170 $style = array(1, 1, 0);
12171 break;
12172 }
12173 case 1: {
12174 // draw closed arrowhead, but no fill
12175 $mode = 'D';
12176 break;
12177 }
12178 case 2: {
12179 // closed and filled arrowhead
12180 $mode = 'DF';
12181 break;
12182 }
12183 case 3: {
12184 // filled arrowhead
12185 $mode = 'F';
12186 break;
12187 }
12188 }
12189 $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
12190 }
12191
12192 // END GRAPHIC FUNCTIONS SECTION -----------------------
12193
12206 public function setDestination($name, $y=-1, $page='', $x=-1) {
12207 // remove unsupported characters
12210 return false;
12211 }
12212 if ($y == -1) {
12213 $y = $this->GetY();
12214 } elseif ($y < 0) {
12215 $y = 0;
12216 } elseif ($y > $this->h) {
12217 $y = $this->h;
12218 }
12219 if ($x == -1) {
12220 $x = $this->GetX();
12221 } elseif ($x < 0) {
12222 $x = 0;
12223 } elseif ($x > $this->w) {
12224 $x = $this->w;
12225 }
12226 $fixed = false;
12227 if (!empty($page) AND ($page[0] == '*')) {
12228 $page = intval(substr($page, 1));
12229 // this page number will not be changed when moving/add/deleting pages
12230 $fixed = true;
12231 }
12232 if (empty($page)) {
12233 $page = $this->PageNo();
12234 if (empty($page)) {
12235 return;
12236 }
12237 }
12238 $this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed);
12239 return $name;
12240 }
12241
12249 public function getDestination() {
12250 return $this->dests;
12251 }
12252
12259 protected function _putdests() {
12260 if (empty($this->dests)) {
12261 return;
12262 }
12263 $this->n_dests = $this->_newobj();
12264 $out = ' <<';
12265 foreach($this->dests as $name => $o) {
12266 $out .= ' /'.$name.' '.sprintf('[%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
12267 }
12268 $out .= ' >>';
12269 $out .= "\n".'endobj';
12270 $this->_out($out);
12271 }
12272
12285 public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12286 $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link);
12287 }
12288
12302 public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12303 if ($level < 0) {
12304 $level = 0;
12305 }
12306 if (isset($this->outlines[0])) {
12307 $lastoutline = end($this->outlines);
12308 $maxlevel = $lastoutline['l'] + 1;
12309 } else {
12310 $maxlevel = 0;
12311 }
12312 if ($level > $maxlevel) {
12313 $level = $maxlevel;
12314 }
12315 if ($y == -1) {
12316 $y = $this->GetY();
12317 } elseif ($y < 0) {
12318 $y = 0;
12319 } elseif ($y > $this->h) {
12320 $y = $this->h;
12321 }
12322 if ($x == -1) {
12323 $x = $this->GetX();
12324 } elseif ($x < 0) {
12325 $x = 0;
12326 } elseif ($x > $this->w) {
12327 $x = $this->w;
12328 }
12329 $fixed = false;
12330 if (!empty($page) AND ($page[0] == '*')) {
12331 $page = intval(substr($page, 1));
12332 // this page number will not be changed when moving/add/deleting pages
12333 $fixed = true;
12334 }
12335 if (empty($page)) {
12336 $page = $this->PageNo();
12337 if (empty($page)) {
12338 return;
12339 }
12340 }
12341 $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed, 's' => strtoupper($style), 'c' => $color, 'u' => $link);
12342 }
12343
12349 protected function sortBookmarks() {
12350 // get sorting columns
12351 $outline_p = array();
12352 $outline_y = array();
12353 foreach ($this->outlines as $key => $row) {
12354 $outline_p[$key] = $row['p'];
12355 $outline_k[$key] = $key;
12356 }
12357 // sort outlines by page and original position
12358 array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
12359 }
12360
12367 protected function _putbookmarks() {
12368 $nb = count($this->outlines);
12369 if ($nb == 0) {
12370 return;
12371 }
12372 // sort bookmarks
12373 $this->sortBookmarks();
12374 $lru = array();
12375 $level = 0;
12376 foreach ($this->outlines as $i => $o) {
12377 if ($o['l'] > 0) {
12378 $parent = $lru[($o['l'] - 1)];
12379 //Set parent and last pointers
12380 $this->outlines[$i]['parent'] = $parent;
12381 $this->outlines[$parent]['last'] = $i;
12382 if ($o['l'] > $level) {
12383 //Level increasing: set first pointer
12384 $this->outlines[$parent]['first'] = $i;
12385 }
12386 } else {
12387 $this->outlines[$i]['parent'] = $nb;
12388 }
12389 if (($o['l'] <= $level) AND ($i > 0)) {
12390 //Set prev and next pointers
12391 $prev = $lru[$o['l']];
12392 $this->outlines[$prev]['next'] = $i;
12393 $this->outlines[$i]['prev'] = $prev;
12394 }
12395 $lru[$o['l']] = $i;
12396 $level = $o['l'];
12397 }
12398 //Outline items
12399 $n = $this->n + 1;
12400 $nltags = '/<br[\s]?\/>|<\/(blockquote|dd|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|p|pre|ul|tcpdf|table|tr|td)>/si';
12401 foreach ($this->outlines as $i => $o) {
12402 $oid = $this->_newobj();
12403 // covert HTML title to string
12404 $title = preg_replace($nltags, "\n", $o['t']);
12405 $title = preg_replace("/[\r]+/si", '', $title);
12406 $title = preg_replace("/[\n]+/si", "\n", $title);
12407 $title = strip_tags($title);
12408 $title = $this->stringTrim($title);
12409 $out = '<</Title '.$this->_textstring($title, $oid);
12410 $out .= ' /Parent '.($n + $o['parent']).' 0 R';
12411 if (isset($o['prev'])) {
12412 $out .= ' /Prev '.($n + $o['prev']).' 0 R';
12413 }
12414 if (isset($o['next'])) {
12415 $out .= ' /Next '.($n + $o['next']).' 0 R';
12416 }
12417 if (isset($o['first'])) {
12418 $out .= ' /First '.($n + $o['first']).' 0 R';
12419 }
12420 if (isset($o['last'])) {
12421 $out .= ' /Last '.($n + $o['last']).' 0 R';
12422 }
12423 if (isset($o['u']) AND !empty($o['u'])) {
12424 // link
12425 if (is_string($o['u'])) {
12426 if ($o['u'][0] == '#') {
12427 // internal destination
12428 $out .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($o['u'], 1));
12429 } elseif ($o['u'][0] == '%') {
12430 // embedded PDF file
12431 $filename = basename(substr($o['u'], 1));
12432 $out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
12433 } elseif ($o['u'][0] == '*') {
12434 // embedded generic file
12435 $filename = basename(substr($o['u'], 1));
12436 $jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});';
12437 $out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>';
12438 } else {
12439 // external URI link
12440 $out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>';
12441 }
12442 } elseif (isset($this->links[$o['u']])) {
12443 // internal link ID
12444 $l = $this->links[$o['u']];
12445 if (isset($this->page_obj_id[($l['p'])])) {
12446 $out .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l['p'])], ($this->pagedim[$l['p']]['h'] - ($l['y'] * $this->k)));
12447 }
12448 }
12449 } elseif (isset($this->page_obj_id[($o['p'])])) {
12450 // link to a page
12451 $out .= ' '.sprintf('/Dest [%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
12452 }
12453 // set font style
12454 $style = 0;
12455 if (!empty($o['s'])) {
12456 // bold
12457 if (strpos($o['s'], 'B') !== false) {
12458 $style |= 2;
12459 }
12460 // oblique
12461 if (strpos($o['s'], 'I') !== false) {
12462 $style |= 1;
12463 }
12464 }
12465 $out .= sprintf(' /F %d', $style);
12466 // set bookmark color
12467 if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
12468 $color = array_values($o['c']);
12469 $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
12470 } else {
12471 // black
12472 $out .= ' /C [0.0 0.0 0.0]';
12473 }
12474 $out .= ' /Count 0'; // normally closed item
12475 $out .= ' >>';
12476 $out .= "\n".'endobj';
12477 $this->_out($out);
12478 }
12479 //Outline root
12480 $this->OutlineRoot = $this->_newobj();
12481 $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj');
12482 }
12483
12484 // --- JAVASCRIPT ------------------------------------------------------
12485
12493 public function IncludeJS($script) {
12494 $this->javascript .= $script;
12495 }
12496
12506 public function addJavascriptObject($script, $onload=false) {
12507 if ($this->pdfa_mode) {
12508 // javascript is not allowed in PDF/A mode
12509 return false;
12510 }
12511 ++$this->n;
12512 $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload);
12513 return $this->n;
12514 }
12515
12522 protected function _putjavascript() {
12523 if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) {
12524 return;
12525 }
12526 if (strpos($this->javascript, 'this.addField') > 0) {
12527 if (!$this->ur['enabled']) {
12528 //$this->setUserRights();
12529 }
12530 // the following two lines are used to avoid form fields duplication after saving
12531 // The addField method only works when releasing user rights (UR3)
12532 $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
12533 $jsb = "getField('tcpdfdocsaved').value='saved';";
12534 $this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
12535 }
12536 // name tree for javascript
12537 $this->n_js = '<< /Names [';
12538 if (!empty($this->javascript)) {
12539 $this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
12540 }
12541 if (!empty($this->js_objects)) {
12542 foreach ($this->js_objects as $key => $val) {
12543 if ($val['onload']) {
12544 $this->n_js .= ' (JS'.$key.') '.$key.' 0 R';
12545 }
12546 }
12547 }
12548 $this->n_js .= ' ] >>';
12549 // default Javascript object
12550 if (!empty($this->javascript)) {
12551 $obj_id = $this->_newobj();
12552 $out = '<< /S /JavaScript';
12553 $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id);
12554 $out .= ' >>';
12555 $out .= "\n".'endobj';
12556 $this->_out($out);
12557 }
12558 // additional Javascript objects
12559 if (!empty($this->js_objects)) {
12560 foreach ($this->js_objects as $key => $val) {
12561 $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
12562 $this->_out($out);
12563 }
12564 }
12565 }
12566
12580 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
12581 if ($this->rtl) {
12582 $x = $x - $w;
12583 }
12584 // the followind avoid fields duplication after saving the document
12585 $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {";
12586 $k = $this->k;
12587 $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%u,[%F,%F,%F,%F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n";
12588 $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
12589 while (list($key, $val) = each($prop)) {
12590 if (strcmp(substr($key, -5), 'Color') == 0) {
12591 $val = TCPDF_COLORS::_JScolor($val);
12592 } else {
12593 $val = "'".$val."'";
12594 }
12595 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
12596 }
12597 if ($this->rtl) {
12598 $this->x -= $w;
12599 } else {
12600 $this->x += $w;
12601 }
12602 $this->javascript .= '}';
12603 }
12604
12605 // --- FORM FIELDS -----------------------------------------------------
12606
12607
12608
12616 public function setFormDefaultProp($prop=array()) {
12617 $this->default_form_prop = $prop;
12618 }
12619
12627 public function getFormDefaultProp() {
12629 }
12630
12645 public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12646 if ($x === '') {
12647 $x = $this->x;
12648 }
12649 if ($y === '') {
12650 $y = $this->y;
12651 }
12652 // check page for no-write regions and adapt page margins if necessary
12653 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12654 if ($js) {
12655 $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
12656 return;
12657 }
12658 // get default style
12659 $prop = array_merge($this->getFormDefaultProp(), $prop);
12660 // get annotation data
12661 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12662 // set default appearance stream
12663 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12664 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12665 $popt['da'] = $fontstyle;
12666 // build appearance stream
12667 $popt['ap'] = array();
12668 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12669 $text = '';
12670 if (isset($prop['value']) AND !empty($prop['value'])) {
12671 $text = $prop['value'];
12672 } elseif (isset($opt['v']) AND !empty($opt['v'])) {
12673 $text = $opt['v'];
12674 }
12675 $tmpid = $this->startTemplate($w, $h, false);
12676 $align = '';
12677 if (isset($popt['q'])) {
12678 switch ($popt['q']) {
12679 case 0: {
12680 $align = 'L';
12681 break;
12682 }
12683 case 1: {
12684 $align = 'C';
12685 break;
12686 }
12687 case 2: {
12688 $align = 'R';
12689 break;
12690 }
12691 default: {
12692 $align = '';
12693 break;
12694 }
12695 }
12696 }
12697 $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12698 $this->endTemplate();
12699 --$this->n;
12700 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12701 unset($this->xobjects[$tmpid]);
12702 $popt['ap']['n'] .= 'Q EMC';
12703 // merge options
12704 $opt = array_merge($popt, $opt);
12705 // remove some conflicting options
12706 unset($opt['bs']);
12707 // set remaining annotation data
12708 $opt['Subtype'] = 'Widget';
12709 $opt['ft'] = 'Tx';
12710 $opt['t'] = $name;
12711 // Additional annotation's parameters (check _putannotsobj() method):
12712 //$opt['f']
12713 //$opt['as']
12714 //$opt['bs']
12715 //$opt['be']
12716 //$opt['c']
12717 //$opt['border']
12718 //$opt['h']
12719 //$opt['mk'];
12720 //$opt['mk']['r']
12721 //$opt['mk']['bc'];
12722 //$opt['mk']['bg'];
12723 unset($opt['mk']['ca']);
12724 unset($opt['mk']['rc']);
12725 unset($opt['mk']['ac']);
12726 unset($opt['mk']['i']);
12727 unset($opt['mk']['ri']);
12728 unset($opt['mk']['ix']);
12729 unset($opt['mk']['if']);
12730 //$opt['mk']['if']['sw'];
12731 //$opt['mk']['if']['s'];
12732 //$opt['mk']['if']['a'];
12733 //$opt['mk']['if']['fb'];
12734 unset($opt['mk']['tp']);
12735 //$opt['tu']
12736 //$opt['tm']
12737 //$opt['ff']
12738 //$opt['v']
12739 //$opt['dv']
12740 //$opt['a']
12741 //$opt['aa']
12742 //$opt['q']
12743 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12744 if ($this->rtl) {
12745 $this->x -= $w;
12746 } else {
12747 $this->x += $w;
12748 }
12749 }
12750
12766 public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
12767 if ($x === '') {
12768 $x = $this->x;
12769 }
12770 if ($y === '') {
12771 $y = $this->y;
12772 }
12773 // check page for no-write regions and adapt page margins if necessary
12774 list($x, $y) = $this->checkPageRegions($w, $x, $y);
12775 if ($js) {
12776 $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
12777 return;
12778 }
12779 if (TCPDF_STATIC::empty_string($onvalue)) {
12780 $onvalue = 'On';
12781 }
12782 if ($checked) {
12783 $defval = $onvalue;
12784 } else {
12785 $defval = 'Off';
12786 }
12787 // set font
12788 $font = 'zapfdingbats';
12789 if ($this->pdfa_mode) {
12790 // all fonts must be embedded
12791 $font = 'pdfa'.$font;
12792 }
12793 $this->AddFont($font);
12794 $tmpfont = $this->getFontBuffer($font);
12795 // set data for parent group
12796 if (!isset($this->radiobutton_groups[$this->page])) {
12797 $this->radiobutton_groups[$this->page] = array();
12798 }
12799 if (!isset($this->radiobutton_groups[$this->page][$name])) {
12800 $this->radiobutton_groups[$this->page][$name] = array();
12801 ++$this->n;
12802 $this->radiobutton_groups[$this->page][$name]['n'] = $this->n;
12803 $this->radio_groups[] = $this->n;
12804 }
12805 $kid = ($this->n + 1);
12806 // save object ID to be added on Kids entry on parent object
12807 $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval);
12808 // get default style
12809 $prop = array_merge($this->getFormDefaultProp(), $prop);
12810 $prop['NoToggleToOff'] = 'true';
12811 $prop['Radio'] = 'true';
12812 $prop['borderStyle'] = 'inset';
12813 // get annotation data
12814 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12815 // set additional default options
12816 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
12817 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
12818 $popt['da'] = $fontstyle;
12819 // build appearance stream
12820 $popt['ap'] = array();
12821 $popt['ap']['n'] = array();
12822 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k);
12823 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
12824 $popt['ap']['n'][$onvalue] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(108).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
12825 $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(109).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
12826 if (!isset($popt['mk'])) {
12827 $popt['mk'] = array();
12828 }
12829 $popt['mk']['ca'] = '(l)';
12830 // merge options
12831 $opt = array_merge($popt, $opt);
12832 // set remaining annotation data
12833 $opt['Subtype'] = 'Widget';
12834 $opt['ft'] = 'Btn';
12835 if ($checked) {
12836 $opt['v'] = array('/'.$onvalue);
12837 $opt['as'] = $onvalue;
12838 } else {
12839 $opt['as'] = 'Off';
12840 }
12841 // store readonly flag
12842 if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) {
12843 $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false;
12844 }
12845 $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64);
12846 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
12847 if ($this->rtl) {
12848 $this->x -= $w;
12849 } else {
12850 $this->x += $w;
12851 }
12852 }
12853
12869 public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12870 if ($x === '') {
12871 $x = $this->x;
12872 }
12873 if ($y === '') {
12874 $y = $this->y;
12875 }
12876 // check page for no-write regions and adapt page margins if necessary
12877 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12878 if ($js) {
12879 $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
12880 $s = '';
12881 foreach ($values as $value) {
12882 if (is_array($value)) {
12883 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12884 } else {
12885 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12886 }
12887 }
12888 $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12889 return;
12890 }
12891 // get default style
12892 $prop = array_merge($this->getFormDefaultProp(), $prop);
12893 // get annotation data
12894 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12895 // set additional default values
12896 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12897 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12898 $popt['da'] = $fontstyle;
12899 // build appearance stream
12900 $popt['ap'] = array();
12901 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12902 $text = '';
12903 foreach($values as $item) {
12904 if (is_array($item)) {
12905 $text .= $item[1]."\n";
12906 } else {
12907 $text .= $item."\n";
12908 }
12909 }
12910 $tmpid = $this->startTemplate($w, $h, false);
12911 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12912 $this->endTemplate();
12913 --$this->n;
12914 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12915 unset($this->xobjects[$tmpid]);
12916 $popt['ap']['n'] .= 'Q EMC';
12917 // merge options
12918 $opt = array_merge($popt, $opt);
12919 // set remaining annotation data
12920 $opt['Subtype'] = 'Widget';
12921 $opt['ft'] = 'Ch';
12922 $opt['t'] = $name;
12923 $opt['opt'] = $values;
12924 unset($opt['mk']['ca']);
12925 unset($opt['mk']['rc']);
12926 unset($opt['mk']['ac']);
12927 unset($opt['mk']['i']);
12928 unset($opt['mk']['ri']);
12929 unset($opt['mk']['ix']);
12930 unset($opt['mk']['if']);
12931 unset($opt['mk']['tp']);
12932 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12933 if ($this->rtl) {
12934 $this->x -= $w;
12935 } else {
12936 $this->x += $w;
12937 }
12938 }
12939
12955 public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12956 if ($x === '') {
12957 $x = $this->x;
12958 }
12959 if ($y === '') {
12960 $y = $this->y;
12961 }
12962 // check page for no-write regions and adapt page margins if necessary
12963 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12964 if ($js) {
12965 $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
12966 $s = '';
12967 foreach ($values as $value) {
12968 if (is_array($value)) {
12969 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12970 } else {
12971 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12972 }
12973 }
12974 $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12975 return;
12976 }
12977 // get default style
12978 $prop = array_merge($this->getFormDefaultProp(), $prop);
12979 $prop['Combo'] = true;
12980 // get annotation data
12981 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12982 // set additional default options
12983 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12984 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12985 $popt['da'] = $fontstyle;
12986 // build appearance stream
12987 $popt['ap'] = array();
12988 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12989 $text = '';
12990 foreach($values as $item) {
12991 if (is_array($item)) {
12992 $text .= $item[1]."\n";
12993 } else {
12994 $text .= $item."\n";
12995 }
12996 }
12997 $tmpid = $this->startTemplate($w, $h, false);
12998 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12999 $this->endTemplate();
13000 --$this->n;
13001 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13002 unset($this->xobjects[$tmpid]);
13003 $popt['ap']['n'] .= 'Q EMC';
13004 // merge options
13005 $opt = array_merge($popt, $opt);
13006 // set remaining annotation data
13007 $opt['Subtype'] = 'Widget';
13008 $opt['ft'] = 'Ch';
13009 $opt['t'] = $name;
13010 $opt['opt'] = $values;
13011 unset($opt['mk']['ca']);
13012 unset($opt['mk']['rc']);
13013 unset($opt['mk']['ac']);
13014 unset($opt['mk']['i']);
13015 unset($opt['mk']['ri']);
13016 unset($opt['mk']['ix']);
13017 unset($opt['mk']['if']);
13018 unset($opt['mk']['tp']);
13019 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13020 if ($this->rtl) {
13021 $this->x -= $w;
13022 } else {
13023 $this->x += $w;
13024 }
13025 }
13026
13042 public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
13043 if ($x === '') {
13044 $x = $this->x;
13045 }
13046 if ($y === '') {
13047 $y = $this->y;
13048 }
13049 // check page for no-write regions and adapt page margins if necessary
13050 list($x, $y) = $this->checkPageRegions($w, $x, $y);
13051 if ($js) {
13052 $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
13053 return;
13054 }
13055 if (!isset($prop['value'])) {
13056 $prop['value'] = array('Yes');
13057 }
13058 // get default style
13059 $prop = array_merge($this->getFormDefaultProp(), $prop);
13060 $prop['borderStyle'] = 'inset';
13061 // get annotation data
13062 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13063 // set additional default options
13064 $font = 'zapfdingbats';
13065 if ($this->pdfa_mode) {
13066 // all fonts must be embedded
13067 $font = 'pdfa'.$font;
13068 }
13069 $this->AddFont($font);
13070 $tmpfont = $this->getFontBuffer($font);
13071 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
13072 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
13073 $popt['da'] = $fontstyle;
13074 // build appearance stream
13075 $popt['ap'] = array();
13076 $popt['ap']['n'] = array();
13077 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k);
13078 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
13079 $popt['ap']['n']['Yes'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(110).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
13080 $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(111).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
13081 // merge options
13082 $opt = array_merge($popt, $opt);
13083 // set remaining annotation data
13084 $opt['Subtype'] = 'Widget';
13085 $opt['ft'] = 'Btn';
13086 $opt['t'] = $name;
13087 if (TCPDF_STATIC::empty_string($onvalue)) {
13088 $onvalue = 'Yes';
13089 }
13090 $opt['opt'] = array($onvalue);
13091 if ($checked) {
13092 $opt['v'] = array('/Yes');
13093 $opt['as'] = 'Yes';
13094 } else {
13095 $opt['v'] = array('/Off');
13096 $opt['as'] = 'Off';
13097 }
13098 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
13099 if ($this->rtl) {
13100 $this->x -= $w;
13101 } else {
13102 $this->x += $w;
13103 }
13104 }
13105
13122 public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13123 if ($x === '') {
13124 $x = $this->x;
13125 }
13126 if ($y === '') {
13127 $y = $this->y;
13128 }
13129 // check page for no-write regions and adapt page margins if necessary
13130 list($x, $y) = $this->checkPageRegions($h, $x, $y);
13131 if ($js) {
13132 $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
13133 $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
13134 $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
13135 $this->javascript .= 'f'.$name.".highlight='push';\n";
13136 $this->javascript .= 'f'.$name.".print=false;\n";
13137 return;
13138 }
13139 // get default style
13140 $prop = array_merge($this->getFormDefaultProp(), $prop);
13141 $prop['Pushbutton'] = 'true';
13142 $prop['highlight'] = 'push';
13143 $prop['display'] = 'display.noPrint';
13144 // get annotation data
13145 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13146 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13147 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13148 $popt['da'] = $fontstyle;
13149 // build appearance stream
13150 $popt['ap'] = array();
13151 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13152 $tmpid = $this->startTemplate($w, $h, false);
13153 $bw = (2 / $this->k); // border width
13154 $border = array(
13155 'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13156 'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
13157 'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13158 'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
13159 $this->SetFillColor(204);
13160 $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
13161 $this->endTemplate();
13162 --$this->n;
13163 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13164 unset($this->xobjects[$tmpid]);
13165 $popt['ap']['n'] .= 'Q EMC';
13166 // set additional default options
13167 if (!isset($popt['mk'])) {
13168 $popt['mk'] = array();
13169 }
13170 $ann_obj_id = ($this->n + 1);
13171 if (!empty($action) AND !is_array($action)) {
13172 $ann_obj_id = ($this->n + 2);
13173 }
13174 $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
13175 $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
13176 $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
13177 // merge options
13178 $opt = array_merge($popt, $opt);
13179 // set remaining annotation data
13180 $opt['Subtype'] = 'Widget';
13181 $opt['ft'] = 'Btn';
13182 $opt['t'] = $caption;
13183 $opt['v'] = $name;
13184 if (!empty($action)) {
13185 if (is_array($action)) {
13186 // form action options as on section 12.7.5 of PDF32000_2008.
13187 $opt['aa'] = '/D <<';
13188 $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
13189 foreach ($action AS $key => $val) {
13190 if (($key == 'S') AND in_array($val, $bmode)) {
13191 $opt['aa'] .= ' /S /'.$val;
13192 } elseif (($key == 'F') AND (!empty($val))) {
13193 $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
13194 } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
13195 $opt['aa'] .= ' /Fields [';
13196 foreach ($val AS $field) {
13197 $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
13198 }
13199 $opt['aa'] .= ']';
13200 } elseif (($key == 'Flags')) {
13201 $ff = 0;
13202 if (is_array($val)) {
13203 foreach ($val AS $flag) {
13204 switch ($flag) {
13205 case 'Include/Exclude': {
13206 $ff += 1 << 0;
13207 break;
13208 }
13209 case 'IncludeNoValueFields': {
13210 $ff += 1 << 1;
13211 break;
13212 }
13213 case 'ExportFormat': {
13214 $ff += 1 << 2;
13215 break;
13216 }
13217 case 'GetMethod': {
13218 $ff += 1 << 3;
13219 break;
13220 }
13221 case 'SubmitCoordinates': {
13222 $ff += 1 << 4;
13223 break;
13224 }
13225 case 'XFDF': {
13226 $ff += 1 << 5;
13227 break;
13228 }
13229 case 'IncludeAppendSaves': {
13230 $ff += 1 << 6;
13231 break;
13232 }
13233 case 'IncludeAnnotations': {
13234 $ff += 1 << 7;
13235 break;
13236 }
13237 case 'SubmitPDF': {
13238 $ff += 1 << 8;
13239 break;
13240 }
13241 case 'CanonicalFormat': {
13242 $ff += 1 << 9;
13243 break;
13244 }
13245 case 'ExclNonUserAnnots': {
13246 $ff += 1 << 10;
13247 break;
13248 }
13249 case 'ExclFKey': {
13250 $ff += 1 << 11;
13251 break;
13252 }
13253 case 'EmbedForm': {
13254 $ff += 1 << 13;
13255 break;
13256 }
13257 }
13258 }
13259 } else {
13260 $ff = intval($val);
13261 }
13262 $opt['aa'] .= ' /Flags '.$ff;
13263 }
13264 }
13265 $opt['aa'] .= ' >>';
13266 } else {
13267 // Javascript action or raw action command
13268 $js_obj_id = $this->addJavascriptObject($action);
13269 $opt['aa'] = '/D '.$js_obj_id.' 0 R';
13270 }
13271 }
13272 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13273 if ($this->rtl) {
13274 $this->x -= $w;
13275 } else {
13276 $this->x += $w;
13277 }
13278 }
13279
13280 // --- END FORMS FIELDS ------------------------------------------------
13281
13289 protected function _putsignature() {
13290 if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
13291 return;
13292 }
13293 $sigobjid = ($this->sig_obj_id + 1);
13294 $out = $this->_getobj($sigobjid)."\n";
13295 $out .= '<< /Type /Sig';
13296 $out .= ' /Filter /Adobe.PPKLite';
13297 $out .= ' /SubFilter /adbe.pkcs7.detached';
13298 $out .= ' '.TCPDF_STATIC::$byterange_string;
13299 $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>';
13300 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
13301 $out .= ' /Reference ['; // array of signature reference dictionaries
13302 $out .= ' << /Type /SigRef';
13303 if ($this->signature_data['cert_type'] > 0) {
13304 $out .= ' /TransformMethod /DocMDP';
13305 $out .= ' /TransformParams <<';
13306 $out .= ' /Type /TransformParams';
13307 $out .= ' /P '.$this->signature_data['cert_type'];
13308 $out .= ' /V /1.2';
13309 } else {
13310 $out .= ' /TransformMethod /UR3';
13311 $out .= ' /TransformParams <<';
13312 $out .= ' /Type /TransformParams';
13313 $out .= ' /V /2.2';
13314 if (!TCPDF_STATIC::empty_string($this->ur['document'])) {
13315 $out .= ' /Document['.$this->ur['document'].']';
13316 }
13317 if (!TCPDF_STATIC::empty_string($this->ur['form'])) {
13318 $out .= ' /Form['.$this->ur['form'].']';
13319 }
13320 if (!TCPDF_STATIC::empty_string($this->ur['signature'])) {
13321 $out .= ' /Signature['.$this->ur['signature'].']';
13322 }
13323 if (!TCPDF_STATIC::empty_string($this->ur['annots'])) {
13324 $out .= ' /Annots['.$this->ur['annots'].']';
13325 }
13326 if (!TCPDF_STATIC::empty_string($this->ur['ef'])) {
13327 $out .= ' /EF['.$this->ur['ef'].']';
13328 }
13329 if (!TCPDF_STATIC::empty_string($this->ur['formex'])) {
13330 $out .= ' /FormEX['.$this->ur['formex'].']';
13331 }
13332 }
13333 $out .= ' >>'; // close TransformParams
13334 // optional digest data (values must be calculated and replaced later)
13335 //$out .= ' /Data ********** 0 R';
13336 //$out .= ' /DigestMethod/MD5';
13337 //$out .= ' /DigestLocation[********** 34]';
13338 //$out .= ' /DigestValue<********************************>';
13339 $out .= ' >>';
13340 $out .= ' ]'; // end of reference
13341 }
13342 if (isset($this->signature_data['info']['Name']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Name'])) {
13343 $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid);
13344 }
13345 if (isset($this->signature_data['info']['Location']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Location'])) {
13346 $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid);
13347 }
13348 if (isset($this->signature_data['info']['Reason']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Reason'])) {
13349 $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid);
13350 }
13351 if (isset($this->signature_data['info']['ContactInfo']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['ContactInfo'])) {
13352 $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid);
13353 }
13354 $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp);
13355 $out .= ' >>';
13356 $out .= "\n".'endobj';
13357 $this->_out($out);
13358 }
13359
13377 public function setUserRights(
13378 $enable=true,
13379 $document='/FullSave',
13380 $annots='/Create/Delete/Modify/Copy/Import/Export',
13381 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
13382 $signature='/Modify',
13383 $ef='/Create/Delete/Modify/Import',
13384 $formex='') {
13385 $this->ur['enabled'] = $enable;
13386 $this->ur['document'] = $document;
13387 $this->ur['annots'] = $annots;
13388 $this->ur['form'] = $form;
13389 $this->ur['signature'] = $signature;
13390 $this->ur['ef'] = $ef;
13391 $this->ur['formex'] = $formex;
13392 if (!$this->sign) {
13393 $this->setSignature('', '', '', '', 0, array());
13394 }
13395 }
13396
13414 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='') {
13415 // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13416 // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13417 // to convert pfx certificate to pem: openssl
13418 // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
13419 $this->sign = true;
13420 ++$this->n;
13421 $this->sig_obj_id = $this->n; // signature widget
13422 ++$this->n; // signature object ($this->sig_obj_id + 1)
13423 $this->signature_data = array();
13424 if (strlen($signing_cert) == 0) {
13425 $this->Error('Please provide a certificate file and password!');
13426 }
13427 if (strlen($private_key) == 0) {
13428 $private_key = $signing_cert;
13429 }
13430 $this->signature_data['signcert'] = $signing_cert;
13431 $this->signature_data['privkey'] = $private_key;
13432 $this->signature_data['password'] = $private_key_password;
13433 $this->signature_data['extracerts'] = $extracerts;
13434 $this->signature_data['cert_type'] = $cert_type;
13435 $this->signature_data['info'] = $info;
13436 $this->signature_data['approval'] = $approval;
13437 }
13438
13451 public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13452 $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13453 }
13454
13467 public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13468 ++$this->n;
13469 $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13470 }
13471
13485 protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13486 $sigapp = array();
13487 if (($page < 1) OR ($page > $this->numpages)) {
13488 $sigapp['page'] = $this->page;
13489 } else {
13490 $sigapp['page'] = intval($page);
13491 }
13492 if (empty($name)) {
13493 $sigapp['name'] = 'Signature';
13494 } else {
13495 $sigapp['name'] = $name;
13496 }
13497 $a = $x * $this->k;
13498 $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k);
13499 $c = $w * $this->k;
13500 $d = $h * $this->k;
13501 $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d));
13502 return $sigapp;
13503 }
13504
13517 public function setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='') {
13518 $this->tsa_data = array();
13519 if (!function_exists('curl_init')) {
13520 $this->Error('Please enable cURL PHP extension!');
13521 }
13522 if (strlen($tsa_host) == 0) {
13523 $this->Error('Please specify the host of Time Stamping Authority (TSA)!');
13524 }
13525 $this->tsa_data['tsa_host'] = $tsa_host;
13526 if (is_file($tsa_username)) {
13527 $this->tsa_data['tsa_auth'] = $tsa_username;
13528 } else {
13529 $this->tsa_data['tsa_username'] = $tsa_username;
13530 }
13531 $this->tsa_data['tsa_password'] = $tsa_password;
13532 $this->tsa_data['tsa_cert'] = $tsa_cert;
13533 $this->tsa_timestamp = true;
13534 }
13535
13545 protected function applyTSA($signature) {
13546 if (!$this->tsa_timestamp) {
13547 return $signature;
13548 }
13549 //@TODO: implement this feature
13550 return $signature;
13551 }
13552
13560 public function startPageGroup($page='') {
13561 if (empty($page)) {
13562 $page = $this->page + 1;
13563 }
13564 $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1;
13565 }
13566
13573 public function setStartingPageNumber($num=1) {
13574 $this->starting_page_number = max(0, intval($num));
13575 }
13576
13584 public function getAliasRightShift() {
13585 // calculate aproximatively the ratio between widths of aliases and replacements.
13586 $ref = '{'.TCPDF_STATIC::$alias_right_shift.'}{'.TCPDF_STATIC::$alias_tot_pages.'}{'.TCPDF_STATIC::$alias_num_page.'}';
13587 $rep = str_repeat(' ', $this->GetNumChars($ref));
13588 $wrep = $this->GetStringWidth($rep);
13589 if ($wrep > 0) {
13590 $wdiff = max(1, ($this->GetStringWidth($ref) / $wrep));
13591 } else {
13592 $wdiff = 1;
13593 }
13594 $sdiff = sprintf('%F', $wdiff);
13595 $alias = TCPDF_STATIC::$alias_right_shift.$sdiff.'}';
13596 if ($this->isUnicodeFont()) {
13597 $alias = '{'.$alias;
13598 }
13599 return $alias;
13600 }
13601
13610 public function getAliasNbPages() {
13611 if ($this->isUnicodeFont()) {
13612 return '{'.TCPDF_STATIC::$alias_tot_pages.'}';
13613 }
13615 }
13616
13625 public function getAliasNumPage() {
13626 if ($this->isUnicodeFont()) {
13627 return '{'.TCPDF_STATIC::$alias_num_page.'}';
13628 }
13630 }
13631
13640 public function getPageGroupAlias() {
13641 if ($this->isUnicodeFont()) {
13642 return '{'.TCPDF_STATIC::$alias_group_tot_pages.'}';
13643 }
13645 }
13646
13655 public function getPageNumGroupAlias() {
13656 if ($this->isUnicodeFont()) {
13657 return '{'.TCPDF_STATIC::$alias_group_num_page.'}';
13658 }
13660 }
13661
13668 public function getGroupPageNo() {
13669 return $this->pagegroups[$this->currpagegroup];
13670 }
13671
13678 public function getGroupPageNoFormatted() {
13680 }
13681
13688 public function PageNoFormatted() {
13689 return TCPDF_STATIC::formatPageNumber($this->PageNo());
13690 }
13691
13697 protected function _putocg() {
13698 if (empty($this->pdflayers)) {
13699 return;
13700 }
13701 foreach ($this->pdflayers as $key => $layer) {
13702 $this->pdflayers[$key]['objid'] = $this->_newobj();
13703 $out = '<< /Type /OCG';
13704 $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']);
13705 $out .= ' /Usage <<';
13706 if (isset($layer['print']) AND ($layer['print'] !== NULL)) {
13707 $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>';
13708 }
13709 $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>';
13710 $out .= ' >> >>';
13711 $out .= "\n".'endobj';
13712 $this->_out($out);
13713 }
13714 }
13715
13725 public function startLayer($name='', $print=true, $view=true, $lock=true) {
13726 if ($this->state != 2) {
13727 return;
13728 }
13729 $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1));
13730 if (empty($name)) {
13731 $name = $layer;
13732 } else {
13733 $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
13734 }
13735 $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view, 'lock' => $lock);
13736 $this->openMarkedContent = true;
13737 $this->_out('/OC /'.$layer.' BDC');
13738 }
13739
13745 public function endLayer() {
13746 if ($this->state != 2) {
13747 return;
13748 }
13749 if ($this->openMarkedContent) {
13750 // close existing open marked-content layer
13751 $this->_out('EMC');
13752 $this->openMarkedContent = false;
13753 }
13754 }
13755
13764 public function setVisibility($v) {
13765 if ($this->state != 2) {
13766 return;
13767 }
13768 $this->endLayer();
13769 switch($v) {
13770 case 'print': {
13771 $this->startLayer('Print', true, false);
13772 break;
13773 }
13774 case 'view':
13775 case 'screen': {
13776 $this->startLayer('View', false, true);
13777 break;
13778 }
13779 case 'all': {
13780 $this->_out('');
13781 break;
13782 }
13783 default: {
13784 $this->Error('Incorrect visibility: '.$v);
13785 break;
13786 }
13787 }
13788 }
13789
13797 protected function addExtGState($parms) {
13798 if ($this->pdfa_mode) {
13799 // transparencies are not allowed in PDF/A mode
13800 return;
13801 }
13802 // check if this ExtGState already exist
13803 foreach ($this->extgstates as $i => $ext) {
13804 if ($ext['parms'] == $parms) {
13805 if ($this->inxobj) {
13806 // we are inside an XObject template
13807 $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext;
13808 }
13809 // return reference to existing ExtGState
13810 return $i;
13811 }
13812 }
13813 $n = (count($this->extgstates) + 1);
13814 $this->extgstates[$n] = array('parms' => $parms);
13815 if ($this->inxobj) {
13816 // we are inside an XObject template
13817 $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n];
13818 }
13819 return $n;
13820 }
13821
13828 protected function setExtGState($gs) {
13829 if ($this->pdfa_mode OR ($this->state != 2)) {
13830 // transparency is not allowed in PDF/A mode
13831 return;
13832 }
13833 $this->_out(sprintf('/GS%d gs', $gs));
13834 }
13835
13841 protected function _putextgstates() {
13842 foreach ($this->extgstates as $i => $ext) {
13843 $this->extgstates[$i]['n'] = $this->_newobj();
13844 $out = '<< /Type /ExtGState';
13845 foreach ($ext['parms'] as $k => $v) {
13846 if (is_float($v)) {
13847 $v = sprintf('%F', $v);
13848 } elseif ($v === true) {
13849 $v = 'true';
13850 } elseif ($v === false) {
13851 $v = 'false';
13852 }
13853 $out .= ' /'.$k.' '.$v;
13854 }
13855 $out .= ' >>';
13856 $out .= "\n".'endobj';
13857 $this->_out($out);
13858 }
13859 }
13860
13870 public function setOverprint($stroking=true, $nonstroking='', $mode=0) {
13871 if ($this->state != 2) {
13872 return;
13873 }
13874 $stroking = $stroking ? true : false;
13875 if (TCPDF_STATIC::empty_string($nonstroking)) {
13876 // default value if not set
13877 $nonstroking = $stroking;
13878 } else {
13879 $nonstroking = $nonstroking ? true : false;
13880 }
13881 if (($mode != 0) AND ($mode != 1)) {
13882 $mode = 0;
13883 }
13884 $this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode);
13885 $gs = $this->addExtGState($this->overprint);
13886 $this->setExtGState($gs);
13887 }
13888
13896 public function getOverprint() {
13897 return $this->overprint;
13898 }
13899
13909 public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) {
13910 if ($this->pdfa_mode) {
13911 // transparency is not allowed in PDF/A mode
13912 return;
13913 }
13914 $stroking = floatval($stroking);
13915 if (TCPDF_STATIC::empty_string($nonstroking)) {
13916 // default value if not set
13917 $nonstroking = $stroking;
13918 } else {
13919 $nonstroking = floatval($nonstroking);
13920 }
13921 if ($bm[0] == '/') {
13922 // remove trailing slash
13923 $bm = substr($bm, 1);
13924 }
13925 if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
13926 $bm = 'Normal';
13927 }
13928 $ais = $ais ? true : false;
13929 $this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais);
13930 $gs = $this->addExtGState($this->alpha);
13931 $this->setExtGState($gs);
13932 }
13933
13941 public function getAlpha() {
13942 return $this->alpha;
13943 }
13944
13951 public function setJPEGQuality($quality) {
13952 if (($quality < 1) OR ($quality > 100)) {
13953 $quality = 75;
13954 }
13955 $this->jpeg_quality = intval($quality);
13956 }
13957
13964 public function setDefaultTableColumns($cols=4) {
13965 $this->default_table_columns = intval($cols);
13966 }
13967
13974 public function setCellHeightRatio($h) {
13975 $this->cell_height_ratio = $h;
13976 }
13977
13983 public function getCellHeightRatio() {
13985 }
13986
13993 public function setPDFVersion($version='1.7') {
13994 if ($this->pdfa_mode) {
13995 // PDF/A mode
13996 $this->PDFVersion = '1.4';
13997 } else {
13998 $this->PDFVersion = $version;
13999 }
14000 }
14001
14012 $this->viewer_preferences = $preferences;
14013 }
14014
14028 public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
14029 if (strpos($colors, 'ALLSPOT') !== false) {
14030 // expand spot colors
14031 $spot_colors = '';
14032 foreach ($this->spot_colors as $spot_color_name => $v) {
14033 $spot_colors .= ','.$spot_color_name;
14034 }
14035 if (!empty($spot_colors)) {
14036 $spot_colors = substr($spot_colors, 1);
14037 $colors = str_replace('ALLSPOT', $spot_colors, $colors);
14038 } else {
14039 $colors = str_replace('ALLSPOT', 'NONE', $colors);
14040 }
14041 }
14042 $bars = explode(',', $colors);
14043 $numbars = count($bars); // number of bars to print
14044 if ($numbars <= 0) {
14045 return;
14046 }
14047 // set bar measures
14048 if ($vertical) {
14049 $coords = array(0, 0, 0, 1);
14050 $wb = $w / $numbars; // bar width
14051 $hb = $h; // bar height
14052 $xd = $wb; // delta x
14053 $yd = 0; // delta y
14054 } else {
14055 $coords = array(1, 0, 0, 0);
14056 $wb = $w; // bar width
14057 $hb = $h / $numbars; // bar height
14058 $xd = 0; // delta x
14059 $yd = $hb; // delta y
14060 }
14061 $xb = $x;
14062 $yb = $y;
14063 foreach ($bars as $col) {
14064 switch ($col) {
14065 // set transition colors
14066 case 'A': { // BLACK (GRAYSCALE)
14067 $col_a = array(255);
14068 $col_b = array(0);
14069 break;
14070 }
14071 case 'W': { // WHITE (GRAYSCALE)
14072 $col_a = array(0);
14073 $col_b = array(255);
14074 break;
14075 }
14076 case 'R': { // RED (RGB)
14077 $col_a = array(255,255,255);
14078 $col_b = array(255,0,0);
14079 break;
14080 }
14081 case 'G': { // GREEN (RGB)
14082 $col_a = array(255,255,255);
14083 $col_b = array(0,255,0);
14084 break;
14085 }
14086 case 'B': { // BLUE (RGB)
14087 $col_a = array(255,255,255);
14088 $col_b = array(0,0,255);
14089 break;
14090 }
14091 case 'C': { // CYAN (CMYK)
14092 $col_a = array(0,0,0,0);
14093 $col_b = array(100,0,0,0);
14094 break;
14095 }
14096 case 'M': { // MAGENTA (CMYK)
14097 $col_a = array(0,0,0,0);
14098 $col_b = array(0,100,0,0);
14099 break;
14100 }
14101 case 'Y': { // YELLOW (CMYK)
14102 $col_a = array(0,0,0,0);
14103 $col_b = array(0,0,100,0);
14104 break;
14105 }
14106 case 'K': { // KEY - BLACK (CMYK)
14107 $col_a = array(0,0,0,0);
14108 $col_b = array(0,0,0,100);
14109 break;
14110 }
14111 case 'RGB': { // BLACK REGISTRATION (RGB)
14112 $col_a = array(255,255,255);
14113 $col_b = array(0,0,0);
14114 break;
14115 }
14116 case 'CMYK': { // BLACK REGISTRATION (CMYK)
14117 $col_a = array(0,0,0,0);
14118 $col_b = array(100,100,100,100);
14119 break;
14120 }
14121 case 'ALL': { // SPOT COLOR REGISTRATION
14122 $col_a = array(0,0,0,0,'None');
14123 $col_b = array(100,100,100,100,'All');
14124 break;
14125 }
14126 case 'NONE': { // SKIP THIS COLOR
14127 $col_a = array(0,0,0,0,'None');
14128 $col_b = array(0,0,0,0,'None');
14129 break;
14130 }
14131 default: { // SPECIFIC SPOT COLOR NAME
14132 $col_a = array(0,0,0,0,'None');
14133 $col_b = TCPDF_COLORS::getSpotColor($col, $this->spot_colors);
14134 if ($col_b === false) {
14135 // in case of error defaults to the registration color
14136 $col_b = array(100,100,100,100,'All');
14137 }
14138 break;
14139 }
14140 }
14141 if ($col != 'NONE') {
14142 if ($transition) {
14143 // color gradient
14144 $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
14145 } else {
14146 $this->SetFillColorArray($col_b);
14147 // colored rectangle
14148 $this->Rect($xb, $yb, $wb, $hb, 'F', array());
14149 }
14150 $xb += $xd;
14151 $yb += $yd;
14152 }
14153 }
14154 }
14155
14168 public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(100,100,100,100,'All')) {
14169 $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
14170 $type = strtoupper($type);
14171 $type = preg_replace('/[^A-Z\-\,]*/', '', $type);
14172 // split type in single components
14173 $type = str_replace('-', ',', $type);
14174 $type = str_replace('TL', 'T,L', $type);
14175 $type = str_replace('TR', 'T,R', $type);
14176 $type = str_replace('BL', 'F,L', $type);
14177 $type = str_replace('BR', 'F,R', $type);
14178 $type = str_replace('A', 'T,L', $type);
14179 $type = str_replace('B', 'T,R', $type);
14180 $type = str_replace('T,RO', 'BO', $type);
14181 $type = str_replace('C', 'F,L', $type);
14182 $type = str_replace('D', 'F,R', $type);
14183 $crops = explode(',', strtoupper($type));
14184 // remove duplicates
14185 $crops = array_unique($crops);
14186 $dw = ($w / 4); // horizontal space to leave before the intersection point
14187 $dh = ($h / 4); // vertical space to leave before the intersection point
14188 foreach ($crops as $crop) {
14189 switch ($crop) {
14190 case 'T':
14191 case 'TOP': {
14192 $x1 = $x;
14193 $y1 = ($y - $h);
14194 $x2 = $x;
14195 $y2 = ($y - $dh);
14196 break;
14197 }
14198 case 'F':
14199 case 'BOTTOM': {
14200 $x1 = $x;
14201 $y1 = ($y + $dh);
14202 $x2 = $x;
14203 $y2 = ($y + $h);
14204 break;
14205 }
14206 case 'L':
14207 case 'LEFT': {
14208 $x1 = ($x - $w);
14209 $y1 = $y;
14210 $x2 = ($x - $dw);
14211 $y2 = $y;
14212 break;
14213 }
14214 case 'R':
14215 case 'RIGHT': {
14216 $x1 = ($x + $dw);
14217 $y1 = $y;
14218 $x2 = ($x + $w);
14219 $y2 = $y;
14220 break;
14221 }
14222 }
14223 $this->Line($x1, $y1, $x2, $y2);
14224 }
14225 }
14226
14239 public function registrationMark($x, $y, $r, $double=false, $cola=array(100,100,100,100,'All'), $colb=array(0,0,0,0,'None')) {
14240 $line_style = array('width' => max((0.5 / $this->k),($r / 30)), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
14241 $this->SetFillColorArray($cola);
14242 $this->PieSector($x, $y, $r, 90, 180, 'F');
14243 $this->PieSector($x, $y, $r, 270, 360, 'F');
14244 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14245 if ($double) {
14246 $ri = $r * 0.5;
14247 $this->SetFillColorArray($colb);
14248 $this->PieSector($x, $y, $ri, 90, 180, 'F');
14249 $this->PieSector($x, $y, $ri, 270, 360, 'F');
14250 $this->SetFillColorArray($cola);
14251 $this->PieSector($x, $y, $ri, 0, 90, 'F');
14252 $this->PieSector($x, $y, $ri, 180, 270, 'F');
14253 $this->Circle($x, $y, $ri, 0, 360, 'C', $line_style, array(), 8);
14254 }
14255 }
14256
14266 public function registrationMarkCMYK($x, $y, $r) {
14267 // line width
14268 $lw = max((0.5 / $this->k),($r / 8));
14269 // internal radius
14270 $ri = ($r * 0.6);
14271 // external radius
14272 $re = ($r * 1.3);
14273 // Cyan
14274 $this->SetFillColorArray(array(100,0,0,0));
14275 $this->PieSector($x, $y, $ri, 270, 360, 'F');
14276 // Magenta
14277 $this->SetFillColorArray(array(0,100,0,0));
14278 $this->PieSector($x, $y, $ri, 0, 90, 'F');
14279 // Yellow
14280 $this->SetFillColorArray(array(0,0,100,0));
14281 $this->PieSector($x, $y, $ri, 90, 180, 'F');
14282 // Key - black
14283 $this->SetFillColorArray(array(0,0,0,100));
14284 $this->PieSector($x, $y, $ri, 180, 270, 'F');
14285 // registration color
14286 $line_style = array('width' => $lw, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(100,100,100,100,'All'));
14287 $this->SetFillColorArray(array(100,100,100,100,'All'));
14288 // external circle
14289 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14290 // cross lines
14291 $this->Line($x, ($y - $re), $x, ($y - $ri));
14292 $this->Line($x, ($y + $ri), $x, ($y + $re));
14293 $this->Line(($x - $re), $y, ($x - $ri), $y);
14294 $this->Line(($x + $ri), $y, ($x + $re), $y);
14295 }
14296
14310 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
14311 $this->Clip($x, $y, $w, $h);
14312 $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14313 }
14314
14328 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
14329 $this->Clip($x, $y, $w, $h);
14330 $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14331 }
14332
14351 public function CoonsPatchMesh($x, $y, $w, $h, $col1=array(), $col2=array(), $col3=array(), $col4=array(), $coords=array(0.00,0.0,0.33,0.00,0.67,0.00,1.00,0.00,1.00,0.33,1.00,0.67,1.00,1.00,0.67,1.00,0.33,1.00,0.00,1.00,0.00,0.67,0.00,0.33), $coords_min=0, $coords_max=1, $antialias=false) {
14352 if ($this->pdfa_mode OR ($this->state != 2)) {
14353 return;
14354 }
14355 $this->Clip($x, $y, $w, $h);
14356 $n = count($this->gradients) + 1;
14357 $this->gradients[$n] = array();
14358 $this->gradients[$n]['type'] = 6; //coons patch mesh
14359 $this->gradients[$n]['coords'] = array();
14360 $this->gradients[$n]['antialias'] = $antialias;
14361 $this->gradients[$n]['colors'] = array();
14362 $this->gradients[$n]['transparency'] = false;
14363 //check the coords array if it is the simple array or the multi patch array
14364 if (!isset($coords[0]['f'])) {
14365 //simple array -> convert to multi patch array
14366 if (!isset($col1[1])) {
14367 $col1[1] = $col1[2] = $col1[0];
14368 }
14369 if (!isset($col2[1])) {
14370 $col2[1] = $col2[2] = $col2[0];
14371 }
14372 if (!isset($col3[1])) {
14373 $col3[1] = $col3[2] = $col3[0];
14374 }
14375 if (!isset($col4[1])) {
14376 $col4[1] = $col4[2] = $col4[0];
14377 }
14378 $patch_array[0]['f'] = 0;
14379 $patch_array[0]['points'] = $coords;
14380 $patch_array[0]['colors'][0]['r'] = $col1[0];
14381 $patch_array[0]['colors'][0]['g'] = $col1[1];
14382 $patch_array[0]['colors'][0]['b'] = $col1[2];
14383 $patch_array[0]['colors'][1]['r'] = $col2[0];
14384 $patch_array[0]['colors'][1]['g'] = $col2[1];
14385 $patch_array[0]['colors'][1]['b'] = $col2[2];
14386 $patch_array[0]['colors'][2]['r'] = $col3[0];
14387 $patch_array[0]['colors'][2]['g'] = $col3[1];
14388 $patch_array[0]['colors'][2]['b'] = $col3[2];
14389 $patch_array[0]['colors'][3]['r'] = $col4[0];
14390 $patch_array[0]['colors'][3]['g'] = $col4[1];
14391 $patch_array[0]['colors'][3]['b'] = $col4[2];
14392 } else {
14393 //multi patch array
14395 }
14396 $bpcd = 65535; //16 bits per coordinate
14397 //build the data stream
14398 $this->gradients[$n]['stream'] = '';
14399 $count_patch = count($patch_array);
14400 for ($i=0; $i < $count_patch; ++$i) {
14401 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
14402 $count_points = count($patch_array[$i]['points']);
14403 for ($j=0; $j < $count_points; ++$j) {
14404 //each point as 16 bit
14405 $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
14406 if ($patch_array[$i]['points'][$j] < 0) {
14407 $patch_array[$i]['points'][$j] = 0;
14408 }
14409 if ($patch_array[$i]['points'][$j] > $bpcd) {
14410 $patch_array[$i]['points'][$j] = $bpcd;
14411 }
14412 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
14413 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
14414 }
14415 $count_cols = count($patch_array[$i]['colors']);
14416 for ($j=0; $j < $count_cols; ++$j) {
14417 //each color component as 8 bit
14418 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
14419 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
14420 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
14421 }
14422 }
14423 //paint the gradient
14424 $this->_out('/Sh'.$n.' sh');
14425 //restore previous Graphic State
14426 $this->_outRestoreGraphicsState();
14427 if ($this->inxobj) {
14428 // we are inside an XObject template
14429 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14430 }
14431 }
14432
14443 protected function Clip($x, $y, $w, $h) {
14444 if ($this->state != 2) {
14445 return;
14446 }
14447 if ($this->rtl) {
14448 $x = $this->w - $x - $w;
14449 }
14450 //save current Graphic State
14451 $s = 'q';
14452 //set clipping area
14453 $s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
14454 //set up transformation matrix for gradient
14455 $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
14456 $this->_out($s);
14457 }
14458
14470 public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
14471 if ($this->pdfa_mode OR ($this->state != 2)) {
14472 return;
14473 }
14474 $n = count($this->gradients) + 1;
14475 $this->gradients[$n] = array();
14476 $this->gradients[$n]['type'] = $type;
14477 $this->gradients[$n]['coords'] = $coords;
14478 $this->gradients[$n]['antialias'] = $antialias;
14479 $this->gradients[$n]['colors'] = array();
14480 $this->gradients[$n]['transparency'] = false;
14481 // color space
14482 $numcolspace = count($stops[0]['color']);
14483 $bcolor = array_values($background);
14484 switch($numcolspace) {
14485 case 5: // SPOT
14486 case 4: { // CMYK
14487 $this->gradients[$n]['colspace'] = 'DeviceCMYK';
14488 if (!empty($background)) {
14489 $this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
14490 }
14491 break;
14492 }
14493 case 3: { // RGB
14494 $this->gradients[$n]['colspace'] = 'DeviceRGB';
14495 if (!empty($background)) {
14496 $this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
14497 }
14498 break;
14499 }
14500 case 1: { // GRAY SCALE
14501 $this->gradients[$n]['colspace'] = 'DeviceGray';
14502 if (!empty($background)) {
14503 $this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255);
14504 }
14505 break;
14506 }
14507 }
14508 $num_stops = count($stops);
14509 $last_stop_id = $num_stops - 1;
14510 foreach ($stops as $key => $stop) {
14511 $this->gradients[$n]['colors'][$key] = array();
14512 // offset represents a location along the gradient vector
14513 if (isset($stop['offset'])) {
14514 $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
14515 } else {
14516 if ($key == 0) {
14517 $this->gradients[$n]['colors'][$key]['offset'] = 0;
14518 } elseif ($key == $last_stop_id) {
14519 $this->gradients[$n]['colors'][$key]['offset'] = 1;
14520 } else {
14521 $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
14522 $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
14523 }
14524 }
14525 if (isset($stop['opacity'])) {
14526 $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
14527 if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) {
14528 $this->gradients[$n]['transparency'] = true;
14529 }
14530 } else {
14531 $this->gradients[$n]['colors'][$key]['opacity'] = 1;
14532 }
14533 // exponent for the exponential interpolation function
14534 if (isset($stop['exponent'])) {
14535 $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
14536 } else {
14537 $this->gradients[$n]['colors'][$key]['exponent'] = 1;
14538 }
14539 // set colors
14540 $color = array_values($stop['color']);
14541 switch($numcolspace) {
14542 case 5: // SPOT
14543 case 4: { // CMYK
14544 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
14545 break;
14546 }
14547 case 3: { // RGB
14548 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255);
14549 break;
14550 }
14551 case 1: { // GRAY SCALE
14552 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255);
14553 break;
14554 }
14555 }
14556 }
14557 if ($this->gradients[$n]['transparency']) {
14558 // paint luminosity gradient
14559 $this->_out('/TGS'.$n.' gs');
14560 }
14561 //paint the gradient
14562 $this->_out('/Sh'.$n.' sh');
14563 //restore previous Graphic State
14564 $this->_outRestoreGraphicsState();
14565 if ($this->inxobj) {
14566 // we are inside an XObject template
14567 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14568 }
14569 }
14570
14577 function _putshaders() {
14578 if ($this->pdfa_mode) {
14579 return;
14580 }
14581 $idt = count($this->gradients); //index for transparency gradients
14582 foreach ($this->gradients as $id => $grad) {
14583 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
14584 $fc = $this->_newobj();
14585 $out = '<<';
14586 $out .= ' /FunctionType 3';
14587 $out .= ' /Domain [0 1]';
14588 $functions = '';
14589 $bounds = '';
14590 $encode = '';
14591 $i = 1;
14592 $num_cols = count($grad['colors']);
14593 $lastcols = $num_cols - 1;
14594 for ($i = 1; $i < $num_cols; ++$i) {
14595 $functions .= ($fc + $i).' 0 R ';
14596 if ($i < $lastcols) {
14597 $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']);
14598 }
14599 $encode .= '0 1 ';
14600 }
14601 $out .= ' /Functions ['.trim($functions).']';
14602 $out .= ' /Bounds ['.trim($bounds).']';
14603 $out .= ' /Encode ['.trim($encode).']';
14604 $out .= ' >>';
14605 $out .= "\n".'endobj';
14606 $this->_out($out);
14607 for ($i = 1; $i < $num_cols; ++$i) {
14608 $this->_newobj();
14609 $out = '<<';
14610 $out .= ' /FunctionType 2';
14611 $out .= ' /Domain [0 1]';
14612 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
14613 $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
14614 $out .= ' /N '.$grad['colors'][$i]['exponent'];
14615 $out .= ' >>';
14616 $out .= "\n".'endobj';
14617 $this->_out($out);
14618 }
14619 // set transparency functions
14620 if ($grad['transparency']) {
14621 $ft = $this->_newobj();
14622 $out = '<<';
14623 $out .= ' /FunctionType 3';
14624 $out .= ' /Domain [0 1]';
14625 $functions = '';
14626 $i = 1;
14627 $num_cols = count($grad['colors']);
14628 for ($i = 1; $i < $num_cols; ++$i) {
14629 $functions .= ($ft + $i).' 0 R ';
14630 }
14631 $out .= ' /Functions ['.trim($functions).']';
14632 $out .= ' /Bounds ['.trim($bounds).']';
14633 $out .= ' /Encode ['.trim($encode).']';
14634 $out .= ' >>';
14635 $out .= "\n".'endobj';
14636 $this->_out($out);
14637 for ($i = 1; $i < $num_cols; ++$i) {
14638 $this->_newobj();
14639 $out = '<<';
14640 $out .= ' /FunctionType 2';
14641 $out .= ' /Domain [0 1]';
14642 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
14643 $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
14644 $out .= ' /N '.$grad['colors'][$i]['exponent'];
14645 $out .= ' >>';
14646 $out .= "\n".'endobj';
14647 $this->_out($out);
14648 }
14649 }
14650 }
14651 // set shading object
14652 $this->_newobj();
14653 $out = '<< /ShadingType '.$grad['type'];
14654 if (isset($grad['colspace'])) {
14655 $out .= ' /ColorSpace /'.$grad['colspace'];
14656 } else {
14657 $out .= ' /ColorSpace /DeviceRGB';
14658 }
14659 if (isset($grad['background']) AND !empty($grad['background'])) {
14660 $out .= ' /Background ['.$grad['background'].']';
14661 }
14662 if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
14663 $out .= ' /AntiAlias true';
14664 }
14665 if ($grad['type'] == 2) {
14666 $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
14667 $out .= ' /Domain [0 1]';
14668 $out .= ' /Function '.$fc.' 0 R';
14669 $out .= ' /Extend [true true]';
14670 $out .= ' >>';
14671 } elseif ($grad['type'] == 3) {
14672 //x0, y0, r0, x1, y1, r1
14673 //at this this time radius of inner circle is 0
14674 $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
14675 $out .= ' /Domain [0 1]';
14676 $out .= ' /Function '.$fc.' 0 R';
14677 $out .= ' /Extend [true true]';
14678 $out .= ' >>';
14679 } elseif ($grad['type'] == 6) {
14680 $out .= ' /BitsPerCoordinate 16';
14681 $out .= ' /BitsPerComponent 8';
14682 $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
14683 $out .= ' /BitsPerFlag 8';
14684 $stream = $this->_getrawstream($grad['stream']);
14685 $out .= ' /Length '.strlen($stream);
14686 $out .= ' >>';
14687 $out .= ' stream'."\n".$stream."\n".'endstream';
14688 }
14689 $out .= "\n".'endobj';
14690 $this->_out($out);
14691 if ($grad['transparency']) {
14692 $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
14693 $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
14694 }
14695 $this->gradients[$id]['id'] = $this->n;
14696 // set pattern object
14697 $this->_newobj();
14698 $out = '<< /Type /Pattern /PatternType 2';
14699 $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
14700 $out .= ' >>';
14701 $out .= "\n".'endobj';
14702 $this->_out($out);
14703 $this->gradients[$id]['pattern'] = $this->n;
14704 // set shading and pattern for transparency mask
14705 if ($grad['transparency']) {
14706 // luminosity pattern
14707 $idgs = $id + $idt;
14708 $this->_newobj();
14709 $this->_out($shading_transparency);
14710 $this->gradients[$idgs]['id'] = $this->n;
14711 $this->_newobj();
14712 $out = '<< /Type /Pattern /PatternType 2';
14713 $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
14714 $out .= ' >>';
14715 $out .= "\n".'endobj';
14716 $this->_out($out);
14717 $this->gradients[$idgs]['pattern'] = $this->n;
14718 // luminosity XObject
14719 $oid = $this->_newobj();
14720 $this->xobjects['LX'.$oid] = array('n' => $oid);
14721 $filter = '';
14722 $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
14723 if ($this->compress) {
14724 $filter = ' /Filter /FlateDecode';
14725 $stream = gzcompress($stream);
14726 }
14727 $stream = $this->_getrawstream($stream);
14728 $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
14729 $out .= ' /Length '.strlen($stream);
14730 $rect = sprintf('%F %F', $this->wPt, $this->hPt);
14731 $out .= ' /BBox [0 0 '.$rect.']';
14732 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
14733 $out .= ' /Resources <<';
14734 $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
14735 $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
14736 $out .= ' >>';
14737 $out .= ' >> ';
14738 $out .= ' stream'."\n".$stream."\n".'endstream';
14739 $out .= "\n".'endobj';
14740 $this->_out($out);
14741 // SMask
14742 $this->_newobj();
14743 $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj';
14744 $this->_out($out);
14745 // ExtGState
14746 $this->_newobj();
14747 $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj';
14748 $this->_out($out);
14749 $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
14750 }
14751 }
14752 }
14753
14769 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
14770 $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
14771 }
14772
14790 public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
14791 if ($this->state != 2) {
14792 return;
14793 }
14794 if ($this->rtl) {
14795 $xc = ($this->w - $xc);
14796 }
14798 if ($op == 'f') {
14799 $line_style = array();
14800 }
14801 if ($cw) {
14802 $d = $b;
14803 $b = (360 - $a + $o);
14804 $a = (360 - $d + $o);
14805 } else {
14806 $b += $o;
14807 $a += $o;
14808 }
14809 $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
14810 $this->_out($op);
14811 }
14812
14834 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
14835 if ($this->state != 2) {
14836 return;
14837 }
14838 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
14839 // convert EPS to raster image using GD or ImageMagick libraries
14840 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14841 }
14842 if ($x === '') {
14843 $x = $this->x;
14844 }
14845 if ($y === '') {
14846 $y = $this->y;
14847 }
14848 // check page for no-write regions and adapt page margins if necessary
14849 list($x, $y) = $this->checkPageRegions($h, $x, $y);
14850 $k = $this->k;
14851 if ($file[0] === '@') { // image from string
14852 $data = substr($file, 1);
14853 } else { // EPS/AI file
14855 }
14856 if ($data === FALSE) {
14857 $this->Error('EPS file not found: '.$file);
14858 }
14859 $regs = array();
14860 // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
14861 preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
14862 if (count($regs) > 1) {
14863 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
14864 if (strpos($version_str, 'Adobe Illustrator') !== false) {
14865 $versexp = explode(' ', $version_str);
14866 $version = (float)array_pop($versexp);
14867 if ($version >= 9) {
14868 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
14869 }
14870 }
14871 }
14872 // strip binary bytes in front of PS-header
14873 $start = strpos($data, '%!PS-Adobe');
14874 if ($start > 0) {
14875 $data = substr($data, $start);
14876 }
14877 // find BoundingBox params
14878 preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
14879 if (count($regs) > 1) {
14880 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
14881 } else {
14882 $this->Error('No BoundingBox found in EPS/AI file: '.$file);
14883 }
14884 $start = strpos($data, '%%EndSetup');
14885 if ($start === false) {
14886 $start = strpos($data, '%%EndProlog');
14887 }
14888 if ($start === false) {
14889 $start = strpos($data, '%%BoundingBox');
14890 }
14891 $data = substr($data, $start);
14892 $end = strpos($data, '%%PageTrailer');
14893 if ($end===false) {
14894 $end = strpos($data, 'showpage');
14895 }
14896 if ($end) {
14897 $data = substr($data, 0, $end);
14898 }
14899 // calculate image width and height on document
14900 if (($w <= 0) AND ($h <= 0)) {
14901 $w = ($x2 - $x1) / $k;
14902 $h = ($y2 - $y1) / $k;
14903 } elseif ($w <= 0) {
14904 $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
14905 } elseif ($h <= 0) {
14906 $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
14907 }
14908 // fit the image on available space
14909 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
14910 if ($this->rasterize_vector_images) {
14911 // convert EPS to raster image using GD or ImageMagick libraries
14912 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14913 }
14914 // set scaling factors
14915 $scale_x = $w / (($x2 - $x1) / $k);
14916 $scale_y = $h / (($y2 - $y1) / $k);
14917 // set alignment
14918 $this->img_rb_y = $y + $h;
14919 // set alignment
14920 if ($this->rtl) {
14921 if ($palign == 'L') {
14922 $ximg = $this->lMargin;
14923 } elseif ($palign == 'C') {
14924 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
14925 } elseif ($palign == 'R') {
14926 $ximg = $this->w - $this->rMargin - $w;
14927 } else {
14928 $ximg = $x - $w;
14929 }
14930 $this->img_rb_x = $ximg;
14931 } else {
14932 if ($palign == 'L') {
14933 $ximg = $this->lMargin;
14934 } elseif ($palign == 'C') {
14935 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
14936 } elseif ($palign == 'R') {
14937 $ximg = $this->w - $this->rMargin - $w;
14938 } else {
14939 $ximg = $x;
14940 }
14941 $this->img_rb_x = $ximg + $w;
14942 }
14943 if ($useBoundingBox) {
14944 $dx = $ximg * $k - $x1;
14945 $dy = $y * $k - $y1;
14946 } else {
14947 $dx = $ximg * $k;
14948 $dy = $y * $k;
14949 }
14950 // save the current graphic state
14951 $this->_out('q'.$this->epsmarker);
14952 // translate
14953 $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
14954 // scale
14955 if (isset($scale_x)) {
14956 $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
14957 }
14958 // handle pc/unix/mac line endings
14959 $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY);
14960 $u=0;
14961 $cnt = count($lines);
14962 for ($i=0; $i < $cnt; ++$i) {
14963 $line = $lines[$i];
14964 if (($line == '') OR ($line[0] == '%')) {
14965 continue;
14966 }
14967 $len = strlen($line);
14968 // check for spot color names
14969 $color_name = '';
14970 if (strcasecmp('x', substr(trim($line), -1)) == 0) {
14971 if (preg_match('/\‍([^\‍)]*\‍)/', $line, $matches) > 0) {
14972 // extract spot color name
14973 $color_name = $matches[0];
14974 // remove color name from string
14975 $line = str_replace(' '.$color_name, '', $line);
14976 // remove pharentesis from color name
14977 $color_name = substr($color_name, 1, -1);
14978 }
14979 }
14980 $chunks = explode(' ', $line);
14981 $cmd = trim(array_pop($chunks));
14982 // RGB
14983 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
14984 $b = array_pop($chunks);
14985 $g = array_pop($chunks);
14986 $r = array_pop($chunks);
14987 $this->_out(''.$r.' '.$g.' '.$b.' '.($cmd=='Xa'?'rg':'RG')); //substr($line, 0, -2).'rg' -> in EPS (AI8): c m y k r g b rg!
14988 continue;
14989 }
14990 $skip = false;
14991 if ($fixoutvals) {
14992 // check for values outside the bounding box
14993 switch ($cmd) {
14994 case 'm':
14995 case 'l':
14996 case 'L': {
14997 // skip values outside bounding box
14998 foreach ($chunks as $key => $val) {
14999 if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) {
15000 $skip = true;
15001 } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) {
15002 $skip = true;
15003 }
15004 }
15005 }
15006 }
15007 }
15008 switch ($cmd) {
15009 case 'm':
15010 case 'l':
15011 case 'v':
15012 case 'y':
15013 case 'c':
15014 case 'k':
15015 case 'K':
15016 case 'g':
15017 case 'G':
15018 case 's':
15019 case 'S':
15020 case 'J':
15021 case 'j':
15022 case 'w':
15023 case 'M':
15024 case 'd':
15025 case 'n': {
15026 if ($skip) {
15027 break;
15028 }
15029 $this->_out($line);
15030 break;
15031 }
15032 case 'x': {// custom fill color
15033 if (empty($color_name)) {
15034 // CMYK color
15035 list($col_c, $col_m, $col_y, $col_k) = $chunks;
15036 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
15037 } else {
15038 // Spot Color (CMYK + tint)
15039 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15040 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15041 $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15042 $this->_out($color_cmd);
15043 }
15044 break;
15045 }
15046 case 'X': { // custom stroke color
15047 if (empty($color_name)) {
15048 // CMYK color
15049 list($col_c, $col_m, $col_y, $col_k) = $chunks;
15050 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
15051 } else {
15052 // Spot Color (CMYK + tint)
15053 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15054 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15055 $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15056 $this->_out($color_cmd);
15057 }
15058 break;
15059 }
15060 case 'Y':
15061 case 'N':
15062 case 'V':
15063 case 'L':
15064 case 'C': {
15065 if ($skip) {
15066 break;
15067 }
15068 $line[($len - 1)] = strtolower($cmd);
15069 $this->_out($line);
15070 break;
15071 }
15072 case 'b':
15073 case 'B': {
15074 $this->_out($cmd . '*');
15075 break;
15076 }
15077 case 'f':
15078 case 'F': {
15079 if ($u > 0) {
15080 $isU = false;
15081 $max = min(($i + 5), $cnt);
15082 for ($j = ($i + 1); $j < $max; ++$j) {
15083 $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
15084 }
15085 if ($isU) {
15086 $this->_out('f*');
15087 }
15088 } else {
15089 $this->_out('f*');
15090 }
15091 break;
15092 }
15093 case '*u': {
15094 ++$u;
15095 break;
15096 }
15097 case '*U': {
15098 --$u;
15099 break;
15100 }
15101 }
15102 }
15103 // restore previous graphic state
15104 $this->_out($this->epsmarker.'Q');
15105 if (!empty($border)) {
15106 $bx = $this->x;
15107 $by = $this->y;
15108 $this->x = $ximg;
15109 if ($this->rtl) {
15110 $this->x += $w;
15111 }
15112 $this->y = $y;
15113 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
15114 $this->x = $bx;
15115 $this->y = $by;
15116 }
15117 if ($link) {
15118 $this->Link($ximg, $y, $w, $h, $link, 0);
15119 }
15120 // set pointer to align the next text/objects
15121 switch($align) {
15122 case 'T':{
15123 $this->y = $y;
15124 $this->x = $this->img_rb_x;
15125 break;
15126 }
15127 case 'M':{
15128 $this->y = $y + round($h/2);
15129 $this->x = $this->img_rb_x;
15130 break;
15131 }
15132 case 'B':{
15133 $this->y = $this->img_rb_y;
15134 $this->x = $this->img_rb_x;
15135 break;
15136 }
15137 case 'N':{
15138 $this->SetY($this->img_rb_y);
15139 break;
15140 }
15141 default:{
15142 break;
15143 }
15144 }
15145 $this->endlinex = $this->img_rb_x;
15146 }
15147
15153 public function setBarcode($bc='') {
15154 $this->barcode = $bc;
15155 }
15156
15163 public function getBarcode() {
15164 return $this->barcode;
15165 }
15166
15197 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
15198 if (TCPDF_STATIC::empty_string(trim($code))) {
15199 return;
15200 }
15201 require_once(dirname(__FILE__).'/tcpdf_barcodes_1d.php');
15202 // save current graphic settings
15203 $gvars = $this->getGraphicVars();
15204 // create new barcode object
15206 $arrcode = $barcodeobj->getBarcodeArray();
15207 if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] <= 0)) {
15208 $this->Error('Error in 1D barcode string');
15209 }
15210 if ($arrcode['maxh'] <= 0) {
15211 $arrcode['maxh'] = 1;
15212 }
15213 // set default values
15214 if (!isset($style['position'])) {
15215 $style['position'] = '';
15216 } elseif ($style['position'] == 'S') {
15217 // keep this for backward compatibility
15218 $style['position'] = '';
15219 $style['stretch'] = true;
15220 }
15221 if (!isset($style['fitwidth'])) {
15222 if (!isset($style['stretch'])) {
15223 $style['fitwidth'] = true;
15224 } else {
15225 $style['fitwidth'] = false;
15226 }
15227 }
15228 if ($style['fitwidth']) {
15229 // disable stretch
15230 $style['stretch'] = false;
15231 }
15232 if (!isset($style['stretch'])) {
15233 if (($w === '') OR ($w <= 0)) {
15234 $style['stretch'] = false;
15235 } else {
15236 $style['stretch'] = true;
15237 }
15238 }
15239 if (!isset($style['fgcolor'])) {
15240 $style['fgcolor'] = array(0,0,0); // default black
15241 }
15242 if (!isset($style['bgcolor'])) {
15243 $style['bgcolor'] = false; // default transparent
15244 }
15245 if (!isset($style['border'])) {
15246 $style['border'] = false;
15247 }
15248 $fontsize = 0;
15249 if (!isset($style['text'])) {
15250 $style['text'] = false;
15251 }
15252 if ($style['text'] AND isset($style['font'])) {
15253 if (isset($style['fontsize'])) {
15254 $fontsize = $style['fontsize'];
15255 }
15256 $this->SetFont($style['font'], '', $fontsize);
15257 }
15258 if (!isset($style['stretchtext'])) {
15259 $style['stretchtext'] = 4;
15260 }
15261 if ($x === '') {
15262 $x = $this->x;
15263 }
15264 if ($y === '') {
15265 $y = $this->y;
15266 }
15267 // check page for no-write regions and adapt page margins if necessary
15268 list($x, $y) = $this->checkPageRegions($h, $x, $y);
15269 if (($w === '') OR ($w <= 0)) {
15270 if ($this->rtl) {
15271 $w = $x - $this->lMargin;
15272 } else {
15273 $w = $this->w - $this->rMargin - $x;
15274 }
15275 }
15276 // padding
15277 if (!isset($style['padding'])) {
15278 $padding = 0;
15279 } elseif ($style['padding'] === 'auto') {
15280 $padding = 10 * ($w / ($arrcode['maxw'] + 20));
15281 } else {
15282 $padding = floatval($style['padding']);
15283 }
15284 // horizontal padding
15285 if (!isset($style['hpadding'])) {
15286 $hpadding = $padding;
15287 } elseif ($style['hpadding'] === 'auto') {
15288 $hpadding = 10 * ($w / ($arrcode['maxw'] + 20));
15289 } else {
15290 $hpadding = floatval($style['hpadding']);
15291 }
15292 // vertical padding
15293 if (!isset($style['vpadding'])) {
15294 $vpadding = $padding;
15295 } elseif ($style['vpadding'] === 'auto') {
15296 $vpadding = ($hpadding / 2);
15297 } else {
15298 $vpadding = floatval($style['vpadding']);
15299 }
15300 // calculate xres (single bar width)
15301 $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
15302 if ($style['stretch']) {
15303 $xres = $max_xres;
15304 } else {
15305 if (TCPDF_STATIC::empty_string($xres)) {
15306 $xres = (0.141 * $this->k); // default bar width = 0.4 mm
15307 }
15308 if ($xres > $max_xres) {
15309 // correct xres to fit on $w
15310 $xres = $max_xres;
15311 }
15312 if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
15313 OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
15314 $hpadding = 10 * $xres;
15315 if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
15316 $vpadding = ($hpadding / 2);
15317 }
15318 }
15319 }
15320 if ($style['fitwidth']) {
15321 $wold = $w;
15322 $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding));
15323 if (isset($style['cellfitalign'])) {
15324 switch ($style['cellfitalign']) {
15325 case 'L': {
15326 if ($this->rtl) {
15327 $x -= ($wold - $w);
15328 }
15329 break;
15330 }
15331 case 'R': {
15332 if (!$this->rtl) {
15333 $x += ($wold - $w);
15334 }
15335 break;
15336 }
15337 case 'C': {
15338 if ($this->rtl) {
15339 $x -= (($wold - $w) / 2);
15340 } else {
15341 $x += (($wold - $w) / 2);
15342 }
15343 break;
15344 }
15345 default : {
15346 break;
15347 }
15348 }
15349 }
15350 }
15351 $text_height = $this->getCellHeight($fontsize / $this->k);
15352 // height
15353 if (($h === '') OR ($h <= 0)) {
15354 // set default height
15355 $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height;
15356 }
15357 $barh = $h - $text_height - (2 * $vpadding);
15358 if ($barh <=0) {
15359 // try to reduce font or padding to fit barcode on available height
15360 if ($text_height > $h) {
15361 $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio));
15362 $text_height = $this->getCellHeight($fontsize / $this->k);
15363 $this->SetFont($style['font'], '', $fontsize);
15364 }
15365 if ($vpadding > 0) {
15366 $vpadding = (($h - $text_height) / 4);
15367 }
15368 $barh = $h - $text_height - (2 * $vpadding);
15369 }
15370 // fit the barcode on available space
15371 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15372 // set alignment
15373 $this->img_rb_y = $y + $h;
15374 // set alignment
15375 if ($this->rtl) {
15376 if ($style['position'] == 'L') {
15377 $xpos = $this->lMargin;
15378 } elseif ($style['position'] == 'C') {
15379 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15380 } elseif ($style['position'] == 'R') {
15381 $xpos = $this->w - $this->rMargin - $w;
15382 } else {
15383 $xpos = $x - $w;
15384 }
15385 $this->img_rb_x = $xpos;
15386 } else {
15387 if ($style['position'] == 'L') {
15388 $xpos = $this->lMargin;
15389 } elseif ($style['position'] == 'C') {
15390 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15391 } elseif ($style['position'] == 'R') {
15392 $xpos = $this->w - $this->rMargin - $w;
15393 } else {
15394 $xpos = $x;
15395 }
15396 $this->img_rb_x = $xpos + $w;
15397 }
15398 $xpos_rect = $xpos;
15399 if (!isset($style['align'])) {
15400 $style['align'] = 'C';
15401 }
15402 switch ($style['align']) {
15403 case 'L': {
15404 $xpos = $xpos_rect + $hpadding;
15405 break;
15406 }
15407 case 'R': {
15408 $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding;
15409 break;
15410 }
15411 case 'C':
15412 default : {
15413 $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2);
15414 break;
15415 }
15416 }
15417 $xpos_text = $xpos;
15418 // barcode is always printed in LTR direction
15419 $tempRTL = $this->rtl;
15420 $this->rtl = false;
15421 // print background color
15422 if ($style['bgcolor']) {
15423 $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15424 } elseif ($style['border']) {
15425 $this->Rect($xpos_rect, $y, $w, $h, 'D');
15426 }
15427 // set foreground color
15428 $this->SetDrawColorArray($style['fgcolor']);
15429 $this->SetTextColorArray($style['fgcolor']);
15430 // print bars
15431 foreach ($arrcode['bcode'] as $k => $v) {
15432 $bw = ($v['w'] * $xres);
15433 if ($v['t']) {
15434 // draw a vertical bar
15435 $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']);
15436 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
15437 }
15438 $xpos += $bw;
15439 }
15440 // print text
15441 if ($style['text']) {
15442 if (isset($style['label']) AND !TCPDF_STATIC::empty_string($style['label'])) {
15443 $label = $style['label'];
15444 } else {
15445 $label = $code;
15446 }
15447 $txtwidth = ($arrcode['maxw'] * $xres);
15448 if ($this->GetStringWidth($label) > $txtwidth) {
15449 $style['stretchtext'] = 2;
15450 }
15451 // print text
15452 $this->x = $xpos_text;
15453 $this->y = $y + $vpadding + $barh;
15454 $cellpadding = $this->cell_padding;
15455 $this->SetCellPadding(0);
15456 $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
15457 $this->cell_padding = $cellpadding;
15458 }
15459 // restore original direction
15460 $this->rtl = $tempRTL;
15461 // restore previous settings
15462 $this->setGraphicVars($gvars);
15463 // set pointer to align the next text/objects
15464 switch($align) {
15465 case 'T':{
15466 $this->y = $y;
15467 $this->x = $this->img_rb_x;
15468 break;
15469 }
15470 case 'M':{
15471 $this->y = $y + round($h / 2);
15472 $this->x = $this->img_rb_x;
15473 break;
15474 }
15475 case 'B':{
15476 $this->y = $this->img_rb_y;
15477 $this->x = $this->img_rb_x;
15478 break;
15479 }
15480 case 'N':{
15481 $this->SetY($this->img_rb_y);
15482 break;
15483 }
15484 default:{
15485 break;
15486 }
15487 }
15488 $this->endlinex = $this->img_rb_x;
15489 }
15490
15516 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) {
15517 if (TCPDF_STATIC::empty_string(trim($code))) {
15518 return;
15519 }
15520 require_once(dirname(__FILE__).'/tcpdf_barcodes_2d.php');
15521 // save current graphic settings
15522 $gvars = $this->getGraphicVars();
15523 // create new barcode object
15525 $arrcode = $barcodeobj->getBarcodeArray();
15526 if (($arrcode === false) OR empty($arrcode) OR !isset($arrcode['num_rows']) OR ($arrcode['num_rows'] == 0) OR !isset($arrcode['num_cols']) OR ($arrcode['num_cols'] == 0)) {
15527 $this->Error('Error in 2D barcode string');
15528 }
15529 // set default values
15530 if (!isset($style['position'])) {
15531 $style['position'] = '';
15532 }
15533 if (!isset($style['fgcolor'])) {
15534 $style['fgcolor'] = array(0,0,0); // default black
15535 }
15536 if (!isset($style['bgcolor'])) {
15537 $style['bgcolor'] = false; // default transparent
15538 }
15539 if (!isset($style['border'])) {
15540 $style['border'] = false;
15541 }
15542 // padding
15543 if (!isset($style['padding'])) {
15544 $style['padding'] = 0;
15545 } elseif ($style['padding'] === 'auto') {
15546 $style['padding'] = 4;
15547 }
15548 if (!isset($style['hpadding'])) {
15549 $style['hpadding'] = $style['padding'];
15550 } elseif ($style['hpadding'] === 'auto') {
15551 $style['hpadding'] = 4;
15552 }
15553 if (!isset($style['vpadding'])) {
15554 $style['vpadding'] = $style['padding'];
15555 } elseif ($style['vpadding'] === 'auto') {
15556 $style['vpadding'] = 4;
15557 }
15558 $hpad = (2 * $style['hpadding']);
15559 $vpad = (2 * $style['vpadding']);
15560 // cell (module) dimension
15561 if (!isset($style['module_width'])) {
15562 $style['module_width'] = 1; // width of a single module in points
15563 }
15564 if (!isset($style['module_height'])) {
15565 $style['module_height'] = 1; // height of a single module in points
15566 }
15567 if ($x === '') {
15568 $x = $this->x;
15569 }
15570 if ($y === '') {
15571 $y = $this->y;
15572 }
15573 // check page for no-write regions and adapt page margins if necessary
15574 list($x, $y) = $this->checkPageRegions($h, $x, $y);
15575 // number of barcode columns and rows
15576 $rows = $arrcode['num_rows'];
15577 $cols = $arrcode['num_cols'];
15578 if (($rows <= 0) || ($cols <= 0)){
15579 $this->Error('Error in 2D barcode string');
15580 }
15581 // module width and height
15582 $mw = $style['module_width'];
15583 $mh = $style['module_height'];
15584 if (($mw <= 0) OR ($mh <= 0)) {
15585 $this->Error('Error in 2D barcode string');
15586 }
15587 // get max dimensions
15588 if ($this->rtl) {
15589 $maxw = $x - $this->lMargin;
15590 } else {
15591 $maxw = $this->w - $this->rMargin - $x;
15592 }
15593 $maxh = ($this->h - $this->tMargin - $this->bMargin);
15594 $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad));
15595 $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad));
15596 if (!$distort) {
15597 if (($maxw * $ratioHW) > $maxh) {
15598 $maxw = $maxh * $ratioWH;
15599 }
15600 if (($maxh * $ratioWH) > $maxw) {
15601 $maxh = $maxw * $ratioHW;
15602 }
15603 }
15604 // set maximum dimensions
15605 if ($w > $maxw) {
15606 $w = $maxw;
15607 }
15608 if ($h > $maxh) {
15609 $h = $maxh;
15610 }
15611 // set dimensions
15612 if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
15613 $w = ($cols + $hpad) * ($mw / $this->k);
15614 $h = ($rows + $vpad) * ($mh / $this->k);
15615 } elseif (($w === '') OR ($w <= 0)) {
15616 $w = $h * $ratioWH;
15617 } elseif (($h === '') OR ($h <= 0)) {
15618 $h = $w * $ratioHW;
15619 }
15620 // barcode size (excluding padding)
15621 $bw = ($w * $cols) / ($cols + $hpad);
15622 $bh = ($h * $rows) / ($rows + $vpad);
15623 // dimension of single barcode cell unit
15624 $cw = $bw / $cols;
15625 $ch = $bh / $rows;
15626 if (!$distort) {
15627 if (($cw / $ch) > ($mw / $mh)) {
15628 // correct horizontal distortion
15629 $cw = $ch * $mw / $mh;
15630 $bw = $cw * $cols;
15631 $style['hpadding'] = ($w - $bw) / (2 * $cw);
15632 } else {
15633 // correct vertical distortion
15634 $ch = $cw * $mh / $mw;
15635 $bh = $ch * $rows;
15636 $style['vpadding'] = ($h - $bh) / (2 * $ch);
15637 }
15638 }
15639 // fit the barcode on available space
15640 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15641 // set alignment
15642 $this->img_rb_y = $y + $h;
15643 // set alignment
15644 if ($this->rtl) {
15645 if ($style['position'] == 'L') {
15646 $xpos = $this->lMargin;
15647 } elseif ($style['position'] == 'C') {
15648 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15649 } elseif ($style['position'] == 'R') {
15650 $xpos = $this->w - $this->rMargin - $w;
15651 } else {
15652 $xpos = $x - $w;
15653 }
15654 $this->img_rb_x = $xpos;
15655 } else {
15656 if ($style['position'] == 'L') {
15657 $xpos = $this->lMargin;
15658 } elseif ($style['position'] == 'C') {
15659 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15660 } elseif ($style['position'] == 'R') {
15661 $xpos = $this->w - $this->rMargin - $w;
15662 } else {
15663 $xpos = $x;
15664 }
15665 $this->img_rb_x = $xpos + $w;
15666 }
15667 $xstart = $xpos + ($style['hpadding'] * $cw);
15668 $ystart = $y + ($style['vpadding'] * $ch);
15669 // barcode is always printed in LTR direction
15670 $tempRTL = $this->rtl;
15671 $this->rtl = false;
15672 // print background color
15673 if ($style['bgcolor']) {
15674 $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15675 } elseif ($style['border']) {
15676 $this->Rect($xpos, $y, $w, $h, 'D');
15677 }
15678 // set foreground color
15679 $this->SetDrawColorArray($style['fgcolor']);
15680 // print barcode cells
15681 // for each row
15682 for ($r = 0; $r < $rows; ++$r) {
15683 $xr = $xstart;
15684 // for each column
15685 for ($c = 0; $c < $cols; ++$c) {
15686 if ($arrcode['bcode'][$r][$c] == 1) {
15687 // draw a single barcode cell
15688 $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
15689 }
15690 $xr += $cw;
15691 }
15692 $ystart += $ch;
15693 }
15694 // restore original direction
15695 $this->rtl = $tempRTL;
15696 // restore previous settings
15697 $this->setGraphicVars($gvars);
15698 // set pointer to align the next text/objects
15699 switch($align) {
15700 case 'T':{
15701 $this->y = $y;
15702 $this->x = $this->img_rb_x;
15703 break;
15704 }
15705 case 'M':{
15706 $this->y = $y + round($h/2);
15707 $this->x = $this->img_rb_x;
15708 break;
15709 }
15710 case 'B':{
15711 $this->y = $this->img_rb_y;
15712 $this->x = $this->img_rb_x;
15713 break;
15714 }
15715 case 'N':{
15716 $this->SetY($this->img_rb_y);
15717 break;
15718 }
15719 default:{
15720 break;
15721 }
15722 }
15723 $this->endlinex = $this->img_rb_x;
15724 }
15725
15745 public function getMargins() {
15746 $ret = array(
15747 'left' => $this->lMargin,
15748 'right' => $this->rMargin,
15749 'top' => $this->tMargin,
15750 'bottom' => $this->bMargin,
15751 'header' => $this->header_margin,
15752 'footer' => $this->footer_margin,
15753 'cell' => $this->cell_padding,
15754 'padding_left' => $this->cell_padding['L'],
15755 'padding_top' => $this->cell_padding['T'],
15756 'padding_right' => $this->cell_padding['R'],
15757 'padding_bottom' => $this->cell_padding['B']
15758 );
15759 return $ret;
15760 }
15761
15772 public function getOriginalMargins() {
15773 $ret = array(
15774 'left' => $this->original_lMargin,
15775 'right' => $this->original_rMargin
15776 );
15777 return $ret;
15778 }
15779
15786 public function getFontSize() {
15787 return $this->FontSize;
15788 }
15789
15796 public function getFontSizePt() {
15797 return $this->FontSizePt;
15798 }
15799
15806 public function getFontFamily() {
15807 return $this->FontFamily;
15808 }
15809
15816 public function getFontStyle() {
15817 return $this->FontStyle;
15818 }
15819
15832 public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
15833 return TCPDF_STATIC::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces);
15834 }
15835
15843 protected function getCSSBorderWidth($width) {
15844 if ($width == 'thin') {
15845 $width = (2 / $this->k);
15846 } elseif ($width == 'medium') {
15847 $width = (4 / $this->k);
15848 } elseif ($width == 'thick') {
15849 $width = (6 / $this->k);
15850 } else {
15851 $width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
15852 }
15853 return $width;
15854 }
15855
15863 protected function getCSSBorderDashStyle($style) {
15864 switch (strtolower($style)) {
15865 case 'none':
15866 case 'hidden': {
15867 $dash = -1;
15868 break;
15869 }
15870 case 'dotted': {
15871 $dash = 1;
15872 break;
15873 }
15874 case 'dashed': {
15875 $dash = 3;
15876 break;
15877 }
15878 case 'double':
15879 case 'groove':
15880 case 'ridge':
15881 case 'inset':
15882 case 'outset':
15883 case 'solid':
15884 default: {
15885 $dash = 0;
15886 break;
15887 }
15888 }
15889 return $dash;
15890 }
15891
15899 protected function getCSSBorderStyle($cssborder) {
15900 $bprop = preg_split('/[\s]+/', trim($cssborder));
15901 $border = array(); // value to be returned
15902 switch (count($bprop)) {
15903 case 3: {
15904 $width = $bprop[0];
15905 $style = $bprop[1];
15906 $color = $bprop[2];
15907 break;
15908 }
15909 case 2: {
15910 $width = 'medium';
15911 $style = $bprop[0];
15912 $color = $bprop[1];
15913 break;
15914 }
15915 case 1: {
15916 $width = 'medium';
15917 $style = $bprop[0];
15918 $color = 'black';
15919 break;
15920 }
15921 default: {
15922 $width = 'medium';
15923 $style = 'solid';
15924 $color = 'black';
15925 break;
15926 }
15927 }
15928 if ($style == 'none') {
15929 return array();
15930 }
15931 $border['cap'] = 'square';
15932 $border['join'] = 'miter';
15933 $border['dash'] = $this->getCSSBorderDashStyle($style);
15934 if ($border['dash'] < 0) {
15935 return array();
15936 }
15937 $border['width'] = $this->getCSSBorderWidth($width);
15938 $border['color'] = TCPDF_COLORS::convertHTMLColorToDec($color, $this->spot_colors);
15939 return $border;
15940 }
15941
15950 public function getCSSPadding($csspadding, $width=0) {
15951 $padding = preg_split('/[\s]+/', trim($csspadding));
15952 $cell_padding = array(); // value to be returned
15953 switch (count($padding)) {
15954 case 4: {
15955 $cell_padding['T'] = $padding[0];
15956 $cell_padding['R'] = $padding[1];
15957 $cell_padding['B'] = $padding[2];
15958 $cell_padding['L'] = $padding[3];
15959 break;
15960 }
15961 case 3: {
15962 $cell_padding['T'] = $padding[0];
15963 $cell_padding['R'] = $padding[1];
15964 $cell_padding['B'] = $padding[2];
15965 $cell_padding['L'] = $padding[1];
15966 break;
15967 }
15968 case 2: {
15969 $cell_padding['T'] = $padding[0];
15970 $cell_padding['R'] = $padding[1];
15971 $cell_padding['B'] = $padding[0];
15972 $cell_padding['L'] = $padding[1];
15973 break;
15974 }
15975 case 1: {
15976 $cell_padding['T'] = $padding[0];
15977 $cell_padding['R'] = $padding[0];
15978 $cell_padding['B'] = $padding[0];
15979 $cell_padding['L'] = $padding[0];
15980 break;
15981 }
15982 default: {
15983 return $this->cell_padding;
15984 }
15985 }
15986 if ($width == 0) {
15987 $width = $this->w - $this->lMargin - $this->rMargin;
15988 }
15989 $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
15990 $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
15991 $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
15992 $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
15993 return $cell_padding;
15994 }
15995
16004 public function getCSSMargin($cssmargin, $width=0) {
16005 $margin = preg_split('/[\s]+/', trim($cssmargin));
16006 $cell_margin = array(); // value to be returned
16007 switch (count($margin)) {
16008 case 4: {
16009 $cell_margin['T'] = $margin[0];
16010 $cell_margin['R'] = $margin[1];
16011 $cell_margin['B'] = $margin[2];
16012 $cell_margin['L'] = $margin[3];
16013 break;
16014 }
16015 case 3: {
16016 $cell_margin['T'] = $margin[0];
16017 $cell_margin['R'] = $margin[1];
16018 $cell_margin['B'] = $margin[2];
16019 $cell_margin['L'] = $margin[1];
16020 break;
16021 }
16022 case 2: {
16023 $cell_margin['T'] = $margin[0];
16024 $cell_margin['R'] = $margin[1];
16025 $cell_margin['B'] = $margin[0];
16026 $cell_margin['L'] = $margin[1];
16027 break;
16028 }
16029 case 1: {
16030 $cell_margin['T'] = $margin[0];
16031 $cell_margin['R'] = $margin[0];
16032 $cell_margin['B'] = $margin[0];
16033 $cell_margin['L'] = $margin[0];
16034 break;
16035 }
16036 default: {
16037 return $this->cell_margin;
16038 }
16039 }
16040 if ($width == 0) {
16041 $width = $this->w - $this->lMargin - $this->rMargin;
16042 }
16043 $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
16044 $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
16045 $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
16046 $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
16047 return $cell_margin;
16048 }
16049
16058 public function getCSSBorderMargin($cssbspace, $width=0) {
16059 $space = preg_split('/[\s]+/', trim($cssbspace));
16060 $border_spacing = array(); // value to be returned
16061 switch (count($space)) {
16062 case 2: {
16063 $border_spacing['H'] = $space[0];
16064 $border_spacing['V'] = $space[1];
16065 break;
16066 }
16067 case 1: {
16068 $border_spacing['H'] = $space[0];
16069 $border_spacing['V'] = $space[0];
16070 break;
16071 }
16072 default: {
16073 return array('H' => 0, 'V' => 0);
16074 }
16075 }
16076 if ($width == 0) {
16077 $width = $this->w - $this->lMargin - $this->rMargin;
16078 }
16079 $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
16080 $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
16081 return $border_spacing;
16082 }
16083
16092 protected function getCSSFontSpacing($spacing, $parent=0) {
16093 $val = 0; // value to be returned
16094 $spacing = trim($spacing);
16095 switch ($spacing) {
16096 case 'normal': {
16097 $val = 0;
16098 break;
16099 }
16100 case 'inherit': {
16101 if ($parent == 'normal') {
16102 $val = 0;
16103 } else {
16104 $val = $parent;
16105 }
16106 break;
16107 }
16108 default: {
16109 $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
16110 }
16111 }
16112 return $val;
16113 }
16114
16123 protected function getCSSFontStretching($stretch, $parent=100) {
16124 $val = 100; // value to be returned
16125 $stretch = trim($stretch);
16126 switch ($stretch) {
16127 case 'ultra-condensed': {
16128 $val = 40;
16129 break;
16130 }
16131 case 'extra-condensed': {
16132 $val = 55;
16133 break;
16134 }
16135 case 'condensed': {
16136 $val = 70;
16137 break;
16138 }
16139 case 'semi-condensed': {
16140 $val = 85;
16141 break;
16142 }
16143 case 'normal': {
16144 $val = 100;
16145 break;
16146 }
16147 case 'semi-expanded': {
16148 $val = 115;
16149 break;
16150 }
16151 case 'expanded': {
16152 $val = 130;
16153 break;
16154 }
16155 case 'extra-expanded': {
16156 $val = 145;
16157 break;
16158 }
16159 case 'ultra-expanded': {
16160 $val = 160;
16161 break;
16162 }
16163 case 'wider': {
16164 $val = ($parent + 10);
16165 break;
16166 }
16167 case 'narrower': {
16168 $val = ($parent - 10);
16169 break;
16170 }
16171 case 'inherit': {
16172 if ($parent == 'normal') {
16173 $val = 100;
16174 } else {
16175 $val = $parent;
16176 }
16177 break;
16178 }
16179 default: {
16180 $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
16181 }
16182 }
16183 return $val;
16184 }
16185
16195 public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') {
16196 $refsize = TCPDF_FONTS::getFontRefSize($refsize);
16197 $parent_size = TCPDF_FONTS::getFontRefSize($parent_size, $refsize);
16198 switch ($val) {
16199 case 'xx-small': {
16200 $size = ($refsize - 4);
16201 break;
16202 }
16203 case 'x-small': {
16204 $size = ($refsize - 3);
16205 break;
16206 }
16207 case 'small': {
16208 $size = ($refsize - 2);
16209 break;
16210 }
16211 case 'medium': {
16212 $size = $refsize;
16213 break;
16214 }
16215 case 'large': {
16216 $size = ($refsize + 2);
16217 break;
16218 }
16219 case 'x-large': {
16220 $size = ($refsize + 4);
16221 break;
16222 }
16223 case 'xx-large': {
16224 $size = ($refsize + 6);
16225 break;
16226 }
16227 case 'smaller': {
16228 $size = ($parent_size - 3);
16229 break;
16230 }
16231 case 'larger': {
16232 $size = ($parent_size + 3);
16233 break;
16234 }
16235 default: {
16236 $size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true);
16237 }
16238 }
16239 return $size;
16240 }
16241
16249 protected function getHtmlDomArray($html) {
16250 // array of CSS styles ( selector => properties).
16251 $css = array();
16252 // get CSS array defined at previous call
16253 $matches = array();
16254 if (preg_match_all('/<cssarray>([^<]*)<\/cssarray>/isU', $html, $matches) > 0) {
16255 if (isset($matches[1][0])) {
16256 $css = array_merge($css, json_decode($this->unhtmlentities($matches[1][0]), true));
16257 }
16258 $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
16259 }
16260 // extract external CSS files
16261 $matches = array();
16262 if (preg_match_all('/<link([^>]*)>/isU', $html, $matches) > 0) {
16263 foreach ($matches[1] as $key => $link) {
16264 $type = array();
16265 if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
16266 $type = array();
16267 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
16268 // get 'all' and 'print' media, other media types are discarded
16269 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16270 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16271 $type = array();
16272 if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
16273 // read CSS data file
16274 $cssdata = TCPDF_STATIC::fileGetContents(trim($type[1]));
16275 if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) {
16276 $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16277 }
16278 }
16279 }
16280 }
16281 }
16282 }
16283 // extract style tags
16284 $matches = array();
16285 if (preg_match_all('/<style([^>]*)>([^<]*)<\/style>/isU', $html, $matches) > 0) {
16286 foreach ($matches[1] as $key => $media) {
16287 $type = array();
16288 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
16289 // get 'all' and 'print' media, other media types are discarded
16290 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16291 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16292 $cssdata = $matches[2][$key];
16293 $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16294 }
16295 }
16296 }
16297 // create a special tag to contain the CSS array (used for table content)
16298 $csstagarray = '<cssarray>'.htmlentities(json_encode($css)).'</cssarray>';
16299 // remove head and style blocks
16300 $html = preg_replace('/<head([^>]*)>(.*?)<\/head>/siU', '', $html);
16301 $html = preg_replace('/<style([^>]*)>([^<]*)<\/style>/isU', '', $html);
16302 // define block tags
16303 $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
16304 // define self-closing tags
16305 $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
16306 // remove all unsupported tags (the line below lists all supported tags)
16307 $html = strip_tags($html, '<marker/><a><b><blockquote><body><br><br/><dd><del><div><dl><dt><em><font><form><h1><h2><h3><h4><h5><h6><hr><hr/><i><img><input><label><li><ol><option><p><pre><s><select><small><span><strike><strong><sub><sup><table><tablehead><tcpdf><td><textarea><th><thead><tr><tt><u><ul>');
16308 //replace some blank characters
16309 $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
16310 $html = preg_replace('/<(table|tr|td|th|tcpdf|blockquote|dd|div|dl|dt|form|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^>]*)>[\n\r\t]+/', '<\\1\\2>', $html);
16311 $html = preg_replace('@(\r\n|\r)@', "\n", $html);
16312 $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
16313 $html = strtr($html, $repTable);
16314 $offset = 0;
16315 while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
16316 $html_a = substr($html, 0, $offset);
16317 $html_b = substr($html, $offset, ($pos - $offset + 6));
16318 while (preg_match("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
16319 // preserve newlines on <pre> tag
16320 $html_b = preg_replace("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
16321 }
16322 while (preg_match("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) {
16323 // preserve spaces on <pre> tag
16324 $html_b = preg_replace("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2&nbsp;\\3</pre>", $html_b);
16325 }
16326 $html = $html_a.$html_b.substr($html, $pos + 6);
16327 $offset = strlen($html_a.$html_b);
16328 }
16329 $offset = 0;
16330 while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
16331 $html_a = substr($html, 0, $offset);
16332 $html_b = substr($html, $offset, ($pos - $offset + 11));
16333 while (preg_match("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
16334 // preserve newlines on <textarea> tag
16335 $html_b = preg_replace("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
16336 $html_b = preg_replace("'<textarea([^>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
16337 }
16338 $html = $html_a.$html_b.substr($html, $pos + 11);
16339 $offset = strlen($html_a.$html_b);
16340 }
16341 $html = preg_replace('/([\s]*)<option/si', '<option', $html);
16342 $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
16343 $offset = 0;
16344 while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
16345 $html_a = substr($html, 0, $offset);
16346 $html_b = substr($html, $offset, ($pos - $offset + 9));
16347 while (preg_match("'<option([^>]*)>(.*?)</option>'si", $html_b)) {
16348 $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
16349 $html_b = preg_replace("'<option([^>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
16350 }
16351 $html = $html_a.$html_b.substr($html, $pos + 9);
16352 $offset = strlen($html_a.$html_b);
16353 }
16354 if (preg_match("'</select'si", $html)) {
16355 $html = preg_replace("'<select([^>]*)>'si", "<select\\1 opt=\"", $html);
16356 $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
16357 }
16358 $html = str_replace("\n", ' ', $html);
16359 // restore textarea newlines
16360 $html = str_replace('<TBR>', "\n", $html);
16361 // remove extra spaces from code
16362 $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
16363 $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html);
16364 $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
16365 $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html);
16366 $html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|dt|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html);
16367 $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
16368 $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
16369 $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html);
16370 $html = preg_replace('/<img([^>]*)>[\s]+([^<])/xi', '<img\\1>&nbsp;\\2', $html);
16371 $html = preg_replace('/<img([^>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
16372 $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
16373 $html = preg_replace('/<textarea([^>]*)>([^<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
16374 $html = preg_replace('/<li([^>]*)><\/li>/', '<li\\1>&nbsp;</li>', $html);
16375 $html = preg_replace('/<li([^>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1">&nbsp;</font><img', $html);
16376 $html = preg_replace('/<([^>\/]*)>[\s]/', '<\\1>&nbsp;', $html); // preserve some spaces
16377 $html = preg_replace('/[\s]<\/([^>]*)>/', '&nbsp;</\\1>', $html); // preserve some spaces
16378 $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment
16379 $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment
16380 $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space
16381 // trim string
16382 $html = $this->stringTrim($html);
16383 // fix br tag after li
16384 $html = preg_replace('/<li><br([^>]*)>/', '<li> <br\\1>', $html);
16385 // fix first image tag alignment
16386 $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
16387 // pattern for generic tag
16388 $tagpattern = '/(<[^>]+>)/';
16389 // explodes the string
16390 $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
16391 // count elements
16392 $maxel = count($a);
16393 $elkey = 0;
16394 $key = 0;
16395 // create an array of elements
16396 $dom = array();
16397 $dom[$key] = array();
16398 // set inheritable properties fot the first void element
16399 // possible inheritable properties are: azimuth, border-collapse, border-spacing, caption-side, color, cursor, direction, empty-cells, font, font-family, font-stretch, font-size, font-size-adjust, font-style, font-variant, font-weight, letter-spacing, line-height, list-style, list-style-image, list-style-position, list-style-type, orphans, page, page-break-inside, quotes, speak, speak-header, text-align, text-indent, text-transform, volume, white-space, widows, word-spacing
16400 $dom[$key]['tag'] = false;
16401 $dom[$key]['block'] = false;
16402 $dom[$key]['value'] = '';
16403 $dom[$key]['parent'] = 0;
16404 $dom[$key]['hide'] = false;
16405 $dom[$key]['fontname'] = $this->FontFamily;
16406 $dom[$key]['fontstyle'] = $this->FontStyle;
16407 $dom[$key]['fontsize'] = $this->FontSizePt;
16408 $dom[$key]['font-stretch'] = $this->font_stretching;
16409 $dom[$key]['letter-spacing'] = $this->font_spacing;
16410 $dom[$key]['stroke'] = $this->textstrokewidth;
16411 $dom[$key]['fill'] = (($this->textrendermode % 2) == 0);
16412 $dom[$key]['clip'] = ($this->textrendermode > 3);
16413 $dom[$key]['line-height'] = $this->cell_height_ratio;
16414 $dom[$key]['bgcolor'] = false;
16415 $dom[$key]['fgcolor'] = $this->fgcolor; // color
16416 $dom[$key]['strokecolor'] = $this->strokecolor;
16417 $dom[$key]['align'] = '';
16418 $dom[$key]['listtype'] = '';
16419 $dom[$key]['text-indent'] = 0;
16420 $dom[$key]['text-transform'] = '';
16421 $dom[$key]['border'] = array();
16422 $dom[$key]['dir'] = $this->rtl?'rtl':'ltr';
16423 $thead = false; // true when we are inside the THEAD tag
16424 ++$key;
16425 $level = array();
16426 array_push($level, 0); // root
16427 while ($elkey < $maxel) {
16428 $dom[$key] = array();
16429 $element = $a[$elkey];
16430 $dom[$key]['elkey'] = $elkey;
16431 if (preg_match($tagpattern, $element)) {
16432 // html tag
16433 $element = substr($element, 1, -1);
16434 // get tag name
16435 preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
16436 $tagname = strtolower($tag[1]);
16437 // check if we are inside a table header
16438 if ($tagname == 'thead') {
16439 if ($element[0] == '/') {
16440 $thead = false;
16441 } else {
16442 $thead = true;
16443 }
16444 ++$elkey;
16445 continue;
16446 }
16447 $dom[$key]['tag'] = true;
16448 $dom[$key]['value'] = $tagname;
16449 if (in_array($dom[$key]['value'], $blocktags)) {
16450 $dom[$key]['block'] = true;
16451 } else {
16452 $dom[$key]['block'] = false;
16453 }
16454 if ($element[0] == '/') {
16455 // *** closing html tag
16456 $dom[$key]['opening'] = false;
16457 $dom[$key]['parent'] = end($level);
16458 array_pop($level);
16459 $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
16460 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
16461 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
16462 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
16463 $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
16464 $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
16465 $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
16466 $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
16467 $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
16468 $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
16469 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
16470 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
16471 $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
16472 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
16473 $dom[$key]['text-transform'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['text-transform'];
16474 $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
16475 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
16476 $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
16477 }
16478 // set the number of columns in table tag
16479 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
16480 $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
16481 }
16482 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
16483 $dom[($dom[$key]['parent'])]['content'] = $csstagarray;
16484 for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
16485 $dom[($dom[$key]['parent'])]['content'] .= stripslashes($a[$dom[$i]['elkey']]);
16486 }
16487 $key = $i;
16488 // mark nested tables
16489 $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
16490 // remove thead sections from nested tables
16491 $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
16492 $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
16493 }
16494 // store header rows on a new table
16495 if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
16496 if (TCPDF_STATIC::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
16497 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
16498 }
16499 for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
16500 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
16501 }
16502 if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
16503 $dom[($dom[$key]['parent'])]['attribute'] = array();
16504 }
16505 // header elements must be always contained in a single page
16506 $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
16507 }
16508 if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC::empty_string($dom[($dom[$key]['parent'])]['thead']))) {
16509 // remove the nobr attributes from the table header
16510 $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
16511 $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
16512 }
16513 } else {
16514 // *** opening or self-closing html tag
16515 $dom[$key]['opening'] = true;
16516 $dom[$key]['parent'] = end($level);
16517 if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
16518 // self-closing tag
16519 $dom[$key]['self'] = true;
16520 } else {
16521 // opening tag
16522 array_push($level, $key);
16523 $dom[$key]['self'] = false;
16524 }
16525 // copy some values from parent
16526 $parentkey = 0;
16527 if ($key > 0) {
16528 $parentkey = $dom[$key]['parent'];
16529 $dom[$key]['hide'] = $dom[$parentkey]['hide'];
16530 $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
16531 $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
16532 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
16533 $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
16534 $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
16535 $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
16536 $dom[$key]['fill'] = $dom[$parentkey]['fill'];
16537 $dom[$key]['clip'] = $dom[$parentkey]['clip'];
16538 $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16539 $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
16540 $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
16541 $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
16542 $dom[$key]['align'] = $dom[$parentkey]['align'];
16543 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16544 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16545 $dom[$key]['text-transform'] = $dom[$parentkey]['text-transform'];
16546 $dom[$key]['border'] = array();
16547 $dom[$key]['dir'] = $dom[$parentkey]['dir'];
16548 }
16549 // get attributes
16550 preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER);
16551 $dom[$key]['attribute'] = array(); // reset attribute array
16552 while (list($id, $name) = each($attr_array[1])) {
16553 $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
16554 }
16555 if (!empty($css)) {
16556 // merge CSS style to current style
16557 list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC::getCSSdataArray($dom, $key, $css);
16558 $dom[$key]['attribute']['style'] = TCPDF_STATIC::getTagStyleFromCSSarray($dom[$key]['cssdata']);
16559 }
16560 // split style attributes
16561 if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
16562 // get style attributes
16563 preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
16564 $dom[$key]['style'] = array(); // reset style attribute array
16565 while (list($id, $name) = each($style_array[1])) {
16566 // in case of duplicate attribute the last replace the previous
16567 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
16568 }
16569 // --- get some style attributes ---
16570 // text direction
16571 if (isset($dom[$key]['style']['direction'])) {
16572 $dom[$key]['dir'] = $dom[$key]['style']['direction'];
16573 }
16574 // display
16575 if (isset($dom[$key]['style']['display'])) {
16576 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
16577 }
16578 // font family
16579 if (isset($dom[$key]['style']['font-family'])) {
16580 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
16581 }
16582 // list-style-type
16583 if (isset($dom[$key]['style']['list-style-type'])) {
16584 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
16585 if ($dom[$key]['listtype'] == 'inherit') {
16586 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16587 }
16588 }
16589 // text-indent
16590 if (isset($dom[$key]['style']['text-indent'])) {
16591 $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
16592 if ($dom[$key]['text-indent'] == 'inherit') {
16593 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16594 }
16595 }
16596 // text-transform
16597 if (isset($dom[$key]['style']['text-transform'])) {
16598 $dom[$key]['text-transform'] = $dom[$key]['style']['text-transform'];
16599 }
16600 // font size
16601 if (isset($dom[$key]['style']['font-size'])) {
16602 $fsize = trim($dom[$key]['style']['font-size']);
16603 $dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt');
16604 }
16605 // font-stretch
16606 if (isset($dom[$key]['style']['font-stretch'])) {
16607 $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
16608 }
16609 // letter-spacing
16610 if (isset($dom[$key]['style']['letter-spacing'])) {
16611 $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
16612 }
16613 // line-height (internally is the cell height ratio)
16614 if (isset($dom[$key]['style']['line-height'])) {
16615 $lineheight = trim($dom[$key]['style']['line-height']);
16616 switch ($lineheight) {
16617 // A normal line height. This is default
16618 case 'normal': {
16619 $dom[$key]['line-height'] = $dom[0]['line-height'];
16620 break;
16621 }
16622 case 'inherit': {
16623 $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16624 }
16625 default: {
16626 if (is_numeric($lineheight)) {
16627 // convert to percentage of font height
16628 $lineheight = ($lineheight * 100).'%';
16629 }
16630 $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
16631 if (substr($lineheight, -1) !== '%') {
16632 if ($dom[$key]['fontsize'] <= 0) {
16633 $dom[$key]['line-height'] = 1;
16634 } else {
16635 $dom[$key]['line-height'] = (($dom[$key]['line-height'] - $this->cell_padding['T'] - $this->cell_padding['B']) / $dom[$key]['fontsize']);
16636 }
16637 }
16638 }
16639 }
16640 }
16641 // font style
16642 if (isset($dom[$key]['style']['font-weight'])) {
16643 if (strtolower($dom[$key]['style']['font-weight'][0]) == 'n') {
16644 if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
16645 $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
16646 }
16647 } elseif (strtolower($dom[$key]['style']['font-weight'][0]) == 'b') {
16648 $dom[$key]['fontstyle'] .= 'B';
16649 }
16650 }
16651 if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style'][0]) == 'i')) {
16652 $dom[$key]['fontstyle'] .= 'I';
16653 }
16654 // font color
16655 if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['color']))) {
16656 $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors);
16657 } elseif ($dom[$key]['value'] == 'a') {
16658 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16659 }
16660 // background color
16661 if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['background-color']))) {
16662 $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors);
16663 }
16664 // text-decoration
16665 if (isset($dom[$key]['style']['text-decoration'])) {
16666 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
16667 foreach ($decors as $dec) {
16668 $dec = trim($dec);
16669 if (!TCPDF_STATIC::empty_string($dec)) {
16670 if ($dec[0] == 'u') {
16671 // underline
16672 $dom[$key]['fontstyle'] .= 'U';
16673 } elseif ($dec[0] == 'l') {
16674 // line-through
16675 $dom[$key]['fontstyle'] .= 'D';
16676 } elseif ($dec[0] == 'o') {
16677 // overline
16678 $dom[$key]['fontstyle'] .= 'O';
16679 }
16680 }
16681 }
16682 } elseif ($dom[$key]['value'] == 'a') {
16683 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16684 }
16685 // check for width attribute
16686 if (isset($dom[$key]['style']['width'])) {
16687 $dom[$key]['width'] = $dom[$key]['style']['width'];
16688 }
16689 // check for height attribute
16690 if (isset($dom[$key]['style']['height'])) {
16691 $dom[$key]['height'] = $dom[$key]['style']['height'];
16692 }
16693 // check for text alignment
16694 if (isset($dom[$key]['style']['text-align'])) {
16695 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align'][0]);
16696 }
16697 // check for CSS border properties
16698 if (isset($dom[$key]['style']['border'])) {
16699 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
16700 if (!empty($borderstyle)) {
16701 $dom[$key]['border']['LTRB'] = $borderstyle;
16702 }
16703 }
16704 if (isset($dom[$key]['style']['border-color'])) {
16705 $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
16706 if (isset($brd_colors[3])) {
16707 $dom[$key]['border']['L']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[3], $this->spot_colors);
16708 }
16709 if (isset($brd_colors[1])) {
16710 $dom[$key]['border']['R']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[1], $this->spot_colors);
16711 }
16712 if (isset($brd_colors[0])) {
16713 $dom[$key]['border']['T']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[0], $this->spot_colors);
16714 }
16715 if (isset($brd_colors[2])) {
16716 $dom[$key]['border']['B']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[2], $this->spot_colors);
16717 }
16718 }
16719 if (isset($dom[$key]['style']['border-width'])) {
16720 $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
16721 if (isset($brd_widths[3])) {
16722 $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
16723 }
16724 if (isset($brd_widths[1])) {
16725 $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
16726 }
16727 if (isset($brd_widths[0])) {
16728 $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
16729 }
16730 if (isset($brd_widths[2])) {
16731 $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
16732 }
16733 }
16734 if (isset($dom[$key]['style']['border-style'])) {
16735 $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
16736 if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) {
16737 $dom[$key]['border']['L']['cap'] = 'square';
16738 $dom[$key]['border']['L']['join'] = 'miter';
16739 $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
16740 if ($dom[$key]['border']['L']['dash'] < 0) {
16741 $dom[$key]['border']['L'] = array();
16742 }
16743 }
16744 if (isset($brd_styles[1])) {
16745 $dom[$key]['border']['R']['cap'] = 'square';
16746 $dom[$key]['border']['R']['join'] = 'miter';
16747 $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
16748 if ($dom[$key]['border']['R']['dash'] < 0) {
16749 $dom[$key]['border']['R'] = array();
16750 }
16751 }
16752 if (isset($brd_styles[0])) {
16753 $dom[$key]['border']['T']['cap'] = 'square';
16754 $dom[$key]['border']['T']['join'] = 'miter';
16755 $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
16756 if ($dom[$key]['border']['T']['dash'] < 0) {
16757 $dom[$key]['border']['T'] = array();
16758 }
16759 }
16760 if (isset($brd_styles[2])) {
16761 $dom[$key]['border']['B']['cap'] = 'square';
16762 $dom[$key]['border']['B']['join'] = 'miter';
16763 $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
16764 if ($dom[$key]['border']['B']['dash'] < 0) {
16765 $dom[$key]['border']['B'] = array();
16766 }
16767 }
16768 }
16769 $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
16770 foreach ($cellside as $bsk => $bsv) {
16771 if (isset($dom[$key]['style']['border-'.$bsv])) {
16772 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
16773 if (!empty($borderstyle)) {
16774 $dom[$key]['border'][$bsk] = $borderstyle;
16775 }
16776 }
16777 if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
16778 $dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors);
16779 }
16780 if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
16781 $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
16782 }
16783 if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
16784 $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
16785 if ($dom[$key]['border'][$bsk]['dash'] < 0) {
16786 $dom[$key]['border'][$bsk] = array();
16787 }
16788 }
16789 }
16790 // check for CSS padding properties
16791 if (isset($dom[$key]['style']['padding'])) {
16792 $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
16793 } else {
16794 $dom[$key]['padding'] = $this->cell_padding;
16795 }
16796 foreach ($cellside as $psk => $psv) {
16797 if (isset($dom[$key]['style']['padding-'.$psv])) {
16798 $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
16799 }
16800 }
16801 // check for CSS margin properties
16802 if (isset($dom[$key]['style']['margin'])) {
16803 $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
16804 } else {
16805 $dom[$key]['margin'] = $this->cell_margin;
16806 }
16807 foreach ($cellside as $psk => $psv) {
16808 if (isset($dom[$key]['style']['margin-'.$psv])) {
16809 $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
16810 }
16811 }
16812 // check for CSS border-spacing properties
16813 if (isset($dom[$key]['style']['border-spacing'])) {
16814 $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
16815 }
16816 // page-break-inside
16817 if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
16818 $dom[$key]['attribute']['nobr'] = 'true';
16819 }
16820 // page-break-before
16821 if (isset($dom[$key]['style']['page-break-before'])) {
16822 if ($dom[$key]['style']['page-break-before'] == 'always') {
16823 $dom[$key]['attribute']['pagebreak'] = 'true';
16824 } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
16825 $dom[$key]['attribute']['pagebreak'] = 'left';
16826 } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
16827 $dom[$key]['attribute']['pagebreak'] = 'right';
16828 }
16829 }
16830 // page-break-after
16831 if (isset($dom[$key]['style']['page-break-after'])) {
16832 if ($dom[$key]['style']['page-break-after'] == 'always') {
16833 $dom[$key]['attribute']['pagebreakafter'] = 'true';
16834 } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
16835 $dom[$key]['attribute']['pagebreakafter'] = 'left';
16836 } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
16837 $dom[$key]['attribute']['pagebreakafter'] = 'right';
16838 }
16839 }
16840 }
16841 if (isset($dom[$key]['attribute']['display'])) {
16842 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
16843 }
16844 if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
16845 $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
16846 if (!empty($borderstyle)) {
16847 $dom[$key]['border']['LTRB'] = $borderstyle;
16848 }
16849 }
16850 // check for font tag
16851 if ($dom[$key]['value'] == 'font') {
16852 // font family
16853 if (isset($dom[$key]['attribute']['face'])) {
16854 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
16855 }
16856 // font size
16857 if (isset($dom[$key]['attribute']['size'])) {
16858 if ($key > 0) {
16859 if ($dom[$key]['attribute']['size'][0] == '+') {
16860 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
16861 } elseif ($dom[$key]['attribute']['size'][0] == '-') {
16862 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
16863 } else {
16864 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16865 }
16866 } else {
16867 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16868 }
16869 }
16870 }
16871 // force natural alignment for lists
16872 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
16873 AND (!isset($dom[$key]['align']) OR TCPDF_STATIC::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
16874 if ($this->rtl) {
16875 $dom[$key]['align'] = 'R';
16876 } else {
16877 $dom[$key]['align'] = 'L';
16878 }
16879 }
16880 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
16881 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16882 $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
16883 }
16884 }
16885 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
16886 $dom[$key]['fontstyle'] .= 'B';
16887 }
16888 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
16889 $dom[$key]['fontstyle'] .= 'I';
16890 }
16891 if ($dom[$key]['value'] == 'u') {
16892 $dom[$key]['fontstyle'] .= 'U';
16893 }
16894 if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
16895 $dom[$key]['fontstyle'] .= 'D';
16896 }
16897 if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
16898 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16899 }
16900 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
16901 $dom[$key]['fontname'] = $this->default_monospaced_font;
16902 }
16903 if (!empty($dom[$key]['value']) AND ($dom[$key]['value'][0] == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
16904 // headings h1, h2, h3, h4, h5, h6
16905 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16906 $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
16907 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
16908 }
16909 if (!isset($dom[$key]['style']['font-weight'])) {
16910 $dom[$key]['fontstyle'] .= 'B';
16911 }
16912 }
16913 if (($dom[$key]['value'] == 'table')) {
16914 $dom[$key]['rows'] = 0; // number of rows
16915 $dom[$key]['trids'] = array(); // IDs of TR elements
16916 $dom[$key]['thead'] = ''; // table header rows
16917 }
16918 if (($dom[$key]['value'] == 'tr')) {
16919 $dom[$key]['cols'] = 0;
16920 if ($thead) {
16921 $dom[$key]['thead'] = true;
16922 // rows on thead block are printed as a separate table
16923 } else {
16924 $dom[$key]['thead'] = false;
16925 // store the number of rows on table element
16926 ++$dom[($dom[$key]['parent'])]['rows'];
16927 // store the TR elements IDs on table element
16928 array_push($dom[($dom[$key]['parent'])]['trids'], $key);
16929 }
16930 }
16931 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
16932 if (isset($dom[$key]['attribute']['colspan'])) {
16933 $colspan = intval($dom[$key]['attribute']['colspan']);
16934 } else {
16935 $colspan = 1;
16936 }
16937 $dom[$key]['attribute']['colspan'] = $colspan;
16938 $dom[($dom[$key]['parent'])]['cols'] += $colspan;
16939 }
16940 // text direction
16941 if (isset($dom[$key]['attribute']['dir'])) {
16942 $dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
16943 }
16944 // set foreground color attribute
16945 if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['color']))) {
16946 $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors);
16947 } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
16948 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16949 }
16950 // set background color attribute
16951 if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['bgcolor']))) {
16952 $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors);
16953 }
16954 // set stroke color attribute
16955 if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['strokecolor']))) {
16956 $dom[$key]['strokecolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors);
16957 }
16958 // check for width attribute
16959 if (isset($dom[$key]['attribute']['width'])) {
16960 $dom[$key]['width'] = $dom[$key]['attribute']['width'];
16961 }
16962 // check for height attribute
16963 if (isset($dom[$key]['attribute']['height'])) {
16964 $dom[$key]['height'] = $dom[$key]['attribute']['height'];
16965 }
16966 // check for text alignment
16967 if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
16968 $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align'][0]);
16969 }
16970 // check for text rendering mode (the following attributes do not exist in HTML)
16971 if (isset($dom[$key]['attribute']['stroke'])) {
16972 // font stroke width
16973 $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
16974 }
16975 if (isset($dom[$key]['attribute']['fill'])) {
16976 // font fill
16977 if ($dom[$key]['attribute']['fill'] == 'true') {
16978 $dom[$key]['fill'] = true;
16979 } else {
16980 $dom[$key]['fill'] = false;
16981 }
16982 }
16983 if (isset($dom[$key]['attribute']['clip'])) {
16984 // clipping mode
16985 if ($dom[$key]['attribute']['clip'] == 'true') {
16986 $dom[$key]['clip'] = true;
16987 } else {
16988 $dom[$key]['clip'] = false;
16989 }
16990 }
16991 } // end opening tag
16992 } else {
16993 // text
16994 $dom[$key]['tag'] = false;
16995 $dom[$key]['block'] = false;
16996 $dom[$key]['parent'] = end($level);
16997 $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
16998 if (!empty($dom[$dom[$key]['parent']]['text-transform'])) {
16999 // text-transform for unicode requires mb_convert_case (Multibyte String Functions)
17000 if (function_exists('mb_convert_case')) {
17001 $ttm = array('capitalize' => MB_CASE_TITLE, 'uppercase' => MB_CASE_UPPER, 'lowercase' => MB_CASE_LOWER);
17002 if (isset($ttm[$dom[$dom[$key]['parent']]['text-transform']])) {
17003 $element = mb_convert_case($element, $ttm[$dom[$dom[$key]['parent']]['text-transform']], $this->encoding);
17004 }
17005 } elseif (!$this->isunicode) {
17006 switch ($dom[$dom[$key]['parent']]['text-transform']) {
17007 case 'capitalize': {
17008 $element = ucwords(strtolower($element));
17009 break;
17010 }
17011 case 'uppercase': {
17012 $element = strtoupper($element);
17013 break;
17014 }
17015 case 'lowercase': {
17016 $element = strtolower($element);
17017 break;
17018 }
17019 }
17020 }
17021 }
17022 $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
17023 }
17024 ++$elkey;
17025 ++$key;
17026 }
17027 return $dom;
17028 }
17029
17037 protected function getSpaceString() {
17038 $spacestr = chr(32);
17039 if ($this->isUnicodeFont()) {
17040 $spacestr = chr(0).chr(32);
17041 }
17042 return $spacestr;
17043 }
17044
17051 protected function getHashForTCPDFtagParams($data) {
17052 return md5(strlen($data).$this->file_id.$data);
17053 }
17054
17062 $encoded = urlencode(json_encode($data));
17063 return $this->getHashForTCPDFtagParams($encoded).$encoded;
17064 }
17065
17073 $hash = substr($data, 0, 32);
17074 $encoded = substr($data, 32);
17075 if ($hash != $this->getHashForTCPDFtagParams($encoded)) {
17076 $this->Error('Invalid parameters');
17077 }
17078 return json_decode(urldecode($encoded), true);
17079 }
17080
17103 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
17104 return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
17105 }
17106
17120 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
17121 $gvars = $this->getGraphicVars();
17122 // store current values
17123 $prev_cell_margin = $this->cell_margin;
17124 $prev_cell_padding = $this->cell_padding;
17125 $prevPage = $this->page;
17126 $prevlMargin = $this->lMargin;
17127 $prevrMargin = $this->rMargin;
17128 $curfontname = $this->FontFamily;
17129 $curfontstyle = $this->FontStyle;
17130 $curfontsize = $this->FontSizePt;
17131 $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
17132 $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
17133 $curfontstretcing = $this->font_stretching;
17134 $curfonttracking = $this->font_spacing;
17135 $this->newline = true;
17136 $newline = true;
17137 $startlinepage = $this->page;
17138 $minstartliney = $this->y;
17139 $maxbottomliney = 0;
17140 $startlinex = $this->x;
17141 $startliney = $this->y;
17142 $yshift = 0;
17143 $loop = 0;
17144 $curpos = 0;
17145 $this_method_vars = array();
17146 $undo = false;
17147 $fontaligned = false;
17148 $reverse_dir = false; // true when the text direction is reversed
17149 $this->premode = false;
17150 if ($this->inxobj) {
17151 // we are inside an XObject template
17152 $pask = count($this->xobjects[$this->xobjid]['annotations']);
17153 } elseif (isset($this->PageAnnots[$this->page])) {
17154 $pask = count($this->PageAnnots[$this->page]);
17155 } else {
17156 $pask = 0;
17157 }
17158 if ($this->inxobj) {
17159 // we are inside an XObject template
17160 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17161 } elseif (!$this->InFooter) {
17162 if (isset($this->footerlen[$this->page])) {
17163 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17164 } else {
17165 $this->footerpos[$this->page] = $this->pagelen[$this->page];
17166 }
17167 $startlinepos = $this->footerpos[$this->page];
17168 } else {
17169 // we are inside the footer
17170 $startlinepos = $this->pagelen[$this->page];
17171 }
17172 $lalign = $align;
17173 $plalign = $align;
17174 if ($this->rtl) {
17175 $w = $this->x - $this->lMargin;
17176 } else {
17177 $w = $this->w - $this->rMargin - $this->x;
17178 }
17179 $w -= ($this->cell_padding['L'] + $this->cell_padding['R']);
17180 if ($cell) {
17181 if ($this->rtl) {
17182 $this->x -= $this->cell_padding['R'];
17183 $this->lMargin += $this->cell_padding['R'];
17184 } else {
17185 $this->x += $this->cell_padding['L'];
17186 $this->rMargin += $this->cell_padding['L'];
17187 }
17188 }
17189 if ($this->customlistindent >= 0) {
17190 $this->listindent = $this->customlistindent;
17191 } else {
17192 $this->listindent = $this->GetStringWidth('000000');
17193 }
17194 $this->listindentlevel = 0;
17195 // save previous states
17196 $prev_cell_height_ratio = $this->cell_height_ratio;
17197 $prev_listnum = $this->listnum;
17198 $prev_listordered = $this->listordered;
17199 $prev_listcount = $this->listcount;
17200 $prev_lispacer = $this->lispacer;
17201 $this->listnum = 0;
17202 $this->listordered = array();
17203 $this->listcount = array();
17204 $this->lispacer = '';
17205 if ((TCPDF_STATIC::empty_string($this->lasth)) OR ($reseth)) {
17206 // reset row height
17207 $this->resetLastH();
17208 }
17209 $dom = $this->getHtmlDomArray($html);
17210 $maxel = count($dom);
17211 $key = 0;
17212 while ($key < $maxel) {
17213 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND $dom[$key]['hide']) {
17214 // store the node key
17215 $hidden_node_key = $key;
17216 if ($dom[$key]['self']) {
17217 // skip just this self-closing tag
17218 ++$key;
17219 } else {
17220 // skip this and all children tags
17221 while (($key < $maxel) AND (!$dom[$key]['tag'] OR $dom[$key]['opening'] OR ($dom[$key]['parent'] != $hidden_node_key))) {
17222 // skip hidden objects
17223 ++$key;
17224 }
17225 ++$key;
17226 }
17227 }
17228 if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
17229 // check for pagebreak
17230 if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
17231 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17232 $this->checkPageBreak($this->PageBreakTrigger + 1);
17233 $this->htmlvspace = ($this->PageBreakTrigger + 1);
17234 }
17235 if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
17236 OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
17237 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17238 $this->checkPageBreak($this->PageBreakTrigger + 1);
17239 $this->htmlvspace = ($this->PageBreakTrigger + 1);
17240 }
17241 }
17242 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
17243 if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
17244 $dom[$key]['attribute']['nobr'] = false;
17245 } else {
17246 // store current object
17247 $this->startTransaction();
17248 // save this method vars
17249 $this_method_vars['html'] = $html;
17250 $this_method_vars['ln'] = $ln;
17251 $this_method_vars['fill'] = $fill;
17252 $this_method_vars['reseth'] = $reseth;
17253 $this_method_vars['cell'] = $cell;
17254 $this_method_vars['align'] = $align;
17255 $this_method_vars['gvars'] = $gvars;
17256 $this_method_vars['prevPage'] = $prevPage;
17257 $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
17258 $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
17259 $this_method_vars['prevlMargin'] = $prevlMargin;
17260 $this_method_vars['prevrMargin'] = $prevrMargin;
17261 $this_method_vars['curfontname'] = $curfontname;
17262 $this_method_vars['curfontstyle'] = $curfontstyle;
17263 $this_method_vars['curfontsize'] = $curfontsize;
17264 $this_method_vars['curfontascent'] = $curfontascent;
17265 $this_method_vars['curfontdescent'] = $curfontdescent;
17266 $this_method_vars['curfontstretcing'] = $curfontstretcing;
17267 $this_method_vars['curfonttracking'] = $curfonttracking;
17268 $this_method_vars['minstartliney'] = $minstartliney;
17269 $this_method_vars['maxbottomliney'] = $maxbottomliney;
17270 $this_method_vars['yshift'] = $yshift;
17271 $this_method_vars['startlinepage'] = $startlinepage;
17272 $this_method_vars['startlinepos'] = $startlinepos;
17273 $this_method_vars['startlinex'] = $startlinex;
17274 $this_method_vars['startliney'] = $startliney;
17275 $this_method_vars['newline'] = $newline;
17276 $this_method_vars['loop'] = $loop;
17277 $this_method_vars['curpos'] = $curpos;
17278 $this_method_vars['pask'] = $pask;
17279 $this_method_vars['lalign'] = $lalign;
17280 $this_method_vars['plalign'] = $plalign;
17281 $this_method_vars['w'] = $w;
17282 $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
17283 $this_method_vars['prev_listnum'] = $prev_listnum;
17284 $this_method_vars['prev_listordered'] = $prev_listordered;
17285 $this_method_vars['prev_listcount'] = $prev_listcount;
17286 $this_method_vars['prev_lispacer'] = $prev_lispacer;
17287 $this_method_vars['fontaligned'] = $fontaligned;
17288 $this_method_vars['key'] = $key;
17289 $this_method_vars['dom'] = $dom;
17290 }
17291 }
17292 // print THEAD block
17293 if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
17294 if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC::empty_string($dom[$dom[$key]['parent']]['thead'])) {
17295 $this->inthead = true;
17296 // print table header (thead)
17297 $this->writeHTML($this->thead, false, false, false, false, '');
17298 // check if we are on a new page or on a new column
17299 if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
17300 // we are on a new page or on a new column and the total object height is less than the available vertical space.
17301 // restore previous object
17302 $this->rollbackTransaction(true);
17303 // restore previous values
17304 foreach ($this_method_vars as $vkey => $vval) {
17305 $$vkey = $vval;
17306 }
17307 // disable table header
17308 $tmp_thead = $this->thead;
17309 $this->thead = '';
17310 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17311 $pre_y = $this->y;
17312 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
17313 // fix for multicolumn mode
17314 $startliney = $this->y;
17315 }
17316 $this->start_transaction_page = $this->page;
17317 $this->start_transaction_y = $this->y;
17318 // restore table header
17319 $this->thead = $tmp_thead;
17320 // fix table border properties
17321 if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
17322 $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
17323 } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
17324 $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
17325 } else {
17326 $tmp_cellspacing = 0;
17327 }
17328 $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
17329 $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
17330 $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
17331 $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']);
17332 $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
17333 $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
17334 // print table header (thead)
17335 $this->writeHTML($this->thead, false, false, false, false, '');
17336 }
17337 }
17338 // move $key index forward to skip THEAD block
17339 while ( ($key < $maxel) AND (!(
17340 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
17341 OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
17342 ++$key;
17343 }
17344 }
17345 if ($dom[$key]['tag'] OR ($key == 0)) {
17346 if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
17347 $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
17348 }
17349 // vertically align image in line
17350 if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
17351 // get image height
17352 $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], ($dom[$key]['fontsize'] / $this->k), 'px');
17353 $autolinebreak = false;
17354 if (!empty($dom[$key]['width'])) {
17355 $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], ($dom[$key]['fontsize'] / $this->k), 'px', false);
17356 if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R']))
17357 AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L'])))
17358 OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) {
17359 // add automatic line break
17360 $autolinebreak = true;
17361 $this->Ln('', $cell);
17362 if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
17363 // go back to evaluate this line break
17364 --$key;
17365 }
17366 }
17367 }
17368 if (!$autolinebreak) {
17369 if ($this->inPageBody()) {
17370 $pre_y = $this->y;
17371 // check for page break
17372 if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
17373 // fix for multicolumn mode
17374 $startliney = $this->y;
17375 }
17376 }
17377 if ($this->page > $startlinepage) {
17378 // fix line splitted over two pages
17379 if (isset($this->footerlen[$startlinepage])) {
17380 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17381 }
17382 // line to be moved one page forward
17383 $pagebuff = $this->getPageBuffer($startlinepage);
17384 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17385 $tstart = substr($pagebuff, 0, $startlinepos);
17386 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17387 // remove line from previous page
17388 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17389 $pagebuff = $this->getPageBuffer($this->page);
17390 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17391 $tend = substr($pagebuff, $this->cntmrk[$this->page]);
17392 // add line start to current page
17393 $yshift = ($minstartliney - $this->y);
17394 if ($fontaligned) {
17395 $yshift += ($curfontsize / $this->k);
17396 }
17397 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17398 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17399 // shift the annotations and links
17400 if (isset($this->PageAnnots[$this->page])) {
17401 $next_pask = count($this->PageAnnots[$this->page]);
17402 } else {
17403 $next_pask = 0;
17404 }
17405 if (isset($this->PageAnnots[$startlinepage])) {
17406 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17407 if ($pak >= $pask) {
17408 $this->PageAnnots[$this->page][] = $pac;
17409 unset($this->PageAnnots[$startlinepage][$pak]);
17410 $npak = count($this->PageAnnots[$this->page]) - 1;
17411 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17412 }
17413 }
17414 }
17415 $pask = $next_pask;
17416 $startlinepos = $this->cntmrk[$this->page];
17417 $startlinepage = $this->page;
17418 $startliney = $this->y;
17419 $this->newline = false;
17420 }
17421 $this->y += ($this->getCellHeight($curfontsize / $this->k) - ($curfontdescent * $this->cell_height_ratio) - $imgh);
17422 $minstartliney = min($this->y, $minstartliney);
17423 $maxbottomliney = ($startliney + $this->getCellHeight($curfontsize / $this->k));
17424 }
17425 } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
17426 // account for different font size
17427 $pfontname = $curfontname;
17428 $pfontstyle = $curfontstyle;
17429 $pfontsize = $curfontsize;
17430 $fontname = (isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname);
17431 $fontstyle = (isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle);
17432 $fontsize = (isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize);
17433 $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
17434 $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
17435 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
17436 OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17437 OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
17438 if (($key < ($maxel - 1)) AND (
17439 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
17440 OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17441 OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize)
17442 AND ($fontsize >= 0) AND ($curfontsize >= 0)
17443 AND (($fontsize != $curfontsize) OR ($fontstyle != $curfontstyle) OR ($fontname != $curfontname)))
17444 )) {
17445 if ($this->page > $startlinepage) {
17446 // fix lines splitted over two pages
17447 if (isset($this->footerlen[$startlinepage])) {
17448 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17449 }
17450 // line to be moved one page forward
17451 $pagebuff = $this->getPageBuffer($startlinepage);
17452 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17453 $tstart = substr($pagebuff, 0, $startlinepos);
17454 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17455 // remove line start from previous page
17456 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17457 $pagebuff = $this->getPageBuffer($this->page);
17458 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17459 $tend = substr($pagebuff, $this->cntmrk[$this->page]);
17460 // add line start to current page
17461 $yshift = ($minstartliney - $this->y);
17462 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17463 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17464 // shift the annotations and links
17465 if (isset($this->PageAnnots[$this->page])) {
17466 $next_pask = count($this->PageAnnots[$this->page]);
17467 } else {
17468 $next_pask = 0;
17469 }
17470 if (isset($this->PageAnnots[$startlinepage])) {
17471 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17472 if ($pak >= $pask) {
17473 $this->PageAnnots[$this->page][] = $pac;
17474 unset($this->PageAnnots[$startlinepage][$pak]);
17475 $npak = count($this->PageAnnots[$this->page]) - 1;
17476 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17477 }
17478 }
17479 }
17480 $pask = $next_pask;
17481 $startlinepos = $this->cntmrk[$this->page];
17482 $startlinepage = $this->page;
17483 $startliney = $this->y;
17484 }
17485 if (!isset($dom[$key]['line-height'])) {
17486 $dom[$key]['line-height'] = $this->cell_height_ratio;
17487 }
17488 if (!$dom[$key]['block']) {
17489 if (!(isset($dom[($key + 1)]) AND $dom[($key + 1)]['tag'] AND (!$dom[($key + 1)]['opening']) AND ($dom[($key + 1)]['value'] != 'li') AND $dom[$key]['tag'] AND (!$dom[$key]['opening']))) {
17490 $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
17491 }
17492 if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
17493 $current_line_align_data = array($key, $minstartliney, $maxbottomliney);
17494 if (isset($line_align_data) AND (($line_align_data[0] == ($key - 1)) OR (($line_align_data[0] == ($key - 2)) AND (isset($dom[($key - 1)])) AND (preg_match('/^([\s]+)$/', $dom[($key - 1)]['value']) > 0)))) {
17495 $minstartliney = min($this->y, $line_align_data[1]);
17496 $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $line_align_data[2]);
17497 } else {
17498 $minstartliney = min($this->y, $minstartliney);
17499 $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $maxbottomliney);
17500 }
17501 $line_align_data = $current_line_align_data;
17502 }
17503 }
17504 $this->cell_height_ratio = $dom[$key]['line-height'];
17505 $fontaligned = true;
17506 }
17507 $this->SetFont($fontname, $fontstyle, $fontsize);
17508 // reset row height
17509 $this->resetLastH();
17510 $curfontname = $fontname;
17511 $curfontstyle = $fontstyle;
17512 $curfontsize = $fontsize;
17513 $curfontascent = $fontascent;
17514 $curfontdescent = $fontdescent;
17515 }
17516 }
17517 // set text rendering mode
17518 $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
17519 $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
17520 $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
17521 $this->setTextRenderingMode($textstroke, $textfill, $textclip);
17522 if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
17523 $this->setFontStretching($dom[$key]['font-stretch']);
17524 }
17525 if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
17526 $this->setFontSpacing($dom[$key]['letter-spacing']);
17527 }
17528 if (($plalign == 'J') AND $dom[$key]['block']) {
17529 $plalign = '';
17530 }
17531 // get current position on page buffer
17532 $curpos = $this->pagelen[$startlinepage];
17533 if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
17534 $this->SetFillColorArray($dom[$key]['bgcolor']);
17535 $wfill = true;
17536 } else {
17537 $wfill = $fill | false;
17538 }
17539 if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
17540 $this->SetTextColorArray($dom[$key]['fgcolor']);
17541 }
17542 if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
17543 $this->SetDrawColorArray($dom[$key]['strokecolor']);
17544 }
17545 if (isset($dom[$key]['align'])) {
17546 $lalign = $dom[$key]['align'];
17547 }
17548 if (TCPDF_STATIC::empty_string($lalign)) {
17549 $lalign = $align;
17550 }
17551 }
17552 // align lines
17553 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
17554 $newline = true;
17555 $fontaligned = false;
17556 // we are at the beginning of a new line
17557 if (isset($startlinex)) {
17558 $yshift = ($minstartliney - $startliney);
17559 if (($yshift > 0) OR ($this->page > $startlinepage)) {
17560 $yshift = 0;
17561 }
17562 $t_x = 0;
17563 // the last line must be shifted to be aligned as requested
17564 $linew = abs($this->endlinex - $startlinex);
17565 if ($this->inxobj) {
17566 // we are inside an XObject template
17567 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
17568 if (isset($opentagpos)) {
17569 $midpos = $opentagpos;
17570 } else {
17571 $midpos = 0;
17572 }
17573 if ($midpos > 0) {
17574 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
17575 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
17576 } else {
17577 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
17578 $pend = '';
17579 }
17580 } else {
17581 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
17582 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17583 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17584 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
17585 } elseif (isset($opentagpos)) {
17586 $midpos = $opentagpos;
17587 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17588 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17589 $midpos = $this->footerpos[$startlinepage];
17590 } else {
17591 $midpos = 0;
17592 }
17593 if ($midpos > 0) {
17594 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
17595 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
17596 } else {
17597 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
17598 $pend = '';
17599 }
17600 }
17601 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
17602 // calculate shifting amount
17603 $tw = $w;
17604 if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
17605 $tw += $this->cell_padding['R'];
17606 }
17607 if ($this->lMargin != $prevlMargin) {
17608 $tw += ($prevlMargin - $this->lMargin);
17609 }
17610 if ($this->rMargin != $prevrMargin) {
17611 $tw += ($prevrMargin - $this->rMargin);
17612 }
17613 $one_space_width = $this->GetStringWidth(chr(32));
17614 $no = 0; // number of spaces on a line contained on a single block
17615 if ($this->isRTLTextDir()) { // RTL
17616 // remove left space if exist
17617 $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
17618 if ($pos1 > 0) {
17619 $pos1 = intval($pos1);
17620 if ($this->isUnicodeFont()) {
17621 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
17622 $spacelen = 2;
17623 } else {
17624 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
17625 $spacelen = 1;
17626 }
17627 if ($pos1 == $pos2) {
17628 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
17629 if (substr($pmid, $pos1, 4) == '[()]') {
17630 $linew -= $one_space_width;
17631 } elseif ($pos1 == strpos($pmid, '[(')) {
17632 $no = 1;
17633 }
17634 }
17635 }
17636 } else { // LTR
17637 // remove right space if exist
17638 $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
17639 if ($pos1 > 0) {
17640 $pos1 = intval($pos1);
17641 if ($this->isUnicodeFont()) {
17642 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
17643 $spacelen = 2;
17644 } else {
17645 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
17646 $spacelen = 1;
17647 }
17648 if ($pos1 == $pos2) {
17649 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
17650 $linew -= $one_space_width;
17651 }
17652 }
17653 }
17654 $mdiff = ($tw - $linew);
17655 if ($plalign == 'C') {
17656 if ($this->rtl) {
17657 $t_x = -($mdiff / 2);
17658 } else {
17659 $t_x = ($mdiff / 2);
17660 }
17661 } elseif ($plalign == 'R') {
17662 // right alignment on LTR document
17663 $t_x = $mdiff;
17664 } elseif ($plalign == 'L') {
17665 // left alignment on RTL document
17666 $t_x = -$mdiff;
17667 } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
17668 // Justification
17669 if ($this->isRTLTextDir()) {
17670 // align text on the left
17671 $t_x = -$mdiff;
17672 }
17673 $ns = 0; // number of spaces
17674 $pmidtemp = $pmid;
17675 // escape special characters
17676 $pmidtemp = preg_replace('/[\\\][\‍(]/x', '\\#!#OP#!#', $pmidtemp);
17677 $pmidtemp = preg_replace('/[\\\][\‍)]/x', '\\#!#CP#!#', $pmidtemp);
17678 // search spaces
17679 if (preg_match_all('/\[\‍(([^\‍)]*)\‍)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
17680 $spacestr = $this->getSpaceString();
17681 $maxkk = count($lnstring[1]) - 1;
17682 for ($kk=0; $kk <= $maxkk; ++$kk) {
17683 // restore special characters
17684 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
17685 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
17686 // store number of spaces on the strings
17687 $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
17688 // count total spaces on line
17689 $ns += $lnstring[2][$kk];
17690 $lnstring[3][$kk] = $ns;
17691 }
17692 if ($ns == 0) {
17693 $ns = 1;
17694 }
17695 // calculate additional space to add to each existing space
17696 $spacewidth = ($mdiff / ($ns - $no)) * $this->k;
17697 if ($this->FontSize <= 0) {
17698 $this->FontSize = 1;
17699 }
17700 $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize;
17701 if ($this->font_spacing != 0) {
17702 // fixed spacing mode
17703 $osw = -1000 * $this->font_spacing / $this->FontSize;
17704 $spacewidthu += $osw;
17705 }
17706 $nsmax = $ns;
17707 $ns = 0;
17708 reset($lnstring);
17709 $offset = 0;
17710 $strcount = 0;
17711 $prev_epsposbeg = 0;
17712 $textpos = 0;
17713 if ($this->isRTLTextDir()) {
17714 $textpos = $this->wPt;
17715 }
17716 while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
17717 // check if we are inside a string section '[( ... )]'
17718 $stroffset = strpos($pmid, '[(', $offset);
17719 if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
17720 // set offset to the end of string section
17721 $offset = strpos($pmid, ')]', $stroffset);
17722 while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
17723 $offset = strpos($pmid, ')]', ($offset + 1));
17724 }
17725 if ($offset === false) {
17726 $this->Error('HTML Justification: malformed PDF code.');
17727 }
17728 continue;
17729 }
17730 if ($this->isRTLTextDir()) {
17731 $spacew = ($spacewidth * ($nsmax - $ns));
17732 } else {
17733 $spacew = ($spacewidth * $ns);
17734 }
17735 $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
17736 $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset);
17737 if ($epsposend !== null) {
17738 $epsposend += strlen($this->epsmarker.'Q');
17739 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
17740 if ($epsposbeg === null) {
17741 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
17742 $prev_epsposbeg = $epsposbeg;
17743 }
17744 if (($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) {
17745 // shift EPS images
17746 $trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
17747 $pmid_b = substr($pmid, 0, $epsposbeg);
17748 $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
17749 $pmid_e = substr($pmid, $epsposend);
17750 $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
17751 $offset = $epsposend;
17752 continue;
17753 }
17754 }
17755 $currentxpos = 0;
17756 // shift blocks of code
17757 switch ($strpiece[2][0]) {
17758 case 'Td':
17759 case 'cm':
17760 case 'm':
17761 case 'l': {
17762 // get current X position
17763 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
17764 if (!isset($xmatches[1])) {
17765 break;
17766 }
17767 $currentxpos = $xmatches[1];
17768 $textpos = $currentxpos;
17769 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
17770 $ns = $lnstring[3][$strcount];
17771 if ($this->isRTLTextDir()) {
17772 $spacew = ($spacewidth * ($nsmax - $ns));
17773 }
17774 ++$strcount;
17775 }
17776 // justify block
17777 if (preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $pmatch) == 1) {
17778 $newpmid = sprintf('%F',(floatval($pmatch[1]) + $spacew)).' '.$pmatch[2].' x*#!#*x'.$pmatch[3].$pmatch[4];
17779 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17780 unset($pmatch, $newpmid);
17781 }
17782 break;
17783 }
17784 case 're': {
17785 // justify block
17786 if (!TCPDF_STATIC::empty_string($this->lispacer)) {
17787 $this->lispacer = '';
17788 continue;
17789 }
17790 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
17791 if (!isset($xmatches[1])) {
17792 break;
17793 }
17794 $currentxpos = $xmatches[1];
17795 $x_diff = 0;
17796 $w_diff = 0;
17797 if ($this->isRTLTextDir()) { // RTL
17798 if ($currentxpos < $textpos) {
17799 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
17800 $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17801 } else {
17802 if ($strcount > 0) {
17803 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
17804 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17805 }
17806 }
17807 } else { // LTR
17808 if ($currentxpos > $textpos) {
17809 if ($strcount > 0) {
17810 $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
17811 }
17812 $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17813 } else {
17814 if ($strcount > 1) {
17815 $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
17816 }
17817 if ($strcount > 0) {
17818 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17819 }
17820 }
17821 }
17822 if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $pmatch) == 1) {
17823 $newx = sprintf('%F',(floatval($pmatch[1]) + $x_diff));
17824 $neww = sprintf('%F',(floatval($pmatch[3]) + $w_diff));
17825 $newpmid = $newx.' '.$pmatch[2].' '.$neww.' '.$pmatch[4].' x*#!#*x'.$pmatch[5].$pmatch[6];
17826 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17827 unset($pmatch, $newpmid, $newx, $neww);
17828 }
17829 break;
17830 }
17831 case 'c': {
17832 // get current X position
17833 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', $pmid, $xmatches);
17834 if (!isset($xmatches[1])) {
17835 break;
17836 }
17837 $currentxpos = $xmatches[1];
17838 // justify block
17839 if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$xmatches[4].')[\s]('.$xmatches[5].')[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', $pmid, $pmatch) == 1) {
17840 $newx1 = sprintf('%F',(floatval($pmatch[1]) + $spacew));
17841 $newx2 = sprintf('%F',(floatval($pmatch[3]) + $spacew));
17842 $newx3 = sprintf('%F',(floatval($pmatch[5]) + $spacew));
17843 $newpmid = $newx1.' '.$pmatch[2].' '.$newx2.' '.$pmatch[4].' '.$newx3.' '.$pmatch[6].' x*#!#*x'.$pmatch[7].$pmatch[8];
17844 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17845 unset($pmatch, $newpmid, $newx1, $newx2, $newx3);
17846 }
17847 break;
17848 }
17849 }
17850 // shift the annotations and links
17851 $cxpos = ($currentxpos / $this->k);
17852 $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps);
17853 if ($this->inxobj) {
17854 // we are inside an XObject template
17855 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17856 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17857 if ($cxpos > $lmpos) {
17858 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k);
17859 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17860 } else {
17861 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17862 }
17863 break;
17864 }
17865 }
17866 } elseif (isset($this->PageAnnots[$this->page])) {
17867 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17868 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17869 if ($cxpos > $lmpos) {
17870 $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
17871 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17872 } else {
17873 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17874 }
17875 break;
17876 }
17877 }
17878 }
17879 } // end of while
17880 // remove markers
17881 $pmid = str_replace('x*#!#*x', '', $pmid);
17882 if ($this->isUnicodeFont()) {
17883 // multibyte characters
17884 $spacew = $spacewidthu;
17885 if ($this->font_stretching != 100) {
17886 // word spacing is affected by stretching
17887 $spacew /= ($this->font_stretching / 100);
17888 }
17889 // escape special characters
17890 $pos = 0;
17891 $pmid = preg_replace('/[\\\][\‍(]/x', '\\#!#OP#!#', $pmid);
17892 $pmid = preg_replace('/[\\\][\‍)]/x', '\\#!#CP#!#', $pmid);
17893 if (preg_match_all('/\[\‍(([^\‍)]*)\‍)\]/x', $pmid, $pamatch) > 0) {
17894 foreach($pamatch[0] as $pk => $pmatch) {
17895 $replace = $pamatch[1][$pk];
17896 $replace = str_replace('#!#OP#!#', '(', $replace);
17897 $replace = str_replace('#!#CP#!#', ')', $replace);
17898 $newpmid = '[('.str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacew).' (', $replace).')]';
17899 $pos = strpos($pmid, $pmatch, $pos);
17900 if ($pos !== FALSE) {
17901 $pmid = substr_replace($pmid, $newpmid, $pos, strlen($pmatch));
17902 }
17903 ++$pos;
17904 }
17905 unset($pamatch);
17906 }
17907 if ($this->inxobj) {
17908 // we are inside an XObject template
17909 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend;
17910 } else {
17911 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
17912 }
17913 $endlinepos = strlen($pstart."\n".$pmid."\n");
17914 } else {
17915 // non-unicode (single-byte characters)
17916 if ($this->font_stretching != 100) {
17917 // word spacing (Tw) is affected by stretching
17918 $spacewidth /= ($this->font_stretching / 100);
17919 }
17920 $rs = sprintf('%F Tw', $spacewidth);
17921 $pmid = preg_replace("/\[\‍(/x", $rs.' [(', $pmid);
17922 if ($this->inxobj) {
17923 // we are inside an XObject template
17924 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
17925 } else {
17926 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
17927 }
17928 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
17929 }
17930 }
17931 } // end of J
17932 } // end if $startlinex
17933 if (($t_x != 0) OR ($yshift < 0)) {
17934 // shift the line
17935 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
17936 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
17937 $endlinepos = strlen($pstart);
17938 if ($this->inxobj) {
17939 // we are inside an XObject template
17940 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
17941 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17942 if ($pak >= $pask) {
17943 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
17944 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
17945 }
17946 }
17947 } else {
17948 $this->setPageBuffer($startlinepage, $pstart.$pend);
17949 // shift the annotations and links
17950 if (isset($this->PageAnnots[$this->page])) {
17951 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17952 if ($pak >= $pask) {
17953 $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
17954 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
17955 }
17956 }
17957 }
17958 }
17959 $this->y -= $yshift;
17960 }
17961 }
17962 $pbrk = $this->checkPageBreak($this->lasth);
17963 $this->newline = false;
17964 $startlinex = $this->x;
17965 $startliney = $this->y;
17966 if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
17967 $startliney -= ((0.3 * $this->FontSizePt) / $this->k);
17968 } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
17969 $startliney -= (($this->FontSizePt / 0.7) / $this->k);
17970 } else {
17971 $minstartliney = $startliney;
17972 $maxbottomliney = ($this->y + $this->getCellHeight($fontsize / $this->k));
17973 }
17974 $startlinepage = $this->page;
17975 if (isset($endlinepos) AND (!$pbrk)) {
17976 $startlinepos = $endlinepos;
17977 } else {
17978 if ($this->inxobj) {
17979 // we are inside an XObject template
17980 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17981 } elseif (!$this->InFooter) {
17982 if (isset($this->footerlen[$this->page])) {
17983 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17984 } else {
17985 $this->footerpos[$this->page] = $this->pagelen[$this->page];
17986 }
17987 $startlinepos = $this->footerpos[$this->page];
17988 } else {
17989 $startlinepos = $this->pagelen[$this->page];
17990 }
17991 }
17992 unset($endlinepos);
17993 $plalign = $lalign;
17994 if (isset($this->PageAnnots[$this->page])) {
17995 $pask = count($this->PageAnnots[$this->page]);
17996 } else {
17997 $pask = 0;
17998 }
17999 if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
18000 AND (isset($this->emptypagemrk[$this->page]))
18001 AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) {
18002 $this->SetFont($fontname, $fontstyle, $fontsize);
18003 if ($wfill) {
18004 $this->SetFillColorArray($this->bgcolor);
18005 }
18006 }
18007 } // end newline
18008 if (isset($opentagpos)) {
18009 unset($opentagpos);
18010 }
18011 if ($dom[$key]['tag']) {
18012 if ($dom[$key]['opening']) {
18013 // get text indentation (if any)
18014 if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
18015 $this->textindent = $dom[$key]['text-indent'];
18016 $this->newline = true;
18017 }
18018 // table
18019 if (($dom[$key]['value'] == 'table') AND isset($dom[$key]['cols']) AND ($dom[$key]['cols'] > 0)) {
18020 // available page width
18021 if ($this->rtl) {
18022 $wtmp = $this->x - $this->lMargin;
18023 } else {
18024 $wtmp = $this->w - $this->rMargin - $this->x;
18025 }
18026 // get cell spacing
18027 if (isset($dom[$key]['attribute']['cellspacing'])) {
18028 $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
18029 $cellspacing = array('H' => $clsp, 'V' => $clsp);
18030 } elseif (isset($dom[$key]['border-spacing'])) {
18031 $cellspacing = $dom[$key]['border-spacing'];
18032 } else {
18033 $cellspacing = array('H' => 0, 'V' => 0);
18034 }
18035 // table width
18036 if (isset($dom[$key]['width'])) {
18037 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
18038 } else {
18039 $table_width = $wtmp;
18040 }
18041 $table_width -= (2 * $cellspacing['H']);
18042 if (!$this->inthead) {
18043 $this->y += $cellspacing['V'];
18044 }
18045 if ($this->rtl) {
18046 $cellspacingx = -$cellspacing['H'];
18047 } else {
18048 $cellspacingx = $cellspacing['H'];
18049 }
18050 // total table width without cellspaces
18051 $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
18052 // minimum column width
18053 // tcpdf-patch: begin
18054 if ($table_columns_width == 0 || $dom[$key]['cols'] == 0)
18055 {
18056 $table_min_column_width = 1;
18057 }
18058 else
18059 {
18060 $table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
18061 }
18062 if ($dom[$key]['cols'] != 0)
18063 {
18064 // array of custom column widths
18065 $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
18066 }
18067 // tcpdf-patch: end
18068 }
18069 // table row
18070 if ($dom[$key]['value'] == 'tr') {
18071 // reset column counter
18072 $colid = 0;
18073 }
18074 // table cell
18075 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
18076 $trid = $dom[$key]['parent'];
18077 $table_el = $dom[$trid]['parent'];
18078 if (!isset($dom[$table_el]['cols'])) {
18079 $dom[$table_el]['cols'] = $dom[$trid]['cols'];
18080 }
18081 // store border info
18082 $tdborder = 0;
18083 if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
18084 $tdborder = $dom[$key]['border'];
18085 }
18086 $colspan = intval($dom[$key]['attribute']['colspan']);
18087 if ($colspan <= 0) {
18088 $colspan = 1;
18089 }
18090 $old_cell_padding = $this->cell_padding;
18091 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
18092 $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
18093 $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
18094 } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
18095 $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
18096 } else {
18097 $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
18098 }
18099 $this->cell_padding = $current_cell_padding;
18100 if (isset($dom[$key]['height'])) {
18101 // minimum cell height
18102 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
18103 } else {
18104 $cellh = 0;
18105 }
18106 if (isset($dom[$key]['content'])) {
18107 $cell_content = $dom[$key]['content'];
18108 } else {
18109 $cell_content = '&nbsp;';
18110 }
18111 $tagtype = $dom[$key]['value'];
18112 $parentid = $key;
18113 while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
18114 // move $key index forward
18115 ++$key;
18116 }
18117 if (!isset($dom[$trid]['startpage'])) {
18118 $dom[$trid]['startpage'] = $this->page;
18119 } else {
18120 $this->setPage($dom[$trid]['startpage']);
18121 }
18122 if (!isset($dom[$trid]['startcolumn'])) {
18123 $dom[$trid]['startcolumn'] = $this->current_column;
18124 } elseif ($this->current_column != $dom[$trid]['startcolumn']) {
18125 $tmpx = $this->x;
18126 $this->selectColumn($dom[$trid]['startcolumn']);
18127 $this->x = $tmpx;
18128 }
18129 if (!isset($dom[$trid]['starty'])) {
18130 $dom[$trid]['starty'] = $this->y;
18131 } else {
18132 $this->y = $dom[$trid]['starty'];
18133 }
18134 if (!isset($dom[$trid]['startx'])) {
18135 $dom[$trid]['startx'] = $this->x;
18136 $this->x += $cellspacingx;
18137 } else {
18138 $this->x += ($cellspacingx / 2);
18139 }
18140 if (isset($dom[$parentid]['attribute']['rowspan'])) {
18141 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
18142 } else {
18143 $rowspan = 1;
18144 }
18145 // skip row-spanned cells started on the previous rows
18146 if (isset($dom[$table_el]['rowspans'])) {
18147 $rsk = 0;
18148 $rskmax = count($dom[$table_el]['rowspans']);
18149 while ($rsk < $rskmax) {
18150 $trwsp = $dom[$table_el]['rowspans'][$rsk];
18151 $rsstartx = $trwsp['startx'];
18152 $rsendx = $trwsp['endx'];
18153 // account for margin changes
18154 if ($trwsp['startpage'] < $this->page) {
18155 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
18156 $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
18157 $rsstartx -= $dl;
18158 $rsendx -= $dl;
18159 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
18160 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
18161 $rsstartx += $dl;
18162 $rsendx += $dl;
18163 }
18164 }
18165 if (($trwsp['rowspan'] > 0)
18166 AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps))
18167 AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps))
18168 AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) {
18169 // set the starting X position of the current cell
18170 $this->x = $rsendx + $cellspacingx;
18171 // increment column indicator
18172 $colid += $trwsp['colspan'];
18173 if (($trwsp['rowspan'] == 1)
18174 AND (isset($dom[$trid]['endy']))
18175 AND (isset($dom[$trid]['endpage']))
18176 AND (isset($dom[$trid]['endcolumn']))
18177 AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
18178 AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18179 // set ending Y position for row
18180 $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18181 $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
18182 }
18183 $rsk = 0;
18184 } else {
18185 ++$rsk;
18186 }
18187 }
18188 }
18189 if (isset($dom[$parentid]['width'])) {
18190 // user specified width
18191 $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
18192 $tmpcw = ($cellw / $colspan);
18193 for ($i = 0; $i < $colspan; ++$i) {
18194 $table_colwidths[($colid + $i)] = $tmpcw;
18195 }
18196 } else {
18197 // inherit column width
18198 $cellw = 0;
18199 for ($i = 0; $i < $colspan; ++$i) {
18200 $cellw += (isset($table_colwidths[($colid + $i)]) ? $table_colwidths[($colid + $i)] : 0);
18201 }
18202 }
18203 $cellw += (($colspan - 1) * $cellspacing['H']);
18204 // increment column indicator
18205 $colid += $colspan;
18206 // add rowspan information to table element
18207 if ($rowspan > 1) {
18208 $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startcolumn' => $this->current_column, 'startx' => $this->x, 'starty' => $this->y));
18209 }
18210 $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
18211 if ($rowspan > 1) {
18212 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
18213 }
18214 // push background colors
18215 if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
18216 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
18217 }
18218 // store border info
18219 if (isset($tdborder) AND !empty($tdborder)) {
18220 $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
18221 }
18222 $prevLastH = $this->lasth;
18223 // store some info for multicolumn mode
18224 if ($this->rtl) {
18225 $this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
18226 } else {
18227 $this->colxshift['x'] = $this->x - $this->lMargin;
18228 }
18229 $this->colxshift['s'] = $cellspacing;
18230 $this->colxshift['p'] = $current_cell_padding;
18231 // ****** write the cell content ******
18232 $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
18233 // restore some values
18234 $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
18235 $this->lasth = $prevLastH;
18236 $this->cell_padding = $old_cell_padding;
18237 $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
18238 // update the end of row position
18239 if ($rowspan <= 1) {
18240 if (isset($dom[$trid]['endy'])) {
18241 if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) {
18242 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
18243 } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) {
18244 $dom[$trid]['endy'] = $this->y;
18245 }
18246 } else {
18247 $dom[$trid]['endy'] = $this->y;
18248 }
18249 if (isset($dom[$trid]['endpage'])) {
18250 $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
18251 } else {
18252 $dom[$trid]['endpage'] = $this->page;
18253 }
18254 if (isset($dom[$trid]['endcolumn'])) {
18255 $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
18256 } else {
18257 $dom[$trid]['endcolumn'] = $this->current_column;
18258 }
18259 } else {
18260 // account for row-spanned cells
18261 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
18262 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
18263 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
18264 $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column;
18265 }
18266 if (isset($dom[$table_el]['rowspans'])) {
18267 // update endy and endpage on rowspanned cells
18268 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
18269 if ($trwsp['rowspan'] > 0) {
18270 if (isset($dom[$trid]['endpage'])) {
18271 if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18272 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18273 } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
18274 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
18275 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
18276 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
18277 } else {
18278 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
18279 }
18280 }
18281 }
18282 }
18283 }
18284 $this->x += ($cellspacingx / 2);
18285 } else {
18286 // opening tag (or self-closing tag)
18287 if (!isset($opentagpos)) {
18288 if ($this->inxobj) {
18289 // we are inside an XObject template
18290 $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
18291 } elseif (!$this->InFooter) {
18292 if (isset($this->footerlen[$this->page])) {
18293 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
18294 } else {
18295 $this->footerpos[$this->page] = $this->pagelen[$this->page];
18296 }
18297 $opentagpos = $this->footerpos[$this->page];
18298 }
18299 }
18300 $dom = $this->openHTMLTagHandler($dom, $key, $cell);
18301 }
18302 } else { // closing tag
18303 $prev_numpages = $this->numpages;
18304 $old_bordermrk = $this->bordermrk[$this->page];
18305 $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
18306 if ($this->bordermrk[$this->page] > $old_bordermrk) {
18307 $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk);
18308 }
18309 if ($prev_numpages > $this->numpages) {
18310 $startlinepage = $this->page;
18311 }
18312 }
18313 } elseif (strlen($dom[$key]['value']) > 0) {
18314 // print list-item
18315 if (!TCPDF_STATIC::empty_string($this->lispacer) AND ($this->lispacer != '^')) {
18316 $this->SetFont($pfontname, $pfontstyle, $pfontsize);
18317 $this->resetLastH();
18318 $minstartliney = $this->y;
18319 $maxbottomliney = ($startliney + $this->getCellHeight($this->FontSize));
18320 if (is_numeric($pfontsize) AND ($pfontsize > 0)) {
18321 $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
18322 }
18323 $this->SetFont($curfontname, $curfontstyle, $curfontsize);
18324 $this->resetLastH();
18325 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
18326 $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
18327 $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
18328 $this->y += ($this->getCellHeight(($pfontsize - $curfontsize) / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
18329 $minstartliney = min($this->y, $minstartliney);
18330 $maxbottomliney = max(($this->y + $this->getCellHeight($pfontsize / $this->k)), $maxbottomliney);
18331 }
18332 }
18333 // text
18334 $this->htmlvspace = 0;
18335 if ((!$this->premode) AND $this->isRTLTextDir()) {
18336 // reverse spaces order
18337 $lsp = ''; // left spaces
18338 $rsp = ''; // right spaces
18339 if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18340 $lsp = $matches[1];
18341 }
18342 if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18343 $rsp = $matches[1];
18344 }
18345 $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
18346 }
18347 if ($newline) {
18348 if (!$this->premode) {
18349 $prelen = strlen($dom[$key]['value']);
18350 if ($this->isRTLTextDir()) {
18351 // right trim except non-breaking space
18352 $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
18353 } else {
18354 // left trim except non-breaking space
18355 $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
18356 }
18357 $postlen = strlen($dom[$key]['value']);
18358 if (($postlen == 0) AND ($prelen > 0)) {
18359 $dom[$key]['trimmed_space'] = true;
18360 }
18361 }
18362 $newline = false;
18363 $firstblock = true;
18364 } else {
18365 $firstblock = false;
18366 // replace empty multiple spaces string with a single space
18367 $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']);
18368 }
18369 $strrest = '';
18370 if ($this->rtl) {
18371 $this->x -= $this->textindent;
18372 } else {
18373 $this->x += $this->textindent;
18374 }
18375 if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
18376 $strlinelen = $this->GetStringWidth($dom[$key]['value']);
18377 if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
18378 // HTML <a> Link
18379 $hrefcolor = '';
18380 if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
18381 $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
18382 }
18383 $hrefstyle = -1;
18384 if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
18385 $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
18386 }
18387 $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
18388 } else {
18389 $wadj = 0; // space to leave for block continuity
18390 if ($this->rtl) {
18391 $cwa = ($this->x - $this->lMargin);
18392 } else {
18393 $cwa = ($this->w - $this->rMargin - $this->x);
18394 }
18395 if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) {
18396 // check the next text blocks for continuity
18397 $nkey = ($key + 1);
18398 $write_block = true;
18399 $same_textdir = true;
18400 $tmp_fontname = $this->FontFamily;
18401 $tmp_fontstyle = $this->FontStyle;
18402 $tmp_fontsize = $this->FontSizePt;
18403 while ($write_block AND isset($dom[$nkey])) {
18404 if ($dom[$nkey]['tag']) {
18405 if ($dom[$nkey]['block']) {
18406 // end of block
18407 $write_block = false;
18408 }
18409 $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
18410 $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
18411 $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
18412 $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
18413 } else {
18414 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'+/', $this->re_space['m'], $dom[$nkey]['value']);
18415 if (isset($nextstr[0]) AND $same_textdir) {
18416 $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
18417 if (isset($nextstr[1])) {
18418 $write_block = false;
18419 }
18420 }
18421 }
18422 ++$nkey;
18423 }
18424 }
18425 if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) {
18426 $wadj = 0;
18427 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $dom[$key]['value']);
18428 $numblks = count($nextstr);
18429 if ($numblks > 1) {
18430 // try to split on blank spaces
18431 $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)]));
18432 } else {
18433 // set the entire block on new line
18434 $wadj = $this->GetStringWidth($nextstr[0]);
18435 }
18436 }
18437 // check for reversed text direction
18438 if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) {
18439 // LTR text on RTL direction or RTL text on LTR direction
18440 $reverse_dir = true;
18441 $this->rtl = !$this->rtl;
18442 $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems
18443 if ($this->rtl) {
18444 $this->x += $revshift;
18445 } else {
18446 $this->x -= $revshift;
18447 }
18448 $xws = $this->x;
18449 }
18450 // ****** write only until the end of the line and get the rest ******
18451 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
18452 // restore default direction
18453 if ($reverse_dir AND ($wadj == 0)) {
18454 $this->x = $xws;
18455 $this->rtl = !$this->rtl;
18456 $reverse_dir = false;
18457 }
18458 }
18459 }
18460 $this->textindent = 0;
18461 if (strlen($strrest) > 0) {
18462 // store the remaining string on the previous $key position
18463 $this->newline = true;
18464 if ($strrest == $dom[$key]['value']) {
18465 // used to avoid infinite loop
18466 ++$loop;
18467 } else {
18468 $loop = 0;
18469 }
18470 $dom[$key]['value'] = $strrest;
18471 if ($cell) {
18472 if ($this->rtl) {
18473 $this->x -= $this->cell_padding['R'];
18474 } else {
18475 $this->x += $this->cell_padding['L'];
18476 }
18477 }
18478 if ($loop < 3) {
18479 --$key;
18480 }
18481 } else {
18482 $loop = 0;
18483 // add the positive font spacing of the last character (if any)
18484 if ($this->font_spacing > 0) {
18485 if ($this->rtl) {
18486 $this->x -= $this->font_spacing;
18487 } else {
18488 $this->x += $this->font_spacing;
18489 }
18490 }
18491 }
18492 }
18493 ++$key;
18494 if (isset($dom[$key]['tag']) AND $dom[$key]['tag'] AND (!isset($dom[$key]['opening']) OR !$dom[$key]['opening']) AND isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
18495 // check if we are on a new page or on a new column
18496 if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) {
18497 // we are on a new page or on a new column and the total object height is less than the available vertical space.
18498 // restore previous object
18499 $this->rollbackTransaction(true);
18500 // restore previous values
18501 foreach ($this_method_vars as $vkey => $vval) {
18502 $$vkey = $vval;
18503 }
18504 if (!empty($dom[$key]['thead'])) {
18505 $this->inthead = true;
18506 }
18507 // add a page (or trig AcceptPageBreak() for multicolumn mode)
18508 $pre_y = $this->y;
18509 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
18510 $startliney = $this->y;
18511 }
18512 $undo = true; // avoid infinite loop
18513 } else {
18514 $undo = false;
18515 }
18516 }
18517 } // end for each $key
18518 // align the last line
18519 if (isset($startlinex)) {
18520 $yshift = ($minstartliney - $startliney);
18521 if (($yshift > 0) OR ($this->page > $startlinepage)) {
18522 $yshift = 0;
18523 }
18524 $t_x = 0;
18525 // the last line must be shifted to be aligned as requested
18526 $linew = abs($this->endlinex - $startlinex);
18527 if ($this->inxobj) {
18528 // we are inside an XObject template
18529 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
18530 if (isset($opentagpos)) {
18531 $midpos = $opentagpos;
18532 } else {
18533 $midpos = 0;
18534 }
18535 if ($midpos > 0) {
18536 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
18537 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
18538 } else {
18539 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
18540 $pend = '';
18541 }
18542 } else {
18543 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
18544 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18545 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18546 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
18547 } elseif (isset($opentagpos)) {
18548 $midpos = $opentagpos;
18549 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18550 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18551 $midpos = $this->footerpos[$startlinepage];
18552 } else {
18553 $midpos = 0;
18554 }
18555 if ($midpos > 0) {
18556 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
18557 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
18558 } else {
18559 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
18560 $pend = '';
18561 }
18562 }
18563 if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
18564 // calculate shifting amount
18565 $tw = $w;
18566 if ($this->lMargin != $prevlMargin) {
18567 $tw += ($prevlMargin - $this->lMargin);
18568 }
18569 if ($this->rMargin != $prevrMargin) {
18570 $tw += ($prevrMargin - $this->rMargin);
18571 }
18572 $one_space_width = $this->GetStringWidth(chr(32));
18573 $no = 0; // number of spaces on a line contained on a single block
18574 if ($this->isRTLTextDir()) { // RTL
18575 // remove left space if exist
18576 $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
18577 if ($pos1 > 0) {
18578 $pos1 = intval($pos1);
18579 if ($this->isUnicodeFont()) {
18580 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
18581 $spacelen = 2;
18582 } else {
18583 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
18584 $spacelen = 1;
18585 }
18586 if ($pos1 == $pos2) {
18587 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
18588 if (substr($pmid, $pos1, 4) == '[()]') {
18589 $linew -= $one_space_width;
18590 } elseif ($pos1 == strpos($pmid, '[(')) {
18591 $no = 1;
18592 }
18593 }
18594 }
18595 } else { // LTR
18596 // remove right space if exist
18597 $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
18598 if ($pos1 > 0) {
18599 $pos1 = intval($pos1);
18600 if ($this->isUnicodeFont()) {
18601 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
18602 $spacelen = 2;
18603 } else {
18604 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
18605 $spacelen = 1;
18606 }
18607 if ($pos1 == $pos2) {
18608 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
18609 $linew -= $one_space_width;
18610 }
18611 }
18612 }
18613 $mdiff = ($tw - $linew);
18614 if ($plalign == 'C') {
18615 if ($this->rtl) {
18616 $t_x = -($mdiff / 2);
18617 } else {
18618 $t_x = ($mdiff / 2);
18619 }
18620 } elseif ($plalign == 'R') {
18621 // right alignment on LTR document
18622 $t_x = $mdiff;
18623 } elseif ($plalign == 'L') {
18624 // left alignment on RTL document
18625 $t_x = -$mdiff;
18626 }
18627 } // end if startlinex
18628 if (($t_x != 0) OR ($yshift < 0)) {
18629 // shift the line
18630 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
18631 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18632 $endlinepos = strlen($pstart);
18633 if ($this->inxobj) {
18634 // we are inside an XObject template
18635 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
18636 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
18637 if ($pak >= $pask) {
18638 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
18639 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
18640 }
18641 }
18642 } else {
18643 $this->setPageBuffer($startlinepage, $pstart.$pend);
18644 // shift the annotations and links
18645 if (isset($this->PageAnnots[$this->page])) {
18646 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
18647 if ($pak >= $pask) {
18648 $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
18649 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
18650 }
18651 }
18652 }
18653 }
18654 $this->y -= $yshift;
18655 $yshift = 0;
18656 }
18657 }
18658 // restore previous values
18659 $this->setGraphicVars($gvars);
18660 if ($this->num_columns > 1) {
18661 $this->selectColumn();
18662 } elseif ($this->page > $prevPage) {
18663 $this->lMargin = $this->pagedim[$this->page]['olm'];
18664 $this->rMargin = $this->pagedim[$this->page]['orm'];
18665 }
18666 // restore previous list state
18667 $this->cell_height_ratio = $prev_cell_height_ratio;
18668 $this->listnum = $prev_listnum;
18669 $this->listordered = $prev_listordered;
18670 $this->listcount = $prev_listcount;
18671 $this->lispacer = $prev_lispacer;
18672 if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
18673 $this->Ln($this->lasth);
18674 if (($this->y < $maxbottomliney) AND ($startlinepage == $this->page)) {
18675 $this->y = $maxbottomliney;
18676 }
18677 }
18678 unset($dom);
18679 }
18680
18689 protected function openHTMLTagHandler($dom, $key, $cell) {
18690 $tag = $dom[$key];
18691 $parent = $dom[($dom[$key]['parent'])];
18692 $firsttag = ($key == 1);
18693 // check for text direction attribute
18694 if (isset($tag['dir'])) {
18695 $this->setTempRTL($tag['dir']);
18696 } else {
18697 $this->tmprtl = false;
18698 }
18699 if ($tag['block']) {
18700 $hbz = 0; // distance from y to line bottom
18701 $hb = 0; // vertical space between block tags
18702 // calculate vertical space for block tags
18703 if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
18704 $cur_h = $this->tagvspaces[$tag['value']][0]['h'];
18705 } elseif (isset($tag['fontsize'])) {
18706 $cur_h = $this->getCellHeight($tag['fontsize'] / $this->k);
18707 } else {
18708 $cur_h = $this->getCellHeight($this->FontSize);
18709 }
18710 if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
18711 $on = $this->tagvspaces[$tag['value']][0]['n'];
18712 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18713 $on = 0.6;
18714 } else {
18715 $on = 1;
18716 }
18717 if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br', 'hr')))) {
18718 $hb = 0;
18719 } else {
18720 $hb = ($on * $cur_h);
18721 }
18722 if (($this->htmlvspace <= 0) AND ($on > 0)) {
18723 if (isset($parent['fontsize'])) {
18724 $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
18725 } else {
18726 $hbz = $this->getCellHeight($this->FontSize);
18727 }
18728 }
18729 if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) {
18730 // fix vertical space after table
18731 $hbz = 0;
18732 }
18733 // closing vertical space
18734 $hbc = 0;
18735 if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
18736 $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
18737 } elseif (isset($parent['fontsize'])) {
18738 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
18739 } else {
18740 $pre_h = $this->getCellHeight($this->FontSize);
18741 }
18742 if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
18743 $cn = $this->tagvspaces[$tag['value']][1]['n'];
18744 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18745 $cn = 0.6;
18746 } else {
18747 $cn = 1;
18748 }
18749 if (isset($this->tagvspaces[$tag['value']][1])) {
18750 $hbc = ($cn * $pre_h);
18751 }
18752 }
18753 // Opening tag
18754 switch($tag['value']) {
18755 case 'table': {
18756 $cp = 0;
18757 $cs = 0;
18758 $dom[$key]['rowspans'] = array();
18759 if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
18760 $this->htmlvspace = 0;
18761 // set table header
18762 if (!TCPDF_STATIC::empty_string($dom[$key]['thead'])) {
18763 // set table header
18764 $this->thead = $dom[$key]['thead'];
18765 if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
18766 $this->theadMargins = array();
18767 $this->theadMargins['cell_padding'] = $this->cell_padding;
18768 $this->theadMargins['lmargin'] = $this->lMargin;
18769 $this->theadMargins['rmargin'] = $this->rMargin;
18770 $this->theadMargins['page'] = $this->page;
18771 $this->theadMargins['cell'] = $cell;
18772 $this->theadMargins['gvars'] = $this->getGraphicVars();
18773 }
18774 }
18775 }
18776 // store current margins and page
18777 $dom[$key]['old_cell_padding'] = $this->cell_padding;
18778 if (isset($tag['attribute']['cellpadding'])) {
18779 $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
18780 $this->SetCellPadding($pad);
18781 } elseif (isset($tag['padding'])) {
18782 $this->cell_padding = $tag['padding'];
18783 }
18784 if (isset($tag['attribute']['cellspacing'])) {
18785 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
18786 } elseif (isset($tag['border-spacing'])) {
18787 $cs = $tag['border-spacing']['V'];
18788 }
18789 $prev_y = $this->y;
18790 if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) {
18791 $this->inthead = true;
18792 // add a page (or trig AcceptPageBreak() for multicolumn mode)
18793 $this->checkPageBreak($this->PageBreakTrigger + 1);
18794 }
18795 break;
18796 }
18797 case 'tr': {
18798 // array of columns positions
18799 $dom[$key]['cellpos'] = array();
18800 break;
18801 }
18802 case 'hr': {
18803 if ((isset($tag['height'])) AND ($tag['height'] != '')) {
18804 $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
18805 } else {
18806 $hrHeight = $this->GetLineWidth();
18807 }
18808 $this->addHTMLVertSpace($hbz, max($hb, ($hrHeight / 2)), $cell, $firsttag);
18809 $x = $this->GetX();
18810 $y = $this->GetY();
18811 $wtmp = $this->w - $this->lMargin - $this->rMargin;
18812 if ($cell) {
18813 $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']);
18814 }
18815 if ((isset($tag['width'])) AND ($tag['width'] != '')) {
18816 $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
18817 } else {
18818 $hrWidth = $wtmp;
18819 }
18820 $prevlinewidth = $this->GetLineWidth();
18821 $this->SetLineWidth($hrHeight);
18822 $this->Line($x, $y, $x + $hrWidth, $y);
18823 $this->SetLineWidth($prevlinewidth);
18824 $this->addHTMLVertSpace(max($hbc, ($hrHeight / 2)), 0, $cell, !isset($dom[($key + 1)]));
18825 break;
18826 }
18827 case 'a': {
18828 if (array_key_exists('href', $tag['attribute'])) {
18829 $this->HREF['url'] = $tag['attribute']['href'];
18830 }
18831 break;
18832 }
18833 case 'img': {
18834 if (!empty($tag['attribute']['src'])) {
18835 if ($tag['attribute']['src'][0] === '@') {
18836 // data stream
18837 $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1));
18838 $type = '';
18839 } else {
18840 // get image type
18841 $type = TCPDF_IMAGES::getImageFileType($tag['attribute']['src']);
18842 }
18843 if (!isset($tag['width'])) {
18844 $tag['width'] = 0;
18845 }
18846 if (!isset($tag['height'])) {
18847 $tag['height'] = 0;
18848 }
18849 //if (!isset($tag['attribute']['align'])) {
18850 // the only alignment supported is "bottom"
18851 // further development is required for other modes.
18852 $tag['attribute']['align'] = 'bottom';
18853 //}
18854 switch($tag['attribute']['align']) {
18855 case 'top': {
18856 $align = 'T';
18857 break;
18858 }
18859 case 'middle': {
18860 $align = 'M';
18861 break;
18862 }
18863 case 'bottom': {
18864 $align = 'B';
18865 break;
18866 }
18867 default: {
18868 $align = 'B';
18869 break;
18870 }
18871 }
18872 $prevy = $this->y;
18873 $xpos = $this->x;
18874 $imglink = '';
18875 if (isset($this->HREF['url']) AND !TCPDF_STATIC::empty_string($this->HREF['url'])) {
18876 $imglink = $this->HREF['url'];
18877 if ($imglink[0] == '#') {
18878 // convert url to internal link
18879 $lnkdata = explode(',', $imglink);
18880 if (isset($lnkdata[0])) {
18881 $page = intval(substr($lnkdata[0], 1));
18882 if (empty($page) OR ($page <= 0)) {
18884 }
18885 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
18886 $lnky = floatval($lnkdata[1]);
18887 } else {
18888 $lnky = 0;
18889 }
18890 $imglink = $this->AddLink();
18891 $this->SetLink($imglink, $lnky, $page);
18892 }
18893 }
18894 }
18895 $border = 0;
18896 if (isset($tag['border']) AND !empty($tag['border'])) {
18897 // currently only support 1 (frame) or a combination of 'LTRB'
18898 $border = $tag['border'];
18899 }
18900 $iw = '';
18901 if (isset($tag['width'])) {
18902 $iw = $this->getHTMLUnitToUnits($tag['width'], ($tag['fontsize'] / $this->k), 'px', false);
18903 }
18904 $ih = '';
18905 if (isset($tag['height'])) {
18906 $ih = $this->getHTMLUnitToUnits($tag['height'], ($tag['fontsize'] / $this->k), 'px', false);
18907 }
18908 if (($type == 'eps') OR ($type == 'ai')) {
18909 $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
18910 } elseif ($type == 'svg') {
18911 $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
18912 } else {
18913 $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
18914 }
18915 switch($align) {
18916 case 'T': {
18917 $this->y = $prevy;
18918 break;
18919 }
18920 case 'M': {
18921 $this->y = (($this->img_rb_y + $prevy - ($this->getCellHeight($tag['fontsize'] / $this->k))) / 2);
18922 break;
18923 }
18924 case 'B': {
18925 $this->y = $this->img_rb_y - ($this->getCellHeight($tag['fontsize'] / $this->k) - ($this->getFontDescent($tag['fontname'], $tag['fontstyle'], $tag['fontsize']) * $this->cell_height_ratio));
18926 break;
18927 }
18928 }
18929 }
18930 break;
18931 }
18932 case 'dl': {
18934 if ($this->listnum == 1) {
18935 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18936 } else {
18937 $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18938 }
18939 break;
18940 }
18941 case 'dt': {
18942 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18943 break;
18944 }
18945 case 'dd': {
18946 if ($this->rtl) {
18947 $this->rMargin += $this->listindent;
18948 } else {
18949 $this->lMargin += $this->listindent;
18950 }
18952 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18953 break;
18954 }
18955 case 'ul':
18956 case 'ol': {
18958 if ($tag['value'] == 'ol') {
18959 $this->listordered[$this->listnum] = true;
18960 } else {
18961 $this->listordered[$this->listnum] = false;
18962 }
18963 if (isset($tag['attribute']['start'])) {
18964 $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
18965 } else {
18966 $this->listcount[$this->listnum] = 0;
18967 }
18968 if ($this->rtl) {
18969 $this->rMargin += $this->listindent;
18970 $this->x -= $this->listindent;
18971 } else {
18972 $this->lMargin += $this->listindent;
18973 $this->x += $this->listindent;
18974 }
18976 if ($this->listnum == 1) {
18977 if ($key > 1) {
18978 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18979 }
18980 } else {
18981 $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18982 }
18983 break;
18984 }
18985 case 'li': {
18986 if ($key > 2) {
18987 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18988 }
18989 if ($this->listordered[$this->listnum]) {
18990 // ordered item
18991 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
18992 $this->lispacer = $parent['attribute']['type'];
18993 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
18994 $this->lispacer = $parent['listtype'];
18995 } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
18996 $this->lispacer = $this->lisymbol;
18997 } else {
18998 $this->lispacer = '#';
18999 }
19000 ++$this->listcount[$this->listnum];
19001 if (isset($tag['attribute']['value'])) {
19002 $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
19003 }
19004 } else {
19005 // unordered item
19006 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
19007 $this->lispacer = $parent['attribute']['type'];
19008 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
19009 $this->lispacer = $parent['listtype'];
19010 } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
19011 $this->lispacer = $this->lisymbol;
19012 } else {
19013 $this->lispacer = '!';
19014 }
19015 }
19016 break;
19017 }
19018 case 'blockquote': {
19019 if ($this->rtl) {
19020 $this->rMargin += $this->listindent;
19021 } else {
19022 $this->lMargin += $this->listindent;
19023 }
19025 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19026 break;
19027 }
19028 case 'br': {
19029 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19030 break;
19031 }
19032 case 'div': {
19033 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19034 break;
19035 }
19036 case 'p': {
19037 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19038 break;
19039 }
19040 case 'pre': {
19041 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19042 $this->premode = true;
19043 break;
19044 }
19045 case 'sup': {
19046 $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
19047 break;
19048 }
19049 case 'sub': {
19050 $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
19051 break;
19052 }
19053 case 'h1':
19054 case 'h2':
19055 case 'h3':
19056 case 'h4':
19057 case 'h5':
19058 case 'h6': {
19059 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19060 break;
19061 }
19062 // Form fields (since 4.8.000 - 2009-09-07)
19063 case 'form': {
19064 if (isset($tag['attribute']['action'])) {
19065 $this->form_action = $tag['attribute']['action'];
19066 } else {
19067 $this->Error('Please explicitly set action attribute path!');
19068 }
19069 if (isset($tag['attribute']['enctype'])) {
19070 $this->form_enctype = $tag['attribute']['enctype'];
19071 } else {
19072 $this->form_enctype = 'application/x-www-form-urlencoded';
19073 }
19074 if (isset($tag['attribute']['method'])) {
19075 $this->form_mode = $tag['attribute']['method'];
19076 } else {
19077 $this->form_mode = 'post';
19078 }
19079 break;
19080 }
19081 case 'input': {
19082 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19083 $name = $tag['attribute']['name'];
19084 } else {
19085 break;
19086 }
19087 $prop = array();
19088 $opt = array();
19089 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19090 $prop['readonly'] = true;
19091 }
19092 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19093 $value = $tag['attribute']['value'];
19094 }
19095 if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC::empty_string($tag['attribute']['maxlength'])) {
19096 $opt['maxlen'] = intval($tag['attribute']['maxlength']);
19097 }
19098 $h = $this->getCellHeight($this->FontSize);
19099 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19100 $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
19101 } else {
19102 $w = $h;
19103 }
19104 if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
19105 $checked = true;
19106 } else {
19107 $checked = false;
19108 }
19109 if (isset($tag['align'])) {
19110 switch ($tag['align']) {
19111 case 'C': {
19112 $opt['q'] = 1;
19113 break;
19114 }
19115 case 'R': {
19116 $opt['q'] = 2;
19117 break;
19118 }
19119 case 'L':
19120 default: {
19121 break;
19122 }
19123 }
19124 }
19125 switch ($tag['attribute']['type']) {
19126 case 'text': {
19127 if (isset($value)) {
19128 $opt['v'] = $value;
19129 }
19130 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19131 break;
19132 }
19133 case 'password': {
19134 if (isset($value)) {
19135 $opt['v'] = $value;
19136 }
19137 $prop['password'] = 'true';
19138 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19139 break;
19140 }
19141 case 'checkbox': {
19142 if (!isset($value)) {
19143 break;
19144 }
19145 $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
19146 break;
19147 }
19148 case 'radio': {
19149 if (!isset($value)) {
19150 break;
19151 }
19152 $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
19153 break;
19154 }
19155 case 'submit': {
19156 if (!isset($value)) {
19157 $value = 'submit';
19158 }
19159 $w = $this->GetStringWidth($value) * 1.5;
19160 $h *= 1.6;
19161 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19162 $action = array();
19163 $action['S'] = 'SubmitForm';
19165 if ($this->form_enctype != 'FDF') {
19166 $action['Flags'] = array('ExportFormat');
19167 }
19168 if ($this->form_mode == 'get') {
19169 $action['Flags'] = array('GetMethod');
19170 }
19171 $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
19172 break;
19173 }
19174 case 'reset': {
19175 if (!isset($value)) {
19176 $value = 'reset';
19177 }
19178 $w = $this->GetStringWidth($value) * 1.5;
19179 $h *= 1.6;
19180 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19181 $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
19182 break;
19183 }
19184 case 'file': {
19185 $prop['fileSelect'] = 'true';
19186 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19187 if (!isset($value)) {
19188 $value = '*';
19189 }
19190 $w = $this->GetStringWidth($value) * 2;
19191 $h *= 1.2;
19192 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19193 $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
19194 $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19195 break;
19196 }
19197 case 'hidden': {
19198 if (isset($value)) {
19199 $opt['v'] = $value;
19200 }
19201 $opt['f'] = array('invisible', 'hidden');
19202 $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
19203 break;
19204 }
19205 case 'image': {
19206 // THIS TYPE MUST BE FIXED
19207 if (isset($tag['attribute']['src']) AND !TCPDF_STATIC::empty_string($tag['attribute']['src'])) {
19208 $img = $tag['attribute']['src'];
19209 } else {
19210 break;
19211 }
19212 $value = 'img';
19213 //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
19214 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19215 $jsaction = $tag['attribute']['onclick'];
19216 } else {
19217 $jsaction = '';
19218 }
19219 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19220 break;
19221 }
19222 case 'button': {
19223 if (!isset($value)) {
19224 $value = ' ';
19225 }
19226 $w = $this->GetStringWidth($value) * 1.5;
19227 $h *= 1.6;
19228 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19229 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19230 $jsaction = $tag['attribute']['onclick'];
19231 } else {
19232 $jsaction = '';
19233 }
19234 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19235 break;
19236 }
19237 }
19238 break;
19239 }
19240 case 'textarea': {
19241 $prop = array();
19242 $opt = array();
19243 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19244 $prop['readonly'] = true;
19245 }
19246 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19247 $name = $tag['attribute']['name'];
19248 } else {
19249 break;
19250 }
19251 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19252 $opt['v'] = $tag['attribute']['value'];
19253 }
19254 if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC::empty_string($tag['attribute']['cols'])) {
19255 $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
19256 } else {
19257 $w = 40;
19258 }
19259 if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC::empty_string($tag['attribute']['rows'])) {
19260 $h = intval($tag['attribute']['rows']) * $this->getCellHeight($this->FontSize);
19261 } else {
19262 $h = 10;
19263 }
19264 $prop['multiline'] = 'true';
19265 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19266 break;
19267 }
19268 case 'select': {
19269 $h = $this->getCellHeight($this->FontSize);
19270 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19271 $h *= ($tag['attribute']['size'] + 1);
19272 }
19273 $prop = array();
19274 $opt = array();
19275 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19276 $name = $tag['attribute']['name'];
19277 } else {
19278 break;
19279 }
19280 $w = 0;
19281 if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC::empty_string($tag['attribute']['opt'])) {
19282 $options = explode('#!NwL!#', $tag['attribute']['opt']);
19283 $values = array();
19284 foreach ($options as $val) {
19285 if (strpos($val, '#!TaB!#') !== false) {
19286 $opts = explode('#!TaB!#', $val);
19287 $values[] = $opts;
19288 $w = max($w, $this->GetStringWidth($opts[1]));
19289 } else {
19290 $values[] = $val;
19291 $w = max($w, $this->GetStringWidth($val));
19292 }
19293 }
19294 } else {
19295 break;
19296 }
19297 $w *= 2;
19298 if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
19299 $prop['multipleSelection'] = 'true';
19300 $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19301 } else {
19302 $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19303 }
19304 break;
19305 }
19306 case 'tcpdf': {
19307 if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
19308 // Special tag used to call TCPDF methods
19309 if (isset($tag['attribute']['method'])) {
19310 $tcpdf_method = $tag['attribute']['method'];
19311 if (method_exists($this, $tcpdf_method)) {
19312 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
19313 $params = $this->unserializeTCPDFtagParameters($tag['attribute']['params']);
19314 call_user_func_array(array($this, $tcpdf_method), $params);
19315 } else {
19316 $this->$tcpdf_method();
19317 }
19318 $this->newline = true;
19319 }
19320 }
19321 }
19322 break;
19323 }
19324 default: {
19325 break;
19326 }
19327 }
19328 // define tags that support borders and background colors
19329 $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
19330 if (in_array($tag['value'], $bordertags)) {
19331 // set border
19332 $dom[$key]['borderposition'] = $this->getBorderStartPosition();
19333 }
19334 if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
19335 $pba = $dom[$key]['attribute']['pagebreakafter'];
19336 // check for pagebreak
19337 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19338 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19339 $this->checkPageBreak($this->PageBreakTrigger + 1);
19340 }
19341 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19342 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19343 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19344 $this->checkPageBreak($this->PageBreakTrigger + 1);
19345 }
19346 }
19347 return $dom;
19348 }
19349
19359 protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
19360 $tag = $dom[$key];
19361 $parent = $dom[($dom[$key]['parent'])];
19362 $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
19363 $in_table_head = false;
19364 // maximum x position (used to draw borders)
19365 if ($this->rtl) {
19366 $xmax = $this->w;
19367 } else {
19368 $xmax = 0;
19369 }
19370 if ($tag['block']) {
19371 $hbz = 0; // distance from y to line bottom
19372 $hb = 0; // vertical space between block tags
19373 // calculate vertical space for block tags
19374 if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
19375 $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
19376 } elseif (isset($parent['fontsize'])) {
19377 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
19378 } else {
19379 $pre_h = $this->getCellHeight($this->FontSize);
19380 }
19381 if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
19382 $cn = $this->tagvspaces[$tag['value']][1]['n'];
19383 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
19384 $cn = 0.6;
19385 } else {
19386 $cn = 1;
19387 }
19388 if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) {
19389 $hb = 0;
19390 } else {
19391 $hb = ($cn * $pre_h);
19392 }
19393 if ($maxbottomliney > $this->PageBreakTrigger) {
19394 $hbz = $this->getCellHeight($this->FontSize);
19395 } elseif ($this->y < $maxbottomliney) {
19396 $hbz = ($maxbottomliney - $this->y);
19397 }
19398 }
19399 // Closing tag
19400 switch($tag['value']) {
19401 case 'tr': {
19402 $table_el = $dom[($dom[$key]['parent'])]['parent'];
19403 if (!isset($parent['endy'])) {
19404 $dom[($dom[$key]['parent'])]['endy'] = $this->y;
19405 $parent['endy'] = $this->y;
19406 }
19407 if (!isset($parent['endpage'])) {
19408 $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
19409 $parent['endpage'] = $this->page;
19410 }
19411 if (!isset($parent['endcolumn'])) {
19412 $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column;
19413 $parent['endcolumn'] = $this->current_column;
19414 }
19415 // update row-spanned cells
19416 if (isset($dom[$table_el]['rowspans'])) {
19417 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19418 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
19419 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19420 if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
19421 $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
19422 } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
19423 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19424 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19425 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19426 }
19427 }
19428 }
19429 // report new endy and endpage to the rowspanned cells
19430 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19431 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19432 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
19433 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19434 $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
19435 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19436 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
19437 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19438 }
19439 }
19440 // update remaining rowspanned cells
19441 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19442 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19443 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
19444 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
19445 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
19446 }
19447 }
19448 }
19449 $prev_page = $this->page;
19450 $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
19451 if ($this->num_columns > 1) {
19452 if (($prev_page < $this->page)
19453 AND ((($this->current_column == 0) AND ($dom[($dom[$key]['parent'])]['endcolumn'] == ($this->num_columns - 1)))
19454 OR ($this->current_column == $dom[($dom[$key]['parent'])]['endcolumn']))) {
19455 // page jump
19456 $this->selectColumn(0);
19457 $dom[($dom[$key]['parent'])]['endcolumn'] = 0;
19458 $dom[($dom[$key]['parent'])]['endy'] = $this->y;
19459 } else {
19460 $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
19461 $this->y = $dom[($dom[$key]['parent'])]['endy'];
19462 }
19463 } else {
19464 $this->y = $dom[($dom[$key]['parent'])]['endy'];
19465 }
19466 if (isset($dom[$table_el]['attribute']['cellspacing'])) {
19467 $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
19468 } elseif (isset($dom[$table_el]['border-spacing'])) {
19469 $this->y += $dom[$table_el]['border-spacing']['V'];
19470 }
19471 $this->Ln(0, $cell);
19472 if ($this->current_column == $parent['startcolumn']) {
19473 $this->x = $parent['startx'];
19474 }
19475 // account for booklet mode
19476 if ($this->page > $parent['startpage']) {
19477 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
19478 $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
19479 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
19480 $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
19481 }
19482 }
19483 break;
19484 }
19485 case 'tablehead':
19486 // closing tag used for the thead part
19487 $in_table_head = true;
19488 $this->inthead = false;
19489 case 'table': {
19490 $table_el = $parent;
19491 // set default border
19492 if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
19493 // set default border
19494 $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
19495 } else {
19496 $border = 0;
19497 }
19498 $default_border = $border;
19499 // fix bottom line alignment of last line before page break
19500 foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
19501 // update row-spanned cells
19502 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19503 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19504 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) {
19505 $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
19506 }
19507 if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) {
19508 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
19509 }
19510 }
19511 }
19512 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
19513 $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
19514 $dom[$prevtrkey]['endy'] = $pgendy;
19515 // update row-spanned cells
19516 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19517 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19518 if (($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
19519 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
19520 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
19521 }
19522 }
19523 }
19524 }
19525 $prevtrkey = $trkey;
19526 $table_el = $dom[($dom[$key]['parent'])];
19527 }
19528 // for each row
19529 if (count($table_el['trids']) > 0) {
19530 unset($xmax);
19531 }
19532 foreach ($table_el['trids'] as $j => $trkey) {
19533 $parent = $dom[$trkey];
19534 if (!isset($xmax)) {
19535 $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
19536 }
19537 // for each cell on the row
19538 foreach ($parent['cellpos'] as $k => $cellpos) {
19539 if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
19540 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
19541 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
19542 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
19543 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
19544 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
19545 $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
19546 $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
19547 } else {
19548 $endy = $parent['endy'];
19549 $startpage = $parent['startpage'];
19550 $endpage = $parent['endpage'];
19551 $startcolumn = $parent['startcolumn'];
19552 $endcolumn = $parent['endcolumn'];
19553 }
19554 if ($this->num_columns == 0) {
19555 $this->num_columns = 1;
19556 }
19557 if (isset($cellpos['border'])) {
19558 $border = $cellpos['border'];
19559 }
19560 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
19561 $this->SetFillColorArray($cellpos['bgcolor']);
19562 $fill = true;
19563 } else {
19564 $fill = false;
19565 }
19566 $x = $cellpos['startx'];
19567 $y = $parent['starty'];
19568 $starty = $y;
19569 $w = abs($cellpos['endx'] - $cellpos['startx']);
19570 // get border modes
19571 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
19572 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
19573 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19574 // design borders around HTML cells.
19575 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
19576 $ccode = '';
19577 $this->setPage($page);
19578 if ($this->num_columns < 2) {
19579 // single-column mode
19580 $this->x = $x;
19581 $this->y = $this->tMargin;
19582 }
19583 // account for margin changes
19584 if ($page > $startpage) {
19585 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
19586 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
19587 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
19588 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
19589 }
19590 }
19591 if ($startpage == $endpage) { // single page
19592 $deltacol = 0;
19593 $deltath = 0;
19594 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
19595 $this->selectColumn($column);
19596 if ($startcolumn == $endcolumn) { // single column
19597 $cborder = $border;
19598 $h = $endy - $parent['starty'];
19599 $this->y = $y;
19600 $this->x = $x;
19601 } elseif ($column == $startcolumn) { // first column
19602 $cborder = $border_start;
19603 $this->y = $starty;
19604 $this->x = $x;
19605 $h = $this->h - $this->y - $this->bMargin;
19606 if ($this->rtl) {
19607 $deltacol = $this->x + $this->rMargin - $this->w;
19608 } else {
19609 $deltacol = $this->x - $this->lMargin;
19610 }
19611 } elseif ($column == $endcolumn) { // end column
19612 $cborder = $border_end;
19613 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19614 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19615 }
19616 $this->x += $deltacol;
19617 $h = $endy - $this->y;
19618 } else { // middle column
19619 $cborder = $border_middle;
19620 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19621 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19622 }
19623 $this->x += $deltacol;
19624 $h = $this->h - $this->y - $this->bMargin;
19625 }
19626 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19627 } // end for each column
19628 } elseif ($page == $startpage) { // first page
19629 $deltacol = 0;
19630 $deltath = 0;
19631 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
19632 $this->selectColumn($column);
19633 if ($column == $startcolumn) { // first column
19634 $cborder = $border_start;
19635 $this->y = $starty;
19636 $this->x = $x;
19637 $h = $this->h - $this->y - $this->bMargin;
19638 if ($this->rtl) {
19639 $deltacol = $this->x + $this->rMargin - $this->w;
19640 } else {
19641 $deltacol = $this->x - $this->lMargin;
19642 }
19643 } else { // middle column
19644 $cborder = $border_middle;
19645 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19646 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19647 }
19648 $this->x += $deltacol;
19649 $h = $this->h - $this->y - $this->bMargin;
19650 }
19651 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19652 } // end for each column
19653 } elseif ($page == $endpage) { // last page
19654 $deltacol = 0;
19655 $deltath = 0;
19656 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
19657 $this->selectColumn($column);
19658 if ($column == $endcolumn) { // end column
19659 $cborder = $border_end;
19660 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19661 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19662 }
19663 $this->x += $deltacol;
19664 $h = $endy - $this->y;
19665 } else { // middle column
19666 $cborder = $border_middle;
19667 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19668 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19669 }
19670 $this->x += $deltacol;
19671 $h = $this->h - $this->y - $this->bMargin;
19672 }
19673 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19674 } // end for each column
19675 } else { // middle page
19676 $deltacol = 0;
19677 $deltath = 0;
19678 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
19679 $this->selectColumn($column);
19680 $cborder = $border_middle;
19681 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19682 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19683 }
19684 $this->x += $deltacol;
19685 $h = $this->h - $this->y - $this->bMargin;
19686 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19687 } // end for each column
19688 }
19689 if (!empty($cborder) OR !empty($fill)) {
19690 $offsetlen = strlen($ccode);
19691 // draw border and fill
19692 if ($this->inxobj) {
19693 // we are inside an XObject template
19694 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
19695 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
19696 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
19697 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
19698 } else {
19699 $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
19700 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
19701 }
19702 $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
19703 $pstart = substr($pagebuff, 0, $pagemark);
19704 $pend = substr($pagebuff, $pagemark);
19705 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
19706 } else {
19707 // draw border and fill
19708 if (end($this->transfmrk[$this->page]) !== false) {
19709 $pagemarkkey = key($this->transfmrk[$this->page]);
19710 $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
19711 } elseif ($this->InFooter) {
19712 $pagemark = $this->footerpos[$this->page];
19713 } else {
19714 $pagemark = $this->intmrk[$this->page];
19715 }
19716 $pagebuff = $this->getPageBuffer($this->page);
19717 $pstart = substr($pagebuff, 0, $pagemark);
19718 $pend = substr($pagebuff, $pagemark);
19719 $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
19720 }
19721 }
19722 } // end for each page
19723 // restore default border
19724 $border = $default_border;
19725 } // end for each cell on the row
19726 if (isset($table_el['attribute']['cellspacing'])) {
19727 $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
19728 } elseif (isset($table_el['border-spacing'])) {
19729 $this->y += $table_el['border-spacing']['V'];
19730 }
19731 $this->Ln(0, $cell);
19732 $this->x = $parent['startx'];
19733 if ($endpage > $startpage) {
19734 if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
19735 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
19736 } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
19737 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
19738 }
19739 }
19740 }
19741 if (!$in_table_head) { // we are not inside a thead section
19742 $this->cell_padding = $table_el['old_cell_padding'];
19743 // reset row height
19744 $this->resetLastH();
19745 if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) {
19746 $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]);
19747 if (($plendiff > 0) AND ($plendiff < 60)) {
19748 $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff);
19749 if (substr($pagediff, 0, 5) == 'BT /F') {
19750 // the difference is only a font setting
19751 $plendiff = 0;
19752 }
19753 }
19754 if ($plendiff == 0) {
19755 // remove last blank page
19756 $this->deletePage($this->numpages);
19757 }
19758 }
19759 if (isset($this->theadMargins['top'])) {
19760 // restore top margin
19761 $this->tMargin = $this->theadMargins['top'];
19762 }
19763 if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
19764 // reset main table header
19765 $this->thead = '';
19766 $this->theadMargins = array();
19767 $this->pagedim[$this->page]['tm'] = $this->tMargin;
19768 }
19769 }
19770 $parent = $table_el;
19771 break;
19772 }
19773 case 'a': {
19774 $this->HREF = '';
19775 break;
19776 }
19777 case 'sup': {
19778 $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
19779 break;
19780 }
19781 case 'sub': {
19782 $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k));
19783 break;
19784 }
19785 case 'div': {
19786 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19787 break;
19788 }
19789 case 'blockquote': {
19790 if ($this->rtl) {
19791 $this->rMargin -= $this->listindent;
19792 } else {
19793 $this->lMargin -= $this->listindent;
19794 }
19796 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19797 break;
19798 }
19799 case 'p': {
19800 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19801 break;
19802 }
19803 case 'pre': {
19804 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19805 $this->premode = false;
19806 break;
19807 }
19808 case 'dl': {
19810 if ($this->listnum <= 0) {
19811 $this->listnum = 0;
19812 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19813 } else {
19814 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19815 }
19816 $this->resetLastH();
19817 break;
19818 }
19819 case 'dt': {
19820 $this->lispacer = '';
19821 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19822 break;
19823 }
19824 case 'dd': {
19825 $this->lispacer = '';
19826 if ($this->rtl) {
19827 $this->rMargin -= $this->listindent;
19828 } else {
19829 $this->lMargin -= $this->listindent;
19830 }
19832 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19833 break;
19834 }
19835 case 'ul':
19836 case 'ol': {
19838 $this->lispacer = '';
19839 if ($this->rtl) {
19840 $this->rMargin -= $this->listindent;
19841 } else {
19842 $this->lMargin -= $this->listindent;
19843 }
19845 if ($this->listnum <= 0) {
19846 $this->listnum = 0;
19847 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19848 } else {
19849 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19850 }
19851 $this->resetLastH();
19852 break;
19853 }
19854 case 'li': {
19855 $this->lispacer = '';
19856 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19857 break;
19858 }
19859 case 'h1':
19860 case 'h2':
19861 case 'h3':
19862 case 'h4':
19863 case 'h5':
19864 case 'h6': {
19865 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19866 break;
19867 }
19868 // Form fields (since 4.8.000 - 2009-09-07)
19869 case 'form': {
19870 $this->form_action = '';
19871 $this->form_enctype = 'application/x-www-form-urlencoded';
19872 break;
19873 }
19874 default : {
19875 break;
19876 }
19877 }
19878 // draw border and background (if any)
19879 $this->drawHTMLTagBorder($parent, $xmax);
19880 if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
19881 $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
19882 // check for pagebreak
19883 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19884 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19885 $this->checkPageBreak($this->PageBreakTrigger + 1);
19886 }
19887 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19888 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19889 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19890 $this->checkPageBreak($this->PageBreakTrigger + 1);
19891 }
19892 }
19893 $this->tmprtl = false;
19894 return $dom;
19895 }
19896
19906 protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
19907 if ($firsttag) {
19908 $this->Ln(0, $cell);
19909 $this->htmlvspace = 0;
19910 return;
19911 }
19912 if ($lasttag) {
19913 $this->Ln($hbz, $cell);
19914 $this->htmlvspace = 0;
19915 return;
19916 }
19917 if ($hb < $this->htmlvspace) {
19918 $hd = 0;
19919 } else {
19920 $hd = $hb - $this->htmlvspace;
19921 $this->htmlvspace = $hb;
19922 }
19923 $this->Ln(($hbz + $hd), $cell);
19924 }
19925
19932 protected function getBorderStartPosition() {
19933 if ($this->rtl) {
19934 $xmax = $this->lMargin;
19935 } else {
19936 $xmax = $this->w - $this->rMargin;
19937 }
19938 return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax);
19939 }
19940
19948 protected function drawHTMLTagBorder($tag, $xmax) {
19949 if (!isset($tag['borderposition'])) {
19950 // nothing to draw
19951 return;
19952 }
19953 $prev_x = $this->x;
19954 $prev_y = $this->y;
19955 $prev_lasth = $this->lasth;
19956 $border = 0;
19957 $fill = false;
19958 $this->lasth = 0;
19959 if (isset($tag['border']) AND !empty($tag['border'])) {
19960 // get border style
19961 $border = $tag['border'];
19962 if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
19963 // border for table header
19964 $border = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19965 }
19966 }
19967 if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
19968 // get background color
19969 $old_bgcolor = $this->bgcolor;
19970 $this->SetFillColorArray($tag['bgcolor']);
19971 $fill = true;
19972 }
19973 if (!$border AND !$fill) {
19974 // nothing to draw
19975 return;
19976 }
19977 if (isset($tag['attribute']['cellspacing'])) {
19978 $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
19979 $cellspacing = array('H' => $clsp, 'V' => $clsp);
19980 } elseif (isset($tag['border-spacing'])) {
19981 $cellspacing = $tag['border-spacing'];
19982 } else {
19983 $cellspacing = array('H' => 0, 'V' => 0);
19984 }
19985 if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
19986 // draw the border externally respect the sqare edge.
19987 $border['mode'] = 'ext';
19988 }
19989 if ($this->rtl) {
19990 if ($xmax >= $tag['borderposition']['x']) {
19991 $xmax = $tag['borderposition']['xmax'];
19992 }
19993 $w = ($tag['borderposition']['x'] - $xmax);
19994 } else {
19995 if ($xmax <= $tag['borderposition']['x']) {
19996 $xmax = $tag['borderposition']['xmax'];
19997 }
19998 $w = ($xmax - $tag['borderposition']['x']);
19999 }
20000 if ($w <= 0) {
20001 return;
20002 }
20003 $w += $cellspacing['H'];
20004 $startpage = $tag['borderposition']['page'];
20005 $startcolumn = $tag['borderposition']['column'];
20006 $x = $tag['borderposition']['x'];
20007 $y = $tag['borderposition']['y'];
20008 $endpage = $this->page;
20009 $starty = $tag['borderposition']['y'] - $cellspacing['V'];
20010 $currentY = $this->y;
20011 $this->x = $x;
20012 // get latest column
20013 $endcolumn = $this->current_column;
20014 if ($this->num_columns == 0) {
20015 $this->num_columns = 1;
20016 }
20017 // get border modes
20018 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
20019 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
20020 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
20021 // temporary disable page regions
20022 $temp_page_regions = $this->page_regions;
20023 $this->page_regions = array();
20024 // design borders around HTML cells.
20025 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
20026 $ccode = '';
20027 $this->setPage($page);
20028 if ($this->num_columns < 2) {
20029 // single-column mode
20030 $this->x = $x;
20031 $this->y = $this->tMargin;
20032 }
20033 // account for margin changes
20034 if ($page > $startpage) {
20035 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
20036 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
20037 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
20038 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
20039 }
20040 }
20041 if ($startpage == $endpage) {
20042 // single page
20043 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
20044 $this->selectColumn($column);
20045 if ($startcolumn == $endcolumn) { // single column
20046 $cborder = $border;
20047 $h = ($currentY - $y) + $cellspacing['V'];
20048 $this->y = $starty;
20049 } elseif ($column == $startcolumn) { // first column
20050 $cborder = $border_start;
20051 $this->y = $starty;
20052 $h = $this->h - $this->y - $this->bMargin;
20053 } elseif ($column == $endcolumn) { // end column
20054 $cborder = $border_end;
20055 $h = $currentY - $this->y;
20056 } else { // middle column
20057 $cborder = $border_middle;
20058 $h = $this->h - $this->y - $this->bMargin;
20059 }
20060 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20061 } // end for each column
20062 } elseif ($page == $startpage) { // first page
20063 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
20064 $this->selectColumn($column);
20065 if ($column == $startcolumn) { // first column
20066 $cborder = $border_start;
20067 $this->y = $starty;
20068 $h = $this->h - $this->y - $this->bMargin;
20069 } else { // middle column
20070 $cborder = $border_middle;
20071 $h = $this->h - $this->y - $this->bMargin;
20072 }
20073 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20074 } // end for each column
20075 } elseif ($page == $endpage) { // last page
20076 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
20077 $this->selectColumn($column);
20078 if ($column == $endcolumn) {
20079 // end column
20080 $cborder = $border_end;
20081 $h = $currentY - $this->y;
20082 } else {
20083 // middle column
20084 $cborder = $border_middle;
20085 $h = $this->h - $this->y - $this->bMargin;
20086 }
20087 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20088 } // end for each column
20089 } else { // middle page
20090 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
20091 $this->selectColumn($column);
20092 $cborder = $border_middle;
20093 $h = $this->h - $this->y - $this->bMargin;
20094 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20095 } // end for each column
20096 }
20097 if ($cborder OR $fill) {
20098 $offsetlen = strlen($ccode);
20099 // draw border and fill
20100 if ($this->inxobj) {
20101 // we are inside an XObject template
20102 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
20103 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
20104 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
20105 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
20106 } else {
20107 $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
20108 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
20109 }
20110 $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
20111 $pstart = substr($pagebuff, 0, $pagemark);
20112 $pend = substr($pagebuff, $pagemark);
20113 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
20114 } else {
20115 if (end($this->transfmrk[$this->page]) !== false) {
20116 $pagemarkkey = key($this->transfmrk[$this->page]);
20117 $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
20118 } elseif ($this->InFooter) {
20119 $pagemark = $this->footerpos[$this->page];
20120 } else {
20121 $pagemark = $this->intmrk[$this->page];
20122 }
20123 $pagebuff = $this->getPageBuffer($this->page);
20124 $pstart = substr($pagebuff, 0, $pagemark);
20125 $pend = substr($pagebuff, $pagemark);
20126 $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
20127 $this->bordermrk[$this->page] += $offsetlen;
20128 $this->cntmrk[$this->page] += $offsetlen;
20129 }
20130 }
20131 } // end for each page
20132 // restore page regions
20133 $this->page_regions = $temp_page_regions;
20134 if (isset($old_bgcolor)) {
20135 // restore background color
20136 $this->SetFillColorArray($old_bgcolor);
20137 }
20138 // restore pointer position
20139 $this->x = $prev_x;
20140 $this->y = $prev_y;
20141 $this->lasth = $prev_lasth;
20142 }
20143
20150 public function setLIsymbol($symbol='!') {
20151 // check for custom image symbol
20152 if (substr($symbol, 0, 4) == 'img|') {
20153 $this->lisymbol = $symbol;
20154 return;
20155 }
20156 $symbol = strtolower($symbol);
20157 $valid_symbols = array('!', '#', 'disc', 'circle', 'square', '1', 'decimal', 'decimal-leading-zero', 'i', 'lower-roman', 'I', 'upper-roman', 'a', 'lower-alpha', 'lower-latin', 'A', 'upper-alpha', 'upper-latin', 'lower-greek');
20158 if (in_array($symbol, $valid_symbols)) {
20159 $this->lisymbol = $symbol;
20160 } else {
20161 $this->lisymbol = '';
20162 }
20163 }
20164
20173 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
20174 $this->booklet = $booklet;
20175 if ($inner >= 0) {
20176 $this->lMargin = $inner;
20177 }
20178 if ($outer >= 0) {
20179 $this->rMargin = $outer;
20180 }
20181 }
20182
20189 protected function swapMargins($reverse=true) {
20190 if ($reverse) {
20191 // swap left and right margins
20192 $mtemp = $this->original_lMargin;
20193 $this->original_lMargin = $this->original_rMargin;
20194 $this->original_rMargin = $mtemp;
20195 $deltam = $this->original_lMargin - $this->original_rMargin;
20196 $this->lMargin += $deltam;
20197 $this->rMargin -= $deltam;
20198 }
20199 }
20200
20213 public function setHtmlVSpace($tagvs) {
20214 $this->tagvspaces = $tagvs;
20215 }
20216
20223 public function setListIndentWidth($width) {
20224 return $this->customlistindent = floatval($width);
20225 }
20226
20233 public function setOpenCell($isopen) {
20234 $this->opencell = $isopen;
20235 }
20236
20244 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
20245 $this->htmlLinkColorArray = $color;
20246 $this->htmlLinkFontStyle = $fontstyle;
20247 }
20248
20259 public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
20260 $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
20261 $retval = 0;
20262 $value = 0;
20263 $unit = 'px';
20264 if ($points) {
20265 $k = 1;
20266 } else {
20267 $k = $this->k;
20268 }
20269 if (in_array($defaultunit, $supportedunits)) {
20270 $unit = $defaultunit;
20271 }
20272 if (is_numeric($htmlval)) {
20273 $value = floatval($htmlval);
20274 } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
20275 $value = floatval($mnum[1]);
20276 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
20277 if (in_array($munit[1], $supportedunits)) {
20278 $unit = $munit[1];
20279 }
20280 }
20281 }
20282 switch ($unit) {
20283 // percentage
20284 case '%': {
20285 $retval = (($value * $refsize) / 100);
20286 break;
20287 }
20288 // relative-size
20289 case 'em': {
20290 $retval = ($value * $refsize);
20291 break;
20292 }
20293 // height of lower case 'x' (about half the font-size)
20294 case 'ex': {
20295 $retval = ($value * ($refsize / 2));
20296 break;
20297 }
20298 // absolute-size
20299 case 'in': {
20300 $retval = (($value * $this->dpi) / $k);
20301 break;
20302 }
20303 // centimeters
20304 case 'cm': {
20305 $retval = (($value / 2.54 * $this->dpi) / $k);
20306 break;
20307 }
20308 // millimeters
20309 case 'mm': {
20310 $retval = (($value / 25.4 * $this->dpi) / $k);
20311 break;
20312 }
20313 // one pica is 12 points
20314 case 'pc': {
20315 $retval = (($value * 12) / $k);
20316 break;
20317 }
20318 // points
20319 case 'pt': {
20320 $retval = ($value / $k);
20321 break;
20322 }
20323 // pixels
20324 case 'px': {
20325 $retval = $this->pixelsToUnits($value);
20326 if ($points) {
20327 $retval *= $this->k;
20328 }
20329 break;
20330 }
20331 }
20332 return $retval;
20333 }
20334
20343 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
20344 if ($this->state != 2) {
20345 return;
20346 }
20347 $size /= $this->k;
20348 $fill = '';
20349 $bgcolor = $this->bgcolor;
20350 $color = $this->fgcolor;
20351 $strokecolor = $this->strokecolor;
20352 $width = 0;
20353 $textitem = '';
20354 $tmpx = $this->x;
20355 $lspace = $this->GetStringWidth(' ');
20356 if ($listtype == '^') {
20357 // special symbol used for avoid justification of rect bullet
20358 $this->lispacer = '';
20359 return;
20360 } elseif ($listtype == '!') {
20361 // set default list type for unordered list
20362 $deftypes = array('disc', 'circle', 'square');
20363 $listtype = $deftypes[($listdepth - 1) % 3];
20364 } elseif ($listtype == '#') {
20365 // set default list type for ordered list
20366 $listtype = 'decimal';
20367 } elseif (substr($listtype, 0, 4) == 'img|') {
20368 // custom image type ('img|type|width|height|image.ext')
20369 $img = explode('|', $listtype);
20370 $listtype = 'img';
20371 }
20372 switch ($listtype) {
20373 // unordered types
20374 case 'none': {
20375 break;
20376 }
20377 case 'disc': {
20378 $r = $size / 6;
20379 $lspace += (2 * $r);
20380 if ($this->rtl) {
20381 $this->x += $lspace;
20382 } else {
20383 $this->x -= $lspace;
20384 }
20385 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8);
20386 break;
20387 }
20388 case 'circle': {
20389 $r = $size / 6;
20390 $lspace += (2 * $r);
20391 if ($this->rtl) {
20392 $this->x += $lspace;
20393 } else {
20394 $this->x -= $lspace;
20395 }
20396 $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor;
20397 $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
20398 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
20399 $this->_out($prev_line_style); // restore line settings
20400 break;
20401 }
20402 case 'square': {
20403 $l = $size / 3;
20404 $lspace += $l;
20405 if ($this->rtl) {;
20406 $this->x += $lspace;
20407 } else {
20408 $this->x -= $lspace;
20409 }
20410 $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color);
20411 break;
20412 }
20413 case 'img': {
20414 // 1=>type, 2=>width, 3=>height, 4=>image.ext
20415 $lspace += $img[2];
20416 if ($this->rtl) {;
20417 $this->x += $lspace;
20418 } else {
20419 $this->x -= $lspace;
20420 }
20421 $imgtype = strtolower($img[1]);
20422 $prev_y = $this->y;
20423 switch ($imgtype) {
20424 case 'svg': {
20425 $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
20426 break;
20427 }
20428 case 'ai':
20429 case 'eps': {
20430 $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
20431 break;
20432 }
20433 default: {
20434 $this->Image($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], $img[1], '', 'T', false, 300, '', false, false, 0, false, false, false);
20435 break;
20436 }
20437 }
20438 $this->y = $prev_y;
20439 break;
20440 }
20441 // ordered types
20442 // $this->listcount[$this->listnum];
20443 // $textitem
20444 case '1':
20445 case 'decimal': {
20446 $textitem = $this->listcount[$this->listnum];
20447 break;
20448 }
20449 case 'decimal-leading-zero': {
20450 $textitem = sprintf('%02d', $this->listcount[$this->listnum]);
20451 break;
20452 }
20453 case 'i':
20454 case 'lower-roman': {
20455 $textitem = strtolower(TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]));
20456 break;
20457 }
20458 case 'I':
20459 case 'upper-roman': {
20460 $textitem = TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]);
20461 break;
20462 }
20463 case 'a':
20464 case 'lower-alpha':
20465 case 'lower-latin': {
20466 $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
20467 break;
20468 }
20469 case 'A':
20470 case 'upper-alpha':
20471 case 'upper-latin': {
20472 $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
20473 break;
20474 }
20475 case 'lower-greek': {
20476 $textitem = TCPDF_FONTS::unichr((945 + $this->listcount[$this->listnum] - 1), $this->isunicode);
20477 break;
20478 }
20479 /*
20480 // Types to be implemented (special handling)
20481 case 'hebrew': {
20482 break;
20483 }
20484 case 'armenian': {
20485 break;
20486 }
20487 case 'georgian': {
20488 break;
20489 }
20490 case 'cjk-ideographic': {
20491 break;
20492 }
20493 case 'hiragana': {
20494 break;
20495 }
20496 case 'katakana': {
20497 break;
20498 }
20499 case 'hiragana-iroha': {
20500 break;
20501 }
20502 case 'katakana-iroha': {
20503 break;
20504 }
20505 */
20506 default: {
20507 $textitem = $this->listcount[$this->listnum];
20508 }
20509 }
20510 if (!TCPDF_STATIC::empty_string($textitem)) {
20511 // Check whether we need a new page or new column
20512 $prev_y = $this->y;
20513 $h = $this->getCellHeight($this->FontSize);
20514 if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) {
20515 $tmpx = $this->x;
20516 }
20517 // print ordered item
20518 if ($this->rtl) {
20519 $textitem = '.'.$textitem;
20520 } else {
20521 $textitem = $textitem.'.';
20522 }
20523 $lspace += $this->GetStringWidth($textitem);
20524 if ($this->rtl) {
20525 $this->x += $lspace;
20526 } else {
20527 $this->x -= $lspace;
20528 }
20529 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
20530 }
20531 $this->x = $tmpx;
20532 $this->lispacer = '^';
20533 // restore colors
20534 $this->SetFillColorArray($bgcolor);
20535 $this->SetDrawColorArray($strokecolor);
20536 $this->SettextColorArray($color);
20537 }
20538
20545 protected function getGraphicVars() {
20546 $grapvars = array(
20547 'FontFamily' => $this->FontFamily,
20548 'FontStyle' => $this->FontStyle,
20549 'FontSizePt' => $this->FontSizePt,
20550 'rMargin' => $this->rMargin,
20551 'lMargin' => $this->lMargin,
20552 'cell_padding' => $this->cell_padding,
20553 'cell_margin' => $this->cell_margin,
20554 'LineWidth' => $this->LineWidth,
20555 'linestyleWidth' => $this->linestyleWidth,
20556 'linestyleCap' => $this->linestyleCap,
20557 'linestyleJoin' => $this->linestyleJoin,
20558 'linestyleDash' => $this->linestyleDash,
20559 'textrendermode' => $this->textrendermode,
20560 'textstrokewidth' => $this->textstrokewidth,
20561 'DrawColor' => $this->DrawColor,
20562 'FillColor' => $this->FillColor,
20563 'TextColor' => $this->TextColor,
20564 'ColorFlag' => $this->ColorFlag,
20565 'bgcolor' => $this->bgcolor,
20566 'fgcolor' => $this->fgcolor,
20567 'htmlvspace' => $this->htmlvspace,
20568 'listindent' => $this->listindent,
20569 'listindentlevel' => $this->listindentlevel,
20570 'listnum' => $this->listnum,
20571 'listordered' => $this->listordered,
20572 'listcount' => $this->listcount,
20573 'lispacer' => $this->lispacer,
20574 'cell_height_ratio' => $this->cell_height_ratio,
20575 'font_stretching' => $this->font_stretching,
20576 'font_spacing' => $this->font_spacing,
20577 'alpha' => $this->alpha,
20578 // extended
20579 'lasth' => $this->lasth,
20580 'tMargin' => $this->tMargin,
20581 'bMargin' => $this->bMargin,
20582 'AutoPageBreak' => $this->AutoPageBreak,
20583 'PageBreakTrigger' => $this->PageBreakTrigger,
20584 'x' => $this->x,
20585 'y' => $this->y,
20586 'w' => $this->w,
20587 'h' => $this->h,
20588 'wPt' => $this->wPt,
20589 'hPt' => $this->hPt,
20590 'fwPt' => $this->fwPt,
20591 'fhPt' => $this->fhPt,
20592 'page' => $this->page,
20593 'current_column' => $this->current_column,
20594 'num_columns' => $this->num_columns
20595 );
20596 return $grapvars;
20597 }
20598
20606 protected function setGraphicVars($gvars, $extended=false) {
20607 if ($this->state != 2) {
20608 return;
20609 }
20610 $this->FontFamily = $gvars['FontFamily'];
20611 $this->FontStyle = $gvars['FontStyle'];
20612 $this->FontSizePt = $gvars['FontSizePt'];
20613 $this->rMargin = $gvars['rMargin'];
20614 $this->lMargin = $gvars['lMargin'];
20615 $this->cell_padding = $gvars['cell_padding'];
20616 $this->cell_margin = $gvars['cell_margin'];
20617 $this->LineWidth = $gvars['LineWidth'];
20618 $this->linestyleWidth = $gvars['linestyleWidth'];
20619 $this->linestyleCap = $gvars['linestyleCap'];
20620 $this->linestyleJoin = $gvars['linestyleJoin'];
20621 $this->linestyleDash = $gvars['linestyleDash'];
20622 $this->textrendermode = $gvars['textrendermode'];
20623 $this->textstrokewidth = $gvars['textstrokewidth'];
20624 $this->DrawColor = $gvars['DrawColor'];
20625 $this->FillColor = $gvars['FillColor'];
20626 $this->TextColor = $gvars['TextColor'];
20627 $this->ColorFlag = $gvars['ColorFlag'];
20628 $this->bgcolor = $gvars['bgcolor'];
20629 $this->fgcolor = $gvars['fgcolor'];
20630 $this->htmlvspace = $gvars['htmlvspace'];
20631 $this->listindent = $gvars['listindent'];
20632 $this->listindentlevel = $gvars['listindentlevel'];
20633 $this->listnum = $gvars['listnum'];
20634 $this->listordered = $gvars['listordered'];
20635 $this->listcount = $gvars['listcount'];
20636 $this->lispacer = $gvars['lispacer'];
20637 $this->cell_height_ratio = $gvars['cell_height_ratio'];
20638 $this->font_stretching = $gvars['font_stretching'];
20639 $this->font_spacing = $gvars['font_spacing'];
20640 $this->alpha = $gvars['alpha'];
20641 if ($extended) {
20642 // restore extended values
20643 $this->lasth = $gvars['lasth'];
20644 $this->tMargin = $gvars['tMargin'];
20645 $this->bMargin = $gvars['bMargin'];
20646 $this->AutoPageBreak = $gvars['AutoPageBreak'];
20647 $this->PageBreakTrigger = $gvars['PageBreakTrigger'];
20648 $this->x = $gvars['x'];
20649 $this->y = $gvars['y'];
20650 $this->w = $gvars['w'];
20651 $this->h = $gvars['h'];
20652 $this->wPt = $gvars['wPt'];
20653 $this->hPt = $gvars['hPt'];
20654 $this->fwPt = $gvars['fwPt'];
20655 $this->fhPt = $gvars['fhPt'];
20656 $this->page = $gvars['page'];
20657 $this->current_column = $gvars['current_column'];
20658 $this->num_columns = $gvars['num_columns'];
20659 }
20660 $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
20661 if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
20662 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
20663 }
20664 }
20665
20670 protected function _outSaveGraphicsState() {
20671 $this->_out('q');
20672 }
20673
20678 protected function _outRestoreGraphicsState() {
20679 $this->_out('Q');
20680 }
20681
20688 protected function setBuffer($data) {
20689 $this->bufferlen += strlen($data);
20690 $this->buffer .= $data;
20691 }
20692
20699 protected function replaceBuffer($data) {
20700 $this->bufferlen = strlen($data);
20701 $this->buffer = $data;
20702 }
20703
20710 protected function getBuffer() {
20711 return $this->buffer;
20712 }
20713
20722 protected function setPageBuffer($page, $data, $append=false) {
20723 if ($append) {
20724 $this->pages[$page] .= $data;
20725 } else {
20726 $this->pages[$page] = $data;
20727 }
20728 if ($append AND isset($this->pagelen[$page])) {
20729 $this->pagelen[$page] += strlen($data);
20730 } else {
20731 $this->pagelen[$page] = strlen($data);
20732 }
20733 }
20734
20742 protected function getPageBuffer($page) {
20743 if (isset($this->pages[$page])) {
20744 return $this->pages[$page];
20745 }
20746 return false;
20747 }
20748
20757 protected function setImageBuffer($image, $data) {
20758 if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) {
20759 $this->imagekeys[$this->numimages] = $image;
20760 $data['i'] = $this->numimages;
20761 ++$this->numimages;
20762 }
20763 $this->images[$image] = $data;
20764 return $data['i'];
20765 }
20766
20775 protected function setImageSubBuffer($image, $key, $data) {
20776 if (!isset($this->images[$image])) {
20777 $this->setImageBuffer($image, array());
20778 }
20779 $this->images[$image][$key] = $data;
20780 }
20781
20789 protected function getImageBuffer($image) {
20790 if (isset($this->images[$image])) {
20791 return $this->images[$image];
20792 }
20793 return false;
20794 }
20795
20803 protected function setFontBuffer($font, $data) {
20804 $this->fonts[$font] = $data;
20805 if (!in_array($font, $this->fontkeys)) {
20806 $this->fontkeys[] = $font;
20807 // store object ID for current font
20808 ++$this->n;
20809 $this->font_obj_ids[$font] = $this->n;
20810 $this->setFontSubBuffer($font, 'n', $this->n);
20811 }
20812 }
20813
20822 protected function setFontSubBuffer($font, $key, $data) {
20823 if (!isset($this->fonts[$font])) {
20824 $this->setFontBuffer($font, array());
20825 }
20826 $this->fonts[$font][$key] = $data;
20827 }
20828
20836 protected function getFontBuffer($font) {
20837 if (isset($this->fonts[$font])) {
20838 return $this->fonts[$font];
20839 }
20840 return false;
20841 }
20842
20851 public function movePage($frompage, $topage) {
20852 if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
20853 return false;
20854 }
20855 if ($frompage == $this->page) {
20856 // close the page before moving it
20857 $this->endPage();
20858 }
20859 // move all page-related states
20860 $tmppage = $this->getPageBuffer($frompage);
20861 $tmppagedim = $this->pagedim[$frompage];
20862 $tmppagelen = $this->pagelen[$frompage];
20863 $tmpintmrk = $this->intmrk[$frompage];
20864 $tmpbordermrk = $this->bordermrk[$frompage];
20865 $tmpcntmrk = $this->cntmrk[$frompage];
20866 $tmppageobjects = $this->pageobjects[$frompage];
20867 if (isset($this->footerpos[$frompage])) {
20868 $tmpfooterpos = $this->footerpos[$frompage];
20869 }
20870 if (isset($this->footerlen[$frompage])) {
20871 $tmpfooterlen = $this->footerlen[$frompage];
20872 }
20873 if (isset($this->transfmrk[$frompage])) {
20874 $tmptransfmrk = $this->transfmrk[$frompage];
20875 }
20876 if (isset($this->PageAnnots[$frompage])) {
20877 $tmpannots = $this->PageAnnots[$frompage];
20878 }
20879 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
20880 for ($i = $frompage; $i > $topage; --$i) {
20881 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) {
20882 --$this->pagegroups[$this->newpagegroup[$i]];
20883 break;
20884 }
20885 }
20886 for ($i = $topage; $i > 0; --$i) {
20887 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) {
20888 ++$this->pagegroups[$this->newpagegroup[$i]];
20889 break;
20890 }
20891 }
20892 }
20893 for ($i = $frompage; $i > $topage; --$i) {
20894 $j = $i - 1;
20895 // shift pages down
20896 $this->setPageBuffer($i, $this->getPageBuffer($j));
20897 $this->pagedim[$i] = $this->pagedim[$j];
20898 $this->pagelen[$i] = $this->pagelen[$j];
20899 $this->intmrk[$i] = $this->intmrk[$j];
20900 $this->bordermrk[$i] = $this->bordermrk[$j];
20901 $this->cntmrk[$i] = $this->cntmrk[$j];
20902 $this->pageobjects[$i] = $this->pageobjects[$j];
20903 if (isset($this->footerpos[$j])) {
20904 $this->footerpos[$i] = $this->footerpos[$j];
20905 } elseif (isset($this->footerpos[$i])) {
20906 unset($this->footerpos[$i]);
20907 }
20908 if (isset($this->footerlen[$j])) {
20909 $this->footerlen[$i] = $this->footerlen[$j];
20910 } elseif (isset($this->footerlen[$i])) {
20911 unset($this->footerlen[$i]);
20912 }
20913 if (isset($this->transfmrk[$j])) {
20914 $this->transfmrk[$i] = $this->transfmrk[$j];
20915 } elseif (isset($this->transfmrk[$i])) {
20916 unset($this->transfmrk[$i]);
20917 }
20918 if (isset($this->PageAnnots[$j])) {
20919 $this->PageAnnots[$i] = $this->PageAnnots[$j];
20920 } elseif (isset($this->PageAnnots[$i])) {
20921 unset($this->PageAnnots[$i]);
20922 }
20923 if (isset($this->newpagegroup[$j])) {
20924 $this->newpagegroup[$i] = $this->newpagegroup[$j];
20925 unset($this->newpagegroup[$j]);
20926 }
20927 if ($this->currpagegroup == $j) {
20928 $this->currpagegroup = $i;
20929 }
20930 }
20931 $this->setPageBuffer($topage, $tmppage);
20932 $this->pagedim[$topage] = $tmppagedim;
20933 $this->pagelen[$topage] = $tmppagelen;
20934 $this->intmrk[$topage] = $tmpintmrk;
20935 $this->bordermrk[$topage] = $tmpbordermrk;
20936 $this->cntmrk[$topage] = $tmpcntmrk;
20937 $this->pageobjects[$topage] = $tmppageobjects;
20938 if (isset($tmpfooterpos)) {
20939 $this->footerpos[$topage] = $tmpfooterpos;
20940 } elseif (isset($this->footerpos[$topage])) {
20941 unset($this->footerpos[$topage]);
20942 }
20943 if (isset($tmpfooterlen)) {
20944 $this->footerlen[$topage] = $tmpfooterlen;
20945 } elseif (isset($this->footerlen[$topage])) {
20946 unset($this->footerlen[$topage]);
20947 }
20948 if (isset($tmptransfmrk)) {
20949 $this->transfmrk[$topage] = $tmptransfmrk;
20950 } elseif (isset($this->transfmrk[$topage])) {
20951 unset($this->transfmrk[$topage]);
20952 }
20953 if (isset($tmpannots)) {
20954 $this->PageAnnots[$topage] = $tmpannots;
20955 } elseif (isset($this->PageAnnots[$topage])) {
20956 unset($this->PageAnnots[$topage]);
20957 }
20958 // adjust outlines
20959 $tmpoutlines = $this->outlines;
20960 foreach ($tmpoutlines as $key => $outline) {
20961 if (!$outline['f']) {
20962 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
20963 $this->outlines[$key]['p'] = ($outline['p'] + 1);
20964 } elseif ($outline['p'] == $frompage) {
20965 $this->outlines[$key]['p'] = $topage;
20966 }
20967 }
20968 }
20969 // adjust dests
20970 $tmpdests = $this->dests;
20971 foreach ($tmpdests as $key => $dest) {
20972 if (!$dest['f']) {
20973 if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
20974 $this->dests[$key]['p'] = ($dest['p'] + 1);
20975 } elseif ($dest['p'] == $frompage) {
20976 $this->dests[$key]['p'] = $topage;
20977 }
20978 }
20979 }
20980 // adjust links
20981 $tmplinks = $this->links;
20982 foreach ($tmplinks as $key => $link) {
20983 if (!$link['f']) {
20984 if (($link['p'] >= $topage) AND ($link['p'] < $frompage)) {
20985 $this->links[$key]['p'] = ($link['p'] + 1);
20986 } elseif ($link['p'] == $frompage) {
20987 $this->links[$key]['p'] = $topage;
20988 }
20989 }
20990 }
20991 // adjust javascript
20992 $jfrompage = $frompage;
20993 $jtopage = $topage;
20994 if (preg_match_all('/this\.addField\‍(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) {
20995 foreach($pamatch[0] as $pk => $pmatch) {
20996 $pagenum = intval($pamatch[3][$pk]) + 1;
20997 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
20998 $newpage = ($pagenum + 1);
20999 } elseif ($pagenum == $jfrompage) {
21000 $newpage = $jtopage;
21001 } else {
21002 $newpage = $pagenum;
21003 }
21004 --$newpage;
21005 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21006 $this->javascript = str_replace($pmatch, $newjs, $this->javascript);
21007 }
21008 unset($pamatch);
21009 }
21010 // return to last page
21011 $this->lastPage(true);
21012 return true;
21013 }
21014
21022 public function deletePage($page) {
21023 if (($page < 1) OR ($page > $this->numpages)) {
21024 return false;
21025 }
21026 // delete current page
21027 unset($this->pages[$page]);
21028 unset($this->pagedim[$page]);
21029 unset($this->pagelen[$page]);
21030 unset($this->intmrk[$page]);
21031 unset($this->bordermrk[$page]);
21032 unset($this->cntmrk[$page]);
21033 foreach ($this->pageobjects[$page] as $oid) {
21034 if (isset($this->offsets[$oid])){
21035 unset($this->offsets[$oid]);
21036 }
21037 }
21038 unset($this->pageobjects[$page]);
21039 if (isset($this->footerpos[$page])) {
21040 unset($this->footerpos[$page]);
21041 }
21042 if (isset($this->footerlen[$page])) {
21043 unset($this->footerlen[$page]);
21044 }
21045 if (isset($this->transfmrk[$page])) {
21046 unset($this->transfmrk[$page]);
21047 }
21048 if (isset($this->PageAnnots[$page])) {
21049 unset($this->PageAnnots[$page]);
21050 }
21051 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
21052 for ($i = $page; $i > 0; --$i) {
21053 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) {
21054 --$this->pagegroups[$this->newpagegroup[$i]];
21055 break;
21056 }
21057 }
21058 }
21059 if (isset($this->pageopen[$page])) {
21060 unset($this->pageopen[$page]);
21061 }
21062 if ($page < $this->numpages) {
21063 // update remaining pages
21064 for ($i = $page; $i < $this->numpages; ++$i) {
21065 $j = $i + 1;
21066 // shift pages
21067 $this->setPageBuffer($i, $this->getPageBuffer($j));
21068 $this->pagedim[$i] = $this->pagedim[$j];
21069 $this->pagelen[$i] = $this->pagelen[$j];
21070 $this->intmrk[$i] = $this->intmrk[$j];
21071 $this->bordermrk[$i] = $this->bordermrk[$j];
21072 $this->cntmrk[$i] = $this->cntmrk[$j];
21073 $this->pageobjects[$i] = $this->pageobjects[$j];
21074 if (isset($this->footerpos[$j])) {
21075 $this->footerpos[$i] = $this->footerpos[$j];
21076 } elseif (isset($this->footerpos[$i])) {
21077 unset($this->footerpos[$i]);
21078 }
21079 if (isset($this->footerlen[$j])) {
21080 $this->footerlen[$i] = $this->footerlen[$j];
21081 } elseif (isset($this->footerlen[$i])) {
21082 unset($this->footerlen[$i]);
21083 }
21084 if (isset($this->transfmrk[$j])) {
21085 $this->transfmrk[$i] = $this->transfmrk[$j];
21086 } elseif (isset($this->transfmrk[$i])) {
21087 unset($this->transfmrk[$i]);
21088 }
21089 if (isset($this->PageAnnots[$j])) {
21090 $this->PageAnnots[$i] = $this->PageAnnots[$j];
21091 } elseif (isset($this->PageAnnots[$i])) {
21092 unset($this->PageAnnots[$i]);
21093 }
21094 if (isset($this->newpagegroup[$j])) {
21095 $this->newpagegroup[$i] = $this->newpagegroup[$j];
21096 unset($this->newpagegroup[$j]);
21097 }
21098 if ($this->currpagegroup == $j) {
21099 $this->currpagegroup = $i;
21100 }
21101 if (isset($this->pageopen[$j])) {
21102 $this->pageopen[$i] = $this->pageopen[$j];
21103 } elseif (isset($this->pageopen[$i])) {
21104 unset($this->pageopen[$i]);
21105 }
21106 }
21107 // remove last page
21108 unset($this->pages[$this->numpages]);
21109 unset($this->pagedim[$this->numpages]);
21110 unset($this->pagelen[$this->numpages]);
21111 unset($this->intmrk[$this->numpages]);
21112 unset($this->bordermrk[$this->numpages]);
21113 unset($this->cntmrk[$this->numpages]);
21114 foreach ($this->pageobjects[$this->numpages] as $oid) {
21115 if (isset($this->offsets[$oid])){
21116 unset($this->offsets[$oid]);
21117 }
21118 }
21119 unset($this->pageobjects[$this->numpages]);
21120 if (isset($this->footerpos[$this->numpages])) {
21121 unset($this->footerpos[$this->numpages]);
21122 }
21123 if (isset($this->footerlen[$this->numpages])) {
21124 unset($this->footerlen[$this->numpages]);
21125 }
21126 if (isset($this->transfmrk[$this->numpages])) {
21127 unset($this->transfmrk[$this->numpages]);
21128 }
21129 if (isset($this->PageAnnots[$this->numpages])) {
21130 unset($this->PageAnnots[$this->numpages]);
21131 }
21132 if (isset($this->newpagegroup[$this->numpages])) {
21133 unset($this->newpagegroup[$this->numpages]);
21134 }
21135 if ($this->currpagegroup == $this->numpages) {
21136 $this->currpagegroup = ($this->numpages - 1);
21137 }
21138 if (isset($this->pagegroups[$this->numpages])) {
21139 unset($this->pagegroups[$this->numpages]);
21140 }
21141 if (isset($this->pageopen[$this->numpages])) {
21142 unset($this->pageopen[$this->numpages]);
21143 }
21144 }
21145 --$this->numpages;
21146 $this->page = $this->numpages;
21147 // adjust outlines
21148 $tmpoutlines = $this->outlines;
21149 foreach ($tmpoutlines as $key => $outline) {
21150 if (!$outline['f']) {
21151 if ($outline['p'] > $page) {
21152 $this->outlines[$key]['p'] = $outline['p'] - 1;
21153 } elseif ($outline['p'] == $page) {
21154 unset($this->outlines[$key]);
21155 }
21156 }
21157 }
21158 // adjust dests
21159 $tmpdests = $this->dests;
21160 foreach ($tmpdests as $key => $dest) {
21161 if (!$dest['f']) {
21162 if ($dest['p'] > $page) {
21163 $this->dests[$key]['p'] = $dest['p'] - 1;
21164 } elseif ($dest['p'] == $page) {
21165 unset($this->dests[$key]);
21166 }
21167 }
21168 }
21169 // adjust links
21170 $tmplinks = $this->links;
21171 foreach ($tmplinks as $key => $link) {
21172 if (!$link['f']) {
21173 if ($link['p'] > $page) {
21174 $this->links[$key]['p'] = $link['p'] - 1;
21175 } elseif ($link['p'] == $page) {
21176 unset($this->links[$key]);
21177 }
21178 }
21179 }
21180 // adjust javascript
21181 $jpage = $page;
21182 if (preg_match_all('/this\.addField\‍(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) {
21183 foreach($pamatch[0] as $pk => $pmatch) {
21184 $pagenum = intval($pamatch[3][$pk]) + 1;
21185 if ($pagenum >= $jpage) {
21186 $newpage = ($pagenum - 1);
21187 } elseif ($pagenum == $jpage) {
21188 $newpage = 1;
21189 } else {
21190 $newpage = $pagenum;
21191 }
21192 --$newpage;
21193 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21194 $this->javascript = str_replace($pmatch, $newjs, $this->javascript);
21195 }
21196 unset($pamatch);
21197 }
21198 // return to last page
21199 if ($this->numpages > 0) {
21200 $this->lastPage(true);
21201 }
21202 return true;
21203 }
21204
21212 public function copyPage($page=0) {
21213 if ($page == 0) {
21214 // default value
21215 $page = $this->page;
21216 }
21217 if (($page < 1) OR ($page > $this->numpages)) {
21218 return false;
21219 }
21220 // close the last page
21221 $this->endPage();
21222 // copy all page-related states
21223 ++$this->numpages;
21224 $this->page = $this->numpages;
21225 $this->setPageBuffer($this->page, $this->getPageBuffer($page));
21226 $this->pagedim[$this->page] = $this->pagedim[$page];
21227 $this->pagelen[$this->page] = $this->pagelen[$page];
21228 $this->intmrk[$this->page] = $this->intmrk[$page];
21229 $this->bordermrk[$this->page] = $this->bordermrk[$page];
21230 $this->cntmrk[$this->page] = $this->cntmrk[$page];
21231 $this->pageobjects[$this->page] = $this->pageobjects[$page];
21232 $this->pageopen[$this->page] = false;
21233 if (isset($this->footerpos[$page])) {
21234 $this->footerpos[$this->page] = $this->footerpos[$page];
21235 }
21236 if (isset($this->footerlen[$page])) {
21237 $this->footerlen[$this->page] = $this->footerlen[$page];
21238 }
21239 if (isset($this->transfmrk[$page])) {
21240 $this->transfmrk[$this->page] = $this->transfmrk[$page];
21241 }
21242 if (isset($this->PageAnnots[$page])) {
21243 $this->PageAnnots[$this->page] = $this->PageAnnots[$page];
21244 }
21245 if (isset($this->newpagegroup[$page])) {
21246 // start a new group
21247 $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1;
21248 $this->currpagegroup = $this->newpagegroup[$this->page];
21249 $this->pagegroups[$this->currpagegroup] = 1;
21250 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
21251 ++$this->pagegroups[$this->currpagegroup];
21252 }
21253 // copy outlines
21254 $tmpoutlines = $this->outlines;
21255 foreach ($tmpoutlines as $key => $outline) {
21256 if ($outline['p'] == $page) {
21257 $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'x' => $outline['x'], 'y' => $outline['y'], 'p' => $this->page, 'f' => $outline['f'], 's' => $outline['s'], 'c' => $outline['c']);
21258 }
21259 }
21260 // copy links
21261 $tmplinks = $this->links;
21262 foreach ($tmplinks as $key => $link) {
21263 if ($link['p'] == $page) {
21264 $this->links[] = array('p' => $this->page, 'y' => $link['y'], 'f' => $link['f']);
21265 }
21266 }
21267 // return to last page
21268 $this->lastPage(true);
21269 return true;
21270 }
21271
21289 public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
21290 $fontsize = $this->FontSizePt;
21291 $fontfamily = $this->FontFamily;
21292 $fontstyle = $this->FontStyle;
21293 $w = $this->w - $this->lMargin - $this->rMargin;
21294 $spacer = $this->GetStringWidth(chr(32)) * 4;
21295 $lmargin = $this->lMargin;
21296 $rmargin = $this->rMargin;
21297 $x_start = $this->GetX();
21298 $page_first = $this->page;
21299 $current_page = $this->page;
21300 $page_fill_start = false;
21301 $page_fill_end = false;
21302 $current_column = $this->current_column;
21303 if (TCPDF_STATIC::empty_string($numbersfont)) {
21304 $numbersfont = $this->default_monospaced_font;
21305 }
21306 if (TCPDF_STATIC::empty_string($filler)) {
21307 $filler = ' ';
21308 }
21309 if (TCPDF_STATIC::empty_string($page)) {
21310 $gap = ' ';
21311 } else {
21312 $gap = '';
21313 if ($page < 1) {
21314 $page = 1;
21315 }
21316 }
21317 $this->SetFont($numbersfont, $fontstyle, $fontsize);
21318 $numwidth = $this->GetStringWidth('00000');
21319 $maxpage = 0; //used for pages on attached documents
21320 foreach ($this->outlines as $key => $outline) {
21321 // check for extra pages (used for attachments)
21322 if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) {
21323 $outline['p'] += ($this->page - $page_first);
21324 }
21325 if ($this->rtl) {
21326 $aligntext = 'R';
21327 $alignnum = 'L';
21328 } else {
21329 $aligntext = 'L';
21330 $alignnum = 'R';
21331 }
21332 if ($outline['l'] == 0) {
21333 $this->SetFont($fontfamily, $outline['s'].'B', $fontsize);
21334 } else {
21335 $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']);
21336 }
21337 $this->SetTextColorArray($outline['c']);
21338 // check for page break
21339 $this->checkPageBreak(2 * $this->getCellHeight($this->FontSize));
21340 // set margins and X position
21341 if (($this->page == $current_page) AND ($this->current_column == $current_column)) {
21342 $this->lMargin = $lmargin;
21343 $this->rMargin = $rmargin;
21344 } else {
21345 if ($this->current_column != $current_column) {
21346 if ($this->rtl) {
21347 $x_start = $this->w - $this->columns[$this->current_column]['x'];
21348 } else {
21349 $x_start = $this->columns[$this->current_column]['x'];
21350 }
21351 }
21352 $lmargin = $this->lMargin;
21353 $rmargin = $this->rMargin;
21354 $current_page = $this->page;
21355 $current_column = $this->current_column;
21356 }
21357 $this->SetX($x_start);
21358 $indent = ($spacer * $outline['l']);
21359 if ($this->rtl) {
21360 $this->x -= $indent;
21361 $this->rMargin = $this->w - $this->x;
21362 } else {
21363 $this->x += $indent;
21364 $this->lMargin = $this->x;
21365 }
21366 $link = $this->AddLink();
21367 $this->SetLink($link, $outline['y'], $outline['p']);
21368 // write the text
21369 if ($this->rtl) {
21370 $txt = ' '.$outline['t'];
21371 } else {
21372 $txt = $outline['t'].' ';
21373 }
21374 $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
21375 if ($this->rtl) {
21376 $tw = $this->x - $this->lMargin;
21377 } else {
21378 $tw = $this->w - $this->rMargin - $this->x;
21379 }
21380 $this->SetFont($numbersfont, $fontstyle, $fontsize);
21381 if (TCPDF_STATIC::empty_string($page)) {
21382 $pagenum = $outline['p'];
21383 } else {
21384 // placemark to be replaced with the correct number
21385 $pagenum = '{#'.($outline['p']).'}';
21386 if ($this->isUnicodeFont()) {
21387 $pagenum = '{'.$pagenum.'}';
21388 }
21389 $maxpage = max($maxpage, $outline['p']);
21390 }
21391 $fw = ($tw - $this->GetStringWidth($pagenum.$filler));
21392 $wfiller = $this->GetStringWidth($filler);
21393 if ($wfiller > 0) {
21394 $numfills = floor($fw / $wfiller);
21395 } else {
21396 $numfills = 0;
21397 }
21398 if ($numfills > 0) {
21399 $rowfill = str_repeat($filler, $numfills);
21400 } else {
21401 $rowfill = '';
21402 }
21403 if ($this->rtl) {
21404 $pagenum = $pagenum.$gap.$rowfill;
21405 } else {
21406 $pagenum = $rowfill.$gap.$pagenum;
21407 }
21408 // write the number
21409 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
21410 }
21411 $page_last = $this->getPage();
21412 $numpages = ($page_last - $page_first + 1);
21413 // account for booklet mode
21414 if ($this->booklet) {
21415 // check if a blank page is required before TOC
21416 $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21417 $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21418 if ($page_fill_start) {
21419 // add a page at the end (to be moved before TOC)
21420 $this->addPage();
21421 ++$page_last;
21422 ++$numpages;
21423 }
21424 if ($page_fill_end) {
21425 // add a page at the end
21426 $this->addPage();
21427 ++$page_last;
21428 ++$numpages;
21429 }
21430 }
21431 $maxpage = max($maxpage, $page_last);
21432 if (!TCPDF_STATIC::empty_string($page)) {
21433 for ($p = $page_first; $p <= $page_last; ++$p) {
21434 // get page data
21435 $temppage = $this->getPageBuffer($p);
21436 for ($n = 1; $n <= $maxpage; ++$n) {
21437 // update page numbers
21438 $a = '{#'.$n.'}';
21439 // get page number aliases
21440 $pnalias = $this->getInternalPageNumberAliases($a);
21441 // calculate replacement number
21442 if (($n >= $page) AND ($n <= $this->numpages)) {
21443 $np = $n + $numpages;
21444 } else {
21445 $np = $n;
21446 }
21447 $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21448 $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21449 // replace aliases with numbers
21450 foreach ($pnalias['u'] as $u) {
21451 $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
21452 if ($this->rtl) {
21453 $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21454 } else {
21455 $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21456 }
21457 $temppage = str_replace($u, $nr, $temppage);
21458 }
21459 foreach ($pnalias['a'] as $a) {
21460 $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
21461 if ($this->rtl) {
21462 $nr = $na.' '.$sfill;
21463 } else {
21464 $nr = $sfill.' '.$na;
21465 }
21466 $temppage = str_replace($a, $nr, $temppage);
21467 }
21468 }
21469 // save changes
21470 $this->setPageBuffer($p, $temppage);
21471 }
21472 // move pages
21473 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21474 if ($page_fill_start) {
21475 $this->movePage($page_last, $page_first);
21476 }
21477 for ($i = 0; $i < $numpages; ++$i) {
21478 $this->movePage($page_last, $page);
21479 }
21480 }
21481 }
21482
21499 public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
21500 $filler = ' ';
21501 $prev_htmlLinkColorArray = $this->htmlLinkColorArray;
21502 $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle;
21503 // set new style for link
21504 $this->htmlLinkColorArray = array();
21505 $this->htmlLinkFontStyle = '';
21506 $page_first = $this->getPage();
21507 $page_fill_start = false;
21508 $page_fill_end = false;
21509 // get the font type used for numbers in each template
21510 $current_font = $this->FontFamily;
21511 foreach ($templates as $level => $html) {
21512 $dom = $this->getHtmlDomArray($html);
21513 foreach ($dom as $key => $value) {
21514 if ($value['value'] == '#TOC_PAGE_NUMBER#') {
21515 $this->SetFont($dom[($key - 1)]['fontname']);
21516 $templates['F'.$level] = $this->isUnicodeFont();
21517 }
21518 }
21519 }
21520 $this->SetFont($current_font);
21521 $maxpage = 0; //used for pages on attached documents
21522 foreach ($this->outlines as $key => $outline) {
21523 // get HTML template
21524 $row = $templates[$outline['l']];
21525 if (TCPDF_STATIC::empty_string($page)) {
21526 $pagenum = $outline['p'];
21527 } else {
21528 // placemark to be replaced with the correct number
21529 $pagenum = '{#'.($outline['p']).'}';
21530 if ($templates['F'.$outline['l']]) {
21531 $pagenum = '{'.$pagenum.'}';
21532 }
21533 $maxpage = max($maxpage, $outline['p']);
21534 }
21535 // replace templates with current values
21536 $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
21537 $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
21538 // add link to page
21539 $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
21540 // write bookmark entry
21541 $this->writeHTML($row, false, false, true, false, '');
21542 }
21543 // restore link styles
21544 $this->htmlLinkColorArray = $prev_htmlLinkColorArray;
21545 $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle;
21546 // move TOC page and replace numbers
21547 $page_last = $this->getPage();
21548 $numpages = ($page_last - $page_first + 1);
21549 // account for booklet mode
21550 if ($this->booklet) {
21551 // check if a blank page is required before TOC
21552 $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21553 $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21554 if ($page_fill_start) {
21555 // add a page at the end (to be moved before TOC)
21556 $this->addPage();
21557 ++$page_last;
21558 ++$numpages;
21559 }
21560 if ($page_fill_end) {
21561 // add a page at the end
21562 $this->addPage();
21563 ++$page_last;
21564 ++$numpages;
21565 }
21566 }
21567 $maxpage = max($maxpage, $page_last);
21568 if (!TCPDF_STATIC::empty_string($page)) {
21569 for ($p = $page_first; $p <= $page_last; ++$p) {
21570 // get page data
21571 $temppage = $this->getPageBuffer($p);
21572 for ($n = 1; $n <= $maxpage; ++$n) {
21573 // update page numbers
21574 $a = '{#'.$n.'}';
21575 // get page number aliases
21576 $pnalias = $this->getInternalPageNumberAliases($a);
21577 // calculate replacement number
21578 if ($n >= $page) {
21579 $np = $n + $numpages;
21580 } else {
21581 $np = $n;
21582 }
21583 $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21584 $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21585 // replace aliases with numbers
21586 foreach ($pnalias['u'] as $u) {
21587 if ($correct_align) {
21588 $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
21589 if ($this->rtl) {
21590 $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21591 } else {
21592 $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21593 }
21594 } else {
21595 $nr = $nu;
21596 }
21597 $temppage = str_replace($u, $nr, $temppage);
21598 }
21599 foreach ($pnalias['a'] as $a) {
21600 if ($correct_align) {
21601 $sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
21602 if ($this->rtl) {
21603 $nr = $na.' '.$sfill;
21604 } else {
21605 $nr = $sfill.' '.$na;
21606 }
21607 } else {
21608 $nr = $na;
21609 }
21610 $temppage = str_replace($a, $nr, $temppage);
21611 }
21612 }
21613 // save changes
21614 $this->setPageBuffer($p, $temppage);
21615 }
21616 // move pages
21617 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21618 if ($page_fill_start) {
21619 $this->movePage($page_last, $page_first);
21620 }
21621 for ($i = 0; $i < $numpages; ++$i) {
21622 $this->movePage($page_last, $page);
21623 }
21624 }
21625 }
21626
21632 public function startTransaction() {
21633 if (isset($this->objcopy)) {
21634 // remove previous copy
21635 $this->commitTransaction();
21636 }
21637 // record current page number and Y position
21638 $this->start_transaction_page = $this->page;
21639 $this->start_transaction_y = $this->y;
21640 // clone current object
21641 $this->objcopy = TCPDF_STATIC::objclone($this);
21642 }
21643
21649 public function commitTransaction() {
21650 if (isset($this->objcopy)) {
21651 $this->objcopy->_destroy(true, true);
21652 unset($this->objcopy);
21653 }
21654 }
21655
21663 public function rollbackTransaction($self=false) {
21664 if (isset($this->objcopy)) {
21665 $this->_destroy(true, true);
21666 if ($self) {
21667 $objvars = get_object_vars($this->objcopy);
21668 foreach ($objvars as $key => $value) {
21669 $this->$key = $value;
21670 }
21671 }
21672 return $this->objcopy;
21673 }
21674 return $this;
21675 }
21676
21677 // --- MULTI COLUMNS METHODS -----------------------
21678
21687 public function setEqualColumns($numcols=0, $width=0, $y='') {
21688 $this->columns = array();
21689 if ($numcols < 2) {
21690 $numcols = 0;
21691 $this->columns = array();
21692 } else {
21693 // maximum column width
21694 $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
21695 if (($width == 0) OR ($width > $maxwidth)) {
21696 $width = $maxwidth;
21697 }
21699 $y = $this->y;
21700 }
21701 // space between columns
21702 $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
21703 // fill the columns array (with, space, starting Y position)
21704 for ($i = 0; $i < $numcols; ++$i) {
21705 $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
21706 }
21707 }
21708 $this->num_columns = $numcols;
21709 $this->current_column = 0;
21710 $this->column_start_page = $this->page;
21711 $this->selectColumn(0);
21712 }
21713
21719 public function resetColumns() {
21720 $this->lMargin = $this->original_lMargin;
21721 $this->rMargin = $this->original_rMargin;
21722 $this->setEqualColumns();
21723 }
21724
21732 public function setColumnsArray($columns) {
21733 $this->columns = $columns;
21734 $this->num_columns = count($columns);
21735 $this->current_column = 0;
21736 $this->column_start_page = $this->page;
21737 $this->selectColumn(0);
21738 }
21739
21746 public function selectColumn($col='') {
21747 if (is_string($col)) {
21748 $col = $this->current_column;
21749 } elseif ($col >= $this->num_columns) {
21750 $col = 0;
21751 }
21752 $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
21753 $enable_thead = false;
21754 if ($this->num_columns > 1) {
21755 if ($col != $this->current_column) {
21756 // move Y pointer at the top of the column
21757 if ($this->column_start_page == $this->page) {
21758 $this->y = $this->columns[$col]['y'];
21759 } else {
21760 $this->y = $this->tMargin;
21761 }
21762 // Avoid to write table headers more than once
21763 if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) {
21764 $enable_thead = true;
21765 $this->maxselcol['page'] = $this->page;
21766 $this->maxselcol['column'] = $col;
21767 }
21768 }
21769 $xshift = $this->colxshift;
21770 // set X position of the current column by case
21771 $listindent = ($this->listindentlevel * $this->listindent);
21772 // calculate column X position
21773 $colpos = 0;
21774 for ($i = 0; $i < $col; ++$i) {
21775 $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']);
21776 }
21777 if ($this->rtl) {
21778 $x = $this->w - $this->original_rMargin - $colpos;
21779 $this->rMargin = ($this->w - $x + $listindent);
21780 $this->lMargin = ($x - $this->columns[$col]['w']);
21781 $this->x = $x - $listindent;
21782 } else {
21783 $x = $this->original_lMargin + $colpos;
21784 $this->lMargin = ($x + $listindent);
21785 $this->rMargin = ($this->w - $x - $this->columns[$col]['w']);
21786 $this->x = $x + $listindent;
21787 }
21788 $this->columns[$col]['x'] = $x;
21789 }
21790 $this->current_column = $col;
21791 // fix for HTML mode
21792 $this->newline = true;
21793 // print HTML table header (if any)
21794 if ((!TCPDF_STATIC::empty_string($this->thead)) AND (!$this->inthead)) {
21795 if ($enable_thead) {
21796 // print table header
21797 $this->writeHTML($this->thead, false, false, false, false, '');
21798 $this->y += $xshift['s']['V'];
21799 // store end of header position
21800 if (!isset($this->columns[$col]['th'])) {
21801 $this->columns[$col]['th'] = array();
21802 }
21803 $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y;
21804 $this->lasth = 0;
21805 } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) {
21806 $this->y = $this->columns[$col]['th']['\''.$this->page.'\''];
21807 }
21808 }
21809 // account for an html table cell over multiple columns
21810 if ($this->rtl) {
21811 $this->rMargin += $xshift['x'];
21812 $this->x -= ($xshift['x'] + $xshift['p']['R']);
21813 } else {
21814 $this->lMargin += $xshift['x'];
21815 $this->x += $xshift['x'] + $xshift['p']['L'];
21816 }
21817 }
21818
21825 public function getColumn() {
21826 return $this->current_column;
21827 }
21828
21835 public function getNumberOfColumns() {
21836 return $this->num_columns;
21837 }
21838
21847 public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
21848 // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
21849 // convert text rendering parameters
21850 if ($stroke < 0) {
21851 $stroke = 0;
21852 }
21853 if ($fill === true) {
21854 if ($stroke > 0) {
21855 if ($clip === true) {
21856 // Fill, then stroke text and add to path for clipping
21857 $textrendermode = 6;
21858 } else {
21859 // Fill, then stroke text
21860 $textrendermode = 2;
21861 }
21862 $textstrokewidth = $stroke;
21863 } else {
21864 if ($clip === true) {
21865 // Fill text and add to path for clipping
21866 $textrendermode = 4;
21867 } else {
21868 // Fill text
21869 $textrendermode = 0;
21870 }
21871 }
21872 } else {
21873 if ($stroke > 0) {
21874 if ($clip === true) {
21875 // Stroke text and add to path for clipping
21876 $textrendermode = 5;
21877 } else {
21878 // Stroke text
21879 $textrendermode = 1;
21880 }
21881 $textstrokewidth = $stroke;
21882 } else {
21883 if ($clip === true) {
21884 // Add text to path for clipping
21885 $textrendermode = 7;
21886 } else {
21887 // Neither fill nor stroke text (invisible)
21888 $textrendermode = 3;
21889 }
21890 }
21891 }
21892 $this->textrendermode = $textrendermode;
21893 $this->textstrokewidth = $stroke;
21894 }
21895
21902 public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) {
21903 if (isset($params['enabled'])) {
21904 $this->txtshadow['enabled'] = $params['enabled']?true:false;
21905 } else {
21906 $this->txtshadow['enabled'] = false;
21907 }
21908 if (isset($params['depth_w'])) {
21909 $this->txtshadow['depth_w'] = floatval($params['depth_w']);
21910 } else {
21911 $this->txtshadow['depth_w'] = 0;
21912 }
21913 if (isset($params['depth_h'])) {
21914 $this->txtshadow['depth_h'] = floatval($params['depth_h']);
21915 } else {
21916 $this->txtshadow['depth_h'] = 0;
21917 }
21918 if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) {
21919 $this->txtshadow['color'] = $params['color'];
21920 } else {
21921 $this->txtshadow['color'] = $this->strokecolor;
21922 }
21923 if (isset($params['opacity'])) {
21924 $this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity'])));
21925 } else {
21926 $this->txtshadow['opacity'] = 1;
21927 }
21928 if (isset($params['blend_mode']) AND in_array($params['blend_mode'], array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
21929 $this->txtshadow['blend_mode'] = $params['blend_mode'];
21930 } else {
21931 $this->txtshadow['blend_mode'] = 'Normal';
21932 }
21933 if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) {
21934 $this->txtshadow['enabled'] = false;
21935 }
21936 }
21937
21944 public function getTextShadow() {
21945 return $this->txtshadow;
21946 }
21947
21962 protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
21963 $hyphenword = array(); // hyphens positions
21964 $numchars = count($word);
21965 if ($numchars <= $charmin) {
21966 return $word;
21967 }
21968 $word_string = TCPDF_FONTS::UTF8ArrSubString($word, '', '', $this->isunicode);
21969 // some words will be returned as-is
21970 $pattern = '/^([a-zA-Z0-9_\.\-]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/';
21971 if (preg_match($pattern, $word_string) > 0) {
21972 // email
21973 return $word;
21974 }
21975 $pattern = '/(([a-zA-Z0-9\-]+\.)?)((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/';
21976 if (preg_match($pattern, $word_string) > 0) {
21977 // URL
21978 return $word;
21979 }
21980 if (isset($dictionary[$word_string])) {
21981 return TCPDF_FONTS::UTF8StringToArray($dictionary[$word_string], $this->isunicode, $this->CurrentFont);
21982 }
21983 // surround word with '_' characters
21984 $tmpword = array_merge(array(46), $word, array(46));
21985 $tmpnumchars = $numchars + 2;
21986 $maxpos = $tmpnumchars - 1;
21987 for ($pos = 0; $pos < $maxpos; ++$pos) {
21988 $imax = min(($tmpnumchars - $pos), $charmax);
21989 for ($i = 1; $i <= $imax; ++$i) {
21990 $subword = strtolower(TCPDF_FONTS::UTF8ArrSubString($tmpword, $pos, ($pos + $i), $this->isunicode));
21991 if (isset($patterns[$subword])) {
21992 $pattern = TCPDF_FONTS::UTF8StringToArray($patterns[$subword], $this->isunicode, $this->CurrentFont);
21993 $pattern_length = count($pattern);
21994 $digits = 1;
21995 for ($j = 0; $j < $pattern_length; ++$j) {
21996 // check if $pattern[$j] is a number = hyphenation level (only numbers from 1 to 5 are valid)
21997 if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
21998 if ($j == 0) {
21999 $zero = $pos - 1;
22000 } else {
22001 $zero = $pos + $j - $digits;
22002 }
22003 // get hyphenation level
22004 $level = ($pattern[$j] - 48);
22005 // if two levels from two different patterns match at the same point, the higher one is selected.
22006 if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] < $level)) {
22007 $hyphenword[$zero] = $level;
22008 }
22009 ++$digits;
22010 }
22011 }
22012 }
22013 }
22014 }
22015 $inserted = 0;
22016 $maxpos = $numchars - $rightmin;
22017 for ($i = $leftmin; $i <= $maxpos; ++$i) {
22018 // only odd levels indicate allowed hyphenation points
22019 if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
22020 // 173 = soft hyphen character
22021 array_splice($word, $i + $inserted, 0, 173);
22022 ++$inserted;
22023 }
22024 }
22025 return $word;
22026 }
22027
22042 public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22043 $text = $this->unhtmlentities($text);
22044 $word = array(); // last word
22045 $txtarr = array(); // text to be returned
22046 $intag = false; // true if we are inside an HTML tag
22047 $skip = false; // true to skip hyphenation
22048 if (!is_array($patterns)) {
22049 $patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patterns);
22050 }
22051 // get array of characters
22052 $unichars = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
22053 // for each char
22054 foreach ($unichars as $char) {
22055 if ((!$intag) AND (!$skip) AND TCPDF_FONT_DATA::$uni_type[$char] == 'L') {
22056 // letter character
22057 $word[] = $char;
22058 } else {
22059 // other type of character
22060 if (!TCPDF_STATIC::empty_string($word)) {
22061 // hypenate the word
22062 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22063 $word = array();
22064 }
22065 $txtarr[] = $char;
22066 if (chr($char) == '<') {
22067 // we are inside an HTML tag
22068 $intag = true;
22069 } elseif ($intag AND (chr($char) == '>')) {
22070 // end of HTML tag
22071 $intag = false;
22072 // check for style tag
22073 $expected = array(115, 116, 121, 108, 101); // = 'style'
22074 $current = array_slice($txtarr, -6, 5); // last 5 chars
22075 $compare = array_diff($expected, $current);
22076 if (empty($compare)) {
22077 // check if it is a closing tag
22078 $expected = array(47); // = '/'
22079 $current = array_slice($txtarr, -7, 1);
22080 $compare = array_diff($expected, $current);
22081 if (empty($compare)) {
22082 // closing style tag
22083 $skip = false;
22084 } else {
22085 // opening style tag
22086 $skip = true;
22087 }
22088 }
22089 }
22090 }
22091 }
22092 if (!TCPDF_STATIC::empty_string($word)) {
22093 // hypenate the word
22094 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22095 }
22096 // convert char array to string and return
22097 return TCPDF_FONTS::UTF8ArrSubString($txtarr, '', '', $this->isunicode);
22098 }
22099
22106 public function setRasterizeVectorImages($mode) {
22107 $this->rasterize_vector_images = $mode;
22108 }
22109
22117 public function setFontSubsetting($enable=true) {
22118 if ($this->pdfa_mode) {
22119 $this->font_subsetting = false;
22120 } else {
22121 $this->font_subsetting = $enable ? true : false;
22122 }
22123 }
22124
22132 public function getFontSubsetting() {
22133 return $this->font_subsetting;
22134 }
22135
22145 public function stringLeftTrim($str, $replace='') {
22146 return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str);
22147 }
22148
22158 public function stringRightTrim($str, $replace='') {
22159 return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str);
22160 }
22161
22171 public function stringTrim($str, $replace='') {
22172 $str = $this->stringLeftTrim($str, $replace);
22173 $str = $this->stringRightTrim($str, $replace);
22174 return $str;
22175 }
22176
22184 public function isUnicodeFont() {
22185 return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0'));
22186 }
22187
22196 public function getFontFamilyName($fontfamily) {
22197 // remove spaces and symbols
22198 $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily));
22199 // extract all font names
22200 $fontslist = preg_split('/[,]/', $fontfamily);
22201 // find first valid font name
22202 foreach ($fontslist as $font) {
22203 // replace font variations
22204 $font = preg_replace('/regular$/', '', $font);
22205 $font = preg_replace('/italic$/', 'I', $font);
22206 $font = preg_replace('/oblique$/', 'I', $font);
22207 $font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
22208 // replace common family names and core fonts
22209 $pattern = array();
22210 $replacement = array();
22211 $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
22212 $replacement[] = 'times';
22213 $pattern[] = '/^sansserif/';
22214 $replacement[] = 'helvetica';
22215 $pattern[] = '/^monospace/';
22216 $replacement[] = 'courier';
22217 $font = preg_replace($pattern, $replacement, $font);
22218 if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) {
22219 return $font;
22220 }
22221 }
22222 // return current font as default
22223 return $this->CurrentFont['fontkey'];
22224 }
22225
22240 public function startTemplate($w=0, $h=0, $group=false) {
22241 if ($this->inxobj) {
22242 // we are already inside an XObject template
22243 return false;
22244 }
22245 $this->inxobj = true;
22246 ++$this->n;
22247 // XObject ID
22248 $this->xobjid = 'XT'.$this->n;
22249 // object ID
22250 $this->xobjects[$this->xobjid] = array('n' => $this->n);
22251 // store current graphic state
22252 $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars();
22253 // initialize data
22254 $this->xobjects[$this->xobjid]['intmrk'] = 0;
22255 $this->xobjects[$this->xobjid]['transfmrk'] = array();
22256 $this->xobjects[$this->xobjid]['outdata'] = '';
22257 $this->xobjects[$this->xobjid]['xobjects'] = array();
22258 $this->xobjects[$this->xobjid]['images'] = array();
22259 $this->xobjects[$this->xobjid]['fonts'] = array();
22260 $this->xobjects[$this->xobjid]['annotations'] = array();
22261 $this->xobjects[$this->xobjid]['extgstates'] = array();
22262 $this->xobjects[$this->xobjid]['gradients'] = array();
22263 $this->xobjects[$this->xobjid]['spot_colors'] = array();
22264 // set new environment
22265 $this->num_columns = 1;
22266 $this->current_column = 0;
22267 $this->SetAutoPageBreak(false);
22268 if (($w === '') OR ($w <= 0)) {
22269 $w = $this->w - $this->lMargin - $this->rMargin;
22270 }
22271 if (($h === '') OR ($h <= 0)) {
22272 $h = $this->h - $this->tMargin - $this->bMargin;
22273 }
22274 $this->xobjects[$this->xobjid]['x'] = 0;
22275 $this->xobjects[$this->xobjid]['y'] = 0;
22276 $this->xobjects[$this->xobjid]['w'] = $w;
22277 $this->xobjects[$this->xobjid]['h'] = $h;
22278 $this->w = $w;
22279 $this->h = $h;
22280 $this->wPt = $this->w * $this->k;
22281 $this->hPt = $this->h * $this->k;
22282 $this->fwPt = $this->wPt;
22283 $this->fhPt = $this->hPt;
22284 $this->x = 0;
22285 $this->y = 0;
22286 $this->lMargin = 0;
22287 $this->rMargin = 0;
22288 $this->tMargin = 0;
22289 $this->bMargin = 0;
22290 // set group mode
22291 $this->xobjects[$this->xobjid]['group'] = $group;
22292 return $this->xobjid;
22293 }
22294
22305 public function endTemplate() {
22306 if (!$this->inxobj) {
22307 // we are not inside a template
22308 return false;
22309 }
22310 $this->inxobj = false;
22311 // restore previous graphic state
22312 $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true);
22313 return $this->xobjid;
22314 }
22315
22334 public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
22335 if ($this->state != 2) {
22336 return;
22337 }
22338 if (!isset($this->xobjects[$id])) {
22339 $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
22340 }
22341 if ($this->inxobj) {
22342 if ($id == $this->xobjid) {
22343 // close current template
22344 $this->endTemplate();
22345 } else {
22346 // use the template as resource for the template currently opened
22347 $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id];
22348 }
22349 }
22350 // set default values
22351 if ($x === '') {
22352 $x = $this->x;
22353 }
22354 if ($y === '') {
22355 $y = $this->y;
22356 }
22357 // check page for no-write regions and adapt page margins if necessary
22358 list($x, $y) = $this->checkPageRegions($h, $x, $y);
22359 $ow = $this->xobjects[$id]['w'];
22360 if ($ow <= 0) {
22361 $ow = 1;
22362 }
22363 $oh = $this->xobjects[$id]['h'];
22364 if ($oh <= 0) {
22365 $oh = 1;
22366 }
22367 // calculate template width and height on document
22368 if (($w <= 0) AND ($h <= 0)) {
22369 $w = $ow;
22370 $h = $oh;
22371 } elseif ($w <= 0) {
22372 $w = $h * $ow / $oh;
22373 } elseif ($h <= 0) {
22374 $h = $w * $oh / $ow;
22375 }
22376 // fit the template on available space
22377 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22378 // set page alignment
22379 $rb_y = $y + $h;
22380 // set alignment
22381 if ($this->rtl) {
22382 if ($palign == 'L') {
22383 $xt = $this->lMargin;
22384 } elseif ($palign == 'C') {
22385 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22386 } elseif ($palign == 'R') {
22387 $xt = $this->w - $this->rMargin - $w;
22388 } else {
22389 $xt = $x - $w;
22390 }
22391 $rb_x = $xt;
22392 } else {
22393 if ($palign == 'L') {
22394 $xt = $this->lMargin;
22395 } elseif ($palign == 'C') {
22396 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22397 } elseif ($palign == 'R') {
22398 $xt = $this->w - $this->rMargin - $w;
22399 } else {
22400 $xt = $x;
22401 }
22402 $rb_x = $xt + $w;
22403 }
22404 // print XObject Template + Transformation matrix
22405 $this->StartTransform();
22406 // translate and scale
22407 $sx = ($w / $ow);
22408 $sy = ($h / $oh);
22409 $tm = array();
22410 $tm[0] = $sx;
22411 $tm[1] = 0;
22412 $tm[2] = 0;
22413 $tm[3] = $sy;
22414 $tm[4] = $xt * $this->k;
22415 $tm[5] = ($this->h - $h - $y) * $this->k;
22416 $this->Transform($tm);
22417 // set object
22418 $this->_out('/'.$id.' Do');
22419 $this->StopTransform();
22420 // add annotations
22421 if (!empty($this->xobjects[$id]['annotations'])) {
22422 foreach ($this->xobjects[$id]['annotations'] as $annot) {
22423 // transform original coordinates
22424 $coordlt = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k)));
22425 $ax = ($coordlt[4] / $this->k);
22426 $ay = ($this->h - $h - ($coordlt[5] / $this->k));
22427 $coordrb = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k)));
22428 $aw = ($coordrb[4] / $this->k) - $ax;
22429 $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay;
22430 $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
22431 }
22432 }
22433 // set pointer to align the next text/objects
22434 switch($align) {
22435 case 'T': {
22436 $this->y = $y;
22437 $this->x = $rb_x;
22438 break;
22439 }
22440 case 'M': {
22441 $this->y = $y + round($h/2);
22442 $this->x = $rb_x;
22443 break;
22444 }
22445 case 'B': {
22446 $this->y = $rb_y;
22447 $this->x = $rb_x;
22448 break;
22449 }
22450 case 'N': {
22451 $this->SetY($rb_y);
22452 break;
22453 }
22454 default:{
22455 break;
22456 }
22457 }
22458 }
22459
22467 public function setFontStretching($perc=100) {
22468 $this->font_stretching = $perc;
22469 }
22470
22478 public function getFontStretching() {
22479 return $this->font_stretching;
22480 }
22481
22489 public function setFontSpacing($spacing=0) {
22490 $this->font_spacing = $spacing;
22491 }
22492
22500 public function getFontSpacing() {
22501 return $this->font_spacing;
22502 }
22503
22512 public function getPageRegions() {
22513 return $this->page_regions;
22514 }
22515
22527 public function setPageRegions($regions=array()) {
22528 // empty current regions array
22529 $this->page_regions = array();
22530 // add regions
22531 foreach ($regions as $data) {
22532 $this->addPageRegion($data);
22533 }
22534 }
22535
22547 public function addPageRegion($region) {
22548 if (!isset($region['page']) OR empty($region['page'])) {
22549 $region['page'] = $this->page;
22550 }
22551 if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
22552 AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
22553 AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
22554 $this->page_regions[] = $region;
22555 }
22556 }
22557
22566 public function removePageRegion($key) {
22567 if (isset($this->page_regions[$key])) {
22568 unset($this->page_regions[$key]);
22569 }
22570 }
22571
22584 protected function checkPageRegions($h, $x, $y) {
22585 // set default values
22586 if ($x === '') {
22587 $x = $this->x;
22588 }
22589 if ($y === '') {
22590 $y = $this->y;
22591 }
22592 if (!$this->check_page_regions OR empty($this->page_regions)) {
22593 // no page regions defined
22594 return array($x, $y);
22595 }
22596 if (empty($h)) {
22597 $h = $this->getCellHeight($this->FontSize);
22598 }
22599 // check for page break
22600 if ($this->checkPageBreak($h, $y)) {
22601 // the content will be printed on a new page
22602 $x = $this->x;
22603 $y = $this->y;
22604 }
22605 if ($this->num_columns > 1) {
22606 if ($this->rtl) {
22607 $this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22608 } else {
22609 $this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22610 }
22611 } else {
22612 if ($this->rtl) {
22613 $this->lMargin = max($this->clMargin, $this->original_lMargin);
22614 } else {
22615 $this->rMargin = max($this->crMargin, $this->original_rMargin);
22616 }
22617 }
22618 // adjust coordinates and page margins
22619 foreach ($this->page_regions as $regid => $regdata) {
22620 if ($regdata['page'] == $this->page) {
22621 // check region boundaries
22622 if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
22623 // Y is inside the region
22624 $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
22625 $yt = max($y, $regdata['yt']);
22626 $yb = min(($yt + $h), $regdata['yb']);
22627 $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt'];
22628 $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt'];
22629 if ($regdata['side'] == 'L') { // left side
22630 $new_margin = max($xt, $xb);
22631 if ($this->lMargin < $new_margin) {
22632 if ($this->rtl) {
22633 // adjust left page margin
22634 $this->lMargin = max(0, $new_margin);
22635 }
22636 if ($x < $new_margin) {
22637 // adjust x position
22638 $x = $new_margin;
22639 if ($new_margin > ($this->w - $this->rMargin)) {
22640 // adjust y position
22641 $y = $regdata['yb'] - $h;
22642 }
22643 }
22644 }
22645 } elseif ($regdata['side'] == 'R') { // right side
22646 $new_margin = min($xt, $xb);
22647 if (($this->w - $this->rMargin) > $new_margin) {
22648 if (!$this->rtl) {
22649 // adjust right page margin
22650 $this->rMargin = max(0, ($this->w - $new_margin));
22651 }
22652 if ($x > $new_margin) {
22653 // adjust x position
22654 $x = $new_margin;
22655 if ($new_margin > $this->lMargin) {
22656 // adjust y position
22657 $y = $regdata['yb'] - $h;
22658 }
22659 }
22660 }
22661 }
22662 }
22663 }
22664 }
22665 return array($x, $y);
22666 }
22667
22668 // --- SVG METHODS ---------------------------------------------------------
22669
22687 public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
22688 if ($this->state != 2) {
22689 return;
22690 }
22691 // reset SVG vars
22692 $this->svggradients = array();
22693 $this->svggradientid = 0;
22694 $this->svgdefsmode = false;
22695 $this->svgdefs = array();
22696 $this->svgclipmode = false;
22697 $this->svgclippaths = array();
22698 $this->svgcliptm = array();
22699 $this->svgclipid = 0;
22700 $this->svgtext = '';
22701 $this->svgtextmode = array();
22702 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
22703 // convert SVG to raster image using GD or ImageMagick libraries
22704 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22705 }
22706 if ($file[0] === '@') { // image from string
22707 $this->svgdir = '';
22708 $svgdata = substr($file, 1);
22709 } else { // SVG file
22710 $this->svgdir = dirname($file);
22712 }
22713 if ($svgdata === FALSE) {
22714 $this->Error('SVG file not found: '.$file);
22715 }
22716 if ($x === '') {
22717 $x = $this->x;
22718 }
22719 if ($y === '') {
22720 $y = $this->y;
22721 }
22722 // check page for no-write regions and adapt page margins if necessary
22723 list($x, $y) = $this->checkPageRegions($h, $x, $y);
22724 $k = $this->k;
22725 $ox = 0;
22726 $oy = 0;
22727 $ow = $w;
22728 $oh = $h;
22729 $aspect_ratio_align = 'xMidYMid';
22730 $aspect_ratio_ms = 'meet';
22731 $regs = array();
22732 // get original image width and height
22733 preg_match('/<svg([^>]*)>/si', $svgdata, $regs);
22734 if (isset($regs[1]) AND !empty($regs[1])) {
22735 $tmp = array();
22736 if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22737 $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22738 }
22739 $tmp = array();
22740 if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22741 $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22742 }
22743 $tmp = array();
22744 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22745 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22746 }
22747 $tmp = array();
22748 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22749 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22750 }
22751 $tmp = array();
22752 $view_box = array();
22753 if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
22754 if (count($tmp) == 5) {
22755 array_shift($tmp);
22756 foreach ($tmp as $key => $val) {
22757 $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
22758 }
22759 $ox = $view_box[0];
22760 $oy = $view_box[1];
22761 }
22762 // get aspect ratio
22763 $tmp = array();
22764 if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22765 $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
22766 switch (count($aspect_ratio)) {
22767 case 3: {
22768 $aspect_ratio_align = $aspect_ratio[1];
22769 $aspect_ratio_ms = $aspect_ratio[2];
22770 break;
22771 }
22772 case 2: {
22773 $aspect_ratio_align = $aspect_ratio[0];
22774 $aspect_ratio_ms = $aspect_ratio[1];
22775 break;
22776 }
22777 case 1: {
22778 $aspect_ratio_align = $aspect_ratio[0];
22779 $aspect_ratio_ms = 'meet';
22780 break;
22781 }
22782 }
22783 }
22784 }
22785 }
22786 if ($ow <= 0) {
22787 $ow = 1;
22788 }
22789 if ($oh <= 0) {
22790 $oh = 1;
22791 }
22792 // calculate image width and height on document
22793 if (($w <= 0) AND ($h <= 0)) {
22794 // convert image size to document unit
22795 $w = $ow;
22796 $h = $oh;
22797 } elseif ($w <= 0) {
22798 $w = $h * $ow / $oh;
22799 } elseif ($h <= 0) {
22800 $h = $w * $oh / $ow;
22801 }
22802 // fit the image on available space
22803 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22804 if ($this->rasterize_vector_images) {
22805 // convert SVG to raster image using GD or ImageMagick libraries
22806 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22807 }
22808 // set alignment
22809 $this->img_rb_y = $y + $h;
22810 // set alignment
22811 if ($this->rtl) {
22812 if ($palign == 'L') {
22813 $ximg = $this->lMargin;
22814 } elseif ($palign == 'C') {
22815 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22816 } elseif ($palign == 'R') {
22817 $ximg = $this->w - $this->rMargin - $w;
22818 } else {
22819 $ximg = $x - $w;
22820 }
22821 $this->img_rb_x = $ximg;
22822 } else {
22823 if ($palign == 'L') {
22824 $ximg = $this->lMargin;
22825 } elseif ($palign == 'C') {
22826 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22827 } elseif ($palign == 'R') {
22828 $ximg = $this->w - $this->rMargin - $w;
22829 } else {
22830 $ximg = $x;
22831 }
22832 $this->img_rb_x = $ximg + $w;
22833 }
22834 // store current graphic vars
22835 $gvars = $this->getGraphicVars();
22836 // store SVG position and scale factors
22837 $svgoffset_x = ($ximg - $ox) * $this->k;
22838 $svgoffset_y = -($y - $oy) * $this->k;
22839 if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
22840 $ow = $view_box[2];
22841 $oh = $view_box[3];
22842 } else {
22843 if ($ow <= 0) {
22844 $ow = $w;
22845 }
22846 if ($oh <= 0) {
22847 $oh = $h;
22848 }
22849 }
22850 $svgscale_x = $w / $ow;
22851 $svgscale_y = $h / $oh;
22852 // scaling and alignment
22853 if ($aspect_ratio_align != 'none') {
22854 // store current scaling values
22855 $svgscale_old_x = $svgscale_x;
22856 $svgscale_old_y = $svgscale_y;
22857 // force uniform scaling
22858 if ($aspect_ratio_ms == 'slice') {
22859 // the entire viewport is covered by the viewBox
22860 if ($svgscale_x > $svgscale_y) {
22861 $svgscale_y = $svgscale_x;
22862 } elseif ($svgscale_x < $svgscale_y) {
22863 $svgscale_x = $svgscale_y;
22864 }
22865 } else { // meet
22866 // the entire viewBox is visible within the viewport
22867 if ($svgscale_x < $svgscale_y) {
22868 $svgscale_y = $svgscale_x;
22869 } elseif ($svgscale_x > $svgscale_y) {
22870 $svgscale_x = $svgscale_y;
22871 }
22872 }
22873 // correct X alignment
22874 switch (substr($aspect_ratio_align, 1, 3)) {
22875 case 'Min': {
22876 // do nothing
22877 break;
22878 }
22879 case 'Max': {
22880 $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
22881 break;
22882 }
22883 default:
22884 case 'Mid': {
22885 $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
22886 break;
22887 }
22888 }
22889 // correct Y alignment
22890 switch (substr($aspect_ratio_align, 5)) {
22891 case 'Min': {
22892 // do nothing
22893 break;
22894 }
22895 case 'Max': {
22896 $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
22897 break;
22898 }
22899 default:
22900 case 'Mid': {
22901 $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
22902 break;
22903 }
22904 }
22905 }
22906 // store current page break mode
22907 $page_break_mode = $this->AutoPageBreak;
22908 $page_break_margin = $this->getBreakMargin();
22909 $cell_padding = $this->cell_padding;
22910 $this->SetCellPadding(0);
22911 $this->SetAutoPageBreak(false);
22912 // save the current graphic state
22913 $this->_out('q'.$this->epsmarker);
22914 // set initial clipping mask
22915 $this->Rect($ximg, $y, $w, $h, 'CNZ', array(), array());
22916 // scale and translate
22917 $e = $ox * $this->k * (1 - $svgscale_x);
22918 $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
22919 $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y)));
22920 // creates a new XML parser to be used by the other XML functions
22921 $this->parser = xml_parser_create('UTF-8');
22922 // the following function allows to use parser inside object
22923 xml_set_object($this->parser, $this);
22924 // disable case-folding for this XML parser
22925 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
22926 // sets the element handler functions for the XML parser
22927 xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler');
22928 // sets the character data handler function for the XML parser
22929 xml_set_character_data_handler($this->parser, 'segSVGContentHandler');
22930 // start parsing an XML document
22931 if (!xml_parse($this->parser, $svgdata)) {
22932 $error_message = sprintf('SVG Error: %s at line %d', xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser));
22933 $this->Error($error_message);
22934 }
22935 // free this XML parser
22936 xml_parser_free($this->parser);
22937 // restore previous graphic state
22938 $this->_out($this->epsmarker.'Q');
22939 // restore graphic vars
22940 $this->setGraphicVars($gvars);
22941 $this->lasth = $gvars['lasth'];
22942 if (!empty($border)) {
22943 $bx = $this->x;
22944 $by = $this->y;
22945 $this->x = $ximg;
22946 if ($this->rtl) {
22947 $this->x += $w;
22948 }
22949 $this->y = $y;
22950 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
22951 $this->x = $bx;
22952 $this->y = $by;
22953 }
22954 if ($link) {
22955 $this->Link($ximg, $y, $w, $h, $link, 0);
22956 }
22957 // set pointer to align the next text/objects
22958 switch($align) {
22959 case 'T':{
22960 $this->y = $y;
22961 $this->x = $this->img_rb_x;
22962 break;
22963 }
22964 case 'M':{
22965 $this->y = $y + round($h/2);
22966 $this->x = $this->img_rb_x;
22967 break;
22968 }
22969 case 'B':{
22970 $this->y = $this->img_rb_y;
22971 $this->x = $this->img_rb_x;
22972 break;
22973 }
22974 case 'N':{
22975 $this->SetY($this->img_rb_y);
22976 break;
22977 }
22978 default:{
22979 // restore pointer to starting position
22980 $this->x = $gvars['x'];
22981 $this->y = $gvars['y'];
22982 $this->page = $gvars['page'];
22983 $this->current_column = $gvars['current_column'];
22984 $this->tMargin = $gvars['tMargin'];
22985 $this->bMargin = $gvars['bMargin'];
22986 $this->w = $gvars['w'];
22987 $this->h = $gvars['h'];
22988 $this->wPt = $gvars['wPt'];
22989 $this->hPt = $gvars['hPt'];
22990 $this->fwPt = $gvars['fwPt'];
22991 $this->fhPt = $gvars['fhPt'];
22992 break;
22993 }
22994 }
22995 $this->endlinex = $this->img_rb_x;
22996 // restore page break
22997 $this->SetAutoPageBreak($page_break_mode, $page_break_margin);
22998 $this->cell_padding = $cell_padding;
22999 }
23000
23008 protected function convertSVGtMatrix($tm) {
23009 $a = $tm[0];
23010 $b = -$tm[1];
23011 $c = -$tm[2];
23012 $d = $tm[3];
23013 $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
23014 $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
23015 $x = 0;
23016 $y = $this->h * $this->k;
23017 $e = ($x * (1 - $a)) - ($y * $c) + $e;
23018 $f = ($y * (1 - $d)) - ($x * $b) + $f;
23019 return array($a, $b, $c, $d, $e, $f);
23020 }
23021
23028 protected function SVGTransform($tm) {
23029 $this->Transform($this->convertSVGtMatrix($tm));
23030 }
23031
23047 protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
23048 if ($this->state != 2) {
23049 return;
23050 }
23051 $objstyle = '';
23052 $minlen = (0.01 / $this->k); // minimum acceptable length
23053 if (!isset($svgstyle['opacity'])) {
23054 return $objstyle;
23055 }
23056 // clip-path
23057 $regs = array();
23058 if (preg_match('/url\‍([\s]*\#([^\‍)]*)\‍)/si', $svgstyle['clip-path'], $regs)) {
23059 $clip_path = $this->svgclippaths[$regs[1]];
23060 foreach ($clip_path as $cp) {
23061 $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
23062 }
23063 }
23064 // opacity
23065 if ($svgstyle['opacity'] != 1) {
23066 $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false);
23067 }
23068 // color
23069 $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors);
23070 $this->SetFillColorArray($fill_color);
23071 // text color
23072 $text_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors);
23073 $this->SetTextColorArray($text_color);
23074 // clip
23075 if (preg_match('/rect\‍(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\‍)/si', $svgstyle['clip'], $regs)) {
23076 $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
23077 $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
23078 $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
23079 $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
23080 $cx = $x + $left;
23081 $cy = $y + $top;
23082 $cw = $w - $left - $right;
23083 $ch = $h - $top - $bottom;
23084 if ($svgstyle['clip-rule'] == 'evenodd') {
23085 $clip_rule = 'CNZ';
23086 } else {
23087 $clip_rule = 'CEO';
23088 }
23089 $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
23090 }
23091 // fill
23092 $regs = array();
23093 if (preg_match('/url\‍([\s]*\#([^\‍)]*)\‍)/si', $svgstyle['fill'], $regs)) {
23094 // gradient
23095 $gradient = $this->svggradients[$regs[1]];
23096 if (isset($gradient['xref'])) {
23097 // reference to another gradient definition
23098 $newgradient = $this->svggradients[$gradient['xref']];
23099 $newgradient['coords'] = $gradient['coords'];
23100 $newgradient['mode'] = $gradient['mode'];
23101 $newgradient['type'] = $gradient['type'];
23102 $newgradient['gradientUnits'] = $gradient['gradientUnits'];
23103 if (isset($gradient['gradientTransform'])) {
23104 $newgradient['gradientTransform'] = $gradient['gradientTransform'];
23105 }
23106 $gradient = $newgradient;
23107 }
23108 //save current Graphic State
23109 $this->_outSaveGraphicsState();
23110 //set clipping area
23111 if (!empty($clip_function) AND method_exists($this, $clip_function)) {
23112 $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
23113 if ((!isset($gradient['type']) OR ($gradient['type'] != 3)) AND is_array($bbox) AND (count($bbox) == 4)) {
23114 list($x, $y, $w, $h) = $bbox;
23115 }
23116 }
23117 if ($gradient['mode'] == 'measure') {
23118 if (!isset($gradient['coords'][4])) {
23119 $gradient['coords'][4] = 0.5;
23120 }
23121 if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
23122 $gtm = $gradient['gradientTransform'];
23123 // apply transformation matrix
23124 $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
23125 $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
23126 $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
23127 $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
23128 $r = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
23129 $gradient['coords'][0] = $xa;
23130 $gradient['coords'][1] = $ya;
23131 $gradient['coords'][2] = $xb;
23132 $gradient['coords'][3] = $yb;
23133 $gradient['coords'][4] = $r;
23134 }
23135 // convert SVG coordinates to user units
23136 $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
23137 $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
23138 $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
23139 $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
23140 $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
23141 if ($w <= $minlen) {
23142 $w = $minlen;
23143 }
23144 if ($h <= $minlen) {
23145 $h = $minlen;
23146 }
23147 // shift units
23148 if ($gradient['gradientUnits'] == 'objectBoundingBox') {
23149 // convert to SVG coordinate system
23150 $gradient['coords'][0] += $x;
23151 $gradient['coords'][1] += $y;
23152 $gradient['coords'][2] += $x;
23153 $gradient['coords'][3] += $y;
23154 }
23155 // calculate percentages
23156 $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w);
23157 $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h);
23158 $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w);
23159 $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h);
23160 $gradient['coords'][4] /= $w;
23161 } elseif ($gradient['mode'] == 'percentage') {
23162 foreach($gradient['coords'] as $key => $val) {
23163 $gradient['coords'][$key] = (intval($val) / 100);
23164 if ($val < 0) {
23165 $gradient['coords'][$key] = 0;
23166 } elseif ($val > 1) {
23167 $gradient['coords'][$key] = 1;
23168 }
23169 }
23170 }
23171 if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
23172 // single color (no shading)
23173 $gradient['coords'][0] = 1;
23174 $gradient['coords'][1] = 0;
23175 $gradient['coords'][2] = 0.999;
23176 $gradient['coords'][3] = 0;
23177 }
23178 // swap Y coordinates
23179 $tmp = $gradient['coords'][1];
23180 $gradient['coords'][1] = $gradient['coords'][3];
23181 $gradient['coords'][3] = $tmp;
23182 // set transformation map for gradient
23183 $cy = ($this->h - $y);
23184 if ($gradient['type'] == 3) {
23185 // circular gradient
23186 $cy -= ($gradient['coords'][1] * ($w + $h));
23187 $h = $w = max($w, $h);
23188 } else {
23189 $cy -= $h;
23190 }
23191 $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), ($cy * $this->k)));
23192 if (count($gradient['stops']) > 1) {
23193 $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
23194 }
23195 } elseif ($svgstyle['fill'] != 'none') {
23196 $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors);
23197 if ($svgstyle['fill-opacity'] != 1) {
23198 $this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false);
23199 }
23200 $this->SetFillColorArray($fill_color);
23201 if ($svgstyle['fill-rule'] == 'evenodd') {
23202 $objstyle .= 'F*';
23203 } else {
23204 $objstyle .= 'F';
23205 }
23206 }
23207 // stroke
23208 if ($svgstyle['stroke'] != 'none') {
23209 if ($svgstyle['stroke-opacity'] != 1) {
23210 $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false);
23211 } elseif (preg_match('/rgba\‍(\d+%?,\s*\d+%?,\s*\d+%?,\s*(\d+(?:\.\d+)?)\‍)/i', $svgstyle['stroke'], $rgba_matches)) {
23212 $this->setAlpha($rgba_matches[1], 'Normal', $this->alpha['ca'], false);
23213 }
23214 $stroke_style = array(
23215 'color' => TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors),
23216 'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
23217 'cap' => $svgstyle['stroke-linecap'],
23218 'join' => $svgstyle['stroke-linejoin']
23219 );
23220 if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
23221 $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
23222 }
23223 $this->SetLineStyle($stroke_style);
23224 $objstyle .= 'D';
23225 }
23226 // font
23227 $regs = array();
23228 if (!empty($svgstyle['font'])) {
23229 if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
23230 $font_family = $this->getFontFamilyName($regs[1]);
23231 } else {
23232 $font_family = $svgstyle['font-family'];
23233 }
23234 if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23235 $font_size = trim($regs[1]);
23236 } else {
23237 $font_size = $svgstyle['font-size'];
23238 }
23239 if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23240 $font_style = trim($regs[1]);
23241 } else {
23242 $font_style = $svgstyle['font-style'];
23243 }
23244 if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23245 $font_weight = trim($regs[1]);
23246 } else {
23247 $font_weight = $svgstyle['font-weight'];
23248 }
23249 if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23250 $font_stretch = trim($regs[1]);
23251 } else {
23252 $font_stretch = $svgstyle['font-stretch'];
23253 }
23254 if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23255 $font_spacing = trim($regs[1]);
23256 } else {
23257 $font_spacing = $svgstyle['letter-spacing'];
23258 }
23259 } else {
23260 $font_family = $this->getFontFamilyName($svgstyle['font-family']);
23261 $font_size = $svgstyle['font-size'];
23262 $font_style = $svgstyle['font-style'];
23263 $font_weight = $svgstyle['font-weight'];
23264 $font_stretch = $svgstyle['font-stretch'];
23265 $font_spacing = $svgstyle['letter-spacing'];
23266 }
23267 $font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit);
23268 $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
23269 $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
23270 switch ($font_style) {
23271 case 'italic': {
23272 $font_style = 'I';
23273 break;
23274 }
23275 case 'oblique': {
23276 $font_style = 'I';
23277 break;
23278 }
23279 default:
23280 case 'normal': {
23281 $font_style = '';
23282 break;
23283 }
23284 }
23285 switch ($font_weight) {
23286 case 'bold':
23287 case 'bolder': {
23288 $font_style .= 'B';
23289 break;
23290 }
23291 case 'normal': {
23292 if ((substr($font_family, -1) == 'I') AND (substr($font_family, -2, 1) == 'B')) {
23293 $font_family = substr($font_family, 0, -2).'I';
23294 } elseif (substr($font_family, -1) == 'B') {
23295 $font_family = substr($font_family, 0, -1);
23296 }
23297 break;
23298 }
23299 }
23300 switch ($svgstyle['text-decoration']) {
23301 case 'underline': {
23302 $font_style .= 'U';
23303 break;
23304 }
23305 case 'overline': {
23306 $font_style .= 'O';
23307 break;
23308 }
23309 case 'line-through': {
23310 $font_style .= 'D';
23311 break;
23312 }
23313 default:
23314 case 'none': {
23315 break;
23316 }
23317 }
23318 $this->SetFont($font_family, $font_style, $font_size);
23319 $this->setFontStretching($font_stretch);
23320 $this->setFontSpacing($font_spacing);
23321 return $objstyle;
23322 }
23323
23342 protected function SVGPath($d, $style='') {
23343 if ($this->state != 2) {
23344 return;
23345 }
23346 // set fill/stroke style
23348 if (empty($op)) {
23349 return;
23350 }
23351 $paths = array();
23352 $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
23353 preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER);
23354 $x = 0;
23355 $y = 0;
23356 $x1 = 0;
23357 $y1 = 0;
23358 $x2 = 0;
23359 $y2 = 0;
23360 $xmin = 2147483647;
23361 $xmax = 0;
23362 $ymin = 2147483647;
23363 $ymax = 0;
23364 $relcoord = false;
23365 $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
23366 $firstcmd = true; // used to print first point
23367 // draw curve pieces
23368 foreach ($paths as $key => $val) {
23369 // get curve type
23370 $cmd = trim($val[1]);
23371 if (strtolower($cmd) == $cmd) {
23372 // use relative coordinated instead of absolute
23373 $relcoord = true;
23374 $xoffset = $x;
23375 $yoffset = $y;
23376 } else {
23377 $relcoord = false;
23378 $xoffset = 0;
23379 $yoffset = 0;
23380 }
23381 $params = array();
23382 if (isset($val[2])) {
23383 // get curve parameters
23384 $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
23385 $params = array();
23386 foreach ($rawparams as $ck => $cp) {
23387 $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
23388 if (abs($params[$ck]) < $minlen) {
23389 // approximate little values to zero
23390 $params[$ck] = 0;
23391 }
23392 }
23393 }
23394 // store current origin point
23395 $x0 = $x;
23396 $y0 = $y;
23397 switch (strtoupper($cmd)) {
23398 case 'M': { // moveto
23399 foreach ($params as $ck => $cp) {
23400 if (($ck % 2) == 0) {
23401 $x = $cp + $xoffset;
23402 } else {
23403 $y = $cp + $yoffset;
23404 if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23405 if ($ck == 1) {
23406 $this->_outPoint($x, $y);
23407 $firstcmd = false;
23408 } else {
23409 $this->_outLine($x, $y);
23410 }
23411 $x0 = $x;
23412 $y0 = $y;
23413 }
23414 $xmin = min($xmin, $x);
23415 $ymin = min($ymin, $y);
23416 $xmax = max($xmax, $x);
23417 $ymax = max($ymax, $y);
23418 if ($relcoord) {
23419 $xoffset = $x;
23420 $yoffset = $y;
23421 }
23422 }
23423 }
23424 break;
23425 }
23426 case 'L': { // lineto
23427 foreach ($params as $ck => $cp) {
23428 if (($ck % 2) == 0) {
23429 $x = $cp + $xoffset;
23430 } else {
23431 $y = $cp + $yoffset;
23432 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23433 $this->_outLine($x, $y);
23434 $x0 = $x;
23435 $y0 = $y;
23436 }
23437 $xmin = min($xmin, $x);
23438 $ymin = min($ymin, $y);
23439 $xmax = max($xmax, $x);
23440 $ymax = max($ymax, $y);
23441 if ($relcoord) {
23442 $xoffset = $x;
23443 $yoffset = $y;
23444 }
23445 }
23446 }
23447 break;
23448 }
23449 case 'H': { // horizontal lineto
23450 foreach ($params as $ck => $cp) {
23451 $x = $cp + $xoffset;
23452 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23453 $this->_outLine($x, $y);
23454 $x0 = $x;
23455 $y0 = $y;
23456 }
23457 $xmin = min($xmin, $x);
23458 $xmax = max($xmax, $x);
23459 if ($relcoord) {
23460 $xoffset = $x;
23461 }
23462 }
23463 break;
23464 }
23465 case 'V': { // vertical lineto
23466 foreach ($params as $ck => $cp) {
23467 $y = $cp + $yoffset;
23468 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23469 $this->_outLine($x, $y);
23470 $x0 = $x;
23471 $y0 = $y;
23472 }
23473 $ymin = min($ymin, $y);
23474 $ymax = max($ymax, $y);
23475 if ($relcoord) {
23476 $yoffset = $y;
23477 }
23478 }
23479 break;
23480 }
23481 case 'C': { // curveto
23482 foreach ($params as $ck => $cp) {
23483 $params[$ck] = $cp;
23484 if ((($ck + 1) % 6) == 0) {
23485 $x1 = $params[($ck - 5)] + $xoffset;
23486 $y1 = $params[($ck - 4)] + $yoffset;
23487 $x2 = $params[($ck - 3)] + $xoffset;
23488 $y2 = $params[($ck - 2)] + $yoffset;
23489 $x = $params[($ck - 1)] + $xoffset;
23490 $y = $params[($ck)] + $yoffset;
23491 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23492 $xmin = min($xmin, $x, $x1, $x2);
23493 $ymin = min($ymin, $y, $y1, $y2);
23494 $xmax = max($xmax, $x, $x1, $x2);
23495 $ymax = max($ymax, $y, $y1, $y2);
23496 if ($relcoord) {
23497 $xoffset = $x;
23498 $yoffset = $y;
23499 }
23500 }
23501 }
23502 break;
23503 }
23504 case 'S': { // shorthand/smooth curveto
23505 foreach ($params as $ck => $cp) {
23506 $params[$ck] = $cp;
23507 if ((($ck + 1) % 4) == 0) {
23508 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
23509 $x1 = (2 * $x) - $x2;
23510 $y1 = (2 * $y) - $y2;
23511 } else {
23512 $x1 = $x;
23513 $y1 = $y;
23514 }
23515 $x2 = $params[($ck - 3)] + $xoffset;
23516 $y2 = $params[($ck - 2)] + $yoffset;
23517 $x = $params[($ck - 1)] + $xoffset;
23518 $y = $params[($ck)] + $yoffset;
23519 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23520 $xmin = min($xmin, $x, $x1, $x2);
23521 $ymin = min($ymin, $y, $y1, $y2);
23522 $xmax = max($xmax, $x, $x1, $x2);
23523 $ymax = max($ymax, $y, $y1, $y2);
23524 if ($relcoord) {
23525 $xoffset = $x;
23526 $yoffset = $y;
23527 }
23528 }
23529 }
23530 break;
23531 }
23532 case 'Q': { // quadratic Bezier curveto
23533 foreach ($params as $ck => $cp) {
23534 $params[$ck] = $cp;
23535 if ((($ck + 1) % 4) == 0) {
23536 // convert quadratic points to cubic points
23537 $x1 = $params[($ck - 3)] + $xoffset;
23538 $y1 = $params[($ck - 2)] + $yoffset;
23539 $xa = ($x + (2 * $x1)) / 3;
23540 $ya = ($y + (2 * $y1)) / 3;
23541 $x = $params[($ck - 1)] + $xoffset;
23542 $y = $params[($ck)] + $yoffset;
23543 $xb = ($x + (2 * $x1)) / 3;
23544 $yb = ($y + (2 * $y1)) / 3;
23545 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23546 $xmin = min($xmin, $x, $xa, $xb);
23547 $ymin = min($ymin, $y, $ya, $yb);
23548 $xmax = max($xmax, $x, $xa, $xb);
23549 $ymax = max($ymax, $y, $ya, $yb);
23550 if ($relcoord) {
23551 $xoffset = $x;
23552 $yoffset = $y;
23553 }
23554 }
23555 }
23556 break;
23557 }
23558 case 'T': { // shorthand/smooth quadratic Bezier curveto
23559 foreach ($params as $ck => $cp) {
23560 $params[$ck] = $cp;
23561 if (($ck % 2) != 0) {
23562 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
23563 $x1 = (2 * $x) - $x1;
23564 $y1 = (2 * $y) - $y1;
23565 } else {
23566 $x1 = $x;
23567 $y1 = $y;
23568 }
23569 // convert quadratic points to cubic points
23570 $xa = ($x + (2 * $x1)) / 3;
23571 $ya = ($y + (2 * $y1)) / 3;
23572 $x = $params[($ck - 1)] + $xoffset;
23573 $y = $params[($ck)] + $yoffset;
23574 $xb = ($x + (2 * $x1)) / 3;
23575 $yb = ($y + (2 * $y1)) / 3;
23576 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23577 $xmin = min($xmin, $x, $xa, $xb);
23578 $ymin = min($ymin, $y, $ya, $yb);
23579 $xmax = max($xmax, $x, $xa, $xb);
23580 $ymax = max($ymax, $y, $ya, $yb);
23581 if ($relcoord) {
23582 $xoffset = $x;
23583 $yoffset = $y;
23584 }
23585 }
23586 }
23587 break;
23588 }
23589 case 'A': { // elliptical arc
23590 foreach ($params as $ck => $cp) {
23591 $params[$ck] = $cp;
23592 if ((($ck + 1) % 7) == 0) {
23593 $x0 = $x;
23594 $y0 = $y;
23595 $rx = abs($params[($ck - 6)]);
23596 $ry = abs($params[($ck - 5)]);
23597 $ang = -$rawparams[($ck - 4)];
23598 $angle = deg2rad($ang);
23599 $fa = $rawparams[($ck - 3)]; // large-arc-flag
23600 $fs = $rawparams[($ck - 2)]; // sweep-flag
23601 $x = $params[($ck - 1)] + $xoffset;
23602 $y = $params[$ck] + $yoffset;
23603 if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
23604 // endpoints are almost identical
23605 $xmin = min($xmin, $x);
23606 $ymin = min($ymin, $y);
23607 $xmax = max($xmax, $x);
23608 $ymax = max($ymax, $y);
23609 } else {
23610 $cos_ang = cos($angle);
23611 $sin_ang = sin($angle);
23612 $a = (($x0 - $x) / 2);
23613 $b = (($y0 - $y) / 2);
23614 $xa = ($a * $cos_ang) - ($b * $sin_ang);
23615 $ya = ($a * $sin_ang) + ($b * $cos_ang);
23616 $rx2 = $rx * $rx;
23617 $ry2 = $ry * $ry;
23618 $xa2 = $xa * $xa;
23619 $ya2 = $ya * $ya;
23620 $delta = ($xa2 / $rx2) + ($ya2 / $ry2);
23621 if ($delta > 1) {
23622 $rx *= sqrt($delta);
23623 $ry *= sqrt($delta);
23624 $rx2 = $rx * $rx;
23625 $ry2 = $ry * $ry;
23626 }
23627 $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
23628 if ($numerator < 0) {
23629 $root = 0;
23630 } else {
23631 $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
23632 }
23633 if ($fa == $fs){
23634 $root *= -1;
23635 }
23636 $cax = $root * (($rx * $ya) / $ry);
23637 $cay = -$root * (($ry * $xa) / $rx);
23638 // coordinates of ellipse center
23639 $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
23640 $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
23641 // get angles
23642 $angs = TCPDF_STATIC::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
23643 $dang = TCPDF_STATIC::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
23644 if (($fs == 0) AND ($dang > 0)) {
23645 $dang -= (2 * M_PI);
23646 } elseif (($fs == 1) AND ($dang < 0)) {
23647 $dang += (2 * M_PI);
23648 }
23649 $angf = $angs - $dang;
23650 if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
23651 // reverse angles
23652 $tmp = $angs;
23653 $angs = $angf;
23654 $angf = $tmp;
23655 }
23656 $angs = round(rad2deg($angs), 6);
23657 $angf = round(rad2deg($angf), 6);
23658 // covent angles to positive values
23659 if (($angs < 0) AND ($angf < 0)) {
23660 $angs += 360;
23661 $angf += 360;
23662 }
23663 $pie = false;
23664 if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
23665 $pie = true;
23666 }
23667 list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
23668 $xmin = min($xmin, $x, $axmin);
23669 $ymin = min($ymin, $y, $aymin);
23670 $xmax = max($xmax, $x, $axmax);
23671 $ymax = max($ymax, $y, $aymax);
23672 }
23673 if ($relcoord) {
23674 $xoffset = $x;
23675 $yoffset = $y;
23676 }
23677 }
23678 }
23679 break;
23680 }
23681 case 'Z': {
23682 $this->_out('h');
23683 break;
23684 }
23685 }
23686 $firstcmd = false;
23687 } // end foreach
23688 if (!empty($op)) {
23689 $this->_out($op);
23690 }
23691 return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
23692 }
23693
23699 protected function removeTagNamespace($name) {
23700 if(strpos($name, ':') !== false) {
23701 $parts = explode(':', $name);
23702 return $parts[(sizeof($parts) - 1)];
23703 }
23704 return $name;
23705 }
23706
23717 protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
23718 $name = $this->removeTagNamespace($name);
23719 // check if we are in clip mode
23720 if ($this->svgclipmode) {
23721 $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]);
23722 return;
23723 }
23724 if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
23725 if (isset($attribs['id'])) {
23726 $attribs['child_elements'] = array();
23727 $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23728 return;
23729 }
23730 if (end($this->svgdefs) !== FALSE) {
23731 $last_svgdefs_id = key($this->svgdefs);
23732 if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
23733 $attribs['id'] = 'DF_'.(count($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements']) + 1);
23734 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23735 return;
23736 }
23737 }
23738 return;
23739 }
23740 $clipping = false;
23741 if ($parser == 'clip-path') {
23742 // set clipping mode
23743 $clipping = true;
23744 }
23745 // get styling properties
23746 $prev_svgstyle = $this->svgstyles[max(0,(count($this->svgstyles) - 1))]; // previous style
23747 $svgstyle = $this->svgstyles[0]; // set default style
23748 if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) {
23749 // default fill attribute for clipping
23750 $attribs['fill'] = 'none';
23751 }
23752 if (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) {
23753 // fix style for regular expression
23754 $attribs['style'] = ';'.$attribs['style'];
23755 }
23756 foreach ($prev_svgstyle as $key => $val) {
23757 if (in_array($key, TCPDF_IMAGES::$svginheritprop)) {
23758 // inherit previous value
23759 $svgstyle[$key] = $val;
23760 }
23761 if (isset($attribs[$key]) AND !TCPDF_STATIC::empty_string($attribs[$key])) {
23762 // specific attribute settings
23763 if ($attribs[$key] == 'inherit') {
23764 $svgstyle[$key] = $val;
23765 } else {
23766 $svgstyle[$key] = $attribs[$key];
23767 }
23768 } elseif (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) {
23769 // CSS style syntax
23770 $attrval = array();
23771 if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
23772 if ($attrval[1] == 'inherit') {
23773 $svgstyle[$key] = $val;
23774 } else {
23775 $svgstyle[$key] = $attrval[1];
23776 }
23777 }
23778 }
23779 }
23780 // transformation matrix
23781 if (!empty($ctm)) {
23782 $tm = $ctm;
23783 } else {
23784 $tm = array(1,0,0,1,0,0);
23785 }
23786 if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
23788 }
23789 $svgstyle['transfmatrix'] = $tm;
23790 $invisible = false;
23791 if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
23792 // the current graphics element is invisible (nothing is painted)
23793 $invisible = true;
23794 }
23795 // process tag
23796 switch($name) {
23797 case 'defs': {
23798 $this->svgdefsmode = true;
23799 break;
23800 }
23801 // clipPath
23802 case 'clipPath': {
23803 if ($invisible) {
23804 break;
23805 }
23806 $this->svgclipmode = true;
23807 if (!isset($attribs['id'])) {
23808 $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1);
23809 }
23810 $this->svgclipid = $attribs['id'];
23811 $this->svgclippaths[$this->svgclipid] = array();
23812 $this->svgcliptm[$this->svgclipid] = $tm;
23813 break;
23814 }
23815 case 'svg': {
23816 // start of SVG object
23817 if(++$this->svg_tag_depth <= 1) {
23818 break;
23819 }
23820 // inner SVG
23821 array_push($this->svgstyles, $svgstyle);
23822 $this->StartTransform();
23823 $svgX = (isset($attribs['x'])?$attribs['x']:0);
23824 $svgY = (isset($attribs['y'])?$attribs['y']:0);
23825 $svgW = (isset($attribs['width'])?$attribs['width']:0);
23826 $svgH = (isset($attribs['height'])?$attribs['height']:0);
23827 // set x, y position using transform matrix
23828 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array( 1, 0, 0, 1, $svgX, $svgY));
23829 $this->SVGTransform($tm);
23830 // set clipping for width and height
23831 $x = 0;
23832 $y = 0;
23833 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):$this->w);
23834 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):$this->h);
23835 // draw clipping rect
23836 $this->Rect($x, $y, $w, $h, 'CNZ', array(), array());
23837 // parse viewbox, calculate extra transformation matrix
23838 if (isset($attribs['viewBox'])) {
23839 $tmp = array();
23840 preg_match_all("/[0-9]+/", $attribs['viewBox'], $tmp);
23841 $tmp = $tmp[0];
23842 if (sizeof($tmp) == 4) {
23843 $vx = $tmp[0];
23844 $vy = $tmp[1];
23845 $vw = $tmp[2];
23846 $vh = $tmp[3];
23847 // get aspect ratio
23848 $tmp = array();
23849 $aspectX = 'xMid';
23850 $aspectY = 'YMid';
23851 $fit = 'meet';
23852 if (isset($attribs['preserveAspectRatio'])) {
23853 if($attribs['preserveAspectRatio'] == 'none') {
23854 $fit = 'none';
23855 } else {
23856 preg_match_all('/[a-zA-Z]+/', $attribs['preserveAspectRatio'], $tmp);
23857 $tmp = $tmp[0];
23858 if ((sizeof($tmp) == 2) AND (strlen($tmp[0]) == 8) AND (in_array($tmp[1], array('meet', 'slice', 'none')))) {
23859 $aspectX = substr($tmp[0], 0, 4);
23860 $aspectY = substr($tmp[0], 4, 4);
23861 $fit = $tmp[1];
23862 }
23863 }
23864 }
23865 $wr = ($svgW / $vw);
23866 $hr = ($svgH / $vh);
23867 $ax = $ay = 0;
23868 if ((($fit == 'meet') AND ($hr < $wr)) OR (($fit == 'slice') AND ($hr > $wr))) {
23869 if ($aspectX == 'xMax') {
23870 $ax = (($vw * ($wr / $hr)) - $vw);
23871 }
23872 if ($aspectX == 'xMid') {
23873 $ax = ((($vw * ($wr / $hr)) - $vw) / 2);
23874 }
23875 $wr = $hr;
23876 } elseif ((($fit == 'meet') AND ($hr > $wr)) OR (($fit == 'slice') AND ($hr < $wr))) {
23877 if ($aspectY == 'YMax') {
23878 $ay = (($vh * ($hr / $wr)) - $vh);
23879 }
23880 if ($aspectY == 'YMid') {
23881 $ay = ((($vh * ($hr / $wr)) - $vh) / 2);
23882 }
23883 $hr = $wr;
23884 }
23885 $newtm = array($wr, 0, 0, $hr, (($wr * ($ax - $vx)) - $svgX), (($hr * ($ay - $vy)) - $svgY));
23887 $this->SVGTransform($tm);
23888 }
23889 }
23890 $this->setSVGStyles($svgstyle, $prev_svgstyle);
23891 break;
23892 }
23893 case 'g': {
23894 // group together related graphics elements
23895 array_push($this->svgstyles, $svgstyle);
23896 $this->StartTransform();
23897 $x = (isset($attribs['x'])?$attribs['x']:0);
23898 $y = (isset($attribs['y'])?$attribs['y']:0);
23899 $w = 1;//(isset($attribs['width'])?$attribs['width']:1);
23900 $h = 1;//(isset($attribs['height'])?$attribs['height']:1);
23901 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
23902 $this->SVGTransform($tm);
23903 $this->setSVGStyles($svgstyle, $prev_svgstyle);
23904 break;
23905 }
23906 case 'linearGradient': {
23907 if ($this->pdfa_mode) {
23908 break;
23909 }
23910 if (!isset($attribs['id'])) {
23911 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
23912 }
23913 $this->svggradientid = $attribs['id'];
23914 $this->svggradients[$this->svggradientid] = array();
23915 $this->svggradients[$this->svggradientid]['type'] = 2;
23916 $this->svggradients[$this->svggradientid]['stops'] = array();
23917 if (isset($attribs['gradientUnits'])) {
23918 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
23919 } else {
23920 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
23921 }
23922 //$attribs['spreadMethod']
23923 if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2'])))
23924 OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%'))
23925 OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%'))
23926 OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%'))
23927 OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) {
23928 $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
23929 } else {
23930 $this->svggradients[$this->svggradientid]['mode'] = 'measure';
23931 }
23932 $x1 = (isset($attribs['x1'])?$attribs['x1']:'0');
23933 $y1 = (isset($attribs['y1'])?$attribs['y1']:'0');
23934 $x2 = (isset($attribs['x2'])?$attribs['x2']:'100');
23935 $y2 = (isset($attribs['y2'])?$attribs['y2']:'0');
23936 if (isset($attribs['gradientTransform'])) {
23937 $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
23938 }
23939 $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
23940 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23941 // gradient is defined on another place
23942 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
23943 }
23944 break;
23945 }
23946 case 'radialGradient': {
23947 if ($this->pdfa_mode) {
23948 break;
23949 }
23950 if (!isset($attribs['id'])) {
23951 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
23952 }
23953 $this->svggradientid = $attribs['id'];
23954 $this->svggradients[$this->svggradientid] = array();
23955 $this->svggradients[$this->svggradientid]['type'] = 3;
23956 $this->svggradients[$this->svggradientid]['stops'] = array();
23957 if (isset($attribs['gradientUnits'])) {
23958 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
23959 } else {
23960 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
23961 }
23962 //$attribs['spreadMethod']
23963 if (((!isset($attribs['cx'])) AND (!isset($attribs['cy'])))
23964 OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%'))
23965 OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')))) {
23966 $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
23967 } elseif (isset($attribs['r']) AND is_numeric($attribs['r']) AND ($attribs['r']) <= 1) {
23968 $this->svggradients[$this->svggradientid]['mode'] = 'ratio';
23969 } else {
23970 $this->svggradients[$this->svggradientid]['mode'] = 'measure';
23971 }
23972 $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5);
23973 $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5);
23974 $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx);
23975 $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy);
23976 $r = (isset($attribs['r']) ? $attribs['r'] : 0.5);
23977 if (isset($attribs['gradientTransform'])) {
23978 $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
23979 }
23980 $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
23981 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23982 // gradient is defined on another place
23983 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
23984 }
23985 break;
23986 }
23987 case 'stop': {
23988 // gradient stops
23989 if (substr($attribs['offset'], -1) == '%') {
23990 $offset = floatval(substr($attribs['offset'], -1)) / 100;
23991 } else {
23992 $offset = floatval($attribs['offset']);
23993 if ($offset > 1) {
23994 $offset /= 100;
23995 }
23996 }
23997 $stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors):'black';
23998 $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
23999 $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
24000 break;
24001 }
24002 // paths
24003 case 'path': {
24004 if ($invisible) {
24005 break;
24006 }
24007 if (isset($attribs['d'])) {
24008 $d = trim($attribs['d']);
24009 if (!empty($d)) {
24010 $x = (isset($attribs['x'])?$attribs['x']:0);
24011 $y = (isset($attribs['y'])?$attribs['y']:0);
24012 $w = (isset($attribs['width'])?$attribs['width']:1);
24013 $h = (isset($attribs['height'])?$attribs['height']:1);
24014 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
24015 if ($clipping) {
24016 $this->SVGTransform($tm);
24017 $this->SVGPath($d, 'CNZ');
24018 } else {
24019 $this->StartTransform();
24020 $this->SVGTransform($tm);
24021 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ'));
24022 if (!empty($obstyle)) {
24023 $this->SVGPath($d, $obstyle);
24024 }
24025 $this->StopTransform();
24026 }
24027 }
24028 }
24029 break;
24030 }
24031 // shapes
24032 case 'rect': {
24033 if ($invisible) {
24034 break;
24035 }
24036 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24037 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24038 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24039 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24040 $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
24041 $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
24042 if ($clipping) {
24043 $this->SVGTransform($tm);
24044 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
24045 } else {
24046 $this->StartTransform();
24047 $this->SVGTransform($tm);
24048 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
24049 if (!empty($obstyle)) {
24050 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
24051 }
24052 $this->StopTransform();
24053 }
24054 break;
24055 }
24056 case 'circle': {
24057 if ($invisible) {
24058 break;
24059 }
24060 $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0);
24061 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
24062 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
24063 $x = ($cx - $r);
24064 $y = ($cy - $r);
24065 $w = (2 * $r);
24066 $h = $w;
24067 if ($clipping) {
24068 $this->SVGTransform($tm);
24069 $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
24070 } else {
24071 $this->StartTransform();
24072 $this->SVGTransform($tm);
24073 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
24074 if (!empty($obstyle)) {
24075 $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
24076 }
24077 $this->StopTransform();
24078 }
24079 break;
24080 }
24081 case 'ellipse': {
24082 if ($invisible) {
24083 break;
24084 }
24085 $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0);
24086 $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0);
24087 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
24088 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
24089 $x = ($cx - $rx);
24090 $y = ($cy - $ry);
24091 $w = (2 * $rx);
24092 $h = (2 * $ry);
24093 if ($clipping) {
24094 $this->SVGTransform($tm);
24095 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
24096 } else {
24097 $this->StartTransform();
24098 $this->SVGTransform($tm);
24099 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
24100 if (!empty($obstyle)) {
24101 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
24102 }
24103 $this->StopTransform();
24104 }
24105 break;
24106 }
24107 case 'line': {
24108 if ($invisible) {
24109 break;
24110 }
24111 $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
24112 $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
24113 $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
24114 $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
24115 $x = $x1;
24116 $y = $y1;
24117 $w = abs($x2 - $x1);
24118 $h = abs($y2 - $y1);
24119 if (!$clipping) {
24120 $this->StartTransform();
24121 $this->SVGTransform($tm);
24122 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
24123 $this->Line($x1, $y1, $x2, $y2);
24124 $this->StopTransform();
24125 }
24126 break;
24127 }
24128 case 'polyline':
24129 case 'polygon': {
24130 if ($invisible) {
24131 break;
24132 }
24133 $points = (isset($attribs['points'])?$attribs['points']:'0 0');
24134 $points = trim($points);
24135 // note that point may use a complex syntax not covered here
24136 $points = preg_split('/[\,\s]+/si', $points);
24137 if (count($points) < 4) {
24138 break;
24139 }
24140 $p = array();
24141 $xmin = 2147483647;
24142 $xmax = 0;
24143 $ymin = 2147483647;
24144 $ymax = 0;
24145 foreach ($points as $key => $val) {
24146 $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
24147 if (($key % 2) == 0) {
24148 // X coordinate
24149 $xmin = min($xmin, $p[$key]);
24150 $xmax = max($xmax, $p[$key]);
24151 } else {
24152 // Y coordinate
24153 $ymin = min($ymin, $p[$key]);
24154 $ymax = max($ymax, $p[$key]);
24155 }
24156 }
24157 $x = $xmin;
24158 $y = $ymin;
24159 $w = ($xmax - $xmin);
24160 $h = ($ymax - $ymin);
24161 if ($name == 'polyline') {
24162 $this->StartTransform();
24163 $this->SVGTransform($tm);
24164 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
24165 if (!empty($obstyle)) {
24166 $this->PolyLine($p, $obstyle, array(), array());
24167 }
24168 $this->StopTransform();
24169 } else { // polygon
24170 if ($clipping) {
24171 $this->SVGTransform($tm);
24172 $this->Polygon($p, 'CNZ', array(), array(), true);
24173 } else {
24174 $this->StartTransform();
24175 $this->SVGTransform($tm);
24176 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
24177 if (!empty($obstyle)) {
24178 $this->Polygon($p, $obstyle, array(), array(), true);
24179 }
24180 $this->StopTransform();
24181 }
24182 }
24183 break;
24184 }
24185 // image
24186 case 'image': {
24187 if ($invisible) {
24188 break;
24189 }
24190 if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
24191 break;
24192 }
24193 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24194 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24195 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24196 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24197 $img = $attribs['xlink:href'];
24198 if (!$clipping) {
24199 $this->StartTransform();
24200 $this->SVGTransform($tm);
24201 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
24202 if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
24203 // embedded image encoded as base64
24204 $img = '@'.base64_decode(substr($img, strlen($m[0])));
24205 } else {
24206 // fix image path
24207 if (!TCPDF_STATIC::empty_string($this->svgdir) AND (($img[0] == '.') OR (basename($img) == $img))) {
24208 // replace relative path with full server path
24209 $img = $this->svgdir.'/'.$img;
24210 }
24211 if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
24212 $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
24213 if (($findroot === false) OR ($findroot > 1)) {
24214 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
24215 $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
24216 } else {
24217 $img = $_SERVER['DOCUMENT_ROOT'].$img;
24218 }
24219 }
24220 }
24221 $img = urldecode($img);
24222 $testscrtype = @parse_url($img);
24223 if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
24224 // convert URL to server path
24225 $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
24226 }
24227 }
24228 // get image type
24230 if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
24231 $this->ImageEps($img, $x, $y, $w, $h);
24232 } elseif ($imgtype == 'svg') {
24233 // store SVG vars
24234 $svggradients = $this->svggradients;
24235 $svggradientid = $this->svggradientid;
24236 $svgdefsmode = $this->svgdefsmode;
24237 $svgdefs = $this->svgdefs;
24238 $svgclipmode = $this->svgclipmode;
24239 $svgclippaths = $this->svgclippaths;
24240 $svgcliptm = $this->svgcliptm;
24241 $svgclipid = $this->svgclipid;
24242 $svgtext = $this->svgtext;
24243 $svgtextmode = $this->svgtextmode;
24244 $this->ImageSVG($img, $x, $y, $w, $h);
24245 // restore SVG vars
24246 $this->svggradients = $svggradients;
24247 $this->svggradientid = $svggradientid;
24248 $this->svgdefsmode = $svgdefsmode;
24249 $this->svgdefs = $svgdefs;
24250 $this->svgclipmode = $svgclipmode;
24251 $this->svgclippaths = $svgclippaths;
24252 $this->svgcliptm = $svgcliptm;
24253 $this->svgclipid = $svgclipid;
24254 $this->svgtext = $svgtext;
24255 $this->svgtextmode = $svgtextmode;
24256 } else {
24257 $this->Image($img, $x, $y, $w, $h);
24258 }
24259 $this->StopTransform();
24260 }
24261 break;
24262 }
24263 // text
24264 case 'text':
24265 case 'tspan': {
24266 if (isset($this->svgtextmode['text-anchor']) AND !empty($this->svgtext)) {
24267 // @TODO: unsupported feature
24268 }
24269 // only basic support - advanced features must be implemented
24270 $this->svgtextmode['invisible'] = $invisible;
24271 if ($invisible) {
24272 break;
24273 }
24274 array_push($this->svgstyles, $svgstyle);
24275 if (isset($attribs['x'])) {
24276 $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false);
24277 } elseif ($name == 'tspan') {
24278 $x = $this->x;
24279 } else {
24280 $x = 0;
24281 }
24282 if (isset($attribs['dx'])) {
24283 $x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false);
24284 }
24285 if (isset($attribs['y'])) {
24286 $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false);
24287 } elseif ($name == 'tspan') {
24288 $y = $this->y;
24289 } else {
24290 $y = 0;
24291 }
24292 if (isset($attribs['dy'])) {
24293 $y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false);
24294 }
24295 $svgstyle['text-color'] = $svgstyle['fill'];
24296 $this->svgtext = '';
24297 if (isset($svgstyle['text-anchor'])) {
24298 $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor'];
24299 } else {
24300 $this->svgtextmode['text-anchor'] = 'start';
24301 }
24302 if (isset($svgstyle['direction'])) {
24303 if ($svgstyle['direction'] == 'rtl') {
24304 $this->svgtextmode['rtl'] = true;
24305 } else {
24306 $this->svgtextmode['rtl'] = false;
24307 }
24308 } else {
24309 $this->svgtextmode['rtl'] = false;
24310 }
24311 if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
24312 $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false);
24313 } else {
24314 $this->svgtextmode['stroke'] = false;
24315 }
24316 $this->StartTransform();
24317 $this->SVGTransform($tm);
24318 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
24319 $this->x = $x;
24320 $this->y = $y;
24321 break;
24322 }
24323 // use
24324 case 'use': {
24325 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24326 $svgdefid = substr($attribs['xlink:href'], 1);
24327 if (isset($this->svgdefs[$svgdefid])) {
24328 $use = $this->svgdefs[$svgdefid];
24329 if (isset($attribs['xlink:href'])) {
24330 unset($attribs['xlink:href']);
24331 }
24332 if (isset($attribs['id'])) {
24333 unset($attribs['id']);
24334 }
24335 if (isset($use['attribs']['x']) AND isset($attribs['x'])) {
24336 $attribs['x'] += $use['attribs']['x'];
24337 }
24338 if (isset($use['attribs']['y']) AND isset($attribs['y'])) {
24339 $attribs['y'] += $use['attribs']['y'];
24340 }
24341 if (empty($attribs['style'])) {
24342 $attribs['style'] = '';
24343 }
24344 if (!empty($use['attribs']['style'])) {
24345 // merge styles
24346 $attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']);
24347 }
24348 $attribs = array_merge($use['attribs'], $attribs);
24349 $this->startSVGElementHandler($parser, $use['name'], $attribs);
24350 return;
24351 }
24352 }
24353 break;
24354 }
24355 default: {
24356 break;
24357 }
24358 } // end of switch
24359 // process child elements
24360 if (!empty($attribs['child_elements'])) {
24361 $child_elements = $attribs['child_elements'];
24362 unset($attribs['child_elements']);
24363 foreach($child_elements as $child_element) {
24364 if (empty($child_element['attribs']['closing_tag'])) {
24365 $this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']);
24366 } else {
24367 if (isset($child_element['attribs']['content'])) {
24368 $this->svgtext = $child_element['attribs']['content'];
24369 }
24370 $this->endSVGElementHandler('child-tag', $child_element['name']);
24371 }
24372 }
24373 }
24374 }
24375
24384 protected function endSVGElementHandler($parser, $name) {
24385 $name = $this->removeTagNamespace($name);
24386 if ($this->svgdefsmode AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {;
24387 if (end($this->svgdefs) !== FALSE) {
24388 $last_svgdefs_id = key($this->svgdefs);
24389 if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
24390 foreach($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) {
24391 if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) {
24392 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24393 return;
24394 }
24395 }
24396 if ($this->svgdefs[$last_svgdefs_id]['name'] == $name) {
24397 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24398 return;
24399 }
24400 }
24401 }
24402 return;
24403 }
24404 switch($name) {
24405 case 'defs': {
24406 $this->svgdefsmode = false;
24407 break;
24408 }
24409 // clipPath
24410 case 'clipPath': {
24411 $this->svgclipmode = false;
24412 break;
24413 }
24414 case 'svg': {
24415 if (--$this->svg_tag_depth <= 0) {
24416 break;
24417 }
24418 }
24419 case 'g': {
24420 // ungroup: remove last style from array
24421 array_pop($this->svgstyles);
24422 $this->StopTransform();
24423 break;
24424 }
24425 case 'text':
24426 case 'tspan': {
24427 if ($this->svgtextmode['invisible']) {
24428 // This implementation must be fixed to following the rule:
24429 // If the 'visibility' property is set to hidden on a 'tspan', 'tref' or 'altGlyph' element, then the text is invisible but still takes up space in text layout calculations.
24430 break;
24431 }
24432 // print text
24433 $text = $this->svgtext;
24434 //$text = $this->stringTrim($text);
24435 $textlen = $this->GetStringWidth($text);
24436 if ($this->svgtextmode['text-anchor'] != 'start') {
24437 // check if string is RTL text
24438 if ($this->svgtextmode['text-anchor'] == 'end') {
24439 if ($this->svgtextmode['rtl']) {
24440 $this->x += $textlen;
24441 } else {
24442 $this->x -= $textlen;
24443 }
24444 } elseif ($this->svgtextmode['text-anchor'] == 'middle') {
24445 if ($this->svgtextmode['rtl']) {
24446 $this->x += ($textlen / 2);
24447 } else {
24448 $this->x -= ($textlen / 2);
24449 }
24450 }
24451 }
24452 $textrendermode = $this->textrendermode;
24453 $textstrokewidth = $this->textstrokewidth;
24454 $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false);
24455 if ($name == 'text') {
24456 // store current coordinates
24457 $tmpx = $this->x;
24458 $tmpy = $this->y;
24459 }
24460 // print the text
24461 $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
24462 if ($name == 'text') {
24463 // restore coordinates
24464 $this->x = $tmpx;
24465 $this->y = $tmpy;
24466 }
24467 // restore previous rendering mode
24468 $this->textrendermode = $textrendermode;
24469 $this->textstrokewidth = $textstrokewidth;
24470 $this->svgtext = '';
24471 $this->StopTransform();
24472 if (!$this->svgdefsmode) {
24473 array_pop($this->svgstyles);
24474 }
24475 break;
24476 }
24477 default: {
24478 break;
24479 }
24480 }
24481 }
24482
24491 protected function segSVGContentHandler($parser, $data) {
24492 $this->svgtext .= $data;
24493 }
24494
24495 // --- END SVG METHODS -----------------------------------------------------
24496
24497} // END OF TCPDF CLASS
24498
24499//============================================================+
24500// END OF FILE
24501//============================================================+
sprintf('%.4f', $callTime)
$column
Definition: 39dropdown.php:62
$parser
Definition: BPMN2Parser.php:23
$result
$n
Definition: RandomTest.php:85
$size
Definition: RandomTest.php:84
$space
Definition: Sanitizer.php:43
if(! $in) $columns
Definition: Utf8Test.php:45
global $l
Definition: afr.php:30
foreach($mandatory_scripts as $file) $timestamp
Definition: buildRTE.php:81
PHPExcel root directory.
Definition: PHPExcel.php:30
An exception for terminatinating execution or to throw for unit testing.
PHP class to creates array representations for 2D barcodes to be used with TCPDF (http://www....
PHP class to creates array representations for common 1D barcodes to be used with TCPDF (http://www....
static getSpotColor($name, &$spotc)
Return the Spot color array.
static convertHTMLColorToDec($hcolor, &$spotc, $defcol=array('R'=>128, 'G'=>128, 'B'=>128))
Returns an array (RGB or CMYK) from an html color name, or a six-digit (i.e.
static _JScolor($color)
Convert color to javascript color.
static utf8StrRev($str, $setbom=false, $forcertl=false, $isunicode=true, &$currentfont)
Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
static UTF8ArrSubString($strarr, $start='', $end='', $unicode=true)
Extract a slice of the $strarr array and return it as string.
static UniArrSubString($uniarr, $start='', $end='')
Extract a slice of the $uniarr array and return it as string.
static getFontFullPath($file, $fontdir=false)
Return font full path.
static UTF8ArrToLatin1Arr($unicode)
Converts UTF-8 characters array to array of Latin1 characters array
static utf8Bidi($ta, $str='', $forcertl=false, $isunicode=true, &$currentfont)
Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
static UTF8ToLatin1($str, $isunicode=true, &$currentfont)
Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.
static unichr($c, $unicode=true)
Returns the unicode caracter specified by the value.
static _getfontpath()
Return fonts path.
static UTF8StringToArray($str, $isunicode=true, &$currentfont)
Converts UTF-8 strings to codepoints array.
static arrUTF8ToUTF16BE($unicode, $setbom=false)
Converts array of UTF-8 characters to UTF16-BE string.
static getFontRefSize($size, $refsize=12)
Get a reference font size.
static UTF8ToUTF16BE($str, $setbom=false, $isunicode=true, &$currentfont)
Converts UTF-8 strings to UTF16-BE.
static UTF8ArrayToUniArray($ta, $isunicode=true)
Convert an array of UTF8 values to array of unicode characters.
static _getTrueTypeFontSubset($font, $subsetchars)
Returns a subset of the TrueType font data without the unused glyphs.
static $uni_type
Array of Unicode types.
static $uni_identity_h
ToUnicode map for Identity-H stream static.
static $uni_RE_PATTERN_ARABIC
Pattern to test Arabic strings using regular expressions.
static $uni_RE_PATTERN_RTL
Pattern to test RTL (Righ-To-Left) strings using regular expressions.
static setGDImageTransparency($new_image, $image)
Set the transparency for the given GD image.
static getImageFileType($imgfile, $iminfo=array())
Return the image type given the file name or array returned by getimagesize() function.
static _toPNG($image, $tempfile)
Convert the loaded image to a PNG and then return a structure for the PDF creator.
static _parsejpeg($file)
Extract info from a JPEG file without using the GD library.
static $svginheritprop
Array of hinheritable SVG properties.
static _toJPEG($image, $quality, $tempfile)
Convert the loaded image to a JPEG and then return a structure for the PDF creator.
Static methods used by the TCPDF class.
static setPageBoxes($page, $type, $llx, $lly, $urx, $ury, $points=false, $k, $pagedim=array())
Set page boundaries.
static getPathPaintOperator($style, $default='S')
Get the Path-Painting Operators.
static getPageMode($mode='UseNone')
Get the canonical page layout mode.
static fopenLocal($filename, $mode)
Wrapper to use fopen only with local files.
static getCSSdataArray($dom, $key, $css)
Returns the styles array that apply for the selected HTML tag.
static getUserPermissionCode($permissions, $mode=0)
Return the permission code used on encryption (P value).
static getFormattedDate($time)
Returns a formatted date-time.
static extractCSSproperties($cssdata)
Extracts the CSS properties from a CSS string.
static $alias_group_tot_pages
String alias for total number of pages in a single group.
static getAnnotOptFromJSProp($prop, &$spot_colors, $rtl=false)
Convert JavaScript form fields properties array to Annotation Properties array.
static getEncPermissionsString($protection)
Convert encryption P value to a string of bytes, low-order byte first.
static getPageLayoutMode($layout='SinglePage')
Get the canonical page layout mode.
static _escape($s)
Add "\" before "", "(" and ")".
static getObjFilename($type='tmp', $file_id='')
Returns a temporary filename for caching object on filesystem.
static getPageSizeFromFormat($format)
Get page dimensions from format name.
static $enc_padding
Encryption padding string.
static $pageboxes
Array page boxes names static.
static $alias_tot_pages
String alias for total number of pages.
static getTagStyleFromCSSarray($css)
Compact CSS data array into single string.
static intToRoman($number)
Returns the Roman representation of an integer number.
static sendOutputData($data, $length)
Output input data and compress it if possible.
static replacePageNumAliases($page, $replace, $diff=0)
Replace page number aliases with number.
static fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='', &$tagvspaces)
Cleanup HTML code (requires HTML Tidy library).
static getTCPDFProducer()
Return the current TCPDF producer.
static getVectorsAngle($x1, $y1, $x2, $y2)
Returns the angle in radiants between two vectors.
static $alias_group_num_page
String alias for group page number.
static $byterange_string
ByteRange placemark used during digital signature process.
static objclone($object)
Creates a copy of a class object.
static encodeNameObject($name)
Encode a name object.
static fileGetContents($file)
Reads entire file into a string.
static formatPageNumber($num)
Format the page numbers.
static removeSHY($txt='', $unicode=true)
Removes SHY characters from text.
static pregSplit($pattern, $modifiers, $subject, $limit=NULL, $flags=NULL)
Split string by a regular expression.
static convertHexStringToString($bs)
Convert hexadecimal string to string.
static formatTOCPageNumber($num)
Format the page numbers on the Table Of Content.
static revstrpos($haystack, $needle, $offset=0)
Find position of last occurrence of a substring in a string.
static swapPageBoxCoordinates($page, $pagedim)
Swap X and Y coordinates of page boxes (change page boxes orientation).
static empty_string($str)
Determine whether a string is empty.
static _AESnopad($key, $text)
Returns the input text exrypted using AES algorithm and the specified key.
static _escapeXML($str)
Escape some special characters (< > &) for XML output.
static set_mqr($mqr)
Sets the current active configuration setting of magic_quotes_runtime (if the set_magic_quotes_runtim...
static getHyphenPatternsFromTEX($file)
Returns an array of hyphenation patterns.
static getRandomSeed($seed='')
Returns a string containing random data to be used as a seed for encryption methods.
static _AES($key, $text)
Returns the input text exrypted using AES algorithm and the specified key.
static _md5_16($str)
Encrypts a string using MD5 and returns it's value as a binary string.
static getBorderMode($brd, $position='start', $opencell=true)
Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages)
static $alias_num_page
String alias for page number.
static _RC4($key, $text, &$last_enc_key, &$last_enc_key_c)
Returns the input text encrypted using RC4 algorithm and the specified key.
static getSVGTransformMatrix($attribute)
Get the tranformation matrix from SVG transform attribute.
static $alias_right_shift
String alias for right shift compensation used to correctly align page numbers on the right.
static getTransformationMatrixProduct($ta, $tb)
Get the product of two SVG tranformation matrices.
static isValidURL($url)
Check if the URL exist.
static getTimestamp($date)
Returns timestamp in seconds from formatted date-time.
static get_mqr()
Gets the current active configuration setting of magic_quotes_runtime (if the get_magic_quotes_runtim...
PHP class for generating PDF documents without requiring external extensions.
Definition: tcpdf.php:134
Ln($h='', $cell=false)
Performs a line break.
Definition: tcpdf.php:7367
SetXY($x, $y, $rtloff=false)
Defines the abscissa and ordinate of the current position.
Definition: tcpdf.php:7507
getPageNumGroupAlias()
Return the alias for the page number on the current page group.
Definition: tcpdf.php:13655
$colxshift
Array of: X difference between table cell x start and starting page margin, cellspacing,...
Definition: tcpdf.php:1457
endTOCPage()
Terminate the current TOC (Table Of Content) page.
Definition: tcpdf.php:3087
startPage($orientation='', $format='', $tocpage=false)
Starts a new page to the document.
Definition: tcpdf.php:3152
$DrawColor
Commands for drawing color.
Definition: tcpdf.php:422
getImageRBY()
Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image.
Definition: tcpdf.php:3366
$doc_modification_timestamp
Document modification date-time.
Definition: tcpdf.php:1772
$crMargin
Cell right margin (used by regions).
Definition: tcpdf.php:262
IncludeJS($script)
Adds a javascript.
Definition: tcpdf.php:12493
MirrorV($y='')
Verical Mirroring.
Definition: tcpdf.php:11077
unserializeTCPDFtagParameters($data)
Unserialize parameters to be used with TCPDF tag in HTML code.
Definition: tcpdf.php:17072
$pdfunit
Default unit of measure for document.
Definition: tcpdf.php:1485
setJPEGQuality($quality)
Set the default JPEG compression quality (1-100)
Definition: tcpdf.php:13951
AddFont($family, $style='', $fontfile='', $subset='default')
Imports a TrueType, Type1, core, or CID0 font and makes it available.
Definition: tcpdf.php:4171
$encryptdata
Array containing encryption settings.
Definition: tcpdf.php:808
setGraphicVars($gvars, $extended=false)
Set graphic variables.
Definition: tcpdf.php:20606
$feps
Epsilon value used for float calculations.
Definition: tcpdf.php:1092
endSVGElementHandler($parser, $name)
Sets the closing SVG element handler function for the XML parser.
Definition: tcpdf.php:24384
_putimages()
Output images.
Definition: tcpdf.php:9100
AddSpotColor($name, $c, $m, $y, $k)
Defines a new spot color.
Definition: tcpdf.php:3716
setFontSubsetting($enable=true)
Enable or disable default option for font subsetting.
Definition: tcpdf.php:22117
$custom_xmp
Custom XMP data.
Definition: tcpdf.php:1779
stringLeftTrim($str, $replace='')
Left trim the input string.
Definition: tcpdf.php:22145
_putsignature()
Add certification signature (DocMDP or UR3) You can set only one signature type.
Definition: tcpdf.php:13289
endTemplate()
End the current XObject Template started with startTemplate() and restore the previous graphic state.
Definition: tcpdf.php:22305
$CoreFonts
Array of standard font names.
Definition: tcpdf.php:318
lastPage($resetmargins=false)
Reset pointer to the last document page.
Definition: tcpdf.php:3042
getFontDescent($font, $style='', $size=0)
Return the font descent value.
Definition: tcpdf.php:4567
getOriginalMargins()
Returns an array containing original margins:
Definition: tcpdf.php:15772
getCSSFontStretching($stretch, $parent=100)
Returns the percentage of font stretching from CSS value.
Definition: tcpdf.php:16123
getFontBBox()
Returns the bounding box of the current font in user units.
Definition: tcpdf.php:4495
$listcount
HTML PARSER: array count list items on nested lists.
Definition: tcpdf.php:728
$lisymbol
Symbol used for HTML unordered list items.
Definition: tcpdf.php:1057
SetLineWidth($width)
Defines the line width.
Definition: tcpdf.php:11279
$InFooter
Flag set when processing page footer.
Definition: tcpdf.php:464
resetLastH()
Reset the last cell height.
Definition: tcpdf.php:2440
$page_obj_id
ID of page objects.
Definition: tcpdf.php:1324
$OutlineRoot
Outline root for bookmark.
Definition: tcpdf.php:845
SetTopMargin($margin)
Defines the top margin.
Definition: tcpdf.php:2598
$sign
Boolean flag to enable document digital signature.
Definition: tcpdf.php:1254
setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='')
Enable document signature (requires the OpenSSL Library).
Definition: tcpdf.php:13414
setFooterData($tc=array(0, 0, 0), $lc=array(0, 0, 0))
Set footer data.
Definition: tcpdf.php:3272
$svgclippaths
Array of SVG clipPath commands.
Definition: tcpdf.php:1645
$file_id
File ID (used on document trailer).
Definition: tcpdf.php:829
$sig_obj_id
Digital signature object ID.
Definition: tcpdf.php:1317
getCellBorder($x, $y, $w, $h, $brd)
Returns the code to draw the cell border.
Definition: tcpdf.php:5554
$inthead
True when we are printing the thead section on a new page.
Definition: tcpdf.php:1415
$transfmrk
Array used to store positions of graphics transformation blocks inside the page buffer.
Definition: tcpdf.php:1135
$form_action
Current form action (used during XHTML rendering).
Definition: tcpdf.php:1352
openHTMLTagHandler($dom, $key, $cell)
Process opening tags.
Definition: tcpdf.php:18689
getFontStyle()
Returns the current font style.
Definition: tcpdf.php:15816
__construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false)
This is the class constructor.
Definition: tcpdf.php:1838
addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0, 0, 0))
Output a Table Of Content Index (TOC) using HTML templates.
Definition: tcpdf.php:21499
inPageBody()
Check if we are on the page body (excluding page header and footer).
Definition: tcpdf.php:3613
$re_spaces
Regular expression used to find blank characters (required for word-wrapping).
Definition: tcpdf.php:1303
getFontFamilyName($fontfamily)
Return normalized font name.
Definition: tcpdf.php:22196
$isunicode
Boolean flag set to true when the input text is unicode (require unicode fonts).
Definition: tcpdf.php:550
$theadMargins
Margins used for table header.
Definition: tcpdf.php:1247
setPageMark()
Set start-writing mark on current page stream used to put borders and fills.
Definition: tcpdf.php:3223
setFormDefaultProp($prop=array())
Set default properties for form fields.
Definition: tcpdf.php:12616
_dooverlinew($x, $y, $w)
Overline for rectangular text area.
Definition: tcpdf.php:10155
_putviewerpreferences()
Output viewer preferences.
Definition: tcpdf.php:9837
LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0, 0, 1, 0))
Paints a linear colour gradient.
Definition: tcpdf.php:14310
_dochecks()
Check for locale-related bug.
Definition: tcpdf.php:7791
$page
Current page number.
Definition: tcpdf.php:142
$tagvspaces
Array used for custom vertical spaces for HTML tags.
Definition: tcpdf.php:1099
$print_footer
Boolean flag to print/hide page footer.
Definition: tcpdf.php:631
setLastH($h)
Set the last cell height.
Definition: tcpdf.php:2417
$clMargin
Cell left margin (used by regions).
Definition: tcpdf.php:256
getImageBuffer($image)
Get image buffer content.
Definition: tcpdf.php:20789
getPDFData()
Returns the PDF data.
Definition: tcpdf.php:10355
setListIndentWidth($width)
Set custom width for list indentation.
Definition: tcpdf.php:20223
colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A, R, G, B, C, M, Y, K')
Paints color transition registration bars.
Definition: tcpdf.php:14028
checkPageRegions($h, $x, $y)
Check page for no-write regions and adapt current coordinates and page margins if necessary.
Definition: tcpdf.php:22584
$txtshadow
Text shadow data array.
Definition: tcpdf.php:690
_putocg()
Put pdf layers.
Definition: tcpdf.php:13697
SetCreator($creator)
Defines the creator of the document.
Definition: tcpdf.php:2911
_getobj($objid='')
Return the starting object string for the selected object ID.
Definition: tcpdf.php:10074
getAlpha()
Get the alpha mode array (CA, ca, BM, AIS).
Definition: tcpdf.php:13941
$listindentlevel
HTML PARSER: current list indententation level.
Definition: tcpdf.php:746
$form_mode
Current method to submit forms.
Definition: tcpdf.php:1366
_addfield($type, $name, $x, $y, $w, $h, $prop)
Adds a javascript form field.
Definition: tcpdf.php:12580
_putspotcolors()
Output Spot Colors Resources.
Definition: tcpdf.php:9332
_putfonts()
Output fonts.
Definition: tcpdf.php:8774
$pagelen
Array containing page lengths in bytes.
Definition: tcpdf.php:1163
TranslateX($t_x)
Translate graphic object horizontally.
Definition: tcpdf.php:11114
ScaleY($s_y, $x='', $y='')
Vertical Scaling.
Definition: tcpdf.php:11006
$dpi
DPI (Dot Per Inch) Document Resolution (do not change).
Definition: tcpdf.php:882
$internal_encoding
PHP internal encoding.
Definition: tcpdf.php:778
$ZoomMode
Zoom display mode.
Definition: tcpdf.php:470
Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array())
Draws a Bezier curve.
Definition: tcpdf.php:11582
getHeaderFont()
Get header font.
Definition: tcpdf.php:10312
getBuffer()
Get buffer content.
Definition: tcpdf.php:20710
getGraphicVars()
Returns current graphic variables as array.
Definition: tcpdf.php:20545
$default_monospaced_font
Default monospace font.
Definition: tcpdf.php:1219
$author
Document author.
Definition: tcpdf.php:500
getCellHeight($fontsize, $padding=TRUE)
Return the cell height.
Definition: tcpdf.php:2427
$spot_colors
Array of Spot colors.
Definition: tcpdf.php:1050
StopTransform()
Stops a 2D tranformation restoring previous graphic state.
Definition: tcpdf.php:10968
$page_regions
Array of no-write regions.
Definition: tcpdf.php:1555
getOverprint()
Get the overprint mode array (OP, op, OPM).
Definition: tcpdf.php:13896
PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2)
Draw the sector of an ellipse.
Definition: tcpdf.php:14790
$overprint
Overprint mode array.
Definition: tcpdf.php:1787
$encoding
Default encoding.
Definition: tcpdf.php:771
$svgcliptm
Array of SVG clipPath tranformation matrix.
Definition: tcpdf.php:1652
setContentMark($page=0)
Set start-writing mark on selected page.
Definition: tcpdf.php:3236
$ColorFlag
Indicates whether fill and text colors are different.
Definition: tcpdf.php:440
Error($msg)
Throw an exception or print an error message and die if the K_TCPDF_PARSER_THROW_EXCEPTION_ERROR cons...
Definition: tcpdf.php:2921
setPDFVersion($version='1.7')
Set the PDF version (check PDF reference for valid values).
Definition: tcpdf.php:13993
GetCharWidth($char, $notlast=true)
Returns the length of the char in user unit for the current font considering current stretching and s...
Definition: tcpdf.php:4087
SetLeftMargin($margin)
Defines the left margin.
Definition: tcpdf.php:2583
$pageopen
Store the fage status (true when opened, false when closed).
Definition: tcpdf.php:1212
$svggradientid
ID of last SVG gradient.
Definition: tcpdf.php:1617
setStartingPageNumber($num=1)
Set the starting page number.
Definition: tcpdf.php:13573
$page_boxes
Define the page boundaries boxes to be set on document.
Definition: tcpdf.php:1802
Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0, 0, 0), $x=-1, $link='')
Adds a bookmark.
Definition: tcpdf.php:12302
$images
Array of used images.
Definition: tcpdf.php:342
removePageRegion($key)
Remove a single no-write region.
Definition: tcpdf.php:22566
setLanguageArray($language)
Set language array.
Definition: tcpdf.php:10342
Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true)
Draws a polygon.
Definition: tcpdf.php:11861
getAbsFontMeasure($s)
Convert a relative font measure into absolute value.
Definition: tcpdf.php:4534
setPageBuffer($page, $data, $append=false)
Set page buffer content.
Definition: tcpdf.php:20722
$fwPt
Width of page format in points.
Definition: tcpdf.php:208
SetAbsX($x)
Set the absolute X coordinate of the current pointer.
Definition: tcpdf.php:7519
copyPage($page=0)
Clone the specified page to a new page.
Definition: tcpdf.php:21212
putHtmlListBullet($listdepth, $listtype='', $size=10)
Output an HTML list bullet or ordered item symbol.
Definition: tcpdf.php:20343
getCSSBorderStyle($cssborder)
Returns the border style array from CSS border properties.
Definition: tcpdf.php:15899
Close()
Terminates the PDF document.
Definition: tcpdf.php:2951
startPageGroup($page='')
Create a new page group.
Definition: tcpdf.php:13560
setSRGBmode($mode=false)
Set flag to force sRGB_IEC61966-2.1 black scaled ICC color profile for the whole document.
Definition: tcpdf.php:2845
$annotation_fonts
List of fonts used on form fields (fontname => fontkey).
Definition: tcpdf.php:1373
$tsa_data
Timestamping data.
Definition: tcpdf.php:1296
$linestyleJoin
PDF string for join value of the last line.
Definition: tcpdf.php:1022
getNumberOfColumns()
Return the current number of columns.
Definition: tcpdf.php:21835
rollbackTransaction($self=false)
This method allows to undo the latest transaction by returning the latest saved TCPDF object with sta...
Definition: tcpdf.php:21663
Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M')
Prints a cell (rectangular area) with optional borders, background color and character string.
Definition: tcpdf.php:4994
deletePage($page)
Remove the specified page.
Definition: tcpdf.php:21022
segSVGContentHandler($parser, $data)
Sets the character data handler function for the XML parser.
Definition: tcpdf.php:24491
$objcopy
Cloned copy of the current class object.
Definition: tcpdf.php:1226
SetCellPadding($pad)
Set the same internal Cell padding for top, right, bottom, left-.
Definition: tcpdf.php:2627
$footer_text_color
Color for footer text (RGB array).
Definition: tcpdf.php:676
hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8)
Returns an array of chars containing soft hyphens.
Definition: tcpdf.php:21962
$imagekeys
Store the image keys.
Definition: tcpdf.php:1177
printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false)
Print an XObject Template.
Definition: tcpdf.php:22334
getAliasNbPages()
Returns the string alias used for the total number of pages.
Definition: tcpdf.php:13610
movePage($frompage, $topage)
Move a page to a previous position.
Definition: tcpdf.php:20851
SetRightMargin($margin)
Defines the right margin.
Definition: tcpdf.php:2613
getImageScale()
Returns the adjusting factor to convert pixels to user units.
Definition: tcpdf.php:2472
SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='')
Defines the color used for all drawing operations (lines, rectangles and cell borders).
Definition: tcpdf.php:3982
$pagegroups
Array that contains the number of pages in each page group.
Definition: tcpdf.php:896
Output($name='doc.pdf', $dest='I')
Send the document to a given destination: string, local file or browser.
Definition: tcpdf.php:7558
SetLineStyle($style, $ret=false)
Set line style.
Definition: tcpdf.php:11322
_putjavascript()
Create a javascript PDF string.
Definition: tcpdf.php:12522
$svgdefs
Array of SVG defs.
Definition: tcpdf.php:1631
_putresources()
Output Resources.
Definition: tcpdf.php:9428
$font_spacing
Increases or decreases the space between characters in a text by the specified amount (tracking).
Definition: tcpdf.php:1547
getFooterFont()
Get Footer font.
Definition: tcpdf.php:10332
$PDFVersion
PDF version.
Definition: tcpdf.php:557
sortBookmarks()
Sort bookmarks for page and key.
Definition: tcpdf.php:12349
GetX()
Returns the relative X value of current position.
Definition: tcpdf.php:7401
$lasth
Height of last cell printed.
Definition: tcpdf.php:306
$font_stretching
Percentage of character stretching.
Definition: tcpdf.php:1540
_putAPXObject($w=0, $h=0, $stream='')
Put appearance streams XObject used to define annotation's appearance states.
Definition: tcpdf.php:8744
_putbookmarks()
Create a bookmark PDF string.
Definition: tcpdf.php:12367
_getannotsrefs($n)
Get references to page annotations.
Definition: tcpdf.php:8057
_dounderlinew($x, $y, $w)
Underline for rectangular text area.
Definition: tcpdf.php:10104
PageNoFormatted()
Returns the current page number formatted as a string.
Definition: tcpdf.php:13688
SetAutoPageBreak($auto, $margin=0)
Enables or disables the automatic page breaking mode.
Definition: tcpdf.php:2790
$rMargin
Right margin.
Definition: tcpdf.php:250
$textrendermode
Text rendering mode: 0 = Fill text; 1 = Stroke text; 2 = Fill, then stroke text; 3 = Neither fill nor...
Definition: tcpdf.php:1464
isCharDefined($char, $font='', $style='')
Return true in the character is present in the specified font.
Definition: tcpdf.php:4608
Translate($t_x, $t_y)
Translate graphic object horizontally and vertically.
Definition: tcpdf.php:11137
setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false)
Set alpha for stroking (CA) and non-stroking (ca) operations.
Definition: tcpdf.php:13909
$signature_appearance
Data for digital signature appearance.
Definition: tcpdf.php:1275
$footerlen
Array used to store footer length of each page.
Definition: tcpdf.php:987
$force_srgb
If true force sRGB color profile for all document.
Definition: tcpdf.php:1751
_UEvalue()
Compute UE value (used for encryption)
Definition: tcpdf.php:10643
setColumnsArray($columns)
Set columns array.
Definition: tcpdf.php:21732
$header_string
String to pring on page header after title.
Definition: tcpdf.php:655
write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='')
Print a Linear Barcode.
Definition: tcpdf.php:15197
setOverprint($stroking=true, $nonstroking='', $mode=0)
Set overprint mode for stroking (OP) and non-stroking (op) painting operations.
Definition: tcpdf.php:13870
registrationMark($x, $y, $r, $double=false, $cola=array(100, 100, 100, 100, 'All'), $colb=array(0, 0, 0, 0, 'None'))
Paints a registration mark.
Definition: tcpdf.php:14239
$fhPt
Height of page format in points.
Definition: tcpdf.php:214
getDestination()
Return the Named Destination array.
Definition: tcpdf.php:12249
PolyLine($p, $style='', $line_style=array(), $fill_color=array())
Draws a polygonal line.
Definition: tcpdf.php:11842
MirrorH($x='')
Horizontal Mirroring.
Definition: tcpdf.php:11066
$rtl
Boolean flag to indicate if the document language is Right-To-Left.
Definition: tcpdf.php:785
addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='')
Add an empty digital signature appearance (a cliccable rectangle area to get signature properties)
Definition: tcpdf.php:13467
getPageBuffer($page)
Get page buffer content.
Definition: tcpdf.php:20742
removeTagNamespace($name)
Return the tag name without the namespace.
Definition: tcpdf.php:23699
_newobj()
Begin a new object and return the object number.
Definition: tcpdf.php:10062
startSVGElementHandler($parser, $name, $attribs, $ctm=array())
Sets the opening SVG element handler function for the XML parser.
Definition: tcpdf.php:23717
addExtGState($parms)
Add transparency parameters to the current extgstate.
Definition: tcpdf.php:13797
selectColumn($col='')
Set position at a given column.
Definition: tcpdf.php:21746
getCSSFontSpacing($spacing, $parent=0)
Returns the letter-spacing value from CSS value.
Definition: tcpdf.php:16092
$pagedim
Page dimensions.
Definition: tcpdf.php:196
$newline
Boolean flag to indicate if a new line is created.
Definition: tcpdf.php:994
getPageWidth($pagenum='')
Returns the page width in units.
Definition: tcpdf.php:2501
$FontFiles
Array of font files.
Definition: tcpdf.php:330
AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false)
Adds a new page to the document.
Definition: tcpdf.php:3102
setTableHeader()
This method is used to render the table header on new page (if any).
Definition: tcpdf.php:3622
closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0)
Process closing tags.
Definition: tcpdf.php:19359
$lMargin
Left margin.
Definition: tcpdf.php:244
$svgunit
Deafult unit of measure for SVG.
Definition: tcpdf.php:1603
$linethrough
line through state
Definition: tcpdf.php:868
replaceMissingChars($text, $font='', $style='', $subs=array())
Replace missing font characters on selected font with specified substitutions.
Definition: tcpdf.php:4635
Header()
This method is used to render the page header.
Definition: tcpdf.php:3392
$underline
Underlining flag.
Definition: tcpdf.php:392
_enddoc()
Output end of document (EOF).
Definition: tcpdf.php:9918
getRemainingWidth()
Returns the remaining width between the current position and margins.
Definition: tcpdf.php:6703
ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false)
Embedd a Scalable Vector Graphics (SVG) image.
Definition: tcpdf.php:22687
getCSSMargin($cssmargin, $width=0)
Get the internal Cell margin from CSS attribute.
Definition: tcpdf.php:16004
setRasterizeVectorImages($mode)
Enable/disable rasterization of vector images using ImageMagick library.
Definition: tcpdf.php:22106
$svgclipmode
Boolean value true when in SVG clipPath tag.
Definition: tcpdf.php:1638
_putXMP()
Put XMP data object and return ID.
Definition: tcpdf.php:9512
adjustCellPadding($brd=0)
Adjust the internal Cell padding array to take account of the line width.
Definition: tcpdf.php:2715
getHashForTCPDFtagParams($data)
Return an hash code used to ensure that the serialized data has been generated by this TCPDF instance...
Definition: tcpdf.php:17051
$default_table_columns
Default number of columns for html table.
Definition: tcpdf.php:696
Open()
This method begins the generation of the PDF document.
Definition: tcpdf.php:2939
getHtmlDomArray($html)
Returns the HTML DOM array.
Definition: tcpdf.php:16249
$LineWidth
Line width in user unit.
Definition: tcpdf.php:312
$efnames
Embedded Files Names.
Definition: tcpdf.php:1589
Scale($s_x, $s_y, $x='', $y='')
Vertical and horizontal non-proportional Scaling.
Definition: tcpdf.php:11033
__destruct()
Default destructor.
Definition: tcpdf.php:1996
_putcidfont0($font)
Output CID-0 fonts.
Definition: tcpdf.php:9030
_putextgstates()
Put extgstates for object transparency.
Definition: tcpdf.php:13841
$header_logo_width
Width of header image logo in user units.
Definition: tcpdf.php:643
GetAbsX()
Returns the absolute X value of current position.
Definition: tcpdf.php:7417
$tempfontsize
Temporary font size in points.
Definition: tcpdf.php:758
$jpeg_quality
Set the default JPEG compression quality (1-100).
Definition: tcpdf.php:917
$htmlLinkFontStyle
Default font style to add to html links.
Definition: tcpdf.php:1149
getDocModificationTimestamp()
Returns document modification timestamp in seconds.
Definition: tcpdf.php:10218
$js_objects
Javascript objects array.
Definition: tcpdf.php:1345
_outCurveV($x2, $y2, $x3, $y3)
Append a cubic Bezier curve to the current path.
Definition: tcpdf.php:11448
RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array())
Draws a rounded rectangle.
Definition: tcpdf.php:12052
getBorderStartPosition()
Return the starting coordinates to draw an html border.
Definition: tcpdf.php:19932
$CurrentFont
Current font info.
Definition: tcpdf.php:404
SetCompression($compress=true)
Activates or deactivates page compression.
Definition: tcpdf.php:2831
resetHeaderTemplate()
Reset the xobject template used by Header() method.
Definition: tcpdf.php:3374
$buffer
Buffer holding in-memory PDF.
Definition: tcpdf.php:166
getSpaceString()
Returns the string used to find spaces.
Definition: tcpdf.php:17037
$svgdefsmode
Boolean value true when in SVG defs group.
Definition: tcpdf.php:1624
$font_obj_ids
Store the font object IDs.
Definition: tcpdf.php:1205
$fontkeys
Store the font keys.
Definition: tcpdf.php:1198
$FontAscent
Current font ascent (distance between font top and baseline).
Definition: tcpdf.php:379
getPageDimensions($pagenum='')
Returns an array of page dimensions:
Definition: tcpdf.php:2485
ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='')
Extract info from a PNG image with alpha channel using the Imagick or GD library.
Definition: tcpdf.php:7248
$title
Document title.
Definition: tcpdf.php:488
$footer_font
Default font used on page footer.
Definition: tcpdf.php:607
$default_graphic_vars
Array of default graphic settings.
Definition: tcpdf.php:1512
$FontFamily
Current font family.
Definition: tcpdf.php:366
$endlinex
End position of the latest inserted line.
Definition: tcpdf.php:1001
Transform($tm)
Apply graphic transformations.
Definition: tcpdf.php:11247
getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false)
Convert HTML string containing value and unit of measure to user's units or points.
Definition: tcpdf.php:20259
SkewY($angle_y, $x='', $y='')
Skew vertically.
Definition: tcpdf.php:11202
_putpages()
Output pages (and replace page number aliases).
Definition: tcpdf.php:7895
Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='')
This method prints text from the current position.
Definition: tcpdf.php:6272
$listindent
HTML PARSER: indent amount for lists.
Definition: tcpdf.php:740
getFontStretching()
Get the percentage of character stretching.
Definition: tcpdf.php:22478
getImageRBX()
Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image.
Definition: tcpdf.php:3357
$n_js
Javascript counter.
Definition: tcpdf.php:861
setImageBuffer($image, $data)
Set image buffer content.
Definition: tcpdf.php:20757
$FillColor
Commands for filling color.
Definition: tcpdf.php:428
$PageMode
A name object specifying how the document should be displayed when opened.
Definition: tcpdf.php:938
$svgclipid
ID of last SVG clipPath.
Definition: tcpdf.php:1659
$n
Current object number.
Definition: tcpdf.php:148
$javascript
Javascript code.
Definition: tcpdf.php:854
setTempRTL($mode)
Force temporary RTL language direction.
Definition: tcpdf.php:2374
startLayer($name='', $print=true, $view=true, $lock=true)
Start a new pdf layer.
Definition: tcpdf.php:13725
$cell_margin
Array of cell margins ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
Definition: tcpdf.php:288
SetFontSize($size, $out=true)
Defines the size of the current font.
Definition: tcpdf.php:4455
getCellHeightRatio()
return the height of cell repect font height.
Definition: tcpdf.php:13983
$cache_file_length
Array used to store the lengths of cache files.
Definition: tcpdf.php:1233
$FontStyle
Current font style.
Definition: tcpdf.php:372
getHeaderData()
Returns header data:
Definition: tcpdf.php:3284
$empty_signature_appearance
Array of empty digital signature appearances.
Definition: tcpdf.php:1282
$header_text_color
Color for header text (RGB array).
Definition: tcpdf.php:662
Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false, $hidden=false, $fitonpage=false, $alt=false, $altimgs=array())
Puts an image in the page.
Definition: tcpdf.php:6828
$footer_line_color
Color for footer line (RGB array).
Definition: tcpdf.php:683
setOpenCell($isopen)
Set the top/bottom cell sides to be open or closed when the cell cross the page.
Definition: tcpdf.php:20233
setHtmlVSpace($tagvs)
Set the vertical spaces for HTML tags.
Definition: tcpdf.php:20213
TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false)
Creates a text field.
Definition: tcpdf.php:12645
$linestyleWidth
PDF string for width value of the last line.
Definition: tcpdf.php:1008
ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false)
Creates a List-box field.
Definition: tcpdf.php:12869
$linestyleDash
PDF string for dash value of the last line.
Definition: tcpdf.php:1029
_getxobjectdict()
Return XObjects Dictionary.
Definition: tcpdf.php:9352
_putinfo()
Adds some Metadata information (Document Information Dictionary) (see Chapter 14.3....
Definition: tcpdf.php:9451
_fixAES256Password($password)
Convert password for AES-256 encryption mode.
Definition: tcpdf.php:10706
$diffs
Array of encoding differences.
Definition: tcpdf.php:336
getAutoPageBreak()
Return the auto-page-break mode (true or false).
Definition: tcpdf.php:2802
addPageRegion($region)
Add a single no-write region on selected page.
Definition: tcpdf.php:22547
AddLink()
Creates a new internal link and returns its identifier.
Definition: tcpdf.php:4683
$keywords
Document keywords.
Definition: tcpdf.php:506
$cell_padding
Array of cell internal paddings ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
Definition: tcpdf.php:281
$extgstates
Array of transparency objects and parameters.
Definition: tcpdf.php:910
$dests
A dictionary of names and corresponding destinations (Dests key on document Catalog).
Definition: tcpdf.php:1575
ScaleXY($s, $x='', $y='')
Vertical and horizontal proportional Scaling.
Definition: tcpdf.php:11019
$original_rMargin
Original right margin value.
Definition: tcpdf.php:595
setDefaultTableColumns($cols=4)
Set the default number of columns in a row for HTML tables.
Definition: tcpdf.php:13964
SetFillSpotColor($name, $tint=100)
Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
Definition: tcpdf.php:3790
$form_enctype
Current form encryption type (used during XHTML rendering).
Definition: tcpdf.php:1359
_putcatalog()
Output Catalog.
Definition: tcpdf.php:9650
$hPt
Current height of page in points.
Definition: tcpdf.php:226
getCSSBorderWidth($width)
Returns the border width from CSS property.
Definition: tcpdf.php:15843
startTransaction()
Stores a copy of the current TCPDF object used for undo operation.
Definition: tcpdf.php:21632
endPage($tocpage=false)
Terminate the current page.
Definition: tcpdf.php:3126
$creator
Document creator.
Definition: tcpdf.php:512
$w
Current width of page in user unit.
Definition: tcpdf.php:232
setBuffer($data)
Set buffer content (always append data).
Definition: tcpdf.php:20688
$openMarkedContent
Boolean flag to indicate if marked-content sequence is open.
Definition: tcpdf.php:1036
$l
Language templates.
Definition: tcpdf.php:613
$header_title
Title to be printed on default page header.
Definition: tcpdf.php:649
setFooter()
This method is used to render the page footer.
Definition: tcpdf.php:3557
setUserRights( $enable=true, $document='/FullSave', $annots='/Create/Delete/Modify/Copy/Import/Export', $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate', $signature='/Modify', $ef='/Create/Delete/Modify/Import', $formex='')
Set User's Rights for PDF Reader WARNING: This is experimental and currently do not work.
Definition: tcpdf.php:13377
_getrawstream($s, $n=0)
get raw output stream.
Definition: tcpdf.php:10260
getCSSBorderDashStyle($style)
Returns the border dash style from CSS property.
Definition: tcpdf.php:15863
$intmrk
Array used to store positions inside the pages buffer (keys are the page numbers).
Definition: tcpdf.php:952
setRTL($enable, $resetx=true)
Enable or disable Right-To-Left language mode.
Definition: tcpdf.php:2348
getColumn()
Return the current column number.
Definition: tcpdf.php:21825
_datestring($n=0, $timestamp=0)
Returns a formatted date for meta information.
Definition: tcpdf.php:10230
$premode
Boolean flag to indicate if we are inside a PRE tag.
Definition: tcpdf.php:1127
Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2)
Draws a circle.
Definition: tcpdf.php:11824
SetProtection($permissions=array('print', 'modify', 'copy', 'annot-forms', 'fill-forms', 'extract', 'assemble', 'print-high'), $user_pass='', $owner_pass=null, $mode=0, $pubkeys=null)
Set document protection Remark: the protection against modification is for people who have the full A...
Definition: tcpdf.php:10839
startTemplate($w=0, $h=0, $group=false)
Start a new XObject Template.
Definition: tcpdf.php:22240
getCSSBorderMargin($cssbspace, $width=0)
Get the border-spacing from CSS attribute.
Definition: tcpdf.php:16058
getFooterMargin()
Returns footer margin in user units.
Definition: tcpdf.php:3331
$FontDescent
Current font descent (distance between font bottom and baseline).
Definition: tcpdf.php:386
setImageSubBuffer($image, $key, $data)
Set image buffer content for a specified sub-key.
Definition: tcpdf.php:20775
cropMark($x, $y, $w, $h, $type='T, R, B, L', $color=array(100, 100, 100, 100, 'All'))
Paints crop marks.
Definition: tcpdf.php:14168
$header_line_color
Color for header line (RGB array).
Definition: tcpdf.php:669
_dooverline($x, $y, $txt)
Overline text.
Definition: tcpdf.php:10142
GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false)
Returns the length of a string in user unit.
Definition: tcpdf.php:4032
setCellMargins($left='', $top='', $right='', $bottom='')
Set the internal Cell margins.
Definition: tcpdf.php:2682
isUnicodeFont()
Return true if the current font is unicode type.
Definition: tcpdf.php:22184
$tmprtl
Boolean flag used to force RTL or LTR string direction.
Definition: tcpdf.php:792
getBreakMargin($pagenum='')
Returns the page break margin.
Definition: tcpdf.php:2533
setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0, 0, 0), $lc=array(0, 0, 0))
Set header data.
Definition: tcpdf.php:3257
setFooterMargin($fm=10)
Set footer margin.
Definition: tcpdf.php:3321
$tcpdflink
If true print TCPDF meta link.
Definition: tcpdf.php:1809
SetAbsY($y)
Set the absolute Y coordinate of the current pointer.
Definition: tcpdf.php:7530
writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true)
Definition: tcpdf.php:17103
setPageBoxTypes($boxes)
Set page boxes to be included on page descriptions.
Definition: tcpdf.php:7882
SetFillColorArray($color, $ret=false)
Defines the color used for all filling operations (filled rectangles and cell backgrounds).
Definition: tcpdf.php:3862
$columns
Array of column measures (width, space, starting Y position).
Definition: tcpdf.php:1422
_objectkey($n)
Compute encryption key depending on object number where the encrypted data is stored.
Definition: tcpdf.php:10442
setHtmlLinksStyle($color=array(0, 0, 255), $fontstyle='U')
Set the color and font style for HTML links.
Definition: tcpdf.php:20244
_dounderline($x, $y, $txt)
Underline text.
Definition: tcpdf.php:10091
drawHTMLTagBorder($tag, $xmax)
Draw an HTML block border and fill.
Definition: tcpdf.php:19948
addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false)
Add vertical spaces if needed.
Definition: tcpdf.php:19906
Line($x1, $y1, $x2, $y2, $style=array())
Draws a line between two points.
Definition: tcpdf.php:11481
$y
Current vertical position in user unit for cell positioning.
Definition: tcpdf.php:300
_dolinethrough($x, $y, $txt)
Line through text.
Definition: tcpdf.php:10116
RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false)
Creates a RadioButton field.
Definition: tcpdf.php:12766
setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0, 0, 0), $x=-1, $link='')
Adds a bookmark - alias for Bookmark().
Definition: tcpdf.php:12285
$ur
Array with additional document-wide usage rights for the document.
Definition: tcpdf.php:875
setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='')
Defines the color used by the specified type ('draw', 'fill', 'text').
Definition: tcpdf.php:3892
getPageHeight($pagenum='')
Returns the page height in units.
Definition: tcpdf.php:2517
getFontSizePt()
Returns the current font size in points unit.
Definition: tcpdf.php:15796
$last_enc_key
Last RC4 key encrypted (cached for optimisation).
Definition: tcpdf.php:815
SetX($x, $rtloff=false)
Defines the abscissa of the current position.
Definition: tcpdf.php:7441
setPrintFooter($val=true)
Set a flag to print page footer.
Definition: tcpdf.php:3348
$opencell
Boolean flag to indicate if the border of the cell sides that cross the page should be removed.
Definition: tcpdf.php:1113
getAllSpotColors()
Returns the array of spot colors.
Definition: tcpdf.php:3699
SetDrawColorArray($color, $ret=false)
Defines the color used for all drawing operations (lines, rectangles and cell borders).
Definition: tcpdf.php:3848
registrationMarkCMYK($x, $y, $r)
Paints a CMYK registration mark.
Definition: tcpdf.php:14266
commitTransaction()
Delete the copy of the current TCPDF object used for undo operation.
Definition: tcpdf.php:21649
hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8)
Returns text with soft hyphens.
Definition: tcpdf.php:22042
Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array())
Draws a rectangle.
Definition: tcpdf.php:11511
$embeddedfiles
Array of files to embedd.
Definition: tcpdf.php:1120
setCellHeightRatio($h)
Set the height of the cell (line height) respect the font height.
Definition: tcpdf.php:13974
$original_lMargin
Original left margin value.
Definition: tcpdf.php:588
_outCurve($x1, $y1, $x2, $y2, $x3, $y3)
Append a cubic Bezier curve to the current path.
Definition: tcpdf.php:11432
getFontSpacing()
Get the amount to increase or decrease the space between characters in a text.
Definition: tcpdf.php:22500
$lispacer
Spacer string for LI tags.
Definition: tcpdf.php:764
CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false)
Creates a CheckBox field.
Definition: tcpdf.php:13042
setPrintHeader($val=true)
Set a flag to print page header.
Definition: tcpdf.php:3339
$listnum
HTML PARSER: current list nesting level.
Definition: tcpdf.php:734
setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='')
Enable document timestamping (requires the OpenSSL Library).
Definition: tcpdf.php:13517
setSpacesRE($re='/[^\S\xa0]/')
Set regular expression to detect withespaces or word separators.
Definition: tcpdf.php:2323
Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false)
Creates a button field.
Definition: tcpdf.php:13122
$print_header
Boolean flag to print/hide page header.
Definition: tcpdf.php:625
setDestination($name, $y=-1, $page='', $x=-1)
Add a Named Destination.
Definition: tcpdf.php:12206
$svgdir
Directory used for the last SVG image.
Definition: tcpdf.php:1596
setHeaderTemplateAutoreset($val=true)
Set a flag to automatically reset the xobject template used by Header() method at each page.
Definition: tcpdf.php:3383
setExtraXMP($xmp)
Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag.
Definition: tcpdf.php:9502
getNumPages()
Get the total number of insered pages.
Definition: tcpdf.php:3064
$htmlvspace
Count the latest inserted vertical spaces on HTML.
Definition: tcpdf.php:1043
replaceChar($oldchar, $newchar)
Replace a char if is defined on the current font.
Definition: tcpdf.php:5531
Skew($angle_x, $angle_y, $x='', $y='')
Skew.
Definition: tcpdf.php:11216
SetAuthor($author)
Defines the author of the document.
Definition: tcpdf.php:2889
$fgcolor
Current foreground color.
Definition: tcpdf.php:716
setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=> 'Normal'))
Set parameters for drop shadow effect for text.
Definition: tcpdf.php:21902
setPage($pnum, $resetmargins=false)
Move pointer at the specified document page and update page dimensions.
Definition: tcpdf.php:2995
endLayer()
End the current PDF layer.
Definition: tcpdf.php:13745
setPageRegions($regions=array())
Set no-write regions on page.
Definition: tcpdf.php:22527
pixelsToUnits($px)
Converts pixels to User's Units.
Definition: tcpdf.php:10416
$FontSizePt
Current font size in points.
Definition: tcpdf.php:410
$header_xobjid
ID of the stored default header template (-1 = not set).
Definition: tcpdf.php:563
PageNo()
Returns the current page number.
Definition: tcpdf.php:3689
Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=> 'Text'), $spaces=0)
Puts a markup annotation on a rectangular area of the page.
Definition: tcpdf.php:4745
$start_transaction_page
Store page number when startTransaction() is called.
Definition: tcpdf.php:1401
$PageAnnots
Array of Annotations in pages.
Definition: tcpdf.php:354
_OEvalue()
Compute OE value (used for encryption)
Definition: tcpdf.php:10693
$customlistindent
HTML PARSER: custom indent amount for lists.
Definition: tcpdf.php:1106
_putannotsobjs()
Output annotations objects for all pages.
Definition: tcpdf.php:8101
GetLineWidth()
Returns the current the line width.
Definition: tcpdf.php:11295
_putheader()
Output PDF File Header (7.5.2).
Definition: tcpdf.php:9909
setHeader()
This method is used to render the page header.
Definition: tcpdf.php:3516
$compress
Compression flag.
Definition: tcpdf.php:184
checkPageBreak($h=0, $y='', $addpage=true)
Add page if needed.
Definition: tcpdf.php:4940
$footerpos
Array used to store footer positions of each page.
Definition: tcpdf.php:980
$emptypagemrk
Array used to store page positions to track empty pages (keys are the page numbers).
Definition: tcpdf.php:966
$barcode
Barcode to print on page footer (only if set).
Definition: tcpdf.php:619
getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M')
Returns the PDF string code to print a cell (rectangular area) with optional borders,...
Definition: tcpdf.php:5063
StartTransform()
Starts a 2D tranformation saving current graphic state.
Definition: tcpdf.php:10945
setColorArray($type, $color, $ret=false)
Set the color array for the specified type ('draw', 'fill', 'text').
Definition: tcpdf.php:3817
$check_page_regions
Boolean value true when page region check is active.
Definition: tcpdf.php:1561
_encrypt_data($n, $s)
Encrypt the input string.
Definition: tcpdf.php:10462
setTextRenderingMode($stroke=0, $fill=true, $clip=false)
Set Text rendering mode.
Definition: tcpdf.php:21847
stringRightTrim($str, $replace='')
Right trim the input string.
Definition: tcpdf.php:22158
AcceptPageBreak()
Whenever a page break condition is met, the method is called, and the break is issued or not dependin...
Definition: tcpdf.php:4913
$strokecolor
Current stroke color.
Definition: tcpdf.php:1478
$header_font
Default font used on page header.
Definition: tcpdf.php:601
$tocpage
Boolean flag true when we are on TOC (Table Of Content) page.
Definition: tcpdf.php:1491
_outSaveGraphicsState()
Outputs the "save graphics state" operator 'q'.
Definition: tcpdf.php:20670
setDocCreationTimestamp($time)
Set the document creation timestamp.
Definition: tcpdf.php:10182
_outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false)
Append an elliptical arc to the current path.
Definition: tcpdf.php:11693
setPageFormat($format, $orientation='P')
Change the format of the current page.
Definition: tcpdf.php:2103
getBarcode()
Get current barcode.
Definition: tcpdf.php:15163
MirrorP($x='', $y='')
Point reflection mirroring.
Definition: tcpdf.php:11089
$FontSize
Current font size in user unit.
Definition: tcpdf.php:416
_Uvalue()
Compute U value (used for encryption)
Definition: tcpdf.php:10610
StarPolygon($x0, $y0, $r, $nv, $ng, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array())
Draws a star polygon.
Definition: tcpdf.php:12010
SetDocInfoUnicode($unicode=true)
Turn on/off Unicode mode for document information dictionary (meta tags).
Definition: tcpdf.php:2856
getPage()
Get current document page number.
Definition: tcpdf.php:3053
$radio_groups
List of radio group objects IDs.
Definition: tcpdf.php:1387
$starting_page_number
Starting page number.
Definition: tcpdf.php:518
MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $valign='T', $fitcell=false)
This method allows printing text with line breaks.
Definition: tcpdf.php:5757
TranslateY($t_y)
Translate graphic object vertically.
Definition: tcpdf.php:11125
setFontSpacing($spacing=0)
Set the amount to increase or decrease the space between characters in a text.
Definition: tcpdf.php:22489
setFontBuffer($font, $data)
Set font buffer content.
Definition: tcpdf.php:20803
$last_enc_key_c
Last RC4 computed key.
Definition: tcpdf.php:822
getTextShadow()
Return the text shadow parameters array.
Definition: tcpdf.php:21944
$current_column
Current column number.
Definition: tcpdf.php:1436
$pdfa_mode
If true set the document to PDF/A mode.
Definition: tcpdf.php:1758
$default_form_prop
Deafult Javascript field properties.
Definition: tcpdf.php:1338
_outRect($x, $y, $w, $h, $op)
Append a rectangle to the current path as a complete subpath, with lower-left corner (x,...
Definition: tcpdf.php:11414
setPageUnit($unit)
Set the units of measure for the document.
Definition: tcpdf.php:2011
swapMargins($reverse=true)
Swap the left and right margins.
Definition: tcpdf.php:20189
RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array())
Draws a rounded rectangle.
Definition: tcpdf.php:12071
RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5, 0.5, 0.5, 0.5, 1))
Paints a radial colour gradient.
Definition: tcpdf.php:14328
getScaleFactor()
Returns the scale factor (number of points in user unit).
Definition: tcpdf.php:2547
Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array())
Draws a poly-Bezier curve.
Definition: tcpdf.php:11612
$pdflayers
Array of PDF layers data.
Definition: tcpdf.php:1568
$links
Array of internal links.
Definition: tcpdf.php:360
_outPoint($x, $y)
Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line s...
Definition: tcpdf.php:11384
setVisibility($v)
Set the visibility of the successive elements.
Definition: tcpdf.php:13764
SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='')
Defines the color used for text.
Definition: tcpdf.php:4016
$bufferlen
Length of the buffer in bytes.
Definition: tcpdf.php:1184
$form_obj_id
List of form annotations IDs.
Definition: tcpdf.php:1331
SetMargins($left, $top, $right=-1, $keepmargins=false)
Defines the left, top and right margins.
Definition: tcpdf.php:2561
$HREF
HTML PARSER: array to store current link and rendering styles.
Definition: tcpdf.php:704
$xobjects
Array of XObjects.
Definition: tcpdf.php:1519
Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2)
Draws an ellipse.
Definition: tcpdf.php:11652
Gradient($type, $coords, $stops, $background=array(), $antialias=false)
Output gradient.
Definition: tcpdf.php:14470
$gradients
Array for storing gradient information.
Definition: tcpdf.php:945
$wPt
Current width of page in points.
Definition: tcpdf.php:220
$docinfounicode
If true set the document information dictionary in Unicode.
Definition: tcpdf.php:482
_putencryption()
Put encryption on PDF document.
Definition: tcpdf.php:10490
$header_xobj_autoreset
If true reset the Header Xobject template at each page.
Definition: tcpdf.php:569
_puttruetypeunicode($font)
Adds unicode fonts.
Definition: tcpdf.php:8926
SVGTransform($tm)
Apply SVG graphic transformation matrix.
Definition: tcpdf.php:23028
Link($x, $y, $w, $h, $link, $spaces=0)
Puts a link on a rectangular area of the page.
Definition: tcpdf.php:4728
$InHeader
Flag set when processing page header.
Definition: tcpdf.php:458
$transfmatrix
Array of transformation matrix.
Definition: tcpdf.php:1071
setCellPaddings($left='', $top='', $right='', $bottom='')
Set the internal Cell paddings.
Definition: tcpdf.php:2646
SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='')
Defines the color used for all filling operations (filled rectangles and cell backgrounds).
Definition: tcpdf.php:3999
getGroupPageNo()
Return the current page in the group.
Definition: tcpdf.php:13668
SkewX($angle_x, $x='', $y='')
Skew horizontally.
Definition: tcpdf.php:11189
$subject
Document subject.
Definition: tcpdf.php:494
$pages
Array containing pages.
Definition: tcpdf.php:172
setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array())
Apply the requested SVG styles (*** TO BE COMPLETED ***)
Definition: tcpdf.php:23047
replaceBuffer($data)
Replace the buffer content.
Definition: tcpdf.php:20699
$img_rb_y
The right-bottom corner Y coordinate of last inserted image.
Definition: tcpdf.php:534
_putdests()
Insert Named Destinations.
Definition: tcpdf.php:12259
getCharBBox($char)
Returns the glyph bounding box of the specified character in the current font in user units.
Definition: tcpdf.php:4544
$pageobjects
Array of object IDs for each page.
Definition: tcpdf.php:160
stringTrim($str, $replace='')
Trim the input string.
Definition: tcpdf.php:22171
$header_logo
Header image logo.
Definition: tcpdf.php:637
$CurOrientation
Current page orientation (P = Portrait, L = Landscape).
Definition: tcpdf.php:190
$xobjid
Current XObject ID.
Definition: tcpdf.php:1533
_Ovalue()
Compute O value (used for encryption)
Definition: tcpdf.php:10655
$numfonts
Counts the number of fonts.
Definition: tcpdf.php:1191
Text($x, $y, $txt, $fstroke=false, $fclip=false, $ffill=true, $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M', $rtloff=false)
Prints a text cell at the specified position.
Definition: tcpdf.php:4893
PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90)
Draw the sector of a circle.
Definition: tcpdf.php:14769
$start_transaction_y
Store Y position when startTransaction() is called.
Definition: tcpdf.php:1408
$booklet
Booklet mode for double-sided pages.
Definition: tcpdf.php:1085
$radiobutton_groups
List of radio buttons parent objects.
Definition: tcpdf.php:1380
$tsa_timestamp
Boolean flag to enable document timestamping with TSA.
Definition: tcpdf.php:1289
$n_dests
Object ID for Named Destinations.
Definition: tcpdf.php:1582
writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='')
Allows to preserve some HTML formatting (limited support).
Definition: tcpdf.php:17120
SetTitle($title)
Defines the title of the document.
Definition: tcpdf.php:2867
_putshaders()
Output gradient shaders.
Definition: tcpdf.php:14577
_out($s)
Output a string to the document.
Definition: tcpdf.php:10273
setFontSubBuffer($font, $key, $data)
Set font buffer content.
Definition: tcpdf.php:20822
getFontBuffer($font)
Get font buffer content.
Definition: tcpdf.php:20836
$svg_tag_depth
Depth of the svg tag, to keep track if the svg tag is a subtag or the root tag.
Definition: tcpdf.php:348
_textstring($s, $n=0)
Format a text string for meta information.
Definition: tcpdf.php:10244
$currpagegroup
Current page group number.
Definition: tcpdf.php:903
getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt')
Convert HTML string containing font size value to points.
Definition: tcpdf.php:16195
_putxobjects()
Output Form XObjects Templates.
Definition: tcpdf.php:9229
$signature_max_length
Digital signature max length.
Definition: tcpdf.php:1268
getGroupPageNoFormatted()
Returns the current group page number formatted as a string.
Definition: tcpdf.php:13678
getDocCreationTimestamp()
Returns document creation timestamp in seconds.
Definition: tcpdf.php:10208
addJavascriptObject($script, $onload=false)
Adds a javascript object and return object ID.
Definition: tcpdf.php:12506
GetNumChars($s)
Returns the numbero of characters in a string.
Definition: tcpdf.php:4135
getPageRegions()
Return an array of no-write page regions.
Definition: tcpdf.php:22512
getFormDefaultProp()
Return the default properties for form fields.
Definition: tcpdf.php:12627
SetSubject($subject)
Defines the subject of the document.
Definition: tcpdf.php:2878
$gdgammacache
Cache array for computed GD gamma values.
Definition: tcpdf.php:1816
$viewer_preferences
PDF viewer preferences.
Definition: tcpdf.php:931
$fonts
Array of used fonts.
Definition: tcpdf.php:324
isRTLTextDir()
Return the current temporary RTL status.
Definition: tcpdf.php:2406
getFontSize()
Returns the current font size.
Definition: tcpdf.php:15786
getCSSPadding($csspadding, $width=0)
Get the internal Cell padding from CSS attribute.
Definition: tcpdf.php:15950
ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false)
Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
Definition: tcpdf.php:14834
$k
Scale factor (number of points in user unit).
Definition: tcpdf.php:202
SetLink($link, $y=0, $page=-1)
Defines the page and position a link points to.
Definition: tcpdf.php:4699
$tMargin
Top margin.
Definition: tcpdf.php:268
$img_rb_x
The right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image.
Definition: tcpdf.php:526
$textstrokewidth
Text stroke width in doc units.
Definition: tcpdf.php:1471
getFontAscent($font, $style='', $size=0)
Return the font ascent value.
Definition: tcpdf.php:4588
getFontFamily()
Returns the current font family name.
Definition: tcpdf.php:15806
$bMargin
Page break margin.
Definition: tcpdf.php:274
getFontSubsetting()
Return the default option for font subsetting.
Definition: tcpdf.php:22132
getAliasRightShift()
Returns the string alias used right align page numbers.
Definition: tcpdf.php:13584
setViewerPreferences($preferences)
Set the viewer preferences dictionary controlling the way the document is to be presented on the scre...
Definition: tcpdf.php:14011
$cntmrk
Array used to store content positions inside the pages buffer (keys are the page numbers).
Definition: tcpdf.php:973
$font_subsetting
Boolean flag: if true enables font subsetting by default.
Definition: tcpdf.php:1505
MirrorL($angle=0, $x='', $y='')
Reflection against a straight line through point (x, y) with the gradient angle (angle).
Definition: tcpdf.php:11102
$svgstyles
Array of SVG properties.
Definition: tcpdf.php:1680
$AutoPageBreak
Automatic page breaking.
Definition: tcpdf.php:446
$htmlLinkColorArray
Default color for html links.
Definition: tcpdf.php:1142
convertSVGtMatrix($tm)
Convert SVG transformation matrix to PDF.
Definition: tcpdf.php:23008
SetY($y, $resetx=true, $rtloff=false)
Moves the current abscissa back to the left margin and sets the ordinate.
Definition: tcpdf.php:7474
$svgtextmode
SVG text properties.
Definition: tcpdf.php:1673
_putEmbeddedFiles()
Embedd the attached files.
Definition: tcpdf.php:4835
_datastring($s, $n=0)
Format a data string for meta information.
Definition: tcpdf.php:10168
setDocModificationTimestamp($time)
Set the document modification timestamp.
Definition: tcpdf.php:10195
addTOCPage($orientation='', $format='', $keepmargins=false)
Adds a new TOC (Table Of Content) page to the document.
Definition: tcpdf.php:3077
$linestyleCap
PDF string for CAP value of the last line.
Definition: tcpdf.php:1015
$footer_margin
Minimum distance between footer and bottom page margin.
Definition: tcpdf.php:581
setBarcode($bc='')
Set document barcode.
Definition: tcpdf.php:15153
getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0)
This method return the estimated height needed for printing a simple text string using the Multicell(...
Definition: tcpdf.php:6239
setFontStretching($perc=100)
Set the percentage of character stretching.
Definition: tcpdf.php:22467
setPageOrientation($orientation, $autopagebreak='', $bottommargin='')
Set page orientation.
Definition: tcpdf.php:2217
ScaleX($s_x, $x='', $y='')
Horizontal Scaling.
Definition: tcpdf.php:10993
$h
Current height of page in user unit.
Definition: tcpdf.php:238
SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone')
Defines the way the document is to be displayed by the viewer.
Definition: tcpdf.php:2814
Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15)
Draws a grahic arrow.
Definition: tcpdf.php:12141
$rasterize_vector_images
Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick librar...
Definition: tcpdf.php:1498
RegularPolygon($x0, $y0, $r, $ns, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array())
Draws a regular polygon.
Definition: tcpdf.php:11962
$listordered
HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
Definition: tcpdf.php:722
$TextColor
Commands for text color.
Definition: tcpdf.php:434
$textindent
Text indentation value (used for text-indent CSS attribute).
Definition: tcpdf.php:1394
setHeaderMargin($hm=10)
Set header margin.
Definition: tcpdf.php:3301
setExtGState($gs)
Add an extgstate.
Definition: tcpdf.php:13828
resetColumns()
Remove columns and reset page margins.
Definition: tcpdf.php:21719
fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='')
Cleanup HTML code (requires HTML Tidy library).
Definition: tcpdf.php:15832
getAliasNumPage()
Returns the string alias used for the page number.
Definition: tcpdf.php:13625
getCellPaddings()
Get the internal Cell padding array.
Definition: tcpdf.php:2668
GetY()
Returns the ordinate of the current position.
Definition: tcpdf.php:7428
Footer()
This method is used to render the page footer.
Definition: tcpdf.php:3469
$bgcolor
Current background color.
Definition: tcpdf.php:752
$imgscale
Adjusting factor to convert pixels to user units.
Definition: tcpdf.php:542
$overline
Overlining flag.
Definition: tcpdf.php:398
$alpha
Alpha mode array.
Definition: tcpdf.php:1795
unhtmlentities($text_to_convert)
Reverse function for htmlentities.
Definition: tcpdf.php:10427
$bordermrk
Array used to store positions inside the pages buffer (keys are the page numbers).
Definition: tcpdf.php:959
$numpages
Counts the number of pages.
Definition: tcpdf.php:1156
addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0, 0, 0))
Output a Table of Content Index (TOC).
Definition: tcpdf.php:21289
SetTextColorArray($color, $ret=false)
Defines the color used for text.
Definition: tcpdf.php:3875
$maxselcol
Maximum page and column selected.
Definition: tcpdf.php:1450
$thead
Table header content to be repeated on each new page.
Definition: tcpdf.php:1240
getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='')
Get the array that defines the signature appearance (page and rectangle coordinates).
Definition: tcpdf.php:13485
getFontsList()
Fill the list of available fonts ($this->fontlist).
Definition: tcpdf.php:4147
$PageBreakTrigger
Threshold used to trigger page breaks.
Definition: tcpdf.php:452
_destroy($destroyall=false, $preserve_objcopy=false)
Unset all class variables except the following critical variables.
Definition: tcpdf.php:7756
$state
Current document state.
Definition: tcpdf.php:178
SetDefaultMonospacedFont($font)
Defines the default monospaced font.
Definition: tcpdf.php:4672
getMargins()
Returns an array containing current margins:
Definition: tcpdf.php:15745
$num_columns
Number of colums.
Definition: tcpdf.php:1429
setEqualColumns($numcols=0, $width=0, $y='')
Set multiple columns of the same size.
Definition: tcpdf.php:21687
_beginpage($orientation='', $format='')
Initialize a new page.
Definition: tcpdf.php:10009
getCellMargins()
Get the internal Cell margin array.
Definition: tcpdf.php:2704
_generateencryptionkey()
Compute encryption key.
Definition: tcpdf.php:10721
serializeTCPDFtagParameters($data)
Serialize an array of parameters to be used with TCPDF tag in HTML code.
Definition: tcpdf.php:17061
$inxobj
Boolean value true when we are inside an XObject.
Definition: tcpdf.php:1526
$svgtext
SVG text.
Definition: tcpdf.php:1666
setHeaderFont($font)
Set header font.
Definition: tcpdf.php:10302
SetAbsXY($x, $y)
Set the absolute X and Y coordinates of the current pointer.
Definition: tcpdf.php:7542
Rotate($angle, $x='', $y='')
Rotate object.
Definition: tcpdf.php:11159
applyTSA($signature)
NOT YET IMPLEMENTED Request TSA for a timestamp.
Definition: tcpdf.php:13545
$re_space
Array of $re_spaces parts.
Definition: tcpdf.php:1310
_putresourcedict()
Output Resources Dictionary.
Definition: tcpdf.php:9364
getRTL()
Return the RTL status.
Definition: tcpdf.php:2364
_endpage()
Mark end of page.
Definition: tcpdf.php:10052
$transfmatrix_key
Current key for transformation matrix.
Definition: tcpdf.php:1078
SetDrawSpotColor($name, $tint=100)
Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
Definition: tcpdf.php:3778
SetTextSpotColor($name, $tint=100)
Defines the spot color used for text.
Definition: tcpdf.php:3802
setFooterFont($font)
Set footer font.
Definition: tcpdf.php:10322
fitBlock($w, $h, $x, $y, $fitonpage=false)
Set the block dimensions accounting for page breaks and page/column fitting.
Definition: tcpdf.php:6723
addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false)
Output anchor link.
Definition: tcpdf.php:10374
_dolinethroughw($x, $y, $w)
Line through for rectangular text area.
Definition: tcpdf.php:10129
getInternalPageNumberAliases($a='')
Return an array containing variations for the basic page number alias.
Definition: tcpdf.php:7808
getHeaderMargin()
Returns header margin in user units.
Definition: tcpdf.php:3311
$offsets
Array of object offsets.
Definition: tcpdf.php:154
Clip($x, $y, $w, $h)
Set a rectangular clipping area.
Definition: tcpdf.php:14443
$doc_creation_timestamp
Document creation date-time.
Definition: tcpdf.php:1765
setLIsymbol($symbol='!')
Set the default bullet to be used as LI bullet symbol.
Definition: tcpdf.php:20150
replaceRightShiftPageNumAliases($page, $aliases, $diff)
Replace right shift page number aliases with spaces to correct right alignment.
Definition: tcpdf.php:7847
getRawCharWidth($char)
Returns the length of the char in user unit for the current font.
Definition: tcpdf.php:4109
$svggradients
Array of SVG gradients.
Definition: tcpdf.php:1610
setImageScale($scale)
Set the adjusting factor to convert pixels to user units.
Definition: tcpdf.php:2461
SetBooklet($booklet=true, $inner=-1, $outer=-1)
Set the booklet mode for double-sided pages.
Definition: tcpdf.php:20173
getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0)
This method return the estimated number of lines for print a simple text string using Multicell() met...
Definition: tcpdf.php:6120
_outLine($x, $y)
Append a straight line segment from the current point to the point (x, y).
Definition: tcpdf.php:11398
_outRestoreGraphicsState()
Outputs the "restore graphics state" operator 'Q'.
Definition: tcpdf.php:20678
$x
Current horizontal position in user unit for cell positioning.
Definition: tcpdf.php:294
getPageGroupAlias()
Return the alias for the total number of pages in the current page group.
Definition: tcpdf.php:13640
$fontlist
List of available fonts on filesystem.
Definition: tcpdf.php:710
$column_start_page
Starting page for columns.
Definition: tcpdf.php:1443
setSpotColor($type, $name, $tint=100)
Set the spot color for the specified type ('draw', 'fill', 'text').
Definition: tcpdf.php:3732
$cell_height_ratio
Default cell height ratio.
Definition: tcpdf.php:924
getAllInternalPageNumberAliases()
Return an array containing all internal page aliases.
Definition: tcpdf.php:7829
SVGPath($d, $style='')
Draws an SVG path.
Definition: tcpdf.php:23342
$numimages
Counts the number of pages.
Definition: tcpdf.php:1170
$epsmarker
String used to mark the beginning and end of EPS image blocks.
Definition: tcpdf.php:1064
$signature_data
Digital signature data.
Definition: tcpdf.php:1261
CoonsPatchMesh($x, $y, $w, $h, $col1=array(), $col2=array(), $col3=array(), $col4=array(), $coords=array(0.00, 0.0, 0.33, 0.00, 0.67, 0.00, 1.00, 0.00, 1.00, 0.33, 1.00, 0.67, 1.00, 1.00, 0.67, 1.00, 0.33, 1.00, 0.00, 1.00, 0.00, 0.67, 0.00, 0.33), $coords_min=0, $coords_max=1, $antialias=false)
Paints a coons patch mesh.
Definition: tcpdf.php:14351
$newpagegroup
Array of page numbers were a new page group was started (the page numbers are the keys of the array).
Definition: tcpdf.php:889
SetKeywords($keywords)
Associates keywords with the document, generally in the form 'keyword1 keyword2 .....
Definition: tcpdf.php:2900
_outCurveY($x1, $y1, $x3, $y3)
Append a cubic Bezier curve to the current path.
Definition: tcpdf.php:11464
$LayoutMode
Layout display mode.
Definition: tcpdf.php:476
SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true)
Sets the font used to print character strings.
Definition: tcpdf.php:4427
$header_margin
Minimum distance between header and top page margin.
Definition: tcpdf.php:575
write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false)
Print 2D Barcode.
Definition: tcpdf.php:15516
setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='')
Set the digital signature appearance (a cliccable rectangle area to get signature properties)
Definition: tcpdf.php:13451
$outlines
Outlines for bookmark.
Definition: tcpdf.php:838
GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false)
Returns the string length of an array of chars in user unit or an array of characters widths.
Definition: tcpdf.php:4048
getLastH()
Get the last cell height.
Definition: tcpdf.php:2450
getGDgamma($img, $c)
Get the GD-corrected PNG gamma value from alpha color.
Definition: tcpdf.php:7342
$encrypted
IBoolean flag indicating whether document is protected.
Definition: tcpdf.php:801
$as
$action
$key
Definition: croninfo.php:18
for( $i=6;$i< 13;$i++) for($i=1; $i< 13; $i++) $d
Definition: date.php:296
$lc
Definition: date.php:267
$i
Definition: disco.tpl.php:19
$txt
Definition: error.php:11
$html
Definition: example_001.php:87
$y
Definition: example_007.php:83
$x
Definition: example_009.php:98
$imgdata
Definition: example_009.php:81
$h
$w
$style
Definition: example_012.php:70
$yc
$xc
$px
$angle
$js
$border_style
Definition: example_022.php:83
if(@file_exists(dirname(__FILE__).'/lang/eng.php')) $preferences
Definition: example_029.php:70
$coords
Definition: example_030.php:88
$patch_array[0]['f']
$coords_max
$coords_min
$r
Definition: example_031.php:79
$starty
$code
Definition: example_050.php:99
$bMargin
$border
$regions
$barcodeobj
if(!array_key_exists('StateId', $_REQUEST)) $id
e($cmd)
Definition: flush.php:14
$links
if(!is_dir( $entity_dir)) exit("Fatal Error ([A-Za-z0-9]+)\s+" &#(? foreach( $entity_files as $file) $output
if(function_exists( 'posix_getuid') &&posix_getuid()===0) if(!array_key_exists('t', $options)) $tag
Definition: cron.php:35
$time
Definition: cron.php:21
if($format !==null) $name
Definition: metadata.php:146
$format
Definition: metadata.php:141
$end
Definition: saml1-acs.php:18
$info
Definition: index.php:5
$stream
PHP stream implementation.
hash(StreamInterface $stream, $algo, $rawOutput=false)
Calculate a hash of a Stream.
Definition: functions.php:406
$ret
Definition: parser.php:6
defined( 'APPLICATION_ENV')||define( 'APPLICATION_ENV'
Definition: bootstrap.php:27
$type
$url
$s
Definition: pwgen.php:45
$password
Definition: pwgen.php:17
if(isset($_POST['submit'])) $form
if(!file_exists("$old.txt")) if( $old===$new) if(file_exists("$new.txt")) $file
echo;exit;}function LogoutNotification($SessionID){ global $ilDB;$q="SELECT session_id, data FROM usr_session WHERE expires > (\w+)\|/" PREG_SPLIT_NO_EMPTY PREG_SPLIT_DELIM_CAPTURE
if((!isset($_SERVER['DOCUMENT_ROOT'])) OR(empty($_SERVER['DOCUMENT_ROOT']))) $_SERVER['DOCUMENT_ROOT']
const K_TCPDF_THROW_EXCEPTION_ERROR
If true and PHP version is greater than 5, then the Error() method throw new exception instead of ter...
const PDF_FONT_NAME_MAIN
Default main font name.
const K_TCPDF_CALLS_IN_HTML
If true allows to call TCPDF methods using HTML syntax IMPORTANT: For security reason,...
const K_THAI_TOPCHARS
Set to true to enable the special procedure used to avoid the overlappind of symbols on Thai language...
const K_CELL_HEIGHT_RATIO
Height of cell respect font height.
const K_BLANK_IMAGE
Installation path (/var/www/tcpdf/).
const K_SMALL_RATIO
Reduction factor for small font.
const K_PATH_CACHE
Cache directory for temporary files (full path).
const K_PATH_IMAGES
Installation path (/var/www/tcpdf/).
$params
Definition: disable.php:11
$text
Definition: errorreport.php:18
$cols
Definition: xhr_table.php:11
$rows
Definition: xhr_table.php:10