ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
tcpdf.php
Go to the documentation of this file.
1<?php
2//============================================================+
3// File name : tcpdf.php
4// Version : 6.2.13
5// Begin : 2002-08-03
6// Last Update : 2015-06-18
7// Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
8// License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
9// -------------------------------------------------------------------
10// Copyright (C) 2002-2015 Nicola Asuni - Tecnick.com LTD
11//
12// This file is part of TCPDF software library.
13//
14// TCPDF is free software: you can redistribute it and/or modify it
15// under the terms of the GNU Lesser General Public License as
16// published by the Free Software Foundation, either version 3 of the
17// License, or (at your option) any later version.
18//
19// TCPDF is distributed in the hope that it will be useful, but
20// WITHOUT ANY WARRANTY; without even the implied warranty of
21// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22// See the GNU Lesser General Public License for more details.
23//
24// You should have received a copy of the License
25// along with TCPDF. If not, see
26// <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>.
27//
28// See LICENSE.TXT file for more information.
29// -------------------------------------------------------------------
30//
31// Description :
32// This is a PHP class for generating PDF documents without requiring external extensions.
33//
34// NOTE:
35// This class was originally derived in 2002 from the Public
36// Domain FPDF class by Olivier Plathey (http://www.fpdf.org),
37// but now is almost entirely rewritten and contains thousands of
38// new lines of code and hundreds new features.
39//
40// Main features:
41// * no external libraries are required for the basic functions;
42// * all standard page formats, custom page formats, custom margins and units of measure;
43// * UTF-8 Unicode and Right-To-Left languages;
44// * TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;
45// * font subsetting;
46// * methods to publish some XHTML + CSS code, Javascript and Forms;
47// * images, graphic (geometric figures) and transformation methods;
48// * supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)
49// * 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extension, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, Datamatrix, QR-Code, PDF417;
50// * JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;
51// * automatic page header and footer management;
52// * document encryption up to 256 bit and digital signature certifications;
53// * transactions to UNDO commands;
54// * PDF annotations, including links, text and file attachments;
55// * text rendering modes (fill, stroke and clipping);
56// * multiple columns mode;
57// * no-write page regions;
58// * bookmarks, named destinations and table of content;
59// * text hyphenation;
60// * text stretching and spacing (tracking);
61// * automatic page break, line break and text alignments including justification;
62// * automatic page numbering and page groups;
63// * move and delete pages;
64// * page compression (requires php-zlib extension);
65// * XOBject Templates;
66// * Layers and object visibility.
67// * PDF/A-1b support
68//============================================================+
69
110// TCPDF configuration
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 }
2127 $pf = TCPDF_STATIC::getPageSizeFromFormat($format['format']);
2128 }
2129 $this->fwPt = $pf[0];
2130 $this->fhPt = $pf[1];
2131 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2132 }
2133 // the visible region of default user space
2134 if (isset($format['CropBox'])) {
2135 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'CropBox', $format['CropBox']['llx'], $format['CropBox']['lly'], $format['CropBox']['urx'], $format['CropBox']['ury'], false, $this->k, $this->pagedim);
2136 }
2137 // the region to which the contents of the page shall be clipped when output in a production environment
2138 if (isset($format['BleedBox'])) {
2139 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'BleedBox', $format['BleedBox']['llx'], $format['BleedBox']['lly'], $format['BleedBox']['urx'], $format['BleedBox']['ury'], false, $this->k, $this->pagedim);
2140 }
2141 // the intended dimensions of the finished page after trimming
2142 if (isset($format['TrimBox'])) {
2143 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'TrimBox', $format['TrimBox']['llx'], $format['TrimBox']['lly'], $format['TrimBox']['urx'], $format['TrimBox']['ury'], false, $this->k, $this->pagedim);
2144 }
2145 // the page's meaningful content (including potential white space)
2146 if (isset($format['ArtBox'])) {
2147 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'ArtBox', $format['ArtBox']['llx'], $format['ArtBox']['lly'], $format['ArtBox']['urx'], $format['ArtBox']['ury'], false, $this->k, $this->pagedim);
2148 }
2149 // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
2150 if (isset($format['BoxColorInfo'])) {
2151 $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo'];
2152 }
2153 if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) {
2154 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2155 $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']);
2156 }
2157 if (isset($format['PZ'])) {
2158 // The page's preferred zoom (magnification) factor
2159 $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']);
2160 }
2161 if (isset($format['trans'])) {
2162 // The style and duration of the visual transition to use when moving from another page to the given page during a presentation
2163 if (isset($format['trans']['Dur'])) {
2164 // The page's display duration
2165 $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']);
2166 }
2167 $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
2168 if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
2169 // The transition style that shall be used when moving to this page from another during a presentation
2170 $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S'];
2171 $valid_effect = array('Split', 'Blinds');
2172 $valid_vals = array('H', 'V');
2173 if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
2174 $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm'];
2175 }
2176 $valid_effect = array('Split', 'Box', 'Fly');
2177 $valid_vals = array('I', 'O');
2178 if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
2179 $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M'];
2180 }
2181 $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
2182 if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
2183 if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
2184 OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
2185 OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
2186 $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']);
2187 }
2188 }
2189 if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
2190 $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']);
2191 }
2192 if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
2193 $this->pagedim[$this->page]['trans']['B'] = 'true';
2194 }
2195 } else {
2196 $this->pagedim[$this->page]['trans']['S'] = 'R';
2197 }
2198 if (isset($format['trans']['D'])) {
2199 // The duration of the transition effect, in seconds
2200 $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']);
2201 } else {
2202 $this->pagedim[$this->page]['trans']['D'] = 1;
2203 }
2204 }
2205 }
2206 $this->setPageOrientation($orientation);
2207 }
2208
2217 public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
2218 if (!isset($this->pagedim[$this->page]['MediaBox'])) {
2219 // the boundaries of the physical medium on which the page shall be displayed or printed
2220 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2221 }
2222 if (!isset($this->pagedim[$this->page]['CropBox'])) {
2223 // the visible region of default user space
2224 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'CropBox', $this->pagedim[$this->page]['MediaBox']['llx'], $this->pagedim[$this->page]['MediaBox']['lly'], $this->pagedim[$this->page]['MediaBox']['urx'], $this->pagedim[$this->page]['MediaBox']['ury'], true, $this->k, $this->pagedim);
2225 }
2226 if (!isset($this->pagedim[$this->page]['BleedBox'])) {
2227 // the region to which the contents of the page shall be clipped when output in a production environment
2228 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'BleedBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2229 }
2230 if (!isset($this->pagedim[$this->page]['TrimBox'])) {
2231 // the intended dimensions of the finished page after trimming
2232 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'TrimBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2233 }
2234 if (!isset($this->pagedim[$this->page]['ArtBox'])) {
2235 // the page's meaningful content (including potential white space)
2236 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'ArtBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2237 }
2238 if (!isset($this->pagedim[$this->page]['Rotate'])) {
2239 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2240 $this->pagedim[$this->page]['Rotate'] = 0;
2241 }
2242 if (!isset($this->pagedim[$this->page]['PZ'])) {
2243 // The page's preferred zoom (magnification) factor
2244 $this->pagedim[$this->page]['PZ'] = 1;
2245 }
2246 if ($this->fwPt > $this->fhPt) {
2247 // landscape
2248 $default_orientation = 'L';
2249 } else {
2250 // portrait
2251 $default_orientation = 'P';
2252 }
2253 $valid_orientations = array('P', 'L');
2254 if (empty($orientation)) {
2255 $orientation = $default_orientation;
2256 } else {
2257 $orientation = strtoupper($orientation[0]);
2258 }
2259 if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
2260 $this->CurOrientation = $orientation;
2261 $this->wPt = $this->fhPt;
2262 $this->hPt = $this->fwPt;
2263 } else {
2264 $this->CurOrientation = $default_orientation;
2265 $this->wPt = $this->fwPt;
2266 $this->hPt = $this->fhPt;
2267 }
2268 if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
2269 // swap X and Y coordinates (change page orientation)
2270 $this->pagedim = TCPDF_STATIC::swapPageBoxCoordinates($this->page, $this->pagedim);
2271 }
2272 $this->w = ($this->wPt / $this->k);
2273 $this->h = ($this->hPt / $this->k);
2274 if (TCPDF_STATIC::empty_string($autopagebreak)) {
2275 if (isset($this->AutoPageBreak)) {
2276 $autopagebreak = $this->AutoPageBreak;
2277 } else {
2278 $autopagebreak = true;
2279 }
2280 }
2281 if (TCPDF_STATIC::empty_string($bottommargin)) {
2282 if (isset($this->bMargin)) {
2283 $bottommargin = $this->bMargin;
2284 } else {
2285 // default value = 2 cm
2286 $bottommargin = 2 * 28.35 / $this->k;
2287 }
2288 }
2289 $this->SetAutoPageBreak($autopagebreak, $bottommargin);
2290 // store page dimensions
2291 $this->pagedim[$this->page]['w'] = $this->wPt;
2292 $this->pagedim[$this->page]['h'] = $this->hPt;
2293 $this->pagedim[$this->page]['wk'] = $this->w;
2294 $this->pagedim[$this->page]['hk'] = $this->h;
2295 $this->pagedim[$this->page]['tm'] = $this->tMargin;
2296 $this->pagedim[$this->page]['bm'] = $bottommargin;
2297 $this->pagedim[$this->page]['lm'] = $this->lMargin;
2298 $this->pagedim[$this->page]['rm'] = $this->rMargin;
2299 $this->pagedim[$this->page]['pb'] = $autopagebreak;
2300 $this->pagedim[$this->page]['or'] = $this->CurOrientation;
2301 $this->pagedim[$this->page]['olm'] = $this->original_lMargin;
2302 $this->pagedim[$this->page]['orm'] = $this->original_rMargin;
2303 }
2304
2323 public function setSpacesRE($re='/[^\S\xa0]/') {
2324 $this->re_spaces = $re;
2325 $re_parts = explode('/', $re);
2326 // get pattern parts
2327 $this->re_space = array();
2328 if (isset($re_parts[1]) AND !empty($re_parts[1])) {
2329 $this->re_space['p'] = $re_parts[1];
2330 } else {
2331 $this->re_space['p'] = '[\s]';
2332 }
2333 // set pattern modifiers
2334 if (isset($re_parts[2]) AND !empty($re_parts[2])) {
2335 $this->re_space['m'] = $re_parts[2];
2336 } else {
2337 $this->re_space['m'] = '';
2338 }
2339 }
2340
2348 public function setRTL($enable, $resetx=true) {
2349 $enable = $enable ? true : false;
2350 $resetx = ($resetx AND ($enable != $this->rtl));
2351 $this->rtl = $enable;
2352 $this->tmprtl = false;
2353 if ($resetx) {
2354 $this->Ln(0);
2355 }
2356 }
2357
2364 public function getRTL() {
2365 return $this->rtl;
2366 }
2367
2374 public function setTempRTL($mode) {
2375 $newmode = false;
2376 switch (strtoupper($mode)) {
2377 case 'LTR':
2378 case 'L': {
2379 if ($this->rtl) {
2380 $newmode = 'L';
2381 }
2382 break;
2383 }
2384 case 'RTL':
2385 case 'R': {
2386 if (!$this->rtl) {
2387 $newmode = 'R';
2388 }
2389 break;
2390 }
2391 case false:
2392 default: {
2393 $newmode = false;
2394 break;
2395 }
2396 }
2397 $this->tmprtl = $newmode;
2398 }
2399
2406 public function isRTLTextDir() {
2407 return ($this->rtl OR ($this->tmprtl == 'R'));
2408 }
2409
2417 public function setLastH($h) {
2418 $this->lasth = $h;
2419 }
2420
2427 public function getCellHeight($fontsize, $padding=TRUE) {
2428 $height = ($fontsize * $this->cell_height_ratio);
2429 if ($padding) {
2430 $height += ($this->cell_padding['T'] + $this->cell_padding['B']);
2431 }
2432 return round($height, 6);
2433 }
2434
2440 public function resetLastH() {
2441 $this->lasth = $this->getCellHeight($this->FontSize);
2442 }
2443
2450 public function getLastH() {
2451 return $this->lasth;
2452 }
2453
2461 public function setImageScale($scale) {
2462 $this->imgscale = $scale;
2463 }
2464
2472 public function getImageScale() {
2473 return $this->imgscale;
2474 }
2475
2485 public function getPageDimensions($pagenum='') {
2486 if (empty($pagenum)) {
2487 $pagenum = $this->page;
2488 }
2489 return $this->pagedim[$pagenum];
2490 }
2491
2501 public function getPageWidth($pagenum='') {
2502 if (empty($pagenum)) {
2503 return $this->w;
2504 }
2505 return $this->pagedim[$pagenum]['w'];
2506 }
2507
2517 public function getPageHeight($pagenum='') {
2518 if (empty($pagenum)) {
2519 return $this->h;
2520 }
2521 return $this->pagedim[$pagenum]['h'];
2522 }
2523
2533 public function getBreakMargin($pagenum='') {
2534 if (empty($pagenum)) {
2535 return $this->bMargin;
2536 }
2537 return $this->pagedim[$pagenum]['bm'];
2538 }
2539
2547 public function getScaleFactor() {
2548 return $this->k;
2549 }
2550
2561 public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
2562 //Set left, top and right margins
2563 $this->lMargin = $left;
2564 $this->tMargin = $top;
2565 if ($right == -1) {
2566 $right = $left;
2567 }
2568 $this->rMargin = $right;
2569 if ($keepmargins) {
2570 // overwrite original values
2571 $this->original_lMargin = $this->lMargin;
2572 $this->original_rMargin = $this->rMargin;
2573 }
2574 }
2575
2583 public function SetLeftMargin($margin) {
2584 //Set left margin
2585 $this->lMargin = $margin;
2586 if (($this->page > 0) AND ($this->x < $margin)) {
2587 $this->x = $margin;
2588 }
2589 }
2590
2598 public function SetTopMargin($margin) {
2599 //Set top margin
2600 $this->tMargin = $margin;
2601 if (($this->page > 0) AND ($this->y < $margin)) {
2602 $this->y = $margin;
2603 }
2604 }
2605
2613 public function SetRightMargin($margin) {
2614 $this->rMargin = $margin;
2615 if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
2616 $this->x = $this->w - $margin;
2617 }
2618 }
2619
2627 public function SetCellPadding($pad) {
2628 if ($pad >= 0) {
2629 $this->cell_padding['L'] = $pad;
2630 $this->cell_padding['T'] = $pad;
2631 $this->cell_padding['R'] = $pad;
2632 $this->cell_padding['B'] = $pad;
2633 }
2634 }
2635
2646 public function setCellPaddings($left='', $top='', $right='', $bottom='') {
2647 if (($left !== '') AND ($left >= 0)) {
2648 $this->cell_padding['L'] = $left;
2649 }
2650 if (($top !== '') AND ($top >= 0)) {
2651 $this->cell_padding['T'] = $top;
2652 }
2653 if (($right !== '') AND ($right >= 0)) {
2654 $this->cell_padding['R'] = $right;
2655 }
2656 if (($bottom !== '') AND ($bottom >= 0)) {
2657 $this->cell_padding['B'] = $bottom;
2658 }
2659 }
2660
2668 public function getCellPaddings() {
2669 return $this->cell_padding;
2670 }
2671
2682 public function setCellMargins($left='', $top='', $right='', $bottom='') {
2683 if (($left !== '') AND ($left >= 0)) {
2684 $this->cell_margin['L'] = $left;
2685 }
2686 if (($top !== '') AND ($top >= 0)) {
2687 $this->cell_margin['T'] = $top;
2688 }
2689 if (($right !== '') AND ($right >= 0)) {
2690 $this->cell_margin['R'] = $right;
2691 }
2692 if (($bottom !== '') AND ($bottom >= 0)) {
2693 $this->cell_margin['B'] = $bottom;
2694 }
2695 }
2696
2704 public function getCellMargins() {
2705 return $this->cell_margin;
2706 }
2707
2715 protected function adjustCellPadding($brd=0) {
2716 if (empty($brd)) {
2717 return;
2718 }
2719 if (is_string($brd)) {
2720 // convert string to array
2721 $slen = strlen($brd);
2722 $newbrd = array();
2723 for ($i = 0; $i < $slen; ++$i) {
2724 $newbrd[$brd[$i]] = true;
2725 }
2726 $brd = $newbrd;
2727 } elseif (($brd === 1) OR ($brd === true) OR (is_numeric($brd) AND (intval($brd) > 0))) {
2728 $brd = array('LRTB' => true);
2729 }
2730 if (!is_array($brd)) {
2731 return;
2732 }
2733 // store current cell padding
2734 $cp = $this->cell_padding;
2735 // select border mode
2736 if (isset($brd['mode'])) {
2737 $mode = $brd['mode'];
2738 unset($brd['mode']);
2739 } else {
2740 $mode = 'normal';
2741 }
2742 // process borders
2743 foreach ($brd as $border => $style) {
2744 $line_width = $this->LineWidth;
2745 if (is_array($style) AND isset($style['width'])) {
2746 // get border width
2747 $line_width = $style['width'];
2748 }
2749 $adj = 0; // line width inside the cell
2750 switch ($mode) {
2751 case 'ext': {
2752 $adj = 0;
2753 break;
2754 }
2755 case 'int': {
2756 $adj = $line_width;
2757 break;
2758 }
2759 case 'normal':
2760 default: {
2761 $adj = ($line_width / 2);
2762 break;
2763 }
2764 }
2765 // correct internal cell padding if required to avoid overlap between text and lines
2766 if ((strpos($border,'T') !== false) AND ($this->cell_padding['T'] < $adj)) {
2767 $this->cell_padding['T'] = $adj;
2768 }
2769 if ((strpos($border,'R') !== false) AND ($this->cell_padding['R'] < $adj)) {
2770 $this->cell_padding['R'] = $adj;
2771 }
2772 if ((strpos($border,'B') !== false) AND ($this->cell_padding['B'] < $adj)) {
2773 $this->cell_padding['B'] = $adj;
2774 }
2775 if ((strpos($border,'L') !== false) AND ($this->cell_padding['L'] < $adj)) {
2776 $this->cell_padding['L'] = $adj;
2777 }
2778 }
2779 return array('T' => ($this->cell_padding['T'] - $cp['T']), 'R' => ($this->cell_padding['R'] - $cp['R']), 'B' => ($this->cell_padding['B'] - $cp['B']), 'L' => ($this->cell_padding['L'] - $cp['L']));
2780 }
2781
2790 public function SetAutoPageBreak($auto, $margin=0) {
2791 $this->AutoPageBreak = $auto ? true : false;
2792 $this->bMargin = $margin;
2793 $this->PageBreakTrigger = $this->h - $margin;
2794 }
2795
2802 public function getAutoPageBreak() {
2803 return $this->AutoPageBreak;
2804 }
2805
2814 public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
2815 if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
2816 $this->ZoomMode = $zoom;
2817 } else {
2818 $this->Error('Incorrect zoom display mode: '.$zoom);
2819 }
2820 $this->LayoutMode = TCPDF_STATIC::getPageLayoutMode($layout);
2821 $this->PageMode = TCPDF_STATIC::getPageMode($mode);
2822 }
2823
2831 public function SetCompression($compress=true) {
2832 if (function_exists('gzcompress')) {
2833 $this->compress = $compress ? true : false;
2834 } else {
2835 $this->compress = false;
2836 }
2837 }
2838
2845 public function setSRGBmode($mode=false) {
2846 $this->force_srgb = $mode ? true : false;
2847 }
2848
2856 public function SetDocInfoUnicode($unicode=true) {
2857 $this->docinfounicode = $unicode ? true : false;
2858 }
2859
2867 public function SetTitle($title) {
2868 $this->title = $title;
2869 }
2870
2878 public function SetSubject($subject) {
2879 $this->subject = $subject;
2880 }
2881
2889 public function SetAuthor($author) {
2890 $this->author = $author;
2891 }
2892
2900 public function SetKeywords($keywords) {
2901 $this->keywords = $keywords;
2902 }
2903
2911 public function SetCreator($creator) {
2912 $this->creator = $creator;
2913 }
2914
2921 public function Error($msg) {
2922 // unset all class variables
2923 $this->_destroy(true);
2924 if (defined('K_TCPDF_THROW_EXCEPTION_ERROR') AND !K_TCPDF_THROW_EXCEPTION_ERROR) {
2925 die('<strong>TCPDF ERROR: </strong>'.$msg);
2926 } else {
2927 throw new Exception('TCPDF ERROR: '.$msg);
2928 }
2929 }
2930
2939 public function Open() {
2940 $this->state = 1;
2941 }
2942
2951 public function Close() {
2952 if ($this->state == 3) {
2953 return;
2954 }
2955 if ($this->page == 0) {
2956 $this->AddPage();
2957 }
2958 $this->endLayer();
2959 if ($this->tcpdflink) {
2960 // save current graphic settings
2961 $gvars = $this->getGraphicVars();
2962 $this->setEqualColumns();
2963 $this->lastpage(true);
2964 $this->SetAutoPageBreak(false);
2965 $this->x = 0;
2966 $this->y = $this->h - (1 / $this->k);
2967 $this->lMargin = 0;
2968 $this->_outSaveGraphicsState();
2969 $font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
2970 $this->SetFont($font, '', 1);
2971 $this->setTextRenderingMode(0, false, false);
2972 $msg = "\x50\x6f\x77\x65\x72\x65\x64\x20\x62\x79\x20\x54\x43\x50\x44\x46\x20\x28\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29";
2973 $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
2974 $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
2975 $this->_outRestoreGraphicsState();
2976 // restore graphic settings
2977 $this->setGraphicVars($gvars);
2978 }
2979 // close page
2980 $this->endPage();
2981 // close document
2982 $this->_enddoc();
2983 // unset all class variables (except critical ones)
2984 $this->_destroy(false);
2985 }
2986
2995 public function setPage($pnum, $resetmargins=false) {
2996 if (($pnum == $this->page) AND ($this->state == 2)) {
2997 return;
2998 }
2999 if (($pnum > 0) AND ($pnum <= $this->numpages)) {
3000 $this->state = 2;
3001 // save current graphic settings
3002 //$gvars = $this->getGraphicVars();
3003 $oldpage = $this->page;
3004 $this->page = $pnum;
3005 $this->wPt = $this->pagedim[$this->page]['w'];
3006 $this->hPt = $this->pagedim[$this->page]['h'];
3007 $this->w = $this->pagedim[$this->page]['wk'];
3008 $this->h = $this->pagedim[$this->page]['hk'];
3009 $this->tMargin = $this->pagedim[$this->page]['tm'];
3010 $this->bMargin = $this->pagedim[$this->page]['bm'];
3011 $this->original_lMargin = $this->pagedim[$this->page]['olm'];
3012 $this->original_rMargin = $this->pagedim[$this->page]['orm'];
3013 $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
3014 $this->CurOrientation = $this->pagedim[$this->page]['or'];
3015 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
3016 // restore graphic settings
3017 //$this->setGraphicVars($gvars);
3018 if ($resetmargins) {
3019 $this->lMargin = $this->pagedim[$this->page]['olm'];
3020 $this->rMargin = $this->pagedim[$this->page]['orm'];
3021 $this->SetY($this->tMargin);
3022 } else {
3023 // account for booklet mode
3024 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3025 $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
3026 $this->lMargin += $deltam;
3027 $this->rMargin -= $deltam;
3028 }
3029 }
3030 } else {
3031 $this->Error('Wrong page number on setPage() function: '.$pnum);
3032 }
3033 }
3034
3042 public function lastPage($resetmargins=false) {
3043 $this->setPage($this->getNumPages(), $resetmargins);
3044 }
3045
3053 public function getPage() {
3054 return $this->page;
3055 }
3056
3064 public function getNumPages() {
3065 return $this->numpages;
3066 }
3067
3077 public function addTOCPage($orientation='', $format='', $keepmargins=false) {
3078 $this->AddPage($orientation, $format, $keepmargins, true);
3079 }
3080
3087 public function endTOCPage() {
3088 $this->endPage(true);
3089 }
3090
3102 public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
3103 if ($this->inxobj) {
3104 // we are inside an XObject template
3105 return;
3106 }
3107 if (!isset($this->original_lMargin) OR $keepmargins) {
3108 $this->original_lMargin = $this->lMargin;
3109 }
3110 if (!isset($this->original_rMargin) OR $keepmargins) {
3111 $this->original_rMargin = $this->rMargin;
3112 }
3113 // terminate previous page
3114 $this->endPage();
3115 // start new page
3116 $this->startPage($orientation, $format, $tocpage);
3117 }
3118
3126 public function endPage($tocpage=false) {
3127 // check if page is already closed
3128 if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
3129 return;
3130 }
3131 // print page footer
3132 $this->setFooter();
3133 // close page
3134 $this->_endpage();
3135 // mark page as closed
3136 $this->pageopen[$this->page] = false;
3137 if ($tocpage) {
3138 $this->tocpage = false;
3139 }
3140 }
3141
3152 public function startPage($orientation='', $format='', $tocpage=false) {
3153 if ($tocpage) {
3154 $this->tocpage = true;
3155 }
3156 // move page numbers of documents to be attached
3157 if ($this->tocpage) {
3158 // move reference to unexistent pages (used for page attachments)
3159 // adjust outlines
3160 $tmpoutlines = $this->outlines;
3161 foreach ($tmpoutlines as $key => $outline) {
3162 if (!$outline['f'] AND ($outline['p'] > $this->numpages)) {
3163 $this->outlines[$key]['p'] = ($outline['p'] + 1);
3164 }
3165 }
3166 // adjust dests
3167 $tmpdests = $this->dests;
3168 foreach ($tmpdests as $key => $dest) {
3169 if (!$dest['f'] AND ($dest['p'] > $this->numpages)) {
3170 $this->dests[$key]['p'] = ($dest['p'] + 1);
3171 }
3172 }
3173 // adjust links
3174 $tmplinks = $this->links;
3175 foreach ($tmplinks as $key => $link) {
3176 if (!$link['f'] AND ($link['p'] > $this->numpages)) {
3177 $this->links[$key]['p'] = ($link['p'] + 1);
3178 }
3179 }
3180 }
3181 if ($this->numpages > $this->page) {
3182 // this page has been already added
3183 $this->setPage($this->page + 1);
3184 $this->SetY($this->tMargin);
3185 return;
3186 }
3187 // start a new page
3188 if ($this->state == 0) {
3189 $this->Open();
3190 }
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;
5012 $sc = $this->strokecolor;
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
6286 if (preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_ARABIC, $s)) {
6287 $arabic = true;
6288 } else {
6289 $arabic = false;
6290 }
6291 // check if string contains RTL text
6292 if ($arabic OR ($this->tmprtl == 'R') OR preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $s)) {
6293 $rtlmode = true;
6294 } else {
6295 $rtlmode = false;
6296 }
6297 // get a char width
6298 $chrwidth = $this->GetCharWidth(46); // dot character
6299 // get array of unicode values
6300 $chars = TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont);
6301 // calculate maximum width for a single character on string
6302 $chrw = $this->GetArrStringWidth($chars, '', '', 0, true);
6303 array_walk($chrw, array($this, 'getRawCharWidth'));
6304 $maxchwidth = max($chrw);
6305 // get array of chars
6306 $uchars = TCPDF_FONTS::UTF8ArrayToUniArray($chars, $this->isunicode);
6307 // get the number of characters
6308 $nb = count($chars);
6309 // replacement for SHY character (minus symbol)
6310 $shy_replacement = 45;
6311 $shy_replacement_char = TCPDF_FONTS::unichr($shy_replacement, $this->isunicode);
6312 // widht for SHY replacement
6313 $shy_replacement_width = $this->GetCharWidth($shy_replacement);
6314 // page width
6315 $pw = $w = $this->w - $this->lMargin - $this->rMargin;
6316 // calculate remaining line width ($w)
6317 if ($this->rtl) {
6318 $w = $this->x - $this->lMargin;
6319 } else {
6320 $w = $this->w - $this->rMargin - $this->x;
6321 }
6322 // max column width
6323 $wmax = ($w - $wadj);
6324 if (!$firstline) {
6325 $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']);
6326 }
6327 if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) {
6328 // the maximum width character do not fit on column
6329 return '';
6330 }
6331 // minimum row height
6332 $row_height = max($h, $this->getCellHeight($this->FontSize));
6333 // max Y
6334 $maxy = $this->y + $maxh - max($row_height, $h);
6335 $start_page = $this->page;
6336 $i = 0; // character position
6337 $j = 0; // current starting position
6338 $sep = -1; // position of the last blank space
6339 $prevsep = $sep; // previous separator
6340 $shy = false; // true if the last blank is a soft hypen (SHY)
6341 $prevshy = $shy; // previous shy mode
6342 $l = 0; // current string length
6343 $nl = 0; //number of lines
6344 $linebreak = false;
6345 $pc = 0; // previous character
6346 // for each character
6347 while ($i < $nb) {
6348 if (($maxh > 0) AND ($this->y > $maxy) ) {
6349 break;
6350 }
6351 //Get the current character
6352 $c = $chars[$i];
6353 if ($c == 10) { // 10 = "\n" = new line
6354 //Explicit line break
6355 if ($align == 'J') {
6356 if ($this->rtl) {
6357 $talign = 'R';
6358 } else {
6359 $talign = 'L';
6360 }
6361 } else {
6362 $talign = $align;
6363 }
6364 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6365 if ($firstline) {
6366 $startx = $this->x;
6367 $tmparr = array_slice($chars, $j, ($i - $j));
6368 if ($rtlmode) {
6369 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6370 }
6371 $linew = $this->GetArrStringWidth($tmparr);
6372 unset($tmparr);
6373 if ($this->rtl) {
6374 $this->endlinex = $startx - $linew;
6375 } else {
6376 $this->endlinex = $startx + $linew;
6377 }
6378 $w = $linew;
6379 $tmpcellpadding = $this->cell_padding;
6380 if ($maxh == 0) {
6381 $this->SetCellPadding(0);
6382 }
6383 }
6384 if ($firstblock AND $this->isRTLTextDir()) {
6385 $tmpstr = $this->stringRightTrim($tmpstr);
6386 }
6387 // Skip newlines at the beginning of a page or column
6388 if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) {
6389 $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
6390 }
6391 unset($tmpstr);
6392 if ($firstline) {
6393 $this->cell_padding = $tmpcellpadding;
6394 return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6395 }
6396 ++$nl;
6397 $j = $i + 1;
6398 $l = 0;
6399 $sep = -1;
6400 $prevsep = $sep;
6401 $shy = false;
6402 // account for margin changes
6403 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6404 $this->AcceptPageBreak();
6405 if ($this->rtl) {
6406 $this->x -= $margin['R'];
6407 } else {
6408 $this->x += $margin['L'];
6409 }
6410 $this->lMargin += $margin['L'];
6411 $this->rMargin += $margin['R'];
6412 }
6413 $w = $this->getRemainingWidth();
6414 $wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']);
6415 } else {
6416 // 160 is the non-breaking space.
6417 // 173 is SHY (Soft Hypen).
6418 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
6419 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
6420 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
6421 if (($c != 160)
6422 AND (($c == 173)
6423 OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6424 OR (($c == 45)
6425 AND ($i < ($nb - 1))
6426 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($pc, $this->isunicode))
6427 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6428 )
6429 )
6430 ) {
6431 // update last blank space position
6432 $prevsep = $sep;
6433 $sep = $i;
6434 // check if is a SHY
6435 if (($c == 173) OR ($c == 45)) {
6436 $prevshy = $shy;
6437 $shy = true;
6438 if ($pc == 45) {
6439 $tmp_shy_replacement_width = 0;
6440 $tmp_shy_replacement_char = '';
6441 } else {
6442 $tmp_shy_replacement_width = $shy_replacement_width;
6443 $tmp_shy_replacement_char = $shy_replacement_char;
6444 }
6445 } else {
6446 $shy = false;
6447 }
6448 }
6449 // update string length
6450 if ($this->isUnicodeFont() AND ($arabic)) {
6451 // with bidirectional algorithm some chars may be changed affecting the line length
6452 // *** very slow ***
6453 $l = $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl, $this->isunicode, $this->CurrentFont));
6454 } else {
6455 $l += $this->GetCharWidth($c);
6456 }
6457 if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) >= $wmax))) {
6458 if (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) {
6459 $sep = $prevsep;
6460 $shy = $prevshy;
6461 }
6462 // we have reached the end of column
6463 if ($sep == -1) {
6464 // check if the line was already started
6465 if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth)))
6466 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) {
6467 // print a void cell and go to next line
6468 $this->Cell($w, $h, '', 0, 1);
6469 $linebreak = true;
6470 if ($firstline) {
6471 return (TCPDF_FONTS::UniArrSubString($uchars, $j));
6472 }
6473 } else {
6474 // truncate the word because do not fit on column
6475 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6476 if ($firstline) {
6477 $startx = $this->x;
6478 $tmparr = array_slice($chars, $j, ($i - $j));
6479 if ($rtlmode) {
6480 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6481 }
6482 $linew = $this->GetArrStringWidth($tmparr);
6483 unset($tmparr);
6484 if ($this->rtl) {
6485 $this->endlinex = $startx - $linew;
6486 } else {
6487 $this->endlinex = $startx + $linew;
6488 }
6489 $w = $linew;
6490 $tmpcellpadding = $this->cell_padding;
6491 if ($maxh == 0) {
6492 $this->SetCellPadding(0);
6493 }
6494 }
6495 if ($firstblock AND $this->isRTLTextDir()) {
6496 $tmpstr = $this->stringRightTrim($tmpstr);
6497 }
6498 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6499 unset($tmpstr);
6500 if ($firstline) {
6501 $this->cell_padding = $tmpcellpadding;
6502 return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6503 }
6504 $j = $i;
6505 --$i;
6506 }
6507 } else {
6508 // word wrapping
6509 if ($this->rtl AND (!$firstblock) AND ($sep < $i)) {
6510 $endspace = 1;
6511 } else {
6512 $endspace = 0;
6513 }
6514 // check the length of the next string
6515 $strrest = TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace));
6516 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $this->stringTrim($strrest));
6517 if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
6518 // truncate the word because do not fit on a full page width
6519 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6520 if ($firstline) {
6521 $startx = $this->x;
6522 $tmparr = array_slice($chars, $j, ($i - $j));
6523 if ($rtlmode) {
6524 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6525 }
6526 $linew = $this->GetArrStringWidth($tmparr);
6527 unset($tmparr);
6528 if ($this->rtl) {
6529 $this->endlinex = ($startx - $linew);
6530 } else {
6531 $this->endlinex = ($startx + $linew);
6532 }
6533 $w = $linew;
6534 $tmpcellpadding = $this->cell_padding;
6535 if ($maxh == 0) {
6536 $this->SetCellPadding(0);
6537 }
6538 }
6539 if ($firstblock AND $this->isRTLTextDir()) {
6540 $tmpstr = $this->stringRightTrim($tmpstr);
6541 }
6542 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6543 unset($tmpstr);
6544 if ($firstline) {
6545 $this->cell_padding = $tmpcellpadding;
6546 return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6547 }
6548 $j = $i;
6549 --$i;
6550 } else {
6551 // word wrapping
6552 if ($shy) {
6553 // add hypen (minus symbol) at the end of the line
6554 $shy_width = $tmp_shy_replacement_width;
6555 if ($this->rtl) {
6556 $shy_char_left = $tmp_shy_replacement_char;
6557 $shy_char_right = '';
6558 } else {
6559 $shy_char_left = '';
6560 $shy_char_right = $tmp_shy_replacement_char;
6561 }
6562 } else {
6563 $shy_width = 0;
6564 $shy_char_left = '';
6565 $shy_char_right = '';
6566 }
6567 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, ($sep + $endspace));
6568 if ($firstline) {
6569 $startx = $this->x;
6570 $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
6571 if ($rtlmode) {
6572 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6573 }
6574 $linew = $this->GetArrStringWidth($tmparr);
6575 unset($tmparr);
6576 if ($this->rtl) {
6577 $this->endlinex = $startx - $linew - $shy_width;
6578 } else {
6579 $this->endlinex = $startx + $linew + $shy_width;
6580 }
6581 $w = $linew;
6582 $tmpcellpadding = $this->cell_padding;
6583 if ($maxh == 0) {
6584 $this->SetCellPadding(0);
6585 }
6586 }
6587 // print the line
6588 if ($firstblock AND $this->isRTLTextDir()) {
6589 $tmpstr = $this->stringRightTrim($tmpstr);
6590 }
6591 $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
6592 unset($tmpstr);
6593 if ($firstline) {
6594 if ($chars[$sep] == 45) {
6595 $endspace += 1;
6596 }
6597 // return the remaining text
6598 $this->cell_padding = $tmpcellpadding;
6599 return (TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace)));
6600 }
6601 $i = $sep;
6602 $sep = -1;
6603 $shy = false;
6604 $j = ($i + 1);
6605 }
6606 }
6607 // account for margin changes
6608 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6609 $this->AcceptPageBreak();
6610 if ($this->rtl) {
6611 $this->x -= $margin['R'];
6612 } else {
6613 $this->x += $margin['L'];
6614 }
6615 $this->lMargin += $margin['L'];
6616 $this->rMargin += $margin['R'];
6617 }
6618 $w = $this->getRemainingWidth();
6619 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6620 if ($linebreak) {
6621 $linebreak = false;
6622 } else {
6623 ++$nl;
6624 $l = 0;
6625 }
6626 }
6627 }
6628 // save last character
6629 $pc = $c;
6630 ++$i;
6631 } // end while i < nb
6632 // print last substring (if any)
6633 if ($l > 0) {
6634 switch ($align) {
6635 case 'J':
6636 case 'C': {
6637 $w = $w;
6638 break;
6639 }
6640 case 'L': {
6641 if ($this->rtl) {
6642 $w = $w;
6643 } else {
6644 $w = $l;
6645 }
6646 break;
6647 }
6648 case 'R': {
6649 if ($this->rtl) {
6650 $w = $l;
6651 } else {
6652 $w = $w;
6653 }
6654 break;
6655 }
6656 default: {
6657 $w = $l;
6658 break;
6659 }
6660 }
6661 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $nb);
6662 if ($firstline) {
6663 $startx = $this->x;
6664 $tmparr = array_slice($chars, $j, ($nb - $j));
6665 if ($rtlmode) {
6666 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6667 }
6668 $linew = $this->GetArrStringWidth($tmparr);
6669 unset($tmparr);
6670 if ($this->rtl) {
6671 $this->endlinex = $startx - $linew;
6672 } else {
6673 $this->endlinex = $startx + $linew;
6674 }
6675 $w = $linew;
6676 $tmpcellpadding = $this->cell_padding;
6677 if ($maxh == 0) {
6678 $this->SetCellPadding(0);
6679 }
6680 }
6681 if ($firstblock AND $this->isRTLTextDir()) {
6682 $tmpstr = $this->stringRightTrim($tmpstr);
6683 }
6684 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
6685 unset($tmpstr);
6686 if ($firstline) {
6687 $this->cell_padding = $tmpcellpadding;
6688 return (TCPDF_FONTS::UniArrSubString($uchars, $nb));
6689 }
6690 ++$nl;
6691 }
6692 if ($firstline) {
6693 return '';
6694 }
6695 return $nl;
6696 }
6697
6703 protected function getRemainingWidth() {
6704 list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y);
6705 if ($this->rtl) {
6706 return ($this->x - $this->lMargin);
6707 } else {
6708 return ($this->w - $this->rMargin - $this->x);
6709 }
6710 }
6711
6723 protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
6724 if ($w <= 0) {
6725 // set maximum width
6726 $w = ($this->w - $this->lMargin - $this->rMargin);
6727 if ($w <= 0) {
6728 $w = 1;
6729 }
6730 }
6731 if ($h <= 0) {
6732 // set maximum height
6733 $h = ($this->PageBreakTrigger - $this->tMargin);
6734 if ($h <= 0) {
6735 $h = 1;
6736 }
6737 }
6738 // resize the block to be vertically contained on a single page or single column
6739 if ($fitonpage OR $this->AutoPageBreak) {
6740 $ratio_wh = ($w / $h);
6741 if ($h > ($this->PageBreakTrigger - $this->tMargin)) {
6742 $h = $this->PageBreakTrigger - $this->tMargin;
6743 $w = ($h * $ratio_wh);
6744 }
6745 // resize the block to be horizontally contained on a single page or single column
6746 if ($fitonpage) {
6747 $maxw = ($this->w - $this->lMargin - $this->rMargin);
6748 if ($w > $maxw) {
6749 $w = $maxw;
6750 $h = ($w / $ratio_wh);
6751 }
6752 }
6753 }
6754 // Check whether we need a new page or new column first as this does not fit
6755 $prev_x = $this->x;
6756 $prev_y = $this->y;
6757 if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) {
6758 $y = $this->y;
6759 if ($this->rtl) {
6760 $x += ($prev_x - $this->x);
6761 } else {
6762 $x += ($this->x - $prev_x);
6763 }
6764 $this->newline = true;
6765 }
6766 // resize the block to be contained on the remaining available page or column space
6767 if ($fitonpage) {
6768 //$ratio_wh = ($w / $h);
6769 // PATCH BEGIN
6770 if($h)
6771 {
6772 $ratio_wh = ($w / $h);
6773 }
6774 else
6775 {
6776 $ratio_wh = 1;
6777 }
6778 // PATCH END
6779
6780 if (($y + $h) > $this->PageBreakTrigger) {
6781 $h = $this->PageBreakTrigger - $y;
6782 $w = ($h * $ratio_wh);
6783 }
6784 if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
6785 $w = $this->w - $this->rMargin - $x;
6786 $h = ($w / $ratio_wh);
6787 } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
6788 $w = $x - $this->lMargin;
6789 $h = ($w / $ratio_wh);
6790 }
6791 }
6792 return array($w, $h, $x, $y);
6793 }
6794
6829 public function Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false, $hidden=false, $fitonpage=false, $alt=false, $altimgs=array()) {
6830 if ($this->state != 2) {
6831 return;
6832 }
6833 if (strcmp($x, '') === 0) {
6834 $x = $this->x;
6835 }
6836 if (strcmp($y, '') === 0) {
6837 $y = $this->y;
6838 }
6839 // check page for no-write regions and adapt page margins if necessary
6840 list($x, $y) = $this->checkPageRegions($h, $x, $y);
6841 $exurl = ''; // external streams
6842 $imsize = FALSE;
6843 // check if we are passing an image as file or string
6844 if ($file[0] === '@') {
6845 // image from string
6846 $imgdata = substr($file, 1);
6847 } else { // image file
6848 if ($file[0] === '*') {
6849 // image as external stream
6850 $file = substr($file, 1);
6851 $exurl = $file;
6852 }
6853 // check if is a local file
6854 if (!@file_exists($file)) {
6855 // try to encode spaces on filename
6856 $tfile = str_replace(' ', '%20', $file);
6857 if (@file_exists($tfile)) {
6858 $file = $tfile;
6859 }
6860 }
6861 if (($imsize = @getimagesize($file)) === FALSE) {
6862 if (in_array($file, $this->imagekeys)) {
6863 // get existing image data
6864 $info = $this->getImageBuffer($file);
6865 $imsize = array($info['w'], $info['h']);
6866 } elseif (strpos($file, '__tcpdf_'.$this->file_id.'_img') === FALSE) {
6868 }
6869 }
6870 }
6871 if (!empty($imgdata)) {
6872 // copy image to cache
6873 $original_file = $file;
6874 $file = TCPDF_STATIC::getObjFilename('img', $this->file_id);
6875 $fp = TCPDF_STATIC::fopenLocal($file, 'w');
6876 if (!$fp) {
6877 $this->Error('Unable to write file: '.$file);
6878 }
6879 fwrite($fp, $imgdata);
6880 fclose($fp);
6881 unset($imgdata);
6882 $imsize = @getimagesize($file);
6883 if ($imsize === FALSE) {
6884 unlink($file);
6885 $file = $original_file;
6886 }
6887 }
6888 if ($imsize === FALSE) {
6889 if (($w > 0) AND ($h > 0)) {
6890 // get measures from specified data
6891 $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6892 $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6893 $imsize = array($pw, $ph);
6894 } else {
6895 $this->Error('[Image] Unable to get the size of the image: '.$file);
6896 }
6897 }
6898 // file hash
6899 $filehash = md5($file);
6900 // get original image width and height in pixels
6901 list($pixw, $pixh) = $imsize;
6902 // calculate image width and height on document
6903 if (($w <= 0) AND ($h <= 0)) {
6904 // convert image size to document unit
6905 $w = $this->pixelsToUnits($pixw);
6906 $h = $this->pixelsToUnits($pixh);
6907 } elseif ($w <= 0) {
6908 $w = $h * $pixw / $pixh;
6909 } elseif ($h <= 0) {
6910 $h = $w * $pixh / $pixw;
6911 } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
6912 if (strlen($fitbox) !== 2) {
6913 // set default alignment
6914 $fitbox = '--';
6915 }
6916 // scale image dimensions proportionally to fit within the ($w, $h) box
6917 if ((($w * $pixh) / ($h * $pixw)) < 1) {
6918 // store current height
6919 $oldh = $h;
6920 // calculate new height
6921 $h = $w * $pixh / $pixw;
6922 // height difference
6923 $hdiff = ($oldh - $h);
6924 // vertical alignment
6925 switch (strtoupper($fitbox[1])) {
6926 case 'T': {
6927 break;
6928 }
6929 case 'M': {
6930 $y += ($hdiff / 2);
6931 break;
6932 }
6933 case 'B': {
6934 $y += $hdiff;
6935 break;
6936 }
6937 }
6938 } else {
6939 // store current width
6940 $oldw = $w;
6941 // calculate new width
6942 $w = $h * $pixw / $pixh;
6943 // width difference
6944 $wdiff = ($oldw - $w);
6945 // horizontal alignment
6946 switch (strtoupper($fitbox[0])) {
6947 case 'L': {
6948 if ($this->rtl) {
6949 $x -= $wdiff;
6950 }
6951 break;
6952 }
6953 case 'C': {
6954 if ($this->rtl) {
6955 $x -= ($wdiff / 2);
6956 } else {
6957 $x += ($wdiff / 2);
6958 }
6959 break;
6960 }
6961 case 'R': {
6962 if (!$this->rtl) {
6963 $x += $wdiff;
6964 }
6965 break;
6966 }
6967 }
6968 }
6969 }
6970 // fit the image on available space
6971 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
6972 // calculate new minimum dimensions in pixels
6973 $neww = round($w * $this->k * $dpi / $this->dpi);
6974 $newh = round($h * $this->k * $dpi / $this->dpi);
6975 // check if resize is necessary (resize is used only to reduce the image)
6976 $newsize = ($neww * $newh);
6977 $pixsize = ($pixw * $pixh);
6978 if (intval($resize) == 2) {
6979 $resize = true;
6980 } elseif ($newsize >= $pixsize) {
6981 $resize = false;
6982 }
6983 // check if image has been already added on document
6984 $newimage = true;
6985 if (in_array($file, $this->imagekeys)) {
6986 $newimage = false;
6987 // get existing image data
6988 $info = $this->getImageBuffer($file);
6989 if (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE) {
6990 // check if the newer image is larger
6991 $oldsize = ($info['w'] * $info['h']);
6992 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
6993 $newimage = true;
6994 }
6995 }
6996 } elseif (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE)) {
6997 // create temp image file (without alpha channel)
6998 $tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash;
6999 // create temp alpha file
7000 $tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash;
7001 // check for cached images
7002 if (in_array($tempfile_plain, $this->imagekeys)) {
7003 // get existing image data
7004 $info = $this->getImageBuffer($tempfile_plain);
7005 // check if the newer image is larger
7006 $oldsize = ($info['w'] * $info['h']);
7007 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7008 $newimage = true;
7009 } else {
7010 $newimage = false;
7011 // embed mask image
7012 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7013 // embed image, masked with previously embedded mask
7014 return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7015 }
7016 }
7017 }
7018 if ($newimage) {
7019 //First use of image, get info
7020 $type = strtolower($type);
7021 if ($type == '') {
7022 $type = TCPDF_IMAGES::getImageFileType($file, $imsize);
7023 } elseif ($type == 'jpg') {
7024 $type = 'jpeg';
7025 }
7026 $mqr = TCPDF_STATIC::get_mqr();
7027 TCPDF_STATIC::set_mqr(false);
7028 // Specific image handlers (defined on TCPDF_IMAGES CLASS)
7029 $mtd = '_parse'.$type;
7030 // GD image handler function
7031 $gdfunction = 'imagecreatefrom'.$type;
7032 $info = false;
7033 if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
7034 // TCPDF image functions
7035 $info = TCPDF_IMAGES::$mtd($file);
7036 if (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE)
7037 AND (($info === 'pngalpha') OR (isset($info['trns']) AND !empty($info['trns'])))) {
7038 return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
7039 }
7040 }
7041 if (($info === false) AND function_exists($gdfunction)) {
7042 try {
7043 // GD library
7044 $img = $gdfunction($file);
7045 if ($img !== false) {
7046 if ($resize) {
7047 $imgr = imagecreatetruecolor($neww, $newh);
7048 if (($type == 'gif') OR ($type == 'png')) {
7050 }
7051 imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
7052 $img = $imgr;
7053 }
7054 if (($type == 'gif') OR ($type == 'png')) {
7056 } else {
7057 $info = TCPDF_IMAGES::_toJPEG($img, $this->jpeg_quality, TCPDF_STATIC::getObjFilename('img', $this->file_id));
7058 }
7059 }
7060 } catch(Exception $e) {
7061 $info = false;
7062 }
7063 }
7064 if (($info === false) AND extension_loaded('imagick')) {
7065 try {
7066 // ImageMagick library
7067 $img = new Imagick();
7068 if ($type == 'svg') {
7069 if ($file[0] === '@') {
7070 // image from string
7071 $svgimg = substr($file, 1);
7072 } else {
7073 // get SVG file content
7075 }
7076 if ($svgimg !== FALSE) {
7077 // get width and height
7078 $regs = array();
7079 if (preg_match('/<svg([^>]*)>/si', $svgimg, $regs)) {
7080 $svgtag = $regs[1];
7081 $tmp = array();
7082 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7083 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7084 $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit;
7085 $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
7086 } else {
7087 $ow = $w;
7088 }
7089 $tmp = array();
7090 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7091 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7092 $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit;
7093 $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
7094 } else {
7095 $oh = $h;
7096 }
7097 $tmp = array();
7098 if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
7099 $vbw = ($ow * $this->imgscale * $this->k);
7100 $vbh = ($oh * $this->imgscale * $this->k);
7101 $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh);
7102 $svgtag = $vbox.$svgtag;
7103 }
7104 $svgimg = preg_replace('/<svg([^>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
7105 }
7106 $img->readImageBlob($svgimg);
7107 }
7108 } else {
7109 $img->readImage($file);
7110 }
7111 if ($resize) {
7112 $img->resizeImage($neww, $newh, 10, 1, false);
7113 }
7114 $img->setCompressionQuality($this->jpeg_quality);
7115 $img->setImageFormat('jpeg');
7116 $tempname = TCPDF_STATIC::getObjFilename('img', $this->file_id);
7117 $img->writeImage($tempname);
7118 $info = TCPDF_IMAGES::_parsejpeg($tempname);
7119 unlink($tempname);
7120 $img->destroy();
7121 } catch(Exception $e) {
7122 $info = false;
7123 }
7124 }
7125 if ($info === false) {
7126 // unable to process image
7127 return;
7128 }
7130 if ($ismask) {
7131 // force grayscale
7132 $info['cs'] = 'DeviceGray';
7133 }
7134 if ($imgmask !== false) {
7135 $info['masked'] = $imgmask;
7136 }
7137 if (!empty($exurl)) {
7138 $info['exurl'] = $exurl;
7139 }
7140 // array of alternative images
7141 $info['altimgs'] = $altimgs;
7142 // add image to document
7143 $info['i'] = $this->setImageBuffer($file, $info);
7144 }
7145 // set alignment
7146 $this->img_rb_y = $y + $h;
7147 // set alignment
7148 if ($this->rtl) {
7149 if ($palign == 'L') {
7150 $ximg = $this->lMargin;
7151 } elseif ($palign == 'C') {
7152 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7153 } elseif ($palign == 'R') {
7154 $ximg = $this->w - $this->rMargin - $w;
7155 } else {
7156 $ximg = $x - $w;
7157 }
7158 $this->img_rb_x = $ximg;
7159 } else {
7160 if ($palign == 'L') {
7161 $ximg = $this->lMargin;
7162 } elseif ($palign == 'C') {
7163 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7164 } elseif ($palign == 'R') {
7165 $ximg = $this->w - $this->rMargin - $w;
7166 } else {
7167 $ximg = $x;
7168 }
7169 $this->img_rb_x = $ximg + $w;
7170 }
7171 if ($ismask OR $hidden) {
7172 // image is not displayed
7173 return $info['i'];
7174 }
7175 $xkimg = $ximg * $this->k;
7176 if (!$alt) {
7177 // only non-alternative immages will be set
7178 $this->_out(sprintf('q %F 0 0 %F %F %F cm /I%u Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i']));
7179 }
7180 if (!empty($border)) {
7181 $bx = $this->x;
7182 $by = $this->y;
7183 $this->x = $ximg;
7184 if ($this->rtl) {
7185 $this->x += $w;
7186 }
7187 $this->y = $y;
7188 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
7189 $this->x = $bx;
7190 $this->y = $by;
7191 }
7192 if ($link) {
7193 $this->Link($ximg, $y, $w, $h, $link, 0);
7194 }
7195 // set pointer to align the next text/objects
7196 switch($align) {
7197 case 'T': {
7198 $this->y = $y;
7199 $this->x = $this->img_rb_x;
7200 break;
7201 }
7202 case 'M': {
7203 $this->y = $y + round($h/2);
7204 $this->x = $this->img_rb_x;
7205 break;
7206 }
7207 case 'B': {
7208 $this->y = $this->img_rb_y;
7209 $this->x = $this->img_rb_x;
7210 break;
7211 }
7212 case 'N': {
7213 $this->SetY($this->img_rb_y);
7214 break;
7215 }
7216 default:{
7217 break;
7218 }
7219 }
7220 $this->endlinex = $this->img_rb_x;
7221 if ($this->inxobj) {
7222 // we are inside an XObject template
7223 $this->xobjects[$this->xobjid]['images'][] = $info['i'];
7224 }
7225 return $info['i'];
7226 }
7227
7249 protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
7250 // create temp images
7251 if (empty($filehash)) {
7252 $filehash = md5($file);
7253 }
7254 // create temp image file (without alpha channel)
7255 $tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash;
7256 // create temp alpha file
7257 $tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash;
7258 $parsed = false;
7259 $parse_error = '';
7260 // ImageMagick extension
7261 if (($parsed === false) AND extension_loaded('imagick')) {
7262 try {
7263 // ImageMagick library
7264 $img = new Imagick();
7265 $img->readImage($file);
7266 // clone image object
7268 // extract alpha channel
7269 if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) {
7270 $img->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT);
7271 } else {
7272 $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
7273 $img->negateImage(true);
7274 }
7275 $img->setImageFormat('png');
7276 $img->writeImage($tempfile_alpha);
7277 // remove alpha channel
7278 if (method_exists($imga, 'setImageMatte')) {
7279 $imga->setImageMatte(false);
7280 } else {
7281 $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
7282 }
7283 $imga->setImageFormat('png');
7284 $imga->writeImage($tempfile_plain);
7285 $parsed = true;
7286 } catch (Exception $e) {
7287 // Imagemagick fails, try with GD
7288 $parse_error = 'Imagick library error: '.$e->getMessage();
7289 }
7290 }
7291 // GD extension
7292 if (($parsed === false) AND function_exists('imagecreatefrompng')) {
7293 try {
7294 // generate images
7295 $img = imagecreatefrompng($file);
7296 $imgalpha = imagecreate($wpx, $hpx);
7297 // generate gray scale palette (0 -> 255)
7298 for ($c = 0; $c < 256; ++$c) {
7299 ImageColorAllocate($imgalpha, $c, $c, $c);
7300 }
7301 // extract alpha channel
7302 for ($xpx = 0; $xpx < $wpx; ++$xpx) {
7303 for ($ypx = 0; $ypx < $hpx; ++$ypx) {
7304 $color = imagecolorat($img, $xpx, $ypx);
7305 // get and correct gamma color
7306 $alpha = $this->getGDgamma($img, $color);
7307 imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
7308 }
7309 }
7310 imagepng($imgalpha, $tempfile_alpha);
7311 imagedestroy($imgalpha);
7312 // extract image without alpha channel
7313 $imgplain = imagecreatetruecolor($wpx, $hpx);
7314 imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
7315 imagepng($imgplain, $tempfile_plain);
7316 imagedestroy($imgplain);
7317 $parsed = true;
7318 } catch (Exception $e) {
7319 // GD fails
7320 $parse_error = 'GD library error: '.$e->getMessage();
7321 }
7322 }
7323 if ($parsed === false) {
7324 if (empty($parse_error)) {
7325 $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
7326 } else {
7327 $this->Error($parse_error);
7328 }
7329 }
7330 // embed mask image
7331 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7332 // embed image, masked with previously embedded mask
7333 $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7334 }
7335
7343 protected function getGDgamma($img, $c) {
7344 if (!isset($this->gdgammacache['#'.$c])) {
7345 $colors = imagecolorsforindex($img, $c);
7346 // GD alpha is only 7 bit (0 -> 127)
7347 $this->gdgammacache['#'.$c] = (((127 - $colors['alpha']) / 127) * 255);
7348 // correct gamma
7349 $this->gdgammacache['#'.$c] = (pow(($this->gdgammacache['#'.$c] / 255), 2.2) * 255);
7350 // store the latest values on cache to improve performances
7351 if (count($this->gdgammacache) > 8) {
7352 // remove one element from the cache array
7353 array_shift($this->gdgammacache);
7354 }
7355 }
7356 return $this->gdgammacache['#'.$c];
7357 }
7358
7368 public function Ln($h='', $cell=false) {
7369 if (($this->num_columns > 1) AND ($this->y == $this->columns[$this->current_column]['y']) AND isset($this->columns[$this->current_column]['x']) AND ($this->x == $this->columns[$this->current_column]['x'])) {
7370 // revove vertical space from the top of the column
7371 return;
7372 }
7373 if ($cell) {
7374 if ($this->rtl) {
7375 $cellpadding = $this->cell_padding['R'];
7376 } else {
7377 $cellpadding = $this->cell_padding['L'];
7378 }
7379 } else {
7380 $cellpadding = 0;
7381 }
7382 if ($this->rtl) {
7383 $this->x = $this->w - $this->rMargin - $cellpadding;
7384 } else {
7385 $this->x = $this->lMargin + $cellpadding;
7386 }
7387 if (is_string($h)) {
7388 $h = $this->lasth;
7389 }
7390 $this->y += $h;
7391 $this->newline = true;
7392 }
7393
7402 public function GetX() {
7403 //Get x position
7404 if ($this->rtl) {
7405 return ($this->w - $this->x);
7406 } else {
7407 return $this->x;
7408 }
7409 }
7410
7418 public function GetAbsX() {
7419 return $this->x;
7420 }
7421
7429 public function GetY() {
7430 return $this->y;
7431 }
7432
7442 public function SetX($x, $rtloff=false) {
7443 $x = floatval($x);
7444 if (!$rtloff AND $this->rtl) {
7445 if ($x >= 0) {
7446 $this->x = $this->w - $x;
7447 } else {
7448 $this->x = abs($x);
7449 }
7450 } else {
7451 if ($x >= 0) {
7452 $this->x = $x;
7453 } else {
7454 $this->x = $this->w + $x;
7455 }
7456 }
7457 if ($this->x < 0) {
7458 $this->x = 0;
7459 }
7460 if ($this->x > $this->w) {
7461 $this->x = $this->w;
7462 }
7463 }
7464
7475 public function SetY($y, $resetx=true, $rtloff=false) {
7476 $y = floatval($y);
7477 if ($resetx) {
7478 //reset x
7479 if (!$rtloff AND $this->rtl) {
7480 $this->x = $this->w - $this->rMargin;
7481 } else {
7482 $this->x = $this->lMargin;
7483 }
7484 }
7485 if ($y >= 0) {
7486 $this->y = $y;
7487 } else {
7488 $this->y = $this->h + $y;
7489 }
7490 if ($this->y < 0) {
7491 $this->y = 0;
7492 }
7493 if ($this->y > $this->h) {
7494 $this->y = $this->h;
7495 }
7496 }
7497
7508 public function SetXY($x, $y, $rtloff=false) {
7509 $this->SetY($y, false, $rtloff);
7510 $this->SetX($x, $rtloff);
7511 }
7512
7520 public function SetAbsX($x) {
7521 $this->x = floatval($x);
7522 }
7523
7531 public function SetAbsY($y) {
7532 $this->y = floatval($y);
7533 }
7534
7543 public function SetAbsXY($x, $y) {
7544 $this->SetAbsX($x);
7545 $this->SetAbsY($y);
7546 }
7547
7559 public function Output($name='doc.pdf', $dest='I') {
7560 //Output PDF to some destination
7561 //Finish document if necessary
7562 if ($this->state < 3) {
7563 $this->Close();
7564 }
7565 //Normalize parameters
7566 if (is_bool($dest)) {
7567 $dest = $dest ? 'D' : 'F';
7568 }
7569 $dest = strtoupper($dest);
7570 if ($dest[0] != 'F') {
7571 $name = preg_replace('/[\s]+/', '_', $name);
7572 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
7573 }
7574 if ($this->sign) {
7575 // *** apply digital signature to the document ***
7576 // get the document content
7577 $pdfdoc = $this->getBuffer();
7578 // remove last newline
7579 $pdfdoc = substr($pdfdoc, 0, -1);
7580 // remove filler space
7581 $byterange_string_len = strlen(TCPDF_STATIC::$byterange_string);
7582 // define the ByteRange
7583 $byte_range = array();
7584 $byte_range[0] = 0;
7585 $byte_range[1] = strpos($pdfdoc, TCPDF_STATIC::$byterange_string) + $byterange_string_len + 10;
7586 $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
7587 $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
7588 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
7589 // replace the ByteRange
7590 $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
7591 $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
7592 $pdfdoc = str_replace(TCPDF_STATIC::$byterange_string, $byterange, $pdfdoc);
7593 // write the document to a temporary folder
7594 $tempdoc = TCPDF_STATIC::getObjFilename('doc', $this->file_id);
7595 $f = TCPDF_STATIC::fopenLocal($tempdoc, 'wb');
7596 if (!$f) {
7597 $this->Error('Unable to create temporary file: '.$tempdoc);
7598 }
7599 $pdfdoc_length = strlen($pdfdoc);
7600 fwrite($f, $pdfdoc, $pdfdoc_length);
7601 fclose($f);
7602 // get digital signature via openssl library
7603 $tempsign = TCPDF_STATIC::getObjFilename('sig', $this->file_id);
7604 if (empty($this->signature_data['extracerts'])) {
7605 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
7606 } else {
7607 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED, $this->signature_data['extracerts']);
7608 }
7609 // read signature
7610 $signature = file_get_contents($tempsign);
7611 // extract signature
7612 $signature = substr($signature, $pdfdoc_length);
7613 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
7614 $tmparr = explode("\n\n", $signature);
7615 $signature = $tmparr[1];
7616 // decode signature
7617 $signature = base64_decode(trim($signature));
7618 // add TSA timestamp to signature
7619 $signature = $this->applyTSA($signature);
7620 // convert signature to hex
7621 $signature = current(unpack('H*', $signature));
7622 $signature = str_pad($signature, $this->signature_max_length, '0');
7623 // Add signature to the document
7624 $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
7625 $this->bufferlen = strlen($this->buffer);
7626 }
7627 switch($dest) {
7628 case 'I': {
7629 // Send PDF to the standard output
7630 if (ob_get_contents()) {
7631 $this->Error('Some data has already been output, can\'t send PDF file');
7632 }
7633 if (php_sapi_name() != 'cli') {
7634 // send output to a browser
7635 header('Content-Type: application/pdf');
7636 if (headers_sent()) {
7637 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7638 }
7639 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7640 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7641 header('Pragma: public');
7642 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7643 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7644 header('Content-Disposition: inline; filename="'.basename($name).'"');
7645 TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7646 } else {
7647 echo $this->getBuffer();
7648 }
7649 break;
7650 }
7651 case 'D': {
7652 // download PDF as file
7653 if (ob_get_contents()) {
7654 $this->Error('Some data has already been output, can\'t send PDF file');
7655 }
7656 header('Content-Description: File Transfer');
7657 if (headers_sent()) {
7658 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7659 }
7660 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7661 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7662 header('Pragma: public');
7663 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7664 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7665 // force download dialog
7666 if (strpos(php_sapi_name(), 'cgi') === false) {
7667 header('Content-Type: application/force-download');
7668 header('Content-Type: application/octet-stream', false);
7669 header('Content-Type: application/download', false);
7670 header('Content-Type: application/pdf', false);
7671 } else {
7672 header('Content-Type: application/pdf');
7673 }
7674 // use the Content-Disposition header to supply a recommended filename
7675 header('Content-Disposition: attachment; filename="'.basename($name).'"');
7676 header('Content-Transfer-Encoding: binary');
7677 TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7678 break;
7679 }
7680 case 'F':
7681 case 'FI':
7682 case 'FD': {
7683 // save PDF to a local file
7684 $f = TCPDF_STATIC::fopenLocal($name, 'wb');
7685 if (!$f) {
7686 $this->Error('Unable to create output file: '.$name);
7687 }
7688 fwrite($f, $this->getBuffer(), $this->bufferlen);
7689 fclose($f);
7690 if ($dest == 'FI') {
7691 // send headers to browser
7692 header('Content-Type: application/pdf');
7693 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7694 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7695 header('Pragma: public');
7696 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7697 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7698 header('Content-Disposition: inline; filename="'.basename($name).'"');
7699 TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7700 } elseif ($dest == 'FD') {
7701 // send headers to browser
7702 if (ob_get_contents()) {
7703 $this->Error('Some data has already been output, can\'t send PDF file');
7704 }
7705 header('Content-Description: File Transfer');
7706 if (headers_sent()) {
7707 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7708 }
7709 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7710 header('Pragma: public');
7711 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7712 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7713 // force download dialog
7714 if (strpos(php_sapi_name(), 'cgi') === false) {
7715 header('Content-Type: application/force-download');
7716 header('Content-Type: application/octet-stream', false);
7717 header('Content-Type: application/download', false);
7718 header('Content-Type: application/pdf', false);
7719 } else {
7720 header('Content-Type: application/pdf');
7721 }
7722 // use the Content-Disposition header to supply a recommended filename
7723 header('Content-Disposition: attachment; filename="'.basename($name).'"');
7724 header('Content-Transfer-Encoding: binary');
7725 TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7726 }
7727 break;
7728 }
7729 case 'E': {
7730 // return PDF as base64 mime multi-part email attachment (RFC 2045)
7731 $retval = 'Content-Type: application/pdf;'."\r\n";
7732 $retval .= ' name="'.$name.'"'."\r\n";
7733 $retval .= 'Content-Transfer-Encoding: base64'."\r\n";
7734 $retval .= 'Content-Disposition: attachment;'."\r\n";
7735 $retval .= ' filename="'.$name.'"'."\r\n\r\n";
7736 $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
7737 return $retval;
7738 }
7739 case 'S': {
7740 // returns PDF as a string
7741 return $this->getBuffer();
7742 }
7743 default: {
7744 $this->Error('Incorrect output destination: '.$dest);
7745 }
7746 }
7747 return '';
7748 }
7749
7757 public function _destroy($destroyall=false, $preserve_objcopy=false) {
7758 if ($destroyall AND !$preserve_objcopy) {
7759 // remove all temporary files
7760 $tmpfiles = glob(K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_*');
7761 if (!empty($tmpfiles)) {
7762 array_map('unlink', $tmpfiles);
7763 }
7764 }
7765 $preserve = array(
7766 'file_id',
7767 'internal_encoding',
7768 'state',
7769 'bufferlen',
7770 'buffer',
7771 'cached_files',
7772 'sign',
7773 'signature_data',
7774 'signature_max_length',
7775 'byterange_string',
7776 'tsa_timestamp',
7777 'tsa_data'
7778 );
7779 foreach (array_keys(get_object_vars($this)) as $val) {
7780 if ($destroyall OR !in_array($val, $preserve)) {
7781 if ((!$preserve_objcopy OR ($val != 'objcopy')) AND ($val != 'file_id') AND isset($this->$val)) {
7782 unset($this->$val);
7783 }
7784 }
7785 }
7786 }
7787
7792 protected function _dochecks() {
7793 //Check for locale-related bug
7794 if (1.1 == 1) {
7795 $this->Error('Don\'t alter the locale before including class file');
7796 }
7797 //Check for decimal separator
7798 if (sprintf('%.1F', 1.0) != '1.0') {
7799 setlocale(LC_NUMERIC, 'C');
7800 }
7801 }
7802
7809 protected function getInternalPageNumberAliases($a= '') {
7810 $alias = array();
7811 // build array of Unicode + ASCII variants (the order is important)
7812 $alias = array('u' => array(), 'a' => array());
7813 $u = '{'.$a.'}';
7814 $alias['u'][] = TCPDF_STATIC::_escape($u);
7815 if ($this->isunicode) {
7816 $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($u, $this->isunicode, $this->CurrentFont));
7817 $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($u, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7818 $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($a, $this->isunicode, $this->CurrentFont));
7819 $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($a, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7820 }
7821 $alias['a'][] = TCPDF_STATIC::_escape($a);
7822 return $alias;
7823 }
7824
7830 protected function getAllInternalPageNumberAliases() {
7832 $pnalias = array();
7833 foreach($basic_alias as $k => $a) {
7834 $pnalias[$k] = $this->getInternalPageNumberAliases($a);
7835 }
7836 return $pnalias;
7837 }
7838
7848 protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
7849 foreach ($aliases as $type => $alias) {
7850 foreach ($alias as $a) {
7851 // find position of compensation factor
7852 $startnum = (strpos($a, ':') + 1);
7853 $a = substr($a, 0, $startnum);
7854 if (($pos = strpos($page, $a)) !== false) {
7855 // end of alias
7856 $endnum = strpos($page, '}', $pos);
7857 // string to be replaced
7858 $aa = substr($page, $pos, ($endnum - $pos + 1));
7859 // get compensation factor
7860 $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum));
7861 $ratio = preg_replace('/[^0-9\.]/', '', $ratio);
7862 $ratio = floatval($ratio);
7863 if ($type == 'u') {
7864 $chrdiff = floor(($diff + 12) * $ratio);
7865 $shift = str_repeat(' ', $chrdiff);
7866 $shift = TCPDF_FONTS::UTF8ToUTF16BE($shift, false, $this->isunicode, $this->CurrentFont);
7867 } else {
7868 $chrdiff = floor(($diff + 11) * $ratio);
7869 $shift = str_repeat(' ', $chrdiff);
7870 }
7871 $page = str_replace($aa, $shift, $page);
7872 }
7873 }
7874 }
7875 return $page;
7876 }
7877
7883 protected function setPageBoxTypes($boxes) {
7884 $this->page_boxes = array();
7885 foreach ($boxes as $box) {
7886 if (in_array($box, TCPDF_STATIC::$pageboxes)) {
7887 $this->page_boxes[] = $box;
7888 }
7889 }
7890 }
7891
7896 protected function _putpages() {
7897 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
7898 // get internal aliases for page numbers
7899 $pnalias = $this->getAllInternalPageNumberAliases();
7900 $num_pages = $this->numpages;
7901 $ptpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $num_pages - 1));
7902 $ptpu = TCPDF_FONTS::UTF8ToUTF16BE($ptpa, false, $this->isunicode, $this->CurrentFont);
7903 $ptp_num_chars = $this->GetNumChars($ptpa);
7904 $pagegroupnum = 0;
7905 $groupnum = 0;
7906 $ptgu = 1;
7907 $ptga = 1;
7908 $ptg_num_chars = 1;
7909 for ($n = 1; $n <= $num_pages; ++$n) {
7910 // get current page
7911 $temppage = $this->getPageBuffer($n);
7912 $pagelen = strlen($temppage);
7913 // set replacements for total pages number
7914 $pnpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $n - 1));
7915 $pnpu = TCPDF_FONTS::UTF8ToUTF16BE($pnpa, false, $this->isunicode, $this->CurrentFont);
7916 $pnp_num_chars = $this->GetNumChars($pnpa);
7917 $pdiff = 0; // difference used for right shift alignment of page numbers
7918 $gdiff = 0; // difference used for right shift alignment of page group numbers
7919 if (!empty($this->pagegroups)) {
7920 if (isset($this->newpagegroup[$n])) {
7921 $pagegroupnum = 0;
7922 ++$groupnum;
7923 $ptga = TCPDF_STATIC::formatPageNumber($this->pagegroups[$groupnum]);
7924 $ptgu = TCPDF_FONTS::UTF8ToUTF16BE($ptga, false, $this->isunicode, $this->CurrentFont);
7925 $ptg_num_chars = $this->GetNumChars($ptga);
7926 }
7927 ++$pagegroupnum;
7928 $pnga = TCPDF_STATIC::formatPageNumber($pagegroupnum);
7929 $pngu = TCPDF_FONTS::UTF8ToUTF16BE($pnga, false, $this->isunicode, $this->CurrentFont);
7930 $png_num_chars = $this->GetNumChars($pnga);
7931 // replace page numbers
7932 $replace = array();
7933 $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
7934 $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
7935 $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
7936 $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
7937 list($temppage, $gdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $gdiff);
7938 }
7939 // replace page numbers
7940 $replace = array();
7941 $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
7942 $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
7943 $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
7944 $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
7945 list($temppage, $pdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $pdiff);
7946 // replace right shift alias
7947 $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
7948 // replace EPS marker
7949 $temppage = str_replace($this->epsmarker, '', $temppage);
7950 //Page
7951 $this->page_obj_id[$n] = $this->_newobj();
7952 $out = '<<';
7953 $out .= ' /Type /Page';
7954 $out .= ' /Parent 1 0 R';
7955 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
7956 $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp);
7957 }
7958 $out .= ' /Resources 2 0 R';
7959 foreach ($this->page_boxes as $box) {
7960 $out .= ' /'.$box;
7961 $out .= sprintf(' [%F %F %F %F]', $this->pagedim[$n][$box]['llx'], $this->pagedim[$n][$box]['lly'], $this->pagedim[$n][$box]['urx'], $this->pagedim[$n][$box]['ury']);
7962 }
7963 if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
7964 $out .= ' /BoxColorInfo <<';
7965 foreach ($this->page_boxes as $box) {
7966 if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
7967 $out .= ' /'.$box.' <<';
7968 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
7969 $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
7970 $out .= ' /C [';
7971 $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
7972 $out .= ' ]';
7973 }
7974 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
7975 $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
7976 }
7977 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
7978 $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
7979 }
7980 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
7981 $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
7982 $out .= ' /D [';
7983 foreach ($dashes as $dash) {
7984 $out .= sprintf(' %F', ($dash * $this->k));
7985 }
7986 $out .= ' ]';
7987 }
7988 $out .= ' >>';
7989 }
7990 }
7991 $out .= ' >>';
7992 }
7993 $out .= ' /Contents '.($this->n + 1).' 0 R';
7994 $out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
7995 if (!$this->pdfa_mode) {
7996 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
7997 }
7998 if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
7999 // page transitions
8000 if (isset($this->pagedim[$n]['trans']['Dur'])) {
8001 $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
8002 }
8003 $out .= ' /Trans <<';
8004 $out .= ' /Type /Trans';
8005 if (isset($this->pagedim[$n]['trans']['S'])) {
8006 $out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
8007 }
8008 if (isset($this->pagedim[$n]['trans']['D'])) {
8009 $out .= ' /D '.$this->pagedim[$n]['trans']['D'];
8010 }
8011 if (isset($this->pagedim[$n]['trans']['Dm'])) {
8012 $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
8013 }
8014 if (isset($this->pagedim[$n]['trans']['M'])) {
8015 $out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
8016 }
8017 if (isset($this->pagedim[$n]['trans']['Di'])) {
8018 $out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
8019 }
8020 if (isset($this->pagedim[$n]['trans']['SS'])) {
8021 $out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
8022 }
8023 if (isset($this->pagedim[$n]['trans']['B'])) {
8024 $out .= ' /B '.$this->pagedim[$n]['trans']['B'];
8025 }
8026 $out .= ' >>';
8027 }
8028 $out .= $this->_getannotsrefs($n);
8029 $out .= ' /PZ '.$this->pagedim[$n]['PZ'];
8030 $out .= ' >>';
8031 $out .= "\n".'endobj';
8032 $this->_out($out);
8033 //Page content
8034 $p = ($this->compress) ? gzcompress($temppage) : $temppage;
8035 $this->_newobj();
8036 $p = $this->_getrawstream($p);
8037 $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
8038 }
8039 //Pages root
8040 $out = $this->_getobj(1)."\n";
8041 $out .= '<< /Type /Pages /Kids [';
8042 foreach($this->page_obj_id as $page_obj) {
8043 $out .= ' '.$page_obj.' 0 R';
8044 }
8045 $out .= ' ] /Count '.$num_pages.' >>';
8046 $out .= "\n".'endobj';
8047 $this->_out($out);
8048 }
8049
8058 protected function _getannotsrefs($n) {
8059 if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
8060 return '';
8061 }
8062 $out = ' /Annots [';
8063 if (isset($this->PageAnnots[$n])) {
8064 foreach ($this->PageAnnots[$n] as $key => $val) {
8065 if (!in_array($val['n'], $this->radio_groups)) {
8066 $out .= ' '.$val['n'].' 0 R';
8067 }
8068 }
8069 // add radiobutton groups
8070 if (isset($this->radiobutton_groups[$n])) {
8071 foreach ($this->radiobutton_groups[$n] as $key => $data) {
8072 if (isset($data['n'])) {
8073 $out .= ' '.$data['n'].' 0 R';
8074 }
8075 }
8076 }
8077 }
8078 if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
8079 // set reference for signature object
8080 $out .= ' '.$this->sig_obj_id.' 0 R';
8081 }
8082 if (!empty($this->empty_signature_appearance)) {
8083 foreach ($this->empty_signature_appearance as $esa) {
8084 if ($esa['page'] == $n) {
8085 // set reference for empty signature objects
8086 $out .= ' '.$esa['objid'].' 0 R';
8087 }
8088 }
8089 }
8090 $out .= ' ]';
8091 return $out;
8092 }
8093
8102 protected function _putannotsobjs() {
8103 // reset object counter
8104 for ($n=1; $n <= $this->numpages; ++$n) {
8105 if (isset($this->PageAnnots[$n])) {
8106 // set page annotations
8107 foreach ($this->PageAnnots[$n] as $key => $pl) {
8108 $annot_obj_id = $this->PageAnnots[$n][$key]['n'];
8109 // create annotation object for grouping radiobuttons
8110 if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
8111 $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n'];
8112 $annots = '<<';
8113 $annots .= ' /Type /Annot';
8114 $annots .= ' /Subtype /Widget';
8115 $annots .= ' /Rect [0 0 0 0]';
8116 if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) {
8117 // read only
8118 $annots .= ' /F 68';
8119 $annots .= ' /Ff 49153';
8120 } else {
8121 $annots .= ' /F 4'; // default print for PDF/A
8122 $annots .= ' /Ff 49152';
8123 }
8124 $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
8125 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8126 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id);
8127 }
8128 $annots .= ' /FT /Btn';
8129 $annots .= ' /Kids [';
8130 $defval = '';
8131 foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) {
8132 if (isset($data['kid'])) {
8133 $annots .= ' '.$data['kid'].' 0 R';
8134 if ($data['def'] !== 'Off') {
8135 $defval = $data['def'];
8136 }
8137 }
8138 }
8139 $annots .= ' ]';
8140 if (!empty($defval)) {
8141 $annots .= ' /V /'.$defval;
8142 }
8143 $annots .= ' >>';
8144 $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
8145 $this->form_obj_id[] = $radio_button_obj_id;
8146 // store object id to be used on Parent entry of Kids
8147 $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id;
8148 }
8149 $formfield = false;
8150 $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
8151 $a = $pl['x'] * $this->k;
8152 $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k);
8153 $c = $pl['w'] * $this->k;
8154 $d = $pl['h'] * $this->k;
8155 $rect = sprintf('%F %F %F %F', $a, $b, $a+$c, $b+$d);
8156 // create new annotation object
8157 $annots = '<</Type /Annot';
8158 $annots .= ' /Subtype /'.$pl['opt']['subtype'];
8159 $annots .= ' /Rect ['.$rect.']';
8160 $ft = array('Btn', 'Tx', 'Ch', 'Sig');
8161 if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
8162 $annots .= ' /FT /'.$pl['opt']['ft'];
8163 $formfield = true;
8164 }
8165 $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
8166 $annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
8167 $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
8168 $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp);
8169 if (isset($pl['opt']['f'])) {
8170 $fval = 0;
8171 if (is_array($pl['opt']['f'])) {
8172 foreach ($pl['opt']['f'] as $f) {
8173 switch (strtolower($f)) {
8174 case 'invisible': {
8175 $fval += 1 << 0;
8176 break;
8177 }
8178 case 'hidden': {
8179 $fval += 1 << 1;
8180 break;
8181 }
8182 case 'print': {
8183 $fval += 1 << 2;
8184 break;
8185 }
8186 case 'nozoom': {
8187 $fval += 1 << 3;
8188 break;
8189 }
8190 case 'norotate': {
8191 $fval += 1 << 4;
8192 break;
8193 }
8194 case 'noview': {
8195 $fval += 1 << 5;
8196 break;
8197 }
8198 case 'readonly': {
8199 $fval += 1 << 6;
8200 break;
8201 }
8202 case 'locked': {
8203 $fval += 1 << 8;
8204 break;
8205 }
8206 case 'togglenoview': {
8207 $fval += 1 << 9;
8208 break;
8209 }
8210 case 'lockedcontents': {
8211 $fval += 1 << 10;
8212 break;
8213 }
8214 default: {
8215 break;
8216 }
8217 }
8218 }
8219 } else {
8220 $fval = intval($pl['opt']['f']);
8221 }
8222 } else {
8223 $fval = 4;
8224 }
8225 if ($this->pdfa_mode) {
8226 // force print flag for PDF/A mode
8227 $fval |= 4;
8228 }
8229 $annots .= ' /F '.intval($fval);
8230 if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
8231 $annots .= ' /AS /'.$pl['opt']['as'];
8232 }
8233 if (isset($pl['opt']['ap'])) {
8234 // appearance stream
8235 $annots .= ' /AP <<';
8236 if (is_array($pl['opt']['ap'])) {
8237 foreach ($pl['opt']['ap'] as $apmode => $apdef) {
8238 // $apmode can be: n = normal; r = rollover; d = down;
8239 $annots .= ' /'.strtoupper($apmode);
8240 if (is_array($apdef)) {
8241 $annots .= ' <<';
8242 foreach ($apdef as $apstate => $stream) {
8243 // reference to XObject that define the appearance for this mode-state
8244 $apsobjid = $this->_putAPXObject($c, $d, $stream);
8245 $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
8246 }
8247 $annots .= ' >>';
8248 } else {
8249 // reference to XObject that define the appearance for this mode
8250 $apsobjid = $this->_putAPXObject($c, $d, $apdef);
8251 $annots .= ' '.$apsobjid.' 0 R';
8252 }
8253 }
8254 } else {
8255 $annots .= $pl['opt']['ap'];
8256 }
8257 $annots .= ' >>';
8258 }
8259 if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
8260 $annots .= ' /BS <<';
8261 $annots .= ' /Type /Border';
8262 if (isset($pl['opt']['bs']['w'])) {
8263 $annots .= ' /W '.intval($pl['opt']['bs']['w']);
8264 }
8265 $bstyles = array('S', 'D', 'B', 'I', 'U');
8266 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
8267 $annots .= ' /S /'.$pl['opt']['bs']['s'];
8268 }
8269 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
8270 $annots .= ' /D [';
8271 foreach ($pl['opt']['bs']['d'] as $cord) {
8272 $annots .= ' '.intval($cord);
8273 }
8274 $annots .= ']';
8275 }
8276 $annots .= ' >>';
8277 } else {
8278 $annots .= ' /Border [';
8279 if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
8280 $annots .= intval($pl['opt']['border'][0]).' ';
8281 $annots .= intval($pl['opt']['border'][1]).' ';
8282 $annots .= intval($pl['opt']['border'][2]);
8283 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
8284 $annots .= ' [';
8285 foreach ($pl['opt']['border'][3] as $dash) {
8286 $annots .= intval($dash).' ';
8287 }
8288 $annots .= ']';
8289 }
8290 } else {
8291 $annots .= '0 0 0';
8292 }
8293 $annots .= ']';
8294 }
8295 if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
8296 $annots .= ' /BE <<';
8297 $bstyles = array('S', 'C');
8298 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) {
8299 $annots .= ' /S /'.$pl['opt']['bs']['s'];
8300 } else {
8301 $annots .= ' /S /S';
8302 }
8303 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
8304 $annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']);
8305 }
8306 $annots .= '>>';
8307 }
8308 if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
8309 $annots .= ' /C '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['c']);
8310 }
8311 //$annots .= ' /StructParent ';
8312 //$annots .= ' /OC ';
8313 $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
8314 if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
8315 // this is a markup type
8316 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8317 $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
8318 }
8319 //$annots .= ' /Popup ';
8320 if (isset($pl['opt']['ca'])) {
8321 $annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca']));
8322 }
8323 if (isset($pl['opt']['rc'])) {
8324 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8325 }
8326 $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp);
8327 //$annots .= ' /IRT ';
8328 if (isset($pl['opt']['subj'])) {
8329 $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
8330 }
8331 //$annots .= ' /RT ';
8332 //$annots .= ' /IT ';
8333 //$annots .= ' /ExData ';
8334 }
8335 $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
8336 // Annotation types
8337 switch (strtolower($pl['opt']['subtype'])) {
8338 case 'text': {
8339 if (isset($pl['opt']['open'])) {
8340 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
8341 }
8342 $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
8343 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8344 $annots .= ' /Name /'.$pl['opt']['name'];
8345 } else {
8346 $annots .= ' /Name /Note';
8347 }
8348 $statemodels = array('Marked', 'Review');
8349 if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
8350 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8351 } else {
8352 $pl['opt']['statemodel'] = 'Marked';
8353 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8354 }
8355 if ($pl['opt']['statemodel'] == 'Marked') {
8356 $states = array('Accepted', 'Unmarked');
8357 } else {
8358 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
8359 }
8360 if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
8361 $annots .= ' /State /'.$pl['opt']['state'];
8362 } else {
8363 if ($pl['opt']['statemodel'] == 'Marked') {
8364 $annots .= ' /State /Unmarked';
8365 } else {
8366 $annots .= ' /State /None';
8367 }
8368 }
8369 break;
8370 }
8371 case 'link': {
8372 if (is_string($pl['txt'])) {
8373 if ($pl['txt'][0] == '#') {
8374 // internal destination
8375 $annots .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($pl['txt'], 1));
8376 } elseif ($pl['txt'][0] == '%') {
8377 // embedded PDF file
8378 $filename = basename(substr($pl['txt'], 1));
8379 $annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
8380 } elseif ($pl['txt'][0] == '*') {
8381 // embedded generic file
8382 $filename = basename(substr($pl['txt'], 1));
8383 $jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});';
8384 $annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>';
8385 } else {
8386 $parsedUrl = parse_url($pl['txt']);
8387 if (empty($parsedUrl['scheme']) AND (strtolower(substr($parsedUrl['path'], -4)) == '.pdf')) {
8388 // relative link to a PDF file
8389 $dest = '[0 /Fit]'; // default page 0
8390 if (!empty($parsedUrl['fragment'])) {
8391 // check for named destination
8392 $tmp = explode('=', $parsedUrl['fragment']);
8393 $dest = '('.((count($tmp) == 2) ? $tmp[1] : $tmp[0]).')';
8394 }
8395 $annots .= ' /A <</S /GoToR /D '.$dest.' /F '.$this->_datastring($this->unhtmlentities($parsedUrl['path']), $annot_obj_id).' /NewWindow true>>';
8396 } else {
8397 // external URI link
8398 $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
8399 }
8400 }
8401 } elseif (isset($this->links[$pl['txt']])) {
8402 // internal link ID
8403 $l = $this->links[$pl['txt']];
8404 if (isset($this->page_obj_id[($l['p'])])) {
8405 $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l['p'])], ($this->pagedim[$l['p']]['h'] - ($l['y'] * $this->k)));
8406 }
8407 }
8408 $hmodes = array('N', 'I', 'O', 'P');
8409 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
8410 $annots .= ' /H /'.$pl['opt']['h'];
8411 } else {
8412 $annots .= ' /H /I';
8413 }
8414 //$annots .= ' /PA ';
8415 //$annots .= ' /Quadpoints ';
8416 break;
8417 }
8418 case 'freetext': {
8419 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8420 $annots .= ' /DA ('.$pl['opt']['da'].')';
8421 }
8422 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8423 $annots .= ' /Q '.intval($pl['opt']['q']);
8424 }
8425 if (isset($pl['opt']['rc'])) {
8426 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8427 }
8428 if (isset($pl['opt']['ds'])) {
8429 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
8430 }
8431 if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
8432 $annots .= ' /CL [';
8433 foreach ($pl['opt']['cl'] as $cl) {
8434 $annots .= sprintf('%F ', $cl * $this->k);
8435 }
8436 $annots .= ']';
8437 }
8438 $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
8439 if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
8440 $annots .= ' /IT /'.$pl['opt']['it'];
8441 }
8442 if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
8443 $l = $pl['opt']['rd'][0] * $this->k;
8444 $r = $pl['opt']['rd'][1] * $this->k;
8445 $t = $pl['opt']['rd'][2] * $this->k;
8446 $b = $pl['opt']['rd'][3] * $this->k;
8447 $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']';
8448 }
8449 if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
8450 $annots .= ' /LE /'.$pl['opt']['le'];
8451 }
8452 break;
8453 }
8454 case 'line': {
8455 break;
8456 }
8457 case 'square': {
8458 break;
8459 }
8460 case 'circle': {
8461 break;
8462 }
8463 case 'polygon': {
8464 break;
8465 }
8466 case 'polyline': {
8467 break;
8468 }
8469 case 'highlight': {
8470 break;
8471 }
8472 case 'underline': {
8473 break;
8474 }
8475 case 'squiggly': {
8476 break;
8477 }
8478 case 'strikeout': {
8479 break;
8480 }
8481 case 'stamp': {
8482 break;
8483 }
8484 case 'caret': {
8485 break;
8486 }
8487 case 'ink': {
8488 break;
8489 }
8490 case 'popup': {
8491 break;
8492 }
8493 case 'fileattachment': {
8494 if ($this->pdfa_mode) {
8495 // embedded files are not allowed in PDF/A mode
8496 break;
8497 }
8498 if (!isset($pl['opt']['fs'])) {
8499 break;
8500 }
8501 $filename = basename($pl['opt']['fs']);
8502 if (isset($this->embeddedfiles[$filename]['f'])) {
8503 $annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R';
8504 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
8505 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8506 $annots .= ' /Name /'.$pl['opt']['name'];
8507 } else {
8508 $annots .= ' /Name /PushPin';
8509 }
8510 // index (zero-based) of the annotation in the Annots array of this page
8511 $this->embeddedfiles[$filename]['a'] = $key;
8512 }
8513 break;
8514 }
8515 case 'sound': {
8516 if (!isset($pl['opt']['fs'])) {
8517 break;
8518 }
8519 $filename = basename($pl['opt']['fs']);
8520 if (isset($this->embeddedfiles[$filename]['f'])) {
8521 // ... TO BE COMPLETED ...
8522 // /R /C /B /E /CO /CP
8523 $annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R';
8524 $iconsapp = array('Speaker', 'Mic');
8525 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8526 $annots .= ' /Name /'.$pl['opt']['name'];
8527 } else {
8528 $annots .= ' /Name /Speaker';
8529 }
8530 }
8531 break;
8532 }
8533 case 'movie': {
8534 break;
8535 }
8536 case 'widget': {
8537 $hmode = array('N', 'I', 'O', 'P', 'T');
8538 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
8539 $annots .= ' /H /'.$pl['opt']['h'];
8540 }
8541 if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
8542 $annots .= ' /MK <<';
8543 if (isset($pl['opt']['mk']['r'])) {
8544 $annots .= ' /R '.$pl['opt']['mk']['r'];
8545 }
8546 if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
8547 $annots .= ' /BC '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bc']);
8548 }
8549 if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
8550 $annots .= ' /BG '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bg']);
8551 }
8552 if (isset($pl['opt']['mk']['ca'])) {
8553 $annots .= ' /CA '.$pl['opt']['mk']['ca'];
8554 }
8555 if (isset($pl['opt']['mk']['rc'])) {
8556 $annots .= ' /RC '.$pl['opt']['mk']['rc'];
8557 }
8558 if (isset($pl['opt']['mk']['ac'])) {
8559 $annots .= ' /AC '.$pl['opt']['mk']['ac'];
8560 }
8561 if (isset($pl['opt']['mk']['i'])) {
8562 $info = $this->getImageBuffer($pl['opt']['mk']['i']);
8563 if ($info !== false) {
8564 $annots .= ' /I '.$info['n'].' 0 R';
8565 }
8566 }
8567 if (isset($pl['opt']['mk']['ri'])) {
8568 $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
8569 if ($info !== false) {
8570 $annots .= ' /RI '.$info['n'].' 0 R';
8571 }
8572 }
8573 if (isset($pl['opt']['mk']['ix'])) {
8574 $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
8575 if ($info !== false) {
8576 $annots .= ' /IX '.$info['n'].' 0 R';
8577 }
8578 }
8579 if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
8580 $annots .= ' /IF <<';
8581 $if_sw = array('A', 'B', 'S', 'N');
8582 if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
8583 $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
8584 }
8585 $if_s = array('A', 'P');
8586 if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
8587 $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
8588 }
8589 if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
8590 $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
8591 }
8592 if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
8593 $annots .= ' /FB true';
8594 }
8595 $annots .= '>>';
8596 }
8597 if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
8598 $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
8599 }
8600 $annots .= '>>';
8601 } // end MK
8602 // --- Entries for field dictionaries ---
8603 if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
8604 // set parent
8605 $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
8606 }
8607 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8608 $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
8609 }
8610 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8611 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
8612 }
8613 if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
8614 $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
8615 }
8616 if (isset($pl['opt']['ff'])) {
8617 if (is_array($pl['opt']['ff'])) {
8618 // array of bit settings
8619 $flag = 0;
8620 foreach($pl['opt']['ff'] as $val) {
8621 $flag += 1 << ($val - 1);
8622 }
8623 } else {
8624 $flag = intval($pl['opt']['ff']);
8625 }
8626 $annots .= ' /Ff '.$flag;
8627 }
8628 if (isset($pl['opt']['maxlen'])) {
8629 $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
8630 }
8631 if (isset($pl['opt']['v'])) {
8632 $annots .= ' /V';
8633 if (is_array($pl['opt']['v'])) {
8634 foreach ($pl['opt']['v'] AS $optval) {
8635 if (is_float($optval)) {
8636 $optval = sprintf('%F', $optval);
8637 }
8638 $annots .= ' '.$optval;
8639 }
8640 } else {
8641 $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
8642 }
8643 }
8644 if (isset($pl['opt']['dv'])) {
8645 $annots .= ' /DV';
8646 if (is_array($pl['opt']['dv'])) {
8647 foreach ($pl['opt']['dv'] AS $optval) {
8648 if (is_float($optval)) {
8649 $optval = sprintf('%F', $optval);
8650 }
8651 $annots .= ' '.$optval;
8652 }
8653 } else {
8654 $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
8655 }
8656 }
8657 if (isset($pl['opt']['rv'])) {
8658 $annots .= ' /RV';
8659 if (is_array($pl['opt']['rv'])) {
8660 foreach ($pl['opt']['rv'] AS $optval) {
8661 if (is_float($optval)) {
8662 $optval = sprintf('%F', $optval);
8663 }
8664 $annots .= ' '.$optval;
8665 }
8666 } else {
8667 $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
8668 }
8669 }
8670 if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
8671 $annots .= ' /A << '.$pl['opt']['a'].' >>';
8672 }
8673 if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
8674 $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
8675 }
8676 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8677 $annots .= ' /DA ('.$pl['opt']['da'].')';
8678 }
8679 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8680 $annots .= ' /Q '.intval($pl['opt']['q']);
8681 }
8682 if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
8683 $annots .= ' /Opt [';
8684 foreach($pl['opt']['opt'] AS $copt) {
8685 if (is_array($copt)) {
8686 $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
8687 } else {
8688 $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
8689 }
8690 }
8691 $annots .= ']';
8692 }
8693 if (isset($pl['opt']['ti'])) {
8694 $annots .= ' /TI '.intval($pl['opt']['ti']);
8695 }
8696 if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
8697 $annots .= ' /I [';
8698 foreach($pl['opt']['i'] AS $copt) {
8699 $annots .= intval($copt).' ';
8700 }
8701 $annots .= ']';
8702 }
8703 break;
8704 }
8705 case 'screen': {
8706 break;
8707 }
8708 case 'printermark': {
8709 break;
8710 }
8711 case 'trapnet': {
8712 break;
8713 }
8714 case 'watermark': {
8715 break;
8716 }
8717 case '3d': {
8718 break;
8719 }
8720 default: {
8721 break;
8722 }
8723 }
8724 $annots .= '>>';
8725 // create new annotation object
8726 $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
8727 if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
8728 // store reference of form object
8729 $this->form_obj_id[] = $annot_obj_id;
8730 }
8731 }
8732 }
8733 } // end for each page
8734 }
8735
8745 protected function _putAPXObject($w=0, $h=0, $stream='') {
8746 $stream = trim($stream);
8747 $out = $this->_getobj()."\n";
8748 $this->xobjects['AX'.$this->n] = array('n' => $this->n);
8749 $out .= '<<';
8750 $out .= ' /Type /XObject';
8751 $out .= ' /Subtype /Form';
8752 $out .= ' /FormType 1';
8753 if ($this->compress) {
8754 $stream = gzcompress($stream);
8755 $out .= ' /Filter /FlateDecode';
8756 }
8757 $rect = sprintf('%F %F', $w, $h);
8758 $out .= ' /BBox [0 0 '.$rect.']';
8759 $out .= ' /Matrix [1 0 0 1 0 0]';
8760 $out .= ' /Resources 2 0 R';
8761 $stream = $this->_getrawstream($stream);
8762 $out .= ' /Length '.strlen($stream);
8763 $out .= ' >>';
8764 $out .= ' stream'."\n".$stream."\n".'endstream';
8765 $out .= "\n".'endobj';
8766 $this->_out($out);
8767 return $this->n;
8768 }
8769
8775 protected function _putfonts() {
8776 $nf = $this->n;
8777 foreach ($this->diffs as $diff) {
8778 //Encodings
8779 $this->_newobj();
8780 $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
8781 }
8782 $mqr = TCPDF_STATIC::get_mqr();
8783 TCPDF_STATIC::set_mqr(false);
8784 foreach ($this->FontFiles as $file => $info) {
8785 // search and get font file to embedd
8786 $fontfile = TCPDF_FONTS::getFontFullPath($file, $info['fontdir']);
8787 if (!TCPDF_STATIC::empty_string($fontfile)) {
8788 $font = file_get_contents($fontfile);
8789 $compressed = (substr($file, -2) == '.z');
8790 if ((!$compressed) AND (isset($info['length2']))) {
8791 $header = (ord($font[0]) == 128);
8792 if ($header) {
8793 // strip first binary header
8794 $font = substr($font, 6);
8795 }
8796 if ($header AND (ord($font[$info['length1']]) == 128)) {
8797 // strip second binary header
8798 $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
8799 }
8800 } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
8801 if ($compressed) {
8802 // uncompress font
8803 $font = gzuncompress($font);
8804 }
8805 // merge subset characters
8806 $subsetchars = array(); // used chars
8807 foreach ($info['fontkeys'] as $fontkey) {
8808 $fontinfo = $this->getFontBuffer($fontkey);
8809 $subsetchars += $fontinfo['subsetchars'];
8810 }
8811 // rebuild a font subset
8812 $font = TCPDF_FONTS::_getTrueTypeFontSubset($font, $subsetchars);
8813 // calculate new font length
8814 $info['length1'] = strlen($font);
8815 if ($compressed) {
8816 // recompress font
8817 $font = gzcompress($font);
8818 }
8819 }
8820 $this->_newobj();
8821 $this->FontFiles[$file]['n'] = $this->n;
8822 $stream = $this->_getrawstream($font);
8823 $out = '<< /Length '.strlen($stream);
8824 if ($compressed) {
8825 $out .= ' /Filter /FlateDecode';
8826 }
8827 $out .= ' /Length1 '.$info['length1'];
8828 if (isset($info['length2'])) {
8829 $out .= ' /Length2 '.$info['length2'].' /Length3 0';
8830 }
8831 $out .= ' >>';
8832 $out .= ' stream'."\n".$stream."\n".'endstream';
8833 $out .= "\n".'endobj';
8834 $this->_out($out);
8835 }
8836 }
8838 foreach ($this->fontkeys as $k) {
8839 //Font objects
8840 $font = $this->getFontBuffer($k);
8841 $type = $font['type'];
8842 $name = $font['name'];
8843 if ($type == 'core') {
8844 // standard core font
8845 $out = $this->_getobj($this->font_obj_ids[$k])."\n";
8846 $out .= '<</Type /Font';
8847 $out .= ' /Subtype /Type1';
8848 $out .= ' /BaseFont /'.$name;
8849 $out .= ' /Name /F'.$font['i'];
8850 if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
8851 $out .= ' /Encoding /WinAnsiEncoding';
8852 }
8853 if ($k == 'helvetica') {
8854 // add default font for annotations
8855 $this->annotation_fonts[$k] = $font['i'];
8856 }
8857 $out .= ' >>';
8858 $out .= "\n".'endobj';
8859 $this->_out($out);
8860 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
8861 // additional Type1 or TrueType font
8862 $out = $this->_getobj($this->font_obj_ids[$k])."\n";
8863 $out .= '<</Type /Font';
8864 $out .= ' /Subtype /'.$type;
8865 $out .= ' /BaseFont /'.$name;
8866 $out .= ' /Name /F'.$font['i'];
8867 $out .= ' /FirstChar 32 /LastChar 255';
8868 $out .= ' /Widths '.($this->n + 1).' 0 R';
8869 $out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
8870 if ($font['enc']) {
8871 if (isset($font['diff'])) {
8872 $out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
8873 } else {
8874 $out .= ' /Encoding /WinAnsiEncoding';
8875 }
8876 }
8877 $out .= ' >>';
8878 $out .= "\n".'endobj';
8879 $this->_out($out);
8880 // Widths
8881 $this->_newobj();
8882 $s = '[';
8883 for ($i = 32; $i < 256; ++$i) {
8884 if (isset($font['cw'][$i])) {
8885 $s .= $font['cw'][$i].' ';
8886 } else {
8887 $s .= $font['dw'].' ';
8888 }
8889 }
8890 $s .= ']';
8891 $s .= "\n".'endobj';
8892 $this->_out($s);
8893 //Descriptor
8894 $this->_newobj();
8895 $s = '<</Type /FontDescriptor /FontName /'.$name;
8896 foreach ($font['desc'] as $fdk => $fdv) {
8897 if (is_float($fdv)) {
8898 $fdv = sprintf('%F', $fdv);
8899 }
8900 $s .= ' /'.$fdk.' '.$fdv.'';
8901 }
8902 if (!TCPDF_STATIC::empty_string($font['file'])) {
8903 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
8904 }
8905 $s .= '>>';
8906 $s .= "\n".'endobj';
8907 $this->_out($s);
8908 } else {
8909 // additional types
8910 $mtd = '_put'.strtolower($type);
8911 if (!method_exists($this, $mtd)) {
8912 $this->Error('Unsupported font type: '.$type);
8913 }
8914 $this->$mtd($font);
8915 }
8916 }
8917 }
8918
8927 protected function _puttruetypeunicode($font) {
8928 $fontname = '';
8929 if ($font['subset']) {
8930 // change name for font subsetting
8931 $subtag = sprintf('%06u', $font['i']);
8932 $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
8933 $fontname .= $subtag.'+';
8934 }
8935 $fontname .= $font['name'];
8936 // Type0 Font
8937 // A composite font composed of other fonts, organized hierarchically
8938 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
8939 $out .= '<< /Type /Font';
8940 $out .= ' /Subtype /Type0';
8941 $out .= ' /BaseFont /'.$fontname;
8942 $out .= ' /Name /F'.$font['i'];
8943 $out .= ' /Encoding /'.$font['enc'];
8944 $out .= ' /ToUnicode '.($this->n + 1).' 0 R';
8945 $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
8946 $out .= ' >>';
8947 $out .= "\n".'endobj';
8948 $this->_out($out);
8949 // ToUnicode map for Identity-H
8951 // ToUnicode Object
8952 $this->_newobj();
8953 $stream = ($this->compress) ? gzcompress($stream) : $stream;
8954 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
8955 $stream = $this->_getrawstream($stream);
8956 $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
8957 // CIDFontType2
8958 // A CIDFont whose glyph descriptions are based on TrueType font technology
8959 $oid = $this->_newobj();
8960 $out = '<< /Type /Font';
8961 $out .= ' /Subtype /CIDFontType2';
8962 $out .= ' /BaseFont /'.$fontname;
8963 // A dictionary containing entries that define the character collection of the CIDFont.
8964 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
8965 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
8966 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
8967 $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
8968 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
8969 $out .= ' /DW '.$font['dw']; // default width
8970 $out .= "\n".TCPDF_FONTS::_putfontwidths($font, 0);
8971 if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
8972 $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
8973 }
8974 $out .= ' >>';
8975 $out .= "\n".'endobj';
8976 $this->_out($out);
8977 // Font descriptor
8978 // A font descriptor describing the CIDFont default metrics other than its glyph widths
8979 $this->_newobj();
8980 $out = '<< /Type /FontDescriptor';
8981 $out .= ' /FontName /'.$fontname;
8982 foreach ($font['desc'] as $key => $value) {
8983 if (is_float($value)) {
8984 $value = sprintf('%F', $value);
8985 }
8986 $out .= ' /'.$key.' '.$value;
8987 }
8988 $fontdir = false;
8989 if (!TCPDF_STATIC::empty_string($font['file'])) {
8990 // A stream containing a TrueType font
8991 $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
8992 $fontdir = $this->FontFiles[$font['file']]['fontdir'];
8993 }
8994 $out .= ' >>';
8995 $out .= "\n".'endobj';
8996 $this->_out($out);
8997 if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
8998 $this->_newobj();
8999 // Embed CIDToGIDMap
9000 // A specification of the mapping from CIDs to glyph indices
9001 // search and get CTG font file to embedd
9002 $ctgfile = strtolower($font['ctg']);
9003 // search and get ctg font file to embedd
9004 $fontfile = TCPDF_FONTS::getFontFullPath($ctgfile, $fontdir);
9005 if (TCPDF_STATIC::empty_string($fontfile)) {
9006 $this->Error('Font file not found: '.$ctgfile);
9007 }
9008 $stream = $this->_getrawstream(file_get_contents($fontfile));
9009 $out = '<< /Length '.strlen($stream).'';
9010 if (substr($fontfile, -2) == '.z') { // check file extension
9011 // Decompresses data encoded using the public-domain
9012 // zlib/deflate compression method, reproducing the
9013 // original text or binary data
9014 $out .= ' /Filter /FlateDecode';
9015 }
9016 $out .= ' >>';
9017 $out .= ' stream'."\n".$stream."\n".'endstream';
9018 $out .= "\n".'endobj';
9019 $this->_out($out);
9020 }
9021 }
9022
9031 protected function _putcidfont0($font) {
9032 $cidoffset = 0;
9033 if (!isset($font['cw'][1])) {
9034 $cidoffset = 31;
9035 }
9036 if (isset($font['cidinfo']['uni2cid'])) {
9037 // convert unicode to cid.
9038 $uni2cid = $font['cidinfo']['uni2cid'];
9039 $cw = array();
9040 foreach ($font['cw'] as $uni => $width) {
9041 if (isset($uni2cid[$uni])) {
9042 $cw[($uni2cid[$uni] + $cidoffset)] = $width;
9043 } elseif ($uni < 256) {
9044 $cw[$uni] = $width;
9045 } // else unknown character
9046 }
9047 $font = array_merge($font, array('cw' => $cw));
9048 }
9049 $name = $font['name'];
9050 $enc = $font['enc'];
9051 if ($enc) {
9052 $longname = $name.'-'.$enc;
9053 } else {
9054 $longname = $name;
9055 }
9056 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
9057 $out .= '<</Type /Font';
9058 $out .= ' /Subtype /Type0';
9059 $out .= ' /BaseFont /'.$longname;
9060 $out .= ' /Name /F'.$font['i'];
9061 if ($enc) {
9062 $out .= ' /Encoding /'.$enc;
9063 }
9064 $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
9065 $out .= ' >>';
9066 $out .= "\n".'endobj';
9067 $this->_out($out);
9068 $oid = $this->_newobj();
9069 $out = '<</Type /Font';
9070 $out .= ' /Subtype /CIDFontType0';
9071 $out .= ' /BaseFont /'.$name;
9072 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9073 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9074 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9075 $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
9076 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
9077 $out .= ' /DW '.$font['dw'];
9078 $out .= "\n".TCPDF_FONTS::_putfontwidths($font, $cidoffset);
9079 $out .= ' >>';
9080 $out .= "\n".'endobj';
9081 $this->_out($out);
9082 $this->_newobj();
9083 $s = '<</Type /FontDescriptor /FontName /'.$name;
9084 foreach ($font['desc'] as $k => $v) {
9085 if ($k != 'Style') {
9086 if (is_float($v)) {
9087 $v = sprintf('%F', $v);
9088 }
9089 $s .= ' /'.$k.' '.$v.'';
9090 }
9091 }
9092 $s .= '>>';
9093 $s .= "\n".'endobj';
9094 $this->_out($s);
9095 }
9096
9101 protected function _putimages() {
9102 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9103 foreach ($this->imagekeys as $file) {
9104 $info = $this->getImageBuffer($file);
9105 // set object for alternate images array
9106 if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
9107 $altoid = $this->_newobj();
9108 $out = '[';
9109 foreach ($info['altimgs'] as $altimage) {
9110 if (isset($this->xobjects['I'.$altimage[0]]['n'])) {
9111 $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R';
9112 $out .= ' /DefaultForPrinting';
9113 if ($altimage[1] === true) {
9114 $out .= ' true';
9115 } else {
9116 $out .= ' false';
9117 }
9118 $out .= ' >>';
9119 }
9120 }
9121 $out .= ' ]';
9122 $out .= "\n".'endobj';
9123 $this->_out($out);
9124 }
9125 // set image object
9126 $oid = $this->_newobj();
9127 $this->xobjects['I'.$info['i']] = array('n' => $oid);
9128 $this->setImageSubBuffer($file, 'n', $this->n);
9129 $out = '<</Type /XObject';
9130 $out .= ' /Subtype /Image';
9131 $out .= ' /Width '.$info['w'];
9132 $out .= ' /Height '.$info['h'];
9133 if (array_key_exists('masked', $info)) {
9134 $out .= ' /SMask '.($this->n - 1).' 0 R';
9135 }
9136 // set color space
9137 $icc = false;
9138 if (isset($info['icc']) AND ($info['icc'] !== false)) {
9139 // ICC Colour Space
9140 $icc = true;
9141 $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]';
9142 } elseif ($info['cs'] == 'Indexed') {
9143 // Indexed Colour Space
9144 $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
9145 } else {
9146 // Device Colour Space
9147 $out .= ' /ColorSpace /'.$info['cs'];
9148 }
9149 if ($info['cs'] == 'DeviceCMYK') {
9150 $out .= ' /Decode [1 0 1 0 1 0 1 0]';
9151 }
9152 $out .= ' /BitsPerComponent '.$info['bpc'];
9153 if (isset($altoid) AND ($altoid > 0)) {
9154 // reference to alternate images dictionary
9155 $out .= ' /Alternates '.$altoid.' 0 R';
9156 }
9157 if (isset($info['exurl']) AND !empty($info['exurl'])) {
9158 // external stream
9159 $out .= ' /Length 0';
9160 $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
9161 if (isset($info['f'])) {
9162 $out .= ' /FFilter /'.$info['f'];
9163 }
9164 $out .= ' >>';
9165 $out .= ' stream'."\n".'endstream';
9166 } else {
9167 if (isset($info['f'])) {
9168 $out .= ' /Filter /'.$info['f'];
9169 }
9170 if (isset($info['parms'])) {
9171 $out .= ' '.$info['parms'];
9172 }
9173 if (isset($info['trns']) AND is_array($info['trns'])) {
9174 $trns = '';
9175 $count_info = count($info['trns']);
9176 if ($info['cs'] == 'Indexed') {
9177 $maxval =(pow(2, $info['bpc']) - 1);
9178 for ($i = 0; $i < $count_info; ++$i) {
9179 if (($info['trns'][$i] != 0) AND ($info['trns'][$i] != $maxval)) {
9180 // this is not a binary type mask @TODO: create a SMask
9181 $trns = '';
9182 break;
9183 } elseif (empty($trns) AND ($info['trns'][$i] == 0)) {
9184 // store the first fully transparent value
9185 $trns .= $i.' '.$i.' ';
9186 }
9187 }
9188 } else {
9189 // grayscale or RGB
9190 for ($i = 0; $i < $count_info; ++$i) {
9191 if ($info['trns'][$i] == 0) {
9192 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
9193 }
9194 }
9195 }
9196 // Colour Key Masking
9197 if (!empty($trns)) {
9198 $out .= ' /Mask ['.$trns.']';
9199 }
9200 }
9201 $stream = $this->_getrawstream($info['data']);
9202 $out .= ' /Length '.strlen($stream).' >>';
9203 $out .= ' stream'."\n".$stream."\n".'endstream';
9204 }
9205 $out .= "\n".'endobj';
9206 $this->_out($out);
9207 if ($icc) {
9208 // ICC colour profile
9209 $this->_newobj();
9210 $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
9211 $icc = $this->_getrawstream($icc);
9212 $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9213 } elseif ($info['cs'] == 'Indexed') {
9214 // colour palette
9215 $this->_newobj();
9216 $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
9217 $pal = $this->_getrawstream($pal);
9218 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
9219 }
9220 }
9221 }
9222
9230 protected function _putxobjects() {
9231 foreach ($this->xobjects as $key => $data) {
9232 if (isset($data['outdata'])) {
9233 $stream = str_replace($this->epsmarker, '', trim($data['outdata']));
9234 $out = $this->_getobj($data['n'])."\n";
9235 $out .= '<<';
9236 $out .= ' /Type /XObject';
9237 $out .= ' /Subtype /Form';
9238 $out .= ' /FormType 1';
9239 if ($this->compress) {
9240 $stream = gzcompress($stream);
9241 $out .= ' /Filter /FlateDecode';
9242 }
9243 $out .= sprintf(' /BBox [%F %F %F %F]', ($data['x'] * $this->k), (-$data['y'] * $this->k), (($data['w'] + $data['x']) * $this->k), (($data['h'] - $data['y']) * $this->k));
9244 $out .= ' /Matrix [1 0 0 1 0 0]';
9245 $out .= ' /Resources <<';
9246 $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9247 if (!$this->pdfa_mode) {
9248 // transparency
9249 if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
9250 $out .= ' /ExtGState <<';
9251 foreach ($data['extgstates'] as $k => $extgstate) {
9252 if (isset($this->extgstates[$k]['name'])) {
9253 $out .= ' /'.$this->extgstates[$k]['name'];
9254 } else {
9255 $out .= ' /GS'.$k;
9256 }
9257 $out .= ' '.$this->extgstates[$k]['n'].' 0 R';
9258 }
9259 $out .= ' >>';
9260 }
9261 if (isset($data['gradients']) AND !empty($data['gradients'])) {
9262 $gp = '';
9263 $gs = '';
9264 foreach ($data['gradients'] as $id => $grad) {
9265 // gradient patterns
9266 $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R';
9267 // gradient shadings
9268 $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R';
9269 }
9270 $out .= ' /Pattern <<'.$gp.' >>';
9271 $out .= ' /Shading <<'.$gs.' >>';
9272 }
9273 }
9274 // spot colors
9275 if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
9276 $out .= ' /ColorSpace <<';
9277 foreach ($data['spot_colors'] as $name => $color) {
9278 $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R';
9279 }
9280 $out .= ' >>';
9281 }
9282 // fonts
9283 if (!empty($data['fonts'])) {
9284 $out .= ' /Font <<';
9285 foreach ($data['fonts'] as $fontkey => $fontid) {
9286 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9287 }
9288 $out .= ' >>';
9289 }
9290 // images or nested xobjects
9291 if (!empty($data['images']) OR !empty($data['xobjects'])) {
9292 $out .= ' /XObject <<';
9293 foreach ($data['images'] as $imgid) {
9294 $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
9295 }
9296 foreach ($data['xobjects'] as $sub_id => $sub_objid) {
9297 $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
9298 }
9299 $out .= ' >>';
9300 }
9301 $out .= ' >>'; //end resources
9302 if (isset($data['group']) AND ($data['group'] !== false)) {
9303 // set transparency group
9304 $out .= ' /Group << /Type /Group /S /Transparency';
9305 if (is_array($data['group'])) {
9306 if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
9307 $out .= ' /CS /'.$data['group']['CS'];
9308 }
9309 if (isset($data['group']['I'])) {
9310 $out .= ' /I /'.($data['group']['I']===true?'true':'false');
9311 }
9312 if (isset($data['group']['K'])) {
9313 $out .= ' /K /'.($data['group']['K']===true?'true':'false');
9314 }
9315 }
9316 $out .= ' >>';
9317 }
9318 $stream = $this->_getrawstream($stream, $data['n']);
9319 $out .= ' /Length '.strlen($stream);
9320 $out .= ' >>';
9321 $out .= ' stream'."\n".$stream."\n".'endstream';
9322 $out .= "\n".'endobj';
9323 $this->_out($out);
9324 }
9325 }
9326 }
9327
9333 protected function _putspotcolors() {
9334 foreach ($this->spot_colors as $name => $color) {
9335 $this->_newobj();
9336 $this->spot_colors[$name]['n'] = $this->n;
9337 $out = '[/Separation /'.str_replace(' ', '#20', $name);
9338 $out .= ' /DeviceCMYK <<';
9339 $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
9340 $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
9341 $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
9342 $out .= "\n".'endobj';
9343 $this->_out($out);
9344 }
9345 }
9346
9353 protected function _getxobjectdict() {
9354 $out = '';
9355 foreach ($this->xobjects as $id => $objid) {
9356 $out .= ' /'.$id.' '.$objid['n'].' 0 R';
9357 }
9358 return $out;
9359 }
9360
9365 protected function _putresourcedict() {
9366 $out = $this->_getobj(2)."\n";
9367 $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9368 $out .= ' /Font <<';
9369 foreach ($this->fontkeys as $fontkey) {
9370 $font = $this->getFontBuffer($fontkey);
9371 $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
9372 }
9373 $out .= ' >>';
9374 $out .= ' /XObject <<';
9375 $out .= $this->_getxobjectdict();
9376 $out .= ' >>';
9377 // layers
9378 if (!empty($this->pdflayers)) {
9379 $out .= ' /Properties <<';
9380 foreach ($this->pdflayers as $layer) {
9381 $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
9382 }
9383 $out .= ' >>';
9384 }
9385 if (!$this->pdfa_mode) {
9386 // transparency
9387 if (isset($this->extgstates) AND !empty($this->extgstates)) {
9388 $out .= ' /ExtGState <<';
9389 foreach ($this->extgstates as $k => $extgstate) {
9390 if (isset($extgstate['name'])) {
9391 $out .= ' /'.$extgstate['name'];
9392 } else {
9393 $out .= ' /GS'.$k;
9394 }
9395 $out .= ' '.$extgstate['n'].' 0 R';
9396 }
9397 $out .= ' >>';
9398 }
9399 if (isset($this->gradients) AND !empty($this->gradients)) {
9400 $gp = '';
9401 $gs = '';
9402 foreach ($this->gradients as $id => $grad) {
9403 // gradient patterns
9404 $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
9405 // gradient shadings
9406 $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
9407 }
9408 $out .= ' /Pattern <<'.$gp.' >>';
9409 $out .= ' /Shading <<'.$gs.' >>';
9410 }
9411 }
9412 // spot colors
9413 if (isset($this->spot_colors) AND !empty($this->spot_colors)) {
9414 $out .= ' /ColorSpace <<';
9415 foreach ($this->spot_colors as $color) {
9416 $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
9417 }
9418 $out .= ' >>';
9419 }
9420 $out .= ' >>';
9421 $out .= "\n".'endobj';
9422 $this->_out($out);
9423 }
9424
9429 protected function _putresources() {
9430 $this->_putextgstates();
9431 $this->_putocg();
9432 $this->_putfonts();
9433 $this->_putimages();
9434 $this->_putspotcolors();
9435 $this->_putshaders();
9436 $this->_putxobjects();
9437 $this->_putresourcedict();
9438 $this->_putdests();
9439 $this->_putEmbeddedFiles();
9440 $this->_putannotsobjs();
9441 $this->_putjavascript();
9442 $this->_putbookmarks();
9443 $this->_putencryption();
9444 }
9445
9452 protected function _putinfo() {
9453 $oid = $this->_newobj();
9454 $out = '<<';
9455 // store current isunicode value
9456 $prev_isunicode = $this->isunicode;
9457 if ($this->docinfounicode) {
9458 $this->isunicode = true;
9459 }
9460 if (!TCPDF_STATIC::empty_string($this->title)) {
9461 // The document's title.
9462 $out .= ' /Title '.$this->_textstring($this->title, $oid);
9463 }
9464 if (!TCPDF_STATIC::empty_string($this->author)) {
9465 // The name of the person who created the document.
9466 $out .= ' /Author '.$this->_textstring($this->author, $oid);
9467 }
9468 if (!TCPDF_STATIC::empty_string($this->subject)) {
9469 // The subject of the document.
9470 $out .= ' /Subject '.$this->_textstring($this->subject, $oid);
9471 }
9472 if (!TCPDF_STATIC::empty_string($this->keywords)) {
9473 // Keywords associated with the document.
9474 $out .= ' /Keywords '.$this->_textstring($this->keywords, $oid);
9475 }
9476 if (!TCPDF_STATIC::empty_string($this->creator)) {
9477 // If the document was converted to PDF from another format, the name of the conforming product that created the original document from which it was converted.
9478 $out .= ' /Creator '.$this->_textstring($this->creator, $oid);
9479 }
9480 // restore previous isunicode value
9481 $this->isunicode = $prev_isunicode;
9482 // default producer
9483 $out .= ' /Producer '.$this->_textstring(TCPDF_STATIC::getTCPDFProducer(), $oid);
9484 // The date and time the document was created, in human-readable form
9485 $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp);
9486 // The date and time the document was most recently modified, in human-readable form
9487 $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp);
9488 // A name object indicating whether the document has been modified to include trapping information
9489 $out .= ' /Trapped /False';
9490 $out .= ' >>';
9491 $out .= "\n".'endobj';
9492 $this->_out($out);
9493 return $oid;
9494 }
9495
9503 public function setExtraXMP($xmp) {
9504 $this->custom_xmp = $xmp;
9505 }
9506
9513 protected function _putXMP() {
9514 $oid = $this->_newobj();
9515 // store current isunicode value
9516 $prev_isunicode = $this->isunicode;
9517 $this->isunicode = true;
9518 $prev_encrypted = $this->encrypted;
9519 $this->encrypted = false;
9520 // set XMP data
9521 $xmp = '<?xpacket begin="'.TCPDF_FONTS::unichr(0xfeff, $this->isunicode).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
9522 $xmp .= '<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.1-c043 52.372728, 2009/01/18-15:08:04">'."\n";
9523 $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
9524 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
9525 $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
9526 $xmp .= "\t\t\t".'<dc:title>'."\n";
9527 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9528 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->title).'</rdf:li>'."\n";
9529 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9530 $xmp .= "\t\t\t".'</dc:title>'."\n";
9531 $xmp .= "\t\t\t".'<dc:creator>'."\n";
9532 $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
9533 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->author).'</rdf:li>'."\n";
9534 $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
9535 $xmp .= "\t\t\t".'</dc:creator>'."\n";
9536 $xmp .= "\t\t\t".'<dc:description>'."\n";
9537 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9538 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->subject).'</rdf:li>'."\n";
9539 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9540 $xmp .= "\t\t\t".'</dc:description>'."\n";
9541 $xmp .= "\t\t\t".'<dc:subject>'."\n";
9542 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9543 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->keywords).'</rdf:li>'."\n";
9544 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9545 $xmp .= "\t\t\t".'</dc:subject>'."\n";
9546 $xmp .= "\t\t".'</rdf:Description>'."\n";
9547 // convert doc creation date format
9548 $dcdate = TCPDF_STATIC::getFormattedDate($this->doc_creation_timestamp);
9549 $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2);
9550 $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2);
9551 $doccreationdate .= substr($dcdate, 14, 3).':'.substr($dcdate, 18, 2);
9552 $doccreationdate = TCPDF_STATIC::_escapeXML($doccreationdate);
9553 // convert doc modification date format
9554 $dmdate = TCPDF_STATIC::getFormattedDate($this->doc_modification_timestamp);
9555 $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2);
9556 $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2);
9557 $docmoddate .= substr($dmdate, 14, 3).':'.substr($dmdate, 18, 2);
9558 $docmoddate = TCPDF_STATIC::_escapeXML($docmoddate);
9559 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
9560 $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n";
9561 $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n";
9562 $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n";
9563 $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n";
9564 $xmp .= "\t\t".'</rdf:Description>'."\n";
9565 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
9566 $xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC::_escapeXML($this->keywords).'</pdf:Keywords>'."\n";
9567 $xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC::_escapeXML(TCPDF_STATIC::getTCPDFProducer()).'</pdf:Producer>'."\n";
9568 $xmp .= "\t\t".'</rdf:Description>'."\n";
9569 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
9570 $uuid = 'uuid:'.substr($this->file_id, 0, 8).'-'.substr($this->file_id, 8, 4).'-'.substr($this->file_id, 12, 4).'-'.substr($this->file_id, 16, 4).'-'.substr($this->file_id, 20, 12);
9571 $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
9572 $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
9573 $xmp .= "\t\t".'</rdf:Description>'."\n";
9574 if ($this->pdfa_mode) {
9575 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
9576 $xmp .= "\t\t\t".'<pdfaid:part>1</pdfaid:part>'."\n";
9577 $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
9578 $xmp .= "\t\t".'</rdf:Description>'."\n";
9579 }
9580 // XMP extension schemas
9581 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/" xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#" xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">'."\n";
9582 $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
9583 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9584 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9585 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
9586 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
9587 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
9588 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9589 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9590 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
9591 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
9592 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
9593 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9594 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9595 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9596 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9597 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
9598 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9599 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9600 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9601 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9602 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9603 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9604 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9605 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
9606 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
9607 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
9608 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9609 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9610 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9611 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9612 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
9613 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
9614 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
9615 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9616 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9617 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9618 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
9619 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
9620 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9621 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9622 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9623 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9624 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
9625 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
9626 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9627 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9628 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9629 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9630 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9631 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9632 $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
9633 $xmp .= "\t\t".'</rdf:Description>'."\n";
9634 $xmp .= "\t".'</rdf:RDF>'."\n";
9635 $xmp .= $this->custom_xmp;
9636 $xmp .= '</x:xmpmeta>'."\n";
9637 $xmp .= '<?xpacket end="w"?>';
9638 $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
9639 // restore previous isunicode value
9640 $this->isunicode = $prev_isunicode;
9641 $this->encrypted = $prev_encrypted;
9642 $this->_out($out);
9643 return $oid;
9644 }
9645
9651 protected function _putcatalog() {
9652 // put XMP
9653 $xmpobj = $this->_putXMP();
9654 // if required, add standard sRGB_IEC61966-2.1 blackscaled ICC colour profile
9655 if ($this->pdfa_mode OR $this->force_srgb) {
9656 $iccobj = $this->_newobj();
9657 $icc = file_get_contents(dirname(__FILE__).'/include/sRGB.icc');
9658 $filter = '';
9659 if ($this->compress) {
9660 $filter = ' /Filter /FlateDecode';
9661 $icc = gzcompress($icc);
9662 }
9663 $icc = $this->_getrawstream($icc);
9664 $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9665 }
9666 // start catalog
9667 $oid = $this->_newobj();
9668 $out = '<< /Type /Catalog';
9669 $out .= ' /Version /'.$this->PDFVersion;
9670 //$out .= ' /Extensions <<>>';
9671 $out .= ' /Pages 1 0 R';
9672 //$out .= ' /PageLabels ' //...;
9673 $out .= ' /Names <<';
9674 if ((!$this->pdfa_mode) AND !empty($this->n_js)) {
9675 $out .= ' /JavaScript '.$this->n_js;
9676 }
9677 if (!empty($this->efnames)) {
9678 $out .= ' /EmbeddedFiles <</Names [';
9679 foreach ($this->efnames AS $fn => $fref) {
9680 $out .= ' '.$this->_datastring($fn).' '.$fref;
9681 }
9682 $out .= ' ]>>';
9683 }
9684 $out .= ' >>';
9685 if (!empty($this->dests)) {
9686 $out .= ' /Dests '.($this->n_dests).' 0 R';
9687 }
9688 $out .= $this->_putviewerpreferences();
9689 if (isset($this->LayoutMode) AND (!TCPDF_STATIC::empty_string($this->LayoutMode))) {
9690 $out .= ' /PageLayout /'.$this->LayoutMode;
9691 }
9692 if (isset($this->PageMode) AND (!TCPDF_STATIC::empty_string($this->PageMode))) {
9693 $out .= ' /PageMode /'.$this->PageMode;
9694 }
9695 if (count($this->outlines) > 0) {
9696 $out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
9697 $out .= ' /PageMode /UseOutlines';
9698 }
9699 //$out .= ' /Threads []';
9700 if ($this->ZoomMode == 'fullpage') {
9701 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
9702 } elseif ($this->ZoomMode == 'fullwidth') {
9703 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
9704 } elseif ($this->ZoomMode == 'real') {
9705 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
9706 } elseif (!is_string($this->ZoomMode)) {
9707 $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100));
9708 }
9709 //$out .= ' /AA <<>>';
9710 //$out .= ' /URI <<>>';
9711 $out .= ' /Metadata '.$xmpobj.' 0 R';
9712 //$out .= ' /StructTreeRoot <<>>';
9713 //$out .= ' /MarkInfo <<>>';
9714 if (isset($this->l['a_meta_language'])) {
9715 $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
9716 }
9717 //$out .= ' /SpiderInfo <<>>';
9718 // set OutputIntent to sRGB IEC61966-2.1 if required
9719 if ($this->pdfa_mode OR $this->force_srgb) {
9720 $out .= ' /OutputIntents [<<';
9721 $out .= ' /Type /OutputIntent';
9722 $out .= ' /S /GTS_PDFA1';
9723 $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9724 $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9725 $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
9726 $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9727 $out .= ' /DestOutputProfile '.$iccobj.' 0 R';
9728 $out .= ' >>]';
9729 }
9730 //$out .= ' /PieceInfo <<>>';
9731 if (!empty($this->pdflayers)) {
9732 $lyrobjs = '';
9733 $lyrobjs_off = '';
9734 $lyrobjs_lock = '';
9735 foreach ($this->pdflayers as $layer) {
9736 $layer_obj_ref = ' '.$layer['objid'].' 0 R';
9737 $lyrobjs .= $layer_obj_ref;
9738 if ($layer['view'] === false) {
9739 $lyrobjs_off .= $layer_obj_ref;
9740 }
9741 if ($layer['lock']) {
9742 $lyrobjs_lock .= $layer_obj_ref;
9743 }
9744 }
9745 $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
9746 $out .= ' /D <<';
9747 $out .= ' /Name '.$this->_textstring('Layers', $oid);
9748 $out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
9749 $out .= ' /BaseState /ON';
9750 $out .= ' /OFF ['.$lyrobjs_off.']';
9751 $out .= ' /Locked ['.$lyrobjs_lock.']';
9752 $out .= ' /Intent /View';
9753 $out .= ' /AS [';
9754 $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
9755 $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
9756 $out .= ' ]';
9757 $out .= ' /Order ['.$lyrobjs.']';
9758 $out .= ' /ListMode /AllPages';
9759 //$out .= ' /RBGroups ['..']';
9760 //$out .= ' /Locked ['..']';
9761 $out .= ' >>';
9762 $out .= ' >>';
9763 }
9764 // AcroForm
9765 if (!empty($this->form_obj_id)
9766 OR ($this->sign AND isset($this->signature_data['cert_type']))
9767 OR !empty($this->empty_signature_appearance)) {
9768 $out .= ' /AcroForm <<';
9769 $objrefs = '';
9770 if ($this->sign AND isset($this->signature_data['cert_type'])) {
9771 // set reference for signature object
9772 $objrefs .= $this->sig_obj_id.' 0 R';
9773 }
9774 if (!empty($this->empty_signature_appearance)) {
9775 foreach ($this->empty_signature_appearance as $esa) {
9776 // set reference for empty signature objects
9777 $objrefs .= ' '.$esa['objid'].' 0 R';
9778 }
9779 }
9780 if (!empty($this->form_obj_id)) {
9781 foreach($this->form_obj_id as $objid) {
9782 $objrefs .= ' '.$objid.' 0 R';
9783 }
9784 }
9785 $out .= ' /Fields ['.$objrefs.']';
9786 // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
9787 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
9788 $out .= ' /NeedAppearances false';
9789 }
9790 if ($this->sign AND isset($this->signature_data['cert_type'])) {
9791 if ($this->signature_data['cert_type'] > 0) {
9792 $out .= ' /SigFlags 3';
9793 } else {
9794 $out .= ' /SigFlags 1';
9795 }
9796 }
9797 //$out .= ' /CO ';
9798 if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
9799 $out .= ' /DR <<';
9800 $out .= ' /Font <<';
9801 foreach ($this->annotation_fonts as $fontkey => $fontid) {
9802 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9803 }
9804 $out .= ' >> >>';
9805 }
9806 $font = $this->getFontBuffer('helvetica');
9807 $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
9808 $out .= ' /Q '.(($this->rtl)?'2':'0');
9809 //$out .= ' /XFA ';
9810 $out .= ' >>';
9811 // signatures
9812 if ($this->sign AND isset($this->signature_data['cert_type'])
9813 AND (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A'))) {
9814 if ($this->signature_data['cert_type'] > 0) {
9815 $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
9816 } else {
9817 $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
9818 }
9819 }
9820 }
9821 //$out .= ' /Legal <<>>';
9822 //$out .= ' /Requirements []';
9823 //$out .= ' /Collection <<>>';
9824 //$out .= ' /NeedsRendering true';
9825 $out .= ' >>';
9826 $out .= "\n".'endobj';
9827 $this->_out($out);
9828 return $oid;
9829 }
9830
9838 protected function _putviewerpreferences() {
9840 $out = ' /ViewerPreferences <<';
9841 if ($this->rtl) {
9842 $out .= ' /Direction /R2L';
9843 } else {
9844 $out .= ' /Direction /L2R';
9845 }
9846 if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) {
9847 $out .= ' /HideToolbar true';
9848 }
9849 if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) {
9850 $out .= ' /HideMenubar true';
9851 }
9852 if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) {
9853 $out .= ' /HideWindowUI true';
9854 }
9855 if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) {
9856 $out .= ' /FitWindow true';
9857 }
9858 if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) {
9859 $out .= ' /CenterWindow true';
9860 }
9861 if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) {
9862 $out .= ' /DisplayDocTitle true';
9863 }
9864 if (isset($vp['NonFullScreenPageMode'])) {
9865 $out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode'];
9866 }
9867 if (isset($vp['ViewArea'])) {
9868 $out .= ' /ViewArea /'.$vp['ViewArea'];
9869 }
9870 if (isset($vp['ViewClip'])) {
9871 $out .= ' /ViewClip /'.$vp['ViewClip'];
9872 }
9873 if (isset($vp['PrintArea'])) {
9874 $out .= ' /PrintArea /'.$vp['PrintArea'];
9875 }
9876 if (isset($vp['PrintClip'])) {
9877 $out .= ' /PrintClip /'.$vp['PrintClip'];
9878 }
9879 if (isset($vp['PrintScaling'])) {
9880 $out .= ' /PrintScaling /'.$vp['PrintScaling'];
9881 }
9882 if (isset($vp['Duplex']) AND (!TCPDF_STATIC::empty_string($vp['Duplex']))) {
9883 $out .= ' /Duplex /'.$vp['Duplex'];
9884 }
9885 if (isset($vp['PickTrayByPDFSize'])) {
9886 if ($vp['PickTrayByPDFSize']) {
9887 $out .= ' /PickTrayByPDFSize true';
9888 } else {
9889 $out .= ' /PickTrayByPDFSize false';
9890 }
9891 }
9892 if (isset($vp['PrintPageRange'])) {
9893 $PrintPageRangeNum = '';
9894 foreach ($vp['PrintPageRange'] as $k => $v) {
9895 $PrintPageRangeNum .= ' '.($v - 1).'';
9896 }
9897 $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
9898 }
9899 if (isset($vp['NumCopies'])) {
9900 $out .= ' /NumCopies '.intval($vp['NumCopies']);
9901 }
9902 $out .= ' >>';
9903 return $out;
9904 }
9905
9910 protected function _putheader() {
9911 $this->_out('%PDF-'.$this->PDFVersion);
9912 $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
9913 }
9914
9919 protected function _enddoc() {
9920 if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
9921 // save subset chars of the previous font
9922 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
9923 }
9924 $this->state = 1;
9925 $this->_putheader();
9926 $this->_putpages();
9927 $this->_putresources();
9928 // empty signature fields
9929 if (!empty($this->empty_signature_appearance)) {
9930 foreach ($this->empty_signature_appearance as $key => $esa) {
9931 // widget annotation for empty signature
9932 $out = $this->_getobj($esa['objid'])."\n";
9933 $out .= '<< /Type /Annot';
9934 $out .= ' /Subtype /Widget';
9935 $out .= ' /Rect ['.$esa['rect'].']';
9936 $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page
9937 $out .= ' /F 4';
9938 $out .= ' /FT /Sig';
9939 $signame = $esa['name'].sprintf(' [%03d]', ($key + 1));
9940 $out .= ' /T '.$this->_textstring($signame, $esa['objid']);
9941 $out .= ' /Ff 0';
9942 $out .= ' >>';
9943 $out .= "\n".'endobj';
9944 $this->_out($out);
9945 }
9946 }
9947 // Signature
9948 if ($this->sign AND isset($this->signature_data['cert_type'])) {
9949 // widget annotation for signature
9950 $out = $this->_getobj($this->sig_obj_id)."\n";
9951 $out .= '<< /Type /Annot';
9952 $out .= ' /Subtype /Widget';
9953 $out .= ' /Rect ['.$this->signature_appearance['rect'].']';
9954 $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
9955 $out .= ' /F 4';
9956 $out .= ' /FT /Sig';
9957 $out .= ' /T '.$this->_textstring($this->signature_appearance['name'], $this->sig_obj_id);
9958 $out .= ' /Ff 0';
9959 $out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
9960 $out .= ' >>';
9961 $out .= "\n".'endobj';
9962 $this->_out($out);
9963 // signature
9964 $this->_putsignature();
9965 }
9966 // Info
9967 $objid_info = $this->_putinfo();
9968 // Catalog
9969 $objid_catalog = $this->_putcatalog();
9970 // Cross-ref
9971 $o = $this->bufferlen;
9972 // XREF section
9973 $this->_out('xref');
9974 $this->_out('0 '.($this->n + 1));
9975 $this->_out('0000000000 65535 f ');
9976 $freegen = ($this->n + 2);
9977 for ($i=1; $i <= $this->n; ++$i) {
9978 if (!isset($this->offsets[$i]) AND ($i > 1)) {
9979 $this->_out(sprintf('0000000000 %05d f ', $freegen));
9980 ++$freegen;
9981 } else {
9982 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
9983 }
9984 }
9985 // TRAILER
9986 $out = 'trailer'."\n";
9987 $out .= '<<';
9988 $out .= ' /Size '.($this->n + 1);
9989 $out .= ' /Root '.$objid_catalog.' 0 R';
9990 $out .= ' /Info '.$objid_info.' 0 R';
9991 if ($this->encrypted) {
9992 $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
9993 }
9994 $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
9995 $out .= ' >>';
9996 $this->_out($out);
9997 $this->_out('startxref');
9998 $this->_out($o);
9999 $this->_out('%%EOF');
10000 $this->state = 3; // end-of-doc
10001 }
10002
10010 protected function _beginpage($orientation='', $format='') {
10011 ++$this->page;
10012 $this->pageobjects[$this->page] = array();
10013 $this->setPageBuffer($this->page, '');
10014 // initialize array for graphics tranformation positions inside a page buffer
10015 $this->transfmrk[$this->page] = array();
10016 $this->state = 2;
10017 if (TCPDF_STATIC::empty_string($orientation)) {
10018 if (isset($this->CurOrientation)) {
10019 $orientation = $this->CurOrientation;
10020 } elseif ($this->fwPt > $this->fhPt) {
10021 // landscape
10022 $orientation = 'L';
10023 } else {
10024 // portrait
10025 $orientation = 'P';
10026 }
10027 }
10028 if (TCPDF_STATIC::empty_string($format)) {
10029 $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
10030 $this->setPageOrientation($orientation);
10031 } else {
10032 $this->setPageFormat($format, $orientation);
10033 }
10034 if ($this->rtl) {
10035 $this->x = $this->w - $this->rMargin;
10036 } else {
10037 $this->x = $this->lMargin;
10038 }
10039 $this->y = $this->tMargin;
10040 if (isset($this->newpagegroup[$this->page])) {
10041 // start a new group
10042 $this->currpagegroup = $this->newpagegroup[$this->page];
10043 $this->pagegroups[$this->currpagegroup] = 1;
10044 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
10045 ++$this->pagegroups[$this->currpagegroup];
10046 }
10047 }
10048
10053 protected function _endpage() {
10054 $this->setVisibility('all');
10055 $this->state = 1;
10056 }
10057
10063 protected function _newobj() {
10064 $this->_out($this->_getobj());
10065 return $this->n;
10066 }
10067
10075 protected function _getobj($objid='') {
10076 if ($objid === '') {
10077 ++$this->n;
10078 $objid = $this->n;
10079 }
10080 $this->offsets[$objid] = $this->bufferlen;
10081 $this->pageobjects[$this->page][] = $objid;
10082 return $objid.' 0 obj';
10083 }
10084
10092 protected function _dounderline($x, $y, $txt) {
10093 $w = $this->GetStringWidth($txt);
10094 return $this->_dounderlinew($x, $y, $w);
10095 }
10096
10105 protected function _dounderlinew($x, $y, $w) {
10106 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10107 return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
10108 }
10109
10117 protected function _dolinethrough($x, $y, $txt) {
10118 $w = $this->GetStringWidth($txt);
10119 return $this->_dolinethroughw($x, $y, $w);
10120 }
10121
10130 protected function _dolinethroughw($x, $y, $w) {
10131 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10132 return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew);
10133 }
10134
10143 protected function _dooverline($x, $y, $txt) {
10144 $w = $this->GetStringWidth($txt);
10145 return $this->_dooverlinew($x, $y, $w);
10146 }
10147
10156 protected function _dooverlinew($x, $y, $w) {
10157 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10158 return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
10159
10160 }
10161
10169 protected function _datastring($s, $n=0) {
10170 if ($n == 0) {
10171 $n = $this->n;
10172 }
10173 $s = $this->_encrypt_data($n, $s);
10174 return '('. TCPDF_STATIC::_escape($s).')';
10175 }
10176
10183 public function setDocCreationTimestamp($time) {
10184 if (is_string($time)) {
10185 $time = TCPDF_STATIC::getTimestamp($time);
10186 }
10187 $this->doc_creation_timestamp = intval($time);
10188 }
10189
10196 public function setDocModificationTimestamp($time) {
10197 if (is_string($time)) {
10198 $time = TCPDF_STATIC::getTimestamp($time);
10199 }
10200 $this->doc_modification_timestamp = intval($time);
10201 }
10202
10209 public function getDocCreationTimestamp() {
10211 }
10212
10221 }
10222
10231 protected function _datestring($n=0, $timestamp=0) {
10232 if ((empty($timestamp)) OR ($timestamp < 0)) {
10234 }
10236 }
10237
10245 protected function _textstring($s, $n=0) {
10246 if ($this->isunicode) {
10247 //Convert string to UTF-16BE
10248 $s = TCPDF_FONTS::UTF8ToUTF16BE($s, true, $this->isunicode, $this->CurrentFont);
10249 }
10250 return $this->_datastring($s, $n);
10251 }
10252
10261 protected function _getrawstream($s, $n=0) {
10262 if ($n <= 0) {
10263 // default to current object
10264 $n = $this->n;
10265 }
10266 return $this->_encrypt_data($n, $s);
10267 }
10268
10274 protected function _out($s) {
10275 if ($this->state == 2) {
10276 if ($this->inxobj) {
10277 // we are inside an XObject template
10278 $this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
10279 } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
10280 // puts data before page footer
10281 $pagebuff = $this->getPageBuffer($this->page);
10282 $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
10283 $footer = substr($pagebuff, -$this->footerlen[$this->page]);
10284 $this->setPageBuffer($this->page, $page.$s."\n".$footer);
10285 // update footer position
10286 $this->footerpos[$this->page] += strlen($s."\n");
10287 } else {
10288 // set page data
10289 $this->setPageBuffer($this->page, $s."\n", true);
10290 }
10291 } elseif ($this->state > 0) {
10292 // set general data
10293 $this->setBuffer($s."\n");
10294 }
10295 }
10296
10303 public function setHeaderFont($font) {
10304 $this->header_font = $font;
10305 }
10306
10313 public function getHeaderFont() {
10314 return $this->header_font;
10315 }
10316
10323 public function setFooterFont($font) {
10324 $this->footer_font = $font;
10325 }
10326
10333 public function getFooterFont() {
10334 return $this->footer_font;
10335 }
10336
10343 public function setLanguageArray($language) {
10344 $this->l = $language;
10345 if (isset($this->l['a_meta_dir'])) {
10346 $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
10347 } else {
10348 $this->rtl = false;
10349 }
10350 }
10351
10356 public function getPDFData() {
10357 if ($this->state < 3) {
10358 $this->Close();
10359 }
10360 return $this->buffer;
10361 }
10362
10375 public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
10376 if (isset($url[1]) AND ($url[0] == '#') AND is_numeric($url[1])) {
10377 // convert url to internal link
10378 $lnkdata = explode(',', $url);
10379 if (isset($lnkdata[0]) ) {
10380 $page = substr($lnkdata[0], 1);
10381 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
10382 $lnky = floatval($lnkdata[1]);
10383 } else {
10384 $lnky = 0;
10385 }
10386 $url = $this->AddLink();
10387 $this->SetLink($url, $lnky, $page);
10388 }
10389 }
10390 // store current settings
10391 $prevcolor = $this->fgcolor;
10392 $prevstyle = $this->FontStyle;
10393 if (empty($color)) {
10394 $this->SetTextColorArray($this->htmlLinkColorArray);
10395 } else {
10396 $this->SetTextColorArray($color);
10397 }
10398 if ($style == -1) {
10399 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
10400 } else {
10401 $this->SetFont('', $this->FontStyle.$style);
10402 }
10403 $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
10404 // restore settings
10405 $this->SetFont('', $prevstyle);
10406 $this->SetTextColorArray($prevcolor);
10407 return $ret;
10408 }
10409
10417 public function pixelsToUnits($px) {
10418 return ($px / ($this->imgscale * $this->k));
10419 }
10420
10428 public function unhtmlentities($text_to_convert) {
10429 return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
10430 }
10431
10432 // ENCRYPTION METHODS ----------------------------------
10433
10443 protected function _objectkey($n) {
10444 $objkey = $this->encryptdata['key'].pack('VXxx', $n);
10445 if ($this->encryptdata['mode'] == 2) { // AES-128
10446 // AES padding
10447 $objkey .= "\x73\x41\x6C\x54"; // sAlT
10448 }
10449 $objkey = substr(TCPDF_STATIC::_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
10450 $objkey = substr($objkey, 0, 16);
10451 return $objkey;
10452 }
10453
10463 protected function _encrypt_data($n, $s) {
10464 if (!$this->encrypted) {
10465 return $s;
10466 }
10467 switch ($this->encryptdata['mode']) {
10468 case 0: // RC4-40
10469 case 1: { // RC4-128
10470 $s = TCPDF_STATIC::_RC4($this->_objectkey($n), $s, $this->last_enc_key, $this->last_enc_key_c);
10471 break;
10472 }
10473 case 2: { // AES-128
10474 $s = TCPDF_STATIC::_AES($this->_objectkey($n), $s);
10475 break;
10476 }
10477 case 3: { // AES-256
10478 $s = TCPDF_STATIC::_AES($this->encryptdata['key'], $s);
10479 break;
10480 }
10481 }
10482 return $s;
10483 }
10484
10491 protected function _putencryption() {
10492 if (!$this->encrypted) {
10493 return;
10494 }
10495 $this->encryptdata['objid'] = $this->_newobj();
10496 $out = '<<';
10497 if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
10498 $this->encryptdata['Filter'] = 'Standard';
10499 }
10500 $out .= ' /Filter /'.$this->encryptdata['Filter'];
10501 if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
10502 $out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
10503 }
10504 if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
10505 $this->encryptdata['V'] = 1;
10506 }
10507 // V is a code specifying the algorithm to be used in encrypting and decrypting the document
10508 $out .= ' /V '.$this->encryptdata['V'];
10509 if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
10510 // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
10511 $out .= ' /Length '.$this->encryptdata['Length'];
10512 } else {
10513 $out .= ' /Length 40';
10514 }
10515 if ($this->encryptdata['V'] >= 4) {
10516 if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
10517 $this->encryptdata['StmF'] = 'Identity';
10518 }
10519 if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
10520 // The name of the crypt filter that shall be used when decrypting all strings in the document.
10521 $this->encryptdata['StrF'] = 'Identity';
10522 }
10523 // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
10524 if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
10525 $out .= ' /CF <<';
10526 $out .= ' /'.$this->encryptdata['StmF'].' <<';
10527 $out .= ' /Type /CryptFilter';
10528 if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
10529 // The method used
10530 $out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
10531 if ($this->encryptdata['pubkey']) {
10532 $out .= ' /Recipients [';
10533 foreach ($this->encryptdata['Recipients'] as $rec) {
10534 $out .= ' <'.$rec.'>';
10535 }
10536 $out .= ' ]';
10537 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
10538 $out .= ' /EncryptMetadata false';
10539 } else {
10540 $out .= ' /EncryptMetadata true';
10541 }
10542 }
10543 } else {
10544 $out .= ' /CFM /None';
10545 }
10546 if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
10547 // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
10548 $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
10549 } else {
10550 $out .= ' /AuthEvent /DocOpen';
10551 }
10552 if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
10553 // The bit length of the encryption key.
10554 $out .= ' /Length '.$this->encryptdata['CF']['Length'];
10555 }
10556 $out .= ' >> >>';
10557 }
10558 // The name of the crypt filter that shall be used by default when decrypting streams.
10559 $out .= ' /StmF /'.$this->encryptdata['StmF'];
10560 // The name of the crypt filter that shall be used when decrypting all strings in the document.
10561 $out .= ' /StrF /'.$this->encryptdata['StrF'];
10562 if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
10563 // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
10564 $out .= ' /EFF /'.$this->encryptdata[''];
10565 }
10566 }
10567 // Additional encryption dictionary entries for the standard security handler
10568 if ($this->encryptdata['pubkey']) {
10569 if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
10570 $out .= ' /Recipients [';
10571 foreach ($this->encryptdata['Recipients'] as $rec) {
10572 $out .= ' <'.$rec.'>';
10573 }
10574 $out .= ' ]';
10575 }
10576 } else {
10577 $out .= ' /R';
10578 if ($this->encryptdata['V'] == 5) { // AES-256
10579 $out .= ' 5';
10580 $out .= ' /OE ('.TCPDF_STATIC::_escape($this->encryptdata['OE']).')';
10581 $out .= ' /UE ('.TCPDF_STATIC::_escape($this->encryptdata['UE']).')';
10582 $out .= ' /Perms ('.TCPDF_STATIC::_escape($this->encryptdata['perms']).')';
10583 } elseif ($this->encryptdata['V'] == 4) { // AES-128
10584 $out .= ' 4';
10585 } elseif ($this->encryptdata['V'] < 2) { // RC-40
10586 $out .= ' 2';
10587 } else { // RC-128
10588 $out .= ' 3';
10589 }
10590 $out .= ' /O ('.TCPDF_STATIC::_escape($this->encryptdata['O']).')';
10591 $out .= ' /U ('.TCPDF_STATIC::_escape($this->encryptdata['U']).')';
10592 $out .= ' /P '.$this->encryptdata['P'];
10593 if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
10594 $out .= ' /EncryptMetadata false';
10595 } else {
10596 $out .= ' /EncryptMetadata true';
10597 }
10598 }
10599 $out .= ' >>';
10600 $out .= "\n".'endobj';
10601 $this->_out($out);
10602 }
10603
10611 protected function _Uvalue() {
10612 if ($this->encryptdata['mode'] == 0) { // RC4-40
10613 return TCPDF_STATIC::_RC4($this->encryptdata['key'], TCPDF_STATIC::$enc_padding, $this->last_enc_key, $this->last_enc_key_c);
10614 } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
10615 $tmp = TCPDF_STATIC::_md5_16(TCPDF_STATIC::$enc_padding.$this->encryptdata['fileid']);
10616 $enc = TCPDF_STATIC::_RC4($this->encryptdata['key'], $tmp, $this->last_enc_key, $this->last_enc_key_c);
10617 $len = strlen($tmp);
10618 for ($i = 1; $i <= 19; ++$i) {
10619 $ek = '';
10620 for ($j = 0; $j < $len; ++$j) {
10621 $ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i);
10622 }
10623 $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10624 }
10625 $enc .= str_repeat("\x00", 16);
10626 return substr($enc, 0, 32);
10627 } elseif ($this->encryptdata['mode'] == 3) { // AES-256
10629 // User Validation Salt
10630 $this->encryptdata['UVS'] = substr($seed, 0, 8);
10631 // User Key Salt
10632 $this->encryptdata['UKS'] = substr($seed, 8, 16);
10633 return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
10634 }
10635 }
10636
10644 protected function _UEvalue() {
10645 $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
10646 return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']);
10647 }
10648
10656 protected function _Ovalue() {
10657 if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
10658 $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['owner_password']);
10659 if ($this->encryptdata['mode'] > 0) {
10660 for ($i = 0; $i < 50; ++$i) {
10661 $tmp = TCPDF_STATIC::_md5_16($tmp);
10662 }
10663 }
10664 $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
10665 $enc = TCPDF_STATIC::_RC4($owner_key, $this->encryptdata['user_password'], $this->last_enc_key, $this->last_enc_key_c);
10666 if ($this->encryptdata['mode'] > 0) {
10667 $len = strlen($owner_key);
10668 for ($i = 1; $i <= 19; ++$i) {
10669 $ek = '';
10670 for ($j = 0; $j < $len; ++$j) {
10671 $ek .= chr(ord($owner_key[$j]) ^ $i);
10672 }
10673 $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10674 }
10675 }
10676 return $enc;
10677 } elseif ($this->encryptdata['mode'] == 3) { // AES-256
10679 // Owner Validation Salt
10680 $this->encryptdata['OVS'] = substr($seed, 0, 8);
10681 // Owner Key Salt
10682 $this->encryptdata['OKS'] = substr($seed, 8, 16);
10683 return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
10684 }
10685 }
10686
10694 protected function _OEvalue() {
10695 $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
10696 return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']);
10697 }
10698
10707 protected function _fixAES256Password($password) {
10708 $psw = ''; // password to be returned
10709 $psw_array = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($password, $this->isunicode, $this->CurrentFont), $password, $this->rtl, $this->isunicode, $this->CurrentFont);
10710 foreach ($psw_array as $c) {
10711 $psw .= TCPDF_FONTS::unichr($c, $this->isunicode);
10712 }
10713 return substr($psw, 0, 127);
10714 }
10715
10722 protected function _generateencryptionkey() {
10723 $keybytelen = ($this->encryptdata['Length'] / 8);
10724 if (!$this->encryptdata['pubkey']) { // standard mode
10725 if ($this->encryptdata['mode'] == 3) { // AES-256
10726 // generate 256 bit random key
10727 $this->encryptdata['key'] = substr(hash('sha256', TCPDF_STATIC::getRandomSeed(), true), 0, $keybytelen);
10728 // truncate passwords
10729 $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
10730 $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
10731 // Compute U value
10732 $this->encryptdata['U'] = $this->_Uvalue();
10733 // Compute UE value
10734 $this->encryptdata['UE'] = $this->_UEvalue();
10735 // Compute O value
10736 $this->encryptdata['O'] = $this->_Ovalue();
10737 // Compute OE value
10738 $this->encryptdata['OE'] = $this->_OEvalue();
10739 // Compute P value
10740 $this->encryptdata['P'] = $this->encryptdata['protection'];
10741 // Computing the encryption dictionary's Perms (permissions) value
10742 $perms = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
10743 $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
10744 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
10745 $perms .= 'F';
10746 } else {
10747 $perms .= 'T';
10748 }
10749 $perms .= 'adb'; // bytes 9-11
10750 $perms .= 'nick'; // bytes 12-15
10751 $this->encryptdata['perms'] = TCPDF_STATIC::_AESnopad($this->encryptdata['key'], $perms);
10752 } else { // RC4-40, RC4-128, AES-128
10753 // Pad passwords
10754 $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10755 $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10756 // Compute O value
10757 $this->encryptdata['O'] = $this->_Ovalue();
10758 // get default permissions (reverse byte order)
10759 $permissions = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']);
10760 // Compute encryption key
10761 $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
10762 if ($this->encryptdata['mode'] > 0) {
10763 for ($i = 0; $i < 50; ++$i) {
10764 $tmp = TCPDF_STATIC::_md5_16(substr($tmp, 0, $keybytelen));
10765 }
10766 }
10767 $this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
10768 // Compute U value
10769 $this->encryptdata['U'] = $this->_Uvalue();
10770 // Compute P value
10771 $this->encryptdata['P'] = $this->encryptdata['protection'];
10772 }
10773 } else { // Public-Key mode
10774 // random 20-byte seed
10775 $seed = sha1(TCPDF_STATIC::getRandomSeed(), true);
10776 $recipient_bytes = '';
10777 foreach ($this->encryptdata['pubkeys'] as $pubkey) {
10778 // for each public certificate
10779 if (isset($pubkey['p'])) {
10780 $pkprotection = TCPDF_STATIC::getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
10781 } else {
10782 $pkprotection = $this->encryptdata['protection'];
10783 }
10784 // get default permissions (reverse byte order)
10785 $pkpermissions = TCPDF_STATIC::getEncPermissionsString($pkprotection);
10786 // envelope data
10787 $envelope = $seed.$pkpermissions;
10788 // write the envelope data to a temporary file
10789 $tempkeyfile = TCPDF_STATIC::getObjFilename('key', $this->file_id);
10790 $f = TCPDF_STATIC::fopenLocal($tempkeyfile, 'wb');
10791 if (!$f) {
10792 $this->Error('Unable to create temporary key file: '.$tempkeyfile);
10793 }
10794 $envelope_length = strlen($envelope);
10795 fwrite($f, $envelope, $envelope_length);
10796 fclose($f);
10797 $tempencfile = TCPDF_STATIC::getObjFilename('enc', $this->file_id);
10798 if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) {
10799 $this->Error('Unable to encrypt the file: '.$tempkeyfile);
10800 }
10801 // read encryption signature
10802 $signature = file_get_contents($tempencfile, false, null, $envelope_length);
10803 // extract signature
10804 $signature = substr($signature, strpos($signature, 'Content-Disposition'));
10805 $tmparr = explode("\n\n", $signature);
10806 $signature = trim($tmparr[1]);
10807 unset($tmparr);
10808 // decode signature
10809 $signature = base64_decode($signature);
10810 // convert signature to hex
10811 $hexsignature = current(unpack('H*', $signature));
10812 // store signature on recipients array
10813 $this->encryptdata['Recipients'][] = $hexsignature;
10814 // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
10815 $recipient_bytes .= $signature;
10816 }
10817 // calculate encryption key
10818 if ($this->encryptdata['mode'] == 3) { // AES-256
10819 $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
10820 } else { // RC4-40, RC4-128, AES-128
10821 $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
10822 }
10823 }
10824 }
10825
10840 public function SetProtection($permissions=array('print', 'modify', 'copy', 'annot-forms', 'fill-forms', 'extract', 'assemble', 'print-high'), $user_pass='', $owner_pass=null, $mode=0, $pubkeys=null) {
10841 if ($this->pdfa_mode) {
10842 // encryption is not allowed in PDF/A mode
10843 return;
10844 }
10845 $this->encryptdata['protection'] = TCPDF_STATIC::getUserPermissionCode($permissions, $mode);
10846 if (($pubkeys !== null) AND (is_array($pubkeys))) {
10847 // public-key mode
10848 $this->encryptdata['pubkeys'] = $pubkeys;
10849 if ($mode == 0) {
10850 // public-Key Security requires at least 128 bit
10851 $mode = 1;
10852 }
10853 if (!function_exists('openssl_pkcs7_encrypt')) {
10854 $this->Error('Public-Key Security requires openssl library.');
10855 }
10856 // Set Public-Key filter (available are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
10857 $this->encryptdata['pubkey'] = true;
10858 $this->encryptdata['Filter'] = 'Adobe.PubSec';
10859 $this->encryptdata['StmF'] = 'DefaultCryptFilter';
10860 $this->encryptdata['StrF'] = 'DefaultCryptFilter';
10861 } else {
10862 // standard mode (password mode)
10863 $this->encryptdata['pubkey'] = false;
10864 $this->encryptdata['Filter'] = 'Standard';
10865 $this->encryptdata['StmF'] = 'StdCF';
10866 $this->encryptdata['StrF'] = 'StdCF';
10867 }
10868 if ($mode > 1) { // AES
10869 if (!extension_loaded('openssl') && !extension_loaded('mcrypt')) {
10870 $this->Error('AES encryption requires openssl or mcrypt extension (http://www.php.net/manual/en/mcrypt.requirements.php).');
10871 }
10872 if (extension_loaded('openssl') && !in_array('aes-256-cbc', openssl_get_cipher_methods())) {
10873 $this->Error('AES encryption requires openssl/aes-256-cbc cypher.');
10874 }
10875 if (extension_loaded('mcrypt') && mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
10876 $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
10877 }
10878 if (($mode == 3) AND !function_exists('hash')) {
10879 // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
10880 $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
10881 }
10882 }
10883 if ($owner_pass === null) {
10884 $owner_pass = md5(TCPDF_STATIC::getRandomSeed());
10885 }
10886 $this->encryptdata['user_password'] = $user_pass;
10887 $this->encryptdata['owner_password'] = $owner_pass;
10888 $this->encryptdata['mode'] = $mode;
10889 switch ($mode) {
10890 case 0: { // RC4 40 bit
10891 $this->encryptdata['V'] = 1;
10892 $this->encryptdata['Length'] = 40;
10893 $this->encryptdata['CF']['CFM'] = 'V2';
10894 break;
10895 }
10896 case 1: { // RC4 128 bit
10897 $this->encryptdata['V'] = 2;
10898 $this->encryptdata['Length'] = 128;
10899 $this->encryptdata['CF']['CFM'] = 'V2';
10900 if ($this->encryptdata['pubkey']) {
10901 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
10902 $this->encryptdata['Recipients'] = array();
10903 }
10904 break;
10905 }
10906 case 2: { // AES 128 bit
10907 $this->encryptdata['V'] = 4;
10908 $this->encryptdata['Length'] = 128;
10909 $this->encryptdata['CF']['CFM'] = 'AESV2';
10910 $this->encryptdata['CF']['Length'] = 128;
10911 if ($this->encryptdata['pubkey']) {
10912 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
10913 $this->encryptdata['Recipients'] = array();
10914 }
10915 break;
10916 }
10917 case 3: { // AES 256 bit
10918 $this->encryptdata['V'] = 5;
10919 $this->encryptdata['Length'] = 256;
10920 $this->encryptdata['CF']['CFM'] = 'AESV3';
10921 $this->encryptdata['CF']['Length'] = 256;
10922 if ($this->encryptdata['pubkey']) {
10923 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
10924 $this->encryptdata['Recipients'] = array();
10925 }
10926 break;
10927 }
10928 }
10929 $this->encrypted = true;
10930 $this->encryptdata['fileid'] = TCPDF_STATIC::convertHexStringToString($this->file_id);
10931 $this->_generateencryptionkey();
10932 }
10933
10934 // END OF ENCRYPTION FUNCTIONS -------------------------
10935
10936 // START TRANSFORMATIONS SECTION -----------------------
10937
10946 public function StartTransform() {
10947 if ($this->state != 2) {
10948 return;
10949 }
10950 $this->_outSaveGraphicsState();
10951 if ($this->inxobj) {
10952 // we are inside an XObject template
10953 $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
10954 } else {
10955 $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
10956 }
10958 $this->transfmatrix[$this->transfmatrix_key] = array();
10959 }
10960
10969 public function StopTransform() {
10970 if ($this->state != 2) {
10971 return;
10972 }
10973 $this->_outRestoreGraphicsState();
10974 if (isset($this->transfmatrix[$this->transfmatrix_key])) {
10975 array_pop($this->transfmatrix[$this->transfmatrix_key]);
10977 }
10978 if ($this->inxobj) {
10979 // we are inside an XObject template
10980 array_pop($this->xobjects[$this->xobjid]['transfmrk']);
10981 } else {
10982 array_pop($this->transfmrk[$this->page]);
10983 }
10984 }
10994 public function ScaleX($s_x, $x='', $y='') {
10995 $this->Scale($s_x, 100, $x, $y);
10996 }
10997
11007 public function ScaleY($s_y, $x='', $y='') {
11008 $this->Scale(100, $s_y, $x, $y);
11009 }
11010
11020 public function ScaleXY($s, $x='', $y='') {
11021 $this->Scale($s, $s, $x, $y);
11022 }
11023
11034 public function Scale($s_x, $s_y, $x='', $y='') {
11035 if ($x === '') {
11036 $x = $this->x;
11037 }
11038 if ($y === '') {
11039 $y = $this->y;
11040 }
11041 if (($s_x == 0) OR ($s_y == 0)) {
11042 $this->Error('Please do not use values equal to zero for scaling');
11043 }
11044 $y = ($this->h - $y) * $this->k;
11045 $x *= $this->k;
11046 //calculate elements of transformation matrix
11047 $s_x /= 100;
11048 $s_y /= 100;
11049 $tm = array();
11050 $tm[0] = $s_x;
11051 $tm[1] = 0;
11052 $tm[2] = 0;
11053 $tm[3] = $s_y;
11054 $tm[4] = $x * (1 - $s_x);
11055 $tm[5] = $y * (1 - $s_y);
11056 //scale the coordinate system
11057 $this->Transform($tm);
11058 }
11059
11067 public function MirrorH($x='') {
11068 $this->Scale(-100, 100, $x);
11069 }
11070
11078 public function MirrorV($y='') {
11079 $this->Scale(100, -100, '', $y);
11080 }
11081
11090 public function MirrorP($x='',$y='') {
11091 $this->Scale(-100, -100, $x, $y);
11092 }
11093
11103 public function MirrorL($angle=0, $x='',$y='') {
11104 $this->Scale(-100, 100, $x, $y);
11105 $this->Rotate(-2*($angle-90), $x, $y);
11106 }
11107
11115 public function TranslateX($t_x) {
11116 $this->Translate($t_x, 0);
11117 }
11118
11126 public function TranslateY($t_y) {
11127 $this->Translate(0, $t_y);
11128 }
11129
11138 public function Translate($t_x, $t_y) {
11139 //calculate elements of transformation matrix
11140 $tm = array();
11141 $tm[0] = 1;
11142 $tm[1] = 0;
11143 $tm[2] = 0;
11144 $tm[3] = 1;
11145 $tm[4] = $t_x * $this->k;
11146 $tm[5] = -$t_y * $this->k;
11147 //translate the coordinate system
11148 $this->Transform($tm);
11149 }
11150
11160 public function Rotate($angle, $x='', $y='') {
11161 if ($x === '') {
11162 $x = $this->x;
11163 }
11164 if ($y === '') {
11165 $y = $this->y;
11166 }
11167 $y = ($this->h - $y) * $this->k;
11168 $x *= $this->k;
11169 //calculate elements of transformation matrix
11170 $tm = array();
11171 $tm[0] = cos(deg2rad($angle));
11172 $tm[1] = sin(deg2rad($angle));
11173 $tm[2] = -$tm[1];
11174 $tm[3] = $tm[0];
11175 $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
11176 $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
11177 //rotate the coordinate system around ($x,$y)
11178 $this->Transform($tm);
11179 }
11180
11190 public function SkewX($angle_x, $x='', $y='') {
11191 $this->Skew($angle_x, 0, $x, $y);
11192 }
11193
11203 public function SkewY($angle_y, $x='', $y='') {
11204 $this->Skew(0, $angle_y, $x, $y);
11205 }
11206
11217 public function Skew($angle_x, $angle_y, $x='', $y='') {
11218 if ($x === '') {
11219 $x = $this->x;
11220 }
11221 if ($y === '') {
11222 $y = $this->y;
11223 }
11224 if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
11225 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
11226 }
11227 $x *= $this->k;
11228 $y = ($this->h - $y) * $this->k;
11229 //calculate elements of transformation matrix
11230 $tm = array();
11231 $tm[0] = 1;
11232 $tm[1] = tan(deg2rad($angle_y));
11233 $tm[2] = tan(deg2rad($angle_x));
11234 $tm[3] = 1;
11235 $tm[4] = -$tm[2] * $y;
11236 $tm[5] = -$tm[1] * $x;
11237 //skew the coordinate system
11238 $this->Transform($tm);
11239 }
11240
11248 protected function Transform($tm) {
11249 if ($this->state != 2) {
11250 return;
11251 }
11252 $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
11253 // add tranformation matrix
11254 $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
11255 // update transformation mark
11256 if ($this->inxobj) {
11257 // we are inside an XObject template
11258 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
11259 $key = key($this->xobjects[$this->xobjid]['transfmrk']);
11260 $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
11261 }
11262 } elseif (end($this->transfmrk[$this->page]) !== false) {
11263 $key = key($this->transfmrk[$this->page]);
11264 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
11265 }
11266 }
11267
11268 // END TRANSFORMATIONS SECTION -------------------------
11269
11270 // START GRAPHIC FUNCTIONS SECTION ---------------------
11271 // The following section is based on the code provided by David Hernandez Sanz
11272
11280 public function SetLineWidth($width) {
11281 //Set line width
11282 $this->LineWidth = $width;
11283 $this->linestyleWidth = sprintf('%F w', ($width * $this->k));
11284 if ($this->state == 2) {
11285 $this->_out($this->linestyleWidth);
11286 }
11287 }
11288
11296 public function GetLineWidth() {
11297 return $this->LineWidth;
11298 }
11299
11323 public function SetLineStyle($style, $ret=false) {
11324 $s = ''; // string to be returned
11325 if (!is_array($style)) {
11326 return;
11327 }
11328 if (isset($style['width'])) {
11329 $this->LineWidth = $style['width'];
11330 $this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k));
11331 $s .= $this->linestyleWidth.' ';
11332 }
11333 if (isset($style['cap'])) {
11334 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
11335 if (isset($ca[$style['cap']])) {
11336 $this->linestyleCap = $ca[$style['cap']].' J';
11337 $s .= $this->linestyleCap.' ';
11338 }
11339 }
11340 if (isset($style['join'])) {
11341 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
11342 if (isset($ja[$style['join']])) {
11343 $this->linestyleJoin = $ja[$style['join']].' j';
11344 $s .= $this->linestyleJoin.' ';
11345 }
11346 }
11347 if (isset($style['dash'])) {
11348 $dash_string = '';
11349 if ($style['dash']) {
11350 if (preg_match('/^.+,/', $style['dash']) > 0) {
11351 $tab = explode(',', $style['dash']);
11352 } else {
11353 $tab = array($style['dash']);
11354 }
11355 $dash_string = '';
11356 foreach ($tab as $i => $v) {
11357 if ($i) {
11358 $dash_string .= ' ';
11359 }
11360 $dash_string .= sprintf('%F', $v);
11361 }
11362 }
11363 if (!isset($style['phase']) OR !$style['dash']) {
11364 $style['phase'] = 0;
11365 }
11366 $this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']);
11367 $s .= $this->linestyleDash.' ';
11368 }
11369 if (isset($style['color'])) {
11370 $s .= $this->SetDrawColorArray($style['color'], true).' ';
11371 }
11372 if (!$ret AND ($this->state == 2)) {
11373 $this->_out($s);
11374 }
11375 return $s;
11376 }
11377
11385 protected function _outPoint($x, $y) {
11386 if ($this->state == 2) {
11387 $this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k)));
11388 }
11389 }
11390
11399 protected function _outLine($x, $y) {
11400 if ($this->state == 2) {
11401 $this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k)));
11402 }
11403 }
11404
11415 protected function _outRect($x, $y, $w, $h, $op) {
11416 if ($this->state == 2) {
11417 $this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k), (($this->h - $y) * $this->k), ($w * $this->k), (-$h * $this->k), $op));
11418 }
11419 }
11420
11433 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
11434 if ($this->state == 2) {
11435 $this->_out(sprintf('%F %F %F %F %F %F c', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11436 }
11437 }
11438
11449 protected function _outCurveV($x2, $y2, $x3, $y3) {
11450 if ($this->state == 2) {
11451 $this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11452 }
11453 }
11454
11465 protected function _outCurveY($x1, $y1, $x3, $y3) {
11466 if ($this->state == 2) {
11467 $this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11468 }
11469 }
11470
11482 public function Line($x1, $y1, $x2, $y2, $style=array()) {
11483 if ($this->state != 2) {
11484 return;
11485 }
11486 if (is_array($style)) {
11487 $this->SetLineStyle($style);
11488 }
11489 $this->_outPoint($x1, $y1);
11490 $this->_outLine($x2, $y2);
11491 $this->_out('S');
11492 }
11493
11512 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
11513 if ($this->state != 2) {
11514 return;
11515 }
11516 if (empty($style)) {
11517 $style = 'S';
11518 }
11519 if (!(strpos($style, 'F') === false) AND !empty($fill_color)) {
11520 // set background color
11521 $this->SetFillColorArray($fill_color);
11522 }
11523 if (!empty($border_style)) {
11524 if (isset($border_style['all']) AND !empty($border_style['all'])) {
11525 //set global style for border
11526 $this->SetLineStyle($border_style['all']);
11527 $border_style = array();
11528 } else {
11529 // remove stroke operator from style
11530 $opnostroke = array('S' => '', 'D' => '', 's' => '', 'd' => '', 'B' => 'F', 'FD' => 'F', 'DF' => 'F', 'B*' => 'F*', 'F*D' => 'F*', 'DF*' => 'F*', 'b' => 'f', 'fd' => 'f', 'df' => 'f', 'b*' => 'f*', 'f*d' => 'f*', 'df*' => 'f*' );
11531 if (isset($opnostroke[$style])) {
11532 $style = $opnostroke[$style];
11533 }
11534 }
11535 }
11536 if (!empty($style)) {
11538 $this->_outRect($x, $y, $w, $h, $op);
11539 }
11540 if (!empty($border_style)) {
11541 $border_style2 = array();
11542 foreach ($border_style as $line => $value) {
11543 $length = strlen($line);
11544 for ($i = 0; $i < $length; ++$i) {
11545 $border_style2[$line[$i]] = $value;
11546 }
11547 }
11548 $border_style = $border_style2;
11549 if (isset($border_style['L']) AND $border_style['L']) {
11550 $this->Line($x, $y, $x, $y + $h, $border_style['L']);
11551 }
11552 if (isset($border_style['T']) AND $border_style['T']) {
11553 $this->Line($x, $y, $x + $w, $y, $border_style['T']);
11554 }
11555 if (isset($border_style['R']) AND $border_style['R']) {
11556 $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
11557 }
11558 if (isset($border_style['B']) AND $border_style['B']) {
11559 $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
11560 }
11561 }
11562 }
11563
11583 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
11584 if ($this->state != 2) {
11585 return;
11586 }
11587 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11588 $this->SetFillColorArray($fill_color);
11589 }
11591 if ($line_style) {
11592 $this->SetLineStyle($line_style);
11593 }
11594 $this->_outPoint($x0, $y0);
11595 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11596 $this->_out($op);
11597 }
11598
11613 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
11614 if ($this->state != 2) {
11615 return;
11616 }
11617 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11618 $this->SetFillColorArray($fill_color);
11619 }
11621 if ($op == 'f') {
11622 $line_style = array();
11623 }
11624 if ($line_style) {
11625 $this->SetLineStyle($line_style);
11626 }
11627 $this->_outPoint($x0, $y0);
11628 foreach ($segments as $segment) {
11629 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
11630 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11631 }
11632 $this->_out($op);
11633 }
11634
11653 public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11654 if ($this->state != 2) {
11655 return;
11656 }
11657 if (TCPDF_STATIC::empty_string($ry) OR ($ry == 0)) {
11658 $ry = $rx;
11659 }
11660 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11661 $this->SetFillColorArray($fill_color);
11662 }
11664 if ($op == 'f') {
11665 $line_style = array();
11666 }
11667 if ($line_style) {
11668 $this->SetLineStyle($line_style);
11669 }
11670 $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
11671 $this->_out($op);
11672 }
11673
11694 protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
11695 if (($rx <= 0) OR ($ry < 0)) {
11696 return;
11697 }
11698 $k = $this->k;
11699 if ($nc < 2) {
11700 $nc = 2;
11701 }
11702 $xmin = 2147483647;
11703 $ymin = 2147483647;
11704 $xmax = 0;
11705 $ymax = 0;
11706 if ($pie) {
11707 // center of the arc
11708 $this->_outPoint($xc, $yc);
11709 }
11710 $xang = deg2rad((float) $xang);
11711 $angs = deg2rad((float) $angs);
11712 $angf = deg2rad((float) $angf);
11713 if ($svg) {
11714 $as = $angs;
11715 $af = $angf;
11716 } else {
11717 $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
11718 $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
11719 }
11720 if ($as < 0) {
11721 $as += (2 * M_PI);
11722 }
11723 if ($af < 0) {
11724 $af += (2 * M_PI);
11725 }
11726 if ($ccw AND ($as > $af)) {
11727 // reverse rotation
11728 $as -= (2 * M_PI);
11729 } elseif (!$ccw AND ($as < $af)) {
11730 // reverse rotation
11731 $af -= (2 * M_PI);
11732 }
11733 $total_angle = ($af - $as);
11734 if ($nc < 2) {
11735 $nc = 2;
11736 }
11737 // total arcs to draw
11738 $nc *= (2 * abs($total_angle) / M_PI);
11739 $nc = round($nc) + 1;
11740 // angle of each arc
11741 $arcang = ($total_angle / $nc);
11742 // center point in PDF coordinates
11743 $x0 = $xc;
11744 $y0 = ($this->h - $yc);
11745 // starting angle
11746 $ang = $as;
11747 $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
11748 $cos_xang = cos($xang);
11749 $sin_xang = sin($xang);
11750 $cos_ang = cos($ang);
11751 $sin_ang = sin($ang);
11752 // first arc point
11753 $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11754 $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11755 // first Bezier control point
11756 $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11757 $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11758 if ($pie) {
11759 // line from center to arc starting point
11760 $this->_outLine($px1, $this->h - $py1);
11761 } elseif ($startpoint) {
11762 // arc starting point
11763 $this->_outPoint($px1, $this->h - $py1);
11764 }
11765 // draw arcs
11766 for ($i = 1; $i <= $nc; ++$i) {
11767 // starting angle
11768 $ang = $as + ($i * $arcang);
11769 if ($i == $nc) {
11770 $ang = $af;
11771 }
11772 $cos_ang = cos($ang);
11773 $sin_ang = sin($ang);
11774 // second arc point
11775 $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11776 $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11777 // second Bezier control point
11778 $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11779 $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11780 // draw arc
11781 $cx1 = ($px1 + $qx1);
11782 $cy1 = ($this->h - ($py1 + $qy1));
11783 $cx2 = ($px2 - $qx2);
11784 $cy2 = ($this->h - ($py2 - $qy2));
11785 $cx3 = $px2;
11786 $cy3 = ($this->h - $py2);
11787 $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
11788 // get bounding box coordinates
11789 $xmin = min($xmin, $cx1, $cx2, $cx3);
11790 $ymin = min($ymin, $cy1, $cy2, $cy3);
11791 $xmax = max($xmax, $cx1, $cx2, $cx3);
11792 $ymax = max($ymax, $cy1, $cy2, $cy3);
11793 // move to next point
11794 $px1 = $px2;
11795 $py1 = $py2;
11796 $qx1 = $qx2;
11797 $qy1 = $qy2;
11798 }
11799 if ($pie) {
11800 $this->_outLine($xc, $yc);
11801 // get bounding box coordinates
11802 $xmin = min($xmin, $xc);
11803 $ymin = min($ymin, $yc);
11804 $xmax = max($xmax, $xc);
11805 $ymax = max($ymax, $yc);
11806 }
11807 return array($xmin, $ymin, $xmax, $ymax);
11808 }
11809
11825 public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11826 $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
11827 }
11828
11843 public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
11844 $this->Polygon($p, $style, $line_style, $fill_color, false);
11845 }
11846
11862 public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
11863 if ($this->state != 2) {
11864 return;
11865 }
11866 $nc = count($p); // number of coordinates
11867 $np = $nc / 2; // number of points
11868 if ($closed) {
11869 // close polygon by adding the first 2 points at the end (one line)
11870 for ($i = 0; $i < 4; ++$i) {
11871 $p[$nc + $i] = $p[$i];
11872 }
11873 // copy style for the last added line
11874 if (isset($line_style[0])) {
11875 $line_style[$np] = $line_style[0];
11876 }
11877 $nc += 4;
11878 }
11879 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11880 $this->SetFillColorArray($fill_color);
11881 }
11883 if ($op == 'f') {
11884 $line_style = array();
11885 }
11886 $draw = true;
11887 if ($line_style) {
11888 if (isset($line_style['all'])) {
11889 $this->SetLineStyle($line_style['all']);
11890 } else {
11891 $draw = false;
11892 if ($op == 'B') {
11893 // draw fill
11894 $op = 'f';
11895 $this->_outPoint($p[0], $p[1]);
11896 for ($i = 2; $i < $nc; $i = $i + 2) {
11897 $this->_outLine($p[$i], $p[$i + 1]);
11898 }
11899 $this->_out($op);
11900 }
11901 // draw outline
11902 $this->_outPoint($p[0], $p[1]);
11903 for ($i = 2; $i < $nc; $i = $i + 2) {
11904 $line_num = ($i / 2) - 1;
11905 if (isset($line_style[$line_num])) {
11906 if ($line_style[$line_num] != 0) {
11907 if (is_array($line_style[$line_num])) {
11908 $this->_out('S');
11909 $this->SetLineStyle($line_style[$line_num]);
11910 $this->_outPoint($p[$i - 2], $p[$i - 1]);
11911 $this->_outLine($p[$i], $p[$i + 1]);
11912 $this->_out('S');
11913 $this->_outPoint($p[$i], $p[$i + 1]);
11914 } else {
11915 $this->_outLine($p[$i], $p[$i + 1]);
11916 }
11917 }
11918 } else {
11919 $this->_outLine($p[$i], $p[$i + 1]);
11920 }
11921 }
11922 $this->_out($op);
11923 }
11924 }
11925 if ($draw) {
11926 $this->_outPoint($p[0], $p[1]);
11927 for ($i = 2; $i < $nc; $i = $i + 2) {
11928 $this->_outLine($p[$i], $p[$i + 1]);
11929 }
11930 $this->_out($op);
11931 }
11932 }
11933
11963 public function RegularPolygon($x0, $y0, $r, $ns, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
11964 if (3 > $ns) {
11965 $ns = 3;
11966 }
11967 if ($draw_circle) {
11968 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
11969 }
11970 $p = array();
11971 for ($i = 0; $i < $ns; ++$i) {
11972 $a = $angle + ($i * 360 / $ns);
11973 $a_rad = deg2rad((float) $a);
11974 $p[] = $x0 + ($r * sin($a_rad));
11975 $p[] = $y0 + ($r * cos($a_rad));
11976 }
11977 $this->Polygon($p, $style, $line_style, $fill_color);
11978 }
11979
12011 public function StarPolygon($x0, $y0, $r, $nv, $ng, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
12012 if ($nv < 2) {
12013 $nv = 2;
12014 }
12015 if ($draw_circle) {
12016 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12017 }
12018 $p2 = array();
12019 $visited = array();
12020 for ($i = 0; $i < $nv; ++$i) {
12021 $a = $angle + ($i * 360 / $nv);
12022 $a_rad = deg2rad((float) $a);
12023 $p2[] = $x0 + ($r * sin($a_rad));
12024 $p2[] = $y0 + ($r * cos($a_rad));
12025 $visited[] = false;
12026 }
12027 $p = array();
12028 $i = 0;
12029 do {
12030 $p[] = $p2[$i * 2];
12031 $p[] = $p2[($i * 2) + 1];
12032 $visited[$i] = true;
12033 $i += $ng;
12034 $i %= $nv;
12035 } while (!$visited[$i]);
12036 $this->Polygon($p, $style, $line_style, $fill_color);
12037 }
12038
12053 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12054 $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
12055 }
12056
12072 public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12073 if ($this->state != 2) {
12074 return;
12075 }
12076 if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
12077 // Not rounded
12078 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
12079 return;
12080 }
12081 // Rounded
12082 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12083 $this->SetFillColorArray($fill_color);
12084 }
12086 if ($op == 'f') {
12087 $border_style = array();
12088 }
12089 if ($border_style) {
12091 }
12092 $MyArc = 4 / 3 * (sqrt(2) - 1);
12093 $this->_outPoint($x + $rx, $y);
12094 $xc = $x + $w - $rx;
12095 $yc = $y + $ry;
12096 $this->_outLine($xc, $y);
12097 if ($round_corner[0]) {
12098 $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
12099 } else {
12100 $this->_outLine($x + $w, $y);
12101 }
12102 $xc = $x + $w - $rx;
12103 $yc = $y + $h - $ry;
12104 $this->_outLine($x + $w, $yc);
12105 if ($round_corner[1]) {
12106 $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
12107 } else {
12108 $this->_outLine($x + $w, $y + $h);
12109 }
12110 $xc = $x + $rx;
12111 $yc = $y + $h - $ry;
12112 $this->_outLine($xc, $y + $h);
12113 if ($round_corner[2]) {
12114 $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
12115 } else {
12116 $this->_outLine($x, $y + $h);
12117 }
12118 $xc = $x + $rx;
12119 $yc = $y + $ry;
12120 $this->_outLine($x, $yc);
12121 if ($round_corner[3]) {
12122 $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
12123 } else {
12124 $this->_outLine($x, $y);
12125 $this->_outLine($x + $rx, $y);
12126 }
12127 $this->_out($op);
12128 }
12129
12142 public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
12143 // getting arrow direction angle
12144 // 0 deg angle is when both arms go along X axis. angle grows clockwise.
12145 $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
12146 if ($dir_angle < 0) {
12147 $dir_angle += (2 * M_PI);
12148 }
12149 $arm_angle = deg2rad($arm_angle);
12150 $sx1 = $x1;
12151 $sy1 = $y1;
12152 if ($head_style > 0) {
12153 // calculate the stopping point for the arrow shaft
12154 $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
12155 $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
12156 }
12157 // main arrow line / shaft
12158 $this->Line($x0, $y0, $sx1, $sy1);
12159 // left arrowhead arm tip
12160 $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
12161 $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
12162 // right arrowhead arm tip
12163 $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
12164 $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
12165 $mode = 'D';
12166 $style = array();
12167 switch ($head_style) {
12168 case 0: {
12169 // draw only arrowhead arms
12170 $mode = 'D';
12171 $style = array(1, 1, 0);
12172 break;
12173 }
12174 case 1: {
12175 // draw closed arrowhead, but no fill
12176 $mode = 'D';
12177 break;
12178 }
12179 case 2: {
12180 // closed and filled arrowhead
12181 $mode = 'DF';
12182 break;
12183 }
12184 case 3: {
12185 // filled arrowhead
12186 $mode = 'F';
12187 break;
12188 }
12189 }
12190 $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
12191 }
12192
12193 // END GRAPHIC FUNCTIONS SECTION -----------------------
12194
12207 public function setDestination($name, $y=-1, $page='', $x=-1) {
12208 // remove unsupported characters
12209 $name = TCPDF_STATIC::encodeNameObject($name);
12210 if (TCPDF_STATIC::empty_string($name)) {
12211 return false;
12212 }
12213 if ($y == -1) {
12214 $y = $this->GetY();
12215 } elseif ($y < 0) {
12216 $y = 0;
12217 } elseif ($y > $this->h) {
12218 $y = $this->h;
12219 }
12220 if ($x == -1) {
12221 $x = $this->GetX();
12222 } elseif ($x < 0) {
12223 $x = 0;
12224 } elseif ($x > $this->w) {
12225 $x = $this->w;
12226 }
12227 $fixed = false;
12228 if (!empty($page) AND ($page[0] == '*')) {
12229 $page = intval(substr($page, 1));
12230 // this page number will not be changed when moving/add/deleting pages
12231 $fixed = true;
12232 }
12233 if (empty($page)) {
12234 $page = $this->PageNo();
12235 if (empty($page)) {
12236 return;
12237 }
12238 }
12239 $this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed);
12240 return $name;
12241 }
12242
12250 public function getDestination() {
12251 return $this->dests;
12252 }
12253
12260 protected function _putdests() {
12261 if (empty($this->dests)) {
12262 return;
12263 }
12264 $this->n_dests = $this->_newobj();
12265 $out = ' <<';
12266 foreach($this->dests as $name => $o) {
12267 $out .= ' /'.$name.' '.sprintf('[%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
12268 }
12269 $out .= ' >>';
12270 $out .= "\n".'endobj';
12271 $this->_out($out);
12272 }
12273
12286 public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12287 $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link);
12288 }
12289
12303 public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12304 if ($level < 0) {
12305 $level = 0;
12306 }
12307 if (isset($this->outlines[0])) {
12308 $lastoutline = end($this->outlines);
12309 $maxlevel = $lastoutline['l'] + 1;
12310 } else {
12311 $maxlevel = 0;
12312 }
12313 if ($level > $maxlevel) {
12314 $level = $maxlevel;
12315 }
12316 if ($y == -1) {
12317 $y = $this->GetY();
12318 } elseif ($y < 0) {
12319 $y = 0;
12320 } elseif ($y > $this->h) {
12321 $y = $this->h;
12322 }
12323 if ($x == -1) {
12324 $x = $this->GetX();
12325 } elseif ($x < 0) {
12326 $x = 0;
12327 } elseif ($x > $this->w) {
12328 $x = $this->w;
12329 }
12330 $fixed = false;
12331 if (!empty($page) AND ($page[0] == '*')) {
12332 $page = intval(substr($page, 1));
12333 // this page number will not be changed when moving/add/deleting pages
12334 $fixed = true;
12335 }
12336 if (empty($page)) {
12337 $page = $this->PageNo();
12338 if (empty($page)) {
12339 return;
12340 }
12341 }
12342 $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed, 's' => strtoupper($style), 'c' => $color, 'u' => $link);
12343 }
12344
12350 protected function sortBookmarks() {
12351 // get sorting columns
12352 $outline_p = array();
12353 $outline_y = array();
12354 foreach ($this->outlines as $key => $row) {
12355 $outline_p[$key] = $row['p'];
12356 $outline_k[$key] = $key;
12357 }
12358 // sort outlines by page and original position
12359 array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
12360 }
12361
12368 protected function _putbookmarks() {
12369 $nb = count($this->outlines);
12370 if ($nb == 0) {
12371 return;
12372 }
12373 // sort bookmarks
12374 $this->sortBookmarks();
12375 $lru = array();
12376 $level = 0;
12377 foreach ($this->outlines as $i => $o) {
12378 if ($o['l'] > 0) {
12379 $parent = $lru[($o['l'] - 1)];
12380 //Set parent and last pointers
12381 $this->outlines[$i]['parent'] = $parent;
12382 $this->outlines[$parent]['last'] = $i;
12383 if ($o['l'] > $level) {
12384 //Level increasing: set first pointer
12385 $this->outlines[$parent]['first'] = $i;
12386 }
12387 } else {
12388 $this->outlines[$i]['parent'] = $nb;
12389 }
12390 if (($o['l'] <= $level) AND ($i > 0)) {
12391 //Set prev and next pointers
12392 $prev = $lru[$o['l']];
12393 $this->outlines[$prev]['next'] = $i;
12394 $this->outlines[$i]['prev'] = $prev;
12395 }
12396 $lru[$o['l']] = $i;
12397 $level = $o['l'];
12398 }
12399 //Outline items
12400 $n = $this->n + 1;
12401 $nltags = '/<br[\s]?\/>|<\/(blockquote|dd|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|p|pre|ul|tcpdf|table|tr|td)>/si';
12402 foreach ($this->outlines as $i => $o) {
12403 $oid = $this->_newobj();
12404 // covert HTML title to string
12405 $title = preg_replace($nltags, "\n", $o['t']);
12406 $title = preg_replace("/[\r]+/si", '', $title);
12407 $title = preg_replace("/[\n]+/si", "\n", $title);
12408 $title = strip_tags($title);
12409 $title = $this->stringTrim($title);
12410 $out = '<</Title '.$this->_textstring($title, $oid);
12411 $out .= ' /Parent '.($n + $o['parent']).' 0 R';
12412 if (isset($o['prev'])) {
12413 $out .= ' /Prev '.($n + $o['prev']).' 0 R';
12414 }
12415 if (isset($o['next'])) {
12416 $out .= ' /Next '.($n + $o['next']).' 0 R';
12417 }
12418 if (isset($o['first'])) {
12419 $out .= ' /First '.($n + $o['first']).' 0 R';
12420 }
12421 if (isset($o['last'])) {
12422 $out .= ' /Last '.($n + $o['last']).' 0 R';
12423 }
12424 if (isset($o['u']) AND !empty($o['u'])) {
12425 // link
12426 if (is_string($o['u'])) {
12427 if ($o['u'][0] == '#') {
12428 // internal destination
12429 $out .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($o['u'], 1));
12430 } elseif ($o['u'][0] == '%') {
12431 // embedded PDF file
12432 $filename = basename(substr($o['u'], 1));
12433 $out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
12434 } elseif ($o['u'][0] == '*') {
12435 // embedded generic file
12436 $filename = basename(substr($o['u'], 1));
12437 $jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});';
12438 $out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>';
12439 } else {
12440 // external URI link
12441 $out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>';
12442 }
12443 } elseif (isset($this->links[$o['u']])) {
12444 // internal link ID
12445 $l = $this->links[$o['u']];
12446 if (isset($this->page_obj_id[($l['p'])])) {
12447 $out .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l['p'])], ($this->pagedim[$l['p']]['h'] - ($l['y'] * $this->k)));
12448 }
12449 }
12450 } elseif (isset($this->page_obj_id[($o['p'])])) {
12451 // link to a page
12452 $out .= ' '.sprintf('/Dest [%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
12453 }
12454 // set font style
12455 $style = 0;
12456 if (!empty($o['s'])) {
12457 // bold
12458 if (strpos($o['s'], 'B') !== false) {
12459 $style |= 2;
12460 }
12461 // oblique
12462 if (strpos($o['s'], 'I') !== false) {
12463 $style |= 1;
12464 }
12465 }
12466 $out .= sprintf(' /F %d', $style);
12467 // set bookmark color
12468 if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
12469 $color = array_values($o['c']);
12470 $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
12471 } else {
12472 // black
12473 $out .= ' /C [0.0 0.0 0.0]';
12474 }
12475 $out .= ' /Count 0'; // normally closed item
12476 $out .= ' >>';
12477 $out .= "\n".'endobj';
12478 $this->_out($out);
12479 }
12480 //Outline root
12481 $this->OutlineRoot = $this->_newobj();
12482 $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj');
12483 }
12484
12485 // --- JAVASCRIPT ------------------------------------------------------
12486
12494 public function IncludeJS($script) {
12495 $this->javascript .= $script;
12496 }
12497
12507 public function addJavascriptObject($script, $onload=false) {
12508 if ($this->pdfa_mode) {
12509 // javascript is not allowed in PDF/A mode
12510 return false;
12511 }
12512 ++$this->n;
12513 $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload);
12514 return $this->n;
12515 }
12516
12523 protected function _putjavascript() {
12524 if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) {
12525 return;
12526 }
12527 if (strpos($this->javascript, 'this.addField') > 0) {
12528 if (!$this->ur['enabled']) {
12529 //$this->setUserRights();
12530 }
12531 // the following two lines are used to avoid form fields duplication after saving
12532 // The addField method only works when releasing user rights (UR3)
12533 $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
12534 $jsb = "getField('tcpdfdocsaved').value='saved';";
12535 $this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
12536 }
12537 // name tree for javascript
12538 $this->n_js = '<< /Names [';
12539 if (!empty($this->javascript)) {
12540 $this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
12541 }
12542 if (!empty($this->js_objects)) {
12543 foreach ($this->js_objects as $key => $val) {
12544 if ($val['onload']) {
12545 $this->n_js .= ' (JS'.$key.') '.$key.' 0 R';
12546 }
12547 }
12548 }
12549 $this->n_js .= ' ] >>';
12550 // default Javascript object
12551 if (!empty($this->javascript)) {
12552 $obj_id = $this->_newobj();
12553 $out = '<< /S /JavaScript';
12554 $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id);
12555 $out .= ' >>';
12556 $out .= "\n".'endobj';
12557 $this->_out($out);
12558 }
12559 // additional Javascript objects
12560 if (!empty($this->js_objects)) {
12561 foreach ($this->js_objects as $key => $val) {
12562 $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
12563 $this->_out($out);
12564 }
12565 }
12566 }
12567
12581 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
12582 if ($this->rtl) {
12583 $x = $x - $w;
12584 }
12585 // the followind avoid fields duplication after saving the document
12586 $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {";
12587 $k = $this->k;
12588 $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%u,[%F,%F,%F,%F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n";
12589 $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
12590 while (list($key, $val) = each($prop)) {
12591 if (strcmp(substr($key, -5), 'Color') == 0) {
12592 $val = TCPDF_COLORS::_JScolor($val);
12593 } else {
12594 $val = "'".$val."'";
12595 }
12596 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
12597 }
12598 if ($this->rtl) {
12599 $this->x -= $w;
12600 } else {
12601 $this->x += $w;
12602 }
12603 $this->javascript .= '}';
12604 }
12605
12606 // --- FORM FIELDS -----------------------------------------------------
12607
12608
12609
12617 public function setFormDefaultProp($prop=array()) {
12618 $this->default_form_prop = $prop;
12619 }
12620
12628 public function getFormDefaultProp() {
12630 }
12631
12646 public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12647 if ($x === '') {
12648 $x = $this->x;
12649 }
12650 if ($y === '') {
12651 $y = $this->y;
12652 }
12653 // check page for no-write regions and adapt page margins if necessary
12654 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12655 if ($js) {
12656 $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
12657 return;
12658 }
12659 // get default style
12660 $prop = array_merge($this->getFormDefaultProp(), $prop);
12661 // get annotation data
12662 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12663 // set default appearance stream
12664 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12665 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12666 $popt['da'] = $fontstyle;
12667 // build appearance stream
12668 $popt['ap'] = array();
12669 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12670 $text = '';
12671 if (isset($prop['value']) AND !empty($prop['value'])) {
12672 $text = $prop['value'];
12673 } elseif (isset($opt['v']) AND !empty($opt['v'])) {
12674 $text = $opt['v'];
12675 }
12676 $tmpid = $this->startTemplate($w, $h, false);
12677 $align = '';
12678 if (isset($popt['q'])) {
12679 switch ($popt['q']) {
12680 case 0: {
12681 $align = 'L';
12682 break;
12683 }
12684 case 1: {
12685 $align = 'C';
12686 break;
12687 }
12688 case 2: {
12689 $align = 'R';
12690 break;
12691 }
12692 default: {
12693 $align = '';
12694 break;
12695 }
12696 }
12697 }
12698 $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12699 $this->endTemplate();
12700 --$this->n;
12701 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12702 unset($this->xobjects[$tmpid]);
12703 $popt['ap']['n'] .= 'Q EMC';
12704 // merge options
12705 $opt = array_merge($popt, $opt);
12706 // remove some conflicting options
12707 unset($opt['bs']);
12708 // set remaining annotation data
12709 $opt['Subtype'] = 'Widget';
12710 $opt['ft'] = 'Tx';
12711 $opt['t'] = $name;
12712 // Additional annotation's parameters (check _putannotsobj() method):
12713 //$opt['f']
12714 //$opt['as']
12715 //$opt['bs']
12716 //$opt['be']
12717 //$opt['c']
12718 //$opt['border']
12719 //$opt['h']
12720 //$opt['mk'];
12721 //$opt['mk']['r']
12722 //$opt['mk']['bc'];
12723 //$opt['mk']['bg'];
12724 unset($opt['mk']['ca']);
12725 unset($opt['mk']['rc']);
12726 unset($opt['mk']['ac']);
12727 unset($opt['mk']['i']);
12728 unset($opt['mk']['ri']);
12729 unset($opt['mk']['ix']);
12730 unset($opt['mk']['if']);
12731 //$opt['mk']['if']['sw'];
12732 //$opt['mk']['if']['s'];
12733 //$opt['mk']['if']['a'];
12734 //$opt['mk']['if']['fb'];
12735 unset($opt['mk']['tp']);
12736 //$opt['tu']
12737 //$opt['tm']
12738 //$opt['ff']
12739 //$opt['v']
12740 //$opt['dv']
12741 //$opt['a']
12742 //$opt['aa']
12743 //$opt['q']
12744 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12745 if ($this->rtl) {
12746 $this->x -= $w;
12747 } else {
12748 $this->x += $w;
12749 }
12750 }
12751
12767 public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
12768 if ($x === '') {
12769 $x = $this->x;
12770 }
12771 if ($y === '') {
12772 $y = $this->y;
12773 }
12774 // check page for no-write regions and adapt page margins if necessary
12775 list($x, $y) = $this->checkPageRegions($w, $x, $y);
12776 if ($js) {
12777 $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
12778 return;
12779 }
12780 if (TCPDF_STATIC::empty_string($onvalue)) {
12781 $onvalue = 'On';
12782 }
12783 if ($checked) {
12784 $defval = $onvalue;
12785 } else {
12786 $defval = 'Off';
12787 }
12788 // set font
12789 $font = 'zapfdingbats';
12790 if ($this->pdfa_mode) {
12791 // all fonts must be embedded
12792 $font = 'pdfa'.$font;
12793 }
12794 $this->AddFont($font);
12795 $tmpfont = $this->getFontBuffer($font);
12796 // set data for parent group
12797 if (!isset($this->radiobutton_groups[$this->page])) {
12798 $this->radiobutton_groups[$this->page] = array();
12799 }
12800 if (!isset($this->radiobutton_groups[$this->page][$name])) {
12801 $this->radiobutton_groups[$this->page][$name] = array();
12802 ++$this->n;
12803 $this->radiobutton_groups[$this->page][$name]['n'] = $this->n;
12804 $this->radio_groups[] = $this->n;
12805 }
12806 $kid = ($this->n + 1);
12807 // save object ID to be added on Kids entry on parent object
12808 $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval);
12809 // get default style
12810 $prop = array_merge($this->getFormDefaultProp(), $prop);
12811 $prop['NoToggleToOff'] = 'true';
12812 $prop['Radio'] = 'true';
12813 $prop['borderStyle'] = 'inset';
12814 // get annotation data
12815 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12816 // set additional default options
12817 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
12818 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
12819 $popt['da'] = $fontstyle;
12820 // build appearance stream
12821 $popt['ap'] = array();
12822 $popt['ap']['n'] = array();
12823 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k);
12824 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
12825 $popt['ap']['n'][$onvalue] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(108).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
12826 $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(109).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
12827 if (!isset($popt['mk'])) {
12828 $popt['mk'] = array();
12829 }
12830 $popt['mk']['ca'] = '(l)';
12831 // merge options
12832 $opt = array_merge($popt, $opt);
12833 // set remaining annotation data
12834 $opt['Subtype'] = 'Widget';
12835 $opt['ft'] = 'Btn';
12836 if ($checked) {
12837 $opt['v'] = array('/'.$onvalue);
12838 $opt['as'] = $onvalue;
12839 } else {
12840 $opt['as'] = 'Off';
12841 }
12842 // store readonly flag
12843 if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) {
12844 $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false;
12845 }
12846 $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64);
12847 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
12848 if ($this->rtl) {
12849 $this->x -= $w;
12850 } else {
12851 $this->x += $w;
12852 }
12853 }
12854
12870 public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12871 if ($x === '') {
12872 $x = $this->x;
12873 }
12874 if ($y === '') {
12875 $y = $this->y;
12876 }
12877 // check page for no-write regions and adapt page margins if necessary
12878 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12879 if ($js) {
12880 $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
12881 $s = '';
12882 foreach ($values as $value) {
12883 if (is_array($value)) {
12884 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12885 } else {
12886 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12887 }
12888 }
12889 $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12890 return;
12891 }
12892 // get default style
12893 $prop = array_merge($this->getFormDefaultProp(), $prop);
12894 // get annotation data
12895 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12896 // set additional default values
12897 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12898 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12899 $popt['da'] = $fontstyle;
12900 // build appearance stream
12901 $popt['ap'] = array();
12902 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12903 $text = '';
12904 foreach($values as $item) {
12905 if (is_array($item)) {
12906 $text .= $item[1]."\n";
12907 } else {
12908 $text .= $item."\n";
12909 }
12910 }
12911 $tmpid = $this->startTemplate($w, $h, false);
12912 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12913 $this->endTemplate();
12914 --$this->n;
12915 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12916 unset($this->xobjects[$tmpid]);
12917 $popt['ap']['n'] .= 'Q EMC';
12918 // merge options
12919 $opt = array_merge($popt, $opt);
12920 // set remaining annotation data
12921 $opt['Subtype'] = 'Widget';
12922 $opt['ft'] = 'Ch';
12923 $opt['t'] = $name;
12924 $opt['opt'] = $values;
12925 unset($opt['mk']['ca']);
12926 unset($opt['mk']['rc']);
12927 unset($opt['mk']['ac']);
12928 unset($opt['mk']['i']);
12929 unset($opt['mk']['ri']);
12930 unset($opt['mk']['ix']);
12931 unset($opt['mk']['if']);
12932 unset($opt['mk']['tp']);
12933 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12934 if ($this->rtl) {
12935 $this->x -= $w;
12936 } else {
12937 $this->x += $w;
12938 }
12939 }
12940
12956 public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12957 if ($x === '') {
12958 $x = $this->x;
12959 }
12960 if ($y === '') {
12961 $y = $this->y;
12962 }
12963 // check page for no-write regions and adapt page margins if necessary
12964 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12965 if ($js) {
12966 $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
12967 $s = '';
12968 foreach ($values as $value) {
12969 if (is_array($value)) {
12970 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12971 } else {
12972 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12973 }
12974 }
12975 $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12976 return;
12977 }
12978 // get default style
12979 $prop = array_merge($this->getFormDefaultProp(), $prop);
12980 $prop['Combo'] = true;
12981 // get annotation data
12982 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12983 // set additional default options
12984 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12985 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12986 $popt['da'] = $fontstyle;
12987 // build appearance stream
12988 $popt['ap'] = array();
12989 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12990 $text = '';
12991 foreach($values as $item) {
12992 if (is_array($item)) {
12993 $text .= $item[1]."\n";
12994 } else {
12995 $text .= $item."\n";
12996 }
12997 }
12998 $tmpid = $this->startTemplate($w, $h, false);
12999 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13000 $this->endTemplate();
13001 --$this->n;
13002 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13003 unset($this->xobjects[$tmpid]);
13004 $popt['ap']['n'] .= 'Q EMC';
13005 // merge options
13006 $opt = array_merge($popt, $opt);
13007 // set remaining annotation data
13008 $opt['Subtype'] = 'Widget';
13009 $opt['ft'] = 'Ch';
13010 $opt['t'] = $name;
13011 $opt['opt'] = $values;
13012 unset($opt['mk']['ca']);
13013 unset($opt['mk']['rc']);
13014 unset($opt['mk']['ac']);
13015 unset($opt['mk']['i']);
13016 unset($opt['mk']['ri']);
13017 unset($opt['mk']['ix']);
13018 unset($opt['mk']['if']);
13019 unset($opt['mk']['tp']);
13020 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13021 if ($this->rtl) {
13022 $this->x -= $w;
13023 } else {
13024 $this->x += $w;
13025 }
13026 }
13027
13043 public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
13044 if ($x === '') {
13045 $x = $this->x;
13046 }
13047 if ($y === '') {
13048 $y = $this->y;
13049 }
13050 // check page for no-write regions and adapt page margins if necessary
13051 list($x, $y) = $this->checkPageRegions($w, $x, $y);
13052 if ($js) {
13053 $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
13054 return;
13055 }
13056 if (!isset($prop['value'])) {
13057 $prop['value'] = array('Yes');
13058 }
13059 // get default style
13060 $prop = array_merge($this->getFormDefaultProp(), $prop);
13061 $prop['borderStyle'] = 'inset';
13062 // get annotation data
13063 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13064 // set additional default options
13065 $font = 'zapfdingbats';
13066 if ($this->pdfa_mode) {
13067 // all fonts must be embedded
13068 $font = 'pdfa'.$font;
13069 }
13070 $this->AddFont($font);
13071 $tmpfont = $this->getFontBuffer($font);
13072 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
13073 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
13074 $popt['da'] = $fontstyle;
13075 // build appearance stream
13076 $popt['ap'] = array();
13077 $popt['ap']['n'] = array();
13078 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k);
13079 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
13080 $popt['ap']['n']['Yes'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(110).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
13081 $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(111).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
13082 // merge options
13083 $opt = array_merge($popt, $opt);
13084 // set remaining annotation data
13085 $opt['Subtype'] = 'Widget';
13086 $opt['ft'] = 'Btn';
13087 $opt['t'] = $name;
13088 if (TCPDF_STATIC::empty_string($onvalue)) {
13089 $onvalue = 'Yes';
13090 }
13091 $opt['opt'] = array($onvalue);
13092 if ($checked) {
13093 $opt['v'] = array('/Yes');
13094 $opt['as'] = 'Yes';
13095 } else {
13096 $opt['v'] = array('/Off');
13097 $opt['as'] = 'Off';
13098 }
13099 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
13100 if ($this->rtl) {
13101 $this->x -= $w;
13102 } else {
13103 $this->x += $w;
13104 }
13105 }
13106
13123 public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13124 if ($x === '') {
13125 $x = $this->x;
13126 }
13127 if ($y === '') {
13128 $y = $this->y;
13129 }
13130 // check page for no-write regions and adapt page margins if necessary
13131 list($x, $y) = $this->checkPageRegions($h, $x, $y);
13132 if ($js) {
13133 $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
13134 $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
13135 $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
13136 $this->javascript .= 'f'.$name.".highlight='push';\n";
13137 $this->javascript .= 'f'.$name.".print=false;\n";
13138 return;
13139 }
13140 // get default style
13141 $prop = array_merge($this->getFormDefaultProp(), $prop);
13142 $prop['Pushbutton'] = 'true';
13143 $prop['highlight'] = 'push';
13144 $prop['display'] = 'display.noPrint';
13145 // get annotation data
13146 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13147 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13148 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13149 $popt['da'] = $fontstyle;
13150 // build appearance stream
13151 $popt['ap'] = array();
13152 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13153 $tmpid = $this->startTemplate($w, $h, false);
13154 $bw = (2 / $this->k); // border width
13155 $border = array(
13156 'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13157 'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
13158 'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13159 'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
13160 $this->SetFillColor(204);
13161 $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
13162 $this->endTemplate();
13163 --$this->n;
13164 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13165 unset($this->xobjects[$tmpid]);
13166 $popt['ap']['n'] .= 'Q EMC';
13167 // set additional default options
13168 if (!isset($popt['mk'])) {
13169 $popt['mk'] = array();
13170 }
13171 $ann_obj_id = ($this->n + 1);
13172 if (!empty($action) AND !is_array($action)) {
13173 $ann_obj_id = ($this->n + 2);
13174 }
13175 $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
13176 $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
13177 $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
13178 // merge options
13179 $opt = array_merge($popt, $opt);
13180 // set remaining annotation data
13181 $opt['Subtype'] = 'Widget';
13182 $opt['ft'] = 'Btn';
13183 $opt['t'] = $caption;
13184 $opt['v'] = $name;
13185 if (!empty($action)) {
13186 if (is_array($action)) {
13187 // form action options as on section 12.7.5 of PDF32000_2008.
13188 $opt['aa'] = '/D <<';
13189 $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
13190 foreach ($action AS $key => $val) {
13191 if (($key == 'S') AND in_array($val, $bmode)) {
13192 $opt['aa'] .= ' /S /'.$val;
13193 } elseif (($key == 'F') AND (!empty($val))) {
13194 $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
13195 } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
13196 $opt['aa'] .= ' /Fields [';
13197 foreach ($val AS $field) {
13198 $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
13199 }
13200 $opt['aa'] .= ']';
13201 } elseif (($key == 'Flags')) {
13202 $ff = 0;
13203 if (is_array($val)) {
13204 foreach ($val AS $flag) {
13205 switch ($flag) {
13206 case 'Include/Exclude': {
13207 $ff += 1 << 0;
13208 break;
13209 }
13210 case 'IncludeNoValueFields': {
13211 $ff += 1 << 1;
13212 break;
13213 }
13214 case 'ExportFormat': {
13215 $ff += 1 << 2;
13216 break;
13217 }
13218 case 'GetMethod': {
13219 $ff += 1 << 3;
13220 break;
13221 }
13222 case 'SubmitCoordinates': {
13223 $ff += 1 << 4;
13224 break;
13225 }
13226 case 'XFDF': {
13227 $ff += 1 << 5;
13228 break;
13229 }
13230 case 'IncludeAppendSaves': {
13231 $ff += 1 << 6;
13232 break;
13233 }
13234 case 'IncludeAnnotations': {
13235 $ff += 1 << 7;
13236 break;
13237 }
13238 case 'SubmitPDF': {
13239 $ff += 1 << 8;
13240 break;
13241 }
13242 case 'CanonicalFormat': {
13243 $ff += 1 << 9;
13244 break;
13245 }
13246 case 'ExclNonUserAnnots': {
13247 $ff += 1 << 10;
13248 break;
13249 }
13250 case 'ExclFKey': {
13251 $ff += 1 << 11;
13252 break;
13253 }
13254 case 'EmbedForm': {
13255 $ff += 1 << 13;
13256 break;
13257 }
13258 }
13259 }
13260 } else {
13261 $ff = intval($val);
13262 }
13263 $opt['aa'] .= ' /Flags '.$ff;
13264 }
13265 }
13266 $opt['aa'] .= ' >>';
13267 } else {
13268 // Javascript action or raw action command
13269 $js_obj_id = $this->addJavascriptObject($action);
13270 $opt['aa'] = '/D '.$js_obj_id.' 0 R';
13271 }
13272 }
13273 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13274 if ($this->rtl) {
13275 $this->x -= $w;
13276 } else {
13277 $this->x += $w;
13278 }
13279 }
13280
13281 // --- END FORMS FIELDS ------------------------------------------------
13282
13290 protected function _putsignature() {
13291 if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
13292 return;
13293 }
13294 $sigobjid = ($this->sig_obj_id + 1);
13295 $out = $this->_getobj($sigobjid)."\n";
13296 $out .= '<< /Type /Sig';
13297 $out .= ' /Filter /Adobe.PPKLite';
13298 $out .= ' /SubFilter /adbe.pkcs7.detached';
13299 $out .= ' '.TCPDF_STATIC::$byterange_string;
13300 $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>';
13301 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
13302 $out .= ' /Reference ['; // array of signature reference dictionaries
13303 $out .= ' << /Type /SigRef';
13304 if ($this->signature_data['cert_type'] > 0) {
13305 $out .= ' /TransformMethod /DocMDP';
13306 $out .= ' /TransformParams <<';
13307 $out .= ' /Type /TransformParams';
13308 $out .= ' /P '.$this->signature_data['cert_type'];
13309 $out .= ' /V /1.2';
13310 } else {
13311 $out .= ' /TransformMethod /UR3';
13312 $out .= ' /TransformParams <<';
13313 $out .= ' /Type /TransformParams';
13314 $out .= ' /V /2.2';
13315 if (!TCPDF_STATIC::empty_string($this->ur['document'])) {
13316 $out .= ' /Document['.$this->ur['document'].']';
13317 }
13318 if (!TCPDF_STATIC::empty_string($this->ur['form'])) {
13319 $out .= ' /Form['.$this->ur['form'].']';
13320 }
13321 if (!TCPDF_STATIC::empty_string($this->ur['signature'])) {
13322 $out .= ' /Signature['.$this->ur['signature'].']';
13323 }
13324 if (!TCPDF_STATIC::empty_string($this->ur['annots'])) {
13325 $out .= ' /Annots['.$this->ur['annots'].']';
13326 }
13327 if (!TCPDF_STATIC::empty_string($this->ur['ef'])) {
13328 $out .= ' /EF['.$this->ur['ef'].']';
13329 }
13330 if (!TCPDF_STATIC::empty_string($this->ur['formex'])) {
13331 $out .= ' /FormEX['.$this->ur['formex'].']';
13332 }
13333 }
13334 $out .= ' >>'; // close TransformParams
13335 // optional digest data (values must be calculated and replaced later)
13336 //$out .= ' /Data ********** 0 R';
13337 //$out .= ' /DigestMethod/MD5';
13338 //$out .= ' /DigestLocation[********** 34]';
13339 //$out .= ' /DigestValue<********************************>';
13340 $out .= ' >>';
13341 $out .= ' ]'; // end of reference
13342 }
13343 if (isset($this->signature_data['info']['Name']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Name'])) {
13344 $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid);
13345 }
13346 if (isset($this->signature_data['info']['Location']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Location'])) {
13347 $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid);
13348 }
13349 if (isset($this->signature_data['info']['Reason']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Reason'])) {
13350 $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid);
13351 }
13352 if (isset($this->signature_data['info']['ContactInfo']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['ContactInfo'])) {
13353 $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid);
13354 }
13355 $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp);
13356 $out .= ' >>';
13357 $out .= "\n".'endobj';
13358 $this->_out($out);
13359 }
13360
13378 public function setUserRights(
13379 $enable=true,
13380 $document='/FullSave',
13381 $annots='/Create/Delete/Modify/Copy/Import/Export',
13382 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
13383 $signature='/Modify',
13384 $ef='/Create/Delete/Modify/Import',
13385 $formex='') {
13386 $this->ur['enabled'] = $enable;
13387 $this->ur['document'] = $document;
13388 $this->ur['annots'] = $annots;
13389 $this->ur['form'] = $form;
13390 $this->ur['signature'] = $signature;
13391 $this->ur['ef'] = $ef;
13392 $this->ur['formex'] = $formex;
13393 if (!$this->sign) {
13394 $this->setSignature('', '', '', '', 0, array());
13395 }
13396 }
13397
13415 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='') {
13416 // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13417 // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13418 // to convert pfx certificate to pem: openssl
13419 // OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
13420 $this->sign = true;
13421 ++$this->n;
13422 $this->sig_obj_id = $this->n; // signature widget
13423 ++$this->n; // signature object ($this->sig_obj_id + 1)
13424 $this->signature_data = array();
13425 if (strlen($signing_cert) == 0) {
13426 $this->Error('Please provide a certificate file and password!');
13427 }
13428 if (strlen($private_key) == 0) {
13429 $private_key = $signing_cert;
13430 }
13431 $this->signature_data['signcert'] = $signing_cert;
13432 $this->signature_data['privkey'] = $private_key;
13433 $this->signature_data['password'] = $private_key_password;
13434 $this->signature_data['extracerts'] = $extracerts;
13435 $this->signature_data['cert_type'] = $cert_type;
13436 $this->signature_data['info'] = $info;
13437 $this->signature_data['approval'] = $approval;
13438 }
13439
13452 public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13453 $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13454 }
13455
13468 public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13469 ++$this->n;
13470 $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13471 }
13472
13486 protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13487 $sigapp = array();
13488 if (($page < 1) OR ($page > $this->numpages)) {
13489 $sigapp['page'] = $this->page;
13490 } else {
13491 $sigapp['page'] = intval($page);
13492 }
13493 if (empty($name)) {
13494 $sigapp['name'] = 'Signature';
13495 } else {
13496 $sigapp['name'] = $name;
13497 }
13498 $a = $x * $this->k;
13499 $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k);
13500 $c = $w * $this->k;
13501 $d = $h * $this->k;
13502 $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d));
13503 return $sigapp;
13504 }
13505
13518 public function setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='') {
13519 $this->tsa_data = array();
13520 if (!function_exists('curl_init')) {
13521 $this->Error('Please enable cURL PHP extension!');
13522 }
13523 if (strlen($tsa_host) == 0) {
13524 $this->Error('Please specify the host of Time Stamping Authority (TSA)!');
13525 }
13526 $this->tsa_data['tsa_host'] = $tsa_host;
13527 if (is_file($tsa_username)) {
13528 $this->tsa_data['tsa_auth'] = $tsa_username;
13529 } else {
13530 $this->tsa_data['tsa_username'] = $tsa_username;
13531 }
13532 $this->tsa_data['tsa_password'] = $tsa_password;
13533 $this->tsa_data['tsa_cert'] = $tsa_cert;
13534 $this->tsa_timestamp = true;
13535 }
13536
13546 protected function applyTSA($signature) {
13547 if (!$this->tsa_timestamp) {
13548 return $signature;
13549 }
13550 //@TODO: implement this feature
13551 return $signature;
13552 }
13553
13561 public function startPageGroup($page='') {
13562 if (empty($page)) {
13563 $page = $this->page + 1;
13564 }
13565 $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1;
13566 }
13567
13574 public function setStartingPageNumber($num=1) {
13575 $this->starting_page_number = max(0, intval($num));
13576 }
13577
13585 public function getAliasRightShift() {
13586 // calculate aproximatively the ratio between widths of aliases and replacements.
13587 $ref = '{'.TCPDF_STATIC::$alias_right_shift.'}{'.TCPDF_STATIC::$alias_tot_pages.'}{'.TCPDF_STATIC::$alias_num_page.'}';
13588 $rep = str_repeat(' ', $this->GetNumChars($ref));
13589 $wrep = $this->GetStringWidth($rep);
13590 if ($wrep > 0) {
13591 $wdiff = max(1, ($this->GetStringWidth($ref) / $wrep));
13592 } else {
13593 $wdiff = 1;
13594 }
13595 $sdiff = sprintf('%F', $wdiff);
13596 $alias = TCPDF_STATIC::$alias_right_shift.$sdiff.'}';
13597 if ($this->isUnicodeFont()) {
13598 $alias = '{'.$alias;
13599 }
13600 return $alias;
13601 }
13602
13611 public function getAliasNbPages() {
13612 if ($this->isUnicodeFont()) {
13613 return '{'.TCPDF_STATIC::$alias_tot_pages.'}';
13614 }
13616 }
13617
13626 public function getAliasNumPage() {
13627 if ($this->isUnicodeFont()) {
13628 return '{'.TCPDF_STATIC::$alias_num_page.'}';
13629 }
13631 }
13632
13641 public function getPageGroupAlias() {
13642 if ($this->isUnicodeFont()) {
13643 return '{'.TCPDF_STATIC::$alias_group_tot_pages.'}';
13644 }
13646 }
13647
13656 public function getPageNumGroupAlias() {
13657 if ($this->isUnicodeFont()) {
13658 return '{'.TCPDF_STATIC::$alias_group_num_page.'}';
13659 }
13661 }
13662
13669 public function getGroupPageNo() {
13670 return $this->pagegroups[$this->currpagegroup];
13671 }
13672
13679 public function getGroupPageNoFormatted() {
13681 }
13682
13689 public function PageNoFormatted() {
13690 return TCPDF_STATIC::formatPageNumber($this->PageNo());
13691 }
13692
13698 protected function _putocg() {
13699 if (empty($this->pdflayers)) {
13700 return;
13701 }
13702 foreach ($this->pdflayers as $key => $layer) {
13703 $this->pdflayers[$key]['objid'] = $this->_newobj();
13704 $out = '<< /Type /OCG';
13705 $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']);
13706 $out .= ' /Usage <<';
13707 if (isset($layer['print']) AND ($layer['print'] !== NULL)) {
13708 $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>';
13709 }
13710 $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>';
13711 $out .= ' >> >>';
13712 $out .= "\n".'endobj';
13713 $this->_out($out);
13714 }
13715 }
13716
13726 public function startLayer($name='', $print=true, $view=true, $lock=true) {
13727 if ($this->state != 2) {
13728 return;
13729 }
13730 $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1));
13731 if (empty($name)) {
13732 $name = $layer;
13733 } else {
13734 $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
13735 }
13736 $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view, 'lock' => $lock);
13737 $this->openMarkedContent = true;
13738 $this->_out('/OC /'.$layer.' BDC');
13739 }
13740
13746 public function endLayer() {
13747 if ($this->state != 2) {
13748 return;
13749 }
13750 if ($this->openMarkedContent) {
13751 // close existing open marked-content layer
13752 $this->_out('EMC');
13753 $this->openMarkedContent = false;
13754 }
13755 }
13756
13765 public function setVisibility($v) {
13766 if ($this->state != 2) {
13767 return;
13768 }
13769 $this->endLayer();
13770 switch($v) {
13771 case 'print': {
13772 $this->startLayer('Print', true, false);
13773 break;
13774 }
13775 case 'view':
13776 case 'screen': {
13777 $this->startLayer('View', false, true);
13778 break;
13779 }
13780 case 'all': {
13781 $this->_out('');
13782 break;
13783 }
13784 default: {
13785 $this->Error('Incorrect visibility: '.$v);
13786 break;
13787 }
13788 }
13789 }
13790
13798 protected function addExtGState($parms) {
13799 if ($this->pdfa_mode) {
13800 // transparencies are not allowed in PDF/A mode
13801 return;
13802 }
13803 // check if this ExtGState already exist
13804 foreach ($this->extgstates as $i => $ext) {
13805 if ($ext['parms'] == $parms) {
13806 if ($this->inxobj) {
13807 // we are inside an XObject template
13808 $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext;
13809 }
13810 // return reference to existing ExtGState
13811 return $i;
13812 }
13813 }
13814 $n = (count($this->extgstates) + 1);
13815 $this->extgstates[$n] = array('parms' => $parms);
13816 if ($this->inxobj) {
13817 // we are inside an XObject template
13818 $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n];
13819 }
13820 return $n;
13821 }
13822
13829 protected function setExtGState($gs) {
13830 if ($this->pdfa_mode OR ($this->state != 2)) {
13831 // transparency is not allowed in PDF/A mode
13832 return;
13833 }
13834 $this->_out(sprintf('/GS%d gs', $gs));
13835 }
13836
13842 protected function _putextgstates() {
13843 foreach ($this->extgstates as $i => $ext) {
13844 $this->extgstates[$i]['n'] = $this->_newobj();
13845 $out = '<< /Type /ExtGState';
13846 foreach ($ext['parms'] as $k => $v) {
13847 if (is_float($v)) {
13848 $v = sprintf('%F', $v);
13849 } elseif ($v === true) {
13850 $v = 'true';
13851 } elseif ($v === false) {
13852 $v = 'false';
13853 }
13854 $out .= ' /'.$k.' '.$v;
13855 }
13856 $out .= ' >>';
13857 $out .= "\n".'endobj';
13858 $this->_out($out);
13859 }
13860 }
13861
13871 public function setOverprint($stroking=true, $nonstroking='', $mode=0) {
13872 if ($this->state != 2) {
13873 return;
13874 }
13875 $stroking = $stroking ? true : false;
13876 if (TCPDF_STATIC::empty_string($nonstroking)) {
13877 // default value if not set
13878 $nonstroking = $stroking;
13879 } else {
13880 $nonstroking = $nonstroking ? true : false;
13881 }
13882 if (($mode != 0) AND ($mode != 1)) {
13883 $mode = 0;
13884 }
13885 $this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode);
13886 $gs = $this->addExtGState($this->overprint);
13887 $this->setExtGState($gs);
13888 }
13889
13897 public function getOverprint() {
13898 return $this->overprint;
13899 }
13900
13910 public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) {
13911 if ($this->pdfa_mode) {
13912 // transparency is not allowed in PDF/A mode
13913 return;
13914 }
13915 $stroking = floatval($stroking);
13916 if (TCPDF_STATIC::empty_string($nonstroking)) {
13917 // default value if not set
13918 $nonstroking = $stroking;
13919 } else {
13920 $nonstroking = floatval($nonstroking);
13921 }
13922 if ($bm[0] == '/') {
13923 // remove trailing slash
13924 $bm = substr($bm, 1);
13925 }
13926 if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
13927 $bm = 'Normal';
13928 }
13929 $ais = $ais ? true : false;
13930 $this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais);
13931 $gs = $this->addExtGState($this->alpha);
13932 $this->setExtGState($gs);
13933 }
13934
13942 public function getAlpha() {
13943 return $this->alpha;
13944 }
13945
13952 public function setJPEGQuality($quality) {
13953 if (($quality < 1) OR ($quality > 100)) {
13954 $quality = 75;
13955 }
13956 $this->jpeg_quality = intval($quality);
13957 }
13958
13965 public function setDefaultTableColumns($cols=4) {
13966 $this->default_table_columns = intval($cols);
13967 }
13968
13975 public function setCellHeightRatio($h) {
13976 $this->cell_height_ratio = $h;
13977 }
13978
13984 public function getCellHeightRatio() {
13986 }
13987
13994 public function setPDFVersion($version='1.7') {
13995 if ($this->pdfa_mode) {
13996 // PDF/A mode
13997 $this->PDFVersion = '1.4';
13998 } else {
13999 $this->PDFVersion = $version;
14000 }
14001 }
14002
14013 $this->viewer_preferences = $preferences;
14014 }
14015
14029 public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
14030 if (strpos($colors, 'ALLSPOT') !== false) {
14031 // expand spot colors
14032 $spot_colors = '';
14033 foreach ($this->spot_colors as $spot_color_name => $v) {
14034 $spot_colors .= ','.$spot_color_name;
14035 }
14036 if (!empty($spot_colors)) {
14037 $spot_colors = substr($spot_colors, 1);
14038 $colors = str_replace('ALLSPOT', $spot_colors, $colors);
14039 } else {
14040 $colors = str_replace('ALLSPOT', 'NONE', $colors);
14041 }
14042 }
14043 $bars = explode(',', $colors);
14044 $numbars = count($bars); // number of bars to print
14045 if ($numbars <= 0) {
14046 return;
14047 }
14048 // set bar measures
14049 if ($vertical) {
14050 $coords = array(0, 0, 0, 1);
14051 $wb = $w / $numbars; // bar width
14052 $hb = $h; // bar height
14053 $xd = $wb; // delta x
14054 $yd = 0; // delta y
14055 } else {
14056 $coords = array(1, 0, 0, 0);
14057 $wb = $w; // bar width
14058 $hb = $h / $numbars; // bar height
14059 $xd = 0; // delta x
14060 $yd = $hb; // delta y
14061 }
14062 $xb = $x;
14063 $yb = $y;
14064 foreach ($bars as $col) {
14065 switch ($col) {
14066 // set transition colors
14067 case 'A': { // BLACK (GRAYSCALE)
14068 $col_a = array(255);
14069 $col_b = array(0);
14070 break;
14071 }
14072 case 'W': { // WHITE (GRAYSCALE)
14073 $col_a = array(0);
14074 $col_b = array(255);
14075 break;
14076 }
14077 case 'R': { // RED (RGB)
14078 $col_a = array(255,255,255);
14079 $col_b = array(255,0,0);
14080 break;
14081 }
14082 case 'G': { // GREEN (RGB)
14083 $col_a = array(255,255,255);
14084 $col_b = array(0,255,0);
14085 break;
14086 }
14087 case 'B': { // BLUE (RGB)
14088 $col_a = array(255,255,255);
14089 $col_b = array(0,0,255);
14090 break;
14091 }
14092 case 'C': { // CYAN (CMYK)
14093 $col_a = array(0,0,0,0);
14094 $col_b = array(100,0,0,0);
14095 break;
14096 }
14097 case 'M': { // MAGENTA (CMYK)
14098 $col_a = array(0,0,0,0);
14099 $col_b = array(0,100,0,0);
14100 break;
14101 }
14102 case 'Y': { // YELLOW (CMYK)
14103 $col_a = array(0,0,0,0);
14104 $col_b = array(0,0,100,0);
14105 break;
14106 }
14107 case 'K': { // KEY - BLACK (CMYK)
14108 $col_a = array(0,0,0,0);
14109 $col_b = array(0,0,0,100);
14110 break;
14111 }
14112 case 'RGB': { // BLACK REGISTRATION (RGB)
14113 $col_a = array(255,255,255);
14114 $col_b = array(0,0,0);
14115 break;
14116 }
14117 case 'CMYK': { // BLACK REGISTRATION (CMYK)
14118 $col_a = array(0,0,0,0);
14119 $col_b = array(100,100,100,100);
14120 break;
14121 }
14122 case 'ALL': { // SPOT COLOR REGISTRATION
14123 $col_a = array(0,0,0,0,'None');
14124 $col_b = array(100,100,100,100,'All');
14125 break;
14126 }
14127 case 'NONE': { // SKIP THIS COLOR
14128 $col_a = array(0,0,0,0,'None');
14129 $col_b = array(0,0,0,0,'None');
14130 break;
14131 }
14132 default: { // SPECIFIC SPOT COLOR NAME
14133 $col_a = array(0,0,0,0,'None');
14134 $col_b = TCPDF_COLORS::getSpotColor($col, $this->spot_colors);
14135 if ($col_b === false) {
14136 // in case of error defaults to the registration color
14137 $col_b = array(100,100,100,100,'All');
14138 }
14139 break;
14140 }
14141 }
14142 if ($col != 'NONE') {
14143 if ($transition) {
14144 // color gradient
14145 $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
14146 } else {
14147 $this->SetFillColorArray($col_b);
14148 // colored rectangle
14149 $this->Rect($xb, $yb, $wb, $hb, 'F', array());
14150 }
14151 $xb += $xd;
14152 $yb += $yd;
14153 }
14154 }
14155 }
14156
14169 public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(100,100,100,100,'All')) {
14170 $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
14171 $type = strtoupper($type);
14172 $type = preg_replace('/[^A-Z\-\,]*/', '', $type);
14173 // split type in single components
14174 $type = str_replace('-', ',', $type);
14175 $type = str_replace('TL', 'T,L', $type);
14176 $type = str_replace('TR', 'T,R', $type);
14177 $type = str_replace('BL', 'F,L', $type);
14178 $type = str_replace('BR', 'F,R', $type);
14179 $type = str_replace('A', 'T,L', $type);
14180 $type = str_replace('B', 'T,R', $type);
14181 $type = str_replace('T,RO', 'BO', $type);
14182 $type = str_replace('C', 'F,L', $type);
14183 $type = str_replace('D', 'F,R', $type);
14184 $crops = explode(',', strtoupper($type));
14185 // remove duplicates
14186 $crops = array_unique($crops);
14187 $dw = ($w / 4); // horizontal space to leave before the intersection point
14188 $dh = ($h / 4); // vertical space to leave before the intersection point
14189 foreach ($crops as $crop) {
14190 switch ($crop) {
14191 case 'T':
14192 case 'TOP': {
14193 $x1 = $x;
14194 $y1 = ($y - $h);
14195 $x2 = $x;
14196 $y2 = ($y - $dh);
14197 break;
14198 }
14199 case 'F':
14200 case 'BOTTOM': {
14201 $x1 = $x;
14202 $y1 = ($y + $dh);
14203 $x2 = $x;
14204 $y2 = ($y + $h);
14205 break;
14206 }
14207 case 'L':
14208 case 'LEFT': {
14209 $x1 = ($x - $w);
14210 $y1 = $y;
14211 $x2 = ($x - $dw);
14212 $y2 = $y;
14213 break;
14214 }
14215 case 'R':
14216 case 'RIGHT': {
14217 $x1 = ($x + $dw);
14218 $y1 = $y;
14219 $x2 = ($x + $w);
14220 $y2 = $y;
14221 break;
14222 }
14223 }
14224 $this->Line($x1, $y1, $x2, $y2);
14225 }
14226 }
14227
14240 public function registrationMark($x, $y, $r, $double=false, $cola=array(100,100,100,100,'All'), $colb=array(0,0,0,0,'None')) {
14241 $line_style = array('width' => max((0.5 / $this->k),($r / 30)), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
14242 $this->SetFillColorArray($cola);
14243 $this->PieSector($x, $y, $r, 90, 180, 'F');
14244 $this->PieSector($x, $y, $r, 270, 360, 'F');
14245 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14246 if ($double) {
14247 $ri = $r * 0.5;
14248 $this->SetFillColorArray($colb);
14249 $this->PieSector($x, $y, $ri, 90, 180, 'F');
14250 $this->PieSector($x, $y, $ri, 270, 360, 'F');
14251 $this->SetFillColorArray($cola);
14252 $this->PieSector($x, $y, $ri, 0, 90, 'F');
14253 $this->PieSector($x, $y, $ri, 180, 270, 'F');
14254 $this->Circle($x, $y, $ri, 0, 360, 'C', $line_style, array(), 8);
14255 }
14256 }
14257
14267 public function registrationMarkCMYK($x, $y, $r) {
14268 // line width
14269 $lw = max((0.5 / $this->k),($r / 8));
14270 // internal radius
14271 $ri = ($r * 0.6);
14272 // external radius
14273 $re = ($r * 1.3);
14274 // Cyan
14275 $this->SetFillColorArray(array(100,0,0,0));
14276 $this->PieSector($x, $y, $ri, 270, 360, 'F');
14277 // Magenta
14278 $this->SetFillColorArray(array(0,100,0,0));
14279 $this->PieSector($x, $y, $ri, 0, 90, 'F');
14280 // Yellow
14281 $this->SetFillColorArray(array(0,0,100,0));
14282 $this->PieSector($x, $y, $ri, 90, 180, 'F');
14283 // Key - black
14284 $this->SetFillColorArray(array(0,0,0,100));
14285 $this->PieSector($x, $y, $ri, 180, 270, 'F');
14286 // registration color
14287 $line_style = array('width' => $lw, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(100,100,100,100,'All'));
14288 $this->SetFillColorArray(array(100,100,100,100,'All'));
14289 // external circle
14290 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14291 // cross lines
14292 $this->Line($x, ($y - $re), $x, ($y - $ri));
14293 $this->Line($x, ($y + $ri), $x, ($y + $re));
14294 $this->Line(($x - $re), $y, ($x - $ri), $y);
14295 $this->Line(($x + $ri), $y, ($x + $re), $y);
14296 }
14297
14311 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
14312 $this->Clip($x, $y, $w, $h);
14313 $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14314 }
14315
14329 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
14330 $this->Clip($x, $y, $w, $h);
14331 $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14332 }
14333
14352 public function CoonsPatchMesh($x, $y, $w, $h, $col1=array(), $col2=array(), $col3=array(), $col4=array(), $coords=array(0.00,0.0,0.33,0.00,0.67,0.00,1.00,0.00,1.00,0.33,1.00,0.67,1.00,1.00,0.67,1.00,0.33,1.00,0.00,1.00,0.00,0.67,0.00,0.33), $coords_min=0, $coords_max=1, $antialias=false) {
14353 if ($this->pdfa_mode OR ($this->state != 2)) {
14354 return;
14355 }
14356 $this->Clip($x, $y, $w, $h);
14357 $n = count($this->gradients) + 1;
14358 $this->gradients[$n] = array();
14359 $this->gradients[$n]['type'] = 6; //coons patch mesh
14360 $this->gradients[$n]['coords'] = array();
14361 $this->gradients[$n]['antialias'] = $antialias;
14362 $this->gradients[$n]['colors'] = array();
14363 $this->gradients[$n]['transparency'] = false;
14364 //check the coords array if it is the simple array or the multi patch array
14365 if (!isset($coords[0]['f'])) {
14366 //simple array -> convert to multi patch array
14367 if (!isset($col1[1])) {
14368 $col1[1] = $col1[2] = $col1[0];
14369 }
14370 if (!isset($col2[1])) {
14371 $col2[1] = $col2[2] = $col2[0];
14372 }
14373 if (!isset($col3[1])) {
14374 $col3[1] = $col3[2] = $col3[0];
14375 }
14376 if (!isset($col4[1])) {
14377 $col4[1] = $col4[2] = $col4[0];
14378 }
14379 $patch_array[0]['f'] = 0;
14380 $patch_array[0]['points'] = $coords;
14381 $patch_array[0]['colors'][0]['r'] = $col1[0];
14382 $patch_array[0]['colors'][0]['g'] = $col1[1];
14383 $patch_array[0]['colors'][0]['b'] = $col1[2];
14384 $patch_array[0]['colors'][1]['r'] = $col2[0];
14385 $patch_array[0]['colors'][1]['g'] = $col2[1];
14386 $patch_array[0]['colors'][1]['b'] = $col2[2];
14387 $patch_array[0]['colors'][2]['r'] = $col3[0];
14388 $patch_array[0]['colors'][2]['g'] = $col3[1];
14389 $patch_array[0]['colors'][2]['b'] = $col3[2];
14390 $patch_array[0]['colors'][3]['r'] = $col4[0];
14391 $patch_array[0]['colors'][3]['g'] = $col4[1];
14392 $patch_array[0]['colors'][3]['b'] = $col4[2];
14393 } else {
14394 //multi patch array
14396 }
14397 $bpcd = 65535; //16 bits per coordinate
14398 //build the data stream
14399 $this->gradients[$n]['stream'] = '';
14400 $count_patch = count($patch_array);
14401 for ($i=0; $i < $count_patch; ++$i) {
14402 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
14403 $count_points = count($patch_array[$i]['points']);
14404 for ($j=0; $j < $count_points; ++$j) {
14405 //each point as 16 bit
14406 $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
14407 if ($patch_array[$i]['points'][$j] < 0) {
14408 $patch_array[$i]['points'][$j] = 0;
14409 }
14410 if ($patch_array[$i]['points'][$j] > $bpcd) {
14411 $patch_array[$i]['points'][$j] = $bpcd;
14412 }
14413 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
14414 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
14415 }
14416 $count_cols = count($patch_array[$i]['colors']);
14417 for ($j=0; $j < $count_cols; ++$j) {
14418 //each color component as 8 bit
14419 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
14420 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
14421 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
14422 }
14423 }
14424 //paint the gradient
14425 $this->_out('/Sh'.$n.' sh');
14426 //restore previous Graphic State
14427 $this->_outRestoreGraphicsState();
14428 if ($this->inxobj) {
14429 // we are inside an XObject template
14430 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14431 }
14432 }
14433
14444 protected function Clip($x, $y, $w, $h) {
14445 if ($this->state != 2) {
14446 return;
14447 }
14448 if ($this->rtl) {
14449 $x = $this->w - $x - $w;
14450 }
14451 //save current Graphic State
14452 $s = 'q';
14453 //set clipping area
14454 $s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
14455 //set up transformation matrix for gradient
14456 $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
14457 $this->_out($s);
14458 }
14459
14471 public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
14472 if ($this->pdfa_mode OR ($this->state != 2)) {
14473 return;
14474 }
14475 $n = count($this->gradients) + 1;
14476 $this->gradients[$n] = array();
14477 $this->gradients[$n]['type'] = $type;
14478 $this->gradients[$n]['coords'] = $coords;
14479 $this->gradients[$n]['antialias'] = $antialias;
14480 $this->gradients[$n]['colors'] = array();
14481 $this->gradients[$n]['transparency'] = false;
14482 // color space
14483 $numcolspace = count($stops[0]['color']);
14484 $bcolor = array_values($background);
14485 switch($numcolspace) {
14486 case 5: // SPOT
14487 case 4: { // CMYK
14488 $this->gradients[$n]['colspace'] = 'DeviceCMYK';
14489 if (!empty($background)) {
14490 $this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
14491 }
14492 break;
14493 }
14494 case 3: { // RGB
14495 $this->gradients[$n]['colspace'] = 'DeviceRGB';
14496 if (!empty($background)) {
14497 $this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
14498 }
14499 break;
14500 }
14501 case 1: { // GRAY SCALE
14502 $this->gradients[$n]['colspace'] = 'DeviceGray';
14503 if (!empty($background)) {
14504 $this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255);
14505 }
14506 break;
14507 }
14508 }
14509 $num_stops = count($stops);
14510 $last_stop_id = $num_stops - 1;
14511 foreach ($stops as $key => $stop) {
14512 $this->gradients[$n]['colors'][$key] = array();
14513 // offset represents a location along the gradient vector
14514 if (isset($stop['offset'])) {
14515 $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
14516 } else {
14517 if ($key == 0) {
14518 $this->gradients[$n]['colors'][$key]['offset'] = 0;
14519 } elseif ($key == $last_stop_id) {
14520 $this->gradients[$n]['colors'][$key]['offset'] = 1;
14521 } else {
14522 $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
14523 $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
14524 }
14525 }
14526 if (isset($stop['opacity'])) {
14527 $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
14528 if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) {
14529 $this->gradients[$n]['transparency'] = true;
14530 }
14531 } else {
14532 $this->gradients[$n]['colors'][$key]['opacity'] = 1;
14533 }
14534 // exponent for the exponential interpolation function
14535 if (isset($stop['exponent'])) {
14536 $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
14537 } else {
14538 $this->gradients[$n]['colors'][$key]['exponent'] = 1;
14539 }
14540 // set colors
14541 $color = array_values($stop['color']);
14542 switch($numcolspace) {
14543 case 5: // SPOT
14544 case 4: { // CMYK
14545 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
14546 break;
14547 }
14548 case 3: { // RGB
14549 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255);
14550 break;
14551 }
14552 case 1: { // GRAY SCALE
14553 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255);
14554 break;
14555 }
14556 }
14557 }
14558 if ($this->gradients[$n]['transparency']) {
14559 // paint luminosity gradient
14560 $this->_out('/TGS'.$n.' gs');
14561 }
14562 //paint the gradient
14563 $this->_out('/Sh'.$n.' sh');
14564 //restore previous Graphic State
14565 $this->_outRestoreGraphicsState();
14566 if ($this->inxobj) {
14567 // we are inside an XObject template
14568 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14569 }
14570 }
14571
14578 function _putshaders() {
14579 if ($this->pdfa_mode) {
14580 return;
14581 }
14582 $idt = count($this->gradients); //index for transparency gradients
14583 foreach ($this->gradients as $id => $grad) {
14584 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
14585 $fc = $this->_newobj();
14586 $out = '<<';
14587 $out .= ' /FunctionType 3';
14588 $out .= ' /Domain [0 1]';
14589 $functions = '';
14590 $bounds = '';
14591 $encode = '';
14592 $i = 1;
14593 $num_cols = count($grad['colors']);
14594 $lastcols = $num_cols - 1;
14595 for ($i = 1; $i < $num_cols; ++$i) {
14596 $functions .= ($fc + $i).' 0 R ';
14597 if ($i < $lastcols) {
14598 $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']);
14599 }
14600 $encode .= '0 1 ';
14601 }
14602 $out .= ' /Functions ['.trim($functions).']';
14603 $out .= ' /Bounds ['.trim($bounds).']';
14604 $out .= ' /Encode ['.trim($encode).']';
14605 $out .= ' >>';
14606 $out .= "\n".'endobj';
14607 $this->_out($out);
14608 for ($i = 1; $i < $num_cols; ++$i) {
14609 $this->_newobj();
14610 $out = '<<';
14611 $out .= ' /FunctionType 2';
14612 $out .= ' /Domain [0 1]';
14613 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
14614 $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
14615 $out .= ' /N '.$grad['colors'][$i]['exponent'];
14616 $out .= ' >>';
14617 $out .= "\n".'endobj';
14618 $this->_out($out);
14619 }
14620 // set transparency functions
14621 if ($grad['transparency']) {
14622 $ft = $this->_newobj();
14623 $out = '<<';
14624 $out .= ' /FunctionType 3';
14625 $out .= ' /Domain [0 1]';
14626 $functions = '';
14627 $i = 1;
14628 $num_cols = count($grad['colors']);
14629 for ($i = 1; $i < $num_cols; ++$i) {
14630 $functions .= ($ft + $i).' 0 R ';
14631 }
14632 $out .= ' /Functions ['.trim($functions).']';
14633 $out .= ' /Bounds ['.trim($bounds).']';
14634 $out .= ' /Encode ['.trim($encode).']';
14635 $out .= ' >>';
14636 $out .= "\n".'endobj';
14637 $this->_out($out);
14638 for ($i = 1; $i < $num_cols; ++$i) {
14639 $this->_newobj();
14640 $out = '<<';
14641 $out .= ' /FunctionType 2';
14642 $out .= ' /Domain [0 1]';
14643 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
14644 $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
14645 $out .= ' /N '.$grad['colors'][$i]['exponent'];
14646 $out .= ' >>';
14647 $out .= "\n".'endobj';
14648 $this->_out($out);
14649 }
14650 }
14651 }
14652 // set shading object
14653 $this->_newobj();
14654 $out = '<< /ShadingType '.$grad['type'];
14655 if (isset($grad['colspace'])) {
14656 $out .= ' /ColorSpace /'.$grad['colspace'];
14657 } else {
14658 $out .= ' /ColorSpace /DeviceRGB';
14659 }
14660 if (isset($grad['background']) AND !empty($grad['background'])) {
14661 $out .= ' /Background ['.$grad['background'].']';
14662 }
14663 if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
14664 $out .= ' /AntiAlias true';
14665 }
14666 if ($grad['type'] == 2) {
14667 $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
14668 $out .= ' /Domain [0 1]';
14669 $out .= ' /Function '.$fc.' 0 R';
14670 $out .= ' /Extend [true true]';
14671 $out .= ' >>';
14672 } elseif ($grad['type'] == 3) {
14673 //x0, y0, r0, x1, y1, r1
14674 //at this this time radius of inner circle is 0
14675 $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
14676 $out .= ' /Domain [0 1]';
14677 $out .= ' /Function '.$fc.' 0 R';
14678 $out .= ' /Extend [true true]';
14679 $out .= ' >>';
14680 } elseif ($grad['type'] == 6) {
14681 $out .= ' /BitsPerCoordinate 16';
14682 $out .= ' /BitsPerComponent 8';
14683 $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
14684 $out .= ' /BitsPerFlag 8';
14685 $stream = $this->_getrawstream($grad['stream']);
14686 $out .= ' /Length '.strlen($stream);
14687 $out .= ' >>';
14688 $out .= ' stream'."\n".$stream."\n".'endstream';
14689 }
14690 $out .= "\n".'endobj';
14691 $this->_out($out);
14692 if ($grad['transparency']) {
14693 $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
14694 $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
14695 }
14696 $this->gradients[$id]['id'] = $this->n;
14697 // set pattern object
14698 $this->_newobj();
14699 $out = '<< /Type /Pattern /PatternType 2';
14700 $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
14701 $out .= ' >>';
14702 $out .= "\n".'endobj';
14703 $this->_out($out);
14704 $this->gradients[$id]['pattern'] = $this->n;
14705 // set shading and pattern for transparency mask
14706 if ($grad['transparency']) {
14707 // luminosity pattern
14708 $idgs = $id + $idt;
14709 $this->_newobj();
14710 $this->_out($shading_transparency);
14711 $this->gradients[$idgs]['id'] = $this->n;
14712 $this->_newobj();
14713 $out = '<< /Type /Pattern /PatternType 2';
14714 $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
14715 $out .= ' >>';
14716 $out .= "\n".'endobj';
14717 $this->_out($out);
14718 $this->gradients[$idgs]['pattern'] = $this->n;
14719 // luminosity XObject
14720 $oid = $this->_newobj();
14721 $this->xobjects['LX'.$oid] = array('n' => $oid);
14722 $filter = '';
14723 $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
14724 if ($this->compress) {
14725 $filter = ' /Filter /FlateDecode';
14726 $stream = gzcompress($stream);
14727 }
14728 $stream = $this->_getrawstream($stream);
14729 $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
14730 $out .= ' /Length '.strlen($stream);
14731 $rect = sprintf('%F %F', $this->wPt, $this->hPt);
14732 $out .= ' /BBox [0 0 '.$rect.']';
14733 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
14734 $out .= ' /Resources <<';
14735 $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
14736 $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
14737 $out .= ' >>';
14738 $out .= ' >> ';
14739 $out .= ' stream'."\n".$stream."\n".'endstream';
14740 $out .= "\n".'endobj';
14741 $this->_out($out);
14742 // SMask
14743 $this->_newobj();
14744 $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj';
14745 $this->_out($out);
14746 // ExtGState
14747 $this->_newobj();
14748 $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj';
14749 $this->_out($out);
14750 $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
14751 }
14752 }
14753 }
14754
14770 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
14771 $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
14772 }
14773
14791 public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
14792 if ($this->state != 2) {
14793 return;
14794 }
14795 if ($this->rtl) {
14796 $xc = ($this->w - $xc);
14797 }
14799 if ($op == 'f') {
14800 $line_style = array();
14801 }
14802 if ($cw) {
14803 $d = $b;
14804 $b = (360 - $a + $o);
14805 $a = (360 - $d + $o);
14806 } else {
14807 $b += $o;
14808 $a += $o;
14809 }
14810 $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
14811 $this->_out($op);
14812 }
14813
14835 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
14836 if ($this->state != 2) {
14837 return;
14838 }
14839 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
14840 // convert EPS to raster image using GD or ImageMagick libraries
14841 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14842 }
14843 if ($x === '') {
14844 $x = $this->x;
14845 }
14846 if ($y === '') {
14847 $y = $this->y;
14848 }
14849 // check page for no-write regions and adapt page margins if necessary
14850 list($x, $y) = $this->checkPageRegions($h, $x, $y);
14851 $k = $this->k;
14852 if ($file[0] === '@') { // image from string
14853 $data = substr($file, 1);
14854 } else { // EPS/AI file
14856 }
14857 if ($data === FALSE) {
14858 $this->Error('EPS file not found: '.$file);
14859 }
14860 $regs = array();
14861 // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
14862 preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
14863 if (count($regs) > 1) {
14864 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
14865 if (strpos($version_str, 'Adobe Illustrator') !== false) {
14866 $versexp = explode(' ', $version_str);
14867 $version = (float)array_pop($versexp);
14868 if ($version >= 9) {
14869 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
14870 }
14871 }
14872 }
14873 // strip binary bytes in front of PS-header
14874 $start = strpos($data, '%!PS-Adobe');
14875 if ($start > 0) {
14876 $data = substr($data, $start);
14877 }
14878 // find BoundingBox params
14879 preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
14880 if (count($regs) > 1) {
14881 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
14882 } else {
14883 $this->Error('No BoundingBox found in EPS/AI file: '.$file);
14884 }
14885 $start = strpos($data, '%%EndSetup');
14886 if ($start === false) {
14887 $start = strpos($data, '%%EndProlog');
14888 }
14889 if ($start === false) {
14890 $start = strpos($data, '%%BoundingBox');
14891 }
14892 $data = substr($data, $start);
14893 $end = strpos($data, '%%PageTrailer');
14894 if ($end===false) {
14895 $end = strpos($data, 'showpage');
14896 }
14897 if ($end) {
14898 $data = substr($data, 0, $end);
14899 }
14900 // calculate image width and height on document
14901 if (($w <= 0) AND ($h <= 0)) {
14902 $w = ($x2 - $x1) / $k;
14903 $h = ($y2 - $y1) / $k;
14904 } elseif ($w <= 0) {
14905 $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
14906 } elseif ($h <= 0) {
14907 $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
14908 }
14909 // fit the image on available space
14910 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
14911 if ($this->rasterize_vector_images) {
14912 // convert EPS to raster image using GD or ImageMagick libraries
14913 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14914 }
14915 // set scaling factors
14916 $scale_x = $w / (($x2 - $x1) / $k);
14917 $scale_y = $h / (($y2 - $y1) / $k);
14918 // set alignment
14919 $this->img_rb_y = $y + $h;
14920 // set alignment
14921 if ($this->rtl) {
14922 if ($palign == 'L') {
14923 $ximg = $this->lMargin;
14924 } elseif ($palign == 'C') {
14925 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
14926 } elseif ($palign == 'R') {
14927 $ximg = $this->w - $this->rMargin - $w;
14928 } else {
14929 $ximg = $x - $w;
14930 }
14931 $this->img_rb_x = $ximg;
14932 } else {
14933 if ($palign == 'L') {
14934 $ximg = $this->lMargin;
14935 } elseif ($palign == 'C') {
14936 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
14937 } elseif ($palign == 'R') {
14938 $ximg = $this->w - $this->rMargin - $w;
14939 } else {
14940 $ximg = $x;
14941 }
14942 $this->img_rb_x = $ximg + $w;
14943 }
14944 if ($useBoundingBox) {
14945 $dx = $ximg * $k - $x1;
14946 $dy = $y * $k - $y1;
14947 } else {
14948 $dx = $ximg * $k;
14949 $dy = $y * $k;
14950 }
14951 // save the current graphic state
14952 $this->_out('q'.$this->epsmarker);
14953 // translate
14954 $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
14955 // scale
14956 if (isset($scale_x)) {
14957 $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
14958 }
14959 // handle pc/unix/mac line endings
14960 $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY);
14961 $u=0;
14962 $cnt = count($lines);
14963 for ($i=0; $i < $cnt; ++$i) {
14964 $line = $lines[$i];
14965 if (($line == '') OR ($line[0] == '%')) {
14966 continue;
14967 }
14968 $len = strlen($line);
14969 // check for spot color names
14970 $color_name = '';
14971 if (strcasecmp('x', substr(trim($line), -1)) == 0) {
14972 if (preg_match('/\‍([^\‍)]*\‍)/', $line, $matches) > 0) {
14973 // extract spot color name
14974 $color_name = $matches[0];
14975 // remove color name from string
14976 $line = str_replace(' '.$color_name, '', $line);
14977 // remove pharentesis from color name
14978 $color_name = substr($color_name, 1, -1);
14979 }
14980 }
14981 $chunks = explode(' ', $line);
14982 $cmd = trim(array_pop($chunks));
14983 // RGB
14984 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
14985 $b = array_pop($chunks);
14986 $g = array_pop($chunks);
14987 $r = array_pop($chunks);
14988 $this->_out(''.$r.' '.$g.' '.$b.' '.($cmd=='Xa'?'rg':'RG')); //substr($line, 0, -2).'rg' -> in EPS (AI8): c m y k r g b rg!
14989 continue;
14990 }
14991 $skip = false;
14992 if ($fixoutvals) {
14993 // check for values outside the bounding box
14994 switch ($cmd) {
14995 case 'm':
14996 case 'l':
14997 case 'L': {
14998 // skip values outside bounding box
14999 foreach ($chunks as $key => $val) {
15000 if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) {
15001 $skip = true;
15002 } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) {
15003 $skip = true;
15004 }
15005 }
15006 }
15007 }
15008 }
15009 switch ($cmd) {
15010 case 'm':
15011 case 'l':
15012 case 'v':
15013 case 'y':
15014 case 'c':
15015 case 'k':
15016 case 'K':
15017 case 'g':
15018 case 'G':
15019 case 's':
15020 case 'S':
15021 case 'J':
15022 case 'j':
15023 case 'w':
15024 case 'M':
15025 case 'd':
15026 case 'n': {
15027 if ($skip) {
15028 break;
15029 }
15030 $this->_out($line);
15031 break;
15032 }
15033 case 'x': {// custom fill color
15034 if (empty($color_name)) {
15035 // CMYK color
15036 list($col_c, $col_m, $col_y, $col_k) = $chunks;
15037 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
15038 } else {
15039 // Spot Color (CMYK + tint)
15040 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15041 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15042 $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15043 $this->_out($color_cmd);
15044 }
15045 break;
15046 }
15047 case 'X': { // custom stroke color
15048 if (empty($color_name)) {
15049 // CMYK color
15050 list($col_c, $col_m, $col_y, $col_k) = $chunks;
15051 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
15052 } else {
15053 // Spot Color (CMYK + tint)
15054 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15055 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15056 $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15057 $this->_out($color_cmd);
15058 }
15059 break;
15060 }
15061 case 'Y':
15062 case 'N':
15063 case 'V':
15064 case 'L':
15065 case 'C': {
15066 if ($skip) {
15067 break;
15068 }
15069 $line[($len - 1)] = strtolower($cmd);
15070 $this->_out($line);
15071 break;
15072 }
15073 case 'b':
15074 case 'B': {
15075 $this->_out($cmd . '*');
15076 break;
15077 }
15078 case 'f':
15079 case 'F': {
15080 if ($u > 0) {
15081 $isU = false;
15082 $max = min(($i + 5), $cnt);
15083 for ($j = ($i + 1); $j < $max; ++$j) {
15084 $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
15085 }
15086 if ($isU) {
15087 $this->_out('f*');
15088 }
15089 } else {
15090 $this->_out('f*');
15091 }
15092 break;
15093 }
15094 case '*u': {
15095 ++$u;
15096 break;
15097 }
15098 case '*U': {
15099 --$u;
15100 break;
15101 }
15102 }
15103 }
15104 // restore previous graphic state
15105 $this->_out($this->epsmarker.'Q');
15106 if (!empty($border)) {
15107 $bx = $this->x;
15108 $by = $this->y;
15109 $this->x = $ximg;
15110 if ($this->rtl) {
15111 $this->x += $w;
15112 }
15113 $this->y = $y;
15114 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
15115 $this->x = $bx;
15116 $this->y = $by;
15117 }
15118 if ($link) {
15119 $this->Link($ximg, $y, $w, $h, $link, 0);
15120 }
15121 // set pointer to align the next text/objects
15122 switch($align) {
15123 case 'T':{
15124 $this->y = $y;
15125 $this->x = $this->img_rb_x;
15126 break;
15127 }
15128 case 'M':{
15129 $this->y = $y + round($h/2);
15130 $this->x = $this->img_rb_x;
15131 break;
15132 }
15133 case 'B':{
15134 $this->y = $this->img_rb_y;
15135 $this->x = $this->img_rb_x;
15136 break;
15137 }
15138 case 'N':{
15139 $this->SetY($this->img_rb_y);
15140 break;
15141 }
15142 default:{
15143 break;
15144 }
15145 }
15146 $this->endlinex = $this->img_rb_x;
15147 }
15148
15154 public function setBarcode($bc='') {
15155 $this->barcode = $bc;
15156 }
15157
15164 public function getBarcode() {
15165 return $this->barcode;
15166 }
15167
15198 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style='', $align='') {
15199 if (TCPDF_STATIC::empty_string(trim($code))) {
15200 return;
15201 }
15202 require_once(dirname(__FILE__).'/tcpdf_barcodes_1d.php');
15203 // save current graphic settings
15204 $gvars = $this->getGraphicVars();
15205 // create new barcode object
15206 $barcodeobj = new TCPDFBarcode($code, $type);
15207 $arrcode = $barcodeobj->getBarcodeArray();
15208 if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] <= 0)) {
15209 $this->Error('Error in 1D barcode string');
15210 }
15211 if ($arrcode['maxh'] <= 0) {
15212 $arrcode['maxh'] = 1;
15213 }
15214 // set default values
15215 if (!isset($style['position'])) {
15216 $style['position'] = '';
15217 } elseif ($style['position'] == 'S') {
15218 // keep this for backward compatibility
15219 $style['position'] = '';
15220 $style['stretch'] = true;
15221 }
15222 if (!isset($style['fitwidth'])) {
15223 if (!isset($style['stretch'])) {
15224 $style['fitwidth'] = true;
15225 } else {
15226 $style['fitwidth'] = false;
15227 }
15228 }
15229 if ($style['fitwidth']) {
15230 // disable stretch
15231 $style['stretch'] = false;
15232 }
15233 if (!isset($style['stretch'])) {
15234 if (($w === '') OR ($w <= 0)) {
15235 $style['stretch'] = false;
15236 } else {
15237 $style['stretch'] = true;
15238 }
15239 }
15240 if (!isset($style['fgcolor'])) {
15241 $style['fgcolor'] = array(0,0,0); // default black
15242 }
15243 if (!isset($style['bgcolor'])) {
15244 $style['bgcolor'] = false; // default transparent
15245 }
15246 if (!isset($style['border'])) {
15247 $style['border'] = false;
15248 }
15249 $fontsize = 0;
15250 if (!isset($style['text'])) {
15251 $style['text'] = false;
15252 }
15253 if ($style['text'] AND isset($style['font'])) {
15254 if (isset($style['fontsize'])) {
15255 $fontsize = $style['fontsize'];
15256 }
15257 $this->SetFont($style['font'], '', $fontsize);
15258 }
15259 if (!isset($style['stretchtext'])) {
15260 $style['stretchtext'] = 4;
15261 }
15262 if ($x === '') {
15263 $x = $this->x;
15264 }
15265 if ($y === '') {
15266 $y = $this->y;
15267 }
15268 // check page for no-write regions and adapt page margins if necessary
15269 list($x, $y) = $this->checkPageRegions($h, $x, $y);
15270 if (($w === '') OR ($w <= 0)) {
15271 if ($this->rtl) {
15272 $w = $x - $this->lMargin;
15273 } else {
15274 $w = $this->w - $this->rMargin - $x;
15275 }
15276 }
15277 // padding
15278 if (!isset($style['padding'])) {
15279 $padding = 0;
15280 } elseif ($style['padding'] === 'auto') {
15281 $padding = 10 * ($w / ($arrcode['maxw'] + 20));
15282 } else {
15283 $padding = floatval($style['padding']);
15284 }
15285 // horizontal padding
15286 if (!isset($style['hpadding'])) {
15287 $hpadding = $padding;
15288 } elseif ($style['hpadding'] === 'auto') {
15289 $hpadding = 10 * ($w / ($arrcode['maxw'] + 20));
15290 } else {
15291 $hpadding = floatval($style['hpadding']);
15292 }
15293 // vertical padding
15294 if (!isset($style['vpadding'])) {
15295 $vpadding = $padding;
15296 } elseif ($style['vpadding'] === 'auto') {
15297 $vpadding = ($hpadding / 2);
15298 } else {
15299 $vpadding = floatval($style['vpadding']);
15300 }
15301 // calculate xres (single bar width)
15302 $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
15303 if ($style['stretch']) {
15304 $xres = $max_xres;
15305 } else {
15306 if (TCPDF_STATIC::empty_string($xres)) {
15307 $xres = (0.141 * $this->k); // default bar width = 0.4 mm
15308 }
15309 if ($xres > $max_xres) {
15310 // correct xres to fit on $w
15311 $xres = $max_xres;
15312 }
15313 if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
15314 OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
15315 $hpadding = 10 * $xres;
15316 if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
15317 $vpadding = ($hpadding / 2);
15318 }
15319 }
15320 }
15321 if ($style['fitwidth']) {
15322 $wold = $w;
15323 $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding));
15324 if (isset($style['cellfitalign'])) {
15325 switch ($style['cellfitalign']) {
15326 case 'L': {
15327 if ($this->rtl) {
15328 $x -= ($wold - $w);
15329 }
15330 break;
15331 }
15332 case 'R': {
15333 if (!$this->rtl) {
15334 $x += ($wold - $w);
15335 }
15336 break;
15337 }
15338 case 'C': {
15339 if ($this->rtl) {
15340 $x -= (($wold - $w) / 2);
15341 } else {
15342 $x += (($wold - $w) / 2);
15343 }
15344 break;
15345 }
15346 default : {
15347 break;
15348 }
15349 }
15350 }
15351 }
15352 $text_height = $this->getCellHeight($fontsize / $this->k);
15353 // height
15354 if (($h === '') OR ($h <= 0)) {
15355 // set default height
15356 $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height;
15357 }
15358 $barh = $h - $text_height - (2 * $vpadding);
15359 if ($barh <=0) {
15360 // try to reduce font or padding to fit barcode on available height
15361 if ($text_height > $h) {
15362 $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio));
15363 $text_height = $this->getCellHeight($fontsize / $this->k);
15364 $this->SetFont($style['font'], '', $fontsize);
15365 }
15366 if ($vpadding > 0) {
15367 $vpadding = (($h - $text_height) / 4);
15368 }
15369 $barh = $h - $text_height - (2 * $vpadding);
15370 }
15371 // fit the barcode on available space
15372 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15373 // set alignment
15374 $this->img_rb_y = $y + $h;
15375 // set alignment
15376 if ($this->rtl) {
15377 if ($style['position'] == 'L') {
15378 $xpos = $this->lMargin;
15379 } elseif ($style['position'] == 'C') {
15380 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15381 } elseif ($style['position'] == 'R') {
15382 $xpos = $this->w - $this->rMargin - $w;
15383 } else {
15384 $xpos = $x - $w;
15385 }
15386 $this->img_rb_x = $xpos;
15387 } else {
15388 if ($style['position'] == 'L') {
15389 $xpos = $this->lMargin;
15390 } elseif ($style['position'] == 'C') {
15391 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15392 } elseif ($style['position'] == 'R') {
15393 $xpos = $this->w - $this->rMargin - $w;
15394 } else {
15395 $xpos = $x;
15396 }
15397 $this->img_rb_x = $xpos + $w;
15398 }
15399 $xpos_rect = $xpos;
15400 if (!isset($style['align'])) {
15401 $style['align'] = 'C';
15402 }
15403 switch ($style['align']) {
15404 case 'L': {
15405 $xpos = $xpos_rect + $hpadding;
15406 break;
15407 }
15408 case 'R': {
15409 $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding;
15410 break;
15411 }
15412 case 'C':
15413 default : {
15414 $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2);
15415 break;
15416 }
15417 }
15418 $xpos_text = $xpos;
15419 // barcode is always printed in LTR direction
15420 $tempRTL = $this->rtl;
15421 $this->rtl = false;
15422 // print background color
15423 if ($style['bgcolor']) {
15424 $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15425 } elseif ($style['border']) {
15426 $this->Rect($xpos_rect, $y, $w, $h, 'D');
15427 }
15428 // set foreground color
15429 $this->SetDrawColorArray($style['fgcolor']);
15430 $this->SetTextColorArray($style['fgcolor']);
15431 // print bars
15432 foreach ($arrcode['bcode'] as $k => $v) {
15433 $bw = ($v['w'] * $xres);
15434 if ($v['t']) {
15435 // draw a vertical bar
15436 $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']);
15437 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
15438 }
15439 $xpos += $bw;
15440 }
15441 // print text
15442 if ($style['text']) {
15443 if (isset($style['label']) AND !TCPDF_STATIC::empty_string($style['label'])) {
15444 $label = $style['label'];
15445 } else {
15446 $label = $code;
15447 }
15448 $txtwidth = ($arrcode['maxw'] * $xres);
15449 if ($this->GetStringWidth($label) > $txtwidth) {
15450 $style['stretchtext'] = 2;
15451 }
15452 // print text
15453 $this->x = $xpos_text;
15454 $this->y = $y + $vpadding + $barh;
15455 $cellpadding = $this->cell_padding;
15456 $this->SetCellPadding(0);
15457 $this->Cell($txtwidth, '', $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
15458 $this->cell_padding = $cellpadding;
15459 }
15460 // restore original direction
15461 $this->rtl = $tempRTL;
15462 // restore previous settings
15463 $this->setGraphicVars($gvars);
15464 // set pointer to align the next text/objects
15465 switch($align) {
15466 case 'T':{
15467 $this->y = $y;
15468 $this->x = $this->img_rb_x;
15469 break;
15470 }
15471 case 'M':{
15472 $this->y = $y + round($h / 2);
15473 $this->x = $this->img_rb_x;
15474 break;
15475 }
15476 case 'B':{
15477 $this->y = $this->img_rb_y;
15478 $this->x = $this->img_rb_x;
15479 break;
15480 }
15481 case 'N':{
15482 $this->SetY($this->img_rb_y);
15483 break;
15484 }
15485 default:{
15486 break;
15487 }
15488 }
15489 $this->endlinex = $this->img_rb_x;
15490 }
15491
15517 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='', $distort=false) {
15518 if (TCPDF_STATIC::empty_string(trim($code))) {
15519 return;
15520 }
15521 require_once(dirname(__FILE__).'/tcpdf_barcodes_2d.php');
15522 // save current graphic settings
15523 $gvars = $this->getGraphicVars();
15524 // create new barcode object
15525 $barcodeobj = new TCPDF2DBarcode($code, $type);
15526 $arrcode = $barcodeobj->getBarcodeArray();
15527 if (($arrcode === false) OR empty($arrcode) OR !isset($arrcode['num_rows']) OR ($arrcode['num_rows'] == 0) OR !isset($arrcode['num_cols']) OR ($arrcode['num_cols'] == 0)) {
15528 $this->Error('Error in 2D barcode string');
15529 }
15530 // set default values
15531 if (!isset($style['position'])) {
15532 $style['position'] = '';
15533 }
15534 if (!isset($style['fgcolor'])) {
15535 $style['fgcolor'] = array(0,0,0); // default black
15536 }
15537 if (!isset($style['bgcolor'])) {
15538 $style['bgcolor'] = false; // default transparent
15539 }
15540 if (!isset($style['border'])) {
15541 $style['border'] = false;
15542 }
15543 // padding
15544 if (!isset($style['padding'])) {
15545 $style['padding'] = 0;
15546 } elseif ($style['padding'] === 'auto') {
15547 $style['padding'] = 4;
15548 }
15549 if (!isset($style['hpadding'])) {
15550 $style['hpadding'] = $style['padding'];
15551 } elseif ($style['hpadding'] === 'auto') {
15552 $style['hpadding'] = 4;
15553 }
15554 if (!isset($style['vpadding'])) {
15555 $style['vpadding'] = $style['padding'];
15556 } elseif ($style['vpadding'] === 'auto') {
15557 $style['vpadding'] = 4;
15558 }
15559 $hpad = (2 * $style['hpadding']);
15560 $vpad = (2 * $style['vpadding']);
15561 // cell (module) dimension
15562 if (!isset($style['module_width'])) {
15563 $style['module_width'] = 1; // width of a single module in points
15564 }
15565 if (!isset($style['module_height'])) {
15566 $style['module_height'] = 1; // height of a single module in points
15567 }
15568 if ($x === '') {
15569 $x = $this->x;
15570 }
15571 if ($y === '') {
15572 $y = $this->y;
15573 }
15574 // check page for no-write regions and adapt page margins if necessary
15575 list($x, $y) = $this->checkPageRegions($h, $x, $y);
15576 // number of barcode columns and rows
15577 $rows = $arrcode['num_rows'];
15578 $cols = $arrcode['num_cols'];
15579 if (($rows <= 0) || ($cols <= 0)){
15580 $this->Error('Error in 2D barcode string');
15581 }
15582 // module width and height
15583 $mw = $style['module_width'];
15584 $mh = $style['module_height'];
15585 if (($mw <= 0) OR ($mh <= 0)) {
15586 $this->Error('Error in 2D barcode string');
15587 }
15588 // get max dimensions
15589 if ($this->rtl) {
15590 $maxw = $x - $this->lMargin;
15591 } else {
15592 $maxw = $this->w - $this->rMargin - $x;
15593 }
15594 $maxh = ($this->h - $this->tMargin - $this->bMargin);
15595 $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad));
15596 $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad));
15597 if (!$distort) {
15598 if (($maxw * $ratioHW) > $maxh) {
15599 $maxw = $maxh * $ratioWH;
15600 }
15601 if (($maxh * $ratioWH) > $maxw) {
15602 $maxh = $maxw * $ratioHW;
15603 }
15604 }
15605 // set maximum dimensions
15606 if ($w > $maxw) {
15607 $w = $maxw;
15608 }
15609 if ($h > $maxh) {
15610 $h = $maxh;
15611 }
15612 // set dimensions
15613 if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
15614 $w = ($cols + $hpad) * ($mw / $this->k);
15615 $h = ($rows + $vpad) * ($mh / $this->k);
15616 } elseif (($w === '') OR ($w <= 0)) {
15617 $w = $h * $ratioWH;
15618 } elseif (($h === '') OR ($h <= 0)) {
15619 $h = $w * $ratioHW;
15620 }
15621 // barcode size (excluding padding)
15622 $bw = ($w * $cols) / ($cols + $hpad);
15623 $bh = ($h * $rows) / ($rows + $vpad);
15624 // dimension of single barcode cell unit
15625 $cw = $bw / $cols;
15626 $ch = $bh / $rows;
15627 if (!$distort) {
15628 if (($cw / $ch) > ($mw / $mh)) {
15629 // correct horizontal distortion
15630 $cw = $ch * $mw / $mh;
15631 $bw = $cw * $cols;
15632 $style['hpadding'] = ($w - $bw) / (2 * $cw);
15633 } else {
15634 // correct vertical distortion
15635 $ch = $cw * $mh / $mw;
15636 $bh = $ch * $rows;
15637 $style['vpadding'] = ($h - $bh) / (2 * $ch);
15638 }
15639 }
15640 // fit the barcode on available space
15641 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15642 // set alignment
15643 $this->img_rb_y = $y + $h;
15644 // set alignment
15645 if ($this->rtl) {
15646 if ($style['position'] == 'L') {
15647 $xpos = $this->lMargin;
15648 } elseif ($style['position'] == 'C') {
15649 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15650 } elseif ($style['position'] == 'R') {
15651 $xpos = $this->w - $this->rMargin - $w;
15652 } else {
15653 $xpos = $x - $w;
15654 }
15655 $this->img_rb_x = $xpos;
15656 } else {
15657 if ($style['position'] == 'L') {
15658 $xpos = $this->lMargin;
15659 } elseif ($style['position'] == 'C') {
15660 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15661 } elseif ($style['position'] == 'R') {
15662 $xpos = $this->w - $this->rMargin - $w;
15663 } else {
15664 $xpos = $x;
15665 }
15666 $this->img_rb_x = $xpos + $w;
15667 }
15668 $xstart = $xpos + ($style['hpadding'] * $cw);
15669 $ystart = $y + ($style['vpadding'] * $ch);
15670 // barcode is always printed in LTR direction
15671 $tempRTL = $this->rtl;
15672 $this->rtl = false;
15673 // print background color
15674 if ($style['bgcolor']) {
15675 $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15676 } elseif ($style['border']) {
15677 $this->Rect($xpos, $y, $w, $h, 'D');
15678 }
15679 // set foreground color
15680 $this->SetDrawColorArray($style['fgcolor']);
15681 // print barcode cells
15682 // for each row
15683 for ($r = 0; $r < $rows; ++$r) {
15684 $xr = $xstart;
15685 // for each column
15686 for ($c = 0; $c < $cols; ++$c) {
15687 if ($arrcode['bcode'][$r][$c] == 1) {
15688 // draw a single barcode cell
15689 $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
15690 }
15691 $xr += $cw;
15692 }
15693 $ystart += $ch;
15694 }
15695 // restore original direction
15696 $this->rtl = $tempRTL;
15697 // restore previous settings
15698 $this->setGraphicVars($gvars);
15699 // set pointer to align the next text/objects
15700 switch($align) {
15701 case 'T':{
15702 $this->y = $y;
15703 $this->x = $this->img_rb_x;
15704 break;
15705 }
15706 case 'M':{
15707 $this->y = $y + round($h/2);
15708 $this->x = $this->img_rb_x;
15709 break;
15710 }
15711 case 'B':{
15712 $this->y = $this->img_rb_y;
15713 $this->x = $this->img_rb_x;
15714 break;
15715 }
15716 case 'N':{
15717 $this->SetY($this->img_rb_y);
15718 break;
15719 }
15720 default:{
15721 break;
15722 }
15723 }
15724 $this->endlinex = $this->img_rb_x;
15725 }
15726
15746 public function getMargins() {
15747 $ret = array(
15748 'left' => $this->lMargin,
15749 'right' => $this->rMargin,
15750 'top' => $this->tMargin,
15751 'bottom' => $this->bMargin,
15752 'header' => $this->header_margin,
15753 'footer' => $this->footer_margin,
15754 'cell' => $this->cell_padding,
15755 'padding_left' => $this->cell_padding['L'],
15756 'padding_top' => $this->cell_padding['T'],
15757 'padding_right' => $this->cell_padding['R'],
15758 'padding_bottom' => $this->cell_padding['B']
15759 );
15760 return $ret;
15761 }
15762
15773 public function getOriginalMargins() {
15774 $ret = array(
15775 'left' => $this->original_lMargin,
15776 'right' => $this->original_rMargin
15777 );
15778 return $ret;
15779 }
15780
15787 public function getFontSize() {
15788 return $this->FontSize;
15789 }
15790
15797 public function getFontSizePt() {
15798 return $this->FontSizePt;
15799 }
15800
15807 public function getFontFamily() {
15808 return $this->FontFamily;
15809 }
15810
15817 public function getFontStyle() {
15818 return $this->FontStyle;
15819 }
15820
15833 public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
15834 return TCPDF_STATIC::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces);
15835 }
15836
15844 protected function getCSSBorderWidth($width) {
15845 if ($width == 'thin') {
15846 $width = (2 / $this->k);
15847 } elseif ($width == 'medium') {
15848 $width = (4 / $this->k);
15849 } elseif ($width == 'thick') {
15850 $width = (6 / $this->k);
15851 } else {
15852 $width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
15853 }
15854 return $width;
15855 }
15856
15864 protected function getCSSBorderDashStyle($style) {
15865 switch (strtolower($style)) {
15866 case 'none':
15867 case 'hidden': {
15868 $dash = -1;
15869 break;
15870 }
15871 case 'dotted': {
15872 $dash = 1;
15873 break;
15874 }
15875 case 'dashed': {
15876 $dash = 3;
15877 break;
15878 }
15879 case 'double':
15880 case 'groove':
15881 case 'ridge':
15882 case 'inset':
15883 case 'outset':
15884 case 'solid':
15885 default: {
15886 $dash = 0;
15887 break;
15888 }
15889 }
15890 return $dash;
15891 }
15892
15900 protected function getCSSBorderStyle($cssborder) {
15901 $bprop = preg_split('/[\s]+/', trim($cssborder));
15902 $border = array(); // value to be returned
15903 switch (count($bprop)) {
15904 case 3: {
15905 $width = $bprop[0];
15906 $style = $bprop[1];
15907 $color = $bprop[2];
15908 break;
15909 }
15910 case 2: {
15911 $width = 'medium';
15912 $style = $bprop[0];
15913 $color = $bprop[1];
15914 break;
15915 }
15916 case 1: {
15917 $width = 'medium';
15918 $style = $bprop[0];
15919 $color = 'black';
15920 break;
15921 }
15922 default: {
15923 $width = 'medium';
15924 $style = 'solid';
15925 $color = 'black';
15926 break;
15927 }
15928 }
15929 if ($style == 'none') {
15930 return array();
15931 }
15932 $border['cap'] = 'square';
15933 $border['join'] = 'miter';
15934 $border['dash'] = $this->getCSSBorderDashStyle($style);
15935 if ($border['dash'] < 0) {
15936 return array();
15937 }
15938 $border['width'] = $this->getCSSBorderWidth($width);
15939 $border['color'] = TCPDF_COLORS::convertHTMLColorToDec($color, $this->spot_colors);
15940 return $border;
15941 }
15942
15951 public function getCSSPadding($csspadding, $width=0) {
15952 $padding = preg_split('/[\s]+/', trim($csspadding));
15953 $cell_padding = array(); // value to be returned
15954 switch (count($padding)) {
15955 case 4: {
15956 $cell_padding['T'] = $padding[0];
15957 $cell_padding['R'] = $padding[1];
15958 $cell_padding['B'] = $padding[2];
15959 $cell_padding['L'] = $padding[3];
15960 break;
15961 }
15962 case 3: {
15963 $cell_padding['T'] = $padding[0];
15964 $cell_padding['R'] = $padding[1];
15965 $cell_padding['B'] = $padding[2];
15966 $cell_padding['L'] = $padding[1];
15967 break;
15968 }
15969 case 2: {
15970 $cell_padding['T'] = $padding[0];
15971 $cell_padding['R'] = $padding[1];
15972 $cell_padding['B'] = $padding[0];
15973 $cell_padding['L'] = $padding[1];
15974 break;
15975 }
15976 case 1: {
15977 $cell_padding['T'] = $padding[0];
15978 $cell_padding['R'] = $padding[0];
15979 $cell_padding['B'] = $padding[0];
15980 $cell_padding['L'] = $padding[0];
15981 break;
15982 }
15983 default: {
15984 return $this->cell_padding;
15985 }
15986 }
15987 if ($width == 0) {
15988 $width = $this->w - $this->lMargin - $this->rMargin;
15989 }
15990 $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
15991 $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
15992 $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
15993 $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
15994 return $cell_padding;
15995 }
15996
16005 public function getCSSMargin($cssmargin, $width=0) {
16006 $margin = preg_split('/[\s]+/', trim($cssmargin));
16007 $cell_margin = array(); // value to be returned
16008 switch (count($margin)) {
16009 case 4: {
16010 $cell_margin['T'] = $margin[0];
16011 $cell_margin['R'] = $margin[1];
16012 $cell_margin['B'] = $margin[2];
16013 $cell_margin['L'] = $margin[3];
16014 break;
16015 }
16016 case 3: {
16017 $cell_margin['T'] = $margin[0];
16018 $cell_margin['R'] = $margin[1];
16019 $cell_margin['B'] = $margin[2];
16020 $cell_margin['L'] = $margin[1];
16021 break;
16022 }
16023 case 2: {
16024 $cell_margin['T'] = $margin[0];
16025 $cell_margin['R'] = $margin[1];
16026 $cell_margin['B'] = $margin[0];
16027 $cell_margin['L'] = $margin[1];
16028 break;
16029 }
16030 case 1: {
16031 $cell_margin['T'] = $margin[0];
16032 $cell_margin['R'] = $margin[0];
16033 $cell_margin['B'] = $margin[0];
16034 $cell_margin['L'] = $margin[0];
16035 break;
16036 }
16037 default: {
16038 return $this->cell_margin;
16039 }
16040 }
16041 if ($width == 0) {
16042 $width = $this->w - $this->lMargin - $this->rMargin;
16043 }
16044 $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
16045 $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
16046 $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
16047 $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
16048 return $cell_margin;
16049 }
16050
16059 public function getCSSBorderMargin($cssbspace, $width=0) {
16060 $space = preg_split('/[\s]+/', trim($cssbspace));
16061 $border_spacing = array(); // value to be returned
16062 switch (count($space)) {
16063 case 2: {
16064 $border_spacing['H'] = $space[0];
16065 $border_spacing['V'] = $space[1];
16066 break;
16067 }
16068 case 1: {
16069 $border_spacing['H'] = $space[0];
16070 $border_spacing['V'] = $space[0];
16071 break;
16072 }
16073 default: {
16074 return array('H' => 0, 'V' => 0);
16075 }
16076 }
16077 if ($width == 0) {
16078 $width = $this->w - $this->lMargin - $this->rMargin;
16079 }
16080 $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
16081 $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
16082 return $border_spacing;
16083 }
16084
16093 protected function getCSSFontSpacing($spacing, $parent=0) {
16094 $val = 0; // value to be returned
16095 $spacing = trim($spacing);
16096 switch ($spacing) {
16097 case 'normal': {
16098 $val = 0;
16099 break;
16100 }
16101 case 'inherit': {
16102 if ($parent == 'normal') {
16103 $val = 0;
16104 } else {
16105 $val = $parent;
16106 }
16107 break;
16108 }
16109 default: {
16110 $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
16111 }
16112 }
16113 return $val;
16114 }
16115
16124 protected function getCSSFontStretching($stretch, $parent=100) {
16125 $val = 100; // value to be returned
16126 $stretch = trim($stretch);
16127 switch ($stretch) {
16128 case 'ultra-condensed': {
16129 $val = 40;
16130 break;
16131 }
16132 case 'extra-condensed': {
16133 $val = 55;
16134 break;
16135 }
16136 case 'condensed': {
16137 $val = 70;
16138 break;
16139 }
16140 case 'semi-condensed': {
16141 $val = 85;
16142 break;
16143 }
16144 case 'normal': {
16145 $val = 100;
16146 break;
16147 }
16148 case 'semi-expanded': {
16149 $val = 115;
16150 break;
16151 }
16152 case 'expanded': {
16153 $val = 130;
16154 break;
16155 }
16156 case 'extra-expanded': {
16157 $val = 145;
16158 break;
16159 }
16160 case 'ultra-expanded': {
16161 $val = 160;
16162 break;
16163 }
16164 case 'wider': {
16165 $val = ($parent + 10);
16166 break;
16167 }
16168 case 'narrower': {
16169 $val = ($parent - 10);
16170 break;
16171 }
16172 case 'inherit': {
16173 if ($parent == 'normal') {
16174 $val = 100;
16175 } else {
16176 $val = $parent;
16177 }
16178 break;
16179 }
16180 default: {
16181 $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
16182 }
16183 }
16184 return $val;
16185 }
16186
16196 public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') {
16197 $refsize = TCPDF_FONTS::getFontRefSize($refsize);
16198 $parent_size = TCPDF_FONTS::getFontRefSize($parent_size, $refsize);
16199 switch ($val) {
16200 case 'xx-small': {
16201 $size = ($refsize - 4);
16202 break;
16203 }
16204 case 'x-small': {
16205 $size = ($refsize - 3);
16206 break;
16207 }
16208 case 'small': {
16209 $size = ($refsize - 2);
16210 break;
16211 }
16212 case 'medium': {
16213 $size = $refsize;
16214 break;
16215 }
16216 case 'large': {
16217 $size = ($refsize + 2);
16218 break;
16219 }
16220 case 'x-large': {
16221 $size = ($refsize + 4);
16222 break;
16223 }
16224 case 'xx-large': {
16225 $size = ($refsize + 6);
16226 break;
16227 }
16228 case 'smaller': {
16229 $size = ($parent_size - 3);
16230 break;
16231 }
16232 case 'larger': {
16233 $size = ($parent_size + 3);
16234 break;
16235 }
16236 default: {
16237 $size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true);
16238 }
16239 }
16240 return $size;
16241 }
16242
16250 protected function getHtmlDomArray($html) {
16251 // array of CSS styles ( selector => properties).
16252 $css = array();
16253 // get CSS array defined at previous call
16254 $matches = array();
16255 if (preg_match_all('/<cssarray>([^<]*)<\/cssarray>/isU', $html, $matches) > 0) {
16256 if (isset($matches[1][0])) {
16257 $css = array_merge($css, json_decode($this->unhtmlentities($matches[1][0]), true));
16258 }
16259 $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
16260 }
16261 // extract external CSS files
16262 $matches = array();
16263 if (preg_match_all('/<link([^>]*)>/isU', $html, $matches) > 0) {
16264 foreach ($matches[1] as $key => $link) {
16265 $type = array();
16266 if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
16267 $type = array();
16268 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
16269 // get 'all' and 'print' media, other media types are discarded
16270 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16271 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16272 $type = array();
16273 if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
16274 // read CSS data file
16275 $cssdata = TCPDF_STATIC::fileGetContents(trim($type[1]));
16276 if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) {
16277 $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16278 }
16279 }
16280 }
16281 }
16282 }
16283 }
16284 // extract style tags
16285 $matches = array();
16286 if (preg_match_all('/<style([^>]*)>([^<]*)<\/style>/isU', $html, $matches) > 0) {
16287 foreach ($matches[1] as $key => $media) {
16288 $type = array();
16289 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
16290 // get 'all' and 'print' media, other media types are discarded
16291 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16292 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16293 $cssdata = $matches[2][$key];
16294 $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16295 }
16296 }
16297 }
16298 // create a special tag to contain the CSS array (used for table content)
16299 $csstagarray = '<cssarray>'.htmlentities(json_encode($css)).'</cssarray>';
16300 // remove head and style blocks
16301 $html = preg_replace('/<head([^>]*)>(.*?)<\/head>/siU', '', $html);
16302 $html = preg_replace('/<style([^>]*)>([^<]*)<\/style>/isU', '', $html);
16303 // define block tags
16304 $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
16305 // define self-closing tags
16306 $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
16307 // remove all unsupported tags (the line below lists all supported tags)
16308 $html = strip_tags($html, '<marker/><a><b><blockquote><body><br><br/><dd><del><div><dl><dt><em><font><form><h1><h2><h3><h4><h5><h6><hr><hr/><i><img><input><label><li><ol><option><p><pre><s><select><small><span><strike><strong><sub><sup><table><tablehead><tcpdf><td><textarea><th><thead><tr><tt><u><ul>');
16309 //replace some blank characters
16310 $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
16311 $html = preg_replace('/<(table|tr|td|th|tcpdf|blockquote|dd|div|dl|dt|form|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^>]*)>[\n\r\t]+/', '<\\1\\2>', $html);
16312 $html = preg_replace('@(\r\n|\r)@', "\n", $html);
16313 $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
16314 $html = strtr($html, $repTable);
16315 $offset = 0;
16316 while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
16317 $html_a = substr($html, 0, $offset);
16318 $html_b = substr($html, $offset, ($pos - $offset + 6));
16319 while (preg_match("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
16320 // preserve newlines on <pre> tag
16321 $html_b = preg_replace("'<xre([^>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
16322 }
16323 while (preg_match("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) {
16324 // preserve spaces on <pre> tag
16325 $html_b = preg_replace("'<xre([^>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2&nbsp;\\3</pre>", $html_b);
16326 }
16327 $html = $html_a.$html_b.substr($html, $pos + 6);
16328 $offset = strlen($html_a.$html_b);
16329 }
16330 $offset = 0;
16331 while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
16332 $html_a = substr($html, 0, $offset);
16333 $html_b = substr($html, $offset, ($pos - $offset + 11));
16334 while (preg_match("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
16335 // preserve newlines on <textarea> tag
16336 $html_b = preg_replace("'<textarea([^>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
16337 $html_b = preg_replace("'<textarea([^>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
16338 }
16339 $html = $html_a.$html_b.substr($html, $pos + 11);
16340 $offset = strlen($html_a.$html_b);
16341 }
16342 $html = preg_replace('/([\s]*)<option/si', '<option', $html);
16343 $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
16344 $offset = 0;
16345 while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
16346 $html_a = substr($html, 0, $offset);
16347 $html_b = substr($html, $offset, ($pos - $offset + 9));
16348 while (preg_match("'<option([^>]*)>(.*?)</option>'si", $html_b)) {
16349 $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
16350 $html_b = preg_replace("'<option([^>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
16351 }
16352 $html = $html_a.$html_b.substr($html, $pos + 9);
16353 $offset = strlen($html_a.$html_b);
16354 }
16355 if (preg_match("'</select'si", $html)) {
16356 $html = preg_replace("'<select([^>]*)>'si", "<select\\1 opt=\"", $html);
16357 $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
16358 }
16359 $html = str_replace("\n", ' ', $html);
16360 // restore textarea newlines
16361 $html = str_replace('<TBR>', "\n", $html);
16362 // remove extra spaces from code
16363 $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
16364 $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html);
16365 $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
16366 $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html);
16367 $html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|dt|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html);
16368 $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
16369 $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
16370 $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html);
16371 $html = preg_replace('/<img([^>]*)>[\s]+([^<])/xi', '<img\\1>&nbsp;\\2', $html);
16372 $html = preg_replace('/<img([^>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
16373 $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
16374 $html = preg_replace('/<textarea([^>]*)>([^<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
16375 $html = preg_replace('/<li([^>]*)><\/li>/', '<li\\1>&nbsp;</li>', $html);
16376 $html = preg_replace('/<li([^>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1">&nbsp;</font><img', $html);
16377 $html = preg_replace('/<([^>\/]*)>[\s]/', '<\\1>&nbsp;', $html); // preserve some spaces
16378 $html = preg_replace('/[\s]<\/([^>]*)>/', '&nbsp;</\\1>', $html); // preserve some spaces
16379 $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment
16380 $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment
16381 $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space
16382 // trim string
16383 $html = $this->stringTrim($html);
16384 // fix br tag after li
16385 $html = preg_replace('/<li><br([^>]*)>/', '<li> <br\\1>', $html);
16386 // fix first image tag alignment
16387 $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
16388 // pattern for generic tag
16389 $tagpattern = '/(<[^>]+>)/';
16390 // explodes the string
16391 $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
16392 // count elements
16393 $maxel = count($a);
16394 $elkey = 0;
16395 $key = 0;
16396 // create an array of elements
16397 $dom = array();
16398 $dom[$key] = array();
16399 // set inheritable properties fot the first void element
16400 // possible inheritable properties are: azimuth, border-collapse, border-spacing, caption-side, color, cursor, direction, empty-cells, font, font-family, font-stretch, font-size, font-size-adjust, font-style, font-variant, font-weight, letter-spacing, line-height, list-style, list-style-image, list-style-position, list-style-type, orphans, page, page-break-inside, quotes, speak, speak-header, text-align, text-indent, text-transform, volume, white-space, widows, word-spacing
16401 $dom[$key]['tag'] = false;
16402 $dom[$key]['block'] = false;
16403 $dom[$key]['value'] = '';
16404 $dom[$key]['parent'] = 0;
16405 $dom[$key]['hide'] = false;
16406 $dom[$key]['fontname'] = $this->FontFamily;
16407 $dom[$key]['fontstyle'] = $this->FontStyle;
16408 $dom[$key]['fontsize'] = $this->FontSizePt;
16409 $dom[$key]['font-stretch'] = $this->font_stretching;
16410 $dom[$key]['letter-spacing'] = $this->font_spacing;
16411 $dom[$key]['stroke'] = $this->textstrokewidth;
16412 $dom[$key]['fill'] = (($this->textrendermode % 2) == 0);
16413 $dom[$key]['clip'] = ($this->textrendermode > 3);
16414 $dom[$key]['line-height'] = $this->cell_height_ratio;
16415 $dom[$key]['bgcolor'] = false;
16416 $dom[$key]['fgcolor'] = $this->fgcolor; // color
16417 $dom[$key]['strokecolor'] = $this->strokecolor;
16418 $dom[$key]['align'] = '';
16419 $dom[$key]['listtype'] = '';
16420 $dom[$key]['text-indent'] = 0;
16421 $dom[$key]['text-transform'] = '';
16422 $dom[$key]['border'] = array();
16423 $dom[$key]['dir'] = $this->rtl?'rtl':'ltr';
16424 $thead = false; // true when we are inside the THEAD tag
16425 ++$key;
16426 $level = array();
16427 array_push($level, 0); // root
16428 while ($elkey < $maxel) {
16429 $dom[$key] = array();
16430 $element = $a[$elkey];
16431 $dom[$key]['elkey'] = $elkey;
16432 if (preg_match($tagpattern, $element)) {
16433 // html tag
16434 $element = substr($element, 1, -1);
16435 // get tag name
16436 preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
16437 $tagname = strtolower($tag[1]);
16438 // check if we are inside a table header
16439 if ($tagname == 'thead') {
16440 if ($element[0] == '/') {
16441 $thead = false;
16442 } else {
16443 $thead = true;
16444 }
16445 ++$elkey;
16446 continue;
16447 }
16448 $dom[$key]['tag'] = true;
16449 $dom[$key]['value'] = $tagname;
16450 if (in_array($dom[$key]['value'], $blocktags)) {
16451 $dom[$key]['block'] = true;
16452 } else {
16453 $dom[$key]['block'] = false;
16454 }
16455 if ($element[0] == '/') {
16456 // *** closing html tag
16457 $dom[$key]['opening'] = false;
16458 $dom[$key]['parent'] = end($level);
16459 array_pop($level);
16460 $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
16461 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
16462 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
16463 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
16464 $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
16465 $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
16466 $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
16467 $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
16468 $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
16469 $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
16470 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
16471 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
16472 $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
16473 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
16474 $dom[$key]['text-transform'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['text-transform'];
16475 $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
16476 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
16477 $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
16478 }
16479 // set the number of columns in table tag
16480 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
16481 $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
16482 }
16483 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
16484 $dom[($dom[$key]['parent'])]['content'] = $csstagarray;
16485 for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
16486 $dom[($dom[$key]['parent'])]['content'] .= stripslashes($a[$dom[$i]['elkey']]);
16487 }
16488 $key = $i;
16489 // mark nested tables
16490 $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
16491 // remove thead sections from nested tables
16492 $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
16493 $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
16494 }
16495 // store header rows on a new table
16496 if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
16497 if (TCPDF_STATIC::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
16498 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
16499 }
16500 for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
16501 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
16502 }
16503 if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
16504 $dom[($dom[$key]['parent'])]['attribute'] = array();
16505 }
16506 // header elements must be always contained in a single page
16507 $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
16508 }
16509 if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC::empty_string($dom[($dom[$key]['parent'])]['thead']))) {
16510 // remove the nobr attributes from the table header
16511 $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
16512 $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
16513 }
16514 } else {
16515 // *** opening or self-closing html tag
16516 $dom[$key]['opening'] = true;
16517 $dom[$key]['parent'] = end($level);
16518 if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
16519 // self-closing tag
16520 $dom[$key]['self'] = true;
16521 } else {
16522 // opening tag
16523 array_push($level, $key);
16524 $dom[$key]['self'] = false;
16525 }
16526 // copy some values from parent
16527 $parentkey = 0;
16528 if ($key > 0) {
16529 $parentkey = $dom[$key]['parent'];
16530 $dom[$key]['hide'] = $dom[$parentkey]['hide'];
16531 $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
16532 $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
16533 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
16534 $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
16535 $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
16536 $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
16537 $dom[$key]['fill'] = $dom[$parentkey]['fill'];
16538 $dom[$key]['clip'] = $dom[$parentkey]['clip'];
16539 $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16540 $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
16541 $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
16542 $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
16543 $dom[$key]['align'] = $dom[$parentkey]['align'];
16544 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16545 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16546 $dom[$key]['text-transform'] = $dom[$parentkey]['text-transform'];
16547 $dom[$key]['border'] = array();
16548 $dom[$key]['dir'] = $dom[$parentkey]['dir'];
16549 }
16550 // get attributes
16551 preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER);
16552 $dom[$key]['attribute'] = array(); // reset attribute array
16553 while (list($id, $name) = each($attr_array[1])) {
16554 $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
16555 }
16556 if (!empty($css)) {
16557 // merge CSS style to current style
16558 list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC::getCSSdataArray($dom, $key, $css);
16559 $dom[$key]['attribute']['style'] = TCPDF_STATIC::getTagStyleFromCSSarray($dom[$key]['cssdata']);
16560 }
16561 // split style attributes
16562 if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
16563 // get style attributes
16564 preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
16565 $dom[$key]['style'] = array(); // reset style attribute array
16566 while (list($id, $name) = each($style_array[1])) {
16567 // in case of duplicate attribute the last replace the previous
16568 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
16569 }
16570 // --- get some style attributes ---
16571 // text direction
16572 if (isset($dom[$key]['style']['direction'])) {
16573 $dom[$key]['dir'] = $dom[$key]['style']['direction'];
16574 }
16575 // display
16576 if (isset($dom[$key]['style']['display'])) {
16577 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
16578 }
16579 // font family
16580 if (isset($dom[$key]['style']['font-family'])) {
16581 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
16582 }
16583 // list-style-type
16584 if (isset($dom[$key]['style']['list-style-type'])) {
16585 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
16586 if ($dom[$key]['listtype'] == 'inherit') {
16587 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16588 }
16589 }
16590 // text-indent
16591 if (isset($dom[$key]['style']['text-indent'])) {
16592 $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
16593 if ($dom[$key]['text-indent'] == 'inherit') {
16594 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16595 }
16596 }
16597 // text-transform
16598 if (isset($dom[$key]['style']['text-transform'])) {
16599 $dom[$key]['text-transform'] = $dom[$key]['style']['text-transform'];
16600 }
16601 // font size
16602 if (isset($dom[$key]['style']['font-size'])) {
16603 $fsize = trim($dom[$key]['style']['font-size']);
16604 $dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt');
16605 }
16606 // font-stretch
16607 if (isset($dom[$key]['style']['font-stretch'])) {
16608 $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
16609 }
16610 // letter-spacing
16611 if (isset($dom[$key]['style']['letter-spacing'])) {
16612 $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
16613 }
16614 // line-height (internally is the cell height ratio)
16615 if (isset($dom[$key]['style']['line-height'])) {
16616 $lineheight = trim($dom[$key]['style']['line-height']);
16617 switch ($lineheight) {
16618 // A normal line height. This is default
16619 case 'normal': {
16620 $dom[$key]['line-height'] = $dom[0]['line-height'];
16621 break;
16622 }
16623 case 'inherit': {
16624 $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16625 }
16626 default: {
16627 if (is_numeric($lineheight)) {
16628 // convert to percentage of font height
16629 $lineheight = ($lineheight * 100).'%';
16630 }
16631 $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
16632 if (substr($lineheight, -1) !== '%') {
16633 if ($dom[$key]['fontsize'] <= 0) {
16634 $dom[$key]['line-height'] = 1;
16635 } else {
16636 $dom[$key]['line-height'] = (($dom[$key]['line-height'] - $this->cell_padding['T'] - $this->cell_padding['B']) / $dom[$key]['fontsize']);
16637 }
16638 }
16639 }
16640 }
16641 }
16642 // font style
16643 if (isset($dom[$key]['style']['font-weight'])) {
16644 if (strtolower($dom[$key]['style']['font-weight'][0]) == 'n') {
16645 if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
16646 $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
16647 }
16648 } elseif (strtolower($dom[$key]['style']['font-weight'][0]) == 'b') {
16649 $dom[$key]['fontstyle'] .= 'B';
16650 }
16651 }
16652 if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style'][0]) == 'i')) {
16653 $dom[$key]['fontstyle'] .= 'I';
16654 }
16655 // font color
16656 if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['color']))) {
16657 $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors);
16658 } elseif ($dom[$key]['value'] == 'a') {
16659 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16660 }
16661 // background color
16662 if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['background-color']))) {
16663 $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors);
16664 }
16665 // text-decoration
16666 if (isset($dom[$key]['style']['text-decoration'])) {
16667 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
16668 foreach ($decors as $dec) {
16669 $dec = trim($dec);
16670 if (!TCPDF_STATIC::empty_string($dec)) {
16671 if ($dec[0] == 'u') {
16672 // underline
16673 $dom[$key]['fontstyle'] .= 'U';
16674 } elseif ($dec[0] == 'l') {
16675 // line-through
16676 $dom[$key]['fontstyle'] .= 'D';
16677 } elseif ($dec[0] == 'o') {
16678 // overline
16679 $dom[$key]['fontstyle'] .= 'O';
16680 }
16681 }
16682 }
16683 } elseif ($dom[$key]['value'] == 'a') {
16684 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16685 }
16686 // check for width attribute
16687 if (isset($dom[$key]['style']['width'])) {
16688 $dom[$key]['width'] = $dom[$key]['style']['width'];
16689 }
16690 // check for height attribute
16691 if (isset($dom[$key]['style']['height'])) {
16692 $dom[$key]['height'] = $dom[$key]['style']['height'];
16693 }
16694 // check for text alignment
16695 if (isset($dom[$key]['style']['text-align'])) {
16696 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align'][0]);
16697 }
16698 // check for CSS border properties
16699 if (isset($dom[$key]['style']['border'])) {
16700 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
16701 if (!empty($borderstyle)) {
16702 $dom[$key]['border']['LTRB'] = $borderstyle;
16703 }
16704 }
16705 if (isset($dom[$key]['style']['border-color'])) {
16706 $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
16707 if (isset($brd_colors[3])) {
16708 $dom[$key]['border']['L']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[3], $this->spot_colors);
16709 }
16710 if (isset($brd_colors[1])) {
16711 $dom[$key]['border']['R']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[1], $this->spot_colors);
16712 }
16713 if (isset($brd_colors[0])) {
16714 $dom[$key]['border']['T']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[0], $this->spot_colors);
16715 }
16716 if (isset($brd_colors[2])) {
16717 $dom[$key]['border']['B']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[2], $this->spot_colors);
16718 }
16719 }
16720 if (isset($dom[$key]['style']['border-width'])) {
16721 $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
16722 if (isset($brd_widths[3])) {
16723 $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
16724 }
16725 if (isset($brd_widths[1])) {
16726 $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
16727 }
16728 if (isset($brd_widths[0])) {
16729 $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
16730 }
16731 if (isset($brd_widths[2])) {
16732 $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
16733 }
16734 }
16735 if (isset($dom[$key]['style']['border-style'])) {
16736 $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
16737 if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) {
16738 $dom[$key]['border']['L']['cap'] = 'square';
16739 $dom[$key]['border']['L']['join'] = 'miter';
16740 $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
16741 if ($dom[$key]['border']['L']['dash'] < 0) {
16742 $dom[$key]['border']['L'] = array();
16743 }
16744 }
16745 if (isset($brd_styles[1])) {
16746 $dom[$key]['border']['R']['cap'] = 'square';
16747 $dom[$key]['border']['R']['join'] = 'miter';
16748 $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
16749 if ($dom[$key]['border']['R']['dash'] < 0) {
16750 $dom[$key]['border']['R'] = array();
16751 }
16752 }
16753 if (isset($brd_styles[0])) {
16754 $dom[$key]['border']['T']['cap'] = 'square';
16755 $dom[$key]['border']['T']['join'] = 'miter';
16756 $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
16757 if ($dom[$key]['border']['T']['dash'] < 0) {
16758 $dom[$key]['border']['T'] = array();
16759 }
16760 }
16761 if (isset($brd_styles[2])) {
16762 $dom[$key]['border']['B']['cap'] = 'square';
16763 $dom[$key]['border']['B']['join'] = 'miter';
16764 $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
16765 if ($dom[$key]['border']['B']['dash'] < 0) {
16766 $dom[$key]['border']['B'] = array();
16767 }
16768 }
16769 }
16770 $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
16771 foreach ($cellside as $bsk => $bsv) {
16772 if (isset($dom[$key]['style']['border-'.$bsv])) {
16773 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
16774 if (!empty($borderstyle)) {
16775 $dom[$key]['border'][$bsk] = $borderstyle;
16776 }
16777 }
16778 if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
16779 $dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors);
16780 }
16781 if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
16782 $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
16783 }
16784 if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
16785 $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
16786 if ($dom[$key]['border'][$bsk]['dash'] < 0) {
16787 $dom[$key]['border'][$bsk] = array();
16788 }
16789 }
16790 }
16791 // check for CSS padding properties
16792 if (isset($dom[$key]['style']['padding'])) {
16793 $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
16794 } else {
16795 $dom[$key]['padding'] = $this->cell_padding;
16796 }
16797 foreach ($cellside as $psk => $psv) {
16798 if (isset($dom[$key]['style']['padding-'.$psv])) {
16799 $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
16800 }
16801 }
16802 // check for CSS margin properties
16803 if (isset($dom[$key]['style']['margin'])) {
16804 $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
16805 } else {
16806 $dom[$key]['margin'] = $this->cell_margin;
16807 }
16808 foreach ($cellside as $psk => $psv) {
16809 if (isset($dom[$key]['style']['margin-'.$psv])) {
16810 $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
16811 }
16812 }
16813 // check for CSS border-spacing properties
16814 if (isset($dom[$key]['style']['border-spacing'])) {
16815 $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
16816 }
16817 // page-break-inside
16818 if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
16819 $dom[$key]['attribute']['nobr'] = 'true';
16820 }
16821 // page-break-before
16822 if (isset($dom[$key]['style']['page-break-before'])) {
16823 if ($dom[$key]['style']['page-break-before'] == 'always') {
16824 $dom[$key]['attribute']['pagebreak'] = 'true';
16825 } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
16826 $dom[$key]['attribute']['pagebreak'] = 'left';
16827 } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
16828 $dom[$key]['attribute']['pagebreak'] = 'right';
16829 }
16830 }
16831 // page-break-after
16832 if (isset($dom[$key]['style']['page-break-after'])) {
16833 if ($dom[$key]['style']['page-break-after'] == 'always') {
16834 $dom[$key]['attribute']['pagebreakafter'] = 'true';
16835 } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
16836 $dom[$key]['attribute']['pagebreakafter'] = 'left';
16837 } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
16838 $dom[$key]['attribute']['pagebreakafter'] = 'right';
16839 }
16840 }
16841 }
16842 if (isset($dom[$key]['attribute']['display'])) {
16843 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
16844 }
16845 if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
16846 $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
16847 if (!empty($borderstyle)) {
16848 $dom[$key]['border']['LTRB'] = $borderstyle;
16849 }
16850 }
16851 // check for font tag
16852 if ($dom[$key]['value'] == 'font') {
16853 // font family
16854 if (isset($dom[$key]['attribute']['face'])) {
16855 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
16856 }
16857 // font size
16858 if (isset($dom[$key]['attribute']['size'])) {
16859 if ($key > 0) {
16860 if ($dom[$key]['attribute']['size'][0] == '+') {
16861 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
16862 } elseif ($dom[$key]['attribute']['size'][0] == '-') {
16863 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
16864 } else {
16865 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16866 }
16867 } else {
16868 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16869 }
16870 }
16871 }
16872 // force natural alignment for lists
16873 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
16874 AND (!isset($dom[$key]['align']) OR TCPDF_STATIC::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
16875 if ($this->rtl) {
16876 $dom[$key]['align'] = 'R';
16877 } else {
16878 $dom[$key]['align'] = 'L';
16879 }
16880 }
16881 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
16882 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16883 $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
16884 }
16885 }
16886 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
16887 $dom[$key]['fontstyle'] .= 'B';
16888 }
16889 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
16890 $dom[$key]['fontstyle'] .= 'I';
16891 }
16892 if ($dom[$key]['value'] == 'u') {
16893 $dom[$key]['fontstyle'] .= 'U';
16894 }
16895 if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
16896 $dom[$key]['fontstyle'] .= 'D';
16897 }
16898 if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
16899 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16900 }
16901 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
16902 $dom[$key]['fontname'] = $this->default_monospaced_font;
16903 }
16904 if (!empty($dom[$key]['value']) AND ($dom[$key]['value'][0] == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
16905 // headings h1, h2, h3, h4, h5, h6
16906 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16907 $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
16908 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
16909 }
16910 if (!isset($dom[$key]['style']['font-weight'])) {
16911 $dom[$key]['fontstyle'] .= 'B';
16912 }
16913 }
16914 if (($dom[$key]['value'] == 'table')) {
16915 $dom[$key]['rows'] = 0; // number of rows
16916 $dom[$key]['trids'] = array(); // IDs of TR elements
16917 $dom[$key]['thead'] = ''; // table header rows
16918 }
16919 if (($dom[$key]['value'] == 'tr')) {
16920 $dom[$key]['cols'] = 0;
16921 if ($thead) {
16922 $dom[$key]['thead'] = true;
16923 // rows on thead block are printed as a separate table
16924 } else {
16925 $dom[$key]['thead'] = false;
16926 // store the number of rows on table element
16927 ++$dom[($dom[$key]['parent'])]['rows'];
16928 // store the TR elements IDs on table element
16929 array_push($dom[($dom[$key]['parent'])]['trids'], $key);
16930 }
16931 }
16932 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
16933 if (isset($dom[$key]['attribute']['colspan'])) {
16934 $colspan = intval($dom[$key]['attribute']['colspan']);
16935 } else {
16936 $colspan = 1;
16937 }
16938 $dom[$key]['attribute']['colspan'] = $colspan;
16939 $dom[($dom[$key]['parent'])]['cols'] += $colspan;
16940 }
16941 // text direction
16942 if (isset($dom[$key]['attribute']['dir'])) {
16943 $dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
16944 }
16945 // set foreground color attribute
16946 if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['color']))) {
16947 $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors);
16948 } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
16949 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16950 }
16951 // set background color attribute
16952 if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['bgcolor']))) {
16953 $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors);
16954 }
16955 // set stroke color attribute
16956 if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['strokecolor']))) {
16957 $dom[$key]['strokecolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors);
16958 }
16959 // check for width attribute
16960 if (isset($dom[$key]['attribute']['width'])) {
16961 $dom[$key]['width'] = $dom[$key]['attribute']['width'];
16962 }
16963 // check for height attribute
16964 if (isset($dom[$key]['attribute']['height'])) {
16965 $dom[$key]['height'] = $dom[$key]['attribute']['height'];
16966 }
16967 // check for text alignment
16968 if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
16969 $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align'][0]);
16970 }
16971 // check for text rendering mode (the following attributes do not exist in HTML)
16972 if (isset($dom[$key]['attribute']['stroke'])) {
16973 // font stroke width
16974 $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
16975 }
16976 if (isset($dom[$key]['attribute']['fill'])) {
16977 // font fill
16978 if ($dom[$key]['attribute']['fill'] == 'true') {
16979 $dom[$key]['fill'] = true;
16980 } else {
16981 $dom[$key]['fill'] = false;
16982 }
16983 }
16984 if (isset($dom[$key]['attribute']['clip'])) {
16985 // clipping mode
16986 if ($dom[$key]['attribute']['clip'] == 'true') {
16987 $dom[$key]['clip'] = true;
16988 } else {
16989 $dom[$key]['clip'] = false;
16990 }
16991 }
16992 } // end opening tag
16993 } else {
16994 // text
16995 $dom[$key]['tag'] = false;
16996 $dom[$key]['block'] = false;
16997 $dom[$key]['parent'] = end($level);
16998 $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
16999 if (!empty($dom[$dom[$key]['parent']]['text-transform'])) {
17000 // text-transform for unicode requires mb_convert_case (Multibyte String Functions)
17001 if (function_exists('mb_convert_case')) {
17002 $ttm = array('capitalize' => MB_CASE_TITLE, 'uppercase' => MB_CASE_UPPER, 'lowercase' => MB_CASE_LOWER);
17003 if (isset($ttm[$dom[$dom[$key]['parent']]['text-transform']])) {
17004 $element = mb_convert_case($element, $ttm[$dom[$dom[$key]['parent']]['text-transform']], $this->encoding);
17005 }
17006 } elseif (!$this->isunicode) {
17007 switch ($dom[$dom[$key]['parent']]['text-transform']) {
17008 case 'capitalize': {
17009 $element = ucwords(strtolower($element));
17010 break;
17011 }
17012 case 'uppercase': {
17013 $element = strtoupper($element);
17014 break;
17015 }
17016 case 'lowercase': {
17017 $element = strtolower($element);
17018 break;
17019 }
17020 }
17021 }
17022 }
17023 $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
17024 }
17025 ++$elkey;
17026 ++$key;
17027 }
17028 return $dom;
17029 }
17030
17038 protected function getSpaceString() {
17039 $spacestr = chr(32);
17040 if ($this->isUnicodeFont()) {
17041 $spacestr = chr(0).chr(32);
17042 }
17043 return $spacestr;
17044 }
17045
17052 protected function getHashForTCPDFtagParams($data) {
17053 return md5(strlen($data).$this->file_id.$data);
17054 }
17055
17063 $encoded = urlencode(json_encode($data));
17064 return $this->getHashForTCPDFtagParams($encoded).$encoded;
17065 }
17066
17074 $hash = substr($data, 0, 32);
17075 $encoded = substr($data, 32);
17076 if ($hash != $this->getHashForTCPDFtagParams($encoded)) {
17077 $this->Error('Invalid parameters');
17078 }
17079 return json_decode(urldecode($encoded), true);
17080 }
17081
17104 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
17105 return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
17106 }
17107
17121 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
17122 $gvars = $this->getGraphicVars();
17123 // store current values
17124 $prev_cell_margin = $this->cell_margin;
17125 $prev_cell_padding = $this->cell_padding;
17126 $prevPage = $this->page;
17127 $prevlMargin = $this->lMargin;
17128 $prevrMargin = $this->rMargin;
17129 $curfontname = $this->FontFamily;
17130 $curfontstyle = $this->FontStyle;
17131 $curfontsize = $this->FontSizePt;
17132 $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
17133 $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
17134 $curfontstretcing = $this->font_stretching;
17135 $curfonttracking = $this->font_spacing;
17136 $this->newline = true;
17137 $newline = true;
17138 $startlinepage = $this->page;
17139 $minstartliney = $this->y;
17140 $maxbottomliney = 0;
17141 $startlinex = $this->x;
17142 $startliney = $this->y;
17143 $yshift = 0;
17144 $loop = 0;
17145 $curpos = 0;
17146 $this_method_vars = array();
17147 $undo = false;
17148 $fontaligned = false;
17149 $reverse_dir = false; // true when the text direction is reversed
17150 $this->premode = false;
17151 if ($this->inxobj) {
17152 // we are inside an XObject template
17153 $pask = count($this->xobjects[$this->xobjid]['annotations']);
17154 } elseif (isset($this->PageAnnots[$this->page])) {
17155 $pask = count($this->PageAnnots[$this->page]);
17156 } else {
17157 $pask = 0;
17158 }
17159 if ($this->inxobj) {
17160 // we are inside an XObject template
17161 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17162 } elseif (!$this->InFooter) {
17163 if (isset($this->footerlen[$this->page])) {
17164 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17165 } else {
17166 $this->footerpos[$this->page] = $this->pagelen[$this->page];
17167 }
17168 $startlinepos = $this->footerpos[$this->page];
17169 } else {
17170 // we are inside the footer
17171 $startlinepos = $this->pagelen[$this->page];
17172 }
17173 $lalign = $align;
17174 $plalign = $align;
17175 if ($this->rtl) {
17176 $w = $this->x - $this->lMargin;
17177 } else {
17178 $w = $this->w - $this->rMargin - $this->x;
17179 }
17180 $w -= ($this->cell_padding['L'] + $this->cell_padding['R']);
17181 if ($cell) {
17182 if ($this->rtl) {
17183 $this->x -= $this->cell_padding['R'];
17184 $this->lMargin += $this->cell_padding['R'];
17185 } else {
17186 $this->x += $this->cell_padding['L'];
17187 $this->rMargin += $this->cell_padding['L'];
17188 }
17189 }
17190 if ($this->customlistindent >= 0) {
17191 $this->listindent = $this->customlistindent;
17192 } else {
17193 $this->listindent = $this->GetStringWidth('000000');
17194 }
17195 $this->listindentlevel = 0;
17196 // save previous states
17197 $prev_cell_height_ratio = $this->cell_height_ratio;
17198 $prev_listnum = $this->listnum;
17199 $prev_listordered = $this->listordered;
17200 $prev_listcount = $this->listcount;
17201 $prev_lispacer = $this->lispacer;
17202 $this->listnum = 0;
17203 $this->listordered = array();
17204 $this->listcount = array();
17205 $this->lispacer = '';
17206 if ((TCPDF_STATIC::empty_string($this->lasth)) OR ($reseth)) {
17207 // reset row height
17208 $this->resetLastH();
17209 }
17210 $dom = $this->getHtmlDomArray($html);
17211 $maxel = count($dom);
17212 $key = 0;
17213 while ($key < $maxel) {
17214 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND $dom[$key]['hide']) {
17215 // store the node key
17216 $hidden_node_key = $key;
17217 if ($dom[$key]['self']) {
17218 // skip just this self-closing tag
17219 ++$key;
17220 } else {
17221 // skip this and all children tags
17222 while (($key < $maxel) AND (!$dom[$key]['tag'] OR $dom[$key]['opening'] OR ($dom[$key]['parent'] != $hidden_node_key))) {
17223 // skip hidden objects
17224 ++$key;
17225 }
17226 ++$key;
17227 }
17228 }
17229 if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
17230 // check for pagebreak
17231 if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
17232 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17233 $this->checkPageBreak($this->PageBreakTrigger + 1);
17234 $this->htmlvspace = ($this->PageBreakTrigger + 1);
17235 }
17236 if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
17237 OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
17238 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17239 $this->checkPageBreak($this->PageBreakTrigger + 1);
17240 $this->htmlvspace = ($this->PageBreakTrigger + 1);
17241 }
17242 }
17243 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
17244 if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
17245 $dom[$key]['attribute']['nobr'] = false;
17246 } else {
17247 // store current object
17248 $this->startTransaction();
17249 // save this method vars
17250 $this_method_vars['html'] = $html;
17251 $this_method_vars['ln'] = $ln;
17252 $this_method_vars['fill'] = $fill;
17253 $this_method_vars['reseth'] = $reseth;
17254 $this_method_vars['cell'] = $cell;
17255 $this_method_vars['align'] = $align;
17256 $this_method_vars['gvars'] = $gvars;
17257 $this_method_vars['prevPage'] = $prevPage;
17258 $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
17259 $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
17260 $this_method_vars['prevlMargin'] = $prevlMargin;
17261 $this_method_vars['prevrMargin'] = $prevrMargin;
17262 $this_method_vars['curfontname'] = $curfontname;
17263 $this_method_vars['curfontstyle'] = $curfontstyle;
17264 $this_method_vars['curfontsize'] = $curfontsize;
17265 $this_method_vars['curfontascent'] = $curfontascent;
17266 $this_method_vars['curfontdescent'] = $curfontdescent;
17267 $this_method_vars['curfontstretcing'] = $curfontstretcing;
17268 $this_method_vars['curfonttracking'] = $curfonttracking;
17269 $this_method_vars['minstartliney'] = $minstartliney;
17270 $this_method_vars['maxbottomliney'] = $maxbottomliney;
17271 $this_method_vars['yshift'] = $yshift;
17272 $this_method_vars['startlinepage'] = $startlinepage;
17273 $this_method_vars['startlinepos'] = $startlinepos;
17274 $this_method_vars['startlinex'] = $startlinex;
17275 $this_method_vars['startliney'] = $startliney;
17276 $this_method_vars['newline'] = $newline;
17277 $this_method_vars['loop'] = $loop;
17278 $this_method_vars['curpos'] = $curpos;
17279 $this_method_vars['pask'] = $pask;
17280 $this_method_vars['lalign'] = $lalign;
17281 $this_method_vars['plalign'] = $plalign;
17282 $this_method_vars['w'] = $w;
17283 $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
17284 $this_method_vars['prev_listnum'] = $prev_listnum;
17285 $this_method_vars['prev_listordered'] = $prev_listordered;
17286 $this_method_vars['prev_listcount'] = $prev_listcount;
17287 $this_method_vars['prev_lispacer'] = $prev_lispacer;
17288 $this_method_vars['fontaligned'] = $fontaligned;
17289 $this_method_vars['key'] = $key;
17290 $this_method_vars['dom'] = $dom;
17291 }
17292 }
17293 // print THEAD block
17294 if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
17295 if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC::empty_string($dom[$dom[$key]['parent']]['thead'])) {
17296 $this->inthead = true;
17297 // print table header (thead)
17298 $this->writeHTML($this->thead, false, false, false, false, '');
17299 // check if we are on a new page or on a new column
17300 if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
17301 // we are on a new page or on a new column and the total object height is less than the available vertical space.
17302 // restore previous object
17303 $this->rollbackTransaction(true);
17304 // restore previous values
17305 foreach ($this_method_vars as $vkey => $vval) {
17306 $$vkey = $vval;
17307 }
17308 // disable table header
17309 $tmp_thead = $this->thead;
17310 $this->thead = '';
17311 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17312 $pre_y = $this->y;
17313 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
17314 // fix for multicolumn mode
17315 $startliney = $this->y;
17316 }
17317 $this->start_transaction_page = $this->page;
17318 $this->start_transaction_y = $this->y;
17319 // restore table header
17320 $this->thead = $tmp_thead;
17321 // fix table border properties
17322 if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
17323 $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
17324 } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
17325 $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
17326 } else {
17327 $tmp_cellspacing = 0;
17328 }
17329 $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
17330 $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
17331 $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
17332 $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']);
17333 $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
17334 $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
17335 // print table header (thead)
17336 $this->writeHTML($this->thead, false, false, false, false, '');
17337 }
17338 }
17339 // move $key index forward to skip THEAD block
17340 while ( ($key < $maxel) AND (!(
17341 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
17342 OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
17343 ++$key;
17344 }
17345 }
17346 if ($dom[$key]['tag'] OR ($key == 0)) {
17347 if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
17348 $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
17349 }
17350 // vertically align image in line
17351 if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
17352 // get image height
17353 $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], ($dom[$key]['fontsize'] / $this->k), 'px');
17354 $autolinebreak = false;
17355 if (!empty($dom[$key]['width'])) {
17356 $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], ($dom[$key]['fontsize'] / $this->k), 'px', false);
17357 if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R']))
17358 AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L'])))
17359 OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) {
17360 // add automatic line break
17361 $autolinebreak = true;
17362 $this->Ln('', $cell);
17363 if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
17364 // go back to evaluate this line break
17365 --$key;
17366 }
17367 }
17368 }
17369 if (!$autolinebreak) {
17370 if ($this->inPageBody()) {
17371 $pre_y = $this->y;
17372 // check for page break
17373 if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
17374 // fix for multicolumn mode
17375 $startliney = $this->y;
17376 }
17377 }
17378 if ($this->page > $startlinepage) {
17379 // fix line splitted over two pages
17380 if (isset($this->footerlen[$startlinepage])) {
17381 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17382 }
17383 // line to be moved one page forward
17384 $pagebuff = $this->getPageBuffer($startlinepage);
17385 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17386 $tstart = substr($pagebuff, 0, $startlinepos);
17387 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17388 // remove line from previous page
17389 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17390 $pagebuff = $this->getPageBuffer($this->page);
17391 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17392 $tend = substr($pagebuff, $this->cntmrk[$this->page]);
17393 // add line start to current page
17394 $yshift = ($minstartliney - $this->y);
17395 if ($fontaligned) {
17396 $yshift += ($curfontsize / $this->k);
17397 }
17398 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17399 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17400 // shift the annotations and links
17401 if (isset($this->PageAnnots[$this->page])) {
17402 $next_pask = count($this->PageAnnots[$this->page]);
17403 } else {
17404 $next_pask = 0;
17405 }
17406 if (isset($this->PageAnnots[$startlinepage])) {
17407 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17408 if ($pak >= $pask) {
17409 $this->PageAnnots[$this->page][] = $pac;
17410 unset($this->PageAnnots[$startlinepage][$pak]);
17411 $npak = count($this->PageAnnots[$this->page]) - 1;
17412 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17413 }
17414 }
17415 }
17416 $pask = $next_pask;
17417 $startlinepos = $this->cntmrk[$this->page];
17418 $startlinepage = $this->page;
17419 $startliney = $this->y;
17420 $this->newline = false;
17421 }
17422 $this->y += ($this->getCellHeight($curfontsize / $this->k) - ($curfontdescent * $this->cell_height_ratio) - $imgh);
17423 $minstartliney = min($this->y, $minstartliney);
17424 $maxbottomliney = ($startliney + $this->getCellHeight($curfontsize / $this->k));
17425 }
17426 } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
17427 // account for different font size
17428 $pfontname = $curfontname;
17429 $pfontstyle = $curfontstyle;
17430 $pfontsize = $curfontsize;
17431 $fontname = (isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname);
17432 $fontstyle = (isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle);
17433 $fontsize = (isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize);
17434 $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
17435 $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
17436 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
17437 OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17438 OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
17439 if (($key < ($maxel - 1)) AND (
17440 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
17441 OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17442 OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize)
17443 AND ($fontsize >= 0) AND ($curfontsize >= 0)
17444 AND (($fontsize != $curfontsize) OR ($fontstyle != $curfontstyle) OR ($fontname != $curfontname)))
17445 )) {
17446 if ($this->page > $startlinepage) {
17447 // fix lines splitted over two pages
17448 if (isset($this->footerlen[$startlinepage])) {
17449 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17450 }
17451 // line to be moved one page forward
17452 $pagebuff = $this->getPageBuffer($startlinepage);
17453 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17454 $tstart = substr($pagebuff, 0, $startlinepos);
17455 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17456 // remove line start from previous page
17457 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17458 $pagebuff = $this->getPageBuffer($this->page);
17459 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17460 $tend = substr($pagebuff, $this->cntmrk[$this->page]);
17461 // add line start to current page
17462 $yshift = ($minstartliney - $this->y);
17463 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17464 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17465 // shift the annotations and links
17466 if (isset($this->PageAnnots[$this->page])) {
17467 $next_pask = count($this->PageAnnots[$this->page]);
17468 } else {
17469 $next_pask = 0;
17470 }
17471 if (isset($this->PageAnnots[$startlinepage])) {
17472 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17473 if ($pak >= $pask) {
17474 $this->PageAnnots[$this->page][] = $pac;
17475 unset($this->PageAnnots[$startlinepage][$pak]);
17476 $npak = count($this->PageAnnots[$this->page]) - 1;
17477 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17478 }
17479 }
17480 }
17481 $pask = $next_pask;
17482 $startlinepos = $this->cntmrk[$this->page];
17483 $startlinepage = $this->page;
17484 $startliney = $this->y;
17485 }
17486 if (!isset($dom[$key]['line-height'])) {
17487 $dom[$key]['line-height'] = $this->cell_height_ratio;
17488 }
17489 if (!$dom[$key]['block']) {
17490 if (!(isset($dom[($key + 1)]) AND $dom[($key + 1)]['tag'] AND (!$dom[($key + 1)]['opening']) AND ($dom[($key + 1)]['value'] != 'li') AND $dom[$key]['tag'] AND (!$dom[$key]['opening']))) {
17491 $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
17492 }
17493 if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
17494 $current_line_align_data = array($key, $minstartliney, $maxbottomliney);
17495 if (isset($line_align_data) AND (($line_align_data[0] == ($key - 1)) OR (($line_align_data[0] == ($key - 2)) AND (isset($dom[($key - 1)])) AND (preg_match('/^([\s]+)$/', $dom[($key - 1)]['value']) > 0)))) {
17496 $minstartliney = min($this->y, $line_align_data[1]);
17497 $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $line_align_data[2]);
17498 } else {
17499 $minstartliney = min($this->y, $minstartliney);
17500 $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $maxbottomliney);
17501 }
17502 $line_align_data = $current_line_align_data;
17503 }
17504 }
17505 $this->cell_height_ratio = $dom[$key]['line-height'];
17506 $fontaligned = true;
17507 }
17508 $this->SetFont($fontname, $fontstyle, $fontsize);
17509 // reset row height
17510 $this->resetLastH();
17511 $curfontname = $fontname;
17512 $curfontstyle = $fontstyle;
17513 $curfontsize = $fontsize;
17514 $curfontascent = $fontascent;
17515 $curfontdescent = $fontdescent;
17516 }
17517 }
17518 // set text rendering mode
17519 $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
17520 $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
17521 $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
17522 $this->setTextRenderingMode($textstroke, $textfill, $textclip);
17523 if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
17524 $this->setFontStretching($dom[$key]['font-stretch']);
17525 }
17526 if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
17527 $this->setFontSpacing($dom[$key]['letter-spacing']);
17528 }
17529 if (($plalign == 'J') AND $dom[$key]['block']) {
17530 $plalign = '';
17531 }
17532 // get current position on page buffer
17533 $curpos = $this->pagelen[$startlinepage];
17534 if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
17535 $this->SetFillColorArray($dom[$key]['bgcolor']);
17536 $wfill = true;
17537 } else {
17538 $wfill = $fill | false;
17539 }
17540 if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
17541 $this->SetTextColorArray($dom[$key]['fgcolor']);
17542 }
17543 if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
17544 $this->SetDrawColorArray($dom[$key]['strokecolor']);
17545 }
17546 if (isset($dom[$key]['align'])) {
17547 $lalign = $dom[$key]['align'];
17548 }
17549 if (TCPDF_STATIC::empty_string($lalign)) {
17550 $lalign = $align;
17551 }
17552 }
17553 // align lines
17554 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
17555 $newline = true;
17556 $fontaligned = false;
17557 // we are at the beginning of a new line
17558 if (isset($startlinex)) {
17559 $yshift = ($minstartliney - $startliney);
17560 if (($yshift > 0) OR ($this->page > $startlinepage)) {
17561 $yshift = 0;
17562 }
17563 $t_x = 0;
17564 // the last line must be shifted to be aligned as requested
17565 $linew = abs($this->endlinex - $startlinex);
17566 if ($this->inxobj) {
17567 // we are inside an XObject template
17568 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
17569 if (isset($opentagpos)) {
17570 $midpos = $opentagpos;
17571 } else {
17572 $midpos = 0;
17573 }
17574 if ($midpos > 0) {
17575 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
17576 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
17577 } else {
17578 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
17579 $pend = '';
17580 }
17581 } else {
17582 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
17583 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17584 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17585 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
17586 } elseif (isset($opentagpos)) {
17587 $midpos = $opentagpos;
17588 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17589 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17590 $midpos = $this->footerpos[$startlinepage];
17591 } else {
17592 $midpos = 0;
17593 }
17594 if ($midpos > 0) {
17595 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
17596 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
17597 } else {
17598 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
17599 $pend = '';
17600 }
17601 }
17602 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
17603 // calculate shifting amount
17604 $tw = $w;
17605 if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
17606 $tw += $this->cell_padding['R'];
17607 }
17608 if ($this->lMargin != $prevlMargin) {
17609 $tw += ($prevlMargin - $this->lMargin);
17610 }
17611 if ($this->rMargin != $prevrMargin) {
17612 $tw += ($prevrMargin - $this->rMargin);
17613 }
17614 $one_space_width = $this->GetStringWidth(chr(32));
17615 $no = 0; // number of spaces on a line contained on a single block
17616 if ($this->isRTLTextDir()) { // RTL
17617 // remove left space if exist
17618 $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
17619 if ($pos1 > 0) {
17620 $pos1 = intval($pos1);
17621 if ($this->isUnicodeFont()) {
17622 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
17623 $spacelen = 2;
17624 } else {
17625 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
17626 $spacelen = 1;
17627 }
17628 if ($pos1 == $pos2) {
17629 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
17630 if (substr($pmid, $pos1, 4) == '[()]') {
17631 $linew -= $one_space_width;
17632 } elseif ($pos1 == strpos($pmid, '[(')) {
17633 $no = 1;
17634 }
17635 }
17636 }
17637 } else { // LTR
17638 // remove right space if exist
17639 $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
17640 if ($pos1 > 0) {
17641 $pos1 = intval($pos1);
17642 if ($this->isUnicodeFont()) {
17643 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
17644 $spacelen = 2;
17645 } else {
17646 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
17647 $spacelen = 1;
17648 }
17649 if ($pos1 == $pos2) {
17650 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
17651 $linew -= $one_space_width;
17652 }
17653 }
17654 }
17655 $mdiff = ($tw - $linew);
17656 if ($plalign == 'C') {
17657 if ($this->rtl) {
17658 $t_x = -($mdiff / 2);
17659 } else {
17660 $t_x = ($mdiff / 2);
17661 }
17662 } elseif ($plalign == 'R') {
17663 // right alignment on LTR document
17664 $t_x = $mdiff;
17665 } elseif ($plalign == 'L') {
17666 // left alignment on RTL document
17667 $t_x = -$mdiff;
17668 } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
17669 // Justification
17670 if ($this->isRTLTextDir()) {
17671 // align text on the left
17672 $t_x = -$mdiff;
17673 }
17674 $ns = 0; // number of spaces
17675 $pmidtemp = $pmid;
17676 // escape special characters
17677 $pmidtemp = preg_replace('/[\\\][\‍(]/x', '\\#!#OP#!#', $pmidtemp);
17678 $pmidtemp = preg_replace('/[\\\][\‍)]/x', '\\#!#CP#!#', $pmidtemp);
17679 // search spaces
17680 if (preg_match_all('/\[\‍(([^\‍)]*)\‍)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
17681 $spacestr = $this->getSpaceString();
17682 $maxkk = count($lnstring[1]) - 1;
17683 for ($kk=0; $kk <= $maxkk; ++$kk) {
17684 // restore special characters
17685 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
17686 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
17687 // store number of spaces on the strings
17688 $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
17689 // count total spaces on line
17690 $ns += $lnstring[2][$kk];
17691 $lnstring[3][$kk] = $ns;
17692 }
17693 if ($ns == 0) {
17694 $ns = 1;
17695 }
17696 // calculate additional space to add to each existing space
17697 $spacewidth = ($mdiff / ($ns - $no)) * $this->k;
17698 if ($this->FontSize <= 0) {
17699 $this->FontSize = 1;
17700 }
17701 $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize;
17702 if ($this->font_spacing != 0) {
17703 // fixed spacing mode
17704 $osw = -1000 * $this->font_spacing / $this->FontSize;
17705 $spacewidthu += $osw;
17706 }
17707 $nsmax = $ns;
17708 $ns = 0;
17709 reset($lnstring);
17710 $offset = 0;
17711 $strcount = 0;
17712 $prev_epsposbeg = 0;
17713 $textpos = 0;
17714 if ($this->isRTLTextDir()) {
17715 $textpos = $this->wPt;
17716 }
17717 while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
17718 // check if we are inside a string section '[( ... )]'
17719 $stroffset = strpos($pmid, '[(', $offset);
17720 if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
17721 // set offset to the end of string section
17722 $offset = strpos($pmid, ')]', $stroffset);
17723 while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
17724 $offset = strpos($pmid, ')]', ($offset + 1));
17725 }
17726 if ($offset === false) {
17727 $this->Error('HTML Justification: malformed PDF code.');
17728 }
17729 continue;
17730 }
17731 if ($this->isRTLTextDir()) {
17732 $spacew = ($spacewidth * ($nsmax - $ns));
17733 } else {
17734 $spacew = ($spacewidth * $ns);
17735 }
17736 $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
17737 $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset);
17738 if ($epsposend !== null) {
17739 $epsposend += strlen($this->epsmarker.'Q');
17740 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
17741 if ($epsposbeg === null) {
17742 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
17743 $prev_epsposbeg = $epsposbeg;
17744 }
17745 if (($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) {
17746 // shift EPS images
17747 $trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
17748 $pmid_b = substr($pmid, 0, $epsposbeg);
17749 $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
17750 $pmid_e = substr($pmid, $epsposend);
17751 $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
17752 $offset = $epsposend;
17753 continue;
17754 }
17755 }
17756 $currentxpos = 0;
17757 // shift blocks of code
17758 switch ($strpiece[2][0]) {
17759 case 'Td':
17760 case 'cm':
17761 case 'm':
17762 case 'l': {
17763 // get current X position
17764 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
17765 if (!isset($xmatches[1])) {
17766 break;
17767 }
17768 $currentxpos = $xmatches[1];
17769 $textpos = $currentxpos;
17770 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
17771 $ns = $lnstring[3][$strcount];
17772 if ($this->isRTLTextDir()) {
17773 $spacew = ($spacewidth * ($nsmax - $ns));
17774 }
17775 ++$strcount;
17776 }
17777 // justify block
17778 if (preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $pmatch) == 1) {
17779 $newpmid = sprintf('%F',(floatval($pmatch[1]) + $spacew)).' '.$pmatch[2].' x*#!#*x'.$pmatch[3].$pmatch[4];
17780 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17781 unset($pmatch, $newpmid);
17782 }
17783 break;
17784 }
17785 case 're': {
17786 // justify block
17787 if (!TCPDF_STATIC::empty_string($this->lispacer)) {
17788 $this->lispacer = '';
17789 continue;
17790 }
17791 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
17792 if (!isset($xmatches[1])) {
17793 break;
17794 }
17795 $currentxpos = $xmatches[1];
17796 $x_diff = 0;
17797 $w_diff = 0;
17798 if ($this->isRTLTextDir()) { // RTL
17799 if ($currentxpos < $textpos) {
17800 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
17801 $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17802 } else {
17803 if ($strcount > 0) {
17804 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
17805 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17806 }
17807 }
17808 } else { // LTR
17809 if ($currentxpos > $textpos) {
17810 if ($strcount > 0) {
17811 $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
17812 }
17813 $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17814 } else {
17815 if ($strcount > 1) {
17816 $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
17817 }
17818 if ($strcount > 0) {
17819 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17820 }
17821 }
17822 }
17823 if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $pmatch) == 1) {
17824 $newx = sprintf('%F',(floatval($pmatch[1]) + $x_diff));
17825 $neww = sprintf('%F',(floatval($pmatch[3]) + $w_diff));
17826 $newpmid = $newx.' '.$pmatch[2].' '.$neww.' '.$pmatch[4].' x*#!#*x'.$pmatch[5].$pmatch[6];
17827 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17828 unset($pmatch, $newpmid, $newx, $neww);
17829 }
17830 break;
17831 }
17832 case 'c': {
17833 // get current X position
17834 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', $pmid, $xmatches);
17835 if (!isset($xmatches[1])) {
17836 break;
17837 }
17838 $currentxpos = $xmatches[1];
17839 // justify block
17840 if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$xmatches[4].')[\s]('.$xmatches[5].')[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', $pmid, $pmatch) == 1) {
17841 $newx1 = sprintf('%F',(floatval($pmatch[1]) + $spacew));
17842 $newx2 = sprintf('%F',(floatval($pmatch[3]) + $spacew));
17843 $newx3 = sprintf('%F',(floatval($pmatch[5]) + $spacew));
17844 $newpmid = $newx1.' '.$pmatch[2].' '.$newx2.' '.$pmatch[4].' '.$newx3.' '.$pmatch[6].' x*#!#*x'.$pmatch[7].$pmatch[8];
17845 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17846 unset($pmatch, $newpmid, $newx1, $newx2, $newx3);
17847 }
17848 break;
17849 }
17850 }
17851 // shift the annotations and links
17852 $cxpos = ($currentxpos / $this->k);
17853 $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps);
17854 if ($this->inxobj) {
17855 // we are inside an XObject template
17856 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17857 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17858 if ($cxpos > $lmpos) {
17859 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k);
17860 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17861 } else {
17862 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17863 }
17864 break;
17865 }
17866 }
17867 } elseif (isset($this->PageAnnots[$this->page])) {
17868 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17869 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17870 if ($cxpos > $lmpos) {
17871 $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
17872 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17873 } else {
17874 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17875 }
17876 break;
17877 }
17878 }
17879 }
17880 } // end of while
17881 // remove markers
17882 $pmid = str_replace('x*#!#*x', '', $pmid);
17883 if ($this->isUnicodeFont()) {
17884 // multibyte characters
17885 $spacew = $spacewidthu;
17886 if ($this->font_stretching != 100) {
17887 // word spacing is affected by stretching
17888 $spacew /= ($this->font_stretching / 100);
17889 }
17890 // escape special characters
17891 $pos = 0;
17892 $pmid = preg_replace('/[\\\][\‍(]/x', '\\#!#OP#!#', $pmid);
17893 $pmid = preg_replace('/[\\\][\‍)]/x', '\\#!#CP#!#', $pmid);
17894 if (preg_match_all('/\[\‍(([^\‍)]*)\‍)\]/x', $pmid, $pamatch) > 0) {
17895 foreach($pamatch[0] as $pk => $pmatch) {
17896 $replace = $pamatch[1][$pk];
17897 $replace = str_replace('#!#OP#!#', '(', $replace);
17898 $replace = str_replace('#!#CP#!#', ')', $replace);
17899 $newpmid = '[('.str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacew).' (', $replace).')]';
17900 $pos = strpos($pmid, $pmatch, $pos);
17901 if ($pos !== FALSE) {
17902 $pmid = substr_replace($pmid, $newpmid, $pos, strlen($pmatch));
17903 }
17904 ++$pos;
17905 }
17906 unset($pamatch);
17907 }
17908 if ($this->inxobj) {
17909 // we are inside an XObject template
17910 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend;
17911 } else {
17912 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
17913 }
17914 $endlinepos = strlen($pstart."\n".$pmid."\n");
17915 } else {
17916 // non-unicode (single-byte characters)
17917 if ($this->font_stretching != 100) {
17918 // word spacing (Tw) is affected by stretching
17919 $spacewidth /= ($this->font_stretching / 100);
17920 }
17921 $rs = sprintf('%F Tw', $spacewidth);
17922 $pmid = preg_replace("/\[\‍(/x", $rs.' [(', $pmid);
17923 if ($this->inxobj) {
17924 // we are inside an XObject template
17925 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
17926 } else {
17927 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
17928 }
17929 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
17930 }
17931 }
17932 } // end of J
17933 } // end if $startlinex
17934 if (($t_x != 0) OR ($yshift < 0)) {
17935 // shift the line
17936 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
17937 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
17938 $endlinepos = strlen($pstart);
17939 if ($this->inxobj) {
17940 // we are inside an XObject template
17941 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
17942 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17943 if ($pak >= $pask) {
17944 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
17945 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
17946 }
17947 }
17948 } else {
17949 $this->setPageBuffer($startlinepage, $pstart.$pend);
17950 // shift the annotations and links
17951 if (isset($this->PageAnnots[$this->page])) {
17952 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17953 if ($pak >= $pask) {
17954 $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
17955 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
17956 }
17957 }
17958 }
17959 }
17960 $this->y -= $yshift;
17961 }
17962 }
17963 $pbrk = $this->checkPageBreak($this->lasth);
17964 $this->newline = false;
17965 $startlinex = $this->x;
17966 $startliney = $this->y;
17967 if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
17968 $startliney -= ((0.3 * $this->FontSizePt) / $this->k);
17969 } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
17970 $startliney -= (($this->FontSizePt / 0.7) / $this->k);
17971 } else {
17972 $minstartliney = $startliney;
17973 $maxbottomliney = ($this->y + $this->getCellHeight($fontsize / $this->k));
17974 }
17975 $startlinepage = $this->page;
17976 if (isset($endlinepos) AND (!$pbrk)) {
17977 $startlinepos = $endlinepos;
17978 } else {
17979 if ($this->inxobj) {
17980 // we are inside an XObject template
17981 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17982 } elseif (!$this->InFooter) {
17983 if (isset($this->footerlen[$this->page])) {
17984 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17985 } else {
17986 $this->footerpos[$this->page] = $this->pagelen[$this->page];
17987 }
17988 $startlinepos = $this->footerpos[$this->page];
17989 } else {
17990 $startlinepos = $this->pagelen[$this->page];
17991 }
17992 }
17993 unset($endlinepos);
17994 $plalign = $lalign;
17995 if (isset($this->PageAnnots[$this->page])) {
17996 $pask = count($this->PageAnnots[$this->page]);
17997 } else {
17998 $pask = 0;
17999 }
18000 if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
18001 AND (isset($this->emptypagemrk[$this->page]))
18002 AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) {
18003 $this->SetFont($fontname, $fontstyle, $fontsize);
18004 if ($wfill) {
18005 $this->SetFillColorArray($this->bgcolor);
18006 }
18007 }
18008 } // end newline
18009 if (isset($opentagpos)) {
18010 unset($opentagpos);
18011 }
18012 if ($dom[$key]['tag']) {
18013 if ($dom[$key]['opening']) {
18014 // get text indentation (if any)
18015 if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
18016 $this->textindent = $dom[$key]['text-indent'];
18017 $this->newline = true;
18018 }
18019 // table
18020 if (($dom[$key]['value'] == 'table') AND isset($dom[$key]['cols']) AND ($dom[$key]['cols'] > 0)) {
18021 // available page width
18022 if ($this->rtl) {
18023 $wtmp = $this->x - $this->lMargin;
18024 } else {
18025 $wtmp = $this->w - $this->rMargin - $this->x;
18026 }
18027 // get cell spacing
18028 if (isset($dom[$key]['attribute']['cellspacing'])) {
18029 $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
18030 $cellspacing = array('H' => $clsp, 'V' => $clsp);
18031 } elseif (isset($dom[$key]['border-spacing'])) {
18032 $cellspacing = $dom[$key]['border-spacing'];
18033 } else {
18034 $cellspacing = array('H' => 0, 'V' => 0);
18035 }
18036 // table width
18037 if (isset($dom[$key]['width'])) {
18038 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
18039 } else {
18040 $table_width = $wtmp;
18041 }
18042 $table_width -= (2 * $cellspacing['H']);
18043 if (!$this->inthead) {
18044 $this->y += $cellspacing['V'];
18045 }
18046 if ($this->rtl) {
18047 $cellspacingx = -$cellspacing['H'];
18048 } else {
18049 $cellspacingx = $cellspacing['H'];
18050 }
18051 // total table width without cellspaces
18052 $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
18053 // minimum column width
18054 $table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
18055 // array of custom column widths
18056 $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
18057 }
18058 // table row
18059 if ($dom[$key]['value'] == 'tr') {
18060 // reset column counter
18061 $colid = 0;
18062 }
18063 // table cell
18064 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
18065 $trid = $dom[$key]['parent'];
18066 $table_el = $dom[$trid]['parent'];
18067 if (!isset($dom[$table_el]['cols'])) {
18068 $dom[$table_el]['cols'] = $dom[$trid]['cols'];
18069 }
18070 // store border info
18071 $tdborder = 0;
18072 if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
18073 $tdborder = $dom[$key]['border'];
18074 }
18075 $colspan = intval($dom[$key]['attribute']['colspan']);
18076 if ($colspan <= 0) {
18077 $colspan = 1;
18078 }
18079 $old_cell_padding = $this->cell_padding;
18080 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
18081 $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
18082 $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
18083 } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
18084 $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
18085 } else {
18086 $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
18087 }
18088 $this->cell_padding = $current_cell_padding;
18089 if (isset($dom[$key]['height'])) {
18090 // minimum cell height
18091 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
18092 } else {
18093 $cellh = 0;
18094 }
18095 if (isset($dom[$key]['content'])) {
18096 $cell_content = $dom[$key]['content'];
18097 } else {
18098 $cell_content = '&nbsp;';
18099 }
18100 $tagtype = $dom[$key]['value'];
18101 $parentid = $key;
18102 while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
18103 // move $key index forward
18104 ++$key;
18105 }
18106 if (!isset($dom[$trid]['startpage'])) {
18107 $dom[$trid]['startpage'] = $this->page;
18108 } else {
18109 $this->setPage($dom[$trid]['startpage']);
18110 }
18111 if (!isset($dom[$trid]['startcolumn'])) {
18112 $dom[$trid]['startcolumn'] = $this->current_column;
18113 } elseif ($this->current_column != $dom[$trid]['startcolumn']) {
18114 $tmpx = $this->x;
18115 $this->selectColumn($dom[$trid]['startcolumn']);
18116 $this->x = $tmpx;
18117 }
18118 if (!isset($dom[$trid]['starty'])) {
18119 $dom[$trid]['starty'] = $this->y;
18120 } else {
18121 $this->y = $dom[$trid]['starty'];
18122 }
18123 if (!isset($dom[$trid]['startx'])) {
18124 $dom[$trid]['startx'] = $this->x;
18125 $this->x += $cellspacingx;
18126 } else {
18127 $this->x += ($cellspacingx / 2);
18128 }
18129 if (isset($dom[$parentid]['attribute']['rowspan'])) {
18130 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
18131 } else {
18132 $rowspan = 1;
18133 }
18134 // skip row-spanned cells started on the previous rows
18135 if (isset($dom[$table_el]['rowspans'])) {
18136 $rsk = 0;
18137 $rskmax = count($dom[$table_el]['rowspans']);
18138 while ($rsk < $rskmax) {
18139 $trwsp = $dom[$table_el]['rowspans'][$rsk];
18140 $rsstartx = $trwsp['startx'];
18141 $rsendx = $trwsp['endx'];
18142 // account for margin changes
18143 if ($trwsp['startpage'] < $this->page) {
18144 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
18145 $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
18146 $rsstartx -= $dl;
18147 $rsendx -= $dl;
18148 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
18149 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
18150 $rsstartx += $dl;
18151 $rsendx += $dl;
18152 }
18153 }
18154 if (($trwsp['rowspan'] > 0)
18155 AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps))
18156 AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps))
18157 AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) {
18158 // set the starting X position of the current cell
18159 $this->x = $rsendx + $cellspacingx;
18160 // increment column indicator
18161 $colid += $trwsp['colspan'];
18162 if (($trwsp['rowspan'] == 1)
18163 AND (isset($dom[$trid]['endy']))
18164 AND (isset($dom[$trid]['endpage']))
18165 AND (isset($dom[$trid]['endcolumn']))
18166 AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
18167 AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18168 // set ending Y position for row
18169 $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18170 $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
18171 }
18172 $rsk = 0;
18173 } else {
18174 ++$rsk;
18175 }
18176 }
18177 }
18178 if (isset($dom[$parentid]['width'])) {
18179 // user specified width
18180 $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
18181 $tmpcw = ($cellw / $colspan);
18182 for ($i = 0; $i < $colspan; ++$i) {
18183 $table_colwidths[($colid + $i)] = $tmpcw;
18184 }
18185 } else {
18186 // inherit column width
18187 $cellw = 0;
18188 for ($i = 0; $i < $colspan; ++$i) {
18189 $cellw += (isset($table_colwidths[($colid + $i)]) ? $table_colwidths[($colid + $i)] : 0);
18190 }
18191 }
18192 $cellw += (($colspan - 1) * $cellspacing['H']);
18193 // increment column indicator
18194 $colid += $colspan;
18195 // add rowspan information to table element
18196 if ($rowspan > 1) {
18197 $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startcolumn' => $this->current_column, 'startx' => $this->x, 'starty' => $this->y));
18198 }
18199 $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
18200 if ($rowspan > 1) {
18201 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
18202 }
18203 // push background colors
18204 if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
18205 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
18206 }
18207 // store border info
18208 if (isset($tdborder) AND !empty($tdborder)) {
18209 $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
18210 }
18211 $prevLastH = $this->lasth;
18212 // store some info for multicolumn mode
18213 if ($this->rtl) {
18214 $this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
18215 } else {
18216 $this->colxshift['x'] = $this->x - $this->lMargin;
18217 }
18218 $this->colxshift['s'] = $cellspacing;
18219 $this->colxshift['p'] = $current_cell_padding;
18220 // ****** write the cell content ******
18221 $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
18222 // restore some values
18223 $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
18224 $this->lasth = $prevLastH;
18225 $this->cell_padding = $old_cell_padding;
18226 $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
18227 // update the end of row position
18228 if ($rowspan <= 1) {
18229 if (isset($dom[$trid]['endy'])) {
18230 if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) {
18231 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
18232 } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) {
18233 $dom[$trid]['endy'] = $this->y;
18234 }
18235 } else {
18236 $dom[$trid]['endy'] = $this->y;
18237 }
18238 if (isset($dom[$trid]['endpage'])) {
18239 $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
18240 } else {
18241 $dom[$trid]['endpage'] = $this->page;
18242 }
18243 if (isset($dom[$trid]['endcolumn'])) {
18244 $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
18245 } else {
18246 $dom[$trid]['endcolumn'] = $this->current_column;
18247 }
18248 } else {
18249 // account for row-spanned cells
18250 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
18251 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
18252 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
18253 $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column;
18254 }
18255 if (isset($dom[$table_el]['rowspans'])) {
18256 // update endy and endpage on rowspanned cells
18257 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
18258 if ($trwsp['rowspan'] > 0) {
18259 if (isset($dom[$trid]['endpage'])) {
18260 if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18261 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18262 } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
18263 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
18264 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
18265 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
18266 } else {
18267 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
18268 }
18269 }
18270 }
18271 }
18272 }
18273 $this->x += ($cellspacingx / 2);
18274 } else {
18275 // opening tag (or self-closing tag)
18276 if (!isset($opentagpos)) {
18277 if ($this->inxobj) {
18278 // we are inside an XObject template
18279 $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
18280 } elseif (!$this->InFooter) {
18281 if (isset($this->footerlen[$this->page])) {
18282 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
18283 } else {
18284 $this->footerpos[$this->page] = $this->pagelen[$this->page];
18285 }
18286 $opentagpos = $this->footerpos[$this->page];
18287 }
18288 }
18289 $dom = $this->openHTMLTagHandler($dom, $key, $cell);
18290 }
18291 } else { // closing tag
18292 $prev_numpages = $this->numpages;
18293 $old_bordermrk = $this->bordermrk[$this->page];
18294 $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
18295 if ($this->bordermrk[$this->page] > $old_bordermrk) {
18296 $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk);
18297 }
18298 if ($prev_numpages > $this->numpages) {
18299 $startlinepage = $this->page;
18300 }
18301 }
18302 } elseif (strlen($dom[$key]['value']) > 0) {
18303 // print list-item
18304 if (!TCPDF_STATIC::empty_string($this->lispacer) AND ($this->lispacer != '^')) {
18305 $this->SetFont($pfontname, $pfontstyle, $pfontsize);
18306 $this->resetLastH();
18307 $minstartliney = $this->y;
18308 $maxbottomliney = ($startliney + $this->getCellHeight($this->FontSize));
18309 if (is_numeric($pfontsize) AND ($pfontsize > 0)) {
18310 $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
18311 }
18312 $this->SetFont($curfontname, $curfontstyle, $curfontsize);
18313 $this->resetLastH();
18314 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
18315 $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
18316 $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
18317 $this->y += ($this->getCellHeight(($pfontsize - $curfontsize) / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
18318 $minstartliney = min($this->y, $minstartliney);
18319 $maxbottomliney = max(($this->y + $this->getCellHeight($pfontsize / $this->k)), $maxbottomliney);
18320 }
18321 }
18322 // text
18323 $this->htmlvspace = 0;
18324 if ((!$this->premode) AND $this->isRTLTextDir()) {
18325 // reverse spaces order
18326 $lsp = ''; // left spaces
18327 $rsp = ''; // right spaces
18328 if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18329 $lsp = $matches[1];
18330 }
18331 if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18332 $rsp = $matches[1];
18333 }
18334 $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
18335 }
18336 if ($newline) {
18337 if (!$this->premode) {
18338 $prelen = strlen($dom[$key]['value']);
18339 if ($this->isRTLTextDir()) {
18340 // right trim except non-breaking space
18341 $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
18342 } else {
18343 // left trim except non-breaking space
18344 $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
18345 }
18346 $postlen = strlen($dom[$key]['value']);
18347 if (($postlen == 0) AND ($prelen > 0)) {
18348 $dom[$key]['trimmed_space'] = true;
18349 }
18350 }
18351 $newline = false;
18352 $firstblock = true;
18353 } else {
18354 $firstblock = false;
18355 // replace empty multiple spaces string with a single space
18356 $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']);
18357 }
18358 $strrest = '';
18359 if ($this->rtl) {
18360 $this->x -= $this->textindent;
18361 } else {
18362 $this->x += $this->textindent;
18363 }
18364 if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
18365 $strlinelen = $this->GetStringWidth($dom[$key]['value']);
18366 if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
18367 // HTML <a> Link
18368 $hrefcolor = '';
18369 if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
18370 $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
18371 }
18372 $hrefstyle = -1;
18373 if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
18374 $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
18375 }
18376 $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
18377 } else {
18378 $wadj = 0; // space to leave for block continuity
18379 if ($this->rtl) {
18380 $cwa = ($this->x - $this->lMargin);
18381 } else {
18382 $cwa = ($this->w - $this->rMargin - $this->x);
18383 }
18384 if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) {
18385 // check the next text blocks for continuity
18386 $nkey = ($key + 1);
18387 $write_block = true;
18388 $same_textdir = true;
18389 $tmp_fontname = $this->FontFamily;
18390 $tmp_fontstyle = $this->FontStyle;
18391 $tmp_fontsize = $this->FontSizePt;
18392 while ($write_block AND isset($dom[$nkey])) {
18393 if ($dom[$nkey]['tag']) {
18394 if ($dom[$nkey]['block']) {
18395 // end of block
18396 $write_block = false;
18397 }
18398 $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
18399 $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
18400 $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
18401 $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
18402 } else {
18403 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'+/', $this->re_space['m'], $dom[$nkey]['value']);
18404 if (isset($nextstr[0]) AND $same_textdir) {
18405 $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
18406 if (isset($nextstr[1])) {
18407 $write_block = false;
18408 }
18409 }
18410 }
18411 ++$nkey;
18412 }
18413 }
18414 if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) {
18415 $wadj = 0;
18416 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $dom[$key]['value']);
18417 $numblks = count($nextstr);
18418 if ($numblks > 1) {
18419 // try to split on blank spaces
18420 $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)]));
18421 } else {
18422 // set the entire block on new line
18423 $wadj = $this->GetStringWidth($nextstr[0]);
18424 }
18425 }
18426 // check for reversed text direction
18427 if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) {
18428 // LTR text on RTL direction or RTL text on LTR direction
18429 $reverse_dir = true;
18430 $this->rtl = !$this->rtl;
18431 $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems
18432 if ($this->rtl) {
18433 $this->x += $revshift;
18434 } else {
18435 $this->x -= $revshift;
18436 }
18437 $xws = $this->x;
18438 }
18439 // ****** write only until the end of the line and get the rest ******
18440 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
18441 // restore default direction
18442 if ($reverse_dir AND ($wadj == 0)) {
18443 $this->x = $xws;
18444 $this->rtl = !$this->rtl;
18445 $reverse_dir = false;
18446 }
18447 }
18448 }
18449 $this->textindent = 0;
18450 if (strlen($strrest) > 0) {
18451 // store the remaining string on the previous $key position
18452 $this->newline = true;
18453 if ($strrest == $dom[$key]['value']) {
18454 // used to avoid infinite loop
18455 ++$loop;
18456 } else {
18457 $loop = 0;
18458 }
18459 $dom[$key]['value'] = $strrest;
18460 if ($cell) {
18461 if ($this->rtl) {
18462 $this->x -= $this->cell_padding['R'];
18463 } else {
18464 $this->x += $this->cell_padding['L'];
18465 }
18466 }
18467 if ($loop < 3) {
18468 --$key;
18469 }
18470 } else {
18471 $loop = 0;
18472 // add the positive font spacing of the last character (if any)
18473 if ($this->font_spacing > 0) {
18474 if ($this->rtl) {
18475 $this->x -= $this->font_spacing;
18476 } else {
18477 $this->x += $this->font_spacing;
18478 }
18479 }
18480 }
18481 }
18482 ++$key;
18483 if (isset($dom[$key]['tag']) AND $dom[$key]['tag'] AND (!isset($dom[$key]['opening']) OR !$dom[$key]['opening']) AND isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
18484 // check if we are on a new page or on a new column
18485 if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) {
18486 // we are on a new page or on a new column and the total object height is less than the available vertical space.
18487 // restore previous object
18488 $this->rollbackTransaction(true);
18489 // restore previous values
18490 foreach ($this_method_vars as $vkey => $vval) {
18491 $$vkey = $vval;
18492 }
18493 if (!empty($dom[$key]['thead'])) {
18494 $this->inthead = true;
18495 }
18496 // add a page (or trig AcceptPageBreak() for multicolumn mode)
18497 $pre_y = $this->y;
18498 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
18499 $startliney = $this->y;
18500 }
18501 $undo = true; // avoid infinite loop
18502 } else {
18503 $undo = false;
18504 }
18505 }
18506 } // end for each $key
18507 // align the last line
18508 if (isset($startlinex)) {
18509 $yshift = ($minstartliney - $startliney);
18510 if (($yshift > 0) OR ($this->page > $startlinepage)) {
18511 $yshift = 0;
18512 }
18513 $t_x = 0;
18514 // the last line must be shifted to be aligned as requested
18515 $linew = abs($this->endlinex - $startlinex);
18516 if ($this->inxobj) {
18517 // we are inside an XObject template
18518 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
18519 if (isset($opentagpos)) {
18520 $midpos = $opentagpos;
18521 } else {
18522 $midpos = 0;
18523 }
18524 if ($midpos > 0) {
18525 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
18526 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
18527 } else {
18528 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
18529 $pend = '';
18530 }
18531 } else {
18532 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
18533 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18534 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18535 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
18536 } elseif (isset($opentagpos)) {
18537 $midpos = $opentagpos;
18538 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18539 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18540 $midpos = $this->footerpos[$startlinepage];
18541 } else {
18542 $midpos = 0;
18543 }
18544 if ($midpos > 0) {
18545 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
18546 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
18547 } else {
18548 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
18549 $pend = '';
18550 }
18551 }
18552 if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
18553 // calculate shifting amount
18554 $tw = $w;
18555 if ($this->lMargin != $prevlMargin) {
18556 $tw += ($prevlMargin - $this->lMargin);
18557 }
18558 if ($this->rMargin != $prevrMargin) {
18559 $tw += ($prevrMargin - $this->rMargin);
18560 }
18561 $one_space_width = $this->GetStringWidth(chr(32));
18562 $no = 0; // number of spaces on a line contained on a single block
18563 if ($this->isRTLTextDir()) { // RTL
18564 // remove left space if exist
18565 $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
18566 if ($pos1 > 0) {
18567 $pos1 = intval($pos1);
18568 if ($this->isUnicodeFont()) {
18569 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
18570 $spacelen = 2;
18571 } else {
18572 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
18573 $spacelen = 1;
18574 }
18575 if ($pos1 == $pos2) {
18576 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
18577 if (substr($pmid, $pos1, 4) == '[()]') {
18578 $linew -= $one_space_width;
18579 } elseif ($pos1 == strpos($pmid, '[(')) {
18580 $no = 1;
18581 }
18582 }
18583 }
18584 } else { // LTR
18585 // remove right space if exist
18586 $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
18587 if ($pos1 > 0) {
18588 $pos1 = intval($pos1);
18589 if ($this->isUnicodeFont()) {
18590 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
18591 $spacelen = 2;
18592 } else {
18593 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
18594 $spacelen = 1;
18595 }
18596 if ($pos1 == $pos2) {
18597 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
18598 $linew -= $one_space_width;
18599 }
18600 }
18601 }
18602 $mdiff = ($tw - $linew);
18603 if ($plalign == 'C') {
18604 if ($this->rtl) {
18605 $t_x = -($mdiff / 2);
18606 } else {
18607 $t_x = ($mdiff / 2);
18608 }
18609 } elseif ($plalign == 'R') {
18610 // right alignment on LTR document
18611 $t_x = $mdiff;
18612 } elseif ($plalign == 'L') {
18613 // left alignment on RTL document
18614 $t_x = -$mdiff;
18615 }
18616 } // end if startlinex
18617 if (($t_x != 0) OR ($yshift < 0)) {
18618 // shift the line
18619 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
18620 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18621 $endlinepos = strlen($pstart);
18622 if ($this->inxobj) {
18623 // we are inside an XObject template
18624 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
18625 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
18626 if ($pak >= $pask) {
18627 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
18628 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
18629 }
18630 }
18631 } else {
18632 $this->setPageBuffer($startlinepage, $pstart.$pend);
18633 // shift the annotations and links
18634 if (isset($this->PageAnnots[$this->page])) {
18635 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
18636 if ($pak >= $pask) {
18637 $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
18638 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
18639 }
18640 }
18641 }
18642 }
18643 $this->y -= $yshift;
18644 $yshift = 0;
18645 }
18646 }
18647 // restore previous values
18648 $this->setGraphicVars($gvars);
18649 if ($this->num_columns > 1) {
18650 $this->selectColumn();
18651 } elseif ($this->page > $prevPage) {
18652 $this->lMargin = $this->pagedim[$this->page]['olm'];
18653 $this->rMargin = $this->pagedim[$this->page]['orm'];
18654 }
18655 // restore previous list state
18656 $this->cell_height_ratio = $prev_cell_height_ratio;
18657 $this->listnum = $prev_listnum;
18658 $this->listordered = $prev_listordered;
18659 $this->listcount = $prev_listcount;
18660 $this->lispacer = $prev_lispacer;
18661 if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
18662 $this->Ln($this->lasth);
18663 if (($this->y < $maxbottomliney) AND ($startlinepage == $this->page)) {
18664 $this->y = $maxbottomliney;
18665 }
18666 }
18667 unset($dom);
18668 }
18669
18678 protected function openHTMLTagHandler($dom, $key, $cell) {
18679 $tag = $dom[$key];
18680 $parent = $dom[($dom[$key]['parent'])];
18681 $firsttag = ($key == 1);
18682 // check for text direction attribute
18683 if (isset($tag['dir'])) {
18684 $this->setTempRTL($tag['dir']);
18685 } else {
18686 $this->tmprtl = false;
18687 }
18688 if ($tag['block']) {
18689 $hbz = 0; // distance from y to line bottom
18690 $hb = 0; // vertical space between block tags
18691 // calculate vertical space for block tags
18692 if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
18693 $cur_h = $this->tagvspaces[$tag['value']][0]['h'];
18694 } elseif (isset($tag['fontsize'])) {
18695 $cur_h = $this->getCellHeight($tag['fontsize'] / $this->k);
18696 } else {
18697 $cur_h = $this->getCellHeight($this->FontSize);
18698 }
18699 if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
18700 $on = $this->tagvspaces[$tag['value']][0]['n'];
18701 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18702 $on = 0.6;
18703 } else {
18704 $on = 1;
18705 }
18706 if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br', 'hr')))) {
18707 $hb = 0;
18708 } else {
18709 $hb = ($on * $cur_h);
18710 }
18711 if (($this->htmlvspace <= 0) AND ($on > 0)) {
18712 if (isset($parent['fontsize'])) {
18713 $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
18714 } else {
18715 $hbz = $this->getCellHeight($this->FontSize);
18716 }
18717 }
18718 if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) {
18719 // fix vertical space after table
18720 $hbz = 0;
18721 }
18722 // closing vertical space
18723 $hbc = 0;
18724 if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
18725 $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
18726 } elseif (isset($parent['fontsize'])) {
18727 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
18728 } else {
18729 $pre_h = $this->getCellHeight($this->FontSize);
18730 }
18731 if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
18732 $cn = $this->tagvspaces[$tag['value']][1]['n'];
18733 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18734 $cn = 0.6;
18735 } else {
18736 $cn = 1;
18737 }
18738 if (isset($this->tagvspaces[$tag['value']][1])) {
18739 $hbc = ($cn * $pre_h);
18740 }
18741 }
18742 // Opening tag
18743 switch($tag['value']) {
18744 case 'table': {
18745 $cp = 0;
18746 $cs = 0;
18747 $dom[$key]['rowspans'] = array();
18748 if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
18749 $this->htmlvspace = 0;
18750 // set table header
18751 if (!TCPDF_STATIC::empty_string($dom[$key]['thead'])) {
18752 // set table header
18753 $this->thead = $dom[$key]['thead'];
18754 if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
18755 $this->theadMargins = array();
18756 $this->theadMargins['cell_padding'] = $this->cell_padding;
18757 $this->theadMargins['lmargin'] = $this->lMargin;
18758 $this->theadMargins['rmargin'] = $this->rMargin;
18759 $this->theadMargins['page'] = $this->page;
18760 $this->theadMargins['cell'] = $cell;
18761 $this->theadMargins['gvars'] = $this->getGraphicVars();
18762 }
18763 }
18764 }
18765 // store current margins and page
18766 $dom[$key]['old_cell_padding'] = $this->cell_padding;
18767 if (isset($tag['attribute']['cellpadding'])) {
18768 $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
18769 $this->SetCellPadding($pad);
18770 } elseif (isset($tag['padding'])) {
18771 $this->cell_padding = $tag['padding'];
18772 }
18773 if (isset($tag['attribute']['cellspacing'])) {
18774 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
18775 } elseif (isset($tag['border-spacing'])) {
18776 $cs = $tag['border-spacing']['V'];
18777 }
18778 $prev_y = $this->y;
18779 if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) {
18780 $this->inthead = true;
18781 // add a page (or trig AcceptPageBreak() for multicolumn mode)
18782 $this->checkPageBreak($this->PageBreakTrigger + 1);
18783 }
18784 break;
18785 }
18786 case 'tr': {
18787 // array of columns positions
18788 $dom[$key]['cellpos'] = array();
18789 break;
18790 }
18791 case 'hr': {
18792 if ((isset($tag['height'])) AND ($tag['height'] != '')) {
18793 $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
18794 } else {
18795 $hrHeight = $this->GetLineWidth();
18796 }
18797 $this->addHTMLVertSpace($hbz, max($hb, ($hrHeight / 2)), $cell, $firsttag);
18798 $x = $this->GetX();
18799 $y = $this->GetY();
18800 $wtmp = $this->w - $this->lMargin - $this->rMargin;
18801 if ($cell) {
18802 $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']);
18803 }
18804 if ((isset($tag['width'])) AND ($tag['width'] != '')) {
18805 $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
18806 } else {
18807 $hrWidth = $wtmp;
18808 }
18809 $prevlinewidth = $this->GetLineWidth();
18810 $this->SetLineWidth($hrHeight);
18811 $this->Line($x, $y, $x + $hrWidth, $y);
18812 $this->SetLineWidth($prevlinewidth);
18813 $this->addHTMLVertSpace(max($hbc, ($hrHeight / 2)), 0, $cell, !isset($dom[($key + 1)]));
18814 break;
18815 }
18816 case 'a': {
18817 if (array_key_exists('href', $tag['attribute'])) {
18818 $this->HREF['url'] = $tag['attribute']['href'];
18819 }
18820 break;
18821 }
18822 case 'img': {
18823 if (!empty($tag['attribute']['src'])) {
18824 if ($tag['attribute']['src'][0] === '@') {
18825 // data stream
18826 $tag['attribute']['src'] = '@'.base64_decode(substr($tag['attribute']['src'], 1));
18827 $type = '';
18828 } else {
18829 // get image type
18830 $type = TCPDF_IMAGES::getImageFileType($tag['attribute']['src']);
18831 }
18832 if (!isset($tag['width'])) {
18833 $tag['width'] = 0;
18834 }
18835 if (!isset($tag['height'])) {
18836 $tag['height'] = 0;
18837 }
18838 //if (!isset($tag['attribute']['align'])) {
18839 // the only alignment supported is "bottom"
18840 // further development is required for other modes.
18841 $tag['attribute']['align'] = 'bottom';
18842 //}
18843 switch($tag['attribute']['align']) {
18844 case 'top': {
18845 $align = 'T';
18846 break;
18847 }
18848 case 'middle': {
18849 $align = 'M';
18850 break;
18851 }
18852 case 'bottom': {
18853 $align = 'B';
18854 break;
18855 }
18856 default: {
18857 $align = 'B';
18858 break;
18859 }
18860 }
18861 $prevy = $this->y;
18862 $xpos = $this->x;
18863 $imglink = '';
18864 if (isset($this->HREF['url']) AND !TCPDF_STATIC::empty_string($this->HREF['url'])) {
18865 $imglink = $this->HREF['url'];
18866 if ($imglink[0] == '#') {
18867 // convert url to internal link
18868 $lnkdata = explode(',', $imglink);
18869 if (isset($lnkdata[0])) {
18870 $page = intval(substr($lnkdata[0], 1));
18871 if (empty($page) OR ($page <= 0)) {
18873 }
18874 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
18875 $lnky = floatval($lnkdata[1]);
18876 } else {
18877 $lnky = 0;
18878 }
18879 $imglink = $this->AddLink();
18880 $this->SetLink($imglink, $lnky, $page);
18881 }
18882 }
18883 }
18884 $border = 0;
18885 if (isset($tag['border']) AND !empty($tag['border'])) {
18886 // currently only support 1 (frame) or a combination of 'LTRB'
18887 $border = $tag['border'];
18888 }
18889 $iw = '';
18890 if (isset($tag['width'])) {
18891 $iw = $this->getHTMLUnitToUnits($tag['width'], ($tag['fontsize'] / $this->k), 'px', false);
18892 }
18893 $ih = '';
18894 if (isset($tag['height'])) {
18895 $ih = $this->getHTMLUnitToUnits($tag['height'], ($tag['fontsize'] / $this->k), 'px', false);
18896 }
18897 if (($type == 'eps') OR ($type == 'ai')) {
18898 $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
18899 } elseif ($type == 'svg') {
18900 $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
18901 } else {
18902 $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
18903 }
18904 switch($align) {
18905 case 'T': {
18906 $this->y = $prevy;
18907 break;
18908 }
18909 case 'M': {
18910 $this->y = (($this->img_rb_y + $prevy - ($this->getCellHeight($tag['fontsize'] / $this->k))) / 2);
18911 break;
18912 }
18913 case 'B': {
18914 $this->y = $this->img_rb_y - ($this->getCellHeight($tag['fontsize'] / $this->k) - ($this->getFontDescent($tag['fontname'], $tag['fontstyle'], $tag['fontsize']) * $this->cell_height_ratio));
18915 break;
18916 }
18917 }
18918 }
18919 break;
18920 }
18921 case 'dl': {
18923 if ($this->listnum == 1) {
18924 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18925 } else {
18926 $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18927 }
18928 break;
18929 }
18930 case 'dt': {
18931 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18932 break;
18933 }
18934 case 'dd': {
18935 if ($this->rtl) {
18936 $this->rMargin += $this->listindent;
18937 } else {
18938 $this->lMargin += $this->listindent;
18939 }
18941 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18942 break;
18943 }
18944 case 'ul':
18945 case 'ol': {
18947 if ($tag['value'] == 'ol') {
18948 $this->listordered[$this->listnum] = true;
18949 } else {
18950 $this->listordered[$this->listnum] = false;
18951 }
18952 if (isset($tag['attribute']['start'])) {
18953 $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
18954 } else {
18955 $this->listcount[$this->listnum] = 0;
18956 }
18957 if ($this->rtl) {
18958 $this->rMargin += $this->listindent;
18959 $this->x -= $this->listindent;
18960 } else {
18961 $this->lMargin += $this->listindent;
18962 $this->x += $this->listindent;
18963 }
18965 if ($this->listnum == 1) {
18966 if ($key > 1) {
18967 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18968 }
18969 } else {
18970 $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
18971 }
18972 break;
18973 }
18974 case 'li': {
18975 if ($key > 2) {
18976 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
18977 }
18978 if ($this->listordered[$this->listnum]) {
18979 // ordered item
18980 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
18981 $this->lispacer = $parent['attribute']['type'];
18982 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
18983 $this->lispacer = $parent['listtype'];
18984 } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
18985 $this->lispacer = $this->lisymbol;
18986 } else {
18987 $this->lispacer = '#';
18988 }
18989 ++$this->listcount[$this->listnum];
18990 if (isset($tag['attribute']['value'])) {
18991 $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
18992 }
18993 } else {
18994 // unordered item
18995 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
18996 $this->lispacer = $parent['attribute']['type'];
18997 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
18998 $this->lispacer = $parent['listtype'];
18999 } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
19000 $this->lispacer = $this->lisymbol;
19001 } else {
19002 $this->lispacer = '!';
19003 }
19004 }
19005 break;
19006 }
19007 case 'blockquote': {
19008 if ($this->rtl) {
19009 $this->rMargin += $this->listindent;
19010 } else {
19011 $this->lMargin += $this->listindent;
19012 }
19014 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19015 break;
19016 }
19017 case 'br': {
19018 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19019 break;
19020 }
19021 case 'div': {
19022 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19023 break;
19024 }
19025 case 'p': {
19026 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19027 break;
19028 }
19029 case 'pre': {
19030 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19031 $this->premode = true;
19032 break;
19033 }
19034 case 'sup': {
19035 $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
19036 break;
19037 }
19038 case 'sub': {
19039 $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
19040 break;
19041 }
19042 case 'h1':
19043 case 'h2':
19044 case 'h3':
19045 case 'h4':
19046 case 'h5':
19047 case 'h6': {
19048 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19049 break;
19050 }
19051 // Form fields (since 4.8.000 - 2009-09-07)
19052 case 'form': {
19053 if (isset($tag['attribute']['action'])) {
19054 $this->form_action = $tag['attribute']['action'];
19055 } else {
19056 $this->Error('Please explicitly set action attribute path!');
19057 }
19058 if (isset($tag['attribute']['enctype'])) {
19059 $this->form_enctype = $tag['attribute']['enctype'];
19060 } else {
19061 $this->form_enctype = 'application/x-www-form-urlencoded';
19062 }
19063 if (isset($tag['attribute']['method'])) {
19064 $this->form_mode = $tag['attribute']['method'];
19065 } else {
19066 $this->form_mode = 'post';
19067 }
19068 break;
19069 }
19070 case 'input': {
19071 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19072 $name = $tag['attribute']['name'];
19073 } else {
19074 break;
19075 }
19076 $prop = array();
19077 $opt = array();
19078 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19079 $prop['readonly'] = true;
19080 }
19081 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19082 $value = $tag['attribute']['value'];
19083 }
19084 if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC::empty_string($tag['attribute']['maxlength'])) {
19085 $opt['maxlen'] = intval($tag['attribute']['maxlength']);
19086 }
19087 $h = $this->getCellHeight($this->FontSize);
19088 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19089 $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
19090 } else {
19091 $w = $h;
19092 }
19093 if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
19094 $checked = true;
19095 } else {
19096 $checked = false;
19097 }
19098 if (isset($tag['align'])) {
19099 switch ($tag['align']) {
19100 case 'C': {
19101 $opt['q'] = 1;
19102 break;
19103 }
19104 case 'R': {
19105 $opt['q'] = 2;
19106 break;
19107 }
19108 case 'L':
19109 default: {
19110 break;
19111 }
19112 }
19113 }
19114 switch ($tag['attribute']['type']) {
19115 case 'text': {
19116 if (isset($value)) {
19117 $opt['v'] = $value;
19118 }
19119 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19120 break;
19121 }
19122 case 'password': {
19123 if (isset($value)) {
19124 $opt['v'] = $value;
19125 }
19126 $prop['password'] = 'true';
19127 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19128 break;
19129 }
19130 case 'checkbox': {
19131 if (!isset($value)) {
19132 break;
19133 }
19134 $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
19135 break;
19136 }
19137 case 'radio': {
19138 if (!isset($value)) {
19139 break;
19140 }
19141 $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
19142 break;
19143 }
19144 case 'submit': {
19145 if (!isset($value)) {
19146 $value = 'submit';
19147 }
19148 $w = $this->GetStringWidth($value) * 1.5;
19149 $h *= 1.6;
19150 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19151 $action = array();
19152 $action['S'] = 'SubmitForm';
19153 $action['F'] = $this->form_action;
19154 if ($this->form_enctype != 'FDF') {
19155 $action['Flags'] = array('ExportFormat');
19156 }
19157 if ($this->form_mode == 'get') {
19158 $action['Flags'] = array('GetMethod');
19159 }
19160 $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
19161 break;
19162 }
19163 case 'reset': {
19164 if (!isset($value)) {
19165 $value = 'reset';
19166 }
19167 $w = $this->GetStringWidth($value) * 1.5;
19168 $h *= 1.6;
19169 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19170 $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
19171 break;
19172 }
19173 case 'file': {
19174 $prop['fileSelect'] = 'true';
19175 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19176 if (!isset($value)) {
19177 $value = '*';
19178 }
19179 $w = $this->GetStringWidth($value) * 2;
19180 $h *= 1.2;
19181 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19182 $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
19183 $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19184 break;
19185 }
19186 case 'hidden': {
19187 if (isset($value)) {
19188 $opt['v'] = $value;
19189 }
19190 $opt['f'] = array('invisible', 'hidden');
19191 $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
19192 break;
19193 }
19194 case 'image': {
19195 // THIS TYPE MUST BE FIXED
19196 if (isset($tag['attribute']['src']) AND !TCPDF_STATIC::empty_string($tag['attribute']['src'])) {
19197 $img = $tag['attribute']['src'];
19198 } else {
19199 break;
19200 }
19201 $value = 'img';
19202 //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
19203 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19204 $jsaction = $tag['attribute']['onclick'];
19205 } else {
19206 $jsaction = '';
19207 }
19208 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19209 break;
19210 }
19211 case 'button': {
19212 if (!isset($value)) {
19213 $value = ' ';
19214 }
19215 $w = $this->GetStringWidth($value) * 1.5;
19216 $h *= 1.6;
19217 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19218 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19219 $jsaction = $tag['attribute']['onclick'];
19220 } else {
19221 $jsaction = '';
19222 }
19223 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19224 break;
19225 }
19226 }
19227 break;
19228 }
19229 case 'textarea': {
19230 $prop = array();
19231 $opt = array();
19232 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19233 $prop['readonly'] = true;
19234 }
19235 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19236 $name = $tag['attribute']['name'];
19237 } else {
19238 break;
19239 }
19240 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19241 $opt['v'] = $tag['attribute']['value'];
19242 }
19243 if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC::empty_string($tag['attribute']['cols'])) {
19244 $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
19245 } else {
19246 $w = 40;
19247 }
19248 if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC::empty_string($tag['attribute']['rows'])) {
19249 $h = intval($tag['attribute']['rows']) * $this->getCellHeight($this->FontSize);
19250 } else {
19251 $h = 10;
19252 }
19253 $prop['multiline'] = 'true';
19254 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19255 break;
19256 }
19257 case 'select': {
19258 $h = $this->getCellHeight($this->FontSize);
19259 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19260 $h *= ($tag['attribute']['size'] + 1);
19261 }
19262 $prop = array();
19263 $opt = array();
19264 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19265 $name = $tag['attribute']['name'];
19266 } else {
19267 break;
19268 }
19269 $w = 0;
19270 if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC::empty_string($tag['attribute']['opt'])) {
19271 $options = explode('#!NwL!#', $tag['attribute']['opt']);
19272 $values = array();
19273 foreach ($options as $val) {
19274 if (strpos($val, '#!TaB!#') !== false) {
19275 $opts = explode('#!TaB!#', $val);
19276 $values[] = $opts;
19277 $w = max($w, $this->GetStringWidth($opts[1]));
19278 } else {
19279 $values[] = $val;
19280 $w = max($w, $this->GetStringWidth($val));
19281 }
19282 }
19283 } else {
19284 break;
19285 }
19286 $w *= 2;
19287 if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
19288 $prop['multipleSelection'] = 'true';
19289 $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19290 } else {
19291 $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19292 }
19293 break;
19294 }
19295 case 'tcpdf': {
19296 if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
19297 // Special tag used to call TCPDF methods
19298 if (isset($tag['attribute']['method'])) {
19299 $tcpdf_method = $tag['attribute']['method'];
19300 if (method_exists($this, $tcpdf_method)) {
19301 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
19302 $params = $this->unserializeTCPDFtagParameters($tag['attribute']['params']);
19303 call_user_func_array(array($this, $tcpdf_method), $params);
19304 } else {
19305 $this->$tcpdf_method();
19306 }
19307 $this->newline = true;
19308 }
19309 }
19310 }
19311 break;
19312 }
19313 default: {
19314 break;
19315 }
19316 }
19317 // define tags that support borders and background colors
19318 $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
19319 if (in_array($tag['value'], $bordertags)) {
19320 // set border
19321 $dom[$key]['borderposition'] = $this->getBorderStartPosition();
19322 }
19323 if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
19324 $pba = $dom[$key]['attribute']['pagebreakafter'];
19325 // check for pagebreak
19326 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19327 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19328 $this->checkPageBreak($this->PageBreakTrigger + 1);
19329 }
19330 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19331 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19332 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19333 $this->checkPageBreak($this->PageBreakTrigger + 1);
19334 }
19335 }
19336 return $dom;
19337 }
19338
19348 protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
19349 $tag = $dom[$key];
19350 $parent = $dom[($dom[$key]['parent'])];
19351 $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
19352 $in_table_head = false;
19353 // maximum x position (used to draw borders)
19354 if ($this->rtl) {
19355 $xmax = $this->w;
19356 } else {
19357 $xmax = 0;
19358 }
19359 if ($tag['block']) {
19360 $hbz = 0; // distance from y to line bottom
19361 $hb = 0; // vertical space between block tags
19362 // calculate vertical space for block tags
19363 if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
19364 $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
19365 } elseif (isset($parent['fontsize'])) {
19366 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
19367 } else {
19368 $pre_h = $this->getCellHeight($this->FontSize);
19369 }
19370 if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
19371 $cn = $this->tagvspaces[$tag['value']][1]['n'];
19372 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
19373 $cn = 0.6;
19374 } else {
19375 $cn = 1;
19376 }
19377 if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) {
19378 $hb = 0;
19379 } else {
19380 $hb = ($cn * $pre_h);
19381 }
19382 if ($maxbottomliney > $this->PageBreakTrigger) {
19383 $hbz = $this->getCellHeight($this->FontSize);
19384 } elseif ($this->y < $maxbottomliney) {
19385 $hbz = ($maxbottomliney - $this->y);
19386 }
19387 }
19388 // Closing tag
19389 switch($tag['value']) {
19390 case 'tr': {
19391 $table_el = $dom[($dom[$key]['parent'])]['parent'];
19392 if (!isset($parent['endy'])) {
19393 $dom[($dom[$key]['parent'])]['endy'] = $this->y;
19394 $parent['endy'] = $this->y;
19395 }
19396 if (!isset($parent['endpage'])) {
19397 $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
19398 $parent['endpage'] = $this->page;
19399 }
19400 if (!isset($parent['endcolumn'])) {
19401 $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column;
19402 $parent['endcolumn'] = $this->current_column;
19403 }
19404 // update row-spanned cells
19405 if (isset($dom[$table_el]['rowspans'])) {
19406 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19407 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
19408 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19409 if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
19410 $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
19411 } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
19412 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19413 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19414 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19415 }
19416 }
19417 }
19418 // report new endy and endpage to the rowspanned cells
19419 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19420 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19421 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
19422 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19423 $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
19424 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19425 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
19426 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19427 }
19428 }
19429 // update remaining rowspanned cells
19430 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19431 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19432 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
19433 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
19434 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
19435 }
19436 }
19437 }
19438 $prev_page = $this->page;
19439 $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
19440 if ($this->num_columns > 1) {
19441 if (($prev_page < $this->page)
19442 AND ((($this->current_column == 0) AND ($dom[($dom[$key]['parent'])]['endcolumn'] == ($this->num_columns - 1)))
19443 OR ($this->current_column == $dom[($dom[$key]['parent'])]['endcolumn']))) {
19444 // page jump
19445 $this->selectColumn(0);
19446 $dom[($dom[$key]['parent'])]['endcolumn'] = 0;
19447 $dom[($dom[$key]['parent'])]['endy'] = $this->y;
19448 } else {
19449 $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
19450 $this->y = $dom[($dom[$key]['parent'])]['endy'];
19451 }
19452 } else {
19453 $this->y = $dom[($dom[$key]['parent'])]['endy'];
19454 }
19455 if (isset($dom[$table_el]['attribute']['cellspacing'])) {
19456 $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
19457 } elseif (isset($dom[$table_el]['border-spacing'])) {
19458 $this->y += $dom[$table_el]['border-spacing']['V'];
19459 }
19460 $this->Ln(0, $cell);
19461 if ($this->current_column == $parent['startcolumn']) {
19462 $this->x = $parent['startx'];
19463 }
19464 // account for booklet mode
19465 if ($this->page > $parent['startpage']) {
19466 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
19467 $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
19468 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
19469 $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
19470 }
19471 }
19472 break;
19473 }
19474 case 'tablehead':
19475 // closing tag used for the thead part
19476 $in_table_head = true;
19477 $this->inthead = false;
19478 case 'table': {
19479 $table_el = $parent;
19480 // set default border
19481 if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
19482 // set default border
19483 $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
19484 } else {
19485 $border = 0;
19486 }
19487 $default_border = $border;
19488 // fix bottom line alignment of last line before page break
19489 foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
19490 // update row-spanned cells
19491 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19492 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19493 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) {
19494 $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
19495 }
19496 if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) {
19497 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
19498 }
19499 }
19500 }
19501 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
19502 $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
19503 $dom[$prevtrkey]['endy'] = $pgendy;
19504 // update row-spanned cells
19505 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19506 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19507 if (($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
19508 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
19509 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
19510 }
19511 }
19512 }
19513 }
19514 $prevtrkey = $trkey;
19515 $table_el = $dom[($dom[$key]['parent'])];
19516 }
19517 // for each row
19518 if (count($table_el['trids']) > 0) {
19519 unset($xmax);
19520 }
19521 foreach ($table_el['trids'] as $j => $trkey) {
19522 $parent = $dom[$trkey];
19523 if (!isset($xmax)) {
19524 $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
19525 }
19526 // for each cell on the row
19527 foreach ($parent['cellpos'] as $k => $cellpos) {
19528 if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
19529 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
19530 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
19531 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
19532 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
19533 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
19534 $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
19535 $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
19536 } else {
19537 $endy = $parent['endy'];
19538 $startpage = $parent['startpage'];
19539 $endpage = $parent['endpage'];
19540 $startcolumn = $parent['startcolumn'];
19541 $endcolumn = $parent['endcolumn'];
19542 }
19543 if ($this->num_columns == 0) {
19544 $this->num_columns = 1;
19545 }
19546 if (isset($cellpos['border'])) {
19547 $border = $cellpos['border'];
19548 }
19549 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
19550 $this->SetFillColorArray($cellpos['bgcolor']);
19551 $fill = true;
19552 } else {
19553 $fill = false;
19554 }
19555 $x = $cellpos['startx'];
19556 $y = $parent['starty'];
19557 $starty = $y;
19558 $w = abs($cellpos['endx'] - $cellpos['startx']);
19559 // get border modes
19560 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
19561 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
19562 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19563 // design borders around HTML cells.
19564 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
19565 $ccode = '';
19566 $this->setPage($page);
19567 if ($this->num_columns < 2) {
19568 // single-column mode
19569 $this->x = $x;
19570 $this->y = $this->tMargin;
19571 }
19572 // account for margin changes
19573 if ($page > $startpage) {
19574 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
19575 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
19576 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
19577 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
19578 }
19579 }
19580 if ($startpage == $endpage) { // single page
19581 $deltacol = 0;
19582 $deltath = 0;
19583 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
19584 $this->selectColumn($column);
19585 if ($startcolumn == $endcolumn) { // single column
19586 $cborder = $border;
19587 $h = $endy - $parent['starty'];
19588 $this->y = $y;
19589 $this->x = $x;
19590 } elseif ($column == $startcolumn) { // first column
19591 $cborder = $border_start;
19592 $this->y = $starty;
19593 $this->x = $x;
19594 $h = $this->h - $this->y - $this->bMargin;
19595 if ($this->rtl) {
19596 $deltacol = $this->x + $this->rMargin - $this->w;
19597 } else {
19598 $deltacol = $this->x - $this->lMargin;
19599 }
19600 } elseif ($column == $endcolumn) { // end column
19601 $cborder = $border_end;
19602 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19603 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19604 }
19605 $this->x += $deltacol;
19606 $h = $endy - $this->y;
19607 } else { // middle column
19608 $cborder = $border_middle;
19609 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19610 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19611 }
19612 $this->x += $deltacol;
19613 $h = $this->h - $this->y - $this->bMargin;
19614 }
19615 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19616 } // end for each column
19617 } elseif ($page == $startpage) { // first page
19618 $deltacol = 0;
19619 $deltath = 0;
19620 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
19621 $this->selectColumn($column);
19622 if ($column == $startcolumn) { // first column
19623 $cborder = $border_start;
19624 $this->y = $starty;
19625 $this->x = $x;
19626 $h = $this->h - $this->y - $this->bMargin;
19627 if ($this->rtl) {
19628 $deltacol = $this->x + $this->rMargin - $this->w;
19629 } else {
19630 $deltacol = $this->x - $this->lMargin;
19631 }
19632 } else { // middle column
19633 $cborder = $border_middle;
19634 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19635 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19636 }
19637 $this->x += $deltacol;
19638 $h = $this->h - $this->y - $this->bMargin;
19639 }
19640 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19641 } // end for each column
19642 } elseif ($page == $endpage) { // last page
19643 $deltacol = 0;
19644 $deltath = 0;
19645 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
19646 $this->selectColumn($column);
19647 if ($column == $endcolumn) { // end column
19648 $cborder = $border_end;
19649 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19650 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19651 }
19652 $this->x += $deltacol;
19653 $h = $endy - $this->y;
19654 } else { // middle column
19655 $cborder = $border_middle;
19656 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19657 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19658 }
19659 $this->x += $deltacol;
19660 $h = $this->h - $this->y - $this->bMargin;
19661 }
19662 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19663 } // end for each column
19664 } else { // middle page
19665 $deltacol = 0;
19666 $deltath = 0;
19667 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
19668 $this->selectColumn($column);
19669 $cborder = $border_middle;
19670 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19671 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19672 }
19673 $this->x += $deltacol;
19674 $h = $this->h - $this->y - $this->bMargin;
19675 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19676 } // end for each column
19677 }
19678 if (!empty($cborder) OR !empty($fill)) {
19679 $offsetlen = strlen($ccode);
19680 // draw border and fill
19681 if ($this->inxobj) {
19682 // we are inside an XObject template
19683 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
19684 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
19685 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
19686 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
19687 } else {
19688 $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
19689 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
19690 }
19691 $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
19692 $pstart = substr($pagebuff, 0, $pagemark);
19693 $pend = substr($pagebuff, $pagemark);
19694 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
19695 } else {
19696 // draw border and fill
19697 if (end($this->transfmrk[$this->page]) !== false) {
19698 $pagemarkkey = key($this->transfmrk[$this->page]);
19699 $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
19700 } elseif ($this->InFooter) {
19701 $pagemark = $this->footerpos[$this->page];
19702 } else {
19703 $pagemark = $this->intmrk[$this->page];
19704 }
19705 $pagebuff = $this->getPageBuffer($this->page);
19706 $pstart = substr($pagebuff, 0, $pagemark);
19707 $pend = substr($pagebuff, $pagemark);
19708 $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
19709 }
19710 }
19711 } // end for each page
19712 // restore default border
19713 $border = $default_border;
19714 } // end for each cell on the row
19715 if (isset($table_el['attribute']['cellspacing'])) {
19716 $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
19717 } elseif (isset($table_el['border-spacing'])) {
19718 $this->y += $table_el['border-spacing']['V'];
19719 }
19720 $this->Ln(0, $cell);
19721 $this->x = $parent['startx'];
19722 if ($endpage > $startpage) {
19723 if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
19724 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
19725 } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
19726 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
19727 }
19728 }
19729 }
19730 if (!$in_table_head) { // we are not inside a thead section
19731 $this->cell_padding = $table_el['old_cell_padding'];
19732 // reset row height
19733 $this->resetLastH();
19734 if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) {
19735 $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]);
19736 if (($plendiff > 0) AND ($plendiff < 60)) {
19737 $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff);
19738 if (substr($pagediff, 0, 5) == 'BT /F') {
19739 // the difference is only a font setting
19740 $plendiff = 0;
19741 }
19742 }
19743 if ($plendiff == 0) {
19744 // remove last blank page
19745 $this->deletePage($this->numpages);
19746 }
19747 }
19748 if (isset($this->theadMargins['top'])) {
19749 // restore top margin
19750 $this->tMargin = $this->theadMargins['top'];
19751 }
19752 if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
19753 // reset main table header
19754 $this->thead = '';
19755 $this->theadMargins = array();
19756 $this->pagedim[$this->page]['tm'] = $this->tMargin;
19757 }
19758 }
19759 $parent = $table_el;
19760 break;
19761 }
19762 case 'a': {
19763 $this->HREF = '';
19764 break;
19765 }
19766 case 'sup': {
19767 $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
19768 break;
19769 }
19770 case 'sub': {
19771 $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k));
19772 break;
19773 }
19774 case 'div': {
19775 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19776 break;
19777 }
19778 case 'blockquote': {
19779 if ($this->rtl) {
19780 $this->rMargin -= $this->listindent;
19781 } else {
19782 $this->lMargin -= $this->listindent;
19783 }
19785 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19786 break;
19787 }
19788 case 'p': {
19789 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19790 break;
19791 }
19792 case 'pre': {
19793 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19794 $this->premode = false;
19795 break;
19796 }
19797 case 'dl': {
19799 if ($this->listnum <= 0) {
19800 $this->listnum = 0;
19801 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19802 } else {
19803 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19804 }
19805 $this->resetLastH();
19806 break;
19807 }
19808 case 'dt': {
19809 $this->lispacer = '';
19810 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19811 break;
19812 }
19813 case 'dd': {
19814 $this->lispacer = '';
19815 if ($this->rtl) {
19816 $this->rMargin -= $this->listindent;
19817 } else {
19818 $this->lMargin -= $this->listindent;
19819 }
19821 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19822 break;
19823 }
19824 case 'ul':
19825 case 'ol': {
19827 $this->lispacer = '';
19828 if ($this->rtl) {
19829 $this->rMargin -= $this->listindent;
19830 } else {
19831 $this->lMargin -= $this->listindent;
19832 }
19834 if ($this->listnum <= 0) {
19835 $this->listnum = 0;
19836 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19837 } else {
19838 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19839 }
19840 $this->resetLastH();
19841 break;
19842 }
19843 case 'li': {
19844 $this->lispacer = '';
19845 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19846 break;
19847 }
19848 case 'h1':
19849 case 'h2':
19850 case 'h3':
19851 case 'h4':
19852 case 'h5':
19853 case 'h6': {
19854 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19855 break;
19856 }
19857 // Form fields (since 4.8.000 - 2009-09-07)
19858 case 'form': {
19859 $this->form_action = '';
19860 $this->form_enctype = 'application/x-www-form-urlencoded';
19861 break;
19862 }
19863 default : {
19864 break;
19865 }
19866 }
19867 // draw border and background (if any)
19868 $this->drawHTMLTagBorder($parent, $xmax);
19869 if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
19870 $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
19871 // check for pagebreak
19872 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19873 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19874 $this->checkPageBreak($this->PageBreakTrigger + 1);
19875 }
19876 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19877 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19878 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19879 $this->checkPageBreak($this->PageBreakTrigger + 1);
19880 }
19881 }
19882 $this->tmprtl = false;
19883 return $dom;
19884 }
19885
19895 protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
19896 if ($firsttag) {
19897 $this->Ln(0, $cell);
19898 $this->htmlvspace = 0;
19899 return;
19900 }
19901 if ($lasttag) {
19902 $this->Ln($hbz, $cell);
19903 $this->htmlvspace = 0;
19904 return;
19905 }
19906 if ($hb < $this->htmlvspace) {
19907 $hd = 0;
19908 } else {
19909 $hd = $hb - $this->htmlvspace;
19910 $this->htmlvspace = $hb;
19911 }
19912 $this->Ln(($hbz + $hd), $cell);
19913 }
19914
19921 protected function getBorderStartPosition() {
19922 if ($this->rtl) {
19923 $xmax = $this->lMargin;
19924 } else {
19925 $xmax = $this->w - $this->rMargin;
19926 }
19927 return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax);
19928 }
19929
19937 protected function drawHTMLTagBorder($tag, $xmax) {
19938 if (!isset($tag['borderposition'])) {
19939 // nothing to draw
19940 return;
19941 }
19942 $prev_x = $this->x;
19943 $prev_y = $this->y;
19944 $prev_lasth = $this->lasth;
19945 $border = 0;
19946 $fill = false;
19947 $this->lasth = 0;
19948 if (isset($tag['border']) AND !empty($tag['border'])) {
19949 // get border style
19950 $border = $tag['border'];
19951 if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
19952 // border for table header
19953 $border = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19954 }
19955 }
19956 if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
19957 // get background color
19958 $old_bgcolor = $this->bgcolor;
19959 $this->SetFillColorArray($tag['bgcolor']);
19960 $fill = true;
19961 }
19962 if (!$border AND !$fill) {
19963 // nothing to draw
19964 return;
19965 }
19966 if (isset($tag['attribute']['cellspacing'])) {
19967 $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
19968 $cellspacing = array('H' => $clsp, 'V' => $clsp);
19969 } elseif (isset($tag['border-spacing'])) {
19970 $cellspacing = $tag['border-spacing'];
19971 } else {
19972 $cellspacing = array('H' => 0, 'V' => 0);
19973 }
19974 if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
19975 // draw the border externally respect the sqare edge.
19976 $border['mode'] = 'ext';
19977 }
19978 if ($this->rtl) {
19979 if ($xmax >= $tag['borderposition']['x']) {
19980 $xmax = $tag['borderposition']['xmax'];
19981 }
19982 $w = ($tag['borderposition']['x'] - $xmax);
19983 } else {
19984 if ($xmax <= $tag['borderposition']['x']) {
19985 $xmax = $tag['borderposition']['xmax'];
19986 }
19987 $w = ($xmax - $tag['borderposition']['x']);
19988 }
19989 if ($w <= 0) {
19990 return;
19991 }
19992 $w += $cellspacing['H'];
19993 $startpage = $tag['borderposition']['page'];
19994 $startcolumn = $tag['borderposition']['column'];
19995 $x = $tag['borderposition']['x'];
19996 $y = $tag['borderposition']['y'];
19997 $endpage = $this->page;
19998 $starty = $tag['borderposition']['y'] - $cellspacing['V'];
19999 $currentY = $this->y;
20000 $this->x = $x;
20001 // get latest column
20002 $endcolumn = $this->current_column;
20003 if ($this->num_columns == 0) {
20004 $this->num_columns = 1;
20005 }
20006 // get border modes
20007 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
20008 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
20009 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
20010 // temporary disable page regions
20011 $temp_page_regions = $this->page_regions;
20012 $this->page_regions = array();
20013 // design borders around HTML cells.
20014 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
20015 $ccode = '';
20016 $this->setPage($page);
20017 if ($this->num_columns < 2) {
20018 // single-column mode
20019 $this->x = $x;
20020 $this->y = $this->tMargin;
20021 }
20022 // account for margin changes
20023 if ($page > $startpage) {
20024 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
20025 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
20026 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
20027 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
20028 }
20029 }
20030 if ($startpage == $endpage) {
20031 // single page
20032 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
20033 $this->selectColumn($column);
20034 if ($startcolumn == $endcolumn) { // single column
20035 $cborder = $border;
20036 $h = ($currentY - $y) + $cellspacing['V'];
20037 $this->y = $starty;
20038 } elseif ($column == $startcolumn) { // first column
20039 $cborder = $border_start;
20040 $this->y = $starty;
20041 $h = $this->h - $this->y - $this->bMargin;
20042 } elseif ($column == $endcolumn) { // end column
20043 $cborder = $border_end;
20044 $h = $currentY - $this->y;
20045 } else { // middle column
20046 $cborder = $border_middle;
20047 $h = $this->h - $this->y - $this->bMargin;
20048 }
20049 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20050 } // end for each column
20051 } elseif ($page == $startpage) { // first page
20052 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
20053 $this->selectColumn($column);
20054 if ($column == $startcolumn) { // first column
20055 $cborder = $border_start;
20056 $this->y = $starty;
20057 $h = $this->h - $this->y - $this->bMargin;
20058 } else { // middle column
20059 $cborder = $border_middle;
20060 $h = $this->h - $this->y - $this->bMargin;
20061 }
20062 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20063 } // end for each column
20064 } elseif ($page == $endpage) { // last page
20065 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
20066 $this->selectColumn($column);
20067 if ($column == $endcolumn) {
20068 // end column
20069 $cborder = $border_end;
20070 $h = $currentY - $this->y;
20071 } else {
20072 // middle column
20073 $cborder = $border_middle;
20074 $h = $this->h - $this->y - $this->bMargin;
20075 }
20076 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20077 } // end for each column
20078 } else { // middle page
20079 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
20080 $this->selectColumn($column);
20081 $cborder = $border_middle;
20082 $h = $this->h - $this->y - $this->bMargin;
20083 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20084 } // end for each column
20085 }
20086 if ($cborder OR $fill) {
20087 $offsetlen = strlen($ccode);
20088 // draw border and fill
20089 if ($this->inxobj) {
20090 // we are inside an XObject template
20091 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
20092 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
20093 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
20094 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
20095 } else {
20096 $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
20097 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
20098 }
20099 $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
20100 $pstart = substr($pagebuff, 0, $pagemark);
20101 $pend = substr($pagebuff, $pagemark);
20102 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
20103 } else {
20104 if (end($this->transfmrk[$this->page]) !== false) {
20105 $pagemarkkey = key($this->transfmrk[$this->page]);
20106 $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
20107 } elseif ($this->InFooter) {
20108 $pagemark = $this->footerpos[$this->page];
20109 } else {
20110 $pagemark = $this->intmrk[$this->page];
20111 }
20112 $pagebuff = $this->getPageBuffer($this->page);
20113 $pstart = substr($pagebuff, 0, $pagemark);
20114 $pend = substr($pagebuff, $pagemark);
20115 $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
20116 $this->bordermrk[$this->page] += $offsetlen;
20117 $this->cntmrk[$this->page] += $offsetlen;
20118 }
20119 }
20120 } // end for each page
20121 // restore page regions
20122 $this->page_regions = $temp_page_regions;
20123 if (isset($old_bgcolor)) {
20124 // restore background color
20125 $this->SetFillColorArray($old_bgcolor);
20126 }
20127 // restore pointer position
20128 $this->x = $prev_x;
20129 $this->y = $prev_y;
20130 $this->lasth = $prev_lasth;
20131 }
20132
20139 public function setLIsymbol($symbol='!') {
20140 // check for custom image symbol
20141 if (substr($symbol, 0, 4) == 'img|') {
20142 $this->lisymbol = $symbol;
20143 return;
20144 }
20145 $symbol = strtolower($symbol);
20146 $valid_symbols = array('!', '#', 'disc', 'circle', 'square', '1', 'decimal', 'decimal-leading-zero', 'i', 'lower-roman', 'I', 'upper-roman', 'a', 'lower-alpha', 'lower-latin', 'A', 'upper-alpha', 'upper-latin', 'lower-greek');
20147 if (in_array($symbol, $valid_symbols)) {
20148 $this->lisymbol = $symbol;
20149 } else {
20150 $this->lisymbol = '';
20151 }
20152 }
20153
20162 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
20163 $this->booklet = $booklet;
20164 if ($inner >= 0) {
20165 $this->lMargin = $inner;
20166 }
20167 if ($outer >= 0) {
20168 $this->rMargin = $outer;
20169 }
20170 }
20171
20178 protected function swapMargins($reverse=true) {
20179 if ($reverse) {
20180 // swap left and right margins
20181 $mtemp = $this->original_lMargin;
20182 $this->original_lMargin = $this->original_rMargin;
20183 $this->original_rMargin = $mtemp;
20184 $deltam = $this->original_lMargin - $this->original_rMargin;
20185 $this->lMargin += $deltam;
20186 $this->rMargin -= $deltam;
20187 }
20188 }
20189
20202 public function setHtmlVSpace($tagvs) {
20203 $this->tagvspaces = $tagvs;
20204 }
20205
20212 public function setListIndentWidth($width) {
20213 return $this->customlistindent = floatval($width);
20214 }
20215
20222 public function setOpenCell($isopen) {
20223 $this->opencell = $isopen;
20224 }
20225
20233 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
20234 $this->htmlLinkColorArray = $color;
20235 $this->htmlLinkFontStyle = $fontstyle;
20236 }
20237
20248 public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
20249 $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
20250 $retval = 0;
20251 $value = 0;
20252 $unit = 'px';
20253 if ($points) {
20254 $k = 1;
20255 } else {
20256 $k = $this->k;
20257 }
20258 if (in_array($defaultunit, $supportedunits)) {
20259 $unit = $defaultunit;
20260 }
20261 if (is_numeric($htmlval)) {
20262 $value = floatval($htmlval);
20263 } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
20264 $value = floatval($mnum[1]);
20265 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
20266 if (in_array($munit[1], $supportedunits)) {
20267 $unit = $munit[1];
20268 }
20269 }
20270 }
20271 switch ($unit) {
20272 // percentage
20273 case '%': {
20274 $retval = (($value * $refsize) / 100);
20275 break;
20276 }
20277 // relative-size
20278 case 'em': {
20279 $retval = ($value * $refsize);
20280 break;
20281 }
20282 // height of lower case 'x' (about half the font-size)
20283 case 'ex': {
20284 $retval = ($value * ($refsize / 2));
20285 break;
20286 }
20287 // absolute-size
20288 case 'in': {
20289 $retval = (($value * $this->dpi) / $k);
20290 break;
20291 }
20292 // centimeters
20293 case 'cm': {
20294 $retval = (($value / 2.54 * $this->dpi) / $k);
20295 break;
20296 }
20297 // millimeters
20298 case 'mm': {
20299 $retval = (($value / 25.4 * $this->dpi) / $k);
20300 break;
20301 }
20302 // one pica is 12 points
20303 case 'pc': {
20304 $retval = (($value * 12) / $k);
20305 break;
20306 }
20307 // points
20308 case 'pt': {
20309 $retval = ($value / $k);
20310 break;
20311 }
20312 // pixels
20313 case 'px': {
20314 $retval = $this->pixelsToUnits($value);
20315 if ($points) {
20316 $retval *= $this->k;
20317 }
20318 break;
20319 }
20320 }
20321 return $retval;
20322 }
20323
20332 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
20333 if ($this->state != 2) {
20334 return;
20335 }
20336 $size /= $this->k;
20337 $fill = '';
20338 $bgcolor = $this->bgcolor;
20339 $color = $this->fgcolor;
20340 $strokecolor = $this->strokecolor;
20341 $width = 0;
20342 $textitem = '';
20343 $tmpx = $this->x;
20344 $lspace = $this->GetStringWidth(' ');
20345 if ($listtype == '^') {
20346 // special symbol used for avoid justification of rect bullet
20347 $this->lispacer = '';
20348 return;
20349 } elseif ($listtype == '!') {
20350 // set default list type for unordered list
20351 $deftypes = array('disc', 'circle', 'square');
20352 $listtype = $deftypes[($listdepth - 1) % 3];
20353 } elseif ($listtype == '#') {
20354 // set default list type for ordered list
20355 $listtype = 'decimal';
20356 } elseif (substr($listtype, 0, 4) == 'img|') {
20357 // custom image type ('img|type|width|height|image.ext')
20358 $img = explode('|', $listtype);
20359 $listtype = 'img';
20360 }
20361 switch ($listtype) {
20362 // unordered types
20363 case 'none': {
20364 break;
20365 }
20366 case 'disc': {
20367 $r = $size / 6;
20368 $lspace += (2 * $r);
20369 if ($this->rtl) {
20370 $this->x += $lspace;
20371 } else {
20372 $this->x -= $lspace;
20373 }
20374 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8);
20375 break;
20376 }
20377 case 'circle': {
20378 $r = $size / 6;
20379 $lspace += (2 * $r);
20380 if ($this->rtl) {
20381 $this->x += $lspace;
20382 } else {
20383 $this->x -= $lspace;
20384 }
20385 $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor;
20386 $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
20387 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
20388 $this->_out($prev_line_style); // restore line settings
20389 break;
20390 }
20391 case 'square': {
20392 $l = $size / 3;
20393 $lspace += $l;
20394 if ($this->rtl) {;
20395 $this->x += $lspace;
20396 } else {
20397 $this->x -= $lspace;
20398 }
20399 $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color);
20400 break;
20401 }
20402 case 'img': {
20403 // 1=>type, 2=>width, 3=>height, 4=>image.ext
20404 $lspace += $img[2];
20405 if ($this->rtl) {;
20406 $this->x += $lspace;
20407 } else {
20408 $this->x -= $lspace;
20409 }
20410 $imgtype = strtolower($img[1]);
20411 $prev_y = $this->y;
20412 switch ($imgtype) {
20413 case 'svg': {
20414 $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
20415 break;
20416 }
20417 case 'ai':
20418 case 'eps': {
20419 $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
20420 break;
20421 }
20422 default: {
20423 $this->Image($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], $img[1], '', 'T', false, 300, '', false, false, 0, false, false, false);
20424 break;
20425 }
20426 }
20427 $this->y = $prev_y;
20428 break;
20429 }
20430 // ordered types
20431 // $this->listcount[$this->listnum];
20432 // $textitem
20433 case '1':
20434 case 'decimal': {
20435 $textitem = $this->listcount[$this->listnum];
20436 break;
20437 }
20438 case 'decimal-leading-zero': {
20439 $textitem = sprintf('%02d', $this->listcount[$this->listnum]);
20440 break;
20441 }
20442 case 'i':
20443 case 'lower-roman': {
20444 $textitem = strtolower(TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]));
20445 break;
20446 }
20447 case 'I':
20448 case 'upper-roman': {
20449 $textitem = TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]);
20450 break;
20451 }
20452 case 'a':
20453 case 'lower-alpha':
20454 case 'lower-latin': {
20455 $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
20456 break;
20457 }
20458 case 'A':
20459 case 'upper-alpha':
20460 case 'upper-latin': {
20461 $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
20462 break;
20463 }
20464 case 'lower-greek': {
20465 $textitem = TCPDF_FONTS::unichr((945 + $this->listcount[$this->listnum] - 1), $this->isunicode);
20466 break;
20467 }
20468 /*
20469 // Types to be implemented (special handling)
20470 case 'hebrew': {
20471 break;
20472 }
20473 case 'armenian': {
20474 break;
20475 }
20476 case 'georgian': {
20477 break;
20478 }
20479 case 'cjk-ideographic': {
20480 break;
20481 }
20482 case 'hiragana': {
20483 break;
20484 }
20485 case 'katakana': {
20486 break;
20487 }
20488 case 'hiragana-iroha': {
20489 break;
20490 }
20491 case 'katakana-iroha': {
20492 break;
20493 }
20494 */
20495 default: {
20496 $textitem = $this->listcount[$this->listnum];
20497 }
20498 }
20499 if (!TCPDF_STATIC::empty_string($textitem)) {
20500 // Check whether we need a new page or new column
20501 $prev_y = $this->y;
20502 $h = $this->getCellHeight($this->FontSize);
20503 if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) {
20504 $tmpx = $this->x;
20505 }
20506 // print ordered item
20507 if ($this->rtl) {
20508 $textitem = '.'.$textitem;
20509 } else {
20510 $textitem = $textitem.'.';
20511 }
20512 $lspace += $this->GetStringWidth($textitem);
20513 if ($this->rtl) {
20514 $this->x += $lspace;
20515 } else {
20516 $this->x -= $lspace;
20517 }
20518 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
20519 }
20520 $this->x = $tmpx;
20521 $this->lispacer = '^';
20522 // restore colors
20523 $this->SetFillColorArray($bgcolor);
20524 $this->SetDrawColorArray($strokecolor);
20525 $this->SettextColorArray($color);
20526 }
20527
20534 protected function getGraphicVars() {
20535 $grapvars = array(
20536 'FontFamily' => $this->FontFamily,
20537 'FontStyle' => $this->FontStyle,
20538 'FontSizePt' => $this->FontSizePt,
20539 'rMargin' => $this->rMargin,
20540 'lMargin' => $this->lMargin,
20541 'cell_padding' => $this->cell_padding,
20542 'cell_margin' => $this->cell_margin,
20543 'LineWidth' => $this->LineWidth,
20544 'linestyleWidth' => $this->linestyleWidth,
20545 'linestyleCap' => $this->linestyleCap,
20546 'linestyleJoin' => $this->linestyleJoin,
20547 'linestyleDash' => $this->linestyleDash,
20548 'textrendermode' => $this->textrendermode,
20549 'textstrokewidth' => $this->textstrokewidth,
20550 'DrawColor' => $this->DrawColor,
20551 'FillColor' => $this->FillColor,
20552 'TextColor' => $this->TextColor,
20553 'ColorFlag' => $this->ColorFlag,
20554 'bgcolor' => $this->bgcolor,
20555 'fgcolor' => $this->fgcolor,
20556 'htmlvspace' => $this->htmlvspace,
20557 'listindent' => $this->listindent,
20558 'listindentlevel' => $this->listindentlevel,
20559 'listnum' => $this->listnum,
20560 'listordered' => $this->listordered,
20561 'listcount' => $this->listcount,
20562 'lispacer' => $this->lispacer,
20563 'cell_height_ratio' => $this->cell_height_ratio,
20564 'font_stretching' => $this->font_stretching,
20565 'font_spacing' => $this->font_spacing,
20566 'alpha' => $this->alpha,
20567 // extended
20568 'lasth' => $this->lasth,
20569 'tMargin' => $this->tMargin,
20570 'bMargin' => $this->bMargin,
20571 'AutoPageBreak' => $this->AutoPageBreak,
20572 'PageBreakTrigger' => $this->PageBreakTrigger,
20573 'x' => $this->x,
20574 'y' => $this->y,
20575 'w' => $this->w,
20576 'h' => $this->h,
20577 'wPt' => $this->wPt,
20578 'hPt' => $this->hPt,
20579 'fwPt' => $this->fwPt,
20580 'fhPt' => $this->fhPt,
20581 'page' => $this->page,
20582 'current_column' => $this->current_column,
20583 'num_columns' => $this->num_columns
20584 );
20585 return $grapvars;
20586 }
20587
20595 protected function setGraphicVars($gvars, $extended=false) {
20596 if ($this->state != 2) {
20597 return;
20598 }
20599 $this->FontFamily = $gvars['FontFamily'];
20600 $this->FontStyle = $gvars['FontStyle'];
20601 $this->FontSizePt = $gvars['FontSizePt'];
20602 $this->rMargin = $gvars['rMargin'];
20603 $this->lMargin = $gvars['lMargin'];
20604 $this->cell_padding = $gvars['cell_padding'];
20605 $this->cell_margin = $gvars['cell_margin'];
20606 $this->LineWidth = $gvars['LineWidth'];
20607 $this->linestyleWidth = $gvars['linestyleWidth'];
20608 $this->linestyleCap = $gvars['linestyleCap'];
20609 $this->linestyleJoin = $gvars['linestyleJoin'];
20610 $this->linestyleDash = $gvars['linestyleDash'];
20611 $this->textrendermode = $gvars['textrendermode'];
20612 $this->textstrokewidth = $gvars['textstrokewidth'];
20613 $this->DrawColor = $gvars['DrawColor'];
20614 $this->FillColor = $gvars['FillColor'];
20615 $this->TextColor = $gvars['TextColor'];
20616 $this->ColorFlag = $gvars['ColorFlag'];
20617 $this->bgcolor = $gvars['bgcolor'];
20618 $this->fgcolor = $gvars['fgcolor'];
20619 $this->htmlvspace = $gvars['htmlvspace'];
20620 $this->listindent = $gvars['listindent'];
20621 $this->listindentlevel = $gvars['listindentlevel'];
20622 $this->listnum = $gvars['listnum'];
20623 $this->listordered = $gvars['listordered'];
20624 $this->listcount = $gvars['listcount'];
20625 $this->lispacer = $gvars['lispacer'];
20626 $this->cell_height_ratio = $gvars['cell_height_ratio'];
20627 $this->font_stretching = $gvars['font_stretching'];
20628 $this->font_spacing = $gvars['font_spacing'];
20629 $this->alpha = $gvars['alpha'];
20630 if ($extended) {
20631 // restore extended values
20632 $this->lasth = $gvars['lasth'];
20633 $this->tMargin = $gvars['tMargin'];
20634 $this->bMargin = $gvars['bMargin'];
20635 $this->AutoPageBreak = $gvars['AutoPageBreak'];
20636 $this->PageBreakTrigger = $gvars['PageBreakTrigger'];
20637 $this->x = $gvars['x'];
20638 $this->y = $gvars['y'];
20639 $this->w = $gvars['w'];
20640 $this->h = $gvars['h'];
20641 $this->wPt = $gvars['wPt'];
20642 $this->hPt = $gvars['hPt'];
20643 $this->fwPt = $gvars['fwPt'];
20644 $this->fhPt = $gvars['fhPt'];
20645 $this->page = $gvars['page'];
20646 $this->current_column = $gvars['current_column'];
20647 $this->num_columns = $gvars['num_columns'];
20648 }
20649 $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
20650 if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
20651 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
20652 }
20653 }
20654
20659 protected function _outSaveGraphicsState() {
20660 $this->_out('q');
20661 }
20662
20667 protected function _outRestoreGraphicsState() {
20668 $this->_out('Q');
20669 }
20670
20677 protected function setBuffer($data) {
20678 $this->bufferlen += strlen($data);
20679 $this->buffer .= $data;
20680 }
20681
20688 protected function replaceBuffer($data) {
20689 $this->bufferlen = strlen($data);
20690 $this->buffer = $data;
20691 }
20692
20699 protected function getBuffer() {
20700 return $this->buffer;
20701 }
20702
20711 protected function setPageBuffer($page, $data, $append=false) {
20712 if ($append) {
20713 $this->pages[$page] .= $data;
20714 } else {
20715 $this->pages[$page] = $data;
20716 }
20717 if ($append AND isset($this->pagelen[$page])) {
20718 $this->pagelen[$page] += strlen($data);
20719 } else {
20720 $this->pagelen[$page] = strlen($data);
20721 }
20722 }
20723
20731 protected function getPageBuffer($page) {
20732 if (isset($this->pages[$page])) {
20733 return $this->pages[$page];
20734 }
20735 return false;
20736 }
20737
20746 protected function setImageBuffer($image, $data) {
20747 if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) {
20748 $this->imagekeys[$this->numimages] = $image;
20749 $data['i'] = $this->numimages;
20750 ++$this->numimages;
20751 }
20752 $this->images[$image] = $data;
20753 return $data['i'];
20754 }
20755
20764 protected function setImageSubBuffer($image, $key, $data) {
20765 if (!isset($this->images[$image])) {
20766 $this->setImageBuffer($image, array());
20767 }
20768 $this->images[$image][$key] = $data;
20769 }
20770
20778 protected function getImageBuffer($image) {
20779 if (isset($this->images[$image])) {
20780 return $this->images[$image];
20781 }
20782 return false;
20783 }
20784
20792 protected function setFontBuffer($font, $data) {
20793 $this->fonts[$font] = $data;
20794 if (!in_array($font, $this->fontkeys)) {
20795 $this->fontkeys[] = $font;
20796 // store object ID for current font
20797 ++$this->n;
20798 $this->font_obj_ids[$font] = $this->n;
20799 $this->setFontSubBuffer($font, 'n', $this->n);
20800 }
20801 }
20802
20811 protected function setFontSubBuffer($font, $key, $data) {
20812 if (!isset($this->fonts[$font])) {
20813 $this->setFontBuffer($font, array());
20814 }
20815 $this->fonts[$font][$key] = $data;
20816 }
20817
20825 protected function getFontBuffer($font) {
20826 if (isset($this->fonts[$font])) {
20827 return $this->fonts[$font];
20828 }
20829 return false;
20830 }
20831
20840 public function movePage($frompage, $topage) {
20841 if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
20842 return false;
20843 }
20844 if ($frompage == $this->page) {
20845 // close the page before moving it
20846 $this->endPage();
20847 }
20848 // move all page-related states
20849 $tmppage = $this->getPageBuffer($frompage);
20850 $tmppagedim = $this->pagedim[$frompage];
20851 $tmppagelen = $this->pagelen[$frompage];
20852 $tmpintmrk = $this->intmrk[$frompage];
20853 $tmpbordermrk = $this->bordermrk[$frompage];
20854 $tmpcntmrk = $this->cntmrk[$frompage];
20855 $tmppageobjects = $this->pageobjects[$frompage];
20856 if (isset($this->footerpos[$frompage])) {
20857 $tmpfooterpos = $this->footerpos[$frompage];
20858 }
20859 if (isset($this->footerlen[$frompage])) {
20860 $tmpfooterlen = $this->footerlen[$frompage];
20861 }
20862 if (isset($this->transfmrk[$frompage])) {
20863 $tmptransfmrk = $this->transfmrk[$frompage];
20864 }
20865 if (isset($this->PageAnnots[$frompage])) {
20866 $tmpannots = $this->PageAnnots[$frompage];
20867 }
20868 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
20869 for ($i = $frompage; $i > $topage; --$i) {
20870 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) {
20871 --$this->pagegroups[$this->newpagegroup[$i]];
20872 break;
20873 }
20874 }
20875 for ($i = $topage; $i > 0; --$i) {
20876 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) {
20877 ++$this->pagegroups[$this->newpagegroup[$i]];
20878 break;
20879 }
20880 }
20881 }
20882 for ($i = $frompage; $i > $topage; --$i) {
20883 $j = $i - 1;
20884 // shift pages down
20885 $this->setPageBuffer($i, $this->getPageBuffer($j));
20886 $this->pagedim[$i] = $this->pagedim[$j];
20887 $this->pagelen[$i] = $this->pagelen[$j];
20888 $this->intmrk[$i] = $this->intmrk[$j];
20889 $this->bordermrk[$i] = $this->bordermrk[$j];
20890 $this->cntmrk[$i] = $this->cntmrk[$j];
20891 $this->pageobjects[$i] = $this->pageobjects[$j];
20892 if (isset($this->footerpos[$j])) {
20893 $this->footerpos[$i] = $this->footerpos[$j];
20894 } elseif (isset($this->footerpos[$i])) {
20895 unset($this->footerpos[$i]);
20896 }
20897 if (isset($this->footerlen[$j])) {
20898 $this->footerlen[$i] = $this->footerlen[$j];
20899 } elseif (isset($this->footerlen[$i])) {
20900 unset($this->footerlen[$i]);
20901 }
20902 if (isset($this->transfmrk[$j])) {
20903 $this->transfmrk[$i] = $this->transfmrk[$j];
20904 } elseif (isset($this->transfmrk[$i])) {
20905 unset($this->transfmrk[$i]);
20906 }
20907 if (isset($this->PageAnnots[$j])) {
20908 $this->PageAnnots[$i] = $this->PageAnnots[$j];
20909 } elseif (isset($this->PageAnnots[$i])) {
20910 unset($this->PageAnnots[$i]);
20911 }
20912 if (isset($this->newpagegroup[$j])) {
20913 $this->newpagegroup[$i] = $this->newpagegroup[$j];
20914 unset($this->newpagegroup[$j]);
20915 }
20916 if ($this->currpagegroup == $j) {
20917 $this->currpagegroup = $i;
20918 }
20919 }
20920 $this->setPageBuffer($topage, $tmppage);
20921 $this->pagedim[$topage] = $tmppagedim;
20922 $this->pagelen[$topage] = $tmppagelen;
20923 $this->intmrk[$topage] = $tmpintmrk;
20924 $this->bordermrk[$topage] = $tmpbordermrk;
20925 $this->cntmrk[$topage] = $tmpcntmrk;
20926 $this->pageobjects[$topage] = $tmppageobjects;
20927 if (isset($tmpfooterpos)) {
20928 $this->footerpos[$topage] = $tmpfooterpos;
20929 } elseif (isset($this->footerpos[$topage])) {
20930 unset($this->footerpos[$topage]);
20931 }
20932 if (isset($tmpfooterlen)) {
20933 $this->footerlen[$topage] = $tmpfooterlen;
20934 } elseif (isset($this->footerlen[$topage])) {
20935 unset($this->footerlen[$topage]);
20936 }
20937 if (isset($tmptransfmrk)) {
20938 $this->transfmrk[$topage] = $tmptransfmrk;
20939 } elseif (isset($this->transfmrk[$topage])) {
20940 unset($this->transfmrk[$topage]);
20941 }
20942 if (isset($tmpannots)) {
20943 $this->PageAnnots[$topage] = $tmpannots;
20944 } elseif (isset($this->PageAnnots[$topage])) {
20945 unset($this->PageAnnots[$topage]);
20946 }
20947 // adjust outlines
20948 $tmpoutlines = $this->outlines;
20949 foreach ($tmpoutlines as $key => $outline) {
20950 if (!$outline['f']) {
20951 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
20952 $this->outlines[$key]['p'] = ($outline['p'] + 1);
20953 } elseif ($outline['p'] == $frompage) {
20954 $this->outlines[$key]['p'] = $topage;
20955 }
20956 }
20957 }
20958 // adjust dests
20959 $tmpdests = $this->dests;
20960 foreach ($tmpdests as $key => $dest) {
20961 if (!$dest['f']) {
20962 if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
20963 $this->dests[$key]['p'] = ($dest['p'] + 1);
20964 } elseif ($dest['p'] == $frompage) {
20965 $this->dests[$key]['p'] = $topage;
20966 }
20967 }
20968 }
20969 // adjust links
20970 $tmplinks = $this->links;
20971 foreach ($tmplinks as $key => $link) {
20972 if (!$link['f']) {
20973 if (($link['p'] >= $topage) AND ($link['p'] < $frompage)) {
20974 $this->links[$key]['p'] = ($link['p'] + 1);
20975 } elseif ($link['p'] == $frompage) {
20976 $this->links[$key]['p'] = $topage;
20977 }
20978 }
20979 }
20980 // adjust javascript
20981 $jfrompage = $frompage;
20982 $jtopage = $topage;
20983 if (preg_match_all('/this\.addField\‍(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) {
20984 foreach($pamatch[0] as $pk => $pmatch) {
20985 $pagenum = intval($pamatch[3][$pk]) + 1;
20986 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
20987 $newpage = ($pagenum + 1);
20988 } elseif ($pagenum == $jfrompage) {
20989 $newpage = $jtopage;
20990 } else {
20991 $newpage = $pagenum;
20992 }
20993 --$newpage;
20994 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
20995 $this->javascript = str_replace($pmatch, $newjs, $this->javascript);
20996 }
20997 unset($pamatch);
20998 }
20999 // return to last page
21000 $this->lastPage(true);
21001 return true;
21002 }
21003
21011 public function deletePage($page) {
21012 if (($page < 1) OR ($page > $this->numpages)) {
21013 return false;
21014 }
21015 // delete current page
21016 unset($this->pages[$page]);
21017 unset($this->pagedim[$page]);
21018 unset($this->pagelen[$page]);
21019 unset($this->intmrk[$page]);
21020 unset($this->bordermrk[$page]);
21021 unset($this->cntmrk[$page]);
21022 foreach ($this->pageobjects[$page] as $oid) {
21023 if (isset($this->offsets[$oid])){
21024 unset($this->offsets[$oid]);
21025 }
21026 }
21027 unset($this->pageobjects[$page]);
21028 if (isset($this->footerpos[$page])) {
21029 unset($this->footerpos[$page]);
21030 }
21031 if (isset($this->footerlen[$page])) {
21032 unset($this->footerlen[$page]);
21033 }
21034 if (isset($this->transfmrk[$page])) {
21035 unset($this->transfmrk[$page]);
21036 }
21037 if (isset($this->PageAnnots[$page])) {
21038 unset($this->PageAnnots[$page]);
21039 }
21040 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
21041 for ($i = $page; $i > 0; --$i) {
21042 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) {
21043 --$this->pagegroups[$this->newpagegroup[$i]];
21044 break;
21045 }
21046 }
21047 }
21048 if (isset($this->pageopen[$page])) {
21049 unset($this->pageopen[$page]);
21050 }
21051 if ($page < $this->numpages) {
21052 // update remaining pages
21053 for ($i = $page; $i < $this->numpages; ++$i) {
21054 $j = $i + 1;
21055 // shift pages
21056 $this->setPageBuffer($i, $this->getPageBuffer($j));
21057 $this->pagedim[$i] = $this->pagedim[$j];
21058 $this->pagelen[$i] = $this->pagelen[$j];
21059 $this->intmrk[$i] = $this->intmrk[$j];
21060 $this->bordermrk[$i] = $this->bordermrk[$j];
21061 $this->cntmrk[$i] = $this->cntmrk[$j];
21062 $this->pageobjects[$i] = $this->pageobjects[$j];
21063 if (isset($this->footerpos[$j])) {
21064 $this->footerpos[$i] = $this->footerpos[$j];
21065 } elseif (isset($this->footerpos[$i])) {
21066 unset($this->footerpos[$i]);
21067 }
21068 if (isset($this->footerlen[$j])) {
21069 $this->footerlen[$i] = $this->footerlen[$j];
21070 } elseif (isset($this->footerlen[$i])) {
21071 unset($this->footerlen[$i]);
21072 }
21073 if (isset($this->transfmrk[$j])) {
21074 $this->transfmrk[$i] = $this->transfmrk[$j];
21075 } elseif (isset($this->transfmrk[$i])) {
21076 unset($this->transfmrk[$i]);
21077 }
21078 if (isset($this->PageAnnots[$j])) {
21079 $this->PageAnnots[$i] = $this->PageAnnots[$j];
21080 } elseif (isset($this->PageAnnots[$i])) {
21081 unset($this->PageAnnots[$i]);
21082 }
21083 if (isset($this->newpagegroup[$j])) {
21084 $this->newpagegroup[$i] = $this->newpagegroup[$j];
21085 unset($this->newpagegroup[$j]);
21086 }
21087 if ($this->currpagegroup == $j) {
21088 $this->currpagegroup = $i;
21089 }
21090 if (isset($this->pageopen[$j])) {
21091 $this->pageopen[$i] = $this->pageopen[$j];
21092 } elseif (isset($this->pageopen[$i])) {
21093 unset($this->pageopen[$i]);
21094 }
21095 }
21096 // remove last page
21097 unset($this->pages[$this->numpages]);
21098 unset($this->pagedim[$this->numpages]);
21099 unset($this->pagelen[$this->numpages]);
21100 unset($this->intmrk[$this->numpages]);
21101 unset($this->bordermrk[$this->numpages]);
21102 unset($this->cntmrk[$this->numpages]);
21103 foreach ($this->pageobjects[$this->numpages] as $oid) {
21104 if (isset($this->offsets[$oid])){
21105 unset($this->offsets[$oid]);
21106 }
21107 }
21108 unset($this->pageobjects[$this->numpages]);
21109 if (isset($this->footerpos[$this->numpages])) {
21110 unset($this->footerpos[$this->numpages]);
21111 }
21112 if (isset($this->footerlen[$this->numpages])) {
21113 unset($this->footerlen[$this->numpages]);
21114 }
21115 if (isset($this->transfmrk[$this->numpages])) {
21116 unset($this->transfmrk[$this->numpages]);
21117 }
21118 if (isset($this->PageAnnots[$this->numpages])) {
21119 unset($this->PageAnnots[$this->numpages]);
21120 }
21121 if (isset($this->newpagegroup[$this->numpages])) {
21122 unset($this->newpagegroup[$this->numpages]);
21123 }
21124 if ($this->currpagegroup == $this->numpages) {
21125 $this->currpagegroup = ($this->numpages - 1);
21126 }
21127 if (isset($this->pagegroups[$this->numpages])) {
21128 unset($this->pagegroups[$this->numpages]);
21129 }
21130 if (isset($this->pageopen[$this->numpages])) {
21131 unset($this->pageopen[$this->numpages]);
21132 }
21133 }
21134 --$this->numpages;
21135 $this->page = $this->numpages;
21136 // adjust outlines
21137 $tmpoutlines = $this->outlines;
21138 foreach ($tmpoutlines as $key => $outline) {
21139 if (!$outline['f']) {
21140 if ($outline['p'] > $page) {
21141 $this->outlines[$key]['p'] = $outline['p'] - 1;
21142 } elseif ($outline['p'] == $page) {
21143 unset($this->outlines[$key]);
21144 }
21145 }
21146 }
21147 // adjust dests
21148 $tmpdests = $this->dests;
21149 foreach ($tmpdests as $key => $dest) {
21150 if (!$dest['f']) {
21151 if ($dest['p'] > $page) {
21152 $this->dests[$key]['p'] = $dest['p'] - 1;
21153 } elseif ($dest['p'] == $page) {
21154 unset($this->dests[$key]);
21155 }
21156 }
21157 }
21158 // adjust links
21159 $tmplinks = $this->links;
21160 foreach ($tmplinks as $key => $link) {
21161 if (!$link['f']) {
21162 if ($link['p'] > $page) {
21163 $this->links[$key]['p'] = $link['p'] - 1;
21164 } elseif ($link['p'] == $page) {
21165 unset($this->links[$key]);
21166 }
21167 }
21168 }
21169 // adjust javascript
21170 $jpage = $page;
21171 if (preg_match_all('/this\.addField\‍(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) {
21172 foreach($pamatch[0] as $pk => $pmatch) {
21173 $pagenum = intval($pamatch[3][$pk]) + 1;
21174 if ($pagenum >= $jpage) {
21175 $newpage = ($pagenum - 1);
21176 } elseif ($pagenum == $jpage) {
21177 $newpage = 1;
21178 } else {
21179 $newpage = $pagenum;
21180 }
21181 --$newpage;
21182 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21183 $this->javascript = str_replace($pmatch, $newjs, $this->javascript);
21184 }
21185 unset($pamatch);
21186 }
21187 // return to last page
21188 if ($this->numpages > 0) {
21189 $this->lastPage(true);
21190 }
21191 return true;
21192 }
21193
21201 public function copyPage($page=0) {
21202 if ($page == 0) {
21203 // default value
21204 $page = $this->page;
21205 }
21206 if (($page < 1) OR ($page > $this->numpages)) {
21207 return false;
21208 }
21209 // close the last page
21210 $this->endPage();
21211 // copy all page-related states
21212 ++$this->numpages;
21213 $this->page = $this->numpages;
21214 $this->setPageBuffer($this->page, $this->getPageBuffer($page));
21215 $this->pagedim[$this->page] = $this->pagedim[$page];
21216 $this->pagelen[$this->page] = $this->pagelen[$page];
21217 $this->intmrk[$this->page] = $this->intmrk[$page];
21218 $this->bordermrk[$this->page] = $this->bordermrk[$page];
21219 $this->cntmrk[$this->page] = $this->cntmrk[$page];
21220 $this->pageobjects[$this->page] = $this->pageobjects[$page];
21221 $this->pageopen[$this->page] = false;
21222 if (isset($this->footerpos[$page])) {
21223 $this->footerpos[$this->page] = $this->footerpos[$page];
21224 }
21225 if (isset($this->footerlen[$page])) {
21226 $this->footerlen[$this->page] = $this->footerlen[$page];
21227 }
21228 if (isset($this->transfmrk[$page])) {
21229 $this->transfmrk[$this->page] = $this->transfmrk[$page];
21230 }
21231 if (isset($this->PageAnnots[$page])) {
21232 $this->PageAnnots[$this->page] = $this->PageAnnots[$page];
21233 }
21234 if (isset($this->newpagegroup[$page])) {
21235 // start a new group
21236 $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1;
21237 $this->currpagegroup = $this->newpagegroup[$this->page];
21238 $this->pagegroups[$this->currpagegroup] = 1;
21239 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
21240 ++$this->pagegroups[$this->currpagegroup];
21241 }
21242 // copy outlines
21243 $tmpoutlines = $this->outlines;
21244 foreach ($tmpoutlines as $key => $outline) {
21245 if ($outline['p'] == $page) {
21246 $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'x' => $outline['x'], 'y' => $outline['y'], 'p' => $this->page, 'f' => $outline['f'], 's' => $outline['s'], 'c' => $outline['c']);
21247 }
21248 }
21249 // copy links
21250 $tmplinks = $this->links;
21251 foreach ($tmplinks as $key => $link) {
21252 if ($link['p'] == $page) {
21253 $this->links[] = array('p' => $this->page, 'y' => $link['y'], 'f' => $link['f']);
21254 }
21255 }
21256 // return to last page
21257 $this->lastPage(true);
21258 return true;
21259 }
21260
21278 public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
21279 $fontsize = $this->FontSizePt;
21280 $fontfamily = $this->FontFamily;
21281 $fontstyle = $this->FontStyle;
21282 $w = $this->w - $this->lMargin - $this->rMargin;
21283 $spacer = $this->GetStringWidth(chr(32)) * 4;
21284 $lmargin = $this->lMargin;
21285 $rmargin = $this->rMargin;
21286 $x_start = $this->GetX();
21287 $page_first = $this->page;
21288 $current_page = $this->page;
21289 $page_fill_start = false;
21290 $page_fill_end = false;
21291 $current_column = $this->current_column;
21292 if (TCPDF_STATIC::empty_string($numbersfont)) {
21293 $numbersfont = $this->default_monospaced_font;
21294 }
21295 if (TCPDF_STATIC::empty_string($filler)) {
21296 $filler = ' ';
21297 }
21298 if (TCPDF_STATIC::empty_string($page)) {
21299 $gap = ' ';
21300 } else {
21301 $gap = '';
21302 if ($page < 1) {
21303 $page = 1;
21304 }
21305 }
21306 $this->SetFont($numbersfont, $fontstyle, $fontsize);
21307 $numwidth = $this->GetStringWidth('00000');
21308 $maxpage = 0; //used for pages on attached documents
21309 foreach ($this->outlines as $key => $outline) {
21310 // check for extra pages (used for attachments)
21311 if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) {
21312 $outline['p'] += ($this->page - $page_first);
21313 }
21314 if ($this->rtl) {
21315 $aligntext = 'R';
21316 $alignnum = 'L';
21317 } else {
21318 $aligntext = 'L';
21319 $alignnum = 'R';
21320 }
21321 if ($outline['l'] == 0) {
21322 $this->SetFont($fontfamily, $outline['s'].'B', $fontsize);
21323 } else {
21324 $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']);
21325 }
21326 $this->SetTextColorArray($outline['c']);
21327 // check for page break
21328 $this->checkPageBreak(2 * $this->getCellHeight($this->FontSize));
21329 // set margins and X position
21330 if (($this->page == $current_page) AND ($this->current_column == $current_column)) {
21331 $this->lMargin = $lmargin;
21332 $this->rMargin = $rmargin;
21333 } else {
21334 if ($this->current_column != $current_column) {
21335 if ($this->rtl) {
21336 $x_start = $this->w - $this->columns[$this->current_column]['x'];
21337 } else {
21338 $x_start = $this->columns[$this->current_column]['x'];
21339 }
21340 }
21341 $lmargin = $this->lMargin;
21342 $rmargin = $this->rMargin;
21343 $current_page = $this->page;
21344 $current_column = $this->current_column;
21345 }
21346 $this->SetX($x_start);
21347 $indent = ($spacer * $outline['l']);
21348 if ($this->rtl) {
21349 $this->x -= $indent;
21350 $this->rMargin = $this->w - $this->x;
21351 } else {
21352 $this->x += $indent;
21353 $this->lMargin = $this->x;
21354 }
21355 $link = $this->AddLink();
21356 $this->SetLink($link, $outline['y'], $outline['p']);
21357 // write the text
21358 if ($this->rtl) {
21359 $txt = ' '.$outline['t'];
21360 } else {
21361 $txt = $outline['t'].' ';
21362 }
21363 $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
21364 if ($this->rtl) {
21365 $tw = $this->x - $this->lMargin;
21366 } else {
21367 $tw = $this->w - $this->rMargin - $this->x;
21368 }
21369 $this->SetFont($numbersfont, $fontstyle, $fontsize);
21370 if (TCPDF_STATIC::empty_string($page)) {
21371 $pagenum = $outline['p'];
21372 } else {
21373 // placemark to be replaced with the correct number
21374 $pagenum = '{#'.($outline['p']).'}';
21375 if ($this->isUnicodeFont()) {
21376 $pagenum = '{'.$pagenum.'}';
21377 }
21378 $maxpage = max($maxpage, $outline['p']);
21379 }
21380 $fw = ($tw - $this->GetStringWidth($pagenum.$filler));
21381 $wfiller = $this->GetStringWidth($filler);
21382 if ($wfiller > 0) {
21383 $numfills = floor($fw / $wfiller);
21384 } else {
21385 $numfills = 0;
21386 }
21387 if ($numfills > 0) {
21388 $rowfill = str_repeat($filler, $numfills);
21389 } else {
21390 $rowfill = '';
21391 }
21392 if ($this->rtl) {
21393 $pagenum = $pagenum.$gap.$rowfill;
21394 } else {
21395 $pagenum = $rowfill.$gap.$pagenum;
21396 }
21397 // write the number
21398 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
21399 }
21400 $page_last = $this->getPage();
21401 $numpages = ($page_last - $page_first + 1);
21402 // account for booklet mode
21403 if ($this->booklet) {
21404 // check if a blank page is required before TOC
21405 $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21406 $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21407 if ($page_fill_start) {
21408 // add a page at the end (to be moved before TOC)
21409 $this->addPage();
21410 ++$page_last;
21411 ++$numpages;
21412 }
21413 if ($page_fill_end) {
21414 // add a page at the end
21415 $this->addPage();
21416 ++$page_last;
21417 ++$numpages;
21418 }
21419 }
21420 $maxpage = max($maxpage, $page_last);
21421 if (!TCPDF_STATIC::empty_string($page)) {
21422 for ($p = $page_first; $p <= $page_last; ++$p) {
21423 // get page data
21424 $temppage = $this->getPageBuffer($p);
21425 for ($n = 1; $n <= $maxpage; ++$n) {
21426 // update page numbers
21427 $a = '{#'.$n.'}';
21428 // get page number aliases
21429 $pnalias = $this->getInternalPageNumberAliases($a);
21430 // calculate replacement number
21431 if (($n >= $page) AND ($n <= $this->numpages)) {
21432 $np = $n + $numpages;
21433 } else {
21434 $np = $n;
21435 }
21436 $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21437 $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21438 // replace aliases with numbers
21439 foreach ($pnalias['u'] as $u) {
21440 $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
21441 if ($this->rtl) {
21442 $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21443 } else {
21444 $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21445 }
21446 $temppage = str_replace($u, $nr, $temppage);
21447 }
21448 foreach ($pnalias['a'] as $a) {
21449 $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
21450 if ($this->rtl) {
21451 $nr = $na.' '.$sfill;
21452 } else {
21453 $nr = $sfill.' '.$na;
21454 }
21455 $temppage = str_replace($a, $nr, $temppage);
21456 }
21457 }
21458 // save changes
21459 $this->setPageBuffer($p, $temppage);
21460 }
21461 // move pages
21462 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21463 if ($page_fill_start) {
21464 $this->movePage($page_last, $page_first);
21465 }
21466 for ($i = 0; $i < $numpages; ++$i) {
21467 $this->movePage($page_last, $page);
21468 }
21469 }
21470 }
21471
21488 public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
21489 $filler = ' ';
21490 $prev_htmlLinkColorArray = $this->htmlLinkColorArray;
21491 $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle;
21492 // set new style for link
21493 $this->htmlLinkColorArray = array();
21494 $this->htmlLinkFontStyle = '';
21495 $page_first = $this->getPage();
21496 $page_fill_start = false;
21497 $page_fill_end = false;
21498 // get the font type used for numbers in each template
21499 $current_font = $this->FontFamily;
21500 foreach ($templates as $level => $html) {
21501 $dom = $this->getHtmlDomArray($html);
21502 foreach ($dom as $key => $value) {
21503 if ($value['value'] == '#TOC_PAGE_NUMBER#') {
21504 $this->SetFont($dom[($key - 1)]['fontname']);
21505 $templates['F'.$level] = $this->isUnicodeFont();
21506 }
21507 }
21508 }
21509 $this->SetFont($current_font);
21510 $maxpage = 0; //used for pages on attached documents
21511 foreach ($this->outlines as $key => $outline) {
21512 // get HTML template
21513 $row = $templates[$outline['l']];
21514 if (TCPDF_STATIC::empty_string($page)) {
21515 $pagenum = $outline['p'];
21516 } else {
21517 // placemark to be replaced with the correct number
21518 $pagenum = '{#'.($outline['p']).'}';
21519 if ($templates['F'.$outline['l']]) {
21520 $pagenum = '{'.$pagenum.'}';
21521 }
21522 $maxpage = max($maxpage, $outline['p']);
21523 }
21524 // replace templates with current values
21525 $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
21526 $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
21527 // add link to page
21528 $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
21529 // write bookmark entry
21530 $this->writeHTML($row, false, false, true, false, '');
21531 }
21532 // restore link styles
21533 $this->htmlLinkColorArray = $prev_htmlLinkColorArray;
21534 $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle;
21535 // move TOC page and replace numbers
21536 $page_last = $this->getPage();
21537 $numpages = ($page_last - $page_first + 1);
21538 // account for booklet mode
21539 if ($this->booklet) {
21540 // check if a blank page is required before TOC
21541 $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21542 $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21543 if ($page_fill_start) {
21544 // add a page at the end (to be moved before TOC)
21545 $this->addPage();
21546 ++$page_last;
21547 ++$numpages;
21548 }
21549 if ($page_fill_end) {
21550 // add a page at the end
21551 $this->addPage();
21552 ++$page_last;
21553 ++$numpages;
21554 }
21555 }
21556 $maxpage = max($maxpage, $page_last);
21557 if (!TCPDF_STATIC::empty_string($page)) {
21558 for ($p = $page_first; $p <= $page_last; ++$p) {
21559 // get page data
21560 $temppage = $this->getPageBuffer($p);
21561 for ($n = 1; $n <= $maxpage; ++$n) {
21562 // update page numbers
21563 $a = '{#'.$n.'}';
21564 // get page number aliases
21565 $pnalias = $this->getInternalPageNumberAliases($a);
21566 // calculate replacement number
21567 if ($n >= $page) {
21568 $np = $n + $numpages;
21569 } else {
21570 $np = $n;
21571 }
21572 $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21573 $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21574 // replace aliases with numbers
21575 foreach ($pnalias['u'] as $u) {
21576 if ($correct_align) {
21577 $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
21578 if ($this->rtl) {
21579 $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21580 } else {
21581 $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21582 }
21583 } else {
21584 $nr = $nu;
21585 }
21586 $temppage = str_replace($u, $nr, $temppage);
21587 }
21588 foreach ($pnalias['a'] as $a) {
21589 if ($correct_align) {
21590 $sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
21591 if ($this->rtl) {
21592 $nr = $na.' '.$sfill;
21593 } else {
21594 $nr = $sfill.' '.$na;
21595 }
21596 } else {
21597 $nr = $na;
21598 }
21599 $temppage = str_replace($a, $nr, $temppage);
21600 }
21601 }
21602 // save changes
21603 $this->setPageBuffer($p, $temppage);
21604 }
21605 // move pages
21606 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21607 if ($page_fill_start) {
21608 $this->movePage($page_last, $page_first);
21609 }
21610 for ($i = 0; $i < $numpages; ++$i) {
21611 $this->movePage($page_last, $page);
21612 }
21613 }
21614 }
21615
21621 public function startTransaction() {
21622 if (isset($this->objcopy)) {
21623 // remove previous copy
21624 $this->commitTransaction();
21625 }
21626 // record current page number and Y position
21627 $this->start_transaction_page = $this->page;
21628 $this->start_transaction_y = $this->y;
21629 // clone current object
21630 $this->objcopy = TCPDF_STATIC::objclone($this);
21631 }
21632
21638 public function commitTransaction() {
21639 if (isset($this->objcopy)) {
21640 $this->objcopy->_destroy(true, true);
21641 unset($this->objcopy);
21642 }
21643 }
21644
21652 public function rollbackTransaction($self=false) {
21653 if (isset($this->objcopy)) {
21654 $this->_destroy(true, true);
21655 if ($self) {
21656 $objvars = get_object_vars($this->objcopy);
21657 foreach ($objvars as $key => $value) {
21658 $this->$key = $value;
21659 }
21660 }
21661 return $this->objcopy;
21662 }
21663 return $this;
21664 }
21665
21666 // --- MULTI COLUMNS METHODS -----------------------
21667
21676 public function setEqualColumns($numcols=0, $width=0, $y='') {
21677 $this->columns = array();
21678 if ($numcols < 2) {
21679 $numcols = 0;
21680 $this->columns = array();
21681 } else {
21682 // maximum column width
21683 $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
21684 if (($width == 0) OR ($width > $maxwidth)) {
21685 $width = $maxwidth;
21686 }
21688 $y = $this->y;
21689 }
21690 // space between columns
21691 $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
21692 // fill the columns array (with, space, starting Y position)
21693 for ($i = 0; $i < $numcols; ++$i) {
21694 $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
21695 }
21696 }
21697 $this->num_columns = $numcols;
21698 $this->current_column = 0;
21699 $this->column_start_page = $this->page;
21700 $this->selectColumn(0);
21701 }
21702
21708 public function resetColumns() {
21709 $this->lMargin = $this->original_lMargin;
21710 $this->rMargin = $this->original_rMargin;
21711 $this->setEqualColumns();
21712 }
21713
21721 public function setColumnsArray($columns) {
21722 $this->columns = $columns;
21723 $this->num_columns = count($columns);
21724 $this->current_column = 0;
21725 $this->column_start_page = $this->page;
21726 $this->selectColumn(0);
21727 }
21728
21735 public function selectColumn($col='') {
21736 if (is_string($col)) {
21737 $col = $this->current_column;
21738 } elseif ($col >= $this->num_columns) {
21739 $col = 0;
21740 }
21741 $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
21742 $enable_thead = false;
21743 if ($this->num_columns > 1) {
21744 if ($col != $this->current_column) {
21745 // move Y pointer at the top of the column
21746 if ($this->column_start_page == $this->page) {
21747 $this->y = $this->columns[$col]['y'];
21748 } else {
21749 $this->y = $this->tMargin;
21750 }
21751 // Avoid to write table headers more than once
21752 if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) {
21753 $enable_thead = true;
21754 $this->maxselcol['page'] = $this->page;
21755 $this->maxselcol['column'] = $col;
21756 }
21757 }
21758 $xshift = $this->colxshift;
21759 // set X position of the current column by case
21760 $listindent = ($this->listindentlevel * $this->listindent);
21761 // calculate column X position
21762 $colpos = 0;
21763 for ($i = 0; $i < $col; ++$i) {
21764 $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']);
21765 }
21766 if ($this->rtl) {
21767 $x = $this->w - $this->original_rMargin - $colpos;
21768 $this->rMargin = ($this->w - $x + $listindent);
21769 $this->lMargin = ($x - $this->columns[$col]['w']);
21770 $this->x = $x - $listindent;
21771 } else {
21772 $x = $this->original_lMargin + $colpos;
21773 $this->lMargin = ($x + $listindent);
21774 $this->rMargin = ($this->w - $x - $this->columns[$col]['w']);
21775 $this->x = $x + $listindent;
21776 }
21777 $this->columns[$col]['x'] = $x;
21778 }
21779 $this->current_column = $col;
21780 // fix for HTML mode
21781 $this->newline = true;
21782 // print HTML table header (if any)
21783 if ((!TCPDF_STATIC::empty_string($this->thead)) AND (!$this->inthead)) {
21784 if ($enable_thead) {
21785 // print table header
21786 $this->writeHTML($this->thead, false, false, false, false, '');
21787 $this->y += $xshift['s']['V'];
21788 // store end of header position
21789 if (!isset($this->columns[$col]['th'])) {
21790 $this->columns[$col]['th'] = array();
21791 }
21792 $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y;
21793 $this->lasth = 0;
21794 } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) {
21795 $this->y = $this->columns[$col]['th']['\''.$this->page.'\''];
21796 }
21797 }
21798 // account for an html table cell over multiple columns
21799 if ($this->rtl) {
21800 $this->rMargin += $xshift['x'];
21801 $this->x -= ($xshift['x'] + $xshift['p']['R']);
21802 } else {
21803 $this->lMargin += $xshift['x'];
21804 $this->x += $xshift['x'] + $xshift['p']['L'];
21805 }
21806 }
21807
21814 public function getColumn() {
21815 return $this->current_column;
21816 }
21817
21824 public function getNumberOfColumns() {
21825 return $this->num_columns;
21826 }
21827
21836 public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
21837 // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
21838 // convert text rendering parameters
21839 if ($stroke < 0) {
21840 $stroke = 0;
21841 }
21842 if ($fill === true) {
21843 if ($stroke > 0) {
21844 if ($clip === true) {
21845 // Fill, then stroke text and add to path for clipping
21846 $textrendermode = 6;
21847 } else {
21848 // Fill, then stroke text
21849 $textrendermode = 2;
21850 }
21851 $textstrokewidth = $stroke;
21852 } else {
21853 if ($clip === true) {
21854 // Fill text and add to path for clipping
21855 $textrendermode = 4;
21856 } else {
21857 // Fill text
21858 $textrendermode = 0;
21859 }
21860 }
21861 } else {
21862 if ($stroke > 0) {
21863 if ($clip === true) {
21864 // Stroke text and add to path for clipping
21865 $textrendermode = 5;
21866 } else {
21867 // Stroke text
21868 $textrendermode = 1;
21869 }
21870 $textstrokewidth = $stroke;
21871 } else {
21872 if ($clip === true) {
21873 // Add text to path for clipping
21874 $textrendermode = 7;
21875 } else {
21876 // Neither fill nor stroke text (invisible)
21877 $textrendermode = 3;
21878 }
21879 }
21880 }
21881 $this->textrendermode = $textrendermode;
21882 $this->textstrokewidth = $stroke;
21883 }
21884
21891 public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) {
21892 if (isset($params['enabled'])) {
21893 $this->txtshadow['enabled'] = $params['enabled']?true:false;
21894 } else {
21895 $this->txtshadow['enabled'] = false;
21896 }
21897 if (isset($params['depth_w'])) {
21898 $this->txtshadow['depth_w'] = floatval($params['depth_w']);
21899 } else {
21900 $this->txtshadow['depth_w'] = 0;
21901 }
21902 if (isset($params['depth_h'])) {
21903 $this->txtshadow['depth_h'] = floatval($params['depth_h']);
21904 } else {
21905 $this->txtshadow['depth_h'] = 0;
21906 }
21907 if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) {
21908 $this->txtshadow['color'] = $params['color'];
21909 } else {
21910 $this->txtshadow['color'] = $this->strokecolor;
21911 }
21912 if (isset($params['opacity'])) {
21913 $this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity'])));
21914 } else {
21915 $this->txtshadow['opacity'] = 1;
21916 }
21917 if (isset($params['blend_mode']) AND in_array($params['blend_mode'], array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
21918 $this->txtshadow['blend_mode'] = $params['blend_mode'];
21919 } else {
21920 $this->txtshadow['blend_mode'] = 'Normal';
21921 }
21922 if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) {
21923 $this->txtshadow['enabled'] = false;
21924 }
21925 }
21926
21933 public function getTextShadow() {
21934 return $this->txtshadow;
21935 }
21936
21951 protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
21952 $hyphenword = array(); // hyphens positions
21953 $numchars = count($word);
21954 if ($numchars <= $charmin) {
21955 return $word;
21956 }
21957 $word_string = TCPDF_FONTS::UTF8ArrSubString($word, '', '', $this->isunicode);
21958 // some words will be returned as-is
21959 $pattern = '/^([a-zA-Z0-9_\.\-]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/';
21960 if (preg_match($pattern, $word_string) > 0) {
21961 // email
21962 return $word;
21963 }
21964 $pattern = '/(([a-zA-Z0-9\-]+\.)?)((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/';
21965 if (preg_match($pattern, $word_string) > 0) {
21966 // URL
21967 return $word;
21968 }
21969 if (isset($dictionary[$word_string])) {
21970 return TCPDF_FONTS::UTF8StringToArray($dictionary[$word_string], $this->isunicode, $this->CurrentFont);
21971 }
21972 // surround word with '_' characters
21973 $tmpword = array_merge(array(46), $word, array(46));
21974 $tmpnumchars = $numchars + 2;
21975 $maxpos = $tmpnumchars - 1;
21976 for ($pos = 0; $pos < $maxpos; ++$pos) {
21977 $imax = min(($tmpnumchars - $pos), $charmax);
21978 for ($i = 1; $i <= $imax; ++$i) {
21979 $subword = strtolower(TCPDF_FONTS::UTF8ArrSubString($tmpword, $pos, ($pos + $i), $this->isunicode));
21980 if (isset($patterns[$subword])) {
21981 $pattern = TCPDF_FONTS::UTF8StringToArray($patterns[$subword], $this->isunicode, $this->CurrentFont);
21982 $pattern_length = count($pattern);
21983 $digits = 1;
21984 for ($j = 0; $j < $pattern_length; ++$j) {
21985 // check if $pattern[$j] is a number = hyphenation level (only numbers from 1 to 5 are valid)
21986 if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
21987 if ($j == 0) {
21988 $zero = $pos - 1;
21989 } else {
21990 $zero = $pos + $j - $digits;
21991 }
21992 // get hyphenation level
21993 $level = ($pattern[$j] - 48);
21994 // if two levels from two different patterns match at the same point, the higher one is selected.
21995 if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] < $level)) {
21996 $hyphenword[$zero] = $level;
21997 }
21998 ++$digits;
21999 }
22000 }
22001 }
22002 }
22003 }
22004 $inserted = 0;
22005 $maxpos = $numchars - $rightmin;
22006 for ($i = $leftmin; $i <= $maxpos; ++$i) {
22007 // only odd levels indicate allowed hyphenation points
22008 if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
22009 // 173 = soft hyphen character
22010 array_splice($word, $i + $inserted, 0, 173);
22011 ++$inserted;
22012 }
22013 }
22014 return $word;
22015 }
22016
22031 public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22032 $text = $this->unhtmlentities($text);
22033 $word = array(); // last word
22034 $txtarr = array(); // text to be returned
22035 $intag = false; // true if we are inside an HTML tag
22036 $skip = false; // true to skip hyphenation
22037 if (!is_array($patterns)) {
22038 $patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patterns);
22039 }
22040 // get array of characters
22041 $unichars = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
22042 // for each char
22043 foreach ($unichars as $char) {
22044 if ((!$intag) AND (!$skip) AND TCPDF_FONT_DATA::$uni_type[$char] == 'L') {
22045 // letter character
22046 $word[] = $char;
22047 } else {
22048 // other type of character
22049 if (!TCPDF_STATIC::empty_string($word)) {
22050 // hypenate the word
22051 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22052 $word = array();
22053 }
22054 $txtarr[] = $char;
22055 if (chr($char) == '<') {
22056 // we are inside an HTML tag
22057 $intag = true;
22058 } elseif ($intag AND (chr($char) == '>')) {
22059 // end of HTML tag
22060 $intag = false;
22061 // check for style tag
22062 $expected = array(115, 116, 121, 108, 101); // = 'style'
22063 $current = array_slice($txtarr, -6, 5); // last 5 chars
22064 $compare = array_diff($expected, $current);
22065 if (empty($compare)) {
22066 // check if it is a closing tag
22067 $expected = array(47); // = '/'
22068 $current = array_slice($txtarr, -7, 1);
22069 $compare = array_diff($expected, $current);
22070 if (empty($compare)) {
22071 // closing style tag
22072 $skip = false;
22073 } else {
22074 // opening style tag
22075 $skip = true;
22076 }
22077 }
22078 }
22079 }
22080 }
22081 if (!TCPDF_STATIC::empty_string($word)) {
22082 // hypenate the word
22083 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22084 }
22085 // convert char array to string and return
22086 return TCPDF_FONTS::UTF8ArrSubString($txtarr, '', '', $this->isunicode);
22087 }
22088
22095 public function setRasterizeVectorImages($mode) {
22096 $this->rasterize_vector_images = $mode;
22097 }
22098
22106 public function setFontSubsetting($enable=true) {
22107 if ($this->pdfa_mode) {
22108 $this->font_subsetting = false;
22109 } else {
22110 $this->font_subsetting = $enable ? true : false;
22111 }
22112 }
22113
22121 public function getFontSubsetting() {
22122 return $this->font_subsetting;
22123 }
22124
22134 public function stringLeftTrim($str, $replace='') {
22135 return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str);
22136 }
22137
22147 public function stringRightTrim($str, $replace='') {
22148 return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str);
22149 }
22150
22160 public function stringTrim($str, $replace='') {
22161 $str = $this->stringLeftTrim($str, $replace);
22162 $str = $this->stringRightTrim($str, $replace);
22163 return $str;
22164 }
22165
22173 public function isUnicodeFont() {
22174 return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0'));
22175 }
22176
22185 public function getFontFamilyName($fontfamily) {
22186 // remove spaces and symbols
22187 $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily));
22188 // extract all font names
22189 $fontslist = preg_split('/[,]/', $fontfamily);
22190 // find first valid font name
22191 foreach ($fontslist as $font) {
22192 // replace font variations
22193 $font = preg_replace('/regular$/', '', $font);
22194 $font = preg_replace('/italic$/', 'I', $font);
22195 $font = preg_replace('/oblique$/', 'I', $font);
22196 $font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
22197 // replace common family names and core fonts
22198 $pattern = array();
22199 $replacement = array();
22200 $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
22201 $replacement[] = 'times';
22202 $pattern[] = '/^sansserif/';
22203 $replacement[] = 'helvetica';
22204 $pattern[] = '/^monospace/';
22205 $replacement[] = 'courier';
22206 $font = preg_replace($pattern, $replacement, $font);
22207 if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) {
22208 return $font;
22209 }
22210 }
22211 // return current font as default
22212 return $this->CurrentFont['fontkey'];
22213 }
22214
22229 public function startTemplate($w=0, $h=0, $group=false) {
22230 if ($this->inxobj) {
22231 // we are already inside an XObject template
22232 return false;
22233 }
22234 $this->inxobj = true;
22235 ++$this->n;
22236 // XObject ID
22237 $this->xobjid = 'XT'.$this->n;
22238 // object ID
22239 $this->xobjects[$this->xobjid] = array('n' => $this->n);
22240 // store current graphic state
22241 $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars();
22242 // initialize data
22243 $this->xobjects[$this->xobjid]['intmrk'] = 0;
22244 $this->xobjects[$this->xobjid]['transfmrk'] = array();
22245 $this->xobjects[$this->xobjid]['outdata'] = '';
22246 $this->xobjects[$this->xobjid]['xobjects'] = array();
22247 $this->xobjects[$this->xobjid]['images'] = array();
22248 $this->xobjects[$this->xobjid]['fonts'] = array();
22249 $this->xobjects[$this->xobjid]['annotations'] = array();
22250 $this->xobjects[$this->xobjid]['extgstates'] = array();
22251 $this->xobjects[$this->xobjid]['gradients'] = array();
22252 $this->xobjects[$this->xobjid]['spot_colors'] = array();
22253 // set new environment
22254 $this->num_columns = 1;
22255 $this->current_column = 0;
22256 $this->SetAutoPageBreak(false);
22257 if (($w === '') OR ($w <= 0)) {
22258 $w = $this->w - $this->lMargin - $this->rMargin;
22259 }
22260 if (($h === '') OR ($h <= 0)) {
22261 $h = $this->h - $this->tMargin - $this->bMargin;
22262 }
22263 $this->xobjects[$this->xobjid]['x'] = 0;
22264 $this->xobjects[$this->xobjid]['y'] = 0;
22265 $this->xobjects[$this->xobjid]['w'] = $w;
22266 $this->xobjects[$this->xobjid]['h'] = $h;
22267 $this->w = $w;
22268 $this->h = $h;
22269 $this->wPt = $this->w * $this->k;
22270 $this->hPt = $this->h * $this->k;
22271 $this->fwPt = $this->wPt;
22272 $this->fhPt = $this->hPt;
22273 $this->x = 0;
22274 $this->y = 0;
22275 $this->lMargin = 0;
22276 $this->rMargin = 0;
22277 $this->tMargin = 0;
22278 $this->bMargin = 0;
22279 // set group mode
22280 $this->xobjects[$this->xobjid]['group'] = $group;
22281 return $this->xobjid;
22282 }
22283
22294 public function endTemplate() {
22295 if (!$this->inxobj) {
22296 // we are not inside a template
22297 return false;
22298 }
22299 $this->inxobj = false;
22300 // restore previous graphic state
22301 $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true);
22302 return $this->xobjid;
22303 }
22304
22323 public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
22324 if ($this->state != 2) {
22325 return;
22326 }
22327 if (!isset($this->xobjects[$id])) {
22328 $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
22329 }
22330 if ($this->inxobj) {
22331 if ($id == $this->xobjid) {
22332 // close current template
22333 $this->endTemplate();
22334 } else {
22335 // use the template as resource for the template currently opened
22336 $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id];
22337 }
22338 }
22339 // set default values
22340 if ($x === '') {
22341 $x = $this->x;
22342 }
22343 if ($y === '') {
22344 $y = $this->y;
22345 }
22346 // check page for no-write regions and adapt page margins if necessary
22347 list($x, $y) = $this->checkPageRegions($h, $x, $y);
22348 $ow = $this->xobjects[$id]['w'];
22349 if ($ow <= 0) {
22350 $ow = 1;
22351 }
22352 $oh = $this->xobjects[$id]['h'];
22353 if ($oh <= 0) {
22354 $oh = 1;
22355 }
22356 // calculate template width and height on document
22357 if (($w <= 0) AND ($h <= 0)) {
22358 $w = $ow;
22359 $h = $oh;
22360 } elseif ($w <= 0) {
22361 $w = $h * $ow / $oh;
22362 } elseif ($h <= 0) {
22363 $h = $w * $oh / $ow;
22364 }
22365 // fit the template on available space
22366 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22367 // set page alignment
22368 $rb_y = $y + $h;
22369 // set alignment
22370 if ($this->rtl) {
22371 if ($palign == 'L') {
22372 $xt = $this->lMargin;
22373 } elseif ($palign == 'C') {
22374 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22375 } elseif ($palign == 'R') {
22376 $xt = $this->w - $this->rMargin - $w;
22377 } else {
22378 $xt = $x - $w;
22379 }
22380 $rb_x = $xt;
22381 } else {
22382 if ($palign == 'L') {
22383 $xt = $this->lMargin;
22384 } elseif ($palign == 'C') {
22385 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22386 } elseif ($palign == 'R') {
22387 $xt = $this->w - $this->rMargin - $w;
22388 } else {
22389 $xt = $x;
22390 }
22391 $rb_x = $xt + $w;
22392 }
22393 // print XObject Template + Transformation matrix
22394 $this->StartTransform();
22395 // translate and scale
22396 $sx = ($w / $ow);
22397 $sy = ($h / $oh);
22398 $tm = array();
22399 $tm[0] = $sx;
22400 $tm[1] = 0;
22401 $tm[2] = 0;
22402 $tm[3] = $sy;
22403 $tm[4] = $xt * $this->k;
22404 $tm[5] = ($this->h - $h - $y) * $this->k;
22405 $this->Transform($tm);
22406 // set object
22407 $this->_out('/'.$id.' Do');
22408 $this->StopTransform();
22409 // add annotations
22410 if (!empty($this->xobjects[$id]['annotations'])) {
22411 foreach ($this->xobjects[$id]['annotations'] as $annot) {
22412 // transform original coordinates
22413 $coordlt = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k)));
22414 $ax = ($coordlt[4] / $this->k);
22415 $ay = ($this->h - $h - ($coordlt[5] / $this->k));
22416 $coordrb = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k)));
22417 $aw = ($coordrb[4] / $this->k) - $ax;
22418 $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay;
22419 $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
22420 }
22421 }
22422 // set pointer to align the next text/objects
22423 switch($align) {
22424 case 'T': {
22425 $this->y = $y;
22426 $this->x = $rb_x;
22427 break;
22428 }
22429 case 'M': {
22430 $this->y = $y + round($h/2);
22431 $this->x = $rb_x;
22432 break;
22433 }
22434 case 'B': {
22435 $this->y = $rb_y;
22436 $this->x = $rb_x;
22437 break;
22438 }
22439 case 'N': {
22440 $this->SetY($rb_y);
22441 break;
22442 }
22443 default:{
22444 break;
22445 }
22446 }
22447 }
22448
22456 public function setFontStretching($perc=100) {
22457 $this->font_stretching = $perc;
22458 }
22459
22467 public function getFontStretching() {
22468 return $this->font_stretching;
22469 }
22470
22478 public function setFontSpacing($spacing=0) {
22479 $this->font_spacing = $spacing;
22480 }
22481
22489 public function getFontSpacing() {
22490 return $this->font_spacing;
22491 }
22492
22501 public function getPageRegions() {
22502 return $this->page_regions;
22503 }
22504
22516 public function setPageRegions($regions=array()) {
22517 // empty current regions array
22518 $this->page_regions = array();
22519 // add regions
22520 foreach ($regions as $data) {
22521 $this->addPageRegion($data);
22522 }
22523 }
22524
22536 public function addPageRegion($region) {
22537 if (!isset($region['page']) OR empty($region['page'])) {
22538 $region['page'] = $this->page;
22539 }
22540 if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
22541 AND isset($region['yt']) AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
22542 AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
22543 $this->page_regions[] = $region;
22544 }
22545 }
22546
22555 public function removePageRegion($key) {
22556 if (isset($this->page_regions[$key])) {
22557 unset($this->page_regions[$key]);
22558 }
22559 }
22560
22573 protected function checkPageRegions($h, $x, $y) {
22574 // set default values
22575 if ($x === '') {
22576 $x = $this->x;
22577 }
22578 if ($y === '') {
22579 $y = $this->y;
22580 }
22581 if (!$this->check_page_regions OR empty($this->page_regions)) {
22582 // no page regions defined
22583 return array($x, $y);
22584 }
22585 if (empty($h)) {
22586 $h = $this->getCellHeight($this->FontSize);
22587 }
22588 // check for page break
22589 if ($this->checkPageBreak($h, $y)) {
22590 // the content will be printed on a new page
22591 $x = $this->x;
22592 $y = $this->y;
22593 }
22594 if ($this->num_columns > 1) {
22595 if ($this->rtl) {
22596 $this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22597 } else {
22598 $this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22599 }
22600 } else {
22601 if ($this->rtl) {
22602 $this->lMargin = max($this->clMargin, $this->original_lMargin);
22603 } else {
22604 $this->rMargin = max($this->crMargin, $this->original_rMargin);
22605 }
22606 }
22607 // adjust coordinates and page margins
22608 foreach ($this->page_regions as $regid => $regdata) {
22609 if ($regdata['page'] == $this->page) {
22610 // check region boundaries
22611 if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
22612 // Y is inside the region
22613 $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
22614 $yt = max($y, $regdata['yt']);
22615 $yb = min(($yt + $h), $regdata['yb']);
22616 $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt'];
22617 $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt'];
22618 if ($regdata['side'] == 'L') { // left side
22619 $new_margin = max($xt, $xb);
22620 if ($this->lMargin < $new_margin) {
22621 if ($this->rtl) {
22622 // adjust left page margin
22623 $this->lMargin = max(0, $new_margin);
22624 }
22625 if ($x < $new_margin) {
22626 // adjust x position
22627 $x = $new_margin;
22628 if ($new_margin > ($this->w - $this->rMargin)) {
22629 // adjust y position
22630 $y = $regdata['yb'] - $h;
22631 }
22632 }
22633 }
22634 } elseif ($regdata['side'] == 'R') { // right side
22635 $new_margin = min($xt, $xb);
22636 if (($this->w - $this->rMargin) > $new_margin) {
22637 if (!$this->rtl) {
22638 // adjust right page margin
22639 $this->rMargin = max(0, ($this->w - $new_margin));
22640 }
22641 if ($x > $new_margin) {
22642 // adjust x position
22643 $x = $new_margin;
22644 if ($new_margin > $this->lMargin) {
22645 // adjust y position
22646 $y = $regdata['yb'] - $h;
22647 }
22648 }
22649 }
22650 }
22651 }
22652 }
22653 }
22654 return array($x, $y);
22655 }
22656
22657 // --- SVG METHODS ---------------------------------------------------------
22658
22676 public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
22677 if ($this->state != 2) {
22678 return;
22679 }
22680 // reset SVG vars
22681 $this->svggradients = array();
22682 $this->svggradientid = 0;
22683 $this->svgdefsmode = false;
22684 $this->svgdefs = array();
22685 $this->svgclipmode = false;
22686 $this->svgclippaths = array();
22687 $this->svgcliptm = array();
22688 $this->svgclipid = 0;
22689 $this->svgtext = '';
22690 $this->svgtextmode = array();
22691 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
22692 // convert SVG to raster image using GD or ImageMagick libraries
22693 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22694 }
22695 if ($file[0] === '@') { // image from string
22696 $this->svgdir = '';
22697 $svgdata = substr($file, 1);
22698 } else { // SVG file
22699 $this->svgdir = dirname($file);
22701 }
22702 if ($svgdata === FALSE) {
22703 $this->Error('SVG file not found: '.$file);
22704 }
22705 if ($x === '') {
22706 $x = $this->x;
22707 }
22708 if ($y === '') {
22709 $y = $this->y;
22710 }
22711 // check page for no-write regions and adapt page margins if necessary
22712 list($x, $y) = $this->checkPageRegions($h, $x, $y);
22713 $k = $this->k;
22714 $ox = 0;
22715 $oy = 0;
22716 $ow = $w;
22717 $oh = $h;
22718 $aspect_ratio_align = 'xMidYMid';
22719 $aspect_ratio_ms = 'meet';
22720 $regs = array();
22721 // get original image width and height
22722 preg_match('/<svg([^>]*)>/si', $svgdata, $regs);
22723 if (isset($regs[1]) AND !empty($regs[1])) {
22724 $tmp = array();
22725 if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22726 $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22727 }
22728 $tmp = array();
22729 if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22730 $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22731 }
22732 $tmp = array();
22733 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22734 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22735 }
22736 $tmp = array();
22737 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22738 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22739 }
22740 $tmp = array();
22741 $view_box = array();
22742 if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
22743 if (count($tmp) == 5) {
22744 array_shift($tmp);
22745 foreach ($tmp as $key => $val) {
22746 $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
22747 }
22748 $ox = $view_box[0];
22749 $oy = $view_box[1];
22750 }
22751 // get aspect ratio
22752 $tmp = array();
22753 if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22754 $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
22755 switch (count($aspect_ratio)) {
22756 case 3: {
22757 $aspect_ratio_align = $aspect_ratio[1];
22758 $aspect_ratio_ms = $aspect_ratio[2];
22759 break;
22760 }
22761 case 2: {
22762 $aspect_ratio_align = $aspect_ratio[0];
22763 $aspect_ratio_ms = $aspect_ratio[1];
22764 break;
22765 }
22766 case 1: {
22767 $aspect_ratio_align = $aspect_ratio[0];
22768 $aspect_ratio_ms = 'meet';
22769 break;
22770 }
22771 }
22772 }
22773 }
22774 }
22775 if ($ow <= 0) {
22776 $ow = 1;
22777 }
22778 if ($oh <= 0) {
22779 $oh = 1;
22780 }
22781 // calculate image width and height on document
22782 if (($w <= 0) AND ($h <= 0)) {
22783 // convert image size to document unit
22784 $w = $ow;
22785 $h = $oh;
22786 } elseif ($w <= 0) {
22787 $w = $h * $ow / $oh;
22788 } elseif ($h <= 0) {
22789 $h = $w * $oh / $ow;
22790 }
22791 // fit the image on available space
22792 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22793 if ($this->rasterize_vector_images) {
22794 // convert SVG to raster image using GD or ImageMagick libraries
22795 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22796 }
22797 // set alignment
22798 $this->img_rb_y = $y + $h;
22799 // set alignment
22800 if ($this->rtl) {
22801 if ($palign == 'L') {
22802 $ximg = $this->lMargin;
22803 } elseif ($palign == 'C') {
22804 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22805 } elseif ($palign == 'R') {
22806 $ximg = $this->w - $this->rMargin - $w;
22807 } else {
22808 $ximg = $x - $w;
22809 }
22810 $this->img_rb_x = $ximg;
22811 } else {
22812 if ($palign == 'L') {
22813 $ximg = $this->lMargin;
22814 } elseif ($palign == 'C') {
22815 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22816 } elseif ($palign == 'R') {
22817 $ximg = $this->w - $this->rMargin - $w;
22818 } else {
22819 $ximg = $x;
22820 }
22821 $this->img_rb_x = $ximg + $w;
22822 }
22823 // store current graphic vars
22824 $gvars = $this->getGraphicVars();
22825 // store SVG position and scale factors
22826 $svgoffset_x = ($ximg - $ox) * $this->k;
22827 $svgoffset_y = -($y - $oy) * $this->k;
22828 if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
22829 $ow = $view_box[2];
22830 $oh = $view_box[3];
22831 } else {
22832 if ($ow <= 0) {
22833 $ow = $w;
22834 }
22835 if ($oh <= 0) {
22836 $oh = $h;
22837 }
22838 }
22839 $svgscale_x = $w / $ow;
22840 $svgscale_y = $h / $oh;
22841 // scaling and alignment
22842 if ($aspect_ratio_align != 'none') {
22843 // store current scaling values
22844 $svgscale_old_x = $svgscale_x;
22845 $svgscale_old_y = $svgscale_y;
22846 // force uniform scaling
22847 if ($aspect_ratio_ms == 'slice') {
22848 // the entire viewport is covered by the viewBox
22849 if ($svgscale_x > $svgscale_y) {
22850 $svgscale_y = $svgscale_x;
22851 } elseif ($svgscale_x < $svgscale_y) {
22852 $svgscale_x = $svgscale_y;
22853 }
22854 } else { // meet
22855 // the entire viewBox is visible within the viewport
22856 if ($svgscale_x < $svgscale_y) {
22857 $svgscale_y = $svgscale_x;
22858 } elseif ($svgscale_x > $svgscale_y) {
22859 $svgscale_x = $svgscale_y;
22860 }
22861 }
22862 // correct X alignment
22863 switch (substr($aspect_ratio_align, 1, 3)) {
22864 case 'Min': {
22865 // do nothing
22866 break;
22867 }
22868 case 'Max': {
22869 $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
22870 break;
22871 }
22872 default:
22873 case 'Mid': {
22874 $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
22875 break;
22876 }
22877 }
22878 // correct Y alignment
22879 switch (substr($aspect_ratio_align, 5)) {
22880 case 'Min': {
22881 // do nothing
22882 break;
22883 }
22884 case 'Max': {
22885 $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
22886 break;
22887 }
22888 default:
22889 case 'Mid': {
22890 $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
22891 break;
22892 }
22893 }
22894 }
22895 // store current page break mode
22896 $page_break_mode = $this->AutoPageBreak;
22897 $page_break_margin = $this->getBreakMargin();
22898 $cell_padding = $this->cell_padding;
22899 $this->SetCellPadding(0);
22900 $this->SetAutoPageBreak(false);
22901 // save the current graphic state
22902 $this->_out('q'.$this->epsmarker);
22903 // set initial clipping mask
22904 $this->Rect($ximg, $y, $w, $h, 'CNZ', array(), array());
22905 // scale and translate
22906 $e = $ox * $this->k * (1 - $svgscale_x);
22907 $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
22908 $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y)));
22909 // creates a new XML parser to be used by the other XML functions
22910 $this->parser = xml_parser_create('UTF-8');
22911 // the following function allows to use parser inside object
22912 xml_set_object($this->parser, $this);
22913 // disable case-folding for this XML parser
22914 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
22915 // sets the element handler functions for the XML parser
22916 xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler');
22917 // sets the character data handler function for the XML parser
22918 xml_set_character_data_handler($this->parser, 'segSVGContentHandler');
22919 // start parsing an XML document
22920 if (!xml_parse($this->parser, $svgdata)) {
22921 $error_message = sprintf('SVG Error: %s at line %d', xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser));
22922 $this->Error($error_message);
22923 }
22924 // free this XML parser
22925 xml_parser_free($this->parser);
22926 // restore previous graphic state
22927 $this->_out($this->epsmarker.'Q');
22928 // restore graphic vars
22929 $this->setGraphicVars($gvars);
22930 $this->lasth = $gvars['lasth'];
22931 if (!empty($border)) {
22932 $bx = $this->x;
22933 $by = $this->y;
22934 $this->x = $ximg;
22935 if ($this->rtl) {
22936 $this->x += $w;
22937 }
22938 $this->y = $y;
22939 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
22940 $this->x = $bx;
22941 $this->y = $by;
22942 }
22943 if ($link) {
22944 $this->Link($ximg, $y, $w, $h, $link, 0);
22945 }
22946 // set pointer to align the next text/objects
22947 switch($align) {
22948 case 'T':{
22949 $this->y = $y;
22950 $this->x = $this->img_rb_x;
22951 break;
22952 }
22953 case 'M':{
22954 $this->y = $y + round($h/2);
22955 $this->x = $this->img_rb_x;
22956 break;
22957 }
22958 case 'B':{
22959 $this->y = $this->img_rb_y;
22960 $this->x = $this->img_rb_x;
22961 break;
22962 }
22963 case 'N':{
22964 $this->SetY($this->img_rb_y);
22965 break;
22966 }
22967 default:{
22968 // restore pointer to starting position
22969 $this->x = $gvars['x'];
22970 $this->y = $gvars['y'];
22971 $this->page = $gvars['page'];
22972 $this->current_column = $gvars['current_column'];
22973 $this->tMargin = $gvars['tMargin'];
22974 $this->bMargin = $gvars['bMargin'];
22975 $this->w = $gvars['w'];
22976 $this->h = $gvars['h'];
22977 $this->wPt = $gvars['wPt'];
22978 $this->hPt = $gvars['hPt'];
22979 $this->fwPt = $gvars['fwPt'];
22980 $this->fhPt = $gvars['fhPt'];
22981 break;
22982 }
22983 }
22984 $this->endlinex = $this->img_rb_x;
22985 // restore page break
22986 $this->SetAutoPageBreak($page_break_mode, $page_break_margin);
22987 $this->cell_padding = $cell_padding;
22988 }
22989
22997 protected function convertSVGtMatrix($tm) {
22998 $a = $tm[0];
22999 $b = -$tm[1];
23000 $c = -$tm[2];
23001 $d = $tm[3];
23002 $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
23003 $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
23004 $x = 0;
23005 $y = $this->h * $this->k;
23006 $e = ($x * (1 - $a)) - ($y * $c) + $e;
23007 $f = ($y * (1 - $d)) - ($x * $b) + $f;
23008 return array($a, $b, $c, $d, $e, $f);
23009 }
23010
23017 protected function SVGTransform($tm) {
23018 $this->Transform($this->convertSVGtMatrix($tm));
23019 }
23020
23036 protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
23037 if ($this->state != 2) {
23038 return;
23039 }
23040 $objstyle = '';
23041 $minlen = (0.01 / $this->k); // minimum acceptable length
23042 if (!isset($svgstyle['opacity'])) {
23043 return $objstyle;
23044 }
23045 // clip-path
23046 $regs = array();
23047 if (preg_match('/url\‍([\s]*\#([^\‍)]*)\‍)/si', $svgstyle['clip-path'], $regs)) {
23048 $clip_path = $this->svgclippaths[$regs[1]];
23049 foreach ($clip_path as $cp) {
23050 $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
23051 }
23052 }
23053 // opacity
23054 if ($svgstyle['opacity'] != 1) {
23055 $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false);
23056 }
23057 // color
23058 $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors);
23059 $this->SetFillColorArray($fill_color);
23060 // text color
23061 $text_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors);
23062 $this->SetTextColorArray($text_color);
23063 // clip
23064 if (preg_match('/rect\‍(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\‍)/si', $svgstyle['clip'], $regs)) {
23065 $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
23066 $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
23067 $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
23068 $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
23069 $cx = $x + $left;
23070 $cy = $y + $top;
23071 $cw = $w - $left - $right;
23072 $ch = $h - $top - $bottom;
23073 if ($svgstyle['clip-rule'] == 'evenodd') {
23074 $clip_rule = 'CNZ';
23075 } else {
23076 $clip_rule = 'CEO';
23077 }
23078 $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
23079 }
23080 // fill
23081 $regs = array();
23082 if (preg_match('/url\‍([\s]*\#([^\‍)]*)\‍)/si', $svgstyle['fill'], $regs)) {
23083 // gradient
23084 $gradient = $this->svggradients[$regs[1]];
23085 if (isset($gradient['xref'])) {
23086 // reference to another gradient definition
23087 $newgradient = $this->svggradients[$gradient['xref']];
23088 $newgradient['coords'] = $gradient['coords'];
23089 $newgradient['mode'] = $gradient['mode'];
23090 $newgradient['type'] = $gradient['type'];
23091 $newgradient['gradientUnits'] = $gradient['gradientUnits'];
23092 if (isset($gradient['gradientTransform'])) {
23093 $newgradient['gradientTransform'] = $gradient['gradientTransform'];
23094 }
23095 $gradient = $newgradient;
23096 }
23097 //save current Graphic State
23098 $this->_outSaveGraphicsState();
23099 //set clipping area
23100 if (!empty($clip_function) AND method_exists($this, $clip_function)) {
23101 $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
23102 if ((!isset($gradient['type']) OR ($gradient['type'] != 3)) AND is_array($bbox) AND (count($bbox) == 4)) {
23103 list($x, $y, $w, $h) = $bbox;
23104 }
23105 }
23106 if ($gradient['mode'] == 'measure') {
23107 if (!isset($gradient['coords'][4])) {
23108 $gradient['coords'][4] = 0.5;
23109 }
23110 if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
23111 $gtm = $gradient['gradientTransform'];
23112 // apply transformation matrix
23113 $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
23114 $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
23115 $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
23116 $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
23117 $r = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
23118 $gradient['coords'][0] = $xa;
23119 $gradient['coords'][1] = $ya;
23120 $gradient['coords'][2] = $xb;
23121 $gradient['coords'][3] = $yb;
23122 $gradient['coords'][4] = $r;
23123 }
23124 // convert SVG coordinates to user units
23125 $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
23126 $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
23127 $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
23128 $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
23129 $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
23130 if ($w <= $minlen) {
23131 $w = $minlen;
23132 }
23133 if ($h <= $minlen) {
23134 $h = $minlen;
23135 }
23136 // shift units
23137 if ($gradient['gradientUnits'] == 'objectBoundingBox') {
23138 // convert to SVG coordinate system
23139 $gradient['coords'][0] += $x;
23140 $gradient['coords'][1] += $y;
23141 $gradient['coords'][2] += $x;
23142 $gradient['coords'][3] += $y;
23143 }
23144 // calculate percentages
23145 $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w);
23146 $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h);
23147 $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w);
23148 $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h);
23149 $gradient['coords'][4] /= $w;
23150 } elseif ($gradient['mode'] == 'percentage') {
23151 foreach($gradient['coords'] as $key => $val) {
23152 $gradient['coords'][$key] = (intval($val) / 100);
23153 if ($val < 0) {
23154 $gradient['coords'][$key] = 0;
23155 } elseif ($val > 1) {
23156 $gradient['coords'][$key] = 1;
23157 }
23158 }
23159 }
23160 if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
23161 // single color (no shading)
23162 $gradient['coords'][0] = 1;
23163 $gradient['coords'][1] = 0;
23164 $gradient['coords'][2] = 0.999;
23165 $gradient['coords'][3] = 0;
23166 }
23167 // swap Y coordinates
23168 $tmp = $gradient['coords'][1];
23169 $gradient['coords'][1] = $gradient['coords'][3];
23170 $gradient['coords'][3] = $tmp;
23171 // set transformation map for gradient
23172 $cy = ($this->h - $y);
23173 if ($gradient['type'] == 3) {
23174 // circular gradient
23175 $cy -= ($gradient['coords'][1] * ($w + $h));
23176 $h = $w = max($w, $h);
23177 } else {
23178 $cy -= $h;
23179 }
23180 $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), ($cy * $this->k)));
23181 if (count($gradient['stops']) > 1) {
23182 $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
23183 }
23184 } elseif ($svgstyle['fill'] != 'none') {
23185 $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors);
23186 if ($svgstyle['fill-opacity'] != 1) {
23187 $this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false);
23188 }
23189 $this->SetFillColorArray($fill_color);
23190 if ($svgstyle['fill-rule'] == 'evenodd') {
23191 $objstyle .= 'F*';
23192 } else {
23193 $objstyle .= 'F';
23194 }
23195 }
23196 // stroke
23197 if ($svgstyle['stroke'] != 'none') {
23198 if ($svgstyle['stroke-opacity'] != 1) {
23199 $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false);
23200 } elseif (preg_match('/rgba\‍(\d+%?,\s*\d+%?,\s*\d+%?,\s*(\d+(?:\.\d+)?)\‍)/i', $svgstyle['stroke'], $rgba_matches)) {
23201 $this->setAlpha($rgba_matches[1], 'Normal', $this->alpha['ca'], false);
23202 }
23203 $stroke_style = array(
23204 'color' => TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors),
23205 'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
23206 'cap' => $svgstyle['stroke-linecap'],
23207 'join' => $svgstyle['stroke-linejoin']
23208 );
23209 if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
23210 $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
23211 }
23212 $this->SetLineStyle($stroke_style);
23213 $objstyle .= 'D';
23214 }
23215 // font
23216 $regs = array();
23217 if (!empty($svgstyle['font'])) {
23218 if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
23219 $font_family = $this->getFontFamilyName($regs[1]);
23220 } else {
23221 $font_family = $svgstyle['font-family'];
23222 }
23223 if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23224 $font_size = trim($regs[1]);
23225 } else {
23226 $font_size = $svgstyle['font-size'];
23227 }
23228 if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23229 $font_style = trim($regs[1]);
23230 } else {
23231 $font_style = $svgstyle['font-style'];
23232 }
23233 if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23234 $font_weight = trim($regs[1]);
23235 } else {
23236 $font_weight = $svgstyle['font-weight'];
23237 }
23238 if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23239 $font_stretch = trim($regs[1]);
23240 } else {
23241 $font_stretch = $svgstyle['font-stretch'];
23242 }
23243 if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23244 $font_spacing = trim($regs[1]);
23245 } else {
23246 $font_spacing = $svgstyle['letter-spacing'];
23247 }
23248 } else {
23249 $font_family = $this->getFontFamilyName($svgstyle['font-family']);
23250 $font_size = $svgstyle['font-size'];
23251 $font_style = $svgstyle['font-style'];
23252 $font_weight = $svgstyle['font-weight'];
23253 $font_stretch = $svgstyle['font-stretch'];
23254 $font_spacing = $svgstyle['letter-spacing'];
23255 }
23256 $font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit);
23257 $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
23258 $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
23259 switch ($font_style) {
23260 case 'italic': {
23261 $font_style = 'I';
23262 break;
23263 }
23264 case 'oblique': {
23265 $font_style = 'I';
23266 break;
23267 }
23268 default:
23269 case 'normal': {
23270 $font_style = '';
23271 break;
23272 }
23273 }
23274 switch ($font_weight) {
23275 case 'bold':
23276 case 'bolder': {
23277 $font_style .= 'B';
23278 break;
23279 }
23280 case 'normal': {
23281 if ((substr($font_family, -1) == 'I') AND (substr($font_family, -2, 1) == 'B')) {
23282 $font_family = substr($font_family, 0, -2).'I';
23283 } elseif (substr($font_family, -1) == 'B') {
23284 $font_family = substr($font_family, 0, -1);
23285 }
23286 break;
23287 }
23288 }
23289 switch ($svgstyle['text-decoration']) {
23290 case 'underline': {
23291 $font_style .= 'U';
23292 break;
23293 }
23294 case 'overline': {
23295 $font_style .= 'O';
23296 break;
23297 }
23298 case 'line-through': {
23299 $font_style .= 'D';
23300 break;
23301 }
23302 default:
23303 case 'none': {
23304 break;
23305 }
23306 }
23307 $this->SetFont($font_family, $font_style, $font_size);
23308 $this->setFontStretching($font_stretch);
23309 $this->setFontSpacing($font_spacing);
23310 return $objstyle;
23311 }
23312
23331 protected function SVGPath($d, $style='') {
23332 if ($this->state != 2) {
23333 return;
23334 }
23335 // set fill/stroke style
23337 if (empty($op)) {
23338 return;
23339 }
23340 $paths = array();
23341 $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
23342 preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER);
23343 $x = 0;
23344 $y = 0;
23345 $x1 = 0;
23346 $y1 = 0;
23347 $x2 = 0;
23348 $y2 = 0;
23349 $xmin = 2147483647;
23350 $xmax = 0;
23351 $ymin = 2147483647;
23352 $ymax = 0;
23353 $relcoord = false;
23354 $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
23355 $firstcmd = true; // used to print first point
23356 // draw curve pieces
23357 foreach ($paths as $key => $val) {
23358 // get curve type
23359 $cmd = trim($val[1]);
23360 if (strtolower($cmd) == $cmd) {
23361 // use relative coordinated instead of absolute
23362 $relcoord = true;
23363 $xoffset = $x;
23364 $yoffset = $y;
23365 } else {
23366 $relcoord = false;
23367 $xoffset = 0;
23368 $yoffset = 0;
23369 }
23370 $params = array();
23371 if (isset($val[2])) {
23372 // get curve parameters
23373 $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
23374 $params = array();
23375 foreach ($rawparams as $ck => $cp) {
23376 $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
23377 if (abs($params[$ck]) < $minlen) {
23378 // approximate little values to zero
23379 $params[$ck] = 0;
23380 }
23381 }
23382 }
23383 // store current origin point
23384 $x0 = $x;
23385 $y0 = $y;
23386 switch (strtoupper($cmd)) {
23387 case 'M': { // moveto
23388 foreach ($params as $ck => $cp) {
23389 if (($ck % 2) == 0) {
23390 $x = $cp + $xoffset;
23391 } else {
23392 $y = $cp + $yoffset;
23393 if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23394 if ($ck == 1) {
23395 $this->_outPoint($x, $y);
23396 $firstcmd = false;
23397 } else {
23398 $this->_outLine($x, $y);
23399 }
23400 $x0 = $x;
23401 $y0 = $y;
23402 }
23403 $xmin = min($xmin, $x);
23404 $ymin = min($ymin, $y);
23405 $xmax = max($xmax, $x);
23406 $ymax = max($ymax, $y);
23407 if ($relcoord) {
23408 $xoffset = $x;
23409 $yoffset = $y;
23410 }
23411 }
23412 }
23413 break;
23414 }
23415 case 'L': { // lineto
23416 foreach ($params as $ck => $cp) {
23417 if (($ck % 2) == 0) {
23418 $x = $cp + $xoffset;
23419 } else {
23420 $y = $cp + $yoffset;
23421 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23422 $this->_outLine($x, $y);
23423 $x0 = $x;
23424 $y0 = $y;
23425 }
23426 $xmin = min($xmin, $x);
23427 $ymin = min($ymin, $y);
23428 $xmax = max($xmax, $x);
23429 $ymax = max($ymax, $y);
23430 if ($relcoord) {
23431 $xoffset = $x;
23432 $yoffset = $y;
23433 }
23434 }
23435 }
23436 break;
23437 }
23438 case 'H': { // horizontal lineto
23439 foreach ($params as $ck => $cp) {
23440 $x = $cp + $xoffset;
23441 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23442 $this->_outLine($x, $y);
23443 $x0 = $x;
23444 $y0 = $y;
23445 }
23446 $xmin = min($xmin, $x);
23447 $xmax = max($xmax, $x);
23448 if ($relcoord) {
23449 $xoffset = $x;
23450 }
23451 }
23452 break;
23453 }
23454 case 'V': { // vertical lineto
23455 foreach ($params as $ck => $cp) {
23456 $y = $cp + $yoffset;
23457 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23458 $this->_outLine($x, $y);
23459 $x0 = $x;
23460 $y0 = $y;
23461 }
23462 $ymin = min($ymin, $y);
23463 $ymax = max($ymax, $y);
23464 if ($relcoord) {
23465 $yoffset = $y;
23466 }
23467 }
23468 break;
23469 }
23470 case 'C': { // curveto
23471 foreach ($params as $ck => $cp) {
23472 $params[$ck] = $cp;
23473 if ((($ck + 1) % 6) == 0) {
23474 $x1 = $params[($ck - 5)] + $xoffset;
23475 $y1 = $params[($ck - 4)] + $yoffset;
23476 $x2 = $params[($ck - 3)] + $xoffset;
23477 $y2 = $params[($ck - 2)] + $yoffset;
23478 $x = $params[($ck - 1)] + $xoffset;
23479 $y = $params[($ck)] + $yoffset;
23480 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23481 $xmin = min($xmin, $x, $x1, $x2);
23482 $ymin = min($ymin, $y, $y1, $y2);
23483 $xmax = max($xmax, $x, $x1, $x2);
23484 $ymax = max($ymax, $y, $y1, $y2);
23485 if ($relcoord) {
23486 $xoffset = $x;
23487 $yoffset = $y;
23488 }
23489 }
23490 }
23491 break;
23492 }
23493 case 'S': { // shorthand/smooth curveto
23494 foreach ($params as $ck => $cp) {
23495 $params[$ck] = $cp;
23496 if ((($ck + 1) % 4) == 0) {
23497 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
23498 $x1 = (2 * $x) - $x2;
23499 $y1 = (2 * $y) - $y2;
23500 } else {
23501 $x1 = $x;
23502 $y1 = $y;
23503 }
23504 $x2 = $params[($ck - 3)] + $xoffset;
23505 $y2 = $params[($ck - 2)] + $yoffset;
23506 $x = $params[($ck - 1)] + $xoffset;
23507 $y = $params[($ck)] + $yoffset;
23508 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23509 $xmin = min($xmin, $x, $x1, $x2);
23510 $ymin = min($ymin, $y, $y1, $y2);
23511 $xmax = max($xmax, $x, $x1, $x2);
23512 $ymax = max($ymax, $y, $y1, $y2);
23513 if ($relcoord) {
23514 $xoffset = $x;
23515 $yoffset = $y;
23516 }
23517 }
23518 }
23519 break;
23520 }
23521 case 'Q': { // quadratic Bezier curveto
23522 foreach ($params as $ck => $cp) {
23523 $params[$ck] = $cp;
23524 if ((($ck + 1) % 4) == 0) {
23525 // convert quadratic points to cubic points
23526 $x1 = $params[($ck - 3)] + $xoffset;
23527 $y1 = $params[($ck - 2)] + $yoffset;
23528 $xa = ($x + (2 * $x1)) / 3;
23529 $ya = ($y + (2 * $y1)) / 3;
23530 $x = $params[($ck - 1)] + $xoffset;
23531 $y = $params[($ck)] + $yoffset;
23532 $xb = ($x + (2 * $x1)) / 3;
23533 $yb = ($y + (2 * $y1)) / 3;
23534 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23535 $xmin = min($xmin, $x, $xa, $xb);
23536 $ymin = min($ymin, $y, $ya, $yb);
23537 $xmax = max($xmax, $x, $xa, $xb);
23538 $ymax = max($ymax, $y, $ya, $yb);
23539 if ($relcoord) {
23540 $xoffset = $x;
23541 $yoffset = $y;
23542 }
23543 }
23544 }
23545 break;
23546 }
23547 case 'T': { // shorthand/smooth quadratic Bezier curveto
23548 foreach ($params as $ck => $cp) {
23549 $params[$ck] = $cp;
23550 if (($ck % 2) != 0) {
23551 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
23552 $x1 = (2 * $x) - $x1;
23553 $y1 = (2 * $y) - $y1;
23554 } else {
23555 $x1 = $x;
23556 $y1 = $y;
23557 }
23558 // convert quadratic points to cubic points
23559 $xa = ($x + (2 * $x1)) / 3;
23560 $ya = ($y + (2 * $y1)) / 3;
23561 $x = $params[($ck - 1)] + $xoffset;
23562 $y = $params[($ck)] + $yoffset;
23563 $xb = ($x + (2 * $x1)) / 3;
23564 $yb = ($y + (2 * $y1)) / 3;
23565 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23566 $xmin = min($xmin, $x, $xa, $xb);
23567 $ymin = min($ymin, $y, $ya, $yb);
23568 $xmax = max($xmax, $x, $xa, $xb);
23569 $ymax = max($ymax, $y, $ya, $yb);
23570 if ($relcoord) {
23571 $xoffset = $x;
23572 $yoffset = $y;
23573 }
23574 }
23575 }
23576 break;
23577 }
23578 case 'A': { // elliptical arc
23579 foreach ($params as $ck => $cp) {
23580 $params[$ck] = $cp;
23581 if ((($ck + 1) % 7) == 0) {
23582 $x0 = $x;
23583 $y0 = $y;
23584 $rx = abs($params[($ck - 6)]);
23585 $ry = abs($params[($ck - 5)]);
23586 $ang = -$rawparams[($ck - 4)];
23587 $angle = deg2rad($ang);
23588 $fa = $rawparams[($ck - 3)]; // large-arc-flag
23589 $fs = $rawparams[($ck - 2)]; // sweep-flag
23590 $x = $params[($ck - 1)] + $xoffset;
23591 $y = $params[$ck] + $yoffset;
23592 if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
23593 // endpoints are almost identical
23594 $xmin = min($xmin, $x);
23595 $ymin = min($ymin, $y);
23596 $xmax = max($xmax, $x);
23597 $ymax = max($ymax, $y);
23598 } else {
23599 $cos_ang = cos($angle);
23600 $sin_ang = sin($angle);
23601 $a = (($x0 - $x) / 2);
23602 $b = (($y0 - $y) / 2);
23603 $xa = ($a * $cos_ang) - ($b * $sin_ang);
23604 $ya = ($a * $sin_ang) + ($b * $cos_ang);
23605 $rx2 = $rx * $rx;
23606 $ry2 = $ry * $ry;
23607 $xa2 = $xa * $xa;
23608 $ya2 = $ya * $ya;
23609 $delta = ($xa2 / $rx2) + ($ya2 / $ry2);
23610 if ($delta > 1) {
23611 $rx *= sqrt($delta);
23612 $ry *= sqrt($delta);
23613 $rx2 = $rx * $rx;
23614 $ry2 = $ry * $ry;
23615 }
23616 $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
23617 if ($numerator < 0) {
23618 $root = 0;
23619 } else {
23620 $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
23621 }
23622 if ($fa == $fs){
23623 $root *= -1;
23624 }
23625 $cax = $root * (($rx * $ya) / $ry);
23626 $cay = -$root * (($ry * $xa) / $rx);
23627 // coordinates of ellipse center
23628 $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
23629 $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
23630 // get angles
23631 $angs = TCPDF_STATIC::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
23632 $dang = TCPDF_STATIC::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
23633 if (($fs == 0) AND ($dang > 0)) {
23634 $dang -= (2 * M_PI);
23635 } elseif (($fs == 1) AND ($dang < 0)) {
23636 $dang += (2 * M_PI);
23637 }
23638 $angf = $angs - $dang;
23639 if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
23640 // reverse angles
23641 $tmp = $angs;
23642 $angs = $angf;
23643 $angf = $tmp;
23644 }
23645 $angs = round(rad2deg($angs), 6);
23646 $angf = round(rad2deg($angf), 6);
23647 // covent angles to positive values
23648 if (($angs < 0) AND ($angf < 0)) {
23649 $angs += 360;
23650 $angf += 360;
23651 }
23652 $pie = false;
23653 if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
23654 $pie = true;
23655 }
23656 list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
23657 $xmin = min($xmin, $x, $axmin);
23658 $ymin = min($ymin, $y, $aymin);
23659 $xmax = max($xmax, $x, $axmax);
23660 $ymax = max($ymax, $y, $aymax);
23661 }
23662 if ($relcoord) {
23663 $xoffset = $x;
23664 $yoffset = $y;
23665 }
23666 }
23667 }
23668 break;
23669 }
23670 case 'Z': {
23671 $this->_out('h');
23672 break;
23673 }
23674 }
23675 $firstcmd = false;
23676 } // end foreach
23677 if (!empty($op)) {
23678 $this->_out($op);
23679 }
23680 return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
23681 }
23682
23688 protected function removeTagNamespace($name) {
23689 if(strpos($name, ':') !== false) {
23690 $parts = explode(':', $name);
23691 return $parts[(sizeof($parts) - 1)];
23692 }
23693 return $name;
23694 }
23695
23706 protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
23707 $name = $this->removeTagNamespace($name);
23708 // check if we are in clip mode
23709 if ($this->svgclipmode) {
23710 $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]);
23711 return;
23712 }
23713 if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
23714 if (isset($attribs['id'])) {
23715 $attribs['child_elements'] = array();
23716 $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23717 return;
23718 }
23719 if (end($this->svgdefs) !== FALSE) {
23720 $last_svgdefs_id = key($this->svgdefs);
23721 if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
23722 $attribs['id'] = 'DF_'.(count($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements']) + 1);
23723 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23724 return;
23725 }
23726 }
23727 return;
23728 }
23729 $clipping = false;
23730 if ($parser == 'clip-path') {
23731 // set clipping mode
23732 $clipping = true;
23733 }
23734 // get styling properties
23735 $prev_svgstyle = $this->svgstyles[max(0,(count($this->svgstyles) - 1))]; // previous style
23736 $svgstyle = $this->svgstyles[0]; // set default style
23737 if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) {
23738 // default fill attribute for clipping
23739 $attribs['fill'] = 'none';
23740 }
23741 if (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) {
23742 // fix style for regular expression
23743 $attribs['style'] = ';'.$attribs['style'];
23744 }
23745 foreach ($prev_svgstyle as $key => $val) {
23746 if (in_array($key, TCPDF_IMAGES::$svginheritprop)) {
23747 // inherit previous value
23748 $svgstyle[$key] = $val;
23749 }
23750 if (isset($attribs[$key]) AND !TCPDF_STATIC::empty_string($attribs[$key])) {
23751 // specific attribute settings
23752 if ($attribs[$key] == 'inherit') {
23753 $svgstyle[$key] = $val;
23754 } else {
23755 $svgstyle[$key] = $attribs[$key];
23756 }
23757 } elseif (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) {
23758 // CSS style syntax
23759 $attrval = array();
23760 if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
23761 if ($attrval[1] == 'inherit') {
23762 $svgstyle[$key] = $val;
23763 } else {
23764 $svgstyle[$key] = $attrval[1];
23765 }
23766 }
23767 }
23768 }
23769 // transformation matrix
23770 if (!empty($ctm)) {
23771 $tm = $ctm;
23772 } else {
23773 $tm = array(1,0,0,1,0,0);
23774 }
23775 if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
23777 }
23778 $svgstyle['transfmatrix'] = $tm;
23779 $invisible = false;
23780 if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
23781 // the current graphics element is invisible (nothing is painted)
23782 $invisible = true;
23783 }
23784 // process tag
23785 switch($name) {
23786 case 'defs': {
23787 $this->svgdefsmode = true;
23788 break;
23789 }
23790 // clipPath
23791 case 'clipPath': {
23792 if ($invisible) {
23793 break;
23794 }
23795 $this->svgclipmode = true;
23796 if (!isset($attribs['id'])) {
23797 $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1);
23798 }
23799 $this->svgclipid = $attribs['id'];
23800 $this->svgclippaths[$this->svgclipid] = array();
23801 $this->svgcliptm[$this->svgclipid] = $tm;
23802 break;
23803 }
23804 case 'svg': {
23805 // start of SVG object
23806 if(++$this->svg_tag_depth <= 1) {
23807 break;
23808 }
23809 // inner SVG
23810 array_push($this->svgstyles, $svgstyle);
23811 $this->StartTransform();
23812 $svgX = (isset($attribs['x'])?$attribs['x']:0);
23813 $svgY = (isset($attribs['y'])?$attribs['y']:0);
23814 $svgW = (isset($attribs['width'])?$attribs['width']:0);
23815 $svgH = (isset($attribs['height'])?$attribs['height']:0);
23816 // set x, y position using transform matrix
23817 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array( 1, 0, 0, 1, $svgX, $svgY));
23818 $this->SVGTransform($tm);
23819 // set clipping for width and height
23820 $x = 0;
23821 $y = 0;
23822 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):$this->w);
23823 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):$this->h);
23824 // draw clipping rect
23825 $this->Rect($x, $y, $w, $h, 'CNZ', array(), array());
23826 // parse viewbox, calculate extra transformation matrix
23827 if (isset($attribs['viewBox'])) {
23828 $tmp = array();
23829 preg_match_all("/[0-9]+/", $attribs['viewBox'], $tmp);
23830 $tmp = $tmp[0];
23831 if (sizeof($tmp) == 4) {
23832 $vx = $tmp[0];
23833 $vy = $tmp[1];
23834 $vw = $tmp[2];
23835 $vh = $tmp[3];
23836 // get aspect ratio
23837 $tmp = array();
23838 $aspectX = 'xMid';
23839 $aspectY = 'YMid';
23840 $fit = 'meet';
23841 if (isset($attribs['preserveAspectRatio'])) {
23842 if($attribs['preserveAspectRatio'] == 'none') {
23843 $fit = 'none';
23844 } else {
23845 preg_match_all('/[a-zA-Z]+/', $attribs['preserveAspectRatio'], $tmp);
23846 $tmp = $tmp[0];
23847 if ((sizeof($tmp) == 2) AND (strlen($tmp[0]) == 8) AND (in_array($tmp[1], array('meet', 'slice', 'none')))) {
23848 $aspectX = substr($tmp[0], 0, 4);
23849 $aspectY = substr($tmp[0], 4, 4);
23850 $fit = $tmp[1];
23851 }
23852 }
23853 }
23854 $wr = ($svgW / $vw);
23855 $hr = ($svgH / $vh);
23856 $ax = $ay = 0;
23857 if ((($fit == 'meet') AND ($hr < $wr)) OR (($fit == 'slice') AND ($hr > $wr))) {
23858 if ($aspectX == 'xMax') {
23859 $ax = (($vw * ($wr / $hr)) - $vw);
23860 }
23861 if ($aspectX == 'xMid') {
23862 $ax = ((($vw * ($wr / $hr)) - $vw) / 2);
23863 }
23864 $wr = $hr;
23865 } elseif ((($fit == 'meet') AND ($hr > $wr)) OR (($fit == 'slice') AND ($hr < $wr))) {
23866 if ($aspectY == 'YMax') {
23867 $ay = (($vh * ($hr / $wr)) - $vh);
23868 }
23869 if ($aspectY == 'YMid') {
23870 $ay = ((($vh * ($hr / $wr)) - $vh) / 2);
23871 }
23872 $hr = $wr;
23873 }
23874 $newtm = array($wr, 0, 0, $hr, (($wr * ($ax - $vx)) - $svgX), (($hr * ($ay - $vy)) - $svgY));
23876 $this->SVGTransform($tm);
23877 }
23878 }
23879 $this->setSVGStyles($svgstyle, $prev_svgstyle);
23880 break;
23881 }
23882 case 'g': {
23883 // group together related graphics elements
23884 array_push($this->svgstyles, $svgstyle);
23885 $this->StartTransform();
23886 $x = (isset($attribs['x'])?$attribs['x']:0);
23887 $y = (isset($attribs['y'])?$attribs['y']:0);
23888 $w = 1;//(isset($attribs['width'])?$attribs['width']:1);
23889 $h = 1;//(isset($attribs['height'])?$attribs['height']:1);
23890 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
23891 $this->SVGTransform($tm);
23892 $this->setSVGStyles($svgstyle, $prev_svgstyle);
23893 break;
23894 }
23895 case 'linearGradient': {
23896 if ($this->pdfa_mode) {
23897 break;
23898 }
23899 if (!isset($attribs['id'])) {
23900 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
23901 }
23902 $this->svggradientid = $attribs['id'];
23903 $this->svggradients[$this->svggradientid] = array();
23904 $this->svggradients[$this->svggradientid]['type'] = 2;
23905 $this->svggradients[$this->svggradientid]['stops'] = array();
23906 if (isset($attribs['gradientUnits'])) {
23907 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
23908 } else {
23909 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
23910 }
23911 //$attribs['spreadMethod']
23912 if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2'])))
23913 OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%'))
23914 OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%'))
23915 OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%'))
23916 OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) {
23917 $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
23918 } else {
23919 $this->svggradients[$this->svggradientid]['mode'] = 'measure';
23920 }
23921 $x1 = (isset($attribs['x1'])?$attribs['x1']:'0');
23922 $y1 = (isset($attribs['y1'])?$attribs['y1']:'0');
23923 $x2 = (isset($attribs['x2'])?$attribs['x2']:'100');
23924 $y2 = (isset($attribs['y2'])?$attribs['y2']:'0');
23925 if (isset($attribs['gradientTransform'])) {
23926 $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
23927 }
23928 $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
23929 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23930 // gradient is defined on another place
23931 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
23932 }
23933 break;
23934 }
23935 case 'radialGradient': {
23936 if ($this->pdfa_mode) {
23937 break;
23938 }
23939 if (!isset($attribs['id'])) {
23940 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
23941 }
23942 $this->svggradientid = $attribs['id'];
23943 $this->svggradients[$this->svggradientid] = array();
23944 $this->svggradients[$this->svggradientid]['type'] = 3;
23945 $this->svggradients[$this->svggradientid]['stops'] = array();
23946 if (isset($attribs['gradientUnits'])) {
23947 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
23948 } else {
23949 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
23950 }
23951 //$attribs['spreadMethod']
23952 if (((!isset($attribs['cx'])) AND (!isset($attribs['cy'])))
23953 OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%'))
23954 OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')))) {
23955 $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
23956 } elseif (isset($attribs['r']) AND is_numeric($attribs['r']) AND ($attribs['r']) <= 1) {
23957 $this->svggradients[$this->svggradientid]['mode'] = 'ratio';
23958 } else {
23959 $this->svggradients[$this->svggradientid]['mode'] = 'measure';
23960 }
23961 $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5);
23962 $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5);
23963 $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx);
23964 $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy);
23965 $r = (isset($attribs['r']) ? $attribs['r'] : 0.5);
23966 if (isset($attribs['gradientTransform'])) {
23967 $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
23968 }
23969 $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
23970 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
23971 // gradient is defined on another place
23972 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
23973 }
23974 break;
23975 }
23976 case 'stop': {
23977 // gradient stops
23978 if (substr($attribs['offset'], -1) == '%') {
23979 $offset = floatval(substr($attribs['offset'], -1)) / 100;
23980 } else {
23981 $offset = floatval($attribs['offset']);
23982 if ($offset > 1) {
23983 $offset /= 100;
23984 }
23985 }
23986 $stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors):'black';
23987 $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
23988 $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
23989 break;
23990 }
23991 // paths
23992 case 'path': {
23993 if ($invisible) {
23994 break;
23995 }
23996 if (isset($attribs['d'])) {
23997 $d = trim($attribs['d']);
23998 if (!empty($d)) {
23999 $x = (isset($attribs['x'])?$attribs['x']:0);
24000 $y = (isset($attribs['y'])?$attribs['y']:0);
24001 $w = (isset($attribs['width'])?$attribs['width']:1);
24002 $h = (isset($attribs['height'])?$attribs['height']:1);
24003 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
24004 if ($clipping) {
24005 $this->SVGTransform($tm);
24006 $this->SVGPath($d, 'CNZ');
24007 } else {
24008 $this->StartTransform();
24009 $this->SVGTransform($tm);
24010 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ'));
24011 if (!empty($obstyle)) {
24012 $this->SVGPath($d, $obstyle);
24013 }
24014 $this->StopTransform();
24015 }
24016 }
24017 }
24018 break;
24019 }
24020 // shapes
24021 case 'rect': {
24022 if ($invisible) {
24023 break;
24024 }
24025 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24026 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24027 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24028 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24029 $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
24030 $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
24031 if ($clipping) {
24032 $this->SVGTransform($tm);
24033 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
24034 } else {
24035 $this->StartTransform();
24036 $this->SVGTransform($tm);
24037 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
24038 if (!empty($obstyle)) {
24039 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
24040 }
24041 $this->StopTransform();
24042 }
24043 break;
24044 }
24045 case 'circle': {
24046 if ($invisible) {
24047 break;
24048 }
24049 $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0);
24050 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
24051 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
24052 $x = ($cx - $r);
24053 $y = ($cy - $r);
24054 $w = (2 * $r);
24055 $h = $w;
24056 if ($clipping) {
24057 $this->SVGTransform($tm);
24058 $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
24059 } else {
24060 $this->StartTransform();
24061 $this->SVGTransform($tm);
24062 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
24063 if (!empty($obstyle)) {
24064 $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
24065 }
24066 $this->StopTransform();
24067 }
24068 break;
24069 }
24070 case 'ellipse': {
24071 if ($invisible) {
24072 break;
24073 }
24074 $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0);
24075 $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0);
24076 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
24077 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
24078 $x = ($cx - $rx);
24079 $y = ($cy - $ry);
24080 $w = (2 * $rx);
24081 $h = (2 * $ry);
24082 if ($clipping) {
24083 $this->SVGTransform($tm);
24084 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
24085 } else {
24086 $this->StartTransform();
24087 $this->SVGTransform($tm);
24088 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
24089 if (!empty($obstyle)) {
24090 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
24091 }
24092 $this->StopTransform();
24093 }
24094 break;
24095 }
24096 case 'line': {
24097 if ($invisible) {
24098 break;
24099 }
24100 $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
24101 $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
24102 $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
24103 $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
24104 $x = $x1;
24105 $y = $y1;
24106 $w = abs($x2 - $x1);
24107 $h = abs($y2 - $y1);
24108 if (!$clipping) {
24109 $this->StartTransform();
24110 $this->SVGTransform($tm);
24111 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
24112 $this->Line($x1, $y1, $x2, $y2);
24113 $this->StopTransform();
24114 }
24115 break;
24116 }
24117 case 'polyline':
24118 case 'polygon': {
24119 if ($invisible) {
24120 break;
24121 }
24122 $points = (isset($attribs['points'])?$attribs['points']:'0 0');
24123 $points = trim($points);
24124 // note that point may use a complex syntax not covered here
24125 $points = preg_split('/[\,\s]+/si', $points);
24126 if (count($points) < 4) {
24127 break;
24128 }
24129 $p = array();
24130 $xmin = 2147483647;
24131 $xmax = 0;
24132 $ymin = 2147483647;
24133 $ymax = 0;
24134 foreach ($points as $key => $val) {
24135 $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
24136 if (($key % 2) == 0) {
24137 // X coordinate
24138 $xmin = min($xmin, $p[$key]);
24139 $xmax = max($xmax, $p[$key]);
24140 } else {
24141 // Y coordinate
24142 $ymin = min($ymin, $p[$key]);
24143 $ymax = max($ymax, $p[$key]);
24144 }
24145 }
24146 $x = $xmin;
24147 $y = $ymin;
24148 $w = ($xmax - $xmin);
24149 $h = ($ymax - $ymin);
24150 if ($name == 'polyline') {
24151 $this->StartTransform();
24152 $this->SVGTransform($tm);
24153 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
24154 if (!empty($obstyle)) {
24155 $this->PolyLine($p, $obstyle, array(), array());
24156 }
24157 $this->StopTransform();
24158 } else { // polygon
24159 if ($clipping) {
24160 $this->SVGTransform($tm);
24161 $this->Polygon($p, 'CNZ', array(), array(), true);
24162 } else {
24163 $this->StartTransform();
24164 $this->SVGTransform($tm);
24165 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
24166 if (!empty($obstyle)) {
24167 $this->Polygon($p, $obstyle, array(), array(), true);
24168 }
24169 $this->StopTransform();
24170 }
24171 }
24172 break;
24173 }
24174 // image
24175 case 'image': {
24176 if ($invisible) {
24177 break;
24178 }
24179 if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
24180 break;
24181 }
24182 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24183 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24184 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24185 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24186 $img = $attribs['xlink:href'];
24187 if (!$clipping) {
24188 $this->StartTransform();
24189 $this->SVGTransform($tm);
24190 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
24191 if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
24192 // embedded image encoded as base64
24193 $img = '@'.base64_decode(substr($img, strlen($m[0])));
24194 } else {
24195 // fix image path
24196 if (!TCPDF_STATIC::empty_string($this->svgdir) AND (($img[0] == '.') OR (basename($img) == $img))) {
24197 // replace relative path with full server path
24198 $img = $this->svgdir.'/'.$img;
24199 }
24200 if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
24201 $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
24202 if (($findroot === false) OR ($findroot > 1)) {
24203 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
24204 $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
24205 } else {
24206 $img = $_SERVER['DOCUMENT_ROOT'].$img;
24207 }
24208 }
24209 }
24210 $img = urldecode($img);
24211 $testscrtype = @parse_url($img);
24212 if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
24213 // convert URL to server path
24214 $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
24215 }
24216 }
24217 // get image type
24219 if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
24220 $this->ImageEps($img, $x, $y, $w, $h);
24221 } elseif ($imgtype == 'svg') {
24222 // store SVG vars
24223 $svggradients = $this->svggradients;
24224 $svggradientid = $this->svggradientid;
24225 $svgdefsmode = $this->svgdefsmode;
24226 $svgdefs = $this->svgdefs;
24227 $svgclipmode = $this->svgclipmode;
24228 $svgclippaths = $this->svgclippaths;
24229 $svgcliptm = $this->svgcliptm;
24230 $svgclipid = $this->svgclipid;
24231 $svgtext = $this->svgtext;
24232 $svgtextmode = $this->svgtextmode;
24233 $this->ImageSVG($img, $x, $y, $w, $h);
24234 // restore SVG vars
24235 $this->svggradients = $svggradients;
24236 $this->svggradientid = $svggradientid;
24237 $this->svgdefsmode = $svgdefsmode;
24238 $this->svgdefs = $svgdefs;
24239 $this->svgclipmode = $svgclipmode;
24240 $this->svgclippaths = $svgclippaths;
24241 $this->svgcliptm = $svgcliptm;
24242 $this->svgclipid = $svgclipid;
24243 $this->svgtext = $svgtext;
24244 $this->svgtextmode = $svgtextmode;
24245 } else {
24246 $this->Image($img, $x, $y, $w, $h);
24247 }
24248 $this->StopTransform();
24249 }
24250 break;
24251 }
24252 // text
24253 case 'text':
24254 case 'tspan': {
24255 if (isset($this->svgtextmode['text-anchor']) AND !empty($this->svgtext)) {
24256 // @TODO: unsupported feature
24257 }
24258 // only basic support - advanced features must be implemented
24259 $this->svgtextmode['invisible'] = $invisible;
24260 if ($invisible) {
24261 break;
24262 }
24263 array_push($this->svgstyles, $svgstyle);
24264 if (isset($attribs['x'])) {
24265 $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false);
24266 } elseif ($name == 'tspan') {
24267 $x = $this->x;
24268 } else {
24269 $x = 0;
24270 }
24271 if (isset($attribs['dx'])) {
24272 $x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false);
24273 }
24274 if (isset($attribs['y'])) {
24275 $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false);
24276 } elseif ($name == 'tspan') {
24277 $y = $this->y;
24278 } else {
24279 $y = 0;
24280 }
24281 if (isset($attribs['dy'])) {
24282 $y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false);
24283 }
24284 $svgstyle['text-color'] = $svgstyle['fill'];
24285 $this->svgtext = '';
24286 if (isset($svgstyle['text-anchor'])) {
24287 $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor'];
24288 } else {
24289 $this->svgtextmode['text-anchor'] = 'start';
24290 }
24291 if (isset($svgstyle['direction'])) {
24292 if ($svgstyle['direction'] == 'rtl') {
24293 $this->svgtextmode['rtl'] = true;
24294 } else {
24295 $this->svgtextmode['rtl'] = false;
24296 }
24297 } else {
24298 $this->svgtextmode['rtl'] = false;
24299 }
24300 if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
24301 $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false);
24302 } else {
24303 $this->svgtextmode['stroke'] = false;
24304 }
24305 $this->StartTransform();
24306 $this->SVGTransform($tm);
24307 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
24308 $this->x = $x;
24309 $this->y = $y;
24310 break;
24311 }
24312 // use
24313 case 'use': {
24314 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24315 $svgdefid = substr($attribs['xlink:href'], 1);
24316 if (isset($this->svgdefs[$svgdefid])) {
24317 $use = $this->svgdefs[$svgdefid];
24318 if (isset($attribs['xlink:href'])) {
24319 unset($attribs['xlink:href']);
24320 }
24321 if (isset($attribs['id'])) {
24322 unset($attribs['id']);
24323 }
24324 if (isset($use['attribs']['x']) AND isset($attribs['x'])) {
24325 $attribs['x'] += $use['attribs']['x'];
24326 }
24327 if (isset($use['attribs']['y']) AND isset($attribs['y'])) {
24328 $attribs['y'] += $use['attribs']['y'];
24329 }
24330 if (empty($attribs['style'])) {
24331 $attribs['style'] = '';
24332 }
24333 if (!empty($use['attribs']['style'])) {
24334 // merge styles
24335 $attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']);
24336 }
24337 $attribs = array_merge($use['attribs'], $attribs);
24338 $this->startSVGElementHandler($parser, $use['name'], $attribs);
24339 return;
24340 }
24341 }
24342 break;
24343 }
24344 default: {
24345 break;
24346 }
24347 } // end of switch
24348 // process child elements
24349 if (!empty($attribs['child_elements'])) {
24350 $child_elements = $attribs['child_elements'];
24351 unset($attribs['child_elements']);
24352 foreach($child_elements as $child_element) {
24353 if (empty($child_element['attribs']['closing_tag'])) {
24354 $this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']);
24355 } else {
24356 if (isset($child_element['attribs']['content'])) {
24357 $this->svgtext = $child_element['attribs']['content'];
24358 }
24359 $this->endSVGElementHandler('child-tag', $child_element['name']);
24360 }
24361 }
24362 }
24363 }
24364
24373 protected function endSVGElementHandler($parser, $name) {
24374 $name = $this->removeTagNamespace($name);
24375 if ($this->svgdefsmode AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {;
24376 if (end($this->svgdefs) !== FALSE) {
24377 $last_svgdefs_id = key($this->svgdefs);
24378 if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
24379 foreach($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) {
24380 if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) {
24381 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24382 return;
24383 }
24384 }
24385 if ($this->svgdefs[$last_svgdefs_id]['name'] == $name) {
24386 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24387 return;
24388 }
24389 }
24390 }
24391 return;
24392 }
24393 switch($name) {
24394 case 'defs': {
24395 $this->svgdefsmode = false;
24396 break;
24397 }
24398 // clipPath
24399 case 'clipPath': {
24400 $this->svgclipmode = false;
24401 break;
24402 }
24403 case 'svg': {
24404 if (--$this->svg_tag_depth <= 0) {
24405 break;
24406 }
24407 }
24408 case 'g': {
24409 // ungroup: remove last style from array
24410 array_pop($this->svgstyles);
24411 $this->StopTransform();
24412 break;
24413 }
24414 case 'text':
24415 case 'tspan': {
24416 if ($this->svgtextmode['invisible']) {
24417 // This implementation must be fixed to following the rule:
24418 // If the 'visibility' property is set to hidden on a 'tspan', 'tref' or 'altGlyph' element, then the text is invisible but still takes up space in text layout calculations.
24419 break;
24420 }
24421 // print text
24422 $text = $this->svgtext;
24423 //$text = $this->stringTrim($text);
24424 $textlen = $this->GetStringWidth($text);
24425 if ($this->svgtextmode['text-anchor'] != 'start') {
24426 // check if string is RTL text
24427 if ($this->svgtextmode['text-anchor'] == 'end') {
24428 if ($this->svgtextmode['rtl']) {
24429 $this->x += $textlen;
24430 } else {
24431 $this->x -= $textlen;
24432 }
24433 } elseif ($this->svgtextmode['text-anchor'] == 'middle') {
24434 if ($this->svgtextmode['rtl']) {
24435 $this->x += ($textlen / 2);
24436 } else {
24437 $this->x -= ($textlen / 2);
24438 }
24439 }
24440 }
24441 $textrendermode = $this->textrendermode;
24442 $textstrokewidth = $this->textstrokewidth;
24443 $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false);
24444 if ($name == 'text') {
24445 // store current coordinates
24446 $tmpx = $this->x;
24447 $tmpy = $this->y;
24448 }
24449 // print the text
24450 $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
24451 if ($name == 'text') {
24452 // restore coordinates
24453 $this->x = $tmpx;
24454 $this->y = $tmpy;
24455 }
24456 // restore previous rendering mode
24457 $this->textrendermode = $textrendermode;
24458 $this->textstrokewidth = $textstrokewidth;
24459 $this->svgtext = '';
24460 $this->StopTransform();
24461 if (!$this->svgdefsmode) {
24462 array_pop($this->svgstyles);
24463 }
24464 break;
24465 }
24466 default: {
24467 break;
24468 }
24469 }
24470 }
24471
24480 protected function segSVGContentHandler($parser, $data) {
24481 $this->svgtext .= $data;
24482 }
24483
24484 // --- END SVG METHODS -----------------------------------------------------
24485
24486} // END OF TCPDF CLASS
24487
24488//============================================================+
24489// END OF FILE
24490//============================================================+
$result
print $file
$n
Definition: RandomTest.php:80
$size
Definition: RandomTest.php:79
if(! $in) $columns
Definition: Utf8Test.php:46
$space
Definition: Sanitizer.php:42
global $l
Definition: afr.php:30
$filename
Definition: buildRTE.php:89
foreach($mandatory_scripts as $file) $timestamp
Definition: buildRTE.php:81
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:7368
SetXY($x, $y, $rtloff=false)
Defines the abscissa and ordinate of the current position.
Definition: tcpdf.php:7508
getPageNumGroupAlias()
Return the alias for the page number on the current page group.
Definition: tcpdf.php:13656
$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:12494
MirrorV($y='')
Verical Mirroring.
Definition: tcpdf.php:11078
unserializeTCPDFtagParameters($data)
Unserialize parameters to be used with TCPDF tag in HTML code.
Definition: tcpdf.php:17073
$pdfunit
Default unit of measure for document.
Definition: tcpdf.php:1485
setJPEGQuality($quality)
Set the default JPEG compression quality (1-100)
Definition: tcpdf.php:13952
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:20595
$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:24373
_putimages()
Output images.
Definition: tcpdf.php:9101
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:22106
$custom_xmp
Custom XMP data.
Definition: tcpdf.php:1779
stringLeftTrim($str, $replace='')
Left trim the input string.
Definition: tcpdf.php:22134
_putsignature()
Add certification signature (DocMDP or UR3) You can set only one signature type.
Definition: tcpdf.php:13290
endTemplate()
End the current XObject Template started with startTemplate() and restore the previous graphic state.
Definition: tcpdf.php:22294
$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:15773
getCSSFontStretching($stretch, $parent=100)
Returns the percentage of font stretching from CSS value.
Definition: tcpdf.php:16124
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:11280
$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:13415
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:18678
getFontStyle()
Returns the current font style.
Definition: tcpdf.php:15817
__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:21488
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:22185
$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:12617
_dooverlinew($x, $y, $w)
Overline for rectangular text area.
Definition: tcpdf.php:10156
_putviewerpreferences()
Output viewer preferences.
Definition: tcpdf.php:9838
LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0, 0, 1, 0))
Paints a linear colour gradient.
Definition: tcpdf.php:14311
_dochecks()
Check for locale-related bug.
Definition: tcpdf.php:7792
$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:20778
getPDFData()
Returns the PDF data.
Definition: tcpdf.php:10356
setListIndentWidth($width)
Set custom width for list indentation.
Definition: tcpdf.php:20212
colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A, R, G, B, C, M, Y, K')
Paints color transition registration bars.
Definition: tcpdf.php:14029
checkPageRegions($h, $x, $y)
Check page for no-write regions and adapt current coordinates and page margins if necessary.
Definition: tcpdf.php:22573
$txtshadow
Text shadow data array.
Definition: tcpdf.php:690
_putocg()
Put pdf layers.
Definition: tcpdf.php:13698
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:10075
getAlpha()
Get the alpha mode array (CA, ca, BM, AIS).
Definition: tcpdf.php:13942
$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:12581
_putspotcolors()
Output Spot Colors Resources.
Definition: tcpdf.php:9333
_putfonts()
Output fonts.
Definition: tcpdf.php:8775
$pagelen
Array containing page lengths in bytes.
Definition: tcpdf.php:1163
TranslateX($t_x)
Translate graphic object horizontally.
Definition: tcpdf.php:11115
ScaleY($s_y, $x='', $y='')
Vertical Scaling.
Definition: tcpdf.php:11007
$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:11583
getHeaderFont()
Get header font.
Definition: tcpdf.php:10313
getBuffer()
Get buffer content.
Definition: tcpdf.php:20699
getGraphicVars()
Returns current graphic variables as array.
Definition: tcpdf.php:20534
$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:10969
$page_regions
Array of no-write regions.
Definition: tcpdf.php:1555
getOverprint()
Get the overprint mode array (OP, op, OPM).
Definition: tcpdf.php:13897
PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2)
Draw the sector of an ellipse.
Definition: tcpdf.php:14791
$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:13994
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:13574
$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:12303
$images
Array of used images.
Definition: tcpdf.php:342
removePageRegion($key)
Remove a single no-write region.
Definition: tcpdf.php:22555
setLanguageArray($language)
Set language array.
Definition: tcpdf.php:10343
Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true)
Draws a polygon.
Definition: tcpdf.php:11862
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:20711
$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:7520
copyPage($page=0)
Clone the specified page to a new page.
Definition: tcpdf.php:21201
putHtmlListBullet($listdepth, $listtype='', $size=10)
Output an HTML list bullet or ordered item symbol.
Definition: tcpdf.php:20332
getCSSBorderStyle($cssborder)
Returns the border style array from CSS border properties.
Definition: tcpdf.php:15900
Close()
Terminates the PDF document.
Definition: tcpdf.php:2951
startPageGroup($page='')
Create a new page group.
Definition: tcpdf.php:13561
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:21824
rollbackTransaction($self=false)
This method allows to undo the latest transaction by returning the latest saved TCPDF object with sta...
Definition: tcpdf.php:21652
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:21011
segSVGContentHandler($parser, $data)
Sets the character data handler function for the XML parser.
Definition: tcpdf.php:24480
$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:21951
$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:22323
getAliasNbPages()
Returns the string alias used for the total number of pages.
Definition: tcpdf.php:13611
movePage($frompage, $topage)
Move a page to a previous position.
Definition: tcpdf.php:20840
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:7559
SetLineStyle($style, $ret=false)
Set line style.
Definition: tcpdf.php:11323
_putjavascript()
Create a javascript PDF string.
Definition: tcpdf.php:12523
$svgdefs
Array of SVG defs.
Definition: tcpdf.php:1631
_putresources()
Output Resources.
Definition: tcpdf.php:9429
$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:10333
$PDFVersion
PDF version.
Definition: tcpdf.php:557
sortBookmarks()
Sort bookmarks for page and key.
Definition: tcpdf.php:12350
GetX()
Returns the relative X value of current position.
Definition: tcpdf.php:7402
$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:8745
_putbookmarks()
Create a bookmark PDF string.
Definition: tcpdf.php:12368
_getannotsrefs($n)
Get references to page annotations.
Definition: tcpdf.php:8058
_dounderlinew($x, $y, $w)
Underline for rectangular text area.
Definition: tcpdf.php:10105
PageNoFormatted()
Returns the current page number formatted as a string.
Definition: tcpdf.php:13689
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:11138
setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false)
Set alpha for stroking (CA) and non-stroking (ca) operations.
Definition: tcpdf.php:13910
$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:10644
setColumnsArray($columns)
Set columns array.
Definition: tcpdf.php:21721
$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:15198
setOverprint($stroking=true, $nonstroking='', $mode=0)
Set overprint mode for stroking (OP) and non-stroking (op) painting operations.
Definition: tcpdf.php:13871
registrationMark($x, $y, $r, $double=false, $cola=array(100, 100, 100, 100, 'All'), $colb=array(0, 0, 0, 0, 'None'))
Paints a registration mark.
Definition: tcpdf.php:14240
$fhPt
Height of page format in points.
Definition: tcpdf.php:214
getDestination()
Return the Named Destination array.
Definition: tcpdf.php:12250
PolyLine($p, $style='', $line_style=array(), $fill_color=array())
Draws a polygonal line.
Definition: tcpdf.php:11843
MirrorH($x='')
Horizontal Mirroring.
Definition: tcpdf.php:11067
$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:13468
getPageBuffer($page)
Get page buffer content.
Definition: tcpdf.php:20731
removeTagNamespace($name)
Return the tag name without the namespace.
Definition: tcpdf.php:23688
_newobj()
Begin a new object and return the object number.
Definition: tcpdf.php:10063
startSVGElementHandler($parser, $name, $attribs, $ctm=array())
Sets the opening SVG element handler function for the XML parser.
Definition: tcpdf.php:23706
addExtGState($parms)
Add transparency parameters to the current extgstate.
Definition: tcpdf.php:13798
selectColumn($col='')
Set position at a given column.
Definition: tcpdf.php:21735
getCSSFontSpacing($spacing, $parent=0)
Returns the letter-spacing value from CSS value.
Definition: tcpdf.php:16093
$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:19348
$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:9919
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:22676
getCSSMargin($cssmargin, $width=0)
Get the internal Cell margin from CSS attribute.
Definition: tcpdf.php:16005
setRasterizeVectorImages($mode)
Enable/disable rasterization of vector images using ImageMagick library.
Definition: tcpdf.php:22095
$svgclipmode
Boolean value true when in SVG clipPath tag.
Definition: tcpdf.php:1638
_putXMP()
Put XMP data object and return ID.
Definition: tcpdf.php:9513
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:17052
$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:16250
$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:11034
__destruct()
Default destructor.
Definition: tcpdf.php:1996
_putcidfont0($font)
Output CID-0 fonts.
Definition: tcpdf.php:9031
_putextgstates()
Put extgstates for object transparency.
Definition: tcpdf.php:13842
$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:7418
$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:10219
$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:11449
RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array())
Draws a rounded rectangle.
Definition: tcpdf.php:12053
getBorderStartPosition()
Return the starting coordinates to draw an html border.
Definition: tcpdf.php:19921
$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:17038
$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:7249
$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:11248
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:20248
SkewY($angle_y, $x='', $y='')
Skew vertically.
Definition: tcpdf.php:11203
_putpages()
Output pages (and replace page number aliases).
Definition: tcpdf.php:7896
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:22467
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:20746
$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:13726
$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:13984
$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:6829
$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:20222
setHtmlVSpace($tagvs)
Set the vertical spaces for HTML tags.
Definition: tcpdf.php:20202
TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false)
Creates a text field.
Definition: tcpdf.php:12646
$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:12870
$linestyleDash
PDF string for dash value of the last line.
Definition: tcpdf.php:1029
_getxobjectdict()
Return XObjects Dictionary.
Definition: tcpdf.php:9353
_putinfo()
Adds some Metadata information (Document Information Dictionary) (see Chapter 14.3....
Definition: tcpdf.php:9452
_fixAES256Password($password)
Convert password for AES-256 encryption mode.
Definition: tcpdf.php:10707
$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:22536
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:11020
$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:13965
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:9651
$hPt
Current height of page in points.
Definition: tcpdf.php:226
getCSSBorderWidth($width)
Returns the border width from CSS property.
Definition: tcpdf.php:15844
startTransaction()
Stores a copy of the current TCPDF object used for undo operation.
Definition: tcpdf.php:21621
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:20677
$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:13378
_getrawstream($s, $n=0)
get raw output stream.
Definition: tcpdf.php:10261
getCSSBorderDashStyle($style)
Returns the border dash style from CSS property.
Definition: tcpdf.php:15864
$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:21814
_datestring($n=0, $timestamp=0)
Returns a formatted date for meta information.
Definition: tcpdf.php:10231
$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:11825
SetProtection($permissions=array('print', 'modify', 'copy', 'annot-forms', 'fill-forms', 'extract', 'assemble', 'print-high'), $user_pass='', $owner_pass=null, $mode=0, $pubkeys=null)
Set document protection Remark: the protection against modification is for people who have the full A...
Definition: tcpdf.php:10840
startTemplate($w=0, $h=0, $group=false)
Start a new XObject Template.
Definition: tcpdf.php:22229
getCSSBorderMargin($cssbspace, $width=0)
Get the border-spacing from CSS attribute.
Definition: tcpdf.php:16059
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:20764
cropMark($x, $y, $w, $h, $type='T, R, B, L', $color=array(100, 100, 100, 100, 'All'))
Paints crop marks.
Definition: tcpdf.php:14169
$header_line_color
Color for header line (RGB array).
Definition: tcpdf.php:669
_dooverline($x, $y, $txt)
Overline text.
Definition: tcpdf.php:10143
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:22173
$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:7531
writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true)
Definition: tcpdf.php:17104
setPageBoxTypes($boxes)
Set page boxes to be included on page descriptions.
Definition: tcpdf.php:7883
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:10443
setHtmlLinksStyle($color=array(0, 0, 255), $fontstyle='U')
Set the color and font style for HTML links.
Definition: tcpdf.php:20233
_dounderline($x, $y, $txt)
Underline text.
Definition: tcpdf.php:10092
drawHTMLTagBorder($tag, $xmax)
Draw an HTML block border and fill.
Definition: tcpdf.php:19937
addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false)
Add vertical spaces if needed.
Definition: tcpdf.php:19895
Line($x1, $y1, $x2, $y2, $style=array())
Draws a line between two points.
Definition: tcpdf.php:11482
$y
Current vertical position in user unit for cell positioning.
Definition: tcpdf.php:300
_dolinethrough($x, $y, $txt)
Line through text.
Definition: tcpdf.php:10117
RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false)
Creates a RadioButton field.
Definition: tcpdf.php:12767
setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0, 0, 0), $x=-1, $link='')
Adds a bookmark - alias for Bookmark().
Definition: tcpdf.php:12286
$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:15797
$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:7442
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:14267
commitTransaction()
Delete the copy of the current TCPDF object used for undo operation.
Definition: tcpdf.php:21638
hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8)
Returns text with soft hyphens.
Definition: tcpdf.php:22031
Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array())
Draws a rectangle.
Definition: tcpdf.php:11512
$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:13975
$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:11433
getFontSpacing()
Get the amount to increase or decrease the space between characters in a text.
Definition: tcpdf.php:22489
$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:13043
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:13518
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:13123
$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:12207
$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:9503
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:11217
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:21891
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:13746
setPageRegions($regions=array())
Set no-write regions on page.
Definition: tcpdf.php:22516
pixelsToUnits($px)
Converts pixels to User's Units.
Definition: tcpdf.php:10417
$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:10694
$customlistindent
HTML PARSER: custom indent amount for lists.
Definition: tcpdf.php:1106
_putannotsobjs()
Output annotations objects for all pages.
Definition: tcpdf.php:8102
GetLineWidth()
Returns the current the line width.
Definition: tcpdf.php:11296
_putheader()
Output PDF File Header (7.5.2).
Definition: tcpdf.php:9910
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:10946
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:10463
setTextRenderingMode($stroke=0, $fill=true, $clip=false)
Set Text rendering mode.
Definition: tcpdf.php:21836
stringRightTrim($str, $replace='')
Right trim the input string.
Definition: tcpdf.php:22147
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:20659
setDocCreationTimestamp($time)
Set the document creation timestamp.
Definition: tcpdf.php:10183
_outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false)
Append an elliptical arc to the current path.
Definition: tcpdf.php:11694
setPageFormat($format, $orientation='P')
Change the format of the current page.
Definition: tcpdf.php:2103
getBarcode()
Get current barcode.
Definition: tcpdf.php:15164
MirrorP($x='', $y='')
Point reflection mirroring.
Definition: tcpdf.php:11090
$FontSize
Current font size in user unit.
Definition: tcpdf.php:416
_Uvalue()
Compute U value (used for encryption)
Definition: tcpdf.php:10611
StarPolygon($x0, $y0, $r, $nv, $ng, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array())
Draws a star polygon.
Definition: tcpdf.php:12011
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:11126
setFontSpacing($spacing=0)
Set the amount to increase or decrease the space between characters in a text.
Definition: tcpdf.php:22478
setFontBuffer($font, $data)
Set font buffer content.
Definition: tcpdf.php:20792
$last_enc_key_c
Last RC4 computed key.
Definition: tcpdf.php:822
getTextShadow()
Return the text shadow parameters array.
Definition: tcpdf.php:21933
$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:11415
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:20178
RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array())
Draws a rounded rectangle.
Definition: tcpdf.php:12072
RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5, 0.5, 0.5, 0.5, 1))
Paints a radial colour gradient.
Definition: tcpdf.php:14329
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:11613
$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:11385
setVisibility($v)
Set the visibility of the successive elements.
Definition: tcpdf.php:13765
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:11653
Gradient($type, $coords, $stops, $background=array(), $antialias=false)
Output gradient.
Definition: tcpdf.php:14471
$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:10491
$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:8927
SVGTransform($tm)
Apply SVG graphic transformation matrix.
Definition: tcpdf.php:23017
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:13669
SkewX($angle_x, $x='', $y='')
Skew horizontally.
Definition: tcpdf.php:11190
$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:23036
replaceBuffer($data)
Replace the buffer content.
Definition: tcpdf.php:20688
$img_rb_y
The right-bottom corner Y coordinate of last inserted image.
Definition: tcpdf.php:534
_putdests()
Insert Named Destinations.
Definition: tcpdf.php:12260
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:22160
$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:10656
$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:14770
$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:17121
SetTitle($title)
Defines the title of the document.
Definition: tcpdf.php:2867
_putshaders()
Output gradient shaders.
Definition: tcpdf.php:14578
_out($s)
Output a string to the document.
Definition: tcpdf.php:10274
setFontSubBuffer($font, $key, $data)
Set font buffer content.
Definition: tcpdf.php:20811
getFontBuffer($font)
Get font buffer content.
Definition: tcpdf.php:20825
$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:10245
$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:16196
_putxobjects()
Output Form XObjects Templates.
Definition: tcpdf.php:9230
$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:13679
getDocCreationTimestamp()
Returns document creation timestamp in seconds.
Definition: tcpdf.php:10209
addJavascriptObject($script, $onload=false)
Adds a javascript object and return object ID.
Definition: tcpdf.php:12507
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:22501
getFormDefaultProp()
Return the default properties for form fields.
Definition: tcpdf.php:12628
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:15787
getCSSPadding($csspadding, $width=0)
Get the internal Cell padding from CSS attribute.
Definition: tcpdf.php:15951
ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false)
Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
Definition: tcpdf.php:14835
$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:15807
$bMargin
Page break margin.
Definition: tcpdf.php:274
getFontSubsetting()
Return the default option for font subsetting.
Definition: tcpdf.php:22121
getAliasRightShift()
Returns the string alias used right align page numbers.
Definition: tcpdf.php:13585
setViewerPreferences($preferences)
Set the viewer preferences dictionary controlling the way the document is to be presented on the scre...
Definition: tcpdf.php:14012
$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:11103
$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:22997
SetY($y, $resetx=true, $rtloff=false)
Moves the current abscissa back to the left margin and sets the ordinate.
Definition: tcpdf.php:7475
$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:10169
setDocModificationTimestamp($time)
Set the document modification timestamp.
Definition: tcpdf.php:10196
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:15154
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:22456
setPageOrientation($orientation, $autopagebreak='', $bottommargin='')
Set page orientation.
Definition: tcpdf.php:2217
ScaleX($s_x, $x='', $y='')
Horizontal Scaling.
Definition: tcpdf.php:10994
$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:12142
$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:11963
$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:13829
resetColumns()
Remove columns and reset page margins.
Definition: tcpdf.php:21708
fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='')
Cleanup HTML code (requires HTML Tidy library).
Definition: tcpdf.php:15833
getAliasNumPage()
Returns the string alias used for the page number.
Definition: tcpdf.php:13626
getCellPaddings()
Get the internal Cell padding array.
Definition: tcpdf.php:2668
GetY()
Returns the ordinate of the current position.
Definition: tcpdf.php:7429
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:10428
$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:21278
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:13486
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:7757
$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:15746
$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:21676
_beginpage($orientation='', $format='')
Initialize a new page.
Definition: tcpdf.php:10010
getCellMargins()
Get the internal Cell margin array.
Definition: tcpdf.php:2704
_generateencryptionkey()
Compute encryption key.
Definition: tcpdf.php:10722
serializeTCPDFtagParameters($data)
Serialize an array of parameters to be used with TCPDF tag in HTML code.
Definition: tcpdf.php:17062
$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:10303
SetAbsXY($x, $y)
Set the absolute X and Y coordinates of the current pointer.
Definition: tcpdf.php:7543
Rotate($angle, $x='', $y='')
Rotate object.
Definition: tcpdf.php:11160
applyTSA($signature)
NOT YET IMPLEMENTED Request TSA for a timestamp.
Definition: tcpdf.php:13546
$re_space
Array of $re_spaces parts.
Definition: tcpdf.php:1310
_putresourcedict()
Output Resources Dictionary.
Definition: tcpdf.php:9365
getRTL()
Return the RTL status.
Definition: tcpdf.php:2364
_endpage()
Mark end of page.
Definition: tcpdf.php:10053
$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:10323
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:10375
_dolinethroughw($x, $y, $w)
Line through for rectangular text area.
Definition: tcpdf.php:10130
getInternalPageNumberAliases($a='')
Return an array containing variations for the basic page number alias.
Definition: tcpdf.php:7809
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:14444
$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:20139
replaceRightShiftPageNumAliases($page, $aliases, $diff)
Replace right shift page number aliases with spaces to correct right alignment.
Definition: tcpdf.php:7848
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:20162
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:11399
_outRestoreGraphicsState()
Outputs the "restore graphics state" operator 'Q'.
Definition: tcpdf.php:20667
$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:13641
$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:7830
SVGPath($d, $style='')
Draws an SVG path.
Definition: tcpdf.php:23331
$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:14352
$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:11465
$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:15517
setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='')
Set the digital signature appearance (a cliccable rectangle area to get signature properties)
Definition: tcpdf.php:13452
$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:7343
$encrypted
IBoolean flag indicating whether document is protected.
Definition: tcpdf.php:801
if(!file_exists(getcwd().'/ilias.ini.php')) if(isset( $_GET["client_id"]))
registration confirmation script for ilias
Definition: confirmReg.php:20
$txt
Definition: error.php:12
$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
$header
$data
$style
Definition: example_012.php:70
$yc
$xc
$px
$angle
$js
$text
$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
$params
Definition: example_049.php:96
$code
Definition: example_050.php:99
$bMargin
$info
Definition: example_052.php:80
$border
$regions
$barcodeobj
v($data, $pos)
$cmd
Definition: sahs_server.php:35
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
$url
Definition: shib_logout.php:72
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/).